1 | //===- DebugTranslation.cpp - MLIR to LLVM Debug conversion ---------------===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "DebugTranslation.h" |
10 | #include "mlir/Dialect/LLVMIR/LLVMDialect.h" |
11 | #include "llvm/ADT/TypeSwitch.h" |
12 | #include "llvm/IR/Metadata.h" |
13 | #include "llvm/IR/Module.h" |
14 | #include "llvm/Support/FileSystem.h" |
15 | #include "llvm/Support/Path.h" |
16 | |
17 | using namespace mlir; |
18 | using namespace mlir::LLVM; |
19 | using namespace mlir::LLVM::detail; |
20 | |
21 | /// A utility walker that interrupts if the operation has valid debug |
22 | /// information. |
23 | static WalkResult interruptIfValidLocation(Operation *op) { |
24 | return isa<UnknownLoc>(Val: op->getLoc()) ? WalkResult::advance() |
25 | : WalkResult::interrupt(); |
26 | } |
27 | |
28 | DebugTranslation::DebugTranslation(Operation *module, llvm::Module &llvmModule) |
29 | : debugEmissionIsEnabled(false), llvmModule(llvmModule), |
30 | llvmCtx(llvmModule.getContext()) { |
31 | // If the module has no location information, there is nothing to do. |
32 | if (!module->walk(callback&: interruptIfValidLocation).wasInterrupted()) |
33 | return; |
34 | debugEmissionIsEnabled = true; |
35 | } |
36 | |
37 | static constexpr StringRef kDebugVersionKey = "Debug Info Version"; |
38 | static constexpr StringRef kCodeViewKey = "CodeView"; |
39 | |
40 | void DebugTranslation::addModuleFlagsIfNotPresent() { |
41 | // TODO: The version information should be encoded on the LLVM module itself, |
42 | // not implicitly set here. |
43 | |
44 | // Mark this module as having debug information. |
45 | if (!llvmModule.getModuleFlag(Key: kDebugVersionKey)) |
46 | llvmModule.addModuleFlag(Behavior: llvm::Module::Warning, Key: kDebugVersionKey, |
47 | Val: llvm::DEBUG_METADATA_VERSION); |
48 | |
49 | const llvm::Triple &targetTriple = llvmModule.getTargetTriple(); |
50 | if (targetTriple.isKnownWindowsMSVCEnvironment()) { |
51 | // Dwarf debugging files will be generated by default, unless "CodeView" |
52 | // is set explicitly. Windows/MSVC should use CodeView instead. |
53 | if (!llvmModule.getModuleFlag(Key: kCodeViewKey)) |
54 | llvmModule.addModuleFlag(Behavior: llvm::Module::Warning, Key: kCodeViewKey, Val: 1); |
55 | } |
56 | } |
57 | |
58 | /// Translate the debug information for the given function. |
59 | void DebugTranslation::translate(LLVMFuncOp func, llvm::Function &llvmFunc) { |
60 | if (!debugEmissionIsEnabled) |
61 | return; |
62 | |
63 | // Look for a sub program attached to the function. |
64 | auto spLoc = |
65 | func.getLoc()->findInstanceOf<FusedLocWith<LLVM::DISubprogramAttr>>(); |
66 | if (!spLoc) |
67 | return; |
68 | llvmFunc.setSubprogram(translate(spLoc.getMetadata())); |
69 | } |
70 | |
71 | //===----------------------------------------------------------------------===// |
72 | // Attributes |
73 | //===----------------------------------------------------------------------===// |
74 | |
75 | llvm::DIType *DebugTranslation::translateImpl(DINullTypeAttr attr) { |
76 | // A DINullTypeAttr at the beginning of the subroutine types list models |
77 | // a void result type. If it is at the end, it models a variadic function. |
78 | // Translate the explicit DINullTypeAttr to a nullptr since LLVM IR metadata |
79 | // does not have an explicit void result type nor a variadic type |
80 | // representation. |
81 | return nullptr; |
82 | } |
83 | |
84 | llvm::DIExpression * |
85 | DebugTranslation::getExpressionAttrOrNull(DIExpressionAttr attr) { |
86 | if (!attr) |
87 | return nullptr; |
88 | return translateExpression(attr); |
89 | } |
90 | |
91 | llvm::MDString *DebugTranslation::getMDStringOrNull(StringAttr stringAttr) { |
92 | if (!stringAttr || stringAttr.empty()) |
93 | return nullptr; |
94 | return llvm::MDString::get(llvmCtx, stringAttr); |
95 | } |
96 | |
97 | llvm::MDTuple * |
98 | DebugTranslation::getMDTupleOrNull(ArrayRef<DINodeAttr> elements) { |
99 | if (elements.empty()) |
100 | return nullptr; |
101 | SmallVector<llvm::Metadata *> llvmElements = llvm::to_vector( |
102 | Range: llvm::map_range(C&: elements, F: [&](DINodeAttr attr) -> llvm::Metadata * { |
103 | if (DIAnnotationAttr annAttr = dyn_cast<DIAnnotationAttr>(attr)) { |
104 | llvm::Metadata *ops[2] = { |
105 | llvm::MDString::get(llvmCtx, annAttr.getName()), |
106 | llvm::MDString::get(llvmCtx, annAttr.getValue())}; |
107 | return llvm::MDNode::get(Context&: llvmCtx, MDs: ops); |
108 | } |
109 | return translate(attr); |
110 | })); |
111 | return llvm::MDNode::get(Context&: llvmCtx, MDs: llvmElements); |
112 | } |
113 | |
114 | llvm::DIBasicType *DebugTranslation::translateImpl(DIBasicTypeAttr attr) { |
115 | return llvm::DIBasicType::get( |
116 | llvmCtx, attr.getTag(), getMDStringOrNull(stringAttr: attr.getName()), |
117 | attr.getSizeInBits(), |
118 | /*AlignInBits=*/0, attr.getEncoding(), llvm::DINode::FlagZero); |
119 | } |
120 | |
121 | llvm::DICompileUnit *DebugTranslation::translateImpl(DICompileUnitAttr attr) { |
122 | llvm::DIBuilder builder(llvmModule); |
123 | return builder.createCompileUnit( |
124 | Lang: attr.getSourceLanguage(), File: translate(attr.getFile()), |
125 | Producer: attr.getProducer() ? attr.getProducer().getValue() : "", |
126 | isOptimized: attr.getIsOptimized(), |
127 | /*Flags=*/"", /*RV=*/0, /*SplitName=*/{}, |
128 | Kind: static_cast<llvm::DICompileUnit::DebugEmissionKind>( |
129 | attr.getEmissionKind()), |
130 | DWOId: 0, SplitDebugInlining: true, DebugInfoForProfiling: false, |
131 | NameTableKind: static_cast<llvm::DICompileUnit::DebugNameTableKind>( |
132 | attr.getNameTableKind())); |
133 | } |
134 | |
135 | /// Returns a new `DINodeT` that is either distinct or not, depending on |
136 | /// `isDistinct`. |
137 | template <class DINodeT, class... Ts> |
138 | static DINodeT *getDistinctOrUnique(bool isDistinct, Ts &&...args) { |
139 | if (isDistinct) |
140 | return DINodeT::getDistinct(std::forward<Ts>(args)...); |
141 | return DINodeT::get(std::forward<Ts>(args)...); |
142 | } |
143 | |
144 | llvm::TempDICompositeType |
145 | DebugTranslation::translateTemporaryImpl(DICompositeTypeAttr attr) { |
146 | return llvm::DICompositeType::getTemporary( |
147 | llvmCtx, attr.getTag(), getMDStringOrNull(stringAttr: attr.getName()), nullptr, |
148 | attr.getLine(), nullptr, nullptr, attr.getSizeInBits(), |
149 | attr.getAlignInBits(), |
150 | /*OffsetInBits=*/0, |
151 | /*Flags=*/static_cast<llvm::DINode::DIFlags>(attr.getFlags()), |
152 | /*Elements=*/nullptr, /*RuntimeLang=*/0, /*EnumKind=*/std::nullopt, |
153 | /*VTableHolder=*/nullptr); |
154 | } |
155 | |
156 | llvm::TempDISubprogram |
157 | DebugTranslation::translateTemporaryImpl(DISubprogramAttr attr) { |
158 | return llvm::DISubprogram::getTemporary( |
159 | llvmCtx, /*Scope=*/nullptr, /*Name=*/{}, /*LinkageName=*/{}, |
160 | /*File=*/nullptr, attr.getLine(), /*Type=*/nullptr, |
161 | /*ScopeLine=*/0, /*ContainingType=*/nullptr, /*VirtualIndex=*/0, |
162 | /*ThisAdjustment=*/0, llvm::DINode::FlagZero, |
163 | static_cast<llvm::DISubprogram::DISPFlags>(attr.getSubprogramFlags()), |
164 | /*Unit=*/nullptr); |
165 | } |
166 | |
167 | llvm::DICompositeType * |
168 | DebugTranslation::translateImpl(DICompositeTypeAttr attr) { |
169 | // TODO: Use distinct attributes to model this, once they have landed. |
170 | // Depending on the tag, composite types must be distinct. |
171 | bool isDistinct = false; |
172 | switch (attr.getTag()) { |
173 | case llvm::dwarf::DW_TAG_class_type: |
174 | case llvm::dwarf::DW_TAG_enumeration_type: |
175 | case llvm::dwarf::DW_TAG_structure_type: |
176 | case llvm::dwarf::DW_TAG_union_type: |
177 | isDistinct = true; |
178 | } |
179 | |
180 | return getDistinctOrUnique<llvm::DICompositeType>( |
181 | isDistinct, llvmCtx, attr.getTag(), getMDStringOrNull(attr.getName()), |
182 | translate(attr.getFile()), attr.getLine(), translate(attr.getScope()), |
183 | translate(attr.getBaseType()), attr.getSizeInBits(), |
184 | attr.getAlignInBits(), |
185 | /*OffsetInBits=*/0, |
186 | /*Flags=*/static_cast<llvm::DINode::DIFlags>(attr.getFlags()), |
187 | getMDTupleOrNull(attr.getElements()), |
188 | /*RuntimeLang=*/0, /*EnumKind*/ std::nullopt, /*VTableHolder=*/nullptr, |
189 | /*TemplateParams=*/nullptr, /*Identifier=*/nullptr, |
190 | /*Discriminator=*/nullptr, |
191 | getExpressionAttrOrNull(attr.getDataLocation()), |
192 | getExpressionAttrOrNull(attr.getAssociated()), |
193 | getExpressionAttrOrNull(attr.getAllocated()), |
194 | getExpressionAttrOrNull(attr.getRank())); |
195 | } |
196 | |
197 | llvm::DIDerivedType *DebugTranslation::translateImpl(DIDerivedTypeAttr attr) { |
198 | return llvm::DIDerivedType::get( |
199 | llvmCtx, attr.getTag(), getMDStringOrNull(stringAttr: attr.getName()), |
200 | /*File=*/nullptr, /*Line=*/0, |
201 | /*Scope=*/nullptr, translate(attr.getBaseType()), attr.getSizeInBits(), |
202 | attr.getAlignInBits(), attr.getOffsetInBits(), |
203 | attr.getDwarfAddressSpace(), /*PtrAuthData=*/std::nullopt, |
204 | /*Flags=*/llvm::DINode::FlagZero, translate(attr.getExtraData())); |
205 | } |
206 | |
207 | llvm::DIStringType *DebugTranslation::translateImpl(DIStringTypeAttr attr) { |
208 | return llvm::DIStringType::get( |
209 | llvmCtx, attr.getTag(), getMDStringOrNull(attr.getName()), |
210 | translate(attr.getStringLength()), |
211 | getExpressionAttrOrNull(attr.getStringLengthExp()), |
212 | getExpressionAttrOrNull(attr.getStringLocationExp()), |
213 | attr.getSizeInBits(), attr.getAlignInBits(), attr.getEncoding()); |
214 | } |
215 | |
216 | llvm::DIFile *DebugTranslation::translateImpl(DIFileAttr attr) { |
217 | return llvm::DIFile::get(llvmCtx, getMDStringOrNull(stringAttr: attr.getName()), |
218 | getMDStringOrNull(stringAttr: attr.getDirectory())); |
219 | } |
220 | |
221 | llvm::DILabel *DebugTranslation::translateImpl(DILabelAttr attr) { |
222 | return llvm::DILabel::get(llvmCtx, translate(attr.getScope()), |
223 | getMDStringOrNull(stringAttr: attr.getName()), |
224 | translate(attr.getFile()), attr.getLine()); |
225 | } |
226 | |
227 | llvm::DILexicalBlock *DebugTranslation::translateImpl(DILexicalBlockAttr attr) { |
228 | return llvm::DILexicalBlock::getDistinct(llvmCtx, translate(attr.getScope()), |
229 | translate(attr.getFile()), |
230 | attr.getLine(), attr.getColumn()); |
231 | } |
232 | |
233 | llvm::DILexicalBlockFile * |
234 | DebugTranslation::translateImpl(DILexicalBlockFileAttr attr) { |
235 | return llvm::DILexicalBlockFile::getDistinct( |
236 | llvmCtx, translate(attr.getScope()), translate(attr.getFile()), |
237 | attr.getDiscriminator()); |
238 | } |
239 | |
240 | llvm::DILocalScope *DebugTranslation::translateImpl(DILocalScopeAttr attr) { |
241 | return cast<llvm::DILocalScope>(Val: translate(attr: DINodeAttr(attr))); |
242 | } |
243 | |
244 | llvm::DIVariable *DebugTranslation::translateImpl(DIVariableAttr attr) { |
245 | return cast<llvm::DIVariable>(Val: translate(attr: DINodeAttr(attr))); |
246 | } |
247 | |
248 | llvm::DILocalVariable * |
249 | DebugTranslation::translateImpl(DILocalVariableAttr attr) { |
250 | return llvm::DILocalVariable::get( |
251 | llvmCtx, translate(attr.getScope()), getMDStringOrNull(stringAttr: attr.getName()), |
252 | translate(attr.getFile()), attr.getLine(), translate(attr.getType()), |
253 | attr.getArg(), static_cast<llvm::DINode::DIFlags>(attr.getFlags()), |
254 | attr.getAlignInBits(), |
255 | /*Annotations=*/nullptr); |
256 | } |
257 | |
258 | llvm::DIGlobalVariable * |
259 | DebugTranslation::translateImpl(DIGlobalVariableAttr attr) { |
260 | return llvm::DIGlobalVariable::getDistinct( |
261 | llvmCtx, translate(attr.getScope()), getMDStringOrNull(stringAttr: attr.getName()), |
262 | getMDStringOrNull(stringAttr: attr.getLinkageName()), translate(attr.getFile()), |
263 | attr.getLine(), translate(attr.getType()), attr.getIsLocalToUnit(), |
264 | attr.getIsDefined(), nullptr, nullptr, attr.getAlignInBits(), nullptr); |
265 | } |
266 | |
267 | llvm::DINode * |
268 | DebugTranslation::translateRecursive(DIRecursiveTypeAttrInterface attr) { |
269 | DistinctAttr recursiveId = attr.getRecId(); |
270 | if (auto *iter = recursiveNodeMap.find(Key: recursiveId); |
271 | iter != recursiveNodeMap.end()) { |
272 | return iter->second; |
273 | } |
274 | assert(!attr.getIsRecSelf() && "unbound DI recursive self reference"); |
275 | |
276 | auto setRecursivePlaceholder = [&](llvm::DINode *placeholder) { |
277 | recursiveNodeMap.try_emplace(Key: recursiveId, Args&: placeholder); |
278 | }; |
279 | |
280 | llvm::DINode *result = |
281 | TypeSwitch<DIRecursiveTypeAttrInterface, llvm::DINode *>(attr) |
282 | .Case<DICompositeTypeAttr>([&](auto attr) { |
283 | auto temporary = translateTemporaryImpl(attr); |
284 | setRecursivePlaceholder(temporary.get()); |
285 | // Must call `translateImpl` directly instead of `translate` to |
286 | // avoid handling the recursive interface again. |
287 | auto *concrete = translateImpl(attr); |
288 | temporary->replaceAllUsesWith(concrete); |
289 | return concrete; |
290 | }) |
291 | .Case<DISubprogramAttr>([&](auto attr) { |
292 | auto temporary = translateTemporaryImpl(attr); |
293 | setRecursivePlaceholder(temporary.get()); |
294 | // Must call `translateImpl` directly instead of `translate` to |
295 | // avoid handling the recursive interface again. |
296 | auto *concrete = translateImpl(attr); |
297 | temporary->replaceAllUsesWith(concrete); |
298 | return concrete; |
299 | }); |
300 | |
301 | assert(recursiveNodeMap.back().first == recursiveId && |
302 | "internal inconsistency: unexpected recursive translation stack"); |
303 | recursiveNodeMap.pop_back(); |
304 | |
305 | return result; |
306 | } |
307 | |
308 | llvm::DIScope *DebugTranslation::translateImpl(DIScopeAttr attr) { |
309 | return cast<llvm::DIScope>(Val: translate(attr: DINodeAttr(attr))); |
310 | } |
311 | |
312 | llvm::DISubprogram *DebugTranslation::translateImpl(DISubprogramAttr attr) { |
313 | if (auto iter = distinctAttrToNode.find(attr.getId()); |
314 | iter != distinctAttrToNode.end()) |
315 | return cast<llvm::DISubprogram>(iter->second); |
316 | |
317 | llvm::DIScope *scope = translate(attr.getScope()); |
318 | llvm::DIFile *file = translate(attr.getFile()); |
319 | llvm::DIType *type = translate(attr.getType()); |
320 | llvm::DICompileUnit *compileUnit = translate(attr.getCompileUnit()); |
321 | |
322 | // Check again after recursive calls in case this distinct node recurses back |
323 | // to itself. |
324 | if (auto iter = distinctAttrToNode.find(attr.getId()); |
325 | iter != distinctAttrToNode.end()) |
326 | return cast<llvm::DISubprogram>(iter->second); |
327 | |
328 | bool isDefinition = static_cast<bool>(attr.getSubprogramFlags() & |
329 | LLVM::DISubprogramFlags::Definition); |
330 | |
331 | llvm::DISubprogram *node = getDistinctOrUnique<llvm::DISubprogram>( |
332 | isDefinition, llvmCtx, scope, getMDStringOrNull(stringAttr: attr.getName()), |
333 | getMDStringOrNull(stringAttr: attr.getLinkageName()), file, attr.getLine(), type, |
334 | attr.getScopeLine(), |
335 | /*ContainingType=*/nullptr, /*VirtualIndex=*/0, |
336 | /*ThisAdjustment=*/0, llvm::DINode::FlagZero, |
337 | static_cast<llvm::DISubprogram::DISPFlags>(attr.getSubprogramFlags()), |
338 | compileUnit, /*TemplateParams=*/nullptr, /*Declaration=*/nullptr, |
339 | getMDTupleOrNull(elements: attr.getRetainedNodes()), nullptr, |
340 | getMDTupleOrNull(elements: attr.getAnnotations())); |
341 | if (attr.getId()) |
342 | distinctAttrToNode.try_emplace(attr.getId(), node); |
343 | return node; |
344 | } |
345 | |
346 | llvm::DIModule *DebugTranslation::translateImpl(DIModuleAttr attr) { |
347 | return llvm::DIModule::get( |
348 | llvmCtx, translate(attr.getFile()), translate(attr.getScope()), |
349 | getMDStringOrNull(stringAttr: attr.getName()), |
350 | getMDStringOrNull(stringAttr: attr.getConfigMacros()), |
351 | getMDStringOrNull(stringAttr: attr.getIncludePath()), |
352 | getMDStringOrNull(stringAttr: attr.getApinotes()), attr.getLine(), attr.getIsDecl()); |
353 | } |
354 | |
355 | llvm::DINamespace *DebugTranslation::translateImpl(DINamespaceAttr attr) { |
356 | return llvm::DINamespace::get(llvmCtx, translate(attr.getScope()), |
357 | getMDStringOrNull(stringAttr: attr.getName()), |
358 | attr.getExportSymbols()); |
359 | } |
360 | |
361 | llvm::DIImportedEntity * |
362 | DebugTranslation::translateImpl(DIImportedEntityAttr attr) { |
363 | return llvm::DIImportedEntity::get( |
364 | llvmCtx, attr.getTag(), translate(attr.getScope()), |
365 | translate(attr.getEntity()), translate(attr.getFile()), attr.getLine(), |
366 | getMDStringOrNull(stringAttr: attr.getName()), getMDTupleOrNull(elements: attr.getElements())); |
367 | } |
368 | |
369 | llvm::DISubrange *DebugTranslation::translateImpl(DISubrangeAttr attr) { |
370 | auto getMetadataOrNull = [&](Attribute attr) -> llvm::Metadata * { |
371 | if (!attr) |
372 | return nullptr; |
373 | |
374 | llvm::Metadata *metadata = |
375 | llvm::TypeSwitch<Attribute, llvm::Metadata *>(attr) |
376 | .Case(caseFn: [&](IntegerAttr intAttr) { |
377 | return llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned( |
378 | llvm::Type::getInt64Ty(llvmCtx), intAttr.getInt())); |
379 | }) |
380 | .Case(caseFn: [&](LLVM::DIExpressionAttr expr) { |
381 | return translateExpression(expr); |
382 | }) |
383 | .Case(caseFn: [&](LLVM::DILocalVariableAttr local) { |
384 | return translate(local); |
385 | }) |
386 | .Case<>(caseFn: [&](LLVM::DIGlobalVariableAttr global) { |
387 | return translate(global); |
388 | }) |
389 | .Default(defaultFn: [&](Attribute attr) { return nullptr; }); |
390 | return metadata; |
391 | }; |
392 | return llvm::DISubrange::get(llvmCtx, getMetadataOrNull(attr.getCount()), |
393 | getMetadataOrNull(attr.getLowerBound()), |
394 | getMetadataOrNull(attr.getUpperBound()), |
395 | getMetadataOrNull(attr.getStride())); |
396 | } |
397 | |
398 | llvm::DICommonBlock *DebugTranslation::translateImpl(DICommonBlockAttr attr) { |
399 | return llvm::DICommonBlock::get(llvmCtx, translate(attr.getScope()), |
400 | translate(attr.getDecl()), |
401 | getMDStringOrNull(stringAttr: attr.getName()), |
402 | translate(attr.getFile()), attr.getLine()); |
403 | } |
404 | |
405 | llvm::DIGenericSubrange * |
406 | DebugTranslation::translateImpl(DIGenericSubrangeAttr attr) { |
407 | auto getMetadataOrNull = [&](Attribute attr) -> llvm::Metadata * { |
408 | if (!attr) |
409 | return nullptr; |
410 | |
411 | llvm::Metadata *metadata = |
412 | llvm::TypeSwitch<Attribute, llvm::Metadata *>(attr) |
413 | .Case(caseFn: [&](LLVM::DIExpressionAttr expr) { |
414 | return translateExpression(expr); |
415 | }) |
416 | .Case(caseFn: [&](LLVM::DILocalVariableAttr local) { |
417 | return translate(local); |
418 | }) |
419 | .Case<>(caseFn: [&](LLVM::DIGlobalVariableAttr global) { |
420 | return translate(global); |
421 | }) |
422 | .Default(defaultFn: [&](Attribute attr) { return nullptr; }); |
423 | return metadata; |
424 | }; |
425 | return llvm::DIGenericSubrange::get(Context&: llvmCtx, |
426 | CountNode: getMetadataOrNull(attr.getCount()), |
427 | LowerBound: getMetadataOrNull(attr.getLowerBound()), |
428 | UpperBound: getMetadataOrNull(attr.getUpperBound()), |
429 | Stride: getMetadataOrNull(attr.getStride())); |
430 | } |
431 | |
432 | llvm::DISubroutineType * |
433 | DebugTranslation::translateImpl(DISubroutineTypeAttr attr) { |
434 | // Concatenate the result and argument types into a single array. |
435 | SmallVector<llvm::Metadata *> types; |
436 | for (DITypeAttr type : attr.getTypes()) |
437 | types.push_back(translate(type)); |
438 | return llvm::DISubroutineType::get( |
439 | llvmCtx, llvm::DINode::FlagZero, attr.getCallingConvention(), |
440 | llvm::DITypeRefArray(llvm::MDNode::get(Context&: llvmCtx, MDs: types))); |
441 | } |
442 | |
443 | llvm::DIType *DebugTranslation::translateImpl(DITypeAttr attr) { |
444 | return cast<llvm::DIType>(Val: translate(attr: DINodeAttr(attr))); |
445 | } |
446 | |
447 | llvm::DINode *DebugTranslation::translate(DINodeAttr attr) { |
448 | if (!attr) |
449 | return nullptr; |
450 | // Check for a cached instance. |
451 | if (llvm::DINode *node = attrToNode.lookup(Val: attr)) |
452 | return node; |
453 | |
454 | llvm::DINode *node = nullptr; |
455 | // Recursive types go through a dedicated handler. All other types are |
456 | // dispatched directly to their specific handlers. |
457 | if (auto recTypeAttr = dyn_cast<DIRecursiveTypeAttrInterface>(attr)) |
458 | if (recTypeAttr.getRecId()) |
459 | node = translateRecursive(recTypeAttr); |
460 | |
461 | if (!node) |
462 | node = TypeSwitch<DINodeAttr, llvm::DINode *>(attr) |
463 | .Case<DIBasicTypeAttr, DICommonBlockAttr, DICompileUnitAttr, |
464 | DICompositeTypeAttr, DIDerivedTypeAttr, DIFileAttr, |
465 | DIGenericSubrangeAttr, DIGlobalVariableAttr, |
466 | DIImportedEntityAttr, DILabelAttr, DILexicalBlockAttr, |
467 | DILexicalBlockFileAttr, DILocalVariableAttr, DIModuleAttr, |
468 | DINamespaceAttr, DINullTypeAttr, DIStringTypeAttr, |
469 | DISubprogramAttr, DISubrangeAttr, DISubroutineTypeAttr>( |
470 | [&](auto attr) { return translateImpl(attr); }); |
471 | |
472 | if (node && !node->isTemporary()) |
473 | attrToNode.insert(KV: {attr, node}); |
474 | return node; |
475 | } |
476 | |
477 | //===----------------------------------------------------------------------===// |
478 | // Locations |
479 | //===----------------------------------------------------------------------===// |
480 | |
481 | /// Translate the given location to an llvm debug location. |
482 | llvm::DILocation *DebugTranslation::translateLoc(Location loc, |
483 | llvm::DILocalScope *scope) { |
484 | if (!debugEmissionIsEnabled) |
485 | return nullptr; |
486 | return translateLoc(loc, scope, /*inlinedAt=*/nullptr); |
487 | } |
488 | |
489 | llvm::DIExpression * |
490 | DebugTranslation::translateExpression(LLVM::DIExpressionAttr attr) { |
491 | SmallVector<uint64_t, 1> ops; |
492 | if (attr) { |
493 | // Append operations their operands to the list. |
494 | for (const DIExpressionElemAttr &op : attr.getOperations()) { |
495 | ops.push_back(op.getOpcode()); |
496 | append_range(ops, op.getArguments()); |
497 | } |
498 | } |
499 | return llvm::DIExpression::get(Context&: llvmCtx, Elements: ops); |
500 | } |
501 | |
502 | llvm::DIGlobalVariableExpression * |
503 | DebugTranslation::translateGlobalVariableExpression( |
504 | LLVM::DIGlobalVariableExpressionAttr attr) { |
505 | return llvm::DIGlobalVariableExpression::get( |
506 | llvmCtx, translate(attr.getVar()), translateExpression(attr.getExpr())); |
507 | } |
508 | |
509 | /// Translate the given location to an llvm DebugLoc. |
510 | llvm::DILocation *DebugTranslation::translateLoc(Location loc, |
511 | llvm::DILocalScope *scope, |
512 | llvm::DILocation *inlinedAt) { |
513 | // LLVM doesn't have a representation for unknown. |
514 | if (isa<UnknownLoc>(Val: loc)) |
515 | return nullptr; |
516 | |
517 | // Check for a cached instance. |
518 | auto existingIt = locationToLoc.find(Val: std::make_tuple(args&: loc, args&: scope, args&: inlinedAt)); |
519 | if (existingIt != locationToLoc.end()) |
520 | return existingIt->second; |
521 | |
522 | llvm::DILocation *llvmLoc = nullptr; |
523 | if (auto callLoc = dyn_cast<CallSiteLoc>(loc)) { |
524 | // For callsites, the caller is fed as the inlinedAt for the callee. |
525 | auto *callerLoc = translateLoc(callLoc.getCaller(), scope, inlinedAt); |
526 | // If the caller scope is not translatable, the overall callsite cannot be |
527 | // represented in LLVM (the callee scope may not match the parent function). |
528 | if (!callerLoc) { |
529 | // If there is an inlinedAt scope (an outer caller), skip to that |
530 | // directly. Otherwise, cannot translate. |
531 | if (!inlinedAt) |
532 | return nullptr; |
533 | callerLoc = inlinedAt; |
534 | } |
535 | llvmLoc = translateLoc(callLoc.getCallee(), nullptr, callerLoc); |
536 | // Fallback: Ignore callee if it has no debug scope. |
537 | if (!llvmLoc) |
538 | llvmLoc = callerLoc; |
539 | |
540 | } else if (auto fileLoc = dyn_cast<FileLineColLoc>(loc)) { |
541 | // A scope of a DILocation cannot be null. |
542 | if (!scope) |
543 | return nullptr; |
544 | llvmLoc = |
545 | llvm::DILocation::get(Context&: llvmCtx, Line: fileLoc.getLine(), Column: fileLoc.getColumn(), |
546 | Scope: scope, InlinedAt: const_cast<llvm::DILocation *>(inlinedAt)); |
547 | |
548 | } else if (auto fusedLoc = dyn_cast<FusedLoc>(loc)) { |
549 | ArrayRef<Location> locations = fusedLoc.getLocations(); |
550 | |
551 | // Check for a scope encoded with the location. |
552 | if (auto scopedAttr = |
553 | dyn_cast_or_null<LLVM::DILocalScopeAttr>(fusedLoc.getMetadata())) |
554 | scope = translate(scopedAttr); |
555 | |
556 | // For fused locations, merge each of the nodes. |
557 | llvmLoc = translateLoc(loc: locations.front(), scope, inlinedAt); |
558 | for (Location locIt : locations.drop_front()) { |
559 | llvmLoc = llvm::DILocation::getMergedLocation( |
560 | llvmLoc, translateLoc(locIt, scope, inlinedAt)); |
561 | } |
562 | |
563 | } else if (auto nameLoc = dyn_cast<NameLoc>(loc)) { |
564 | llvmLoc = translateLoc(nameLoc.getChildLoc(), scope, inlinedAt); |
565 | |
566 | } else if (auto opaqueLoc = dyn_cast<OpaqueLoc>(loc)) { |
567 | llvmLoc = translateLoc(opaqueLoc.getFallbackLocation(), scope, inlinedAt); |
568 | } else { |
569 | llvm_unreachable("unknown location kind"); |
570 | } |
571 | |
572 | locationToLoc.try_emplace(Key: std::make_tuple(args&: loc, args&: scope, args&: inlinedAt), Args&: llvmLoc); |
573 | return llvmLoc; |
574 | } |
575 | |
576 | /// Create an llvm debug file for the given file path. |
577 | llvm::DIFile *DebugTranslation::translateFile(StringRef fileName) { |
578 | auto *&file = fileMap[fileName]; |
579 | if (file) |
580 | return file; |
581 | |
582 | // Make sure the current working directory is up-to-date. |
583 | if (currentWorkingDir.empty()) |
584 | llvm::sys::fs::current_path(result&: currentWorkingDir); |
585 | |
586 | StringRef directory = currentWorkingDir; |
587 | SmallString<128> dirBuf; |
588 | SmallString<128> fileBuf; |
589 | if (llvm::sys::path::is_absolute(path: fileName)) { |
590 | // Strip the common prefix (if it is more than just "/") from current |
591 | // directory and FileName for a more space-efficient encoding. |
592 | auto fileIt = llvm::sys::path::begin(path: fileName); |
593 | auto fileE = llvm::sys::path::end(path: fileName); |
594 | auto curDirIt = llvm::sys::path::begin(path: directory); |
595 | auto curDirE = llvm::sys::path::end(path: directory); |
596 | for (; curDirIt != curDirE && *curDirIt == *fileIt; ++curDirIt, ++fileIt) |
597 | llvm::sys::path::append(path&: dirBuf, a: *curDirIt); |
598 | if (std::distance(first: llvm::sys::path::begin(path: directory), last: curDirIt) == 1) { |
599 | // Don't strip the common prefix if it is only the root "/" since that |
600 | // would make LLVM diagnostic locations confusing. |
601 | directory = StringRef(); |
602 | } else { |
603 | for (; fileIt != fileE; ++fileIt) |
604 | llvm::sys::path::append(path&: fileBuf, a: *fileIt); |
605 | directory = dirBuf; |
606 | fileName = fileBuf; |
607 | } |
608 | } |
609 | return (file = llvm::DIFile::get(Context&: llvmCtx, Filename: fileName, Directory: directory)); |
610 | } |
611 |
Definitions
- interruptIfValidLocation
- DebugTranslation
- kDebugVersionKey
- kCodeViewKey
- addModuleFlagsIfNotPresent
- translate
- translateImpl
- getExpressionAttrOrNull
- getMDStringOrNull
- getMDTupleOrNull
- translateImpl
- translateImpl
- getDistinctOrUnique
- translateTemporaryImpl
- translateTemporaryImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateRecursive
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translateImpl
- translate
- translateLoc
- translateExpression
- translateGlobalVariableExpression
- translateLoc
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more