mirror of
				https://github.com/intel/intel-graphics-compiler.git
				synced 2025-10-30 08:18:26 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			468 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			468 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*========================== begin_copyright_notice ============================
 | |
| 
 | |
| Copyright (C) 2018-2021 Intel Corporation
 | |
| 
 | |
| SPDX-License-Identifier: MIT
 | |
| 
 | |
| ============================= end_copyright_notice ===========================*/
 | |
| 
 | |
| #include "Probe/Assertion.h"
 | |
| 
 | |
| #include "DebugInfo/ScalarVISAModule.h"
 | |
| #include "DebugInfo/DwarfDebug.hpp"
 | |
| #include "DebugInfo/VISADebugInfo.hpp"
 | |
| #include "Compiler/CISACodeGen/DebugInfo.hpp"
 | |
| #include "llvm/IR/IntrinsicInst.h"
 | |
| 
 | |
| #include "llvmWrapper/IR/IntrinsicInst.h"
 | |
| 
 | |
| using namespace llvm;
 | |
| using namespace IGC;
 | |
| using namespace IGC::IGCMD;
 | |
| using namespace std;
 | |
| // ElfReader related typedefs
 | |
| using namespace CLElfLib;
 | |
| 
 | |
| char DebugInfoPass::ID = 0;
 | |
| char CatchAllLineNumber::ID = 0;
 | |
| 
 | |
| // Register pass to igc-opt
 | |
| #define PASS_FLAG1 "igc-debug-finalize"
 | |
| #define PASS_DESCRIPTION1 "DebugInfo pass, llvmIR part(WAs)"
 | |
| #define PASS_CFG_ONLY1 false
 | |
| #define PASS_ANALYSIS1 false
 | |
| 
 | |
| IGC_INITIALIZE_PASS_BEGIN(DebugInfoPass, PASS_FLAG1, PASS_DESCRIPTION1, PASS_CFG_ONLY1, PASS_ANALYSIS1)
 | |
| IGC_INITIALIZE_PASS_END(DebugInfoPass, PASS_FLAG1, PASS_DESCRIPTION1, PASS_CFG_ONLY1, PASS_ANALYSIS1)
 | |
| 
 | |
| // Used for opt testing, could be removed if KernelShaderMap is moved to ctx.
 | |
| static CShaderProgram::KernelShaderMap KernelShaderMap;
 | |
| 
 | |
| // Default ctor used for igc-opt testing
 | |
| DebugInfoPass::DebugInfoPass() : ModulePass(ID), kernels(KernelShaderMap) {}
 | |
| 
 | |
| DebugInfoPass::DebugInfoPass(CShaderProgram::KernelShaderMap &k) : ModulePass(ID), kernels(k) {
 | |
|   initializeMetaDataUtilsWrapperPass(*PassRegistry::getPassRegistry());
 | |
| }
 | |
| 
 | |
| DebugInfoPass::~DebugInfoPass() {}
 | |
| 
 | |
| bool DebugInfoPass::doInitialization(llvm::Module &M) { return true; }
 | |
| 
 | |
| bool DebugInfoPass::doFinalization(llvm::Module &M) { return true; }
 | |
| 
 | |
