From fc116cf7475e1aa187c20033e1747cba2e679ee0 Mon Sep 17 00:00:00 2001
From: foreman
Date: Mon, 16 Mar 2015 14:00:52 -0400
Subject: [PATCH] P4 to Git Change 1131041 by yaxunl@yaxunl_stg_win50 on
2015/03/16 13:51:22
ECR #377625 - Refactor amdil function support. Move the code from linker.cpp to llvm32 to facilitate porting linker.cpp to llvm36.
Also made changes to respect user specified noinline attribute and added options to allow a list of functions to be inlined or not inlined to facilitate debugging.
Affected files ...
... //depot/stg/opencl/drivers/opencl/compiler/lib/backends/common/linker.cpp#119 edit
... //depot/stg/opencl/drivers/opencl/compiler/llvm32/include/llvm/AMDILFuncSupport.h#1 add
... //depot/stg/opencl/drivers/opencl/compiler/llvm32/lib/Linker/AMDILFuncSupport.cpp#1 add
[ROCm/clr commit: cc17b01ceeb43451d8209540ad6bc4815fab88d3]
---
.../compiler/lib/backends/common/linker.cpp | 361 ++----------------
1 file changed, 25 insertions(+), 336 deletions(-)
diff --git a/projects/clr/rocclr/compiler/lib/backends/common/linker.cpp b/projects/clr/rocclr/compiler/lib/backends/common/linker.cpp
index 98b154039b..bd28392817 100644
--- a/projects/clr/rocclr/compiler/lib/backends/common/linker.cpp
+++ b/projects/clr/rocclr/compiler/lib/backends/common/linker.cpp
@@ -27,7 +27,6 @@
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Analysis/AMDLocalArrayUsage.h"
-#include "llvm/Analysis/CodeMetrics.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/Analysis/Verifier.h"
@@ -65,6 +64,10 @@
#include "llvm/Transforms/Scalar.h"
#include "llvm/ValueSymbolTable.h"
+#if defined(LEGACY_COMPLIB)
+#include "llvm/AMDILFuncSupport.h"
+#endif
+
#ifdef _DEBUG
#include "llvm/Assembly/Writer.h"
#endif
@@ -202,308 +205,9 @@ static std::set *getAmdRtFunctions()
return result;
}
-// Remove NoInline attribute to functions in a module
-void
-RemoveNoInlineAttr(llvm::Module* M)
-{
- LLVMContext &Context = M->getContext();
- for (llvm::Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
- I->removeFnAttr(Attributes::get(Context, Attributes::NoInline));
- }
}
-bool
-IsKernel(llvm::Function* F)
-{
- return F->getName().startswith("__OpenCL_") &&
- F->getName().endswith("_kernel");
-}
-// Add NoInline attribute to functions in a module
-void
-AddNoInlineAttr(llvm::Module* M)
-{
- LLVMContext &Context = M->getContext();
- for (llvm::Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
- if (!I->isDeclaration() &&
- !I->isIntrinsic() &&
- !I->getName().startswith("__amdil") &&
- !I->getFnAttributes().hasAttribute(Attributes::AlwaysInline) &&
- !IsKernel(I)) {
- DEBUG_WITH_TYPE("noinline",
- dbgs() << "[Candidate] " << I->getName() << '\n');
- I->addFnAttr(Attributes::NoInline);
- }
- }
-}
-
-unsigned
-CountCallSites(llvm::Function* F, llvm::Module* M,
- std::map& counts) {
- std::map::iterator iter = counts.find(F);
- if (iter != counts.end())
- return iter->second;
-
- unsigned numCalled = 0;
- for (Function::use_iterator I = F->use_begin(), E = F->use_end(); I != E;
- ++I) {
- User *UI = *I;
- if (isa(UI) || isa(UI)) {
- ImmutableCallSite CS(cast(UI));
- Function* caller = const_cast(CS.getCaller());
- unsigned callerCount = CountCallSites(caller, M, counts);
- if (caller->getFnAttributes().hasAttribute(Attributes::NoInline) &&
- callerCount > 0)
- numCalled++;
- else
- numCalled += callerCount;
- }
- }
- if (numCalled == 0 && IsKernel(F))
- numCalled = 1;
-
- counts[F] = numCalled;
- return numCalled;
-}
-
-unsigned
-CalculateSize(llvm::Function* F, llvm::Module* M,
- std::map& sizes) {
- std::map::iterator iter = sizes.find(F);
- if (iter != sizes.end())
- return iter->second;
-
- CodeMetrics metrics;
- metrics.analyzeFunction(F);
- unsigned size = metrics.NumInsts;
- for (Function::iterator I = F->begin(), E = F->end(); I != E; ++I) {
- for (BasicBlock::iterator BI = I->begin(), BE = I->end(); BI != BE; ++BI) {
- if (CallInst* callInst = dyn_cast(BI)) {
- Function* called = callInst->getCalledFunction();
- if (called &&
- !called->getFnAttributes().hasAttribute(Attributes::NoInline))
- size += CalculateSize(called, M, sizes);
- }
- }
- }
- sizes[F] = size;
- return size;
-}
-
-// Identify functions with image arguments.
-// Callers may pass images with different resource ids to the callee.
-// Currently pointer manager cannot handle this.
-// ToDo: Should remove this after we find a way to handle image in function.
-bool
-IsImageFunc(Function* F) {
- for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E;
- ++I) {
- if (PointerType *PT = dyn_cast(I->getType())) {
- if (PT->getAddressSpace() != 1) {
- continue;
- }
- if (StructType *ST = dyn_cast(PT->getElementType())) {
- if (ST->getName().startswith("struct._image")) {
- DEBUG_WITH_TYPE("noinline", dbgs() << "[image function] " <<
- F->getName() << " inline\n");
- return true;
- }
- }
- }
- }
- return false;
-}
-
-bool
-MustInline(Function* F) {
- if (F->getFnAttributes().hasAttribute(Attributes::AlwaysInline))
- return true;
- return IsImageFunc(F);
-}
-
-bool
-CallerMustInline(Function* F) {
- return IsImageFunc(F);
-}
-
-bool
-CallsNoInlineFunc(Function* F, std::map& work) {
- DEBUG_WITH_TYPE("noinline", dbgs() << "[CallsNoInlineFunc:" << F->getName() << " ");
- std::map::iterator loc = work.find(F);
- if (loc != work.end()) {
- DEBUG_WITH_TYPE("noinline", dbgs() << loc->second << "(cached)]\n");
- return loc->second;
- }
- for (Function::iterator I = F->begin(), E = F->end(); I != E; ++I) {
- for (BasicBlock::iterator BI = I->begin(), BE = I->end(); BI != BE; ++BI) {
- if (CallInst* callInst = dyn_cast(BI)) {
- Function* called = callInst->getCalledFunction();
- if (called) {
- if (called->getFnAttributes().hasAttribute(Attributes::NoInline) ||
- CallerMustInline(called) ||
- CallsNoInlineFunc(called, work)) {
- work[F] = true;
- DEBUG_WITH_TYPE("noinline", dbgs() << "1(" << called->getName() <<")]\n");
- return true;
- }
- }
- }
- }
- }
- work[F] = false;
- DEBUG_WITH_TYPE("noinline", dbgs() << "0]\n");
- return false;
-}
-
-bool
-CalledByNoInlineFunc(Function* F, std::map& work) {
- DEBUG_WITH_TYPE("noinline", dbgs() << "[CalledByNoInlineFunc: " << F->getName() << " ");
- std::map::iterator loc = work.find(F);
- if (loc != work.end()) {
- DEBUG_WITH_TYPE("noinline", dbgs() << loc->second << "]\n");
- return loc->second;
- }
- for (Function::use_iterator I = F->use_begin(), E = F->use_end(); I != E;
- ++I) {
- User *UI = *I;
- if (isa(UI) || isa(UI)) {
- ImmutableCallSite CS(cast(UI));
- Function* caller = const_cast(CS.getCaller());
- if (caller->getFnAttributes().hasAttribute(Attributes::NoInline) ||
- CalledByNoInlineFunc(caller, work)) {
- work[F] = true;
- DEBUG_WITH_TYPE("noinline", dbgs() << "1(" << caller->getName() <<")]\n");
- return true;
- }
- }
- }
- work[F] = false;
- DEBUG_WITH_TYPE("noinline", dbgs() << "0]\n");
- return false;
-}
-
-bool
-CanBeNoInline(Function* F, std::map& callsNoInline,
- std::map& calledByNoInline, bool allowMultiLevelCall) {
- return !MustInline(F) && (allowMultiLevelCall ||
- (!CallsNoInlineFunc(F, callsNoInline) &&
- !CalledByNoInlineFunc(F, calledByNoInline)));
-}
-
-struct CostInfo {
- unsigned count;
- unsigned size;
- unsigned cost;
-};
-
-unsigned
-CalculateMaxKernelSize(llvm::Module* M) {
- std::map sizes;
- unsigned maxSize = 0;
- for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
- if (IsKernel(I)) {
- unsigned kernelSize = CalculateSize(I, M, sizes);
- DEBUG_WITH_TYPE("noinlines", dbgs() << "[Kernel size] " <<
- I->getName() << " : " << kernelSize << '\n');
- if (maxSize < kernelSize)
- maxSize = kernelSize;
- }
- }
- return maxSize;
-}
-
-void
-RefineNoInlineAttr(llvm::Module* M, int thresh, int sizeThresh,
- int kernelSizeThresh, bool allowMultiLevelCall)
-{
- if (thresh == 0 && sizeThresh == 0)
- return;
-
- std::set candidates;
- LLVMContext &Context = M->getContext();
-
- for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
- if (I->getFnAttributes().hasAttribute(Attributes::NoInline)) {
- candidates.insert(I);
- I->removeFnAttr(Attributes::get(Context, Attributes::NoInline));
- }
- }
-
- unsigned maxKernelSize = CalculateMaxKernelSize(M);
- if (maxKernelSize < unsigned(kernelSizeThresh))
- return;
-
- while (true) {
- std::map counts;
- std::map sizes;
- std::map costInfos;
- std::map callsNoInline;
- std::map calledByNoInline;
- for (std::set::iterator I = candidates.begin(),
- E = candidates.end(); I != E; ++I) {
- Function* F = *I;
- unsigned count = CountCallSites(F, M, counts);
- if (count > 0 && CanBeNoInline(F, callsNoInline, calledByNoInline,
- allowMultiLevelCall)) {
- unsigned size = CalculateSize(F, M, sizes);
- if (size > unsigned(sizeThresh)) {
- CostInfo& info = costInfos[F];
- info.count = count;
- info.size = size;
- info.cost = (count - 1) * size;
- DEBUG_WITH_TYPE("noinline", dbgs() << F->getName() <<
- " : " << count - 1 << " * " << size << " = " << (count-1) * size <<
- "\n");
- }
- }
- }
-
- int maxCost = -1;
- Function* select = NULL;
- for (std::map::iterator I = costInfos.begin(),
- E = costInfos.end(); I != E; ++I) {
- CostInfo& info = I->second;
- if (int(info.cost) > maxCost) {
- maxCost = int(info.cost);
- select = I->first;
- }
- }
- if (select == NULL || maxCost < thresh)
- break;
- CostInfo& info = costInfos[select];
- DEBUG_WITH_TYPE("noinlines", llvm::dbgs() << "select " << select->getName().str()
- << " cost = " << info.count << " x " << info.size << " = " <<
- info.cost << "\n");
-
- select->addFnAttr(Attributes::NoInline);
- candidates.erase(select);
- if (candidates.empty())
- break;
- }
-
- if (getenv("AMD_OCL_INLINE")) {
- for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
- if(I->hasName() && strstr(getenv("AMD_OCL_INLINE"),
- I->getName().str().c_str())) {
- I->removeFnAttr(Attributes::get(Context, Attributes::NoInline));
- printf("force inline %s\n", I->getName().data());
- }
- }
- }
-
- if (getenv("AMD_OCL_NOINLINE")) {
- for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
- if(I->hasName() && strstr(getenv("AMD_OCL_NOINLINE"),
- I->getName().str().c_str())) {
- I->addFnAttr(Attributes::NoInline);
- printf("force noinline %s\n", I->getName().data());
- }
- }
- }
-
-}
-
-} // unnamed namespace
} // namespace amd
// create a llvm function which simply returns the given mask
@@ -998,32 +702,6 @@ amdcl::OCLLinker::link(llvm::Module* input, std::vector &libs)
createASICIDFunctions(LLVMBinary());
- if (!isHSAILTarget(Elf()->target)) {
- // Add NoInline attribute to user functions
- llvm::StringRef family(aclGetFamily(Elf()->target));
- llvm::StringRef chip(aclGetChip(Elf()->target));
-
- // Add NoInline attribute to library functions so that they
- // can be considered for not inlining in codegen.
- if (IsGPUTarget &&
- (Options()->oVariables->OptMem2reg || Options()->oVariables->DebugCall) &&
- !Options()->oVariables->clInternalKernel &&
- !(family == "NI" || family == "Evergreen" || family == "Sumo" ||
- family == "TN")) {
- if (Options()->oVariables->AddUserNoInline)
- amd::AddNoInlineAttr(LLVMBinary());
- if (Options()->oVariables->AddLibNoInline)
- for (unsigned int i=0; i < LibMs.size(); i++)
- amd::AddNoInlineAttr(LibMs[i]);
- }
-
- // Disable outline macro for mem2reg=0 unless -fdebug-call
- // is on.
- if (!Options()->oVariables->OptMem2reg && !Options()->oVariables->DebugCall) {
- Options()->oVariables->UseMacroForCall = false;
- }
- }
-
// Link libraries to get every functions that are referenced.
std::string ErrorMsg;
if (resolveLink(LLVMBinary(), LibMs, RefMapBuilder.getModuleRefMaps(),
@@ -1047,17 +725,28 @@ amdcl::OCLLinker::link(llvm::Module* input, std::vector &libs)
appendLogToCL(CL(), tmp_ss.str());
}
- if (!isHSAILTarget(Elf()->target)) {
- // Refine NoInline attribute of functions
- if (IsGPUTarget && !Options()->oVariables->clInternalKernel) {
- amd::RefineNoInlineAttr(LLVMBinary(),
- Options()->oVariables->InlineCostThreshold,
- Options()->oVariables->InlineSizeThreshold,
- Options()->oVariables->InlineKernelSizeThreshold,
- Options()->oVariables->AllowMultiLevelCall &&
- Options()->oVariables->UseMacroForCall );
- }
+#if defined(LEGACY_COMPLIB)
+ // Disable outline macro for mem2reg=0 unless -fdebug-call
+ // is on.
+ if (!Options()->oVariables->OptMem2reg && !Options()->oVariables->DebugCall)
+ Options()->oVariables->UseMacroForCall = false;
+
+ if (isAMDILTarget(Elf()->target) &&
+ getFamilyEnum(&Elf()->target) >= FAMILY_SI &&
+ !Options()->oVariables->clInternalKernel &&
+ (Options()->oVariables->OptMem2reg ||
+ Options()->oVariables->DebugCall)) {
+ auto OV = Options()->oVariables;
+ AMDILFuncSupport::PostLinkProcForFuncSupport(
+ OV->AddUserNoInline,
+ OV->AddLibNoInline,
+ OV->InlineCostThreshold,
+ OV->InlineSizeThreshold,
+ OV->InlineKernelSizeThreshold,
+ OV->AllowMultiLevelCall && OV->UseMacroForCall,
+ LLVMBinary(), LibMs);
}
+#endif
if (Options()->isDumpFlagSet(amd::option::DUMP_BC_LINKED)) {
std::string MyErrorInfo;