1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
/*========================== begin_copyright_notice ============================
Copyright (C) 2017-2021 Intel Corporation
SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/
#include "AdaptorCommon/ImplicitArgs.hpp"
#include "Compiler/Optimizer/OpenCLPasses/OpenCLPrintf/OpenCLPrintfAnalysis.hpp"
#include "Compiler/IGCPassSupport.h"
#include "common/LLVMWarningsPush.hpp"
#include <llvm/IR/Module.h>
#include <llvm/IR/Function.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/Demangle/Demangle.h>
#include "common/LLVMWarningsPop.hpp"
#include <set>
using namespace llvm;
using namespace IGC;
using namespace IGC::IGCMD;
// Register pass to igc-opt
#define PASS_FLAG "igc-opencl-printf-analysis"
#define PASS_DESCRIPTION "Analyzes OpenCL printf calls"
#define PASS_CFG_ONLY false
#define PASS_ANALYSIS false
IGC_INITIALIZE_PASS_BEGIN(OpenCLPrintfAnalysis, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
IGC_INITIALIZE_PASS_DEPENDENCY(MetaDataUtilsWrapper)
IGC_INITIALIZE_PASS_END(OpenCLPrintfAnalysis, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
char OpenCLPrintfAnalysis::ID = 0;
OpenCLPrintfAnalysis::OpenCLPrintfAnalysis() : ModulePass(ID) {
initializeOpenCLPrintfAnalysisPass(*PassRegistry::getPassRegistry());
}
// TODO: move to a common place
const StringRef OpenCLPrintfAnalysis::OPENCL_PRINTF_FUNCTION_NAME = "printf";
const StringRef OpenCLPrintfAnalysis::ONEAPI_PRINTF_FUNCTION_NAME = "ext::oneapi::experimental::printf";
const StringRef OpenCLPrintfAnalysis::BUILTIN_PRINTF_FUNCTION_NAME = "__builtin_IB_printf_to_buffer";
bool OpenCLPrintfAnalysis::isOpenCLPrintf(const llvm::Function *F) {
return F->getName() == OPENCL_PRINTF_FUNCTION_NAME;
}
bool OpenCLPrintfAnalysis::isOneAPIPrintf(const llvm::Function *F) {
std::string demangledName = llvm::demangle(F->getName().str());
return demangledName.find(ONEAPI_PRINTF_FUNCTION_NAME.data()) != std::string::npos;
}
bool OpenCLPrintfAnalysis::isBuiltinPrintf(const llvm::Function *F) {
return F->getName() == BUILTIN_PRINTF_FUNCTION_NAME;
}
bool OpenCLPrintfAnalysis::runOnModule(Module &M) {
m_pMDUtils = getAnalysis<MetaDataUtilsWrapper>().getMetaDataUtils();
visit(M);
bool changed = false;
if (m_hasPrintfs.size()) {
for (Function &func : M.getFunctionList()) {
if (!func.isDeclaration() && m_hasPrintfs.find(&func) != m_hasPrintfs.end()) {
addPrintfBufferArgs(func);
changed = true;
}
}
}
// Update LLVM metadata based on IGC MetadataUtils
if (changed)
m_pMDUtils->save(M.getContext());
return m_hasPrintfs.size();
}
void OpenCLPrintfAnalysis::visitCallInst(llvm::CallInst &callInst) {
Function *pF = callInst.getParent()->getParent();
if (!callInst.getCalledFunction() || m_hasPrintfs.find(pF) != m_hasPrintfs.end()) {
return;
}
StringRef funcName = callInst.getCalledFunction()->getName();
bool hasPrintf = (funcName == OpenCLPrintfAnalysis::OPENCL_PRINTF_FUNCTION_NAME);
if (hasPrintf) {
m_hasPrintfs.insert(pF);
}
}
void OpenCLPrintfAnalysis::addPrintfBufferArgs(Function &F) {
SmallVector<ImplicitArg::ArgType, 1> implicitArgs;
implicitArgs.push_back(ImplicitArg::PRINTF_BUFFER);
ImplicitArgs::addImplicitArgs(F, implicitArgs, m_pMDUtils);
}
bool isPrintfOnlyStringConstantImpl(const llvm::Value *v, std::set<const llvm::User *> &visited) {
// Recursively check the users of the value until reaching the top level
// user or a call.
// Base case: Return false when use list is empty.
if (v->use_empty()) {
return false;
}
// Check users recursively with a list of permitted in-between uses. Here we
// follow OpenCLPrintfResolution::argIsString() to check if they are one of
// CastInst, GEP with all-zero indices, SelectInst, and PHINode.
for (auto &use : v->uses()) {
auto user = use.getUser();
// Skip if the user is visited.
if (visited.count(user))
continue;
visited.insert(user);
bool res = false;
if (const llvm::CallInst *call = llvm::dyn_cast<llvm::CallInst>(user)) {
// Stop when reaching a call and check if it is an opencl/oneapi
// printf call.
const Function *target = call->getCalledFunction();
bool isStringLiteral = OpenCLPrintfAnalysis::isOpenCLPrintf(target) ||
OpenCLPrintfAnalysis::isOneAPIPrintf(target) ||
OpenCLPrintfAnalysis::isBuiltinPrintf(target);
if (isStringLiteral) {
res = true;
} else {
unsigned int opIndex = call->getDataOperandNo(&use);
res = isPrintfOnlyStringConstantImpl(target->arg_begin() + opIndex, visited);
}
} else if (llvm::dyn_cast<llvm::CastInst>(user) || llvm::dyn_cast<llvm::SelectInst>(user) ||
llvm::dyn_cast<llvm::PHINode>(user)) {
res = isPrintfOnlyStringConstantImpl(user, visited);
} else if (const llvm::GetElementPtrInst *gep = llvm::dyn_cast<llvm::GetElementPtrInst>(user)) {
if (gep->hasAllZeroIndices())
res = isPrintfOnlyStringConstantImpl(user, visited);
}
if (!res)
return false;
}
// Return true as every top level user is a printf call.
return true;
}
// Check paths from a string literal to printf calls and return true if every
// path lead to a printf call.
bool OpenCLPrintfAnalysis::isPrintfOnlyStringConstant(const llvm::GlobalVariable *GV) {
const llvm::Constant *Initializer = GV->getInitializer();
if (!Initializer) {
return false;
}
bool IsNullTerminatedString = false;
if (const auto *cds = llvm::dyn_cast<llvm::ConstantDataSequential>(Initializer)) {
if (cds->isString()) {
StringRef Str = cds->getAsString();
IsNullTerminatedString = Str.contains(0);
}
}
bool IsZeroInitCharArray = Initializer->isZeroValue() && isa<ArrayType>(Initializer->getType()) &&
Initializer->getType()->getArrayElementType()->isIntegerTy(8);
if (IsNullTerminatedString || IsZeroInitCharArray) {
std::set<const llvm::User *> Visited;
return isPrintfOnlyStringConstantImpl(GV, Visited);
}
return false;
}
|