| bool DebugInfoPass::runOnModule(llvm::Module &M) {
 | |
|   // This loop is just a workaround till we add support for DIArgList metadata.
 | |
|   // If we implement DIArgList support, it should be deleted.
 | |
|   for (auto &F : M) {
 | |
|     for (auto &BB : F) {
 | |
|       for (auto &I : BB) {
 | |
|         if (auto *dbgInst = dyn_cast<DbgVariableIntrinsic>(&I)) {
 | |
|           if (dbgInst->getNumVariableLocationOps() > 1) {
 | |
|             IGCLLVM::setKillLocation(dbgInst);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   // Early out
 | |
|   if (kernels.empty())
 | |
|     return false;
 | |
| 
 | |
|   std::vector<CShader *> units;
 | |
| 
 | |
|   auto isCandidate = [](CShaderProgram *shaderProgram, SIMDMode m,
 | |
|                         ShaderDispatchMode mode = ShaderDispatchMode::NOT_APPLICABLE) {
 | |
|     auto currShader = shaderProgram->GetShader(m, mode);
 | |
|     if (!currShader || !currShader->GetDebugInfoData().m_pDebugEmitter)
 | |
|       return (CShader *)nullptr;
 | |
| 
 | |
|     if (currShader->ProgramOutput()->m_programSize == 0)
 | |
|       return (CShader *)nullptr;
 | |
| 
 | |
|     return currShader;
 | |
|   };
 | |
| 
 | |
|   for (auto &k : kernels) {
 | |
|     auto shaderProgram = k.second;
 | |
|     auto simd8 = isCandidate(shaderProgram, SIMDMode::SIMD8);
 | |
|     auto quadSimd8Dynamic = isCandidate(shaderProgram, SIMDMode::SIMD32, ShaderDispatchMode::QUAD_SIMD8_DYNAMIC);
 | |
|     auto simd16 = isCandidate(shaderProgram, SIMDMode::SIMD16);
 | |
|     auto simd32 = isCandidate(shaderProgram, SIMDMode::SIMD32);
 | |
| 
 | |
|     if (simd8)
 | |
|       units.push_back(simd8);
 | |
|     if (simd16)
 | |
|       units.push_back(simd16);
 | |
|     if (simd32)
 | |
|       units.push_back(simd32);
 | |
|     if (quadSimd8Dynamic)
 | |
|       units.push_back(quadSimd8Dynamic);
 | |
|   }
 | |
| 
 | |
|   DwarfDISubprogramCache DISPCache;
 | |
| 
 | |
|   for (auto &currShader : units) {
 | |
|     // Look for the right CShaderProgram instance
 | |
|     m_currShader = currShader;
 | |
| 
 | |
|     MetaDataUtils *pMdUtils = m_currShader->GetMetaDataUtils();
 | |
|     if (!isEntryFunc(pMdUtils, m_currShader->entry))
 | |
|       continue;
 | |
| 
 | |
|     bool finalize = false;
 | |
|     unsigned int size = m_currShader->GetDebugInfoData().m_VISAModules.size();
 | |
|     m_pDebugEmitter = m_currShader->GetDebugInfoData().m_pDebugEmitter;
 | |
|     std::vector<std::pair<unsigned int, std::pair<llvm::Function *, IGC::VISAModule *>>> sortedVISAModules;
 | |
| 
 | |
|     // Sort modules in order of their placement in binary
 | |
|     IGC::VISADebugInfo VisaDbgInfo(m_currShader->ProgramOutput()->m_debugDataGenISA);
 | |
|     const auto &decodedDbg = VisaDbgInfo.getRawDecodedData();
 | |
|     auto getGenOff = [&decodedDbg](const std::vector<std::pair<unsigned int, unsigned int>> &data,
 | |
|                                    unsigned int VISAIndex) {
 | |
|       unsigned retval = 0;
 | |
|       for (auto &item : data) {
 | |
|         if (item.first == VISAIndex) {
 | |
|           retval = item.second;
 | |
|         }
 | |
|       }
 | |
|       return retval;
 | |
|     };
 | |
| 
 | |
|     auto getLastGenOff = [this, &decodedDbg, &getGenOff](IGC::VISAModule *v) {
 | |
|       unsigned int genOff = 0;
 | |
|       // Detect last instructions of kernel. This information is absent in
 | |
|       // dbg info. So detect is as first instruction of first subroutine - 1.
 | |
|       // reloc_index, first sub inst's VISA id
 | |
|       std::unordered_map<uint32_t, unsigned int> firstSubVISAIndex;
 | |
| 
 | |
|       for (auto &item : decodedDbg.compiledObjs) {
 | |
|         firstSubVISAIndex[item.relocOffset] = item.CISAIndexMap.back().first;
 | |
|         for (auto &sub : item.subs) {
 | |
|           auto subStartVISAIndex = sub.startVISAIndex;
 | |
|           if (firstSubVISAIndex[item.relocOffset] > subStartVISAIndex)
 | |
|             firstSubVISAIndex[item.relocOffset] = subStartVISAIndex - 1;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       for (auto &item : decodedDbg.compiledObjs) {
 | |
|         auto &name = item.kernelName;
 | |
|         auto firstInst = (v->GetInstInfoMap()->begin())->first;
 | |
|         auto funcName = firstInst->getParent()->getParent()->getName();
 | |
|         if (item.subs.size() == 0 && funcName.compare(name) == 0) {
 | |
|           genOff = item.CISAIndexMap.back().second;
 | |
|         } else {
 | |
|           if (funcName.compare(name) == 0) {
 | |
|             genOff = getGenOff(item.CISAIndexMap, firstSubVISAIndex[item.relocOffset]);
 | |
|             break;
 | |
|           }
 | |
|           for (auto &sub : item.subs) {
 | |
|             auto &subName = sub.name;
 | |
|             if (funcName.compare(subName) == 0) {
 | |
|               genOff = getGenOff(item.CISAIndexMap, sub.endVISAIndex);
 | |
|               break;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         if (genOff)
 | |
|           break;
 | |
|       }
 | |
| 
 | |
|       return genOff;
 | |
|     };
 | |
| 
 | |
|     auto setType = [&decodedDbg](VISAModule *v) {
 | |
|       auto firstInst = (v->GetInstInfoMap()->begin())->first;
 | |
|       auto funcName = firstInst->getParent()->getParent()->getName();
 | |
| 
 | |
|       for (auto &item : decodedDbg.compiledObjs) {
 | |
|         auto &name = item.kernelName;
 | |
|         if (funcName.compare(name) == 0) {
 | |
|           if (item.relocOffset == 0)
 | |
|             v->SetType(VISAModule::ObjectType::KERNEL);
 | |
|           else
 | |
|             v->SetType(VISAModule::ObjectType::STACKCALL_FUNC);
 | |
|           return;
 | |
|         }
 | |
|         for (auto &sub : item.subs) {
 | |
|           auto &subName = sub.name;
 | |
|           if (funcName.compare(subName) == 0) {
 | |
|             v->SetType(VISAModule::ObjectType::SUBROUTINE);
 | |
|             return;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     };
 | |
| 
 | |
|     for (auto &m : m_currShader->GetDebugInfoData().m_VISAModules) {
 | |
|       setType(m.second);
 | |
|       auto lastVISAId = getLastGenOff(m.second);
 | |
|       // getLastGenOffset returns zero iff debug info for given function
 | |
|       // was not found, skip the function in such case. This can happen,
 | |
|       // when the function was optimized away but the definition is still
 | |
|       // present inside the module.
 | |
|       if (lastVISAId == 0)
 | |
|         continue;
 | |
|       sortedVISAModules.push_back(std::make_pair(lastVISAId, std::make_pair(m.first, m.second)));
 | |
|     }
 | |
| 
 | |
|     std::sort(sortedVISAModules.begin(), sortedVISAModules.end(),
 | |
|               [](std::pair<unsigned int, std::pair<llvm::Function *, IGC::VISAModule *>> &p1,
 | |
|                  std::pair<unsigned int, std::pair<llvm::Function *, IGC::VISAModule *>> &p2) {
 | |
|                 return p1.first < p2.first;
 | |
|               });
 | |
| 
 | |
|     m_pDebugEmitter->SetDISPCache(&DISPCache);
 | |
|     for (auto &m : sortedVISAModules) {
 | |
|       m_pDebugEmitter->registerVISA(m.second.second);
 | |
|     }
 | |
| 
 | |
|     std::vector<llvm::Function *> functions;
 | |
|     std::for_each(sortedVISAModules.begin(), sortedVISAModules.end(),
 | |
|                   [&functions](auto &item) { functions.push_back(item.second.first); });
 | |
| 
 | |
|     for (auto &m : sortedVISAModules) {
 | |
|       m_pDebugEmitter->setCurrentVISA(m.second.second);
 | |
| 
 | |
|       if (--size == 0)
 | |
|         finalize = true;
 | |
| 
 | |
|       EmitDebugInfo(finalize, VisaDbgInfo);
 | |
|     }
 | |
| 
 | |
|     // set VISA dbg info to nullptr to indicate 1-step debug is enabled
 | |
|     if (currShader->ProgramOutput()->m_debugDataGenISA) {
 | |
|       IGC::aligned_free(currShader->ProgramOutput()->m_debugDataGenISA);
 | |
|     }
 | |
|     currShader->ProgramOutput()->m_debugDataGenISASize = 0;
 | |
|     currShader->ProgramOutput()->m_debugDataGenISA = nullptr;
 | |
| 
 | |
|     if (finalize) {
 | |
|       IDebugEmitter::Release(m_pDebugEmitter);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| static void debugDump(const CShader *Shader, llvm::StringRef Ext, ArrayRef<char> Blob) {
 | |
|   if (Blob.empty())
 | |
|     return;
 | |
| 
 | |
|   auto ExtStr = Ext.str();
 | |
|   IGC::Debug::DumpName DumpNameObj = IGC::Debug::GetDumpNameObj(Shader, ExtStr.c_str());
 | |
|   std::string DumpName = DumpNameObj.str();
 | |
|   if (IGC_IS_FLAG_ENABLED(DebugDumpNamePrefix)) {
 | |
|     auto hash = ShaderHash();
 | |
|     DumpNameObj = DumpNameObj.Hash(hash);
 | |
|     DumpName = DumpNameObj.AbsolutePath(IGC_GET_REGKEYSTRING(DebugDumpNamePrefix));
 | |
|   }
 | |
| 
 | |
|   if (DumpNameObj.allow()) {
 | |
|     FILE *const DumpFile = fopen(DumpName.c_str(), "wb+");
 | |
|     if (nullptr == DumpFile)
 | |
|       return;
 | |
| 
 | |
|     fwrite(Blob.data(), Blob.size(), 1, DumpFile);
 | |
|     fclose(DumpFile);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void DebugInfoPass::EmitDebugInfo(bool finalize, const IGC::VISADebugInfo &VisaDbgInfo) {
 | |
|   IGC_ASSERT(m_pDebugEmitter);
 | |
| 
 | |
|   std::vector<char> buffer = m_pDebugEmitter->Finalize(finalize, VisaDbgInfo);
 | |
| 
 | |
|   if (IGC_IS_FLAG_ENABLED(ShaderDumpEnable) || IGC_IS_FLAG_ENABLED(ElfDumpEnable))
 | |
|     debugDump(m_currShader, "elf", {buffer.data(), buffer.size()});
 | |
| 
 | |
|   const std::string &DbgErrors = m_pDebugEmitter->getErrors();
 | |
|   if (IGC_IS_FLAG_ENABLED(ShaderDumpEnable))
 | |
|     debugDump(m_currShader, "dbgerr", {DbgErrors.data(), DbgErrors.size()});
 | |
| 
 | |
|   void *dbgInfo = IGC::aligned_malloc(buffer.size(), sizeof(void *));
 | |
|   if (dbgInfo)
 | |
|     memcpy_s(dbgInfo, buffer.size(), buffer.data(), buffer.size());
 | |
| 
 | |
|   SProgramOutput *pOutput = m_currShader->ProgramOutput();
 | |
|   pOutput->m_debugData = dbgInfo;
 | |
|   pOutput->m_debugDataSize = dbgInfo ? buffer.size() : 0;
 | |
| }
 | |
| 
 | |
| // Detect instructions with an address class pattern. Then remove all opcodes of this pattern from
 | |
| // this instruction's last operand (metadata of DIExpression).
 | |
| // Pattern: !DIExpression(DW_OP_constu, 4, DW_OP_swap, DW_OP_xderef)
 | |
| void DebugInfoData::extractAddressClass(llvm::Function &F) {
 | |
|   DIBuilder di(*F.getParent());
 | |
| 
 | |
|   for (auto &bb : F) {
 | |
|     for (auto &pInst : bb) {
 | |
|       if (auto *DI = dyn_cast<DbgVariableIntrinsic>(&pInst)) {
 | |
|         const DIExpression *DIExpr = DI->getExpression();
 | |
|         llvm::SmallVector<uint64_t, 5> newElements;
 | |
|         for (auto I = DIExpr->expr_op_begin(), E = DIExpr->expr_op_end(); I != E; ++I) {
 | |
|           if (I->getOp() == dwarf::DW_OP_constu) {
 | |
|             auto patternI = I;
 | |
|             if (++patternI != E && patternI->getOp() == dwarf::DW_OP_swap && ++patternI != E &&
 | |
|                 patternI->getOp() == dwarf::DW_OP_xderef) {
 | |
|               I = patternI;
 | |
|               continue;
 | |
|             }
 | |
|           }
 | |
|           I->appendToVector(newElements);
 | |
|         }
 | |
| 
 | |
|         if (newElements.size() < DIExpr->getNumElements()) {
 | |
|           DIExpression *newDIExpr = di.createExpression(newElements);
 | |
|           DI->setExpression(newDIExpr);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void DebugInfoData::markVariableAsOutput(CShader *pShader, CVariable *pVariable) {
 | |
|   IGC_ASSERT(pShader && pVariable);
 | |
| 
 | |
|   // Mark variable with "Output", to extend it's living time will be extended to the end of the function.
 | |
|   pShader->GetEncoder().GetVISAKernel()->AddAttributeToVar(pVariable->visaGenVariable[0], "Output", 0, nullptr);
 | |
|   if (pShader->m_State.m_dispatchSize == SIMDMode::SIMD32 && pVariable->visaGenVariable[1]) {
 | |
|     pShader->GetEncoder().GetVISAKernel()->AddAttributeToVar(pVariable->visaGenVariable[1], "Output", 0, nullptr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void DebugInfoData::saveAndMarkPrivateMemoryVars(llvm::Function &F, CShader *pShader) {
 | |
|   IGC_ASSERT_MESSAGE(pShader, "CShader is missing.");
 | |
| 
 | |
|   IDebugEmitter *pDebugEmitter = pShader->GetDebugInfoData().m_pDebugEmitter;
 | |
| 
 | |
|   ScalarVisaModule *mVISAModule = nullptr;
 | |
|   if (pDebugEmitter) {
 | |
|     mVISAModule = (ScalarVisaModule *)pDebugEmitter->getCurrentVISA();
 | |
|     IGC_ASSERT_MESSAGE(mVISAModule, "Missing VISA module.");
 | |
|   }
 | |
| 
 | |
|   // Add FP to VISA module.
 | |
|   // Debug emitter will decide whether it needs to use it.
 | |
|   if (mVISAModule && pShader->hasFP()) {
 | |
|     mVISAModule->setFramePtr(pShader->GetFP());
 | |
|   }
 | |
| 
 | |
|   for (auto &bb : F) {
 | |
|     for (auto &pInst : bb) {
 | |
|       // If function has "perThreadOffset" variable - add it to VISA module
 | |
|       // and for mark it as output so that emitter will extend it's living time
 | |
|       // to the end of the function.
 | |
|       // Mark privateBase as output, because it will need it too.
 | |
|       if (MDNode *perThreadOffsetMD = pInst.getMetadata("perThreadOffset")) {
 | |
|         CVariable *perThreadOffset = pShader->GetSymbol(&pInst);
 | |
|         CVariable *privateBase = pShader->GetPrivateBase();
 | |
| 
 | |
|         IGC_ASSERT_MESSAGE(perThreadOffset, "Missing perThreadOffset.");
 | |
|         IGC_ASSERT_MESSAGE(privateBase, "Missing privateBase.");
 | |
| 
 | |
|         markVariableAsOutput(pShader, perThreadOffset);
 | |
|         markVariableAsOutput(pShader, privateBase);
 | |
| 
 | |
|         if (mVISAModule) {
 | |
|           mVISAModule->setPerThreadOffset(perThreadOffset);
 | |
|           mVISAModule->setPrivateBase(privateBase);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool IGC::DebugInfoData::hasDebugInfo(CShader *pShader) { return pShader->GetContext()->m_instrTypes.hasDebugInfo; }
 | |
| 
 | |
| void DebugInfoData::transferMappings(const llvm::Function &F) {
 | |
|   auto cacheMapping = [this](llvm::DenseMap<llvm::Value *, CVariable *> &Map) {
 | |
|     for (auto &mapping : Map) {
 | |
|       auto CVar = mapping.second;
 | |
|       if (CVar->visaGenVariable[0]) {
 | |
|         unsigned int lower16Channels =
 | |
|             (unsigned int)m_pShader->GetEncoder().GetVISAKernel()->getDeclarationID(CVar->visaGenVariable[0]);
 | |
|         unsigned int higher16Channels = 0;
 | |
|         if (numLanes(m_pShader->m_State.m_dispatchSize) == 32 && !CVar->IsUniform() && CVar->visaGenVariable[1])
 | |
|           higher16Channels = m_pShader->GetEncoder().GetVISAKernel()->getDeclarationID(CVar->visaGenVariable[1]);
 | |
|         CVarToVISADclId[CVar] = std::make_pair(lower16Channels, higher16Channels);
 | |
|       }
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   // Store llvm::Value->CVariable mappings from CShader.
 | |
|   // CShader clears these mappings before compiling a new function.
 | |
|   // Debug info is computed after all functions are compiled.
 | |
|   // This instance stores mappings per llvm::Function so debug
 | |
|   // info generation can emit variable locations correctly.
 | |
|   auto &SymbolMapping = m_pShader->GetSymbolMapping();
 | |
|   m_FunctionSymbols[&F] = SymbolMapping;
 | |
| 
 | |
|   // VISA builder gets destroyed at end of EmitVISAPass.
 | |
|   // Debug info pass is invoked later. We need a way to
 | |
|   // preserve mapping of CVariable -> VISA reg# so that
 | |
|   // we can emit location information in debug info. This
 | |
|   // code below iterates over all CVariable instances and
 | |
|   // retrieves and stored their VISA reg# in a map. This
 | |
|   // map is later queried by debug info pass.
 | |
|   cacheMapping(SymbolMapping);
 | |
| 
 | |
|   auto &GlobalSymbolMapping = m_pShader->GetGlobalMapping();
 | |
|   cacheMapping(GlobalSymbolMapping);
 | |
| 
 | |
|   if (m_pShader->hasFP()) {
 | |
|     auto FP = m_pShader->GetFP();
 | |
|     auto VISADclIdx = m_pShader->GetEncoder().GetVISAKernel()->getDeclarationID(FP->visaGenVariable[0]);
 | |
|     CVarToVISADclId[FP] = std::make_pair(VISADclIdx, 0);
 | |
|   }
 | |
| }
 | |
| 
 | |
| CVariable *DebugInfoData::getMapping(const llvm::Function &F, const llvm::Value *V) {
 | |
|   auto &Data = m_FunctionSymbols[&F];
 | |
|   auto Iter = Data.find(V);
 | |
|   if (Iter != Data.end())
 | |
|     return (*Iter).second;
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| // Register pass to igc-opt
 | |
| #define PASS_FLAG "igc-catch-all-linenum"
 | |
| #define PASS_DESCRIPTION "CatchAllLineNumber pass"
 | |
| #define PASS_CFG_ONLY false
 | |
| #define PASS_ANALYSIS false
 | |
| IGC_INITIALIZE_PASS_BEGIN(CatchAllLineNumber, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
 | |
| IGC_INITIALIZE_PASS_END(CatchAllLineNumber, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
 | |
| 
 | |
| CatchAllLineNumber::CatchAllLineNumber() : FunctionPass(ID) {
 | |
|   initializeMetaDataUtilsWrapperPass(*PassRegistry::getPassRegistry());
 | |
| }
 | |
| 
 | |
| CatchAllLineNumber::~CatchAllLineNumber() {}
 | |
| 
 | |
| bool CatchAllLineNumber::runOnFunction(llvm::Function &F) {
 | |
|   // Insert placeholder intrinsic instruction in each kernel/stack call function.
 | |
|   if (!F.getSubprogram() || F.isDeclaration() || IGC_IS_FLAG_ENABLED(NoCatchAllDebugLine))
 | |
|     return false;
 | |
| 
 | |
|   if (F.getCallingConv() != llvm::CallingConv::SPIR_KERNEL && !F.hasFnAttribute("visaStackCall"))
 | |
|     return false;
 | |
| 
 | |
|   llvm::IRBuilder<> Builder(F.getParent()->getContext());
 | |
|   DIBuilder di(*F.getParent());
 | |
|   Function *lineNumPlaceholder =
 | |
|       GenISAIntrinsic::getDeclaration(F.getParent(), GenISAIntrinsic::ID::GenISA_CatchAllDebugLine);
 | |
|   auto intCall = Builder.CreateCall(lineNumPlaceholder);
 | |
| 
 | |
|   auto line = F.getSubprogram()->getLine();
 | |
|   auto scope = F.getSubprogram();
 | |
| 
 | |
|   auto dbg = DILocation::get(F.getParent()->getContext(), line, 0, scope);
 | |
| 
 | |
|   intCall->setDebugLoc(dbg);
 | |
| 
 | |
|   intCall->insertBefore(&*F.getEntryBlock().getFirstInsertionPt());
 | |
| 
 | |
|   return true;
 | |
| }
 | 
