1 /*========================== begin_copyright_notice ============================
2
3 Copyright (C) 2018-2021 Intel Corporation
4
5 SPDX-License-Identifier: MIT
6
7 ============================= end_copyright_notice ===========================*/
8
9 #include "GenCodeGenModule.h"
10 #include "Probe/Assertion.h"
11 #include "CLElfLib/ElfReader.h"
12
13 #include "DebugInfo/ScalarVISAModule.h"
14 #include "DebugInfo/DwarfDebug.hpp"
15 #include "Compiler/CISACodeGen/DebugInfo.hpp"
16
17 using namespace llvm;
18 using namespace IGC;
19 using namespace IGC::IGCMD;
20 using namespace std;
21 // ElfReader related typedefs
22 using namespace CLElfLib;
23
24 char DebugInfoPass::ID = 0;
25 char CatchAllLineNumber::ID = 0;
26
DebugInfoPass(CShaderProgram::KernelShaderMap & k)27 DebugInfoPass::DebugInfoPass(CShaderProgram::KernelShaderMap& k) :
28 ModulePass(ID),
29 kernels(k)
30 {
31 initializeMetaDataUtilsWrapperPass(*PassRegistry::getPassRegistry());
32 }
33
~DebugInfoPass()34 DebugInfoPass::~DebugInfoPass()
35 {
36 }
37
doInitialization(llvm::Module & M)38 bool DebugInfoPass::doInitialization(llvm::Module& M)
39 {
40 return true;
41 }
42
doFinalization(llvm::Module & M)43 bool DebugInfoPass::doFinalization(llvm::Module& M)
44 {
45 return true;
46 }
47
runOnModule(llvm::Module & M)48 bool DebugInfoPass::runOnModule(llvm::Module& M)
49 {
50 std::vector<CShader*> units;
51
52 auto isCandidate = [](CShaderProgram* shaderProgram, SIMDMode m, ShaderDispatchMode mode = ShaderDispatchMode::NOT_APPLICABLE)
53 {
54 auto currShader = shaderProgram->GetShader(m, mode);
55 if (!currShader || !currShader->GetDebugInfoData().m_pDebugEmitter)
56 return (CShader*)nullptr;
57
58 if (currShader->ProgramOutput()->m_programSize == 0)
59 return (CShader*)nullptr;
60
61 return currShader;
62 };
63
64 for (auto& k : kernels)
65 {
66 auto shaderProgram = k.second;
67 auto simd8 = isCandidate(shaderProgram, SIMDMode::SIMD8);
68 auto simd16 = isCandidate(shaderProgram, SIMDMode::SIMD16);
69 auto simd32 = isCandidate(shaderProgram, SIMDMode::SIMD32);
70
71 if (simd8) units.push_back(simd8);
72 if (simd16) units.push_back(simd16);
73 if (simd32) units.push_back(simd32);
74 }
75
76 DwarfDISubprogramCache DISPCache;
77
78 for (auto& currShader : units)
79 {
80 // Look for the right CShaderProgram instance
81 m_currShader = currShader;
82
83 MetaDataUtils* pMdUtils = m_currShader->GetMetaDataUtils();
84 if (!isEntryFunc(pMdUtils, m_currShader->entry))
85 continue;
86
87 bool finalize = false;
88 unsigned int size = m_currShader->GetDebugInfoData().m_VISAModules.size();
89 m_pDebugEmitter = m_currShader->GetDebugInfoData().m_pDebugEmitter;
90 std::vector<std::pair<unsigned int, std::pair<llvm::Function*, IGC::VISAModule*>>> sortedVISAModules;
91
92 // Sort modules in order of their placement in binary
93 DbgDecoder decodedDbg(m_currShader->ProgramOutput()->m_debugDataGenISA);
94 auto getGenOff = [&decodedDbg](std::vector<std::pair<unsigned int, unsigned int>>& data, unsigned int VISAIndex)
95 {
96 unsigned retval = 0;
97 for (auto& item : data)
98 {
99 if (item.first == VISAIndex)
100 {
101 retval = item.second;
102 }
103 }
104 return retval;
105 };
106
107 auto getLastGenOff = [this, &decodedDbg, &getGenOff](IGC::VISAModule* v)
108 {
109 unsigned int genOff = 0;
110 // Detect last instructions of kernel. This information is absent in
111 // dbg info. So detect is as first instruction of first subroutine - 1.
112 // reloc_index, first sub inst's VISA id
113 std::unordered_map<uint32_t, unsigned int> firstSubVISAIndex;
114
115 for (auto& item : decodedDbg.compiledObjs)
116 {
117 firstSubVISAIndex[item.relocOffset] = item.CISAIndexMap.back().first;
118 for (auto& sub : item.subs)
119 {
120 auto subStartVISAIndex = sub.startVISAIndex;
121 if (firstSubVISAIndex[item.relocOffset] > subStartVISAIndex)
122 firstSubVISAIndex[item.relocOffset] = subStartVISAIndex - 1;
123 }
124 }
125
126 for (auto& item : decodedDbg.compiledObjs)
127 {
128 auto& name = item.kernelName;
129 auto firstInst = (v->GetInstInfoMap()->begin())->first;
130 auto funcName = firstInst->getParent()->getParent()->getName();
131 if (item.subs.size() == 0 && funcName.compare(name) == 0)
132 {
133 genOff = item.CISAIndexMap.back().second;
134 }
135 else
136 {
137 if (funcName.compare(name) == 0)
138 {
139 genOff = getGenOff(item.CISAIndexMap, firstSubVISAIndex[item.relocOffset]);
140 break;
141 }
142 for (auto& sub : item.subs)
143 {
144 auto& subName = sub.name;
145 if (funcName.compare(subName) == 0)
146 {
147 genOff = getGenOff(item.CISAIndexMap, sub.endVISAIndex);
148 break;
149 }
150 }
151 }
152
153 if (genOff)
154 break;
155 }
156
157 return genOff;
158 };
159
160 auto setType = [&decodedDbg](VISAModule* v)
161 {
162 auto firstInst = (v->GetInstInfoMap()->begin())->first;
163 auto funcName = firstInst->getParent()->getParent()->getName();
164
165 for (auto& item : decodedDbg.compiledObjs)
166 {
167 auto& name = item.kernelName;
168 if (funcName.compare(name) == 0)
169 {
170 if (item.relocOffset == 0)
171 v->SetType(VISAModule::ObjectType::KERNEL);
172 else
173 v->SetType(VISAModule::ObjectType::STACKCALL_FUNC);
174 return;
175 }
176 for (auto& sub : item.subs)
177 {
178 auto& subName = sub.name;
179 if (funcName.compare(subName) == 0)
180 {
181 v->SetType(VISAModule::ObjectType::SUBROUTINE);
182 return;
183 }
184 }
185 }
186 };
187
188 for (auto& m : m_currShader->GetDebugInfoData().m_VISAModules)
189 {
190 setType(m.second);
191 auto lastVISAId = getLastGenOff(m.second);
192 // getLastGenOffset returns zero iff debug info for given function
193 // was not found, skip the function in such case. This can happen,
194 // when the function was optimized away but the definition is still
195 // present inside the module.
196 if (lastVISAId == 0)
197 continue;
198 sortedVISAModules.push_back(std::make_pair(lastVISAId, std::make_pair(m.first, m.second)));
199 }
200
201 std::sort(sortedVISAModules.begin(), sortedVISAModules.end(),
202 [](std::pair<unsigned int, std::pair<llvm::Function*, IGC::VISAModule*>>& p1,
203 std::pair<unsigned int, std::pair<llvm::Function*, IGC::VISAModule*>>& p2)
204 {
205 return p1.first < p2.first;
206 });
207
208 m_pDebugEmitter->SetDISPCache(&DISPCache);
209 for (auto& m : sortedVISAModules)
210 {
211 m_pDebugEmitter->registerVISA(m.second.second);
212 }
213
214 std::vector<llvm::Function*> functions;
215 std::for_each(sortedVISAModules.begin(), sortedVISAModules.end(),
216 [&functions](auto& item) { functions.push_back(item.second.first); });
217
218 for (auto& m : sortedVISAModules)
219 {
220 m_pDebugEmitter->setCurrentVISA(m.second.second);
221
222 if (--size == 0)
223 finalize = true;
224
225 EmitDebugInfo(finalize, &decodedDbg);
226 }
227
228 // set VISA dbg info to nullptr to indicate 1-step debug is enabled
229 currShader->ProgramOutput()->m_debugDataGenISASize = 0;
230 currShader->ProgramOutput()->m_debugDataGenISA = nullptr;
231
232 if (finalize)
233 {
234 IDebugEmitter::Release(m_pDebugEmitter);
235 }
236 }
237
238 return false;
239 }
240
debugDump(const CShader * Shader,llvm::StringRef Ext,ArrayRef<char> Blob)241 static void debugDump(const CShader* Shader, llvm::StringRef Ext,
242 ArrayRef<char> Blob)
243 {
244 if (Blob.empty())
245 return;
246
247 auto ExtStr = Ext.str();
248 std::string DumpName = IGC::Debug::GetDumpName(Shader, ExtStr.c_str());
249 FILE* const DumpFile = fopen(DumpName.c_str(), "wb+");
250 if (nullptr == DumpFile)
251 return;
252
253 fwrite(Blob.data(), Blob.size(), 1, DumpFile);
254 fclose(DumpFile);
255 }
256
EmitDebugInfo(bool finalize,DbgDecoder * decodedDbg)257 void DebugInfoPass::EmitDebugInfo(bool finalize, DbgDecoder* decodedDbg)
258 {
259 IGC_ASSERT(m_pDebugEmitter);
260
261 std::vector<char> buffer = m_pDebugEmitter->Finalize(finalize, decodedDbg);
262
263 if (IGC_IS_FLAG_ENABLED(ShaderDumpEnable) || IGC_IS_FLAG_ENABLED(ElfDumpEnable))
264 debugDump(m_currShader, "elf", { buffer.data(), buffer.size() });
265
266 const std::string& DbgErrors = m_pDebugEmitter->getErrors();
267 if (IGC_IS_FLAG_ENABLED(ShaderDumpEnable))
268 debugDump(m_currShader, "dbgerr", { DbgErrors.data(), DbgErrors.size() });
269
270 void* dbgInfo = IGC::aligned_malloc(buffer.size(), sizeof(void*));
271 if (dbgInfo)
272 memcpy_s(dbgInfo, buffer.size(), buffer.data(), buffer.size());
273
274 SProgramOutput* pOutput = m_currShader->ProgramOutput();
275 pOutput->m_debugData = dbgInfo;
276 pOutput->m_debugDataSize = dbgInfo ? buffer.size() : 0;
277 }
278
279 // Mark privateBase aka ImplicitArg::PRIVATE_BASE as Output for debugging
markOutputPrivateBase(CShader * pShader,IDebugEmitter * pDebugEmitter)280 void DebugInfoData::markOutputPrivateBase(CShader* pShader, IDebugEmitter* pDebugEmitter)
281 {
282 IGC_ASSERT_MESSAGE(IGC_IS_FLAG_ENABLED(UseOffsetInLocation), "UseOffsetInLocation not enabled");
283
284 if (pShader->GetContext()->getModuleMetaData()->compOpt.OptDisable)
285 {
286 CVariable* pVar = pShader->GetPrivateBase();
287 if (pVar)
288 {
289 // cache privateBase as it may be destroyed if subroutine
290 // is emitted.
291 pDebugEmitter->getCurrentVISA()->setPrivateBase(pVar);
292 pShader->GetEncoder().GetVISAKernel()->AddAttributeToVar(pVar->visaGenVariable[0], "Output", 0, nullptr);
293 if (pShader->m_dispatchSize == SIMDMode::SIMD32 && pVar->visaGenVariable[1])
294 {
295 IGC_ASSERT_MESSAGE(false, "Private base expected to be a scalar!"); // Should never happen
296 pShader->GetEncoder().GetVISAKernel()->AddAttributeToVar(pVar->visaGenVariable[1], "Output", 0, nullptr);
297 }
298 }
299 }
300 }
301
markOutputVar(CShader * pShader,IDebugEmitter * pDebugEmitter,llvm::Instruction * pInst,const char * pMetaDataName)302 void DebugInfoData::markOutputVar(CShader* pShader, IDebugEmitter* pDebugEmitter, llvm::Instruction* pInst, const char* pMetaDataName)
303 {
304 Value* pValue = dyn_cast<Value>(pInst);
305
306 IGC_ASSERT_MESSAGE(IGC_IS_FLAG_ENABLED(UseOffsetInLocation), "UseOffsetInLocation not enabled");
307 IGC_ASSERT_MESSAGE(pInst, "Missing instruction");
308
309 CVariable* pVar = pShader->GetSymbol(pValue);
310 if (pVar->GetVarType() == EVARTYPE_GENERAL)
311 {
312 // If UseOffsetInLocation is enabled, we want to attach "Output" attribute to:
313 // 1. Per thread offset only, and/or
314 // 2. Compute thread and global identification variables.
315 // So that finalizer can extend their liveness to end of the program.
316 // This will help debugger examine their values anywhere in the code till they
317 // are in scope. However, emit "Output" attribute when -g and -cl-opt-disable
318 // are both passed -g by itself shouldnt alter generated code.
319 if (pShader->GetContext()->getModuleMetaData()->compOpt.OptDisable)
320 {
321 // If "Output" attribute is emitted for perThreadOffset variable(s)
322 // then debug info emission is preserved for this:
323 // privateBaseMem + perThreadOffset + (simdSize*offImm + simd_lane*sizeof(elem))
324 if (Instruction* pPTOorImplicitGIDInst = dyn_cast<Instruction>(pValue))
325 {
326 MDNode* pPTOorImplicitGIDInstMD = pPTOorImplicitGIDInst->getMetadata(pMetaDataName); // "perThreadOffset" or "implicitGlobalID"
327 if (pPTOorImplicitGIDInstMD)
328 {
329 pShader->GetEncoder().GetVISAKernel()->AddAttributeToVar(pVar->visaGenVariable[0], "Output", 0, nullptr);
330 if (pShader->m_dispatchSize == SIMDMode::SIMD32 && pVar->visaGenVariable[1])
331 {
332 pShader->GetEncoder().GetVISAKernel()->AddAttributeToVar(pVar->visaGenVariable[1], "Output", 0, nullptr);
333 }
334 }
335 }
336 }
337 }
338 else
339 {
340 // Unexpected return empty location!
341 IGC_ASSERT_MESSAGE(false, "No debug info value!");
342 }
343 }
344
markOutput(llvm::Function & F,CShader * pShader,IDebugEmitter * pDebugEmitter)345 void DebugInfoData::markOutput(llvm::Function& F, CShader* pShader, IDebugEmitter* pDebugEmitter)
346 {
347 IGC_ASSERT_MESSAGE(pDebugEmitter, "Missing debug emitter");
348 VISAModule* visaModule = pDebugEmitter->getCurrentVISA();
349 IGC_ASSERT_MESSAGE(visaModule, "Missing visa module");
350
351 for (auto& bb : F)
352 {
353 for (auto& pInst : bb)
354 {
355 if (MDNode* perThreadOffsetMD = pInst.getMetadata("perThreadOffset"))
356 {
357 // Per Thread Offset non-debug instruction must have 'Output' attribute
358 // added in the function to be called.
359 markOutputVar(pShader, pDebugEmitter, &pInst, "perThreadOffset");
360 if (F.getCallingConv() == CallingConv::SPIR_KERNEL)
361 {
362 markOutputPrivateBase(pShader, pDebugEmitter); // Mark privateBase aka ImplicitArg::PRIVATE_BASE as Output for debugging
363 }
364 else
365 {
366 // TODO: Apply privateBase of kernel to SPIR_FUNC if its a subroutine
367 }
368 ScalarVisaModule* scVISAModule = (ScalarVisaModule*)visaModule;
369 IGC_ASSERT_MESSAGE(scVISAModule->getPerThreadOffset()==nullptr, "setPerThreadOffset was set earlier");
370 scVISAModule->setPerThreadOffset(&pInst);
371 if (((OpenCLProgramContext*)(pShader->GetContext()))->m_InternalOptions.KernelDebugEnable == false)
372 {
373 return;
374 }
375 }
376 }
377 }
378
379 if (((OpenCLProgramContext*)(pShader->GetContext()))->m_InternalOptions.KernelDebugEnable)
380 {
381 // Compute thread and group identification instructions will be marked here
382 // regardless of stack calls detection in this shader, so not only when per thread offset
383 // as well as a private base have been marked as Output earlier in this function.
384 // When stack calls are in use then only these group ID instructions are marked as Output.
385 for (auto& bb : F)
386 {
387 for (auto& pInst : bb)
388 {
389 if (MDNode* implicitGlobalIDMD = pInst.getMetadata("implicitGlobalID"))
390 {
391 // Compute thread and group identification instructions must have
392 // 'Output' attribute added in the function to be called.
393 markOutputVar(pShader, pDebugEmitter, &pInst, "implicitGlobalID");
394 }
395 }
396 }
397 }
398 }
399
markOutput(llvm::Function & F,CShader * m_currShader)400 void DebugInfoData::markOutput(llvm::Function& F, CShader* m_currShader)
401 {
402 for (auto& bb : F)
403 {
404 for (auto& pInst : bb)
405 {
406 markOutputVars(&pInst);
407 }
408 }
409 }
410
markOutputVars(const llvm::Instruction * pInst)411 void DebugInfoData::markOutputVars(const llvm::Instruction* pInst)
412 {
413 const Value* pVal = nullptr;
414 if (const DbgDeclareInst * pDbgAddrInst = dyn_cast<DbgDeclareInst>(pInst))
415 {
416 pVal = pDbgAddrInst->getAddress();
417 }
418 else if (const DbgValueInst * pDbgValInst = dyn_cast<DbgValueInst>(pInst))
419 {
420 pVal = pDbgValInst->getValue();
421 }
422 else
423 {
424 return;
425 }
426
427 if (!pVal || isa<UndefValue>(pVal))
428 {
429 // No debug info value, return empty location!
430 return;
431 }
432
433 if (dyn_cast<Constant>(pVal))
434 {
435 if (!isa<GlobalVariable>(pVal) && !isa<ConstantExpr>(pVal))
436 {
437 return;
438 }
439 }
440
441 Value* pValue = const_cast<Value*>(pVal);
442 if (isa<GlobalVariable>(pValue))
443 {
444 return;
445 }
446
447 if (!m_pShader->IsValueUsed(pValue)) {
448 return;
449 }
450
451 CVariable* pVar = m_pShader->GetSymbol(pValue);
452 if (pVar->GetVarType() == EVARTYPE_GENERAL)
453 {
454 // We want to attach "Output" attribute to all variables:
455 // - if UseOffsetInLocation is disabled, or
456 // - if UseOffsetInLocation is enabled but there is a stack call in use,
457 // so that finalizer can extend their liveness to end of
458 // the program. This will help debugger examine their
459 // values anywhere in the code till they are in scope.
460 if (m_outputVals.find(pVar) == m_outputVals.end())
461 {
462 if (m_pShader->GetContext()->getModuleMetaData()->compOpt.OptDisable)
463 {
464 // Emit "Output" attribute only when -g and -cl-opt-disable are both passed
465 // -g by itself shouldnt alter generated code
466 m_pShader->GetEncoder().GetVISAKernel()->AddAttributeToVar(pVar->visaGenVariable[0], "Output", 0, nullptr);
467 if (m_pShader->m_dispatchSize == SIMDMode::SIMD32 && pVar->visaGenVariable[1])
468 {
469 m_pShader->GetEncoder().GetVISAKernel()->AddAttributeToVar(pVar->visaGenVariable[1], "Output", 0, nullptr);
470 }
471 (void)m_outputVals.insert(pVar);
472 }
473 }
474 }
475 }
476
hasDebugInfo(CShader * pShader)477 bool IGC::DebugInfoData::hasDebugInfo(CShader* pShader)
478 {
479 return pShader->GetContext()->m_instrTypes.hasDebugInfo;
480 }
481
transferMappings(const llvm::Function & F)482 void DebugInfoData::transferMappings(const llvm::Function& F)
483 {
484 auto cacheMapping = [this](llvm::DenseMap<llvm::Value*, CVariable*>& Map)
485 {
486 for (auto& mapping : Map)
487 {
488 auto CVar = mapping.second;
489 if (CVar->visaGenVariable[0])
490 {
491 unsigned int lower16Channels = (unsigned int)m_pShader->GetEncoder().GetVISAKernel()->getDeclarationID(CVar->visaGenVariable[0]);
492 unsigned int higher16Channels = 0;
493 if (numLanes(m_pShader->m_dispatchSize) == 32 && !CVar->IsUniform() && CVar->visaGenVariable[1])
494 higher16Channels = m_pShader->GetEncoder().GetVISAKernel()->getDeclarationID(CVar->visaGenVariable[1]);
495 CVarToVISADclId[CVar] = std::make_pair(lower16Channels, higher16Channels);
496 }
497 }
498 };
499
500 // Store llvm::Value->CVariable mappings from CShader.
501 // CShader clears these mappings before compiling a new function.
502 // Debug info is computed after all functions are compiled.
503 // This instance stores mappings per llvm::Function so debug
504 // info generation can emit variable locations correctly.
505 auto& SymbolMapping = m_pShader->GetSymbolMapping();
506 m_FunctionSymbols[&F] = SymbolMapping;
507
508 // VISA builder gets destroyed at end of EmitVISAPass.
509 // Debug info pass is invoked later. We need a way to
510 // preserve mapping of CVariable -> VISA reg# so that
511 // we can emit location information in debug info. This
512 // code below iterates over all CVariable instances and
513 // retrieves and stored their VISA reg# in a map. This
514 // map is later queried by debug info pass.
515 cacheMapping(SymbolMapping);
516
517 auto& GlobalSymbolMapping = m_pShader->GetGlobalMapping();
518 cacheMapping(GlobalSymbolMapping);
519
520 if (m_pShader->hasFP())
521 {
522 auto FP = m_pShader->GetFP();
523 auto VISADclIdx = m_pShader->GetEncoder().GetVISAKernel()->getDeclarationID(FP->visaGenVariable[0]);
524 CVarToVISADclId[FP] = std::make_pair(VISADclIdx, 0);
525 }
526 }
527
getMapping(const llvm::Function & F,const llvm::Value * V)528 CVariable* DebugInfoData::getMapping(const llvm::Function& F, const llvm::Value* V)
529 {
530 auto& Data = m_FunctionSymbols[&F];
531 auto Iter = Data.find(V);
532 if (Iter != Data.end())
533 return (*Iter).second;
534 return nullptr;
535 }
536
537 // Register pass to igc-opt
538 #define PASS_FLAG "igc-catch-all-linenum"
539 #define PASS_DESCRIPTION "CatchAllLineNumber pass"
540 #define PASS_CFG_ONLY false
541 #define PASS_ANALYSIS false
IGC_INITIALIZE_PASS_BEGIN(CatchAllLineNumber,PASS_FLAG,PASS_DESCRIPTION,PASS_CFG_ONLY,PASS_ANALYSIS)542 IGC_INITIALIZE_PASS_BEGIN(CatchAllLineNumber, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
543 IGC_INITIALIZE_PASS_END(CatchAllLineNumber, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
544
545 CatchAllLineNumber::CatchAllLineNumber() :
546 FunctionPass(ID)
547 {
548 initializeMetaDataUtilsWrapperPass(*PassRegistry::getPassRegistry());
549 }
550
~CatchAllLineNumber()551 CatchAllLineNumber::~CatchAllLineNumber()
552 {
553 }
554
runOnFunction(llvm::Function & F)555 bool CatchAllLineNumber::runOnFunction(llvm::Function& F)
556 {
557 // Insert placeholder intrinsic instruction in each kernel.
558 if (!F.getSubprogram() || F.isDeclaration())
559 return false;
560
561 if (F.getCallingConv() != llvm::CallingConv::SPIR_KERNEL)
562 return false;
563
564 llvm::IRBuilder<> Builder(F.getParent()->getContext());
565 DIBuilder di(*F.getParent());
566 Function* lineNumPlaceholder = GenISAIntrinsic::getDeclaration(F.getParent(), GenISAIntrinsic::ID::GenISA_CatchAllDebugLine);
567 auto intCall = Builder.CreateCall(lineNumPlaceholder);
568
569 auto line = F.getSubprogram()->getLine();
570 auto scope = F.getSubprogram();
571
572 auto dbg = DILocation::get(F.getParent()->getContext(), line, 0, scope);
573
574 intCall->setDebugLoc(dbg);
575
576 intCall->insertBefore(&*F.getEntryBlock().getFirstInsertionPt());
577
578 return true;
579 }
580