GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
339 / 340, 1 excl
99.7%
Branches:
114 / 130, 30 excl
87.7%
Functions:
21 / 22
95.5%

irgenerator/debug-info-generator.spice
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 // Std imports
4 import "std/data/vector";
5 import "std/data/stack";
6 import "std/data/unordered-map";
7 import "std/data/pair";
8 import "std/math/hash";
9 import "std/bindings/llvm/llvm" as llvm;
10
11 // Own imports
12 import "@/ast/ast-nodes";
13 import "@/driver";
14 import "@/irgenerator/ir-generator";
15 import "@/irgenerator/name-mangling";
16 import "@/model/function" as model; // Aliased, because Function clashes with llvm::Function
17 import "@/model/struct";
18 import "@/model/interface";
19 import "@/model/union";
20 import "@/reader/code-loc";
21 import "@/symboltablebuilder/symbol-table-entry";
22 import "@/symboltablebuilder/capture";
23 import "@/symboltablebuilder/qual-type";
24 import "@/symboltablebuilder/super-type";
25 import "@/symboltablebuilder/type" as symboltable; // Aliased, because Type clashes with llvm::Type
26
27 /**
28 * Generates the debug info of a source file.
29 *
30 * It uses the LLVM C API (llvm::DIBuilder wrapper in std/bindings/llvm), which represents every debug info node as an
31 * llvm::Metadata. A few capabilities of the C++ DIBuilder have no C-API equivalent and are therefore not supported:
32 * - Methods use createFunction (the C API has no createMethod).
33 * - Subprogram virtuality flags (SPFlagVirtual/SPFlagPureVirtual) cannot be set (the C API createFunction exposes only
34 * IsLocalToUnit/IsDefinition, not DISPFlags).
35 * - Global strings are emitted as a char array type (the C API has no createStringType).
36 * - Generic struct template parameters are omitted (the C API has no createTemplateTypeParameter).
37 * - The interface vtable holder is not set (the C API has no replaceVTableHolder).
38 */
39 3499 public type DebugInfoGenerator struct {
40 IRGenerator* irGenerator
41 llvm::DIBuilder diBuilder
42 llvm::Metadata compileUnit
43 llvm::Metadata diFile
44 Stack<llvm::Metadata> lexicalBlocks
45 UnorderedMap<long, llvm::Metadata> structTypeCache
46 3499 unsigned int pointerWidth = 0u
47 // Debug types
48 llvm::Metadata doubleTy
49 llvm::Metadata intTy
50 llvm::Metadata uIntTy
51 llvm::Metadata shortTy
52 llvm::Metadata uShortTy
53 llvm::Metadata longTy
54 llvm::Metadata uLongTy
55 llvm::Metadata byteTy
56 llvm::Metadata charTy
57 llvm::Metadata stringTy
58 llvm::Metadata boolTy
59 llvm::Metadata voidTy
60 llvm::Metadata fatPtrTy
61 }
62
63 3499 public p DebugInfoGenerator.ctor(IRGenerator* irGenerator) {
64 3499 this.irGenerator = irGenerator;
65 3499 }
66
67 123 public p DebugInfoGenerator.initialize(const String& sourceFileName, const String& sourceFileDir) {
68 // Create DIBuilder
69 123 this.diBuilder = llvm::DIBuilder(*this.irGenerator.module);
70
71 // Create compilation unit
72 // Note: the path is not absolutized here; the directory operand already carries it
73 123 const llvm::Metadata cuDiFile = this.diBuilder.createFile(sourceFileName, sourceFileDir);
74 123 const bool fullDebugInfo = this.irGenerator.cliOptions.instrumentation.emitsFullDebugInfo();
75
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 22 times.
123 const int emissionKind = fullDebugInfo ? llvm::DW_EMISSION_FULL : llvm::DW_EMISSION_LINE_TABLES_ONLY;
76 123 const bool isOptimized = this.irGenerator.cliOptions.optLevel != OptLevel::O0;
77 123 this.compileUnit = this.diBuilder.createCompileUnit(llvm::DW_SOURCE_LANGUAGE_C_PLUS_PLUS_14, cuDiFile, getProducerString(), isOptimized, emissionKind);
78
79 123 this.irGenerator.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MAX, "Dwarf Version", 5);
80 123 this.irGenerator.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_WARNING, "Debug Info Version", 3); // llvm::DEBUG_METADATA_VERSION
81
82 // Create another DIFile as scope for subprograms
83 123 this.diFile = this.diBuilder.createFile(sourceFileName, sourceFileDir);
84
85 123 this.pointerWidth = this.irGenerator.module.getPointerSize() * 8u;
86
87 // Line tables do not carry any type information, so there is no need to build the debug types at all
88
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 101 times.
123 if !fullDebugInfo {
89 22 return;
90 }
91
92 // Initialize primitive debug types
93 101 this.doubleTy = this.diBuilder.createBasicType(String("double"), 64l, llvm::DW_ATE_FLOAT);
94 101 this.intTy = this.diBuilder.createBasicType(String("int"), 32l, llvm::DW_ATE_SIGNED);
95 101 this.uIntTy = this.diBuilder.createBasicType(String("unsigned int"), 32l, llvm::DW_ATE_UNSIGNED);
96 101 this.shortTy = this.diBuilder.createBasicType(String("short"), 16l, llvm::DW_ATE_SIGNED);
97 101 this.uShortTy = this.diBuilder.createBasicType(String("unsigned short"), 16l, llvm::DW_ATE_UNSIGNED);
98 101 this.longTy = this.diBuilder.createBasicType(String("long"), 64l, llvm::DW_ATE_SIGNED);
99 101 this.uLongTy = this.diBuilder.createBasicType(String("unsigned long"), 64l, llvm::DW_ATE_UNSIGNED);
100 101 this.byteTy = this.diBuilder.createBasicType(String("byte"), 8l, llvm::DW_ATE_UNSIGNED);
101 101 this.charTy = this.diBuilder.createBasicType(String("char"), 8l, llvm::DW_ATE_UNSIGNED_CHAR);
102 101 this.stringTy = this.diBuilder.createPointerType(this.charTy, cast<unsigned long>(this.pointerWidth));
103 101 this.boolTy = this.diBuilder.createBasicType(String("bool"), 8l, llvm::DW_ATE_BOOLEAN);
104 101 this.voidTy = this.diBuilder.createBasicType(String("void"), 0l, llvm::DW_ATE_UNSIGNED);
105
106 // Initialize lambda fat ptr type
107 101 const llvm::Type lambdaFatPtrType = this.irGenerator.llvmTypes.lambdaFatPtrType;
108 101 const llvm::Type int64Ty = this.irGenerator.builder.getInt64Ty();
109 101 const llvm::Type ptrTy = this.irGenerator.builder.getPtrTy();
110 101 llvm::DataLayout dataLayout = this.irGenerator.sourceFile.targetMachine.createDataLayout();
111 101 const unsigned int alignInBits = this.irGenerator.module.getABITypeAlign(lambdaFatPtrType) * 8u;
112 101 const unsigned int ptrAlignInBits = this.irGenerator.module.getABITypeAlign(ptrTy) * 8u;
113 101 const unsigned long int64Width = this.irGenerator.module.getTypeSizeInBits(int64Ty);
114 101 const unsigned int int64AlignInBits = this.irGenerator.module.getABITypeAlign(int64Ty) * 8u;
115 101 const unsigned long fctPtrOffset = dataLayout.getStructElementOffset(lambdaFatPtrType, 0u) * 8l;
116 101 const unsigned long capturesOffset = dataLayout.getStructElementOffset(lambdaFatPtrType, 1u) * 8l;
117 101 const unsigned long captureSizeOffset = dataLayout.getStructElementOffset(lambdaFatPtrType, 2u) * 8l;
118
119 101 const llvm::Metadata voidPtrDIType = this.diBuilder.createPointerType(this.voidTy, cast<unsigned long>(this.pointerWidth), ptrAlignInBits);
120 101 Vector<llvm::Metadata> noElements;
121 101 this.fatPtrTy = this.diBuilder.createStructType(this.diFile, String("_lambda"), this.diFile, 0u, this.irGenerator.module.getTypeSizeInBits(lambdaFatPtrType),
122 101 alignInBits, llvm::DI_FLAG_TYPE_PASS_BY_VALUE | llvm::DI_FLAG_NON_TRIVIAL, noElements, String("_lambda"));
123 101 const llvm::Metadata firstType = this.diBuilder.createMemberType(this.fatPtrTy, String("fct"), this.diFile, 0u, cast<unsigned long>(this.pointerWidth), ptrAlignInBits, fctPtrOffset, voidPtrDIType);
124 101 const llvm::Metadata secondType = this.diBuilder.createMemberType(this.fatPtrTy, String("captures"), this.diFile, 0u, cast<unsigned long>(this.pointerWidth), ptrAlignInBits, capturesOffset, voidPtrDIType);
125 101 const llvm::Metadata thirdType = this.diBuilder.createMemberType(this.fatPtrTy, String("captureSize"), this.diFile, 0u, int64Width, int64AlignInBits, captureSizeOffset, this.uLongTy);
126 101 Vector<llvm::Metadata> fatPtrElements;
127 101 fatPtrElements.pushBack(firstType);
128 101 fatPtrElements.pushBack(secondType);
129 101 fatPtrElements.pushBack(thirdType);
130 101 this.diBuilder.replaceArrays(this.fatPtrTy, fatPtrElements);
131 123 }
132
133 44018 public p DebugInfoGenerator.generateFunctionDebugInfo(llvm::Function llvmFunction, const model::Function* spiceFunc, bool isLambda = false) {
134
4/4
✓ Branch 0 taken 42280 times.
✓ Branch 1 taken 1602 times.
✓ Branch 2 taken 126 times.
✓ Branch 3 taken 10 times.
44018 if !this.irGenerator.cliOptions.instrumentation.emitsDebugInfo() {
135 42406 return;
136 }
137
138 1612 const ASTNode* node = spiceFunc.declNode;
139 // Compiler-synthesized functions without a source declaration (e.g. the generated test-suite main) have no code
140 // location to attach debug info to, so skip them
141
3/4
✓ Branch 0 taken 1601 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
1612 if node == nil<ASTNode*> {
142 1 return;
143 }
144 1611 const unsigned int lineNo = cast<unsigned int>(spiceFunc.getDeclCodeLoc().line);
145
146 // Prepare flags
147 1611 llvm::Metadata scope = this.diFile;
148 1611 int flags = llvm::DI_FLAG_PROTOTYPED;
149
3/4
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 1579 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
1611 if spiceFunc.entry != nil<SymbolTableEntry*> {
150 1579 const QualType& entryType = spiceFunc.entry.getQualType();
151
2/4
✓ Branch 0 taken 189 times.
✓ Branch 1 taken 1390 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1579 if entryType.isPublic() {
152 1390 flags = flags | llvm::DI_FLAG_PUBLIC;
153 1390 }
154 1579 }
155
156 // Collect arguments. In line info only mode, the subroutine type stays empty, since no type info is emitted at all
157 1611 Vector<llvm::Metadata> argTypes;
158
3/4
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 1243 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
1611 if this.irGenerator.cliOptions.instrumentation.emitsFullDebugInfo() {
159
4/4
✓ Branch 0 taken 563 times.
✓ Branch 1 taken 680 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 4 times.
1253 if spiceFunc.isProcedure() {
160 569 argTypes.pushBack(this.voidTy);
161 569 } else {
162 684 argTypes.pushBack(this.getDITypeForQualType(node, spiceFunc.returnType)); // Add result type
163 684 }
164
3/4
✓ Branch 0 taken 410 times.
✓ Branch 1 taken 833 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
1253 if spiceFunc.isMethod() {
165 833 argTypes.pushBack(this.getDITypeForQualType(node, spiceFunc.thisType)); // Add this type
166 833 }
167
2/4
✓ Branch 0 taken 1243 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
1253 if isLambda {
168 10 const llvm::Metadata captureStructType = this.generateCaptureStructDebugInfo(spiceFunc);
169 10 scope = captureStructType;
170 10 const llvm::Metadata captureStructPtr = this.diBuilder.createPointerType(captureStructType, cast<unsigned long>(this.pointerWidth));
171 10 argTypes.pushBack(captureStructPtr); // Add this type
172 10 }
173 1253 const QualTypeList paramTypes = spiceFunc.getParamTypes();
174
4/4
✓ Branch 0 taken 1197 times.
✓ Branch 1 taken 1243 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 10 times.
2454 foreach const QualType& argType : paramTypes { // Add arg types
175 1201 argTypes.pushBack(this.getDITypeForQualType(node, argType));
176 1201 }
177 1253 }
178
179 // Create function type
180 1611 const llvm::Metadata functionTy = this.diBuilder.createSubroutineType(this.diFile, argTypes);
181
182 // Note: the C API only exposes IsLocalToUnit/IsDefinition and has no createMethod, so methods are created via
183 // createFunction and the virtual/pure-virtual subprogram flags cannot be represented (see the type doc comment).
184 1611 const bool isLocalToUnit = isLambda;
185 1611 const llvm::Metadata subprogram = this.diBuilder.createFunction(scope, spiceFunc.name, spiceFunc.getMangledName(), this.diFile,
186 1611 lineNo, functionTy, isLocalToUnit, /*isDefinition=*/true, lineNo, flags, /*isOptimized=*/false);
187
188 // Add debug info to LLVM function
189 1611 llvmFunction.setSubprogram(subprogram);
190 // Add scope to lexicalBlocks
191 1611 this.lexicalBlocks.push(subprogram);
192 1611 }
193
194 44744 public p DebugInfoGenerator.concludeFunctionDebugInfo() {
195
2/2
✓ Branch 0 taken 43132 times.
✓ Branch 1 taken 1612 times.
44744 if !this.irGenerator.cliOptions.instrumentation.emitsDebugInfo() {
196 43132 return;
197 }
198 // Functions generateFunctionDebugInfo skipped (no declNode, see there) never pushed a scope to conclude here.
199
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1611 times.
1612 if this.lexicalBlocks.isEmpty() {
200 1 return;
201 }
202
203 1611 this.lexicalBlocks.pop();
204 1611 }
205
206 46582 public p DebugInfoGenerator.pushLexicalBlock(const ASTNode* node) {
207 // Line tables do not carry scope info, so we attach all locations to the enclosing subprogram instead
208
2/2
✓ Branch 0 taken 45324 times.
✓ Branch 1 taken 1258 times.
46582 if !this.irGenerator.cliOptions.instrumentation.emitsFullDebugInfo() {
209 45324 return;
210 }
211
212 1258 const unsigned int line = cast<unsigned int>(node.codeLoc.line);
213 1258 const unsigned int col = cast<unsigned int>(node.codeLoc.col);
214 1258 const llvm::Metadata lexicalBlock = this.diBuilder.createLexicalBlock(this.lexicalBlocks.top(), this.diFile, line, col);
215 1258 this.lexicalBlocks.push(lexicalBlock);
216 1258 }
217
218 46582 public p DebugInfoGenerator.popLexicalBlock() {
219
2/2
✓ Branch 0 taken 45324 times.
✓ Branch 1 taken 1258 times.
46582 if !this.irGenerator.cliOptions.instrumentation.emitsFullDebugInfo() {
220 45324 return;
221 }
222
223 1258 assert !this.lexicalBlocks.isEmpty();
224 1258 this.lexicalBlocks.pop();
225 1258 }
226
227 10 public f<llvm::Metadata> DebugInfoGenerator.generateCaptureStructDebugInfo(const model::Function* spiceFunc) {
228 10 const ASTNode* node = spiceFunc.declNode;
229 10 const unsigned int lineNo = cast<unsigned int>(node.codeLoc.line);
230
231 // Get LLVM type for struct
232 10 Vector<llvm::Type> fieldTypes;
233 10 Vector<SymbolTableEntry*> fieldEntries;
234 10 QualTypeList fieldSymbolTypes;
235
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 10 times.
18 foreach Pair<const String&, Capture&> capturePair : spiceFunc.bodyScope.symbolTable.captures {
236 8 Capture& capture = capturePair.getSecond();
237 8 QualType captureType = capture.capturedSymbol.getQualType();
238
239 // Capture by reference
240
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 if capture.getMode() == CapturePassMode::BY_REFERENCE {
241 4 captureType = captureType.toRef(node);
242 4 }
243
244 8 fieldEntries.pushBack(capture.capturedSymbol);
245 8 fieldSymbolTypes.pushBack(captureType);
246 8 fieldTypes.pushBack(captureType.toLLVMType(this.irGenerator.sourceFile));
247 8 }
248 10 const llvm::Type structType = llvm::getStructType(*this.irGenerator.context, fieldTypes);
249 10 llvm::DataLayout dataLayout = this.irGenerator.sourceFile.targetMachine.createDataLayout();
250 10 const unsigned int alignInBits = this.irGenerator.module.getABITypeAlign(structType) * 8u;
251
252 10 const llvm::Metadata scope = this.lexicalBlocks.top();
253 10 Vector<llvm::Metadata> noElements;
254 10 llvm::Metadata structDiType = this.diBuilder.createClassType(scope, String(""), this.diFile, lineNo, this.irGenerator.module.getTypeSizeInBits(structType),
255 10 alignInBits, llvm::DI_FLAG_TYPE_PASS_BY_VALUE | llvm::DI_FLAG_NON_TRIVIAL, noElements);
256
257 10 Vector<llvm::Metadata> fieldDITypes;
258
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 10 times.
18 for unsigned long i = 0l; i < fieldEntries.getSize(); i++ {
259 8 const llvm::Metadata fieldDiType = this.getDITypeForQualType(node, fieldSymbolTypes.get(i));
260 8 const String& fieldName = fieldEntries.get(i).name;
261 8 const unsigned long offsetInBits = dataLayout.getStructElementOffset(structType, cast<unsigned int>(i)) * 8l;
262 8 const unsigned long fieldSize = fieldDiType.getDITypeSizeInBits();
263 8 const unsigned int fieldAlign = fieldDiType.getDITypeAlignInBits();
264 8 const llvm::Metadata fieldDiDerivedType = this.diBuilder.createMemberType(structDiType, fieldName, this.diFile, lineNo, fieldSize, fieldAlign, offsetInBits, fieldDiType);
265 8 fieldDITypes.pushBack(fieldDiDerivedType);
266 8 }
267 10 this.diBuilder.replaceArrays(structDiType, fieldDITypes);
268
269 20 return structDiType;
270 10 }
271
272 4183 public p DebugInfoGenerator.generateGlobalVarDebugInfo(llvm::Value global, const SymbolTableEntry* globalEntry) {
273
2/2
✓ Branch 0 taken 4140 times.
✓ Branch 1 taken 43 times.
4183 if !this.irGenerator.cliOptions.instrumentation.emitsFullDebugInfo() {
274 4140 return;
275 }
276
277 43 const unsigned int lineNo = cast<unsigned int>(globalEntry.getDeclCodeLoc().line);
278 43 const String name = global.getName();
279 43 const QualType& globalType = globalEntry.getQualType();
280 43 const llvm::Metadata diType = this.getDITypeForQualType(globalEntry.declNode, globalType);
281 43 const bool isLocal = !globalType.isPublic();
282
283 43 const llvm::Metadata gve = this.diBuilder.createGlobalVariableExpression(this.compileUnit, name, name, this.diFile, lineNo, diType, isLocal);
284 43 global.setGlobalMetadata(llvm::getDebugMetadataKindID(*this.irGenerator.context), gve);
285 43 }
286
287 250 public p DebugInfoGenerator.generateGlobalStringDebugInfo(llvm::Value global, const String& name, unsigned long length, const CodeLoc& codeLoc) {
288
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 250 times.
250 if !this.irGenerator.cliOptions.instrumentation.emitsDebugInfo() {
289 ✗ return;
290 }
291 250 const unsigned int lineNo = cast<unsigned int>(codeLoc.line);
292
293 // The C API has no createStringType, so the string is described as a char array (length + 1 for the null-terminator)
294 250 const unsigned long sizeInBits = (length + 1l) * 8l;
295
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 250 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 250 times.
250 const llvm::Metadata stringType = this.diBuilder.createArrayType(sizeInBits, this.charTy.isNull() ? this.diBuilder.createBasicType(String("char"), 8l, llvm::DW_ATE_UNSIGNED_CHAR) : this.charTy);
296 250 const llvm::Metadata gve = this.diBuilder.createGlobalVariableExpression(this.compileUnit, name, name, this.diFile, lineNo, stringType, true);
297 250 global.setGlobalMetadata(llvm::getDebugMetadataKindID(*this.irGenerator.context), gve);
298 250 }
299
300 112562 public p DebugInfoGenerator.generateLocalVarDebugInfo(const String& varName, llvm::Value address, unsigned long argNumber = 18446744073709551615ul, const Vector<unsigned long>& addressOps = Vector<unsigned long>()) {
301
6/6
✓ Branch 0 taken 35569 times.
✓ Branch 1 taken 945 times.
✓ Branch 2 taken 73923 times.
✓ Branch 3 taken 2033 times.
✓ Branch 4 taken 84 times.
✓ Branch 5 taken 8 times.
112562 if !this.irGenerator.cliOptions.instrumentation.emitsFullDebugInfo() {
302 109576 return;
303 }
304
305 // Get symbol table entry
306 2986 const SymbolTableEntry* variableEntry = this.irGenerator.currentScope.lookupStrict(varName);
307 2986 assert variableEntry != nil<SymbolTableEntry*>;
308 // Build debug info
309 2986 const llvm::Metadata scope = this.lexicalBlocks.top();
310 2986 const llvm::Metadata diType = this.getDITypeForQualType(variableEntry.declNode, variableEntry.getQualType());
311 2986 const unsigned int lineNo = cast<unsigned int>(variableEntry.declNode.codeLoc.line);
312
313 2986 llvm::Metadata varInfo;
314
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 945 times.
✓ Branch 2 taken 2033 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
2986 if argNumber != 18446744073709551615ul {
315 2033 varInfo = this.diBuilder.createParameterVariable(scope, varName, cast<unsigned int>(argNumber), this.diFile, lineNo, diType);
316 2033 } else {
317 953 varInfo = this.diBuilder.createAutoVariable(scope, varName, this.diFile, lineNo, diType);
318 953 }
319 // The address ops describe how to get from the given address to the variable, e.g. through the captures pointer
320 2986 const llvm::Metadata expr = this.diBuilder.createExpression(addressOps);
321 2986 const llvm::Metadata debugLocation = this.irGenerator.builder.getCurrentDebugLocation();
322 2986 assert !debugLocation.isNull();
323 2986 this.diBuilder.insertDeclareAtEnd(address, varInfo, expr, debugLocation, this.irGenerator.builder.getInsertBlock());
324 112478 }
325
326 1136550 public p DebugInfoGenerator.setSourceLocation(const CodeLoc& codeLoc) {
327
2/2
✓ Branch 0 taken 1095834 times.
✓ Branch 1 taken 40716 times.
1136550 if !this.irGenerator.cliOptions.instrumentation.emitsDebugInfo() {
328 1095834 return;
329 }
330
331 40716 assert !this.lexicalBlocks.isEmpty();
332 40716 const llvm::Metadata scope = this.lexicalBlocks.top();
333 40716 const llvm::Metadata diCodeLoc = llvm::getDebugLocation(*this.irGenerator.context, cast<unsigned int>(codeLoc.line), cast<unsigned int>(codeLoc.col), scope);
334 40716 this.irGenerator.builder.setCurrentDebugLocation(diCodeLoc);
335 40716 }
336
337 965116 public p DebugInfoGenerator.setSourceLocation(const ASTNode* node) {
338 965116 this.setSourceLocation(node.codeLoc);
339 965116 }
340
341 3499 public p DebugInfoGenerator.finalize() {
342
2/2
✓ Branch 0 taken 3376 times.
✓ Branch 1 taken 123 times.
3499 if this.irGenerator.cliOptions.instrumentation.emitsDebugInfo() {
343 123 this.diBuilder.finalize();
344 123 }
345 3499 }
346
347 9306 f<llvm::Metadata> DebugInfoGenerator.getDITypeForQualType(const ASTNode* node, const QualType& ty) {
348 // Pointer type
349
2/2
✓ Branch 0 taken 1575 times.
✓ Branch 1 taken 7731 times.
9306 if ty.isPtr() {
350 1575 const llvm::Metadata pointeeTy = this.getDITypeForQualType(node, ty.getContained());
351 1575 return this.diBuilder.createPointerType(pointeeTy, cast<unsigned long>(this.pointerWidth));
352 }
353
354 // Reference type
355
2/2
✓ Branch 0 taken 1036 times.
✓ Branch 1 taken 6695 times.
7731 if ty.isRef() {
356 1036 const llvm::Metadata referencedType = this.getDITypeForQualType(node, ty.getContained());
357 1036 return this.diBuilder.createReferenceType(referencedType, cast<unsigned long>(this.pointerWidth));
358 }
359
360 // Array type
361
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 6679 times.
6695 if ty.isArray() {
362 16 const llvm::Metadata itemTy = this.getDITypeForQualType(node, ty.getContained());
363 16 const unsigned long size = cast<unsigned long>(ty.getArraySize());
364 16 return this.diBuilder.createArrayType(size, itemTy);
365 }
366
367 // Primitive types
368 6679 llvm::Metadata baseDiType;
369 6679 const SuperType superType = ty.getSuperType();
370
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 6652 times.
6679 if superType == SuperType::TY_DOUBLE {
371 27 baseDiType = this.doubleTy;
372
2/2
✓ Branch 0 taken 695 times.
✓ Branch 1 taken 5957 times.
6679 } else if superType == SuperType::TY_INT {
373
2/2
✓ Branch 0 taken 475 times.
✓ Branch 1 taken 220 times.
695 baseDiType = ty.isSigned() ? this.intTy : this.uIntTy;
374
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 5933 times.
6652 } else if superType == SuperType::TY_SHORT {
375
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 baseDiType = ty.isSigned() ? this.shortTy : this.uShortTy;
376
2/2
✓ Branch 0 taken 1318 times.
✓ Branch 1 taken 4615 times.
5957 } else if superType == SuperType::TY_LONG {
377
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 1186 times.
1318 baseDiType = ty.isSigned() ? this.longTy : this.uLongTy;
378
2/2
✓ Branch 0 taken 348 times.
✓ Branch 1 taken 4267 times.
5933 } else if superType == SuperType::TY_BYTE {
379 348 baseDiType = this.byteTy;
380
2/2
✓ Branch 0 taken 384 times.
✓ Branch 1 taken 3883 times.
4615 } else if superType == SuperType::TY_CHAR {
381 384 baseDiType = this.charTy;
382
2/2
✓ Branch 0 taken 594 times.
✓ Branch 1 taken 3289 times.
4267 } else if superType == SuperType::TY_STRING {
383 594 baseDiType = this.stringTy;
384
2/2
✓ Branch 0 taken 305 times.
✓ Branch 1 taken 2984 times.
3883 } else if superType == SuperType::TY_BOOL {
385 305 baseDiType = this.boolTy;
386
2/2
✓ Branch 0 taken 2936 times.
✓ Branch 1 taken 48 times.
3289 } else if superType == SuperType::TY_STRUCT {
387 2936 baseDiType = this.getStructDIType(node, ty);
388
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 46 times.
2984 } else if superType == SuperType::TY_INTERFACE {
389 2 baseDiType = this.getInterfaceDIType(node, ty);
390
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 30 times.
48 } else if superType == SuperType::TY_UNION {
391 16 baseDiType = this.getUnionDIType(node, ty);
392
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
46 } else if superType == SuperType::TY_FUNCTION || superType == SuperType::TY_PROCEDURE {
393 30 baseDiType = this.fatPtrTy;
394 30 } else {
395 − panic(Error("Debug Info Type fallthrough")); // LCOV_EXCL_LINE
396 6652 }
397
398
2/2
✓ Branch 0 taken 5784 times.
✓ Branch 1 taken 895 times.
6679 if ty.isConst() {
399 895 baseDiType = this.diBuilder.createQualifiedType(llvm::DW_TAG_CONST_TYPE, baseDiType);
400 895 }
401
402 6679 return baseDiType;
403 }
404
405 2936 f<llvm::Metadata> DebugInfoGenerator.getStructDIType(const ASTNode* node, const QualType& ty) {
406 // Do cache lookup. Qualifiers are applied on top of the cached type, so the cache is keyed by the unqualified type
407 2936 const symboltable::Type* unqualifiedType = ty.getType();
408 2936 const long hashKey = cast<long>(hash(unqualifiedType.getName(true, false, true)));
409
2/2
✓ Branch 0 taken 2568 times.
✓ Branch 1 taken 368 times.
2936 if this.structTypeCache.contains(hashKey) {
410 5136 return this.structTypeCache.get(hashKey);
411 }
412
413 // Cache miss, generate struct type
414 368 const Struct* spiceStruct = ty.getStruct(node);
415 368 assert spiceStruct != nil<Struct*>;
416
417 // Retrieve information about the struct
418 368 const unsigned int lineNo = cast<unsigned int>(spiceStruct.structBase.getDeclCodeLoc().line);
419 368 const QualType& structQualType = spiceStruct.entry.getQualType();
420 368 const llvm::Type structType = structQualType.toLLVMType(this.irGenerator.sourceFile);
421 368 llvm::DataLayout dataLayout = this.irGenerator.sourceFile.targetMachine.createDataLayout();
422 368 const unsigned int alignInBits = this.irGenerator.module.getABITypeAlign(structType) * 8u;
423
424 // Create struct ty. Generic substantiations are named by their signature (e.g. Vector<int>), so that debuggers can
425 // tell the manifestations of a generic struct apart
426 368 const String structName = spiceStruct.structBase.getSignature();
427 368 const String mangledName = mangleStruct(*spiceStruct);
428 368 Vector<llvm::Metadata> noElements;
429 368 llvm::Metadata structDiType = this.diBuilder.createStructType(this.diFile, structName, this.diFile, lineNo, this.irGenerator.module.getTypeSizeInBits(structType),
430 368 alignInBits, llvm::DI_FLAG_TYPE_PASS_BY_REFERENCE | llvm::DI_FLAG_NON_TRIVIAL, noElements, mangledName);
431
432 // Insert into cache
433 368 this.structTypeCache.upsert(hashKey, structDiType);
434
435 // Collect DI types for fields
436 368 Vector<llvm::Metadata> fieldTypes;
437
2/2
✓ Branch 0 taken 368 times.
✓ Branch 1 taken 1006 times.
1374 for unsigned long i = 0l; i < spiceStruct.scope.getFieldCount(); i++ {
438 // Get field entry
439 1006 const SymbolTableEntry* fieldEntry = spiceStruct.scope.lookupField(cast<unsigned int>(i));
440 1006 assert fieldEntry != nil<SymbolTableEntry*> && fieldEntry.isField();
441
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 904 times.
1006 if fieldEntry.isImplicitField {
442 102 continue;
443 }
444
445 904 const QualType fieldType = fieldEntry.getQualType();
446 904 const unsigned int fieldLineNo = cast<unsigned int>(fieldEntry.declNode.codeLoc.line);
447 904 const unsigned long offsetInBits = dataLayout.getStructElementOffset(structType, cast<unsigned int>(spiceStruct.getFieldElementIndex(i))) * 8l;
448
449 904 const llvm::Metadata fieldDiType = this.getDITypeForQualType(node, fieldType);
450 904 const llvm::Metadata fieldDiDerivedType = this.diBuilder.createMemberType(structDiType, fieldEntry.name, this.diFile, fieldLineNo,
451 904 fieldDiType.getDITypeSizeInBits(), fieldDiType.getDITypeAlignInBits(), offsetInBits, fieldDiType);
452
453 904 fieldTypes.pushBack(fieldDiDerivedType);
454 904 }
455
456 // Note: the C API has no createTemplateTypeParameter, so template parameters of generic substantiations are omitted.
457
458 // Attach fields. This may re-unique the struct type, so refresh all references to it
459 368 this.diBuilder.replaceArrays(structDiType, fieldTypes);
460 368 this.structTypeCache.upsert(hashKey, structDiType);
461 736 return structDiType;
462 2936 }
463
464 2 f<llvm::Metadata> DebugInfoGenerator.getInterfaceDIType(const ASTNode* node, const QualType& ty) {
465 // Do cache lookup. Qualifiers are applied on top of the cached type, so the cache is keyed by the unqualified type
466 2 const symboltable::Type* unqualifiedType = ty.getType();
467 2 const long hashKey = cast<long>(hash(unqualifiedType.getName(true, false, true)));
468
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if this.structTypeCache.contains(hashKey) {
469 2 return this.structTypeCache.get(hashKey);
470 }
471
472 // Cache miss, generate interface type
473 1 const Interface* spiceInterface = ty.getInterface(node);
474 1 assert spiceInterface != nil<Interface*>;
475
476 // Retrieve information about the interface
477 1 const unsigned int lineNo = cast<unsigned int>(spiceInterface.structBase.getDeclCodeLoc().line);
478 1 const QualType& interfaceQualType = spiceInterface.entry.getQualType();
479 1 const llvm::Type interfaceType = interfaceQualType.toLLVMType(this.irGenerator.sourceFile);
480 1 const unsigned int alignInBits = this.irGenerator.module.getABITypeAlign(interfaceType) * 8u;
481
482 // Create interface ty. Note: the C API has no replaceVTableHolder, so the vtable holder is not set.
483 1 const String mangledName = mangleInterface(*spiceInterface);
484 1 Vector<llvm::Metadata> noElements;
485 1 const llvm::Metadata interfaceDiType = this.diBuilder.createStructType(this.diFile, spiceInterface.name, this.diFile, lineNo,
486 1 this.irGenerator.module.getTypeSizeInBits(interfaceType), alignInBits, llvm::DI_FLAG_TYPE_PASS_BY_REFERENCE | llvm::DI_FLAG_NON_TRIVIAL, noElements, mangledName);
487
488 // Insert into cache
489 1 this.structTypeCache.upsert(hashKey, interfaceDiType);
490
491 2 return interfaceDiType;
492 2 }
493
494 16 f<llvm::Metadata> DebugInfoGenerator.getUnionDIType(const ASTNode* node, const QualType& ty) {
495 // Do cache lookup. Qualifiers are applied on top of the cached type, so the cache is keyed by the unqualified type
496 16 const symboltable::Type* unqualifiedType = ty.getType();
497 16 const long hashKey = cast<long>(hash(unqualifiedType.getName(true, false, true)));
498
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if this.structTypeCache.contains(hashKey) {
499 16 return this.structTypeCache.get(hashKey);
500 }
501
502 // Cache miss, generate union type
503 8 const Union* spiceUnion = ty.getUnion(node);
504 8 assert spiceUnion != nil<Union*>;
505
506 // Retrieve information about the union
507 8 const unsigned int lineNo = cast<unsigned int>(spiceUnion.structBase.getDeclCodeLoc().line);
508 8 const QualType& unionQualType = spiceUnion.entry.getQualType();
509 8 const llvm::Type unionType = unionQualType.toLLVMType(this.irGenerator.sourceFile);
510 8 llvm::DataLayout dataLayout = this.irGenerator.sourceFile.targetMachine.createDataLayout();
511 8 const unsigned int alignInBits = this.irGenerator.module.getABITypeAlign(unionType) * 8u;
512 // Element 2 is the raw byte-buffer payload that every field aliases at runtime (see Type::toLLVMType's TY_UNION
513 // branch); element 0 is the hidden discriminant tag, which is not surfaced in the debug view.
514 8 const unsigned long payloadOffsetInBits = dataLayout.getStructElementOffset(unionType, 2u) * 8l;
515
516 8 const String unionName = spiceUnion.structBase.getSignature();
517 8 const String mangledName = mangleUnion(*spiceUnion);
518 8 Vector<llvm::Metadata> noElements;
519 8 llvm::Metadata unionDiType = this.diBuilder.createUnionType(this.diFile, unionName, this.diFile, lineNo, this.irGenerator.module.getTypeSizeInBits(unionType),
520 8 alignInBits, llvm::DI_FLAG_ZERO, noElements, mangledName);
521
522 // Insert into cache before computing field types, so that a self-referential field (e.g. a pointer to this same
523 // union) can find this (still-empty) type instead of recursing infinitely
524 8 this.structTypeCache.upsert(hashKey, unionDiType);
525
526 // Collect DI types for fields. Every field overlaps the same payload bytes, so they all share the same offset
527 8 Vector<llvm::Metadata> fieldTypes;
528
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 20 times.
28 for unsigned long i = 0l; i < spiceUnion.scope.getFieldCount(); i++ {
529 20 const SymbolTableEntry* fieldEntry = spiceUnion.scope.lookupField(cast<unsigned int>(i));
530 20 assert fieldEntry != nil<SymbolTableEntry*> && fieldEntry.isField();
531
532 20 const QualType fieldType = fieldEntry.getQualType();
533 20 const unsigned int fieldLineNo = cast<unsigned int>(fieldEntry.declNode.codeLoc.line);
534 20 const llvm::Metadata fieldDiType = this.getDITypeForQualType(node, fieldType);
535 20 const llvm::Metadata fieldDiDerivedType = this.diBuilder.createMemberType(unionDiType, fieldEntry.name, this.diFile, fieldLineNo,
536 20 fieldDiType.getDITypeSizeInBits(), fieldDiType.getDITypeAlignInBits(), payloadOffsetInBits, fieldDiType);
537
538 20 fieldTypes.pushBack(fieldDiDerivedType);
539 20 }
540
541 // Attach fields. This may re-unique the union type, so refresh all references to it
542 8 this.diBuilder.replaceArrays(unionDiType, fieldTypes);
543 8 this.structTypeCache.upsert(hashKey, unionDiType);
544 16 return unionDiType;
545 16 }
546
547