Commit c732c290 authored by Xiao Lei's avatar Xiao Lei Committed by gbsbuild

Add support for allowing usage of function pointers, indirect calls, and...

Add support for allowing usage of function pointers, indirect calls, and calling externally linked functions

Change-Id: I15c87eba93cfb6efdfddcbc3659b4c10148ff236
parent 02a2d81b
......@@ -80,8 +80,8 @@ bool AddImplicitArgs::runOnModule(Module &M)
// Create new functions with implicit args
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
{
// Only handle functions defined in this module
Function* pFunc = &(*I);
// Only handle functions defined in this module
if (pFunc->isDeclaration()) continue;
// skip non-entry functions
if (m_pMdUtils->findFunctionsInfoItem(pFunc) == m_pMdUtils->end_FunctionsInfo()) continue;
......@@ -95,8 +95,25 @@ bool AddImplicitArgs::runOnModule(Module &M)
ImplicitArgs::addBufferOffsetArgs(*pFunc, m_pMdUtils, ctx->getModuleMetaData());
}
// Create the new function body and insert it into the module
ImplicitArgs implicitArgs(*pFunc, m_pMdUtils);
// If enabling indirect call, only R0, PayloadHeader and PrivateBase are allowed!
if (IGC_IS_FLAG_ENABLED(EnableFunctionPointer))
{
if (pFunc->hasFnAttribute("AsFunctionPointer"))
{
if (implicitArgs.size() != 3 ||
!implicitArgs.isImplicitArgExist(ImplicitArg::ArgType::R0) ||
!implicitArgs.isImplicitArgExist(ImplicitArg::ArgType::PAYLOAD_HEADER) ||
!implicitArgs.isImplicitArgExist(ImplicitArg::ArgType::PRIVATE_BASE))
{
assert(0 && "Implicit Arg not supported for indirect calls!");
continue;
}
}
}
// Create the new function body and insert it into the module
FunctionType *pNewFTy = getNewFuncType(pFunc, &implicitArgs);
Function* pNewFunc = Function::Create(pNewFTy, pFunc->getLinkage());
pNewFunc->copyAttributesFrom(pFunc);
......@@ -166,6 +183,11 @@ bool AddImplicitArgs::runOnModule(Module &M)
pFunc->eraseFromParent();
}
if (IGC_IS_FLAG_ENABLED(EnableFunctionPointer))
{
FixIndirectCalls(M);
}
return true;
}
......@@ -330,17 +352,32 @@ void AddImplicitArgs::replaceAllUsesWithNewOCLBuiltinFunction(CodeGenContext* ct
FunctionInfoMetaDataHandle subFuncInfo = m_pMdUtils->getFunctionsInfoItem(old_func);
std::vector<Instruction*> list_delete;
for (auto U = old_func->user_begin(), UE = old_func->user_end(); U != UE; ++U)
std::vector<Value*> functionUserList(old_func->user_begin(), old_func->user_end());
for (auto U : functionUserList)
{
std::vector<Value*> new_args;
CallInst *cInst = dyn_cast<CallInst>(*U);
auto BC = dyn_cast<BitCastInst>(*U);
CallInst *cInst = dyn_cast<CallInst>(U);
auto BC = dyn_cast<BitCastInst>(U);
if (BC && BC->hasOneUse())
cInst = dyn_cast<CallInst>(BC->user_back());
if (IGC_IS_FLAG_ENABLED(EnableFunctionPointer))
{
if (!cInst || cInst->getCalledValue() != old_func)
{
// Support indirect function pointer usages
if (Instruction* userInst = dyn_cast<Instruction>(U))
{
IRBuilder<> builder(userInst);
Value* fncast = builder.CreateBitCast(new_func, old_func->getType());
userInst->replaceUsesOfWith(old_func, fncast);
continue;
}
}
}
if (!cInst)
{
//assert(0 && " Not supported");
assert(0 && "Unknown function usage");
getAnalysis<CodeGenContextWrapper>().getCodeGenContext()->EmitError(" undefined reference to `jmp()' ");
return;
}
......@@ -349,6 +386,8 @@ void AddImplicitArgs::replaceAllUsesWithNewOCLBuiltinFunction(CodeGenContext* ct
{
return;
}
std::vector<Value*> new_args;
Function *parent_func = cInst->getParent()->getParent();
size_t numArgOperands = cInst->getNumArgOperands();
......@@ -453,6 +492,62 @@ void AddImplicitArgs::replaceAllUsesWithNewOCLBuiltinFunction(CodeGenContext* ct
}
}
void AddImplicitArgs::FixIndirectCalls(Module& M)
{
// Handle indirect call instructions by inserting implicit args
std::vector<Instruction*> list_delete;
for (auto &F : M)
{
for (auto &BB : F)
{
for (auto &II : BB)
{
if (CallInst* call = dyn_cast<CallInst>(&II))
{
Function* calledFunc = call->getCalledFunction();
bool externalCall = calledFunc &&
calledFunc->isDeclaration() &&
(calledFunc->getLinkage() == GlobalValue::ExternalLinkage) &&
calledFunc->hasFnAttribute("AsFunctionPointer");
// Only handled indirect calls and external function calls
if (calledFunc && !externalCall) continue;
SmallVector<Value*, 8> args;
SmallVector<Type*, 8> argTys;
for (unsigned i = 0; i < call->getNumArgOperands(); i++)
{
args.push_back(call->getArgOperand(i));
}
Function* pFunc = call->getParent()->getParent();
ImplicitArgs implicitArgs(*pFunc, m_pMdUtils);
args.push_back(implicitArgs.getImplicitArg(*pFunc, ImplicitArg::ArgType::R0));
args.push_back(implicitArgs.getImplicitArg(*pFunc, ImplicitArg::ArgType::PAYLOAD_HEADER));
args.push_back(implicitArgs.getImplicitArg(*pFunc, ImplicitArg::ArgType::PRIVATE_BASE));
for (auto arg : args) argTys.push_back(arg->getType());
IRBuilder<> builder(call);
Value* funcPtr = call->getCalledValue();
PointerType* funcTy = PointerType::get(FunctionType::get(call->getType(), argTys, false), 0);
funcPtr = builder.CreateBitCast(funcPtr, funcTy);
Value* newCall = builder.CreateCall(funcPtr, args);
call->replaceAllUsesWith(newCall);
list_delete.push_back(call);
}
}
}
}
for (auto i : list_delete)
{
i->eraseFromParent();
}
}
// Builtin CallGraph Analysis
#define PASS_FLAG2 "igc-callgraphscc-analysis"
#define PASS_DESCRIPTION2 "Analyzes CallGraphSCC"
......
......@@ -119,6 +119,8 @@ namespace IGC
// @brief replace old CallInst with new CallInst
void replaceAllUsesWithNewOCLBuiltinFunction(CodeGenContext* ctx, llvm::Function* old_func, llvm::Function* new_func);
void FixIndirectCalls(llvm::Module &M);
/// @brief Metadata API obejct.
IGC::IGCMD::MetaDataUtils *m_pMdUtils;
......
......@@ -277,12 +277,9 @@ bool ProcessFuncAttributes::runOnModule(Module& M)
bool keepAlwaysInline = containsSLM;
if (IGC_GET_FLAG_VALUE(FunctionControl) != FLAG_FCALL_FORCE_INLINE)
{
// OCL2.0 allows Objective C's blocks which introduce users other
// than calls. Keep AlwaysInline attribute if there is any use other
// than call. This is missing feature that igc cannot handle
// indirect calls yet. Also AddImplicitArgs cannot handle load/store
// with function pointers.
if (!keepAlwaysInline)
// keep inline if function pointers not enabled and there are uses
// for function pointers other than call instructions
if (IGC_IS_FLAG_DISABLED(EnableFunctionPointer) && !keepAlwaysInline)
{
for (auto U : F->users())
{
......@@ -339,19 +336,47 @@ bool ProcessFuncAttributes::runOnModule(Module& M)
{
if (!keepAlwaysInline)
{
if (IGC_GET_FLAG_VALUE(FunctionControl) == FLAG_FCALL_FORCE_SUBROUTINE ||
IGC_GET_FLAG_VALUE(FunctionControl) == FLAG_FCALL_FORCE_STACKCALL)
bool forceSubroutine = IGC_GET_FLAG_VALUE(FunctionControl) == FLAG_FCALL_FORCE_SUBROUTINE;
bool forceStackCall = IGC_GET_FLAG_VALUE(FunctionControl) == FLAG_FCALL_FORCE_STACKCALL;
if (forceSubroutine || forceStackCall)
{
// add the following line in order to stress-test
// subroutine call or stack call
F->removeFnAttr(llvm::Attribute::AlwaysInline);
F->addFnAttr(llvm::Attribute::NoInline);
if (IGC_GET_FLAG_VALUE(FunctionControl) == FLAG_FCALL_FORCE_STACKCALL)
if (forceStackCall)
{
F->addFnAttr("visaStackCall");
}
}
}
if (IGC_IS_FLAG_ENABLED(EnableFunctionPointer))
{
// Check if the function can be indirectly called either from
// externally or as a function pointer
bool isIndirect = (F->getLinkage() == GlobalValue::ExternalLinkage);
if (!isIndirect)
{
for (auto u = F->user_begin(), e = F->user_end(); u != e; u++)
{
CallInst* call = dyn_cast<CallInst>(*u);
if (!call || call->getCalledValue() != F)
{
isIndirect = true;
}
}
}
if (isIndirect)
{
IGC::CodeGenContext* ctx = getAnalysis<CodeGenContextWrapper>().getCodeGenContext();
ctx->m_enableFunctionPointer = true;
ctx->m_enableSubroutine = false;
F->addFnAttr("AsFunctionPointer");
F->addFnAttr("visaStackCall");
}
}
}
Changed = true;
}
......
......@@ -302,6 +302,17 @@ void CEncoder::StackCall(CVariable* flag, llvm::Function *F, unsigned char argSi
V(vKernel->AppendVISACFFunctionCallInst(predOpnd, emask, execSize, (unsigned short)funcId, argSize, retSize));
}
void CEncoder::IndirectStackCall(CVariable* flag, CVariable* funcPtr, unsigned char argSize, unsigned char retSize)
{
m_encoderState.m_flag.var = flag;
VISA_PredOpnd* predOpnd = GetFlagOperand(m_encoderState.m_flag);
// control flow instructions cannot be broken down into lower SIMD
Common_VISA_EMask_Ctrl emask = m_encoderState.m_noMask ? vISA_EMASK_M1_NM : vISA_EMASK_M1;
Common_ISA_Exec_Size execSize = visaExecSize(m_program->m_dispatchSize);
VISA_VectorOpnd* funcAddrOpnd = GetSourceOperandNoModifier(funcPtr);
V(vKernel->AppendVISACFIndirectFuncCallInst(predOpnd, emask, execSize, funcAddrOpnd, argSize, retSize));
}
void CEncoder::SubroutineRet(CVariable* flag)
{
m_encoderState.m_flag.var = flag;
......@@ -3382,6 +3393,14 @@ void CEncoder::BeginStackFunction(llvm::Function *F)
V(vKernel->AppendVISACFLabelInst(visaLabel));
}
void CEncoder::AddFunctionSymbol(llvm::Function* F, CVariable* fvar)
{
SModifier mod;
mod.init();
VISA_VectorOpnd* visaFuncAddr = GetDestinationOperand(fvar, mod);
V(vKernel->AppendVISACFSymbolInst(F->getName(), visaFuncAddr));
}
void CEncoder::InitEncoder( bool canAbortOnSpill, bool hasStackCall )
{
m_aliasesMap.clear();
......
......@@ -222,6 +222,7 @@ public:
void SubroutineCall(CVariable *flag, llvm::Function *F);
void SubroutineRet(CVariable *flag);
void StackCall(CVariable *flag, llvm::Function *F, unsigned char argSize, unsigned char retSize);
void IndirectStackCall(CVariable* flag, CVariable* funcPtr, unsigned char argSize, unsigned char retSize);
void StackRet(CVariable *flag);
void Loc(unsigned int line);
void File(std::string& s);
......@@ -393,6 +394,8 @@ public:
void DestroyVISABuilder();
void AddFunctionSymbol(llvm::Function* F, CVariable* fvar);
private:
// helper functions
VISA_VectorOpnd* GetSourceOperand(CVariable* var, const SModifier& mod);
......
......@@ -531,6 +531,16 @@ void CShader::CreateConstantBufferOutput(SKernelProgram *pKernelProgram)
}
}
void CShader::CreateFuncSymbolToRegisterMap(llvm::Function* pFunc)
{
CVariable* funcAddr = GetSymbol(pFunc);
//CVariable* funcAddr32bit = GetNewVariable(1, ISA_TYPE_UD, EALIGN_GRF, true);
//encoder.Cast(funcAddr32bit, funcAddr);
//encoder.Push();
encoder.AddFunctionSymbol(pFunc, funcAddr);
encoder.Push();
}
void CShader::CacheArgumentsList()
{
m_argListCache.clear();
......@@ -1798,7 +1808,7 @@ void CShader::BeginFunction(llvm::Function *F)
ccTupleMapping.clear();
ConstantPool.clear();
bool useStackCall = m_FGA->useStackCall(F);
bool useStackCall = m_FGA && m_FGA->useStackCall(F);
if (useStackCall)
{
m_R0 = nullptr;
......@@ -1950,6 +1960,72 @@ CVariable* CShader::getOrCreateArgumentSymbol(llvm::Argument *Arg, bool useStack
return var;
}
CVariable* CShader::getOrCreateArgSymbolForIndirectCall(llvm::CallInst* cInst, unsigned argIdx)
{
assert(cInst->getCalledFunction() == nullptr);
assert(argIdx < cInst->getNumArgOperands());
CVariable* var = nullptr;
Value* Arg = cInst->getArgOperand(argIdx);
llvm::DenseMap<llvm::Value*, CVariable*> *pSymMap = &globalSymbolMapping;
auto it = pSymMap->find(Arg);
if (it != pSymMap->end())
{
return it->second;
}
// Last 3 operands for an indirect call are always R0, PayloadHeader, and PrivateBase
unsigned numImplicitArgs = 3;
unsigned numExplicitArgs = cInst->getNumArgOperands() - numImplicitArgs;
if (argIdx < numExplicitArgs)
{
VISA_Type type = GetType(Arg->getType());
uint16_t nElts = numLanes(m_SIMDSize);
if (Arg->getType()->isVectorTy())
{
assert(Arg->getType()->getVectorElementType()->isIntegerTy() ||
Arg->getType()->getVectorElementType()->isFloatingPointTy());
nElts *= (uint16_t)Arg->getType()->getVectorNumElements();
}
// GetPreferredAlignment treats all arguments as kernel ones, which have
// predefined alignments; but this is not true for subroutines.
// Conservatively use GRF aligned.
e_alignment align = EALIGN_GRF;
var = GetNewVariable(nElts, type, align, /*isUniform*/ false, m_numberInstance);
}
else
{
// Can be mapped to the parent's implicit arg
Function* parentFunc = cInst->getParent()->getParent();
ImplicitArgs implicitArgs(*parentFunc, m_pMdUtils);
for (unsigned i = 0; i < implicitArgs.size(); i++)
{
ImplicitArg implictArg = implicitArgs[i];
auto argType = implictArg.getArgType();
if (argType == ImplicitArg::ArgType::R0 ||
argType == ImplicitArg::ArgType::PAYLOAD_HEADER ||
argType == ImplicitArg::ArgType::PRIVATE_BASE)
{
Argument* implicitArgInFunc = implicitArgs.getImplicitArg(*parentFunc, argType);
if (Arg == implicitArgInFunc)
{
bool isUniform = implictArg.getDependency() == WIAnalysis::UNIFORM;
var = GetNewVariable((uint16_t)implictArg.getNumberElements(),
implictArg.getVISAType(*m_DL),
implictArg.getAlignType(*m_DL), isUniform,
isUniform ? 1 : m_numberInstance);
break;
}
}
}
}
assert(var && "Argument not matched!");
pSymMap->insert(std::make_pair(Arg, var));
return var;
}
// Reuse a varable in the following case
// %x = op1...
// %y = op2 (%x, ...)
......@@ -2152,8 +2228,24 @@ unsigned int CShader::EvaluateSIMDConstExpr(Value* C)
CVariable* CShader::GetSymbol(llvm::Value *value, bool fromConstantPool)
{
CVariable* var = nullptr;
if (Constant *C = llvm::dyn_cast<llvm::Constant>(value))
{
// Function Pointer
if (isa<GlobalValue>(value) &&
value->getType()->isPointerTy() &&
value->getType()->getPointerElementType()->isFunctionTy())
{
auto it = symbolMapping.find(value);
if( it != symbolMapping.end() )
{
return it->second;
}
var = GetNewVariable(1, ISA_TYPE_UQ, EALIGN_QWORD, true, 1);
symbolMapping.insert(std::pair<llvm::Value*, CVariable*>(value, var));
return var;
}
if (fromConstantPool) {
CVariable *cvar = ConstantPool.lookup(C);
if (cvar)
......@@ -3027,4 +3119,4 @@ CShaderProgram::~CShaderProgram()
delete m_SIMDshaders[i];
}
m_context = nullptr;
}
\ No newline at end of file
}
......@@ -340,6 +340,14 @@ bool EmitPass::runOnFunction(llvm::Function &F)
CreateKernelShaderMap(ctx, pMdUtils, F);
bool hasIndirectCall = IGC_IS_FLAG_ENABLED(EnableFunctionPointer) && ctx->m_instrTypes.hasIndirectCall;
// Only try SIMD8 for now if there are indirect calls
if (hasIndirectCall && m_SimdMode != SIMDMode::SIMD8)
{
return false;
}
// If the current kernel is deleted from the shader map, skip this function.
m_FGA = getAnalysisIfAvailable<GenXFunctionGroupAnalysis>();
if (!setCurrentShader(&F))
......@@ -394,9 +402,24 @@ bool EmitPass::runOnFunction(llvm::Function &F)
m_roundingMode = m_encoder->getEncoderRoundingMode(
static_cast<Float_RoundingMode>(ctx->getModuleMetaData()->compOpt.FloatRoundingMode));
m_currShader->PreCompile();
if (hasStackCall)
if (hasStackCall || hasIndirectCall)
{
m_currShader->InitKernelStack(ptr64bits);
if (IGC_IS_FLAG_ENABLED(EnableFunctionPointer))
{
Module* pModule = F.getParent();
for (auto &F : pModule->getFunctionList())
{
// Creates a mapping of the function symbol to a register.
// Any user function used by the kernel, including function declarations,
// should have a register allocated to store it's physical address
if (F.getNumUses() > 0 && F.hasFnAttribute("AsFunctionPointer"))
{
m_currShader->CreateFuncSymbolToRegisterMap(&F);
}
}
}
}
m_currShader->AddPrologue();
}
......@@ -429,7 +452,6 @@ bool EmitPass::runOnFunction(llvm::Function &F)
IF_DEBUG_INFO(m_pDebugEmitter->Initialize(m_currShader, DebugInfoData::hasDebugInfo(m_currShader));)
}
if (DebugInfoData::hasDebugInfo(m_currShader))
{
if (!m_currShader->diData)
......@@ -647,8 +669,13 @@ bool EmitPass::runOnFunction(llvm::Function &F)
m_encoder->Compile();
// if we are doing stack-call, do the following:
// - Hard-code a large scratch-space for visa
if (m_FGA && m_FGA->getGroup(&F)->hasStackCall())
if ((m_FGA && m_FGA->getGroup(&F)->hasStackCall()) || hasIndirectCall)
{
if (m_currShader->ProgramOutput()->m_scratchSpaceUsedBySpills == 0)
{
// Don't retry if we didn't spill
ctx->m_retryManager.Disable();
}
m_currShader->ProgramOutput()->m_scratchSpaceUsedBySpills =
MAX(m_currShader->ProgramOutput()->m_scratchSpaceUsedBySpills, 32 * 1024);
}
......@@ -8229,10 +8256,9 @@ void EmitPass::emitStackAlloca(GenIntrinsicInst* GII)
void EmitPass::emitCall(llvm::CallInst* inst)
{
llvm::Function* F = inst->getCalledFunction();
assert(F && "indirect call not supported");
assert(!F->empty() && "unexpanded builtin?");
if (F) assert(!F->empty() && "unexpanded builtin?");
if (m_FGA && m_FGA->useStackCall(F))
if (!F || (m_FGA && m_FGA->useStackCall(F)))
{
emitStackCall(inst);
return;
......@@ -8366,25 +8392,50 @@ uint EmitPass::stackCallArgumentAlignment(CVariable *argv)
void EmitPass::emitStackCall(llvm::CallInst* inst)
{
llvm::Function* F = inst->getCalledFunction();
assert(F && "indirect call not supported");
assert(!F->empty() && "unexpanded builtin?");
if (F) assert(!F->empty() && "unexpanded builtin?");
CVariable *ArgBlkVar = m_currShader->GetARGV();
uint32_t i = 0;
uint32_t offsetA = 0; // visa argument offset
uint32_t offsetS = 0; // visa stack offset
SmallVector<std::tuple<CVariable*, uint32_t, uint32_t, uint32_t>, 8> owordWrites;
for (auto &Arg : F->args())
for (uint32_t i = 0; i < inst->getNumArgOperands(); i++)
{
// Skip unused arguments if any.
if (Arg.use_empty())
CVariable *ArgCV = nullptr;
CVariable *Src = nullptr;
Type* argType = nullptr;
if (F)
{
++i;
continue;
assert(inst->getNumArgOperands() == F->arg_size());
auto Arg = F->arg_begin();
std::advance(Arg, i);
// For indirect calls we can't skip args since we won't know at compile time if it will be used
if (!F->hasFnAttribute("AsFunctionPointer"))
{
// Skip unused arguments if any.
if (Arg->use_empty())
{
continue;
}
}
ArgCV = m_currShader->getOrCreateArgumentSymbol(&*Arg, true);
Src = GetSymbol(inst->getArgOperand(i));
argType = Arg->getType();
}
else if (IGC_IS_FLAG_ENABLED(EnableFunctionPointer))
{
// Indirect function call
Value* operand = inst->getArgOperand(i);
argType = operand->getType();
Src = GetSymbol(operand);
ArgCV = m_currShader->getOrCreateArgSymbolForIndirectCall(inst, i);
}
else
{
assert(0 && "Indirect Call not supported");
}
CVariable *ArgCV = m_currShader->getOrCreateArgumentSymbol(&Arg, true);
CVariable *Src = GetSymbol(inst->getArgOperand(i++));
if (Src->GetType() == ISA_TYPE_BOOL)
{
assert(ArgCV->GetType() == ISA_TYPE_BOOL);
......@@ -8399,9 +8450,10 @@ void EmitPass::emitStackCall(llvm::CallInst* inst)
}
else
{
emitCopyAll(ArgCV, Src, Arg.getType());
emitCopyAll(ArgCV, Src, argType);
Src = ArgCV;
}
// adjust offset for alignment
uint align = stackCallArgumentAlignment(Src);
offsetA = int_cast<unsigned>(llvm::alignTo(offsetA, align));
......@@ -8422,7 +8474,7 @@ void EmitPass::emitStackCall(llvm::CallInst* inst)
}
else
{
emitCopyAll(Dst, Src, Arg.getType());
emitCopyAll(Dst, Src, argType);
}
offsetA += Src->GetSize();
}
......@@ -8534,9 +8586,23 @@ void EmitPass::emitStackCall(llvm::CallInst* inst)
retOnStack = true;
}
}
unsigned char argSizeInGRF = (offsetA + SIZE_GRF - 1)/SIZE_GRF;
unsigned char retSizeInGRF = retOnStack ? 0 : (retSize + SIZE_GRF - 1) / SIZE_GRF;
m_encoder->StackCall(nullptr, F, (offsetA + SIZE_GRF - 1) / SIZE_GRF, retSizeInGRF);
m_encoder->Push();
if (F)
{
m_encoder->StackCall(nullptr, F, argSizeInGRF, retSizeInGRF);
m_encoder->Push();
}
else if (IGC_IS_FLAG_ENABLED(EnableFunctionPointer))
{
CVariable* funcAddr = GetSymbol(inst->getCalledValue());
CVariable* truncAddr = m_currShader->GetNewVariable(1, ISA_TYPE_UD, EALIGN_GRF, true);
m_encoder->Cast(truncAddr, funcAddr);
m_encoder->Push();
m_encoder->IndirectStackCall(nullptr, truncAddr, argSizeInGRF, retSizeInGRF);
m_encoder->Push();
}
// Emit the return value if used.
if (!inst->use_empty())
......@@ -8684,11 +8750,15 @@ void EmitPass::emitStackFuncEntry(Function *F, bool ptr64bits)
SmallVector<std::tuple<CVariable*, uint32_t, uint32_t, uint32_t>, 8> owordReads;
for (auto &Arg : F->args())
{
// Skip unused arguments if any.
if (Arg.use_empty())
// For indirect calls we can't skip args since we won't know at compile time if it will be used
if (!F->hasFnAttribute("AsFunctionPointer"))
{
++i;
continue;
// Skip unused arguments if any.
if (Arg.use_empty())
{
++i;
continue;
}
}
CVariable *Dst = m_currShader->getOrCreateArgumentSymbol(&Arg, true);
......
......@@ -171,18 +171,41 @@ Function *GenXCodeGenModule::cloneFunc(Function *F)
return ClonedFunc;
}
inline Function* getCallerFunc(Value* user)
{
Function* caller = nullptr;
if (CallInst* CI = dyn_cast<CallInst>(user))
{
caller = CI->getParent()->getParent();
}
else if (IGC_IS_FLAG_ENABLED(EnableFunctionPointer))
{
if (Instruction* II = dyn_cast<Instruction>(user))
{
caller = II->getParent()->getParent();
}
else if (ConstantExpr* CE = dyn_cast<ConstantExpr>(user))
{
return getCallerFunc(CE->user_back());
}
}
assert(caller && "Function Pointer use not supported");
return caller;
}
void GenXCodeGenModule::processFunction(Function &F)
{
// force stack-call for self-recursion
for (auto U : F.users())
{
CallInst *CI = dyn_cast<CallInst>(U);
assert(CI && "user is a not a call");
Function *Caller = CI->getParent()->getParent();
if (Caller == &F)
{
F.addFnAttr("visaStackCall");
break;
if (CallInst *CI = dyn_cast<CallInst>(U))
{
Function *Caller = CI->getParent()->getParent();
if (Caller == &F)
{
F.addFnAttr("visaStackCall");
break;
}
}
}
......@@ -190,9 +213,7 @@ void GenXCodeGenModule::processFunction(Function &F)
SetVector<std::pair<FunctionGroup*, Function*>> CallerFGs;
for (auto U : F.users())
{
CallInst *CI = dyn_cast<CallInst>(U);
assert(CI && "user is a not a call");
Function *Caller = CI->getParent()->getParent();
Function* Caller = getCallerFunc(U);
FunctionGroup *FG = FGA->getGroup(Caller);
Function *SubGrpH = FGA->useStackCall(&F) ? (&F) : FGA->getSubGroupMap(Caller);
if (FG == nullptr || SubGrpH == nullptr)
......@@ -249,9 +270,7 @@ void GenXCodeGenModule::processSCC(std::vector<llvm::CallGraphNode *> *SCCNodes)
Function *F = Node->getFunction();
for (auto U : F->users())
{
CallInst *CI = dyn_cast<CallInst>(U);
assert(CI && "user is a not a call");
Function *Caller = CI->getParent()->getParent();
Function *Caller = getCallerFunc(U);
FunctionGroup *FG = FGA->getGroup(Caller);
if (FG == nullptr)
continue;
......@@ -486,9 +505,7 @@ bool GenXFunctionGroupAnalysis::verify()
// deleted, that is fine.
for (auto U : F->users())
{
CallInst *CI = dyn_cast<CallInst>(U);
assert(CI && "user is a not a call");
Function *Caller = CI->getParent()->getParent();
Function *Caller = getCallerFunc(U);
FunctionGroup *CallerFG = getGroup(Caller);
// Caller's FG should be the same as FG. Otherwise, something is wrong.
if (CallerFG != (*GI))
......
......@@ -1131,6 +1131,11 @@ void CodeGenPatternMatch::visitCallInst(CallInst &I)
{
return;
}
else if (IGC_IS_FLAG_ENABLED(EnableFunctionPointer) && !I.getCalledFunction())
{
// Match indirect call
match = MatchSingleInstruction(I);
}
assert(match && "no match for this call");
}
......@@ -2506,6 +2511,15 @@ bool CodeGenPatternMatch::MatchSingleInstruction(llvm::Instruction& I)
{
MarkAsSource(I.getOperand(i));
}
if (CallInst* callinst = dyn_cast<CallInst>(&I))
{
// Mark the function pointer in indirect calls as a source
if (IGC_IS_FLAG_ENABLED(EnableFunctionPointer) && !callinst->getCalledFunction())
{