GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 96.0% 643 / 1 / 671
Functions: 100.0% 16 / 0 / 16
Branches: 56.2% 794 / 4 / 1416

src/irgenerator/GenValues.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include "IRGenerator.h"
4
5 #include <ast/ASTNodes.h>
6 #include <irgenerator/NameMangling.h>
7 #include <symboltablebuilder/SymbolTableBuilder.h>
8 #include <typechecker/BuiltinFunctions.h>
9
10 #include <llvm/IR/Module.h>
11
12 namespace spice::compiler {
13
14 167054 std::any IRGenerator::visitValue(const ValueNode *node) {
15 167054 diGenerator.setSourceLocation(node);
16
17 // Function call
18
2/2
✓ Branch 3 → 4 taken 146067 times.
✓ Branch 3 → 5 taken 20987 times.
167054 if (node->fctCall)
19 146067 return visit(node->fctCall);
20
21 // Array initialization
22
2/2
✓ Branch 5 → 6 taken 632 times.
✓ Branch 5 → 7 taken 20355 times.
20987 if (node->arrayInitialization)
23 632 return visit(node->arrayInitialization);
24
25 // Struct instantiation
26
2/2
✓ Branch 7 → 8 taken 3457 times.
✓ Branch 7 → 9 taken 16898 times.
20355 if (node->structInstantiation)
27 3457 return visit(node->structInstantiation);
28
29 // Lambda function
30
2/2
✓ Branch 9 → 10 taken 97 times.
✓ Branch 9 → 11 taken 16801 times.
16898 if (node->lambdaFunc)
31 97 return visit(node->lambdaFunc);
32
33 // Lambda procedure
34
2/2
✓ Branch 11 → 12 taken 88 times.
✓ Branch 11 → 13 taken 16713 times.
16801 if (node->lambdaProc)
35 88 return visit(node->lambdaProc);
36
37 // Lambda expression
38
2/2
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 16711 times.
16713 if (node->lambdaExpr)
39 2 return visit(node->lambdaExpr);
40
41
1/2
✓ Branch 15 → 16 taken 16711 times.
✗ Branch 15 → 23 not taken.
16711 if (node->isNil) {
42 // Retrieve type of the nil constant
43
2/4
✓ Branch 16 → 17 taken 16711 times.
✗ Branch 16 → 34 not taken.
✓ Branch 17 → 18 taken 16711 times.
✗ Branch 17 → 32 not taken.
16711 const auto nilType = any_cast<llvm::Type *>(visit(node->nilType));
44 // Create constant nil value
45 16711 llvm::Constant *nilValue = llvm::Constant::getNullValue(nilType);
46 // Return it
47
1/2
✓ Branch 20 → 21 taken 16711 times.
✗ Branch 20 → 35 not taken.
33422 return LLVMExprResult{.constant = nilValue};
48 }
49
50 throw CompilerError(UNHANDLED_BRANCH, "Value fall-through"); // GCOV_EXCL_LINE
51 }
52
53 130556 std::any IRGenerator::visitConstant(const ConstantNode *node) {
54
2/2
✓ Branch 2 → 3 taken 124703 times.
✓ Branch 2 → 5 taken 5853 times.
130556 if (currentScope != rootScope)
55 124703 diGenerator.setSourceLocation(node);
56
3/6
✓ Branch 5 → 6 taken 130556 times.
✗ Branch 5 → 13 not taken.
✓ Branch 7 → 8 taken 130556 times.
✗ Branch 7 → 12 not taken.
✓ Branch 8 → 9 taken 130556 times.
✗ Branch 8 → 12 not taken.
261112 return getConst(node->getCompileTimeValue(manIdx), node->getEvaluatedSymbolType(manIdx), node);
57 }
58
59 146067 std::any IRGenerator::visitFctCall(const FctCallNode *node) {
60 // Check if this is a builtin call
61
2/2
✓ Branch 9 → 3 taken 3155530 times.
✓ Branch 9 → 10 taken 130829 times.
3286359 for (const auto &[builtinFctName, _] : BUILTIN_FUNCTIONS)
62
2/2
✓ Branch 5 → 6 taken 15238 times.
✓ Branch 5 → 8 taken 3140292 times.
3155530 if (node->fqFunctionName == builtinFctName)
63
1/2
✓ Branch 6 → 7 taken 15238 times.
✗ Branch 6 → 634 not taken.
15238 return visitBuiltinCall(node);
64
65
1/2
✓ Branch 10 → 11 taken 130829 times.
✗ Branch 10 → 634 not taken.
130829 const FctCallNode::FctCallData &data = node->data.at(manIdx);
66
67 130829 const Function *spiceFunc = data.callee;
68
3/4
✓ Branch 12 → 13 taken 130600 times.
✓ Branch 12 → 15 taken 229 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 130600 times.
130829 assert(data.isFctPtrCall() || spiceFunc != nullptr); // If not a function pointer call, we must have a function
69 130829 std::string mangledName;
70
2/2
✓ Branch 17 → 18 taken 130600 times.
✓ Branch 17 → 22 taken 229 times.
130829 if (!data.isFctPtrCall())
71
1/2
✓ Branch 18 → 19 taken 130600 times.
✗ Branch 18 → 455 not taken.
130600 mangledName = spiceFunc->getMangledName();
72
73 // Get entry of the first fragment
74 130829 const SymbolTableEntry *firstFragEntry = currentScope->lookup(node->functionNameFragments.front());
75
76 // Get this type
77 130829 std::vector<llvm::Value *> argValues;
78 130829 llvm::Value *thisPtr = nullptr;
79
2/2
✓ Branch 27 → 28 taken 62435 times.
✓ Branch 27 → 110 taken 68394 times.
130829 if (data.isMethodCall()) {
80
1/2
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 62435 times.
62435 assert(!data.isCtorCall());
81
82 // Retrieve entry of the first fragment
83
2/4
✓ Branch 31 → 32 taken 62435 times.
✗ Branch 31 → 488 not taken.
✓ Branch 32 → 33 taken 62435 times.
✗ Branch 32 → 488 not taken.
62435 const QualType baseType = firstFragEntry->getQualType().getBase();
84
3/6
✓ Branch 33 → 34 taken 62435 times.
✗ Branch 33 → 37 not taken.
✓ Branch 34 → 35 taken 62435 times.
✗ Branch 34 → 456 not taken.
✓ Branch 35 → 36 taken 62435 times.
✗ Branch 35 → 37 not taken.
62435 assert(firstFragEntry != nullptr && baseType.isOneOf({TY_STRUCT, TY_INTERFACE}));
85
1/2
✓ Branch 38 → 39 taken 62435 times.
✗ Branch 38 → 488 not taken.
62435 Scope *structScope = baseType.getBodyScope();
86
87 // Get address of the referenced variable / struct instance
88
1/2
✓ Branch 39 → 40 taken 62435 times.
✗ Branch 39 → 488 not taken.
62435 thisPtr = getAddress(firstFragEntry);
89
1/2
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 42 taken 62435 times.
62435 assert(thisPtr != nullptr);
90
91 // Auto de-reference 'this' pointer
92
1/2
✓ Branch 42 → 43 taken 62435 times.
✗ Branch 42 → 488 not taken.
62435 QualType firstFragmentType = firstFragEntry->getQualType();
93
1/2
✓ Branch 43 → 44 taken 62435 times.
✗ Branch 43 → 488 not taken.
62435 autoDeReferencePtr(thisPtr, firstFragmentType);
94
1/2
✓ Branch 44 → 45 taken 62435 times.
✗ Branch 44 → 488 not taken.
62435 llvm::Type *structTy = baseType.toLLVMType(sourceFile);
95
96 // Traverse through structs - the first fragment is already looked up and the last one is the function name
97
2/2
✓ Branch 107 → 46 taken 14178 times.
✓ Branch 107 → 108 taken 62435 times.
76613 for (size_t i = 1; i < node->functionNameFragments.size() - 1; i++) {
98
2/4
✓ Branch 46 → 47 taken 14178 times.
✗ Branch 46 → 487 not taken.
✓ Branch 47 → 48 taken 14178 times.
✗ Branch 47 → 487 not taken.
14178 const std::string identifier = node->functionNameFragments.at(i);
99 // Retrieve field entry, also looking through composed fields
100 14178 std::vector<size_t> indexPath;
101
1/2
✓ Branch 48 → 49 taken 14178 times.
✗ Branch 48 → 483 not taken.
14178 const SymbolTableEntry *fieldEntry = structScope->symbolTable.lookupInComposedFields(identifier, indexPath);
102
1/2
✗ Branch 49 → 50 not taken.
✓ Branch 49 → 51 taken 14178 times.
14178 assert(fieldEntry != nullptr);
103
1/2
✓ Branch 51 → 52 taken 14178 times.
✗ Branch 51 → 483 not taken.
14178 QualType fieldEntryType = fieldEntry->getQualType();
104
3/6
✓ Branch 52 → 53 taken 14178 times.
✗ Branch 52 → 458 not taken.
✓ Branch 53 → 54 taken 14178 times.
✗ Branch 53 → 457 not taken.
✗ Branch 54 → 55 not taken.
✓ Branch 54 → 56 taken 14178 times.
14178 assert(fieldEntryType.getBase().isOneOf({TY_STRUCT, TY_INTERFACE}));
105 // Get struct type and scope
106
2/4
✓ Branch 56 → 57 taken 14178 times.
✗ Branch 56 → 459 not taken.
✓ Branch 57 → 58 taken 14178 times.
✗ Branch 57 → 459 not taken.
14178 structScope = fieldEntryType.getBase().getBodyScope();
107
1/2
✗ Branch 58 → 59 not taken.
✓ Branch 58 → 60 taken 14178 times.
14178 assert(structScope != nullptr);
108 // Get address of field. If the field was found directly (no composition involved), keep using the
109 // single-index helper, since it elides the GEP entirely for fields at offset 0. Otherwise, step through
110 // all composed fields on the way in a single GEP.
111
2/2
✓ Branch 61 → 62 taken 14022 times.
✓ Branch 61 → 70 taken 156 times.
14178 if (indexPath.size() == 1) {
112
1/2
✓ Branch 66 → 67 taken 14022 times.
✗ Branch 66 → 460 not taken.
14022 thisPtr = insertStructGEP(structTy, thisPtr, static_cast<unsigned int>(indexPath.front()));
113 } else {
114
2/4
✓ Branch 70 → 71 taken 156 times.
✗ Branch 70 → 469 not taken.
✓ Branch 73 → 74 taken 156 times.
✗ Branch 73 → 466 not taken.
312 std::vector<llvm::Value *> indices = {builder.getInt64(0)};
115
2/2
✓ Branch 90 → 77 taken 434 times.
✓ Branch 90 → 91 taken 156 times.
746 for (const size_t index : indexPath)
116
2/4
✓ Branch 79 → 80 taken 434 times.
✗ Branch 79 → 470 not taken.
✓ Branch 80 → 81 taken 434 times.
✗ Branch 80 → 470 not taken.
434 indices.push_back(builder.getInt32(index));
117
1/2
✓ Branch 95 → 96 taken 156 times.
✗ Branch 95 → 472 not taken.
156 thisPtr = insertInBoundsGEP(structTy, thisPtr, indices);
118 156 }
119 // Auto de-reference pointer and get new struct type
120
1/2
✓ Branch 100 → 101 taken 14178 times.
✗ Branch 100 → 483 not taken.
14178 autoDeReferencePtr(thisPtr, fieldEntryType);
121
2/4
✓ Branch 101 → 102 taken 14178 times.
✗ Branch 101 → 482 not taken.
✓ Branch 102 → 103 taken 14178 times.
✗ Branch 102 → 482 not taken.
14178 structTy = fieldEntryType.getBase().toLLVMType(sourceFile);
122 14178 }
123
124 // Add 'this' pointer to the front of the argument list
125
1/2
✓ Branch 108 → 109 taken 62435 times.
✗ Branch 108 → 488 not taken.
62435 argValues.push_back(thisPtr);
126 }
127
128
2/2
✓ Branch 111 → 112 taken 19517 times.
✓ Branch 111 → 123 taken 111312 times.
130829 if (data.isCtorCall()) {
129
1/2
✗ Branch 113 → 114 not taken.
✓ Branch 113 → 115 taken 19517 times.
19517 assert(!data.isMethodCall());
130
131
1/2
✓ Branch 115 → 116 taken 19517 times.
✗ Branch 115 → 630 not taken.
19517 llvm::Type *thisType = spiceFunc->thisType.toLLVMType(sourceFile);
132
1/2
✓ Branch 119 → 120 taken 19517 times.
✗ Branch 119 → 489 not taken.
19517 thisPtr = insertAlloca(thisType);
133
134 // Add 'this' pointer to the front of the argument list
135
1/2
✓ Branch 122 → 123 taken 19517 times.
✗ Branch 122 → 630 not taken.
19517 argValues.push_back(thisPtr);
136 }
137
138 // Every callable behind a fat function pointer uses the same calling convention: the capture-struct pointer
139 // (fat ptr slot 1) is always passed as the leading argument, regardless of whether the target actually captures.
140 // Non-capturing lambdas and plain function references ignore it. This lets a lambda be called without the call
141 // site knowing statically whether it captures (e.g. when it was retrieved from the std Lambda wrapper).
142 130829 llvm::Value *fctPtr = nullptr;
143
2/2
✓ Branch 124 → 125 taken 229 times.
✓ Branch 124 → 147 taken 130600 times.
130829 if (data.isFctPtrCall()) {
144
1/2
✓ Branch 125 → 126 taken 229 times.
✗ Branch 125 → 513 not taken.
229 llvm::Value *fatPtr = getAddress(firstFragEntry);
145 // Load fctPtr
146
1/2
✓ Branch 129 → 130 taken 229 times.
✗ Branch 129 → 495 not taken.
229 fctPtr = insertStructGEP(llvmTypes.lambdaFatPtrType, fatPtr, 0);
147 // Load the captures pointer and add it to the argument list
148
1/2
✓ Branch 135 → 136 taken 229 times.
✗ Branch 135 → 501 not taken.
229 llvm::Value *capturesPtrPtr = insertStructGEP(llvmTypes.lambdaFatPtrType, fatPtr, 1);
149
3/6
✓ Branch 140 → 141 taken 229 times.
✗ Branch 140 → 509 not taken.
✓ Branch 141 → 142 taken 229 times.
✗ Branch 141 → 507 not taken.
✓ Branch 142 → 143 taken 229 times.
✗ Branch 142 → 507 not taken.
229 llvm::Value *capturesPtr = insertLoad(builder.getPtrTy(), capturesPtrPtr, false, CAPTURES_PARAM_NAME);
150
1/2
✓ Branch 145 → 146 taken 229 times.
✗ Branch 145 → 513 not taken.
229 argValues.push_back(capturesPtr);
151 }
152
153 // Get arg values
154
2/2
✓ Branch 147 → 148 taken 95697 times.
✓ Branch 147 → 246 taken 35132 times.
130829 if (node->hasArgs) {
155
1/2
✓ Branch 148 → 149 taken 95697 times.
✗ Branch 148 → 545 not taken.
95697 const std::vector<ExprNode *> args = node->argLst->args;
156
1/2
✓ Branch 150 → 151 taken 95697 times.
✗ Branch 150 → 543 not taken.
95697 argValues.reserve(args.size());
157 95697 const QualTypeList paramSTypes =
158
6/10
✓ Branch 152 → 153 taken 177 times.
✓ Branch 152 → 156 taken 95520 times.
✓ Branch 153 → 154 taken 177 times.
✗ Branch 153 → 514 not taken.
✓ Branch 154 → 155 taken 177 times.
✗ Branch 154 → 514 not taken.
✓ Branch 155 → 157 taken 177 times.
✗ Branch 155 → 514 not taken.
✓ Branch 156 → 157 taken 95520 times.
✗ Branch 156 → 514 not taken.
95697 data.isFctPtrCall() ? firstFragEntry->getQualType().getBase().getFunctionParamTypes() : spiceFunc->getParamTypes();
159
1/2
✗ Branch 159 → 160 not taken.
✓ Branch 159 → 161 taken 95697 times.
95697 assert(paramSTypes.size() == args.size());
160
2/2
✓ Branch 242 → 162 taken 156915 times.
✓ Branch 242 → 243 taken 95697 times.
252612 for (size_t i = 0; i < args.size(); i++) {
161
1/2
✓ Branch 162 → 163 taken 156915 times.
✗ Branch 162 → 540 not taken.
156915 ExprNode *argNode = args.at(i);
162
1/2
✓ Branch 163 → 164 taken 156915 times.
✗ Branch 163 → 540 not taken.
156915 const auto &[copyCtor] = node->argLst->argInfos.at(i);
163
164
1/2
✓ Branch 164 → 165 taken 156915 times.
✗ Branch 164 → 540 not taken.
156915 const QualType &expectedSTy = paramSTypes.at(i);
165
1/2
✓ Branch 165 → 166 taken 156915 times.
✗ Branch 165 → 540 not taken.
156915 const QualType &actualSTy = argNode->getEvaluatedSymbolType(manIdx);
166
167 172531 const auto matchFct = [](QualType expectedTy, QualType actualTy) {
168
2/2
✓ Branch 3 → 4 taken 156391 times.
✓ Branch 3 → 5 taken 16140 times.
172531 if (expectedTy.matches(actualTy, false, true, true))
169 156391 return true;
170
171 // Unwrap as far as possible and remove reference wrappers if possible
172 16140 QualType::unwrapBoth(expectedTy, actualTy);
173 16140 return expectedTy.matchesInterfaceImplementedByStruct(actualTy);
174 };
175
176
3/4
✓ Branch 166 → 167 taken 156915 times.
✗ Branch 166 → 540 not taken.
✓ Branch 167 → 168 taken 140817 times.
✓ Branch 167 → 209 taken 16098 times.
156915 if (matchFct(expectedSTy, actualSTy)) {
177 // Resolve address if actual type is reference or a decaying array, otherwise value
178
8/10
✓ Branch 168 → 169 taken 140817 times.
✗ Branch 168 → 540 not taken.
✓ Branch 169 → 170 taken 133268 times.
✓ Branch 169 → 172 taken 7549 times.
✓ Branch 170 → 171 taken 133268 times.
✗ Branch 170 → 540 not taken.
✓ Branch 171 → 172 taken 32 times.
✓ Branch 171 → 173 taken 133236 times.
✓ Branch 174 → 175 taken 7581 times.
✓ Branch 174 → 178 taken 133236 times.
140817 if (actualSTy.isRef() || expectedSTy.isDecayedArray()) {
179
2/4
✓ Branch 175 → 176 taken 7581 times.
✗ Branch 175 → 515 not taken.
✓ Branch 176 → 177 taken 7581 times.
✗ Branch 176 → 515 not taken.
7581 argValues.push_back(resolveAddress(argNode));
180
2/2
✓ Branch 178 → 179 taken 32 times.
✓ Branch 178 → 206 taken 133204 times.
133236 } else if (copyCtor) {
181
2/4
✓ Branch 179 → 180 taken 32 times.
✗ Branch 179 → 535 not taken.
✗ Branch 180 → 181 not taken.
✓ Branch 180 → 182 taken 32 times.
32 assert(!actualSTy.isTriviallyCopyable(node));
182
1/2
✓ Branch 182 → 183 taken 32 times.
✗ Branch 182 → 535 not taken.
32 llvm::Value *originalPtr = resolveAddress(argNode);
183
184 // Generate copy ctor call
185
1/2
✓ Branch 183 → 184 taken 32 times.
✗ Branch 183 → 535 not taken.
32 llvm::Type *valueType = actualSTy.toLLVMType(sourceFile);
186
2/4
✓ Branch 186 → 187 taken 32 times.
✗ Branch 186 → 518 not taken.
✓ Branch 187 → 188 taken 32 times.
✗ Branch 187 → 516 not taken.
32 llvm::Value *valueCopyPtr = insertAlloca(valueType, "arg.copy");
187
2/4
✓ Branch 192 → 193 taken 32 times.
✗ Branch 192 → 524 not taken.
✓ Branch 193 → 194 taken 32 times.
✗ Branch 193 → 522 not taken.
96 generateCtorOrDtorCall(valueCopyPtr, copyCtor, {originalPtr});
188
1/2
✓ Branch 199 → 200 taken 32 times.
✗ Branch 199 → 529 not taken.
32 llvm::Value *newValue = insertLoad(valueType, valueCopyPtr);
189
190 // Attach address of copy to anonymous symbol
191
1/2
✓ Branch 202 → 203 taken 32 times.
✗ Branch 202 → 535 not taken.
32 const SymbolTableEntry *anonymousSymbol = currentScope->symbolTable.lookupAnonymous(argNode, SIZE_MAX);
192
1/2
✓ Branch 203 → 204 taken 32 times.
✗ Branch 203 → 535 not taken.
32 updateAddress(anonymousSymbol, valueCopyPtr);
193
194
1/2
✓ Branch 204 → 205 taken 32 times.
✗ Branch 204 → 535 not taken.
32 argValues.push_back(newValue);
195 } else {
196
2/4
✓ Branch 206 → 207 taken 133204 times.
✗ Branch 206 → 536 not taken.
✓ Branch 207 → 208 taken 133204 times.
✗ Branch 207 → 536 not taken.
133204 argValues.push_back(resolveValue(argNode));
197 }
198
8/12
✓ Branch 209 → 210 taken 16098 times.
✗ Branch 209 → 540 not taken.
✓ Branch 210 → 211 taken 14214 times.
✓ Branch 210 → 215 taken 1884 times.
✓ Branch 211 → 212 taken 14214 times.
✗ Branch 211 → 540 not taken.
✓ Branch 212 → 213 taken 14214 times.
✗ Branch 212 → 540 not taken.
✓ Branch 213 → 214 taken 14214 times.
✗ Branch 213 → 215 not taken.
✓ Branch 216 → 217 taken 14214 times.
✓ Branch 216 → 220 taken 1884 times.
16098 } else if (expectedSTy.isRef() && matchFct(expectedSTy.getContained(), actualSTy)) { // Matches with ref
199
1/2
✓ Branch 217 → 218 taken 14214 times.
✗ Branch 217 → 537 not taken.
14214 llvm::Value *argAddress = resolveAddress(argNode);
200
1/2
✓ Branch 218 → 219 taken 14214 times.
✗ Branch 218 → 537 not taken.
14214 argValues.push_back(argAddress);
201
8/12
✓ Branch 220 → 221 taken 1884 times.
✗ Branch 220 → 540 not taken.
✓ Branch 221 → 222 taken 1402 times.
✓ Branch 221 → 226 taken 482 times.
✓ Branch 222 → 223 taken 1402 times.
✗ Branch 222 → 540 not taken.
✓ Branch 223 → 224 taken 1402 times.
✗ Branch 223 → 540 not taken.
✓ Branch 224 → 225 taken 1402 times.
✗ Branch 224 → 226 not taken.
✓ Branch 227 → 228 taken 1402 times.
✓ Branch 227 → 231 taken 482 times.
1884 } else if (actualSTy.isRef() && matchFct(expectedSTy, actualSTy.getContained())) { // Matches with ref
202
1/2
✓ Branch 228 → 229 taken 1402 times.
✗ Branch 228 → 538 not taken.
1402 llvm::Value *argAddress = resolveValue(argNode);
203
1/2
✓ Branch 229 → 230 taken 1402 times.
✗ Branch 229 → 538 not taken.
1402 argValues.push_back(argAddress);
204 } else { // Need implicit cast
205
1/2
✓ Branch 231 → 232 taken 482 times.
✗ Branch 231 → 540 not taken.
482 llvm::Value *argAddress = resolveAddress(argNode);
206
2/4
✓ Branch 232 → 233 taken 482 times.
✗ Branch 232 → 539 not taken.
✓ Branch 233 → 234 taken 482 times.
✗ Branch 233 → 539 not taken.
482 argValues.push_back(doImplicitCast(argAddress, expectedSTy, actualSTy));
207 }
208
209 // Decayed array params expect the address of a writable array, which not all of the paths above produce
210
3/4
✓ Branch 236 → 237 taken 156915 times.
✗ Branch 236 → 540 not taken.
✓ Branch 237 → 238 taken 32 times.
✓ Branch 237 → 240 taken 156883 times.
156915 if (llvm::Value *&argValue = argValues.back(); expectedSTy.isDecayedArray())
211
1/2
✓ Branch 238 → 239 taken 32 times.
✗ Branch 238 → 540 not taken.
32 argValue = materializeDecayedArrayArg(argValue, expectedSTy);
212 }
213 95697 }
214
215 // Retrieve return and param types
216
1/2
✓ Branch 246 → 247 taken 130829 times.
✗ Branch 246 → 630 not taken.
130829 QualType returnSType(TY_DYN);
217 130829 QualTypeList paramSTypes;
218
2/2
✓ Branch 248 → 249 taken 229 times.
✓ Branch 248 → 262 taken 130600 times.
130829 if (data.isFctPtrCall()) {
219
4/6
✓ Branch 249 → 250 taken 229 times.
✗ Branch 249 → 628 not taken.
✓ Branch 250 → 251 taken 229 times.
✗ Branch 250 → 628 not taken.
✓ Branch 251 → 252 taken 77 times.
✓ Branch 251 → 256 taken 152 times.
229 if (firstFragEntry->getQualType().isBase(TY_FUNCTION))
220
3/6
✓ Branch 252 → 253 taken 77 times.
✗ Branch 252 → 546 not taken.
✓ Branch 253 → 254 taken 77 times.
✗ Branch 253 → 546 not taken.
✓ Branch 254 → 255 taken 77 times.
✗ Branch 254 → 546 not taken.
77 returnSType = firstFragEntry->getQualType().getBase().getFunctionReturnType();
221
3/6
✓ Branch 256 → 257 taken 229 times.
✗ Branch 256 → 547 not taken.
✓ Branch 257 → 258 taken 229 times.
✗ Branch 257 → 547 not taken.
✓ Branch 258 → 259 taken 229 times.
✗ Branch 258 → 547 not taken.
229 paramSTypes = firstFragEntry->getQualType().getBase().getFunctionParamTypes();
222 } else {
223 130600 returnSType = spiceFunc->returnType;
224
1/2
✓ Branch 262 → 263 taken 130600 times.
✗ Branch 262 → 549 not taken.
130600 paramSTypes = spiceFunc->getParamTypes();
225 }
226
227 // Function is not defined in the current module -> declare it
228 llvm::FunctionType *fctType;
229
3/4
✓ Branch 267 → 268 taken 130829 times.
✗ Branch 267 → 550 not taken.
✓ Branch 268 → 269 taken 101615 times.
✓ Branch 268 → 271 taken 29214 times.
130829 if (llvm::Function *fct = module->getFunction(mangledName)) {
230
1/2
✓ Branch 269 → 270 taken 101615 times.
✗ Branch 269 → 628 not taken.
101615 fctType = fct->getFunctionType();
231 } else {
232 // Get returnType
233
1/2
✓ Branch 271 → 272 taken 29214 times.
✗ Branch 271 → 559 not taken.
29214 llvm::Type *returnType = builder.getVoidTy();
234
3/4
✓ Branch 272 → 273 taken 29214 times.
✗ Branch 272 → 559 not taken.
✓ Branch 273 → 274 taken 18027 times.
✓ Branch 273 → 276 taken 11187 times.
29214 if (!returnSType.is(TY_DYN))
235
1/2
✓ Branch 274 → 275 taken 18027 times.
✗ Branch 274 → 559 not taken.
18027 returnType = returnSType.toLLVMType(sourceFile);
236
237 // Get arg types
238 29214 std::vector<llvm::Type *> argTypes;
239
6/6
✓ Branch 277 → 278 taken 11470 times.
✓ Branch 277 → 280 taken 17744 times.
✓ Branch 279 → 280 taken 4001 times.
✓ Branch 279 → 281 taken 7469 times.
✓ Branch 282 → 283 taken 21745 times.
✓ Branch 282 → 286 taken 7469 times.
29214 if (data.isMethodCall() || data.isCtorCall())
240
2/4
✓ Branch 283 → 284 taken 21745 times.
✗ Branch 283 → 551 not taken.
✓ Branch 284 → 285 taken 21745 times.
✗ Branch 284 → 551 not taken.
21745 argTypes.push_back(builder.getPtrTy()); // This pointer
241
2/2
✓ Branch 287 → 288 taken 229 times.
✓ Branch 287 → 291 taken 28985 times.
29214 if (data.isFctPtrCall())
242
2/4
✓ Branch 288 → 289 taken 229 times.
✗ Branch 288 → 552 not taken.
✓ Branch 289 → 290 taken 229 times.
✗ Branch 289 → 552 not taken.
229 argTypes.push_back(builder.getPtrTy()); // Capture pointer (always present in the uniform lambda ABI)
243
2/2
✓ Branch 306 → 293 taken 25957 times.
✓ Branch 306 → 307 taken 29214 times.
84385 for (const QualType &paramType : paramSTypes)
244
2/4
✓ Branch 295 → 296 taken 25957 times.
✗ Branch 295 → 553 not taken.
✓ Branch 296 → 297 taken 25957 times.
✗ Branch 296 → 553 not taken.
25957 argTypes.push_back(paramType.getParamLLVMType(sourceFile));
245
246
1/2
✓ Branch 308 → 309 taken 29214 times.
✗ Branch 308 → 555 not taken.
29214 fctType = llvm::FunctionType::get(returnType, argTypes, false);
247
7/8
✓ Branch 310 → 311 taken 28985 times.
✓ Branch 310 → 314 taken 229 times.
✓ Branch 311 → 312 taken 28985 times.
✗ Branch 311 → 557 not taken.
✓ Branch 312 → 313 taken 24910 times.
✓ Branch 312 → 314 taken 4075 times.
✓ Branch 315 → 316 taken 24910 times.
✓ Branch 315 → 319 taken 4304 times.
29214 if (!data.isFctPtrCall() && !data.isVirtualMethodCall())
248
1/2
✓ Branch 317 → 318 taken 24910 times.
✗ Branch 317 → 556 not taken.
24910 module->getOrInsertFunction(mangledName, fctType);
249 29214 }
250
1/2
✗ Branch 321 → 322 not taken.
✓ Branch 321 → 323 taken 130829 times.
130829 assert(fctType != nullptr);
251
252 llvm::CallInst *callInst;
253
3/4
✓ Branch 323 → 324 taken 130829 times.
✗ Branch 323 → 628 not taken.
✓ Branch 324 → 325 taken 4075 times.
✓ Branch 324 → 357 taken 126754 times.
130829 if (data.isVirtualMethodCall()) {
254
1/2
✗ Branch 325 → 326 not taken.
✓ Branch 325 → 327 taken 4075 times.
4075 assert(data.callee->isVirtual);
255
1/2
✗ Branch 327 → 328 not taken.
✓ Branch 327 → 329 taken 4075 times.
4075 assert(thisPtr != nullptr);
256 // Load VTable
257
3/6
✓ Branch 331 → 332 taken 4075 times.
✗ Branch 331 → 562 not taken.
✓ Branch 332 → 333 taken 4075 times.
✗ Branch 332 → 560 not taken.
✓ Branch 333 → 334 taken 4075 times.
✗ Branch 333 → 560 not taken.
4075 llvm::Value *vtablePtr = insertLoad(builder.getPtrTy(), thisPtr, false, "vtable.addr");
258 4075 const size_t vtableIndex = data.callee->vtableIndex;
259 // Lookup function pointer in VTable
260
4/8
✓ Branch 338 → 339 taken 4075 times.
✗ Branch 338 → 570 not taken.
✓ Branch 339 → 340 taken 4075 times.
✗ Branch 339 → 566 not taken.
✓ Branch 341 → 342 taken 4075 times.
✗ Branch 341 → 566 not taken.
✓ Branch 342 → 343 taken 4075 times.
✗ Branch 342 → 566 not taken.
8150 fctPtr = insertInBoundsGEP(builder.getPtrTy(), vtablePtr, builder.getInt64(vtableIndex), "vfct.addr");
261
3/6
✓ Branch 347 → 348 taken 4075 times.
✗ Branch 347 → 576 not taken.
✓ Branch 348 → 349 taken 4075 times.
✗ Branch 348 → 574 not taken.
✓ Branch 349 → 350 taken 4075 times.
✗ Branch 349 → 574 not taken.
4075 llvm::Value *fct = insertLoad(builder.getPtrTy(), fctPtr, false, "fct");
262
263 // Generate function call
264
2/4
✓ Branch 352 → 353 taken 4075 times.
✗ Branch 352 → 582 not taken.
✓ Branch 355 → 356 taken 4075 times.
✗ Branch 355 → 580 not taken.
4075 callInst = builder.CreateCall({fctType, fct}, argValues);
265
2/2
✓ Branch 358 → 359 taken 229 times.
✓ Branch 358 → 378 taken 126525 times.
126754 } else if (data.isFctPtrCall()) {
266
1/2
✗ Branch 359 → 360 not taken.
✓ Branch 359 → 361 taken 229 times.
229 assert(firstFragEntry != nullptr);
267
1/2
✓ Branch 361 → 362 taken 229 times.
✗ Branch 361 → 592 not taken.
229 QualType firstFragType = firstFragEntry->getQualType();
268
1/2
✗ Branch 362 → 363 not taken.
✓ Branch 362 → 365 taken 229 times.
229 if (!fctPtr)
269 fctPtr = getAddress(firstFragEntry);
270
1/2
✓ Branch 365 → 366 taken 229 times.
✗ Branch 365 → 592 not taken.
229 autoDeReferencePtr(fctPtr, firstFragType);
271
3/6
✓ Branch 368 → 369 taken 229 times.
✗ Branch 368 → 585 not taken.
✓ Branch 369 → 370 taken 229 times.
✗ Branch 369 → 583 not taken.
✓ Branch 370 → 371 taken 229 times.
✗ Branch 370 → 583 not taken.
229 llvm::Value *fct = insertLoad(builder.getPtrTy(), fctPtr, false, "fct");
272
273 // Generate function call
274
2/4
✓ Branch 373 → 374 taken 229 times.
✗ Branch 373 → 591 not taken.
✓ Branch 376 → 377 taken 229 times.
✗ Branch 376 → 589 not taken.
229 callInst = builder.CreateCall({fctType, fct}, argValues);
275 } else {
276 // Get callee function
277
1/2
✓ Branch 379 → 380 taken 126525 times.
✗ Branch 379 → 593 not taken.
126525 llvm::Function *callee = module->getFunction(mangledName);
278
1/2
✗ Branch 380 → 381 not taken.
✓ Branch 380 → 382 taken 126525 times.
126525 assert(callee != nullptr);
279
280 // Generate function call
281
3/6
✓ Branch 382 → 383 taken 126525 times.
✗ Branch 382 → 596 not taken.
✓ Branch 384 → 385 taken 126525 times.
✗ Branch 384 → 594 not taken.
✓ Branch 385 → 386 taken 126525 times.
✗ Branch 385 → 594 not taken.
126525 callInst = builder.CreateCall(callee, argValues);
282 }
283
284 // Set argument and return value attributes
285
2/2
✓ Branch 388 → 389 taken 130600 times.
✓ Branch 388 → 391 taken 229 times.
130829 if (!data.isFctPtrCall()) {
286
1/2
✓ Branch 389 → 390 taken 130600 times.
✗ Branch 389 → 628 not taken.
130600 setCallArgAttrs(callInst, spiceFunc, paramSTypes);
287
1/2
✓ Branch 390 → 391 taken 130600 times.
✗ Branch 390 → 628 not taken.
130600 setCallReturnValAttrs(callInst, returnSType);
288 }
289
290 // Start a fresh error return trace at this call site, since it is where a new error was just put into existence
291
2/2
✓ Branch 391 → 392 taken 10 times.
✓ Branch 391 → 416 taken 130819 times.
130829 if (node->isErrorTraceOrigin) {
292
1/2
✓ Branch 392 → 393 taken 10 times.
✗ Branch 392 → 628 not taken.
10 llvm::Function *resetFct = stdFunctionManager.getErrTraceResetFct();
293 llvm::Constant *signature =
294
3/6
✓ Branch 393 → 394 taken 10 times.
✗ Branch 393 → 605 not taken.
✓ Branch 396 → 397 taken 10 times.
✗ Branch 396 → 599 not taken.
✓ Branch 397 → 398 taken 10 times.
✗ Branch 397 → 597 not taken.
30 createGlobalStringConst("errtrace.origin.sig.", node->getEnclosingFunctionSignature(manIdx), node->codeLoc);
295
3/6
✓ Branch 401 → 402 taken 10 times.
✗ Branch 401 → 614 not taken.
✓ Branch 404 → 405 taken 10 times.
✗ Branch 404 → 608 not taken.
✓ Branch 405 → 406 taken 10 times.
✗ Branch 405 → 606 not taken.
30 llvm::Constant *fileName = createGlobalStringConst("errtrace.origin.file.", node->codeLoc.toPrettyFilePath(), node->codeLoc);
296
1/2
✓ Branch 409 → 410 taken 10 times.
✗ Branch 409 → 628 not taken.
10 llvm::Value *line = builder.getInt32(node->codeLoc.line);
297
1/2
✓ Branch 410 → 411 taken 10 times.
✗ Branch 410 → 628 not taken.
10 llvm::Value *column = builder.getInt32(node->codeLoc.col);
298
3/6
✓ Branch 411 → 412 taken 10 times.
✗ Branch 411 → 618 not taken.
✓ Branch 413 → 414 taken 10 times.
✗ Branch 413 → 615 not taken.
✓ Branch 414 → 415 taken 10 times.
✗ Branch 414 → 615 not taken.
10 builder.CreateCall(resetFct, {signature, fileName, line, column});
299 }
300
301 // Attach address to anonymous symbol to keep track of de-allocation
302 130829 const SymbolTableEntry *anonymousSymbol = nullptr;
303 130829 llvm::Value *resultPtr = nullptr;
304
7/8
✓ Branch 416 → 417 taken 130829 times.
✗ Branch 416 → 628 not taken.
✓ Branch 417 → 418 taken 118869 times.
✓ Branch 417 → 420 taken 11960 times.
✓ Branch 419 → 420 taken 19517 times.
✓ Branch 419 → 421 taken 99352 times.
✓ Branch 422 → 423 taken 31477 times.
✓ Branch 422 → 437 taken 99352 times.
130829 if (returnSType.is(TY_STRUCT) || data.isCtorCall()) {
305
1/2
✓ Branch 423 → 424 taken 31477 times.
✗ Branch 423 → 628 not taken.
31477 anonymousSymbol = currentScope->symbolTable.lookupAnonymous(node);
306
2/2
✓ Branch 424 → 425 taken 4077 times.
✓ Branch 424 → 437 taken 27400 times.
31477 if (anonymousSymbol != nullptr) {
307
2/2
✓ Branch 426 → 427 taken 2527 times.
✓ Branch 426 → 428 taken 1550 times.
4077 if (data.isCtorCall()) {
308
1/2
✓ Branch 427 → 437 taken 2527 times.
✗ Branch 427 → 628 not taken.
2527 updateAddress(anonymousSymbol, thisPtr);
309 } else {
310
1/2
✓ Branch 432 → 433 taken 1550 times.
✗ Branch 432 → 619 not taken.
1550 resultPtr = insertAlloca(callInst->getType());
311
1/2
✓ Branch 435 → 436 taken 1550 times.
✗ Branch 435 → 628 not taken.
1550 insertStore(callInst, resultPtr);
312
1/2
✓ Branch 436 → 437 taken 1550 times.
✗ Branch 436 → 628 not taken.
1550 updateAddress(anonymousSymbol, resultPtr);
313 }
314 }
315 }
316
317 // In case this is a constructor call, return the thisPtr as pointer
318
2/2
✓ Branch 438 → 439 taken 19517 times.
✓ Branch 438 → 442 taken 111312 times.
130829 if (data.isCtorCall())
319
1/2
✓ Branch 439 → 440 taken 19517 times.
✗ Branch 439 → 625 not taken.
39034 return LLVMExprResult{.ptr = thisPtr, .refPtr = resultPtr, .entry = anonymousSymbol};
320
321 // In case this is a callee, returning a reference, return the address
322
3/4
✓ Branch 442 → 443 taken 111312 times.
✗ Branch 442 → 628 not taken.
✓ Branch 443 → 444 taken 7686 times.
✓ Branch 443 → 447 taken 103626 times.
111312 if (returnSType.isRef())
323
1/2
✓ Branch 444 → 445 taken 7686 times.
✗ Branch 444 → 626 not taken.
15372 return LLVMExprResult{.ptr = callInst, .refPtr = resultPtr, .entry = anonymousSymbol};
324
325 // Otherwise return the value
326
1/2
✓ Branch 447 → 448 taken 103626 times.
✗ Branch 447 → 627 not taken.
207252 return LLVMExprResult{.value = callInst, .ptr = resultPtr, .entry = anonymousSymbol};
327 130829 }
328
329 130600 void IRGenerator::setCallArgAttrs(llvm::CallInst *callInst, const Function *spiceFunc, const QualTypeList &paramSTypes) const {
330 130600 const bool isFctPtr = spiceFunc == nullptr;
331
3/4
✓ Branch 2 → 3 taken 130600 times.
✗ Branch 2 → 6 not taken.
✓ Branch 4 → 5 taken 81952 times.
✓ Branch 4 → 6 taken 48648 times.
130600 const bool isMethod = !isFctPtr && !spiceFunc->thisType.is(TY_DYN);
332
3/4
✓ Branch 7 → 8 taken 48648 times.
✓ Branch 7 → 9 taken 81952 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 11 taken 48648 times.
130600 const size_t expectedParamCount = isMethod || isFctPtr ? paramSTypes.size() + 1 : paramSTypes.size();
333
1/2
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 130600 times.
130600 assert(callInst->arg_size() == expectedParamCount);
334
2/2
✓ Branch 48 → 16 taken 238638 times.
✓ Branch 48 → 49 taken 130600 times.
369238 for (size_t i = 0; i < expectedParamCount; i++) {
335
8/10
✓ Branch 16 → 17 taken 129089 times.
✓ Branch 16 → 20 taken 109549 times.
✓ Branch 17 → 18 taken 81952 times.
✓ Branch 17 → 20 taken 47137 times.
✓ Branch 18 → 19 taken 81952 times.
✗ Branch 18 → 52 not taken.
✓ Branch 20 → 21 taken 63479 times.
✓ Branch 20 → 22 taken 93207 times.
✓ Branch 23 → 24 taken 156686 times.
✗ Branch 23 → 52 not taken.
238638 const QualType &paramType = i == 0 && isMethod ? spiceFunc->thisType.toPtr(nullptr) : paramSTypes.at(isMethod ? i - 1 : i);
336
337 // NoUndef attribute
338
1/2
✓ Branch 25 → 26 taken 238638 times.
✗ Branch 25 → 52 not taken.
238638 callInst->addParamAttr(i, llvm::Attribute::NoUndef);
339
340
3/4
✓ Branch 26 → 27 taken 238638 times.
✗ Branch 26 → 52 not taken.
✓ Branch 27 → 28 taken 112825 times.
✓ Branch 27 → 44 taken 125813 times.
238638 if (paramType.isPtr()) {
341 // NonNull attribute
342
4/4
✓ Branch 28 → 29 taken 96692 times.
✓ Branch 28 → 31 taken 16133 times.
✓ Branch 29 → 30 taken 81952 times.
✓ Branch 29 → 31 taken 14740 times.
112825 if (i == 0 && isMethod)
343
1/2
✓ Branch 30 → 31 taken 81952 times.
✗ Branch 30 → 52 not taken.
81952 callInst->addParamAttr(i, llvm::Attribute::NonNull);
344 // Dereferenceable attribute
345
2/4
✓ Branch 31 → 32 taken 112825 times.
✗ Branch 31 → 50 not taken.
✓ Branch 32 → 33 taken 112825 times.
✗ Branch 32 → 50 not taken.
112825 llvm::Type *pointeeType = paramType.getContained().toLLVMType(sourceFile);
346
1/2
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 112825 times.
112825 assert(pointeeType != nullptr);
347
4/8
✓ Branch 35 → 36 taken 112825 times.
✗ Branch 35 → 51 not taken.
✓ Branch 37 → 38 taken 112825 times.
✗ Branch 37 → 51 not taken.
✓ Branch 38 → 39 taken 112825 times.
✗ Branch 38 → 51 not taken.
✓ Branch 39 → 40 taken 112825 times.
✗ Branch 39 → 51 not taken.
112825 callInst->addDereferenceableParamAttr(i, callInst->getModule()->getDataLayout().getTypeStoreSize(pointeeType));
348 // Alignment attribute
349
3/6
✓ Branch 41 → 42 taken 112825 times.
✗ Branch 41 → 52 not taken.
✓ Branch 42 → 43 taken 112825 times.
✗ Branch 42 → 52 not taken.
✓ Branch 43 → 44 taken 112825 times.
✗ Branch 43 → 52 not taken.
112825 callInst->addParamAttr(i, llvm::Attribute::getWithAlignment(context, module->getDataLayout().getABITypeAlign(pointeeType)));
350 }
351
352 // ZExt or SExt attribute
353
3/4
✓ Branch 44 → 45 taken 238638 times.
✗ Branch 44 → 52 not taken.
✓ Branch 45 → 46 taken 20930 times.
✓ Branch 45 → 47 taken 217708 times.
238638 if (const llvm::Attribute::AttrKind extAttrKind = getExtAttrKindForType(paramType); extAttrKind != llvm::Attribute::None)
354
1/2
✓ Branch 46 → 47 taken 20930 times.
✗ Branch 46 → 52 not taken.
20930 callInst->addParamAttr(i, extAttrKind);
355 }
356 130600 }
357
358 130600 void IRGenerator::setCallReturnValAttrs(llvm::CallInst *callInst, const QualType &returnType) const {
359
2/2
✓ Branch 3 → 4 taken 49882 times.
✓ Branch 3 → 5 taken 80718 times.
130600 if (returnType.is(TY_DYN))
360 49882 return;
361
362 // NoUndef attribute
363 80718 callInst->addRetAttr(llvm::Attribute::NoUndef);
364 // ZExt or SExt attribute
365
2/2
✓ Branch 7 → 8 taken 20678 times.
✓ Branch 7 → 9 taken 60040 times.
80718 if (const llvm::Attribute::AttrKind extAttrKind = getExtAttrKindForType(returnType); extAttrKind != llvm::Attribute::None)
366 20678 callInst->addRetAttr(extAttrKind);
367 }
368
369 632 std::any IRGenerator::visitArrayInitialization(const ArrayInitializationNode *node) {
370 // Return immediately if the initialization is empty
371
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 632 times.
632 if (node->actualSize == 0)
372 return LLVMExprResult{.node = node};
373
374 // Visit array items
375 632 bool canBeConstant = true;
376 632 std::vector<LLVMExprResult> itemResults;
377
1/2
✓ Branch 6 → 7 taken 632 times.
✗ Branch 6 → 148 not taken.
632 itemResults.reserve(node->actualSize);
378
2/2
✓ Branch 24 → 9 taken 3356 times.
✓ Branch 24 → 25 taken 632 times.
4620 for (const ExprNode *itemNode : node->itemLst->args) {
379
2/4
✓ Branch 11 → 12 taken 3356 times.
✗ Branch 11 → 110 not taken.
✓ Branch 12 → 13 taken 3356 times.
✗ Branch 12 → 108 not taken.
3356 auto item = std::any_cast<LLVMExprResult>(visit(itemNode));
380 3356 canBeConstant &= item.constant != nullptr;
381 3356 item.node = itemNode;
382
1/2
✓ Branch 14 → 15 taken 3356 times.
✗ Branch 14 → 111 not taken.
3356 itemResults.push_back(item);
383 }
384
385 // Get LLVM type of item and array
386
1/2
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 632 times.
632 assert(!itemResults.empty());
387
1/2
✓ Branch 29 → 30 taken 632 times.
✗ Branch 29 → 148 not taken.
632 const QualType &firstItemSTy = node->itemLst->args.front()->getEvaluatedSymbolType(manIdx);
388
1/2
✓ Branch 30 → 31 taken 632 times.
✗ Branch 30 → 148 not taken.
632 llvm::Type *itemType = firstItemSTy.toLLVMType(sourceFile);
389
1/2
✓ Branch 31 → 32 taken 632 times.
✗ Branch 31 → 148 not taken.
632 llvm::ArrayType *arrayType = llvm::ArrayType::get(itemType, node->actualSize);
390
391
2/2
✓ Branch 32 → 33 taken 628 times.
✓ Branch 32 → 66 taken 4 times.
632 if (canBeConstant) { // All items are constants, so we can create a global constant array
392 // Collect constants
393 628 std::vector<llvm::Constant *> constants;
394
2/2
✓ Branch 53 → 35 taken 3346 times.
✓ Branch 53 → 54 taken 628 times.
4602 for (const LLVMExprResult &exprResult : itemResults) {
395 // Delete potential constant globals, that were already created a layer below
396
2/2
✓ Branch 39 → 40 taken 44 times.
✓ Branch 39 → 43 taken 3302 times.
3346 if (exprResult.constant->getType()->isArrayTy())
397
3/6
✓ Branch 40 → 41 taken 44 times.
✗ Branch 40 → 113 not taken.
✓ Branch 41 → 42 taken 44 times.
✗ Branch 41 → 113 not taken.
✓ Branch 42 → 43 taken 44 times.
✗ Branch 42 → 113 not taken.
44 module->getNamedGlobal(exprResult.ptr->getName())->eraseFromParent();
398
1/2
✓ Branch 43 → 44 taken 3346 times.
✗ Branch 43 → 113 not taken.
3346 constants.push_back(exprResult.constant);
399 }
400
401 // Create global array
402
1/2
✓ Branch 55 → 56 taken 628 times.
✗ Branch 55 → 114 not taken.
628 llvm::Constant *constantArray = llvm::ConstantArray::get(arrayType, constants);
403
2/4
✓ Branch 58 → 59 taken 628 times.
✗ Branch 58 → 117 not taken.
✓ Branch 59 → 60 taken 628 times.
✗ Branch 59 → 115 not taken.
628 llvm::Value *arrayAddr = createGlobalConst(ANON_GLOBAL_ARRAY_NAME, constantArray);
404
405
1/2
✓ Branch 62 → 63 taken 628 times.
✗ Branch 62 → 121 not taken.
628 return LLVMExprResult{.constant = constantArray, .ptr = arrayAddr};
406 628 } else { // We have non-immediate values as items, so we need to take normal arrays as fallback
407
1/2
✓ Branch 69 → 70 taken 4 times.
✗ Branch 69 → 125 not taken.
4 llvm::Value *arrayAddr = insertAlloca(arrayType);
408
409 // Retrieve address of first item
410
2/4
✓ Branch 75 → 76 taken 4 times.
✗ Branch 75 → 131 not taken.
✓ Branch 77 → 78 taken 4 times.
✗ Branch 77 → 131 not taken.
4 llvm::Value *firstItemAddress = insertInBoundsGEP(arrayType, arrayAddr, builder.getInt64(0));
411
412 // Store all array items at their corresponding offsets
413 4 llvm::Value *currentItemAddress = firstItemAddress;
414
2/2
✓ Branch 100 → 81 taken 10 times.
✓ Branch 100 → 101 taken 4 times.
14 for (size_t i = 0; i < itemResults.size(); i++) {
415 10 LLVMExprResult &exprResult = itemResults[i];
416
1/2
✓ Branch 82 → 83 taken 10 times.
✗ Branch 82 → 148 not taken.
10 llvm::Value *itemValue = resolveValue(exprResult.node, exprResult);
417 // Retrieve current item address
418
2/2
✓ Branch 83 → 84 taken 6 times.
✓ Branch 83 → 93 taken 4 times.
10 if (i >= 1)
419
2/4
✓ Branch 87 → 88 taken 6 times.
✗ Branch 87 → 139 not taken.
✓ Branch 89 → 90 taken 6 times.
✗ Branch 89 → 139 not taken.
6 currentItemAddress = insertInBoundsGEP(itemType, currentItemAddress, builder.getInt64(1));
420 // Store the item value
421
3/4
✓ Branch 93 → 94 taken 6 times.
✓ Branch 93 → 96 taken 4 times.
✗ Branch 94 → 95 not taken.
✓ Branch 94 → 96 taken 6 times.
10 const bool storeVolatile = exprResult.entry != nullptr && exprResult.entry->isVolatile;
422
1/2
✓ Branch 97 → 98 taken 10 times.
✗ Branch 97 → 148 not taken.
10 insertStore(itemValue, currentItemAddress, storeVolatile);
423 }
424
425
1/2
✓ Branch 101 → 102 taken 4 times.
✗ Branch 101 → 147 not taken.
8 return LLVMExprResult{.ptr = arrayAddr};
426 }
427 632 }
428
429 3457 std::any IRGenerator::visitStructInstantiation(const StructInstantiationNode *node) {
430 // Get struct object
431
1/2
✓ Branch 2 → 3 taken 3457 times.
✗ Branch 2 → 206 not taken.
3457 const Struct *spiceStruct = node->instantiatedStructs.at(manIdx);
432
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3457 times.
3457 assert(spiceStruct != nullptr);
433 3457 const QualTypeList &fieldTypes = spiceStruct->fieldTypes;
434
435 // Can only be constant if none of the fields is of type reference
436
1/2
✓ Branch 5 → 6 taken 3457 times.
✗ Branch 5 → 206 not taken.
3457 bool canBeConstant = !spiceStruct->hasReferenceFields();
437
438 // Get struct type
439
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 3457 times.
3457 assert(spiceStruct->entry != nullptr);
440
3/6
✓ Branch 8 → 9 taken 3457 times.
✗ Branch 8 → 206 not taken.
✓ Branch 9 → 10 taken 3457 times.
✗ Branch 9 → 206 not taken.
✓ Branch 10 → 11 taken 3457 times.
✗ Branch 10 → 206 not taken.
3457 const auto structType = llvm::cast<llvm::StructType>(spiceStruct->entry->getQualType().toLLVMType(sourceFile));
441
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 3457 times.
3457 assert(structType != nullptr);
442
443
2/2
✓ Branch 13 → 14 taken 42 times.
✓ Branch 13 → 19 taken 3415 times.
3457 if (!node->fieldLst) {
444
2/4
✓ Branch 14 → 15 taken 42 times.
✗ Branch 14 → 206 not taken.
✓ Branch 15 → 16 taken 42 times.
✗ Branch 15 → 206 not taken.
42 llvm::Constant *constantStruct = getDefaultValueForSymbolType(spiceStruct->entry->getQualType());
445
1/2
✓ Branch 16 → 17 taken 42 times.
✗ Branch 16 → 152 not taken.
84 return LLVMExprResult{.constant = constantStruct};
446 }
447
448 // Visit struct field values
449 3415 std::vector<LLVMExprResult> fieldValueResults;
450
1/2
✓ Branch 20 → 21 taken 3415 times.
✗ Branch 20 → 204 not taken.
3415 fieldValueResults.reserve(spiceStruct->fieldTypes.size());
451
2/2
✓ Branch 38 → 23 taken 6910 times.
✓ Branch 38 → 39 taken 3415 times.
13740 for (const ExprNode *fieldValueNode : node->fieldLst->args) {
452
2/4
✓ Branch 25 → 26 taken 6910 times.
✗ Branch 25 → 155 not taken.
✓ Branch 26 → 27 taken 6910 times.
✗ Branch 26 → 153 not taken.
6910 auto fieldValue = std::any_cast<LLVMExprResult>(visit(fieldValueNode));
453 6910 fieldValue.node = fieldValueNode;
454
1/2
✓ Branch 28 → 29 taken 6910 times.
✗ Branch 28 → 156 not taken.
6910 fieldValueResults.push_back(fieldValue);
455 6910 canBeConstant &= fieldValue.constant != nullptr;
456 }
457
458
2/2
✓ Branch 39 → 40 taken 152 times.
✓ Branch 39 → 77 taken 3263 times.
3415 if (canBeConstant) { // All field values are constants, so we can create a global constant struct instantiation
459 // Collect constants
460 152 std::vector<llvm::Constant *> constants;
461 // For each interface a nullptr
462
1/2
✗ Branch 55 → 42 not taken.
✓ Branch 55 → 56 taken 152 times.
304 for (const QualType &interfaceType : spiceStruct->interfaceTypes)
463 constants.push_back(getDefaultValueForSymbolType(interfaceType));
464 // Constant value for each field
465
2/2
✓ Branch 70 → 58 taken 790 times.
✓ Branch 70 → 71 taken 152 times.
1094 for (const LLVMExprResult &exprResult : fieldValueResults)
466
1/2
✓ Branch 60 → 61 taken 790 times.
✗ Branch 60 → 160 not taken.
790 constants.push_back(exprResult.constant);
467
468 // Create global constant struct
469
1/2
✓ Branch 72 → 73 taken 152 times.
✗ Branch 72 → 161 not taken.
152 llvm::Constant *constantStruct = llvm::ConstantStruct::get(structType, constants);
470
471
1/2
✓ Branch 73 → 74 taken 152 times.
✗ Branch 73 → 162 not taken.
152 return LLVMExprResult{.constant = constantStruct};
472 152 } else { // We have at least one non-immediate value, so we need to take normal struct instantiation as fallback
473
1/2
✓ Branch 80 → 81 taken 3263 times.
✗ Branch 80 → 166 not taken.
3263 llvm::Value *structAddr = insertAlloca(structType);
474 3263 const size_t interfaceCount = spiceStruct->interfaceTypes.size();
475 3263 const size_t fieldCount = spiceStruct->fieldTypes.size();
476 3263 size_t i = 0;
477
478 // Store interface values at their corresponding offsets
479
1/2
✗ Branch 96 → 86 not taken.
✓ Branch 96 → 97 taken 3263 times.
3263 for (; i < interfaceCount; i++) {
480 const QualType &interfaceType = spiceStruct->interfaceTypes.at(i);
481 // Get field value
482 llvm::Value *itemValue = getDefaultValueForSymbolType(interfaceType);
483 // Get field address
484 llvm::Value *currentFieldAddress = insertStructGEP(structType, structAddr, i);
485 // Store the item value
486 insertStore(itemValue, currentFieldAddress);
487 }
488
489 // Store all field values at their corresponding offsets
490
2/2
✓ Branch 142 → 98 taken 6120 times.
✓ Branch 142 → 143 taken 3263 times.
9383 for (; i < interfaceCount + fieldCount; i++) {
491
1/2
✓ Branch 98 → 99 taken 6120 times.
✗ Branch 98 → 204 not taken.
6120 LLVMExprResult &exprResult = fieldValueResults.at(i);
492
1/2
✓ Branch 99 → 100 taken 6120 times.
✗ Branch 99 → 204 not taken.
6120 const QualType &fieldType = fieldTypes.at(i);
493 // Get field value
494 llvm::Value *itemValue;
495
3/4
✓ Branch 100 → 101 taken 6120 times.
✗ Branch 100 → 204 not taken.
✓ Branch 101 → 102 taken 8 times.
✓ Branch 101 → 104 taken 6112 times.
6120 if (fieldType.isRef()) {
496
1/2
✓ Branch 102 → 103 taken 8 times.
✗ Branch 102 → 204 not taken.
8 itemValue = resolveAddress(exprResult);
497
4/6
✓ Branch 104 → 105 taken 6112 times.
✗ Branch 104 → 204 not taken.
✓ Branch 105 → 106 taken 6112 times.
✗ Branch 105 → 204 not taken.
✓ Branch 106 → 107 taken 34 times.
✓ Branch 106 → 128 taken 6078 times.
6112 } else if (const Function *copyCtor = node->fieldCopyCtors.at(manIdx).at(i); copyCtor != nullptr) {
498 // Deep-copy the value into the field via its copy ctor: the source is not consumed/moved here, so the
499 // caller keeps its own copy alive and destructs it independently - a raw value copy would alias its
500 // owned heap resources and lead to a double-free once one of the two is destructed.
501
1/2
✓ Branch 107 → 108 taken 34 times.
✗ Branch 107 → 204 not taken.
34 llvm::Value *originalPtr = resolveAddress(exprResult);
502
1/2
✓ Branch 108 → 109 taken 34 times.
✗ Branch 108 → 204 not taken.
34 llvm::Type *fieldLLVMType = fieldType.toLLVMType(sourceFile);
503
2/4
✓ Branch 111 → 112 taken 34 times.
✗ Branch 111 → 180 not taken.
✓ Branch 112 → 113 taken 34 times.
✗ Branch 112 → 178 not taken.
34 llvm::Value *fieldCopyPtr = insertAlloca(fieldLLVMType, "field.copy");
504
2/4
✓ Branch 117 → 118 taken 34 times.
✗ Branch 117 → 186 not taken.
✓ Branch 118 → 119 taken 34 times.
✗ Branch 118 → 184 not taken.
102 generateCtorOrDtorCall(fieldCopyPtr, copyCtor, {originalPtr});
505
1/2
✓ Branch 124 → 125 taken 34 times.
✗ Branch 124 → 191 not taken.
34 itemValue = insertLoad(fieldLLVMType, fieldCopyPtr);
506 } else {
507
1/2
✓ Branch 128 → 129 taken 6078 times.
✗ Branch 128 → 204 not taken.
6078 itemValue = resolveValue(exprResult.node, exprResult);
508 }
509 // Get field address
510
1/2
✓ Branch 133 → 134 taken 6120 times.
✗ Branch 133 → 197 not taken.
6120 llvm::Value *currentFieldAddress = insertStructGEP(structType, structAddr, i);
511 // Store the item value
512
3/4
✓ Branch 136 → 137 taken 4148 times.
✓ Branch 136 → 139 taken 1972 times.
✗ Branch 137 → 138 not taken.
✓ Branch 137 → 139 taken 4148 times.
6120 const bool storeVolatile = exprResult.entry != nullptr && exprResult.entry->isVolatile;
513
1/2
✓ Branch 140 → 141 taken 6120 times.
✗ Branch 140 → 204 not taken.
6120 insertStore(itemValue, currentFieldAddress, storeVolatile);
514 }
515
516 // Attach address to anonymous symbol to keep track of de-allocation
517
1/2
✓ Branch 143 → 144 taken 3263 times.
✗ Branch 143 → 204 not taken.
3263 const SymbolTableEntry *returnSymbol = currentScope->symbolTable.lookupAnonymous(node);
518
1/2
✗ Branch 144 → 145 not taken.
✓ Branch 144 → 146 taken 3263 times.
3263 if (returnSymbol != nullptr)
519 updateAddress(returnSymbol, structAddr);
520
521
1/2
✓ Branch 146 → 147 taken 3263 times.
✗ Branch 146 → 203 not taken.
6526 return LLVMExprResult{.ptr = structAddr};
522 }
523 3415 }
524
525 97 std::any IRGenerator::visitLambdaFunc(const LambdaFuncNode *node) {
526
2/4
✓ Branch 2 → 3 taken 97 times.
✗ Branch 2 → 244 not taken.
✓ Branch 3 → 4 taken 97 times.
✗ Branch 3 → 244 not taken.
97 Function spiceFunc = node->manifestations.at(manIdx);
527 97 ParamInfoList paramInfoList;
528 97 std::vector<llvm::Type *> paramTypes;
529
530 // Change scope
531
2/4
✓ Branch 4 → 5 taken 97 times.
✗ Branch 4 → 177 not taken.
✓ Branch 5 → 6 taken 97 times.
✗ Branch 5 → 175 not taken.
97 Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId());
532
533 // Every lambda uniformly takes a leading capture-struct pointer as its first argument, even when it captures
534 // nothing. This keeps the calling convention of all lambdas (and plain function pointers) identical, so a lambda
535 // can be stored, retrieved and called without the call site knowing statically whether it captures. A
536 // non-capturing lambda simply ignores the passed (poison) pointer.
537 97 const CaptureMap &captures = bodyScope->symbolTable.captures;
538 97 const bool hasCaptures = !captures.empty();
539
3/4
✓ Branch 8 → 9 taken 18 times.
✓ Branch 8 → 11 taken 79 times.
✓ Branch 9 → 10 taken 18 times.
✗ Branch 9 → 238 not taken.
97 llvm::Type *capturesStructType = hasCaptures ? buildCapturesContainerType(captures) : nullptr;
540
1/2
✓ Branch 12 → 13 taken 97 times.
✗ Branch 12 → 178 not taken.
97 paramInfoList.emplace_back(CAPTURES_PARAM_NAME, nullptr);
541
2/4
✓ Branch 13 → 14 taken 97 times.
✗ Branch 13 → 179 not taken.
✓ Branch 14 → 15 taken 97 times.
✗ Branch 14 → 179 not taken.
97 paramTypes.push_back(builder.getPtrTy()); // The capture struct is always passed as pointer
542
543 // Visit parameters
544 97 size_t argIdx = 0;
545
2/2
✓ Branch 15 → 16 taken 89 times.
✓ Branch 15 → 34 taken 8 times.
97 if (node->hasParams) {
546 89 const size_t numOfParams = spiceFunc.paramList.size();
547
1/2
✓ Branch 17 → 18 taken 89 times.
✗ Branch 17 → 238 not taken.
89 paramInfoList.reserve(numOfParams);
548
1/2
✓ Branch 18 → 19 taken 89 times.
✗ Branch 18 → 238 not taken.
89 paramTypes.reserve(numOfParams);
549
2/2
✓ Branch 33 → 20 taken 115 times.
✓ Branch 33 → 34 taken 89 times.
204 for (; argIdx < numOfParams; argIdx++) {
550
1/2
✓ Branch 20 → 21 taken 115 times.
✗ Branch 20 → 183 not taken.
115 const DeclStmtNode *param = node->paramLst->params.at(argIdx);
551 // Get symbol table entry of param
552
1/2
✓ Branch 21 → 22 taken 115 times.
✗ Branch 21 → 183 not taken.
115 const SymbolTableEntry *paramSymbol = currentScope->lookupStrict(param->varName);
553
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 115 times.
115 assert(paramSymbol != nullptr);
554 // Retrieve type of param
555
3/6
✓ Branch 26 → 27 taken 115 times.
✗ Branch 26 → 182 not taken.
✓ Branch 27 → 28 taken 115 times.
✗ Branch 27 → 180 not taken.
✓ Branch 28 → 29 taken 115 times.
✗ Branch 28 → 180 not taken.
115 llvm::Type *paramType = spiceFunc.getParamTypes().at(argIdx).getParamLLVMType(sourceFile);
556 // Add it to the lists
557
1/2
✓ Branch 30 → 31 taken 115 times.
✗ Branch 30 → 183 not taken.
115 paramInfoList.emplace_back(param->varName, paramSymbol);
558
1/2
✓ Branch 31 → 32 taken 115 times.
✗ Branch 31 → 183 not taken.
115 paramTypes.push_back(paramType);
559 }
560 }
561
562 // Get return type
563
1/2
✓ Branch 34 → 35 taken 97 times.
✗ Branch 34 → 238 not taken.
97 llvm::Type *returnType = spiceFunc.returnType.toLLVMType(sourceFile);
564
565 // Create function or implement declared function
566
2/4
✓ Branch 35 → 36 taken 97 times.
✗ Branch 35 → 186 not taken.
✓ Branch 36 → 37 taken 97 times.
✗ Branch 36 → 184 not taken.
97 spiceFunc.mangleSuffix = "." + std::to_string(manIdx);
567
1/2
✓ Branch 40 → 41 taken 97 times.
✗ Branch 40 → 238 not taken.
97 const std::string mangledName = spiceFunc.getMangledName();
568
1/2
✓ Branch 42 → 43 taken 97 times.
✗ Branch 42 → 188 not taken.
97 llvm::FunctionType *funcType = llvm::FunctionType::get(returnType, paramTypes, false);
569
1/2
✓ Branch 44 → 45 taken 97 times.
✗ Branch 44 → 189 not taken.
97 module->getOrInsertFunction(mangledName, funcType);
570
1/2
✓ Branch 46 → 47 taken 97 times.
✗ Branch 46 → 190 not taken.
97 llvm::Function *lambda = module->getFunction(mangledName);
571
572 // Set attributes to function
573
1/2
✓ Branch 47 → 48 taken 97 times.
✗ Branch 47 → 236 not taken.
97 lambda->setLinkage(llvm::Function::InternalLinkage);
574 97 lambda->setDSOLocal(true);
575
1/2
✓ Branch 49 → 50 taken 97 times.
✗ Branch 49 → 236 not taken.
97 addCommonFctAttrs(lambda);
576
1/2
✓ Branch 50 → 51 taken 97 times.
✗ Branch 50 → 236 not taken.
97 enableFunctionInstrumentation(lambda);
577
578 // In case of captures, add attribute to captures argument
579
2/2
✓ Branch 51 → 52 taken 18 times.
✓ Branch 51 → 57 taken 79 times.
97 if (hasCaptures) {
580
1/2
✓ Branch 52 → 53 taken 18 times.
✗ Branch 52 → 236 not taken.
18 lambda->addParamAttr(0, llvm::Attribute::NoUndef);
581
1/2
✓ Branch 53 → 54 taken 18 times.
✗ Branch 53 → 236 not taken.
18 lambda->addParamAttr(0, llvm::Attribute::NonNull);
582
2/4
✓ Branch 55 → 56 taken 18 times.
✗ Branch 55 → 236 not taken.
✓ Branch 56 → 57 taken 18 times.
✗ Branch 56 → 236 not taken.
18 lambda->addDereferenceableParamAttr(0, module->getDataLayout().getPointerSize());
583 }
584
585 // Add debug info
586
1/2
✓ Branch 57 → 58 taken 97 times.
✗ Branch 57 → 236 not taken.
97 diGenerator.generateFunctionDebugInfo(lambda, &spiceFunc, true);
587
1/2
✓ Branch 58 → 59 taken 97 times.
✗ Branch 58 → 236 not taken.
97 diGenerator.setSourceLocation(node);
588
589 // Save alloca insert markers
590 97 llvm::BasicBlock *allocaInsertBlockOrig = allocaInsertBlock;
591 97 llvm::AllocaInst *allocaInsertInstOrig = allocaInsertInst;
592 97 llvm::BasicBlock *bOrig = builder.GetInsertBlock();
593
594 // Create entry block
595
1/2
✓ Branch 63 → 64 taken 97 times.
✗ Branch 63 → 191 not taken.
97 llvm::BasicBlock *bEntry = createBlock();
596
1/2
✓ Branch 66 → 67 taken 97 times.
✗ Branch 66 → 236 not taken.
97 switchToBlock(bEntry, lambda);
597
598 // Reset alloca insert markers to this block
599 97 allocaInsertBlock = bEntry;
600 97 allocaInsertInst = nullptr;
601
602 // Declare result variable
603
1/2
✓ Branch 69 → 70 taken 97 times.
✗ Branch 69 → 199 not taken.
291 const SymbolTableEntry *resultEntry = currentScope->lookupStrict(RETURN_VARIABLE_NAME);
604
1/2
✗ Branch 75 → 76 not taken.
✓ Branch 75 → 77 taken 97 times.
97 assert(resultEntry != nullptr);
605
2/4
✓ Branch 77 → 78 taken 97 times.
✗ Branch 77 → 236 not taken.
✗ Branch 78 → 79 not taken.
✓ Branch 78 → 93 taken 97 times.
97 if (resultEntry->isInitialized()) {
606 llvm::Value *resultAddr = insertAlloca(returnType, RETURN_VARIABLE_NAME);
607 updateAddress(resultEntry, resultAddr);
608 // Generate debug info
609 diGenerator.generateLocalVarDebugInfo(RETURN_VARIABLE_NAME, resultAddr);
610 }
611
612 // Store function argument values
613 97 llvm::Value *captureStructPtrPtr = nullptr;
614
3/4
✓ Branch 93 → 94 taken 97 times.
✗ Branch 93 → 222 not taken.
✓ Branch 119 → 96 taken 212 times.
✓ Branch 119 → 120 taken 97 times.
309 for (auto &arg : lambda->args()) {
615 // Get parameter info
616 212 const size_t argNumber = arg.getArgNo();
617
2/4
✓ Branch 97 → 98 taken 212 times.
✗ Branch 97 → 215 not taken.
✓ Branch 98 → 99 taken 212 times.
✗ Branch 98 → 215 not taken.
212 auto [paramName, paramSymbol] = paramInfoList.at(argNumber);
618 // Decayed array params already carry the address of the array, so they do not need a local copy
619
3/4
✓ Branch 101 → 102 taken 212 times.
✗ Branch 101 → 215 not taken.
✓ Branch 102 → 103 taken 2 times.
✓ Branch 102 → 104 taken 210 times.
212 if (bindDecayedArrayParam(arg, paramName, paramSymbol))
620 2 continue;
621 // Allocate space for it
622
1/2
✓ Branch 104 → 105 taken 210 times.
✗ Branch 104 → 215 not taken.
210 llvm::Type *paramType = funcType->getParamType(argNumber);
623
1/2
✓ Branch 105 → 106 taken 210 times.
✗ Branch 105 → 215 not taken.
210 llvm::Value *paramAddress = insertAlloca(paramType, paramName);
624 // Update the symbol table entry
625 210 const bool isCapturesStruct = argNumber == 0;
626
2/2
✓ Branch 106 → 107 taken 97 times.
✓ Branch 106 → 108 taken 113 times.
210 if (isCapturesStruct)
627 97 captureStructPtrPtr = paramAddress;
628 else
629
1/2
✓ Branch 108 → 109 taken 113 times.
✗ Branch 108 → 215 not taken.
113 updateAddress(paramSymbol, paramAddress);
630 // Generate debug info
631
2/2
✓ Branch 109 → 110 taken 113 times.
✓ Branch 109 → 111 taken 97 times.
210 if (!isCapturesStruct)
632
1/2
✓ Branch 110 → 111 taken 113 times.
✗ Branch 110 → 215 not taken.
113 diGenerator.generateLocalVarDebugInfo(paramName, paramAddress, argNumber + 1);
633 // Store the value at the new address
634
1/2
✓ Branch 111 → 112 taken 210 times.
✗ Branch 111 → 215 not taken.
210 insertStore(&arg, paramAddress);
635
1/4
✓ Branch 113 → 114 taken 212 times.
✗ Branch 113 → 115 not taken.
✗ Branch 215 → 216 not taken.
✗ Branch 215 → 217 not taken.
212 }
636
637 // Store the default values for optional function args
638
2/2
✓ Branch 120 → 121 taken 89 times.
✓ Branch 120 → 131 taken 8 times.
97 if (node->paramLst) {
639
1/2
✓ Branch 121 → 122 taken 89 times.
✗ Branch 121 → 226 not taken.
89 const std::vector<DeclStmtNode *> params = node->paramLst->params;
640
1/2
✗ Branch 128 → 123 not taken.
✓ Branch 128 → 129 taken 89 times.
89 for (; argIdx < params.size(); argIdx++)
641 visit(params.at(argIdx));
642 89 }
643
644 // Extract captures from captures struct
645
2/2
✓ Branch 131 → 132 taken 18 times.
✓ Branch 131 → 136 taken 79 times.
97 if (hasCaptures) {
646
1/2
✗ Branch 133 → 134 not taken.
✓ Branch 133 → 135 taken 18 times.
18 assert(!paramInfoList.empty());
647
1/2
✓ Branch 135 → 136 taken 18 times.
✗ Branch 135 → 236 not taken.
18 unpackCapturesToLocalVariables(captures, captureStructPtrPtr, capturesStructType);
648 }
649
650 // Visit body
651
1/2
✓ Branch 136 → 137 taken 97 times.
✗ Branch 136 → 227 not taken.
97 visit(node->body);
652
653 // Create return statement if the block is not terminated yet
654
1/2
✗ Branch 138 → 139 not taken.
✓ Branch 138 → 151 taken 97 times.
97 if (!blockAlreadyTerminated) {
655 llvm::Value *result = getDefaultValueForSymbolType(spiceFunc.returnType);
656 if (resultEntry->isInitialized())
657 result = insertLoad(returnType, getAddress(resultEntry));
658 builder.CreateRet(result);
659 }
660
661 // Pop capture addresses
662
2/2
✓ Branch 151 → 152 taken 18 times.
✓ Branch 151 → 162 taken 79 times.
97 if (hasCaptures)
663
5/8
✓ Branch 152 → 153 taken 18 times.
✗ Branch 152 → 234 not taken.
✓ Branch 153 → 154 taken 18 times.
✗ Branch 153 → 234 not taken.
✓ Branch 154 → 155 taken 18 times.
✗ Branch 154 → 234 not taken.
✓ Branch 160 → 156 taken 26 times.
✓ Branch 160 → 161 taken 18 times.
44 for (const auto &capture : captures | std::views::values)
664
1/2
✓ Branch 157 → 158 taken 26 times.
✗ Branch 157 → 234 not taken.
26 popAddress(capture.capturedSymbol);
665
666 // Conclude debug info for function
667
1/2
✓ Branch 162 → 163 taken 97 times.
✗ Branch 162 → 236 not taken.
97 diGenerator.concludeFunctionDebugInfo();
668
1/2
✓ Branch 163 → 164 taken 97 times.
✗ Branch 163 → 236 not taken.
97 diGenerator.setSourceLocation(node);
669
670 // Restore alloca insert markers
671
1/2
✓ Branch 164 → 165 taken 97 times.
✗ Branch 164 → 236 not taken.
97 builder.SetInsertPoint(bOrig);
672 97 blockAlreadyTerminated = false;
673 97 allocaInsertBlock = allocaInsertBlockOrig;
674 97 allocaInsertInst = allocaInsertInstOrig;
675
676 // Change back to original scope
677 97 currentScope = currentScope->parent;
678
679 // Verify function
680
1/2
✓ Branch 165 → 166 taken 97 times.
✗ Branch 165 → 236 not taken.
97 verifyFunction(lambda, node->codeLoc);
681
682 // Captures, create a struct { <fct-ptr>, <capture struct ptr> }
683
1/2
✓ Branch 166 → 167 taken 97 times.
✗ Branch 166 → 236 not taken.
97 llvm::Value *result = buildFatFctPtr(bodyScope, capturesStructType, lambda);
684
685
1/2
✓ Branch 167 → 168 taken 97 times.
✗ Branch 167 → 235 not taken.
194 return LLVMExprResult{.ptr = result, .node = node};
686 97 }
687
688 88 std::any IRGenerator::visitLambdaProc(const LambdaProcNode *node) {
689
2/4
✓ Branch 2 → 3 taken 88 times.
✗ Branch 2 → 183 not taken.
✓ Branch 3 → 4 taken 88 times.
✗ Branch 3 → 183 not taken.
88 Function spiceFunc = node->manifestations.at(manIdx);
690 88 ParamInfoList paramInfoList;
691 88 std::vector<llvm::Type *> paramTypes;
692
693 // Change scope
694
2/4
✓ Branch 4 → 5 taken 88 times.
✗ Branch 4 → 140 not taken.
✓ Branch 5 → 6 taken 88 times.
✗ Branch 5 → 138 not taken.
88 Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId());
695
696 // Every lambda uniformly takes a leading capture-struct pointer as its first argument, even when it captures
697 // nothing. This keeps the calling convention of all lambdas (and plain function pointers) identical, so a lambda
698 // can be stored, retrieved and called without the call site knowing statically whether it captures. A
699 // non-capturing lambda simply ignores the passed (poison) pointer.
700 88 const CaptureMap &captures = bodyScope->symbolTable.captures;
701 88 const bool hasCaptures = !captures.empty();
702
3/4
✓ Branch 8 → 9 taken 58 times.
✓ Branch 8 → 11 taken 30 times.
✓ Branch 9 → 10 taken 58 times.
✗ Branch 9 → 177 not taken.
88 llvm::Type *capturesStructType = hasCaptures ? buildCapturesContainerType(captures) : nullptr;
703
1/2
✓ Branch 12 → 13 taken 88 times.
✗ Branch 12 → 141 not taken.
88 paramInfoList.emplace_back(CAPTURES_PARAM_NAME, nullptr);
704
2/4
✓ Branch 13 → 14 taken 88 times.
✗ Branch 13 → 142 not taken.
✓ Branch 14 → 15 taken 88 times.
✗ Branch 14 → 142 not taken.
88 paramTypes.push_back(builder.getPtrTy()); // The captures struct is always passed as pointer
705
706 // Visit parameters
707 88 size_t argIdx = 0;
708
2/2
✓ Branch 15 → 16 taken 54 times.
✓ Branch 15 → 34 taken 34 times.
88 if (node->hasParams) {
709 54 const size_t numOfParams = spiceFunc.paramList.size();
710
1/2
✓ Branch 17 → 18 taken 54 times.
✗ Branch 17 → 177 not taken.
54 paramInfoList.reserve(numOfParams);
711
1/2
✓ Branch 18 → 19 taken 54 times.
✗ Branch 18 → 177 not taken.
54 paramTypes.reserve(numOfParams);
712
2/2
✓ Branch 33 → 20 taken 70 times.
✓ Branch 33 → 34 taken 54 times.
124 for (; argIdx < numOfParams; argIdx++) {
713
1/2
✓ Branch 20 → 21 taken 70 times.
✗ Branch 20 → 146 not taken.
70 const DeclStmtNode *param = node->paramLst->params.at(argIdx);
714 // Get symbol table entry of param
715
1/2
✓ Branch 21 → 22 taken 70 times.
✗ Branch 21 → 146 not taken.
70 const SymbolTableEntry *paramSymbol = currentScope->lookupStrict(param->varName);
716
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 70 times.
70 assert(paramSymbol != nullptr);
717 // Retrieve type of param
718
3/6
✓ Branch 26 → 27 taken 70 times.
✗ Branch 26 → 145 not taken.
✓ Branch 27 → 28 taken 70 times.
✗ Branch 27 → 143 not taken.
✓ Branch 28 → 29 taken 70 times.
✗ Branch 28 → 143 not taken.
70 llvm::Type *paramType = spiceFunc.getParamTypes().at(argIdx).getParamLLVMType(sourceFile);
719 // Add it to the lists
720
1/2
✓ Branch 30 → 31 taken 70 times.
✗ Branch 30 → 146 not taken.
70 paramInfoList.emplace_back(param->varName, paramSymbol);
721
1/2
✓ Branch 31 → 32 taken 70 times.
✗ Branch 31 → 146 not taken.
70 paramTypes.push_back(paramType);
722 }
723 }
724
725 // Create function or implement declared function
726
2/4
✓ Branch 34 → 35 taken 88 times.
✗ Branch 34 → 149 not taken.
✓ Branch 35 → 36 taken 88 times.
✗ Branch 35 → 147 not taken.
88 spiceFunc.mangleSuffix = "." + std::to_string(manIdx);
727
1/2
✓ Branch 39 → 40 taken 88 times.
✗ Branch 39 → 177 not taken.
88 const std::string mangledName = spiceFunc.getMangledName();
728
2/4
✓ Branch 41 → 42 taken 88 times.
✗ Branch 41 → 151 not taken.
✓ Branch 42 → 43 taken 88 times.
✗ Branch 42 → 151 not taken.
88 llvm::FunctionType *funcType = llvm::FunctionType::get(builder.getVoidTy(), paramTypes, false);
729
1/2
✓ Branch 44 → 45 taken 88 times.
✗ Branch 44 → 152 not taken.
88 module->getOrInsertFunction(mangledName, funcType);
730
1/2
✓ Branch 46 → 47 taken 88 times.
✗ Branch 46 → 153 not taken.
88 llvm::Function *lambda = module->getFunction(mangledName);
731
732 // Set attributes to function
733
1/2
✓ Branch 47 → 48 taken 88 times.
✗ Branch 47 → 175 not taken.
88 lambda->setLinkage(llvm::Function::InternalLinkage);
734 88 lambda->setDSOLocal(true);
735
1/2
✓ Branch 49 → 50 taken 88 times.
✗ Branch 49 → 175 not taken.
88 addCommonFctAttrs(lambda);
736
1/2
✓ Branch 50 → 51 taken 88 times.
✗ Branch 50 → 175 not taken.
88 enableFunctionInstrumentation(lambda);
737
738 // In case of captures, add attribute to captures argument
739
2/2
✓ Branch 51 → 52 taken 58 times.
✓ Branch 51 → 57 taken 30 times.
88 if (hasCaptures) {
740
1/2
✓ Branch 52 → 53 taken 58 times.
✗ Branch 52 → 175 not taken.
58 lambda->addParamAttr(0, llvm::Attribute::NoUndef);
741
1/2
✓ Branch 53 → 54 taken 58 times.
✗ Branch 53 → 175 not taken.
58 lambda->addParamAttr(0, llvm::Attribute::NonNull);
742
2/4
✓ Branch 55 → 56 taken 58 times.
✗ Branch 55 → 175 not taken.
✓ Branch 56 → 57 taken 58 times.
✗ Branch 56 → 175 not taken.
58 lambda->addDereferenceableParamAttr(0, module->getDataLayout().getPointerSize());
743 }
744
745 // Add debug info
746
1/2
✓ Branch 57 → 58 taken 88 times.
✗ Branch 57 → 175 not taken.
88 diGenerator.generateFunctionDebugInfo(lambda, &spiceFunc, true);
747
1/2
✓ Branch 58 → 59 taken 88 times.
✗ Branch 58 → 175 not taken.
88 diGenerator.setSourceLocation(node);
748
749 // Save alloca insert markers
750 88 llvm::BasicBlock *allocaInsertBlockOrig = allocaInsertBlock;
751 88 llvm::AllocaInst *allocaInsertInstOrig = allocaInsertInst;
752 88 llvm::BasicBlock *bOrig = builder.GetInsertBlock();
753
754 // Create entry block
755
1/2
✓ Branch 63 → 64 taken 88 times.
✗ Branch 63 → 154 not taken.
88 llvm::BasicBlock *bEntry = createBlock();
756
1/2
✓ Branch 66 → 67 taken 88 times.
✗ Branch 66 → 175 not taken.
88 switchToBlock(bEntry, lambda);
757
758 // Reset alloca insert markers to this block
759 88 allocaInsertBlock = bEntry;
760 88 allocaInsertInst = nullptr;
761
762 // Save values of parameters to locals
763 88 llvm::Value *captureStructPtrPtr = nullptr;
764
3/4
✓ Branch 67 → 68 taken 88 times.
✗ Branch 67 → 167 not taken.
✓ Branch 93 → 70 taken 158 times.
✓ Branch 93 → 94 taken 88 times.
246 for (auto &arg : lambda->args()) {
765 // Get information about the parameter
766 158 const size_t argNumber = arg.getArgNo();
767
2/4
✓ Branch 71 → 72 taken 158 times.
✗ Branch 71 → 160 not taken.
✓ Branch 72 → 73 taken 158 times.
✗ Branch 72 → 160 not taken.
158 auto [paramName, paramSymbol] = paramInfoList.at(argNumber);
768 // Decayed array params already carry the address of the array, so they do not need a local copy
769
2/4
✓ Branch 75 → 76 taken 158 times.
✗ Branch 75 → 160 not taken.
✗ Branch 76 → 77 not taken.
✓ Branch 76 → 78 taken 158 times.
158 if (bindDecayedArrayParam(arg, paramName, paramSymbol))
770 continue;
771 // Allocate space for it
772
1/2
✓ Branch 78 → 79 taken 158 times.
✗ Branch 78 → 160 not taken.
158 llvm::Type *paramType = funcType->getParamType(argNumber);
773
1/2
✓ Branch 79 → 80 taken 158 times.
✗ Branch 79 → 160 not taken.
158 llvm::Value *paramAddress = insertAlloca(paramType, paramName);
774 // Update the symbol table entry
775 158 const bool isCapturesStruct = argNumber == 0;
776
2/2
✓ Branch 80 → 81 taken 88 times.
✓ Branch 80 → 82 taken 70 times.
158 if (isCapturesStruct)
777 88 captureStructPtrPtr = paramAddress;
778 else
779
1/2
✓ Branch 82 → 83 taken 70 times.
✗ Branch 82 → 160 not taken.
70 updateAddress(paramSymbol, paramAddress);
780 // Generate debug info
781
2/2
✓ Branch 83 → 84 taken 70 times.
✓ Branch 83 → 85 taken 88 times.
158 if (!isCapturesStruct)
782
1/2
✓ Branch 84 → 85 taken 70 times.
✗ Branch 84 → 160 not taken.
70 diGenerator.generateLocalVarDebugInfo(paramName, paramAddress, argNumber + 1);
783 // Store the value at the new address
784
1/2
✓ Branch 85 → 86 taken 158 times.
✗ Branch 85 → 160 not taken.
158 insertStore(&arg, paramAddress);
785
1/4
✓ Branch 87 → 88 taken 158 times.
✗ Branch 87 → 89 not taken.
✗ Branch 160 → 161 not taken.
✗ Branch 160 → 162 not taken.
158 }
786
787 // Store the default values for optional function args
788
2/2
✓ Branch 94 → 95 taken 54 times.
✓ Branch 94 → 105 taken 34 times.
88 if (node->paramLst) {
789
1/2
✓ Branch 95 → 96 taken 54 times.
✗ Branch 95 → 171 not taken.
54 const std::vector<DeclStmtNode *> params = node->paramLst->params;
790
1/2
✗ Branch 102 → 97 not taken.
✓ Branch 102 → 103 taken 54 times.
54 for (; argIdx < params.size(); argIdx++)
791 visit(params.at(argIdx));
792 54 }
793
794 // Extract captures from captures struct
795
2/2
✓ Branch 105 → 106 taken 58 times.
✓ Branch 105 → 110 taken 30 times.
88 if (hasCaptures) {
796
1/2
✗ Branch 107 → 108 not taken.
✓ Branch 107 → 109 taken 58 times.
58 assert(!paramInfoList.empty());
797
1/2
✓ Branch 109 → 110 taken 58 times.
✗ Branch 109 → 175 not taken.
58 unpackCapturesToLocalVariables(captures, captureStructPtrPtr, capturesStructType);
798 }
799
800 // Visit body
801
1/2
✓ Branch 110 → 111 taken 88 times.
✗ Branch 110 → 172 not taken.
88 visit(node->body);
802
803 // Create return statement if the block is not terminated yet
804
1/2
✓ Branch 112 → 113 taken 88 times.
✗ Branch 112 → 114 not taken.
88 if (!blockAlreadyTerminated)
805
1/2
✓ Branch 113 → 114 taken 88 times.
✗ Branch 113 → 175 not taken.
88 builder.CreateRetVoid();
806
807 // Pop capture addresses
808
2/2
✓ Branch 114 → 115 taken 58 times.
✓ Branch 114 → 125 taken 30 times.
88 if (hasCaptures)
809
5/8
✓ Branch 115 → 116 taken 58 times.
✗ Branch 115 → 173 not taken.
✓ Branch 116 → 117 taken 58 times.
✗ Branch 116 → 173 not taken.
✓ Branch 117 → 118 taken 58 times.
✗ Branch 117 → 173 not taken.
✓ Branch 123 → 119 taken 100 times.
✓ Branch 123 → 124 taken 58 times.
158 for (const auto &capture : captures | std::views::values)
810
1/2
✓ Branch 120 → 121 taken 100 times.
✗ Branch 120 → 173 not taken.
100 popAddress(capture.capturedSymbol);
811
812 // Conclude debug info for function
813
1/2
✓ Branch 125 → 126 taken 88 times.
✗ Branch 125 → 175 not taken.
88 diGenerator.concludeFunctionDebugInfo();
814
1/2
✓ Branch 126 → 127 taken 88 times.
✗ Branch 126 → 175 not taken.
88 diGenerator.setSourceLocation(node);
815
816 // Restore alloca insert markers
817
1/2
✓ Branch 127 → 128 taken 88 times.
✗ Branch 127 → 175 not taken.
88 builder.SetInsertPoint(bOrig);
818 88 blockAlreadyTerminated = false;
819 88 allocaInsertBlock = allocaInsertBlockOrig;
820 88 allocaInsertInst = allocaInsertInstOrig;
821
822 // Change back to original scope
823 88 currentScope = currentScope->parent;
824
825 // Verify function
826
1/2
✓ Branch 128 → 129 taken 88 times.
✗ Branch 128 → 175 not taken.
88 verifyFunction(lambda, node->codeLoc);
827
828 // Create a struct { <fct-ptr>, <capture struct ptr> }
829
1/2
✓ Branch 129 → 130 taken 88 times.
✗ Branch 129 → 175 not taken.
88 llvm::Value *result = buildFatFctPtr(bodyScope, capturesStructType, lambda);
830
831
1/2
✓ Branch 130 → 131 taken 88 times.
✗ Branch 130 → 174 not taken.
176 return LLVMExprResult{.ptr = result, .node = node};
832 88 }
833
834 2 std::any IRGenerator::visitLambdaExpr(const LambdaExprNode *node) {
835
1/2
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 173 not taken.
2 const Function &spiceFunc = node->manifestations.at(manIdx);
836 2 ParamInfoList paramInfoList;
837 2 std::vector<llvm::Type *> paramTypes;
838
839 // Change scope
840
2/4
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 137 not taken.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 135 not taken.
2 Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId());
841
842 // Every lambda uniformly takes a leading capture-struct pointer as its first argument, even when it captures
843 // nothing. This keeps the calling convention of all lambdas (and plain function pointers) identical, so a lambda
844 // can be stored, retrieved and called without the call site knowing statically whether it captures. A
845 // non-capturing lambda simply ignores the passed (poison) pointer.
846 2 const CaptureMap &captures = bodyScope->symbolTable.captures;
847 2 const bool hasCaptures = !captures.empty();
848
1/4
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 10 taken 2 times.
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 169 not taken.
2 llvm::Type *capturesStructType = hasCaptures ? buildCapturesContainerType(captures) : nullptr;
849
1/2
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 138 not taken.
2 paramInfoList.emplace_back(CAPTURES_PARAM_NAME, nullptr);
850
2/4
✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 139 not taken.
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 139 not taken.
2 paramTypes.push_back(builder.getPtrTy()); // The capture struct is always passed as pointer
851
852 // Visit parameters
853 2 size_t argIdx = 0;
854
1/2
✓ Branch 14 → 15 taken 2 times.
✗ Branch 14 → 33 not taken.
2 if (node->hasParams) {
855 2 const size_t numOfParams = spiceFunc.paramList.size();
856
1/2
✓ Branch 16 → 17 taken 2 times.
✗ Branch 16 → 169 not taken.
2 paramInfoList.reserve(numOfParams);
857
1/2
✓ Branch 17 → 18 taken 2 times.
✗ Branch 17 → 169 not taken.
2 paramTypes.reserve(numOfParams);
858
2/2
✓ Branch 32 → 19 taken 4 times.
✓ Branch 32 → 33 taken 2 times.
6 for (; argIdx < numOfParams; argIdx++) {
859
1/2
✓ Branch 19 → 20 taken 4 times.
✗ Branch 19 → 143 not taken.
4 const DeclStmtNode *param = node->paramLst->params.at(argIdx);
860 // Get symbol table entry of param
861
1/2
✓ Branch 20 → 21 taken 4 times.
✗ Branch 20 → 143 not taken.
4 const SymbolTableEntry *paramSymbol = currentScope->lookupStrict(param->varName);
862
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 4 times.
4 assert(paramSymbol != nullptr);
863 // Retrieve type of param
864
3/6
✓ Branch 25 → 26 taken 4 times.
✗ Branch 25 → 142 not taken.
✓ Branch 26 → 27 taken 4 times.
✗ Branch 26 → 140 not taken.
✓ Branch 27 → 28 taken 4 times.
✗ Branch 27 → 140 not taken.
4 llvm::Type *paramType = spiceFunc.getParamTypes().at(argIdx).getParamLLVMType(sourceFile);
865 // Add it to the lists
866
1/2
✓ Branch 29 → 30 taken 4 times.
✗ Branch 29 → 143 not taken.
4 paramInfoList.emplace_back(param->varName, paramSymbol);
867
1/2
✓ Branch 30 → 31 taken 4 times.
✗ Branch 30 → 143 not taken.
4 paramTypes.push_back(paramType);
868 }
869 }
870
871 // Get return type
872
2/4
✓ Branch 33 → 34 taken 2 times.
✗ Branch 33 → 169 not taken.
✓ Branch 34 → 35 taken 2 times.
✗ Branch 34 → 169 not taken.
2 llvm::Type *returnType = builder.getVoidTy();
873
1/2
✓ Branch 37 → 38 taken 2 times.
✗ Branch 37 → 40 not taken.
2 if (spiceFunc.isFunction())
874
1/2
✓ Branch 38 → 39 taken 2 times.
✗ Branch 38 → 169 not taken.
2 returnType = spiceFunc.returnType.toLLVMType(sourceFile);
875
876 // Create function or implement declared function
877
1/2
✓ Branch 40 → 41 taken 2 times.
✗ Branch 40 → 169 not taken.
2 const std::string mangledName = spiceFunc.getMangledName();
878
1/2
✓ Branch 42 → 43 taken 2 times.
✗ Branch 42 → 144 not taken.
2 llvm::FunctionType *funcType = llvm::FunctionType::get(returnType, paramTypes, false);
879
1/2
✓ Branch 44 → 45 taken 2 times.
✗ Branch 44 → 145 not taken.
2 module->getOrInsertFunction(mangledName, funcType);
880
1/2
✓ Branch 46 → 47 taken 2 times.
✗ Branch 46 → 146 not taken.
2 llvm::Function *lambda = module->getFunction(mangledName);
881
882 // Set attributes to function
883
1/2
✓ Branch 47 → 48 taken 2 times.
✗ Branch 47 → 167 not taken.
2 lambda->setLinkage(llvm::Function::InternalLinkage);
884 2 lambda->setDSOLocal(true);
885
1/2
✓ Branch 49 → 50 taken 2 times.
✗ Branch 49 → 167 not taken.
2 addCommonFctAttrs(lambda);
886
1/2
✓ Branch 50 → 51 taken 2 times.
✗ Branch 50 → 167 not taken.
2 enableFunctionInstrumentation(lambda);
887
888 // In case of captures, add attribute to captures argument
889
1/2
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 57 taken 2 times.
2 if (hasCaptures) {
890 lambda->addParamAttr(0, llvm::Attribute::NoUndef);
891 lambda->addParamAttr(0, llvm::Attribute::NonNull);
892 lambda->addDereferenceableParamAttr(0, module->getDataLayout().getPointerSize());
893 }
894
895 // Add debug info
896
1/2
✓ Branch 57 → 58 taken 2 times.
✗ Branch 57 → 167 not taken.
2 diGenerator.generateFunctionDebugInfo(lambda, &spiceFunc, true);
897
1/2
✓ Branch 58 → 59 taken 2 times.
✗ Branch 58 → 167 not taken.
2 diGenerator.setSourceLocation(node);
898
899 // Save alloca insert markers
900 2 llvm::BasicBlock *allocaInsertBlockOrig = allocaInsertBlock;
901 2 llvm::AllocaInst *allocaInsertInstOrig = allocaInsertInst;
902 2 llvm::BasicBlock *bOrig = builder.GetInsertBlock();
903
904 // Create entry block
905
1/2
✓ Branch 63 → 64 taken 2 times.
✗ Branch 63 → 147 not taken.
2 llvm::BasicBlock *bEntry = createBlock();
906
1/2
✓ Branch 66 → 67 taken 2 times.
✗ Branch 66 → 167 not taken.
2 switchToBlock(bEntry, lambda);
907
908 // Reset alloca insert markers to this block
909 2 allocaInsertBlock = bEntry;
910 2 allocaInsertInst = nullptr;
911
912 // Save values of parameters to locals
913 2 llvm::Value *captureStructPtrPtr = nullptr;
914
3/4
✓ Branch 67 → 68 taken 2 times.
✗ Branch 67 → 160 not taken.
✓ Branch 93 → 70 taken 6 times.
✓ Branch 93 → 94 taken 2 times.
8 for (auto &arg : lambda->args()) {
915 // Get information about the parameter
916 6 const size_t argNumber = arg.getArgNo();
917
2/4
✓ Branch 71 → 72 taken 6 times.
✗ Branch 71 → 153 not taken.
✓ Branch 72 → 73 taken 6 times.
✗ Branch 72 → 153 not taken.
6 auto [paramName, paramSymbol] = paramInfoList.at(argNumber);
918 // Decayed array params already carry the address of the array, so they do not need a local copy
919
2/4
✓ Branch 75 → 76 taken 6 times.
✗ Branch 75 → 153 not taken.
✗ Branch 76 → 77 not taken.
✓ Branch 76 → 78 taken 6 times.
6 if (bindDecayedArrayParam(arg, paramName, paramSymbol))
920 continue;
921 // Allocate space for it
922
1/2
✓ Branch 78 → 79 taken 6 times.
✗ Branch 78 → 153 not taken.
6 llvm::Type *paramType = funcType->getParamType(argNumber);
923
1/2
✓ Branch 79 → 80 taken 6 times.
✗ Branch 79 → 153 not taken.
6 llvm::Value *paramAddress = insertAlloca(paramType, paramName);
924 // Update the symbol table entry
925 6 const bool isCapturesStruct = argNumber == 0;
926
2/2
✓ Branch 80 → 81 taken 2 times.
✓ Branch 80 → 82 taken 4 times.
6 if (isCapturesStruct)
927 2 captureStructPtrPtr = paramAddress;
928 else
929
1/2
✓ Branch 82 → 83 taken 4 times.
✗ Branch 82 → 153 not taken.
4 updateAddress(paramSymbol, paramAddress);
930 // Generate debug info
931
2/2
✓ Branch 83 → 84 taken 4 times.
✓ Branch 83 → 85 taken 2 times.
6 if (!isCapturesStruct)
932
1/2
✓ Branch 84 → 85 taken 4 times.
✗ Branch 84 → 153 not taken.
4 diGenerator.generateLocalVarDebugInfo(paramName, paramAddress, argNumber + 1);
933 // Store the value at the new address
934
1/2
✓ Branch 85 → 86 taken 6 times.
✗ Branch 85 → 153 not taken.
6 insertStore(&arg, paramAddress);
935
1/4
✓ Branch 87 → 88 taken 6 times.
✗ Branch 87 → 89 not taken.
✗ Branch 153 → 154 not taken.
✗ Branch 153 → 155 not taken.
6 }
936
937 // Store the default values for optional function args
938
1/2
✓ Branch 94 → 95 taken 2 times.
✗ Branch 94 → 105 not taken.
2 if (node->paramLst) {
939
1/2
✓ Branch 95 → 96 taken 2 times.
✗ Branch 95 → 164 not taken.
2 const std::vector<DeclStmtNode *> params = node->paramLst->params;
940
1/2
✗ Branch 102 → 97 not taken.
✓ Branch 102 → 103 taken 2 times.
2 for (; argIdx < params.size(); argIdx++)
941 visit(params.at(argIdx));
942 2 }
943
944 // Extract captures from captures struct
945
1/2
✗ Branch 105 → 106 not taken.
✓ Branch 105 → 110 taken 2 times.
2 if (hasCaptures) {
946 assert(!paramInfoList.empty());
947 unpackCapturesToLocalVariables(captures, captureStructPtrPtr, capturesStructType);
948 }
949
950 // Visit lambda expression
951
1/2
✓ Branch 110 → 111 taken 2 times.
✗ Branch 110 → 167 not taken.
2 llvm::Value *exprResult = resolveValue(node->lambdaExpr);
952
1/2
✓ Branch 111 → 112 taken 2 times.
✗ Branch 111 → 167 not taken.
2 builder.CreateRet(exprResult);
953
954 // Pop capture addresses
955
1/2
✗ Branch 112 → 113 not taken.
✓ Branch 112 → 123 taken 2 times.
2 if (hasCaptures)
956 for (const auto &val : captures | std::views::values)
957 popAddress(val.capturedSymbol);
958
959 // Conclude debug info for function
960
1/2
✓ Branch 123 → 124 taken 2 times.
✗ Branch 123 → 167 not taken.
2 diGenerator.concludeFunctionDebugInfo();
961
1/2
✓ Branch 124 → 125 taken 2 times.
✗ Branch 124 → 167 not taken.
2 diGenerator.setSourceLocation(node);
962
963 // Restore alloca insert markers
964
1/2
✓ Branch 125 → 126 taken 2 times.
✗ Branch 125 → 167 not taken.
2 builder.SetInsertPoint(bOrig);
965 2 blockAlreadyTerminated = false;
966 2 allocaInsertBlock = allocaInsertBlockOrig;
967 2 allocaInsertInst = allocaInsertInstOrig;
968
969 // Change back to original scope
970 2 currentScope = currentScope->parent;
971
972 // Verify function
973
1/2
✓ Branch 126 → 127 taken 2 times.
✗ Branch 126 → 167 not taken.
2 verifyFunction(lambda, node->codeLoc);
974 // Create a struct { <fct-ptr>, <capture struct ptr> }
975
1/2
✓ Branch 127 → 128 taken 2 times.
✗ Branch 127 → 167 not taken.
2 llvm::Value *result = buildFatFctPtr(bodyScope, capturesStructType, lambda);
976
977
1/2
✓ Branch 128 → 129 taken 2 times.
✗ Branch 128 → 166 not taken.
4 return LLVMExprResult{.ptr = result, .node = node};
978 2 }
979
980 22564 std::any IRGenerator::visitDataType(const DataTypeNode *node) {
981 // Retrieve symbol type
982
1/2
✓ Branch 2 → 3 taken 22564 times.
✗ Branch 2 → 12 not taken.
22564 const QualType symbolType = node->getEvaluatedSymbolType(manIdx);
983
2/4
✓ Branch 3 → 4 taken 22564 times.
✗ Branch 3 → 12 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 22564 times.
22564 assert(!symbolType.is(TY_DYN)); // Symbol type should not be dyn anymore at this point
984
2/4
✓ Branch 6 → 7 taken 22564 times.
✗ Branch 6 → 11 not taken.
✓ Branch 7 → 8 taken 22564 times.
✗ Branch 7 → 11 not taken.
45128 return symbolType.toLLVMType(sourceFile);
985 }
986
987 32 llvm::Function *IRGenerator::getOrCreateFatFctPtrThunk(llvm::Function *target) {
988 // Plain function/procedure references are stored in fat function pointers and called through the uniform lambda
989 // calling convention, which always passes a leading capture-struct pointer. A named function does not have that
990 // parameter, so we wrap it in a thunk that has the extra (ignored) pointer and forwards to the real function.
991
3/6
✓ Branch 2 → 3 taken 32 times.
✗ Branch 2 → 63 not taken.
✓ Branch 3 → 4 taken 32 times.
✗ Branch 3 → 63 not taken.
✓ Branch 4 → 5 taken 32 times.
✗ Branch 4 → 61 not taken.
32 const std::string thunkName = target->getName().str() + ".fatthunk";
992
3/4
✓ Branch 7 → 8 taken 32 times.
✗ Branch 7 → 65 not taken.
✓ Branch 8 → 9 taken 6 times.
✓ Branch 8 → 10 taken 26 times.
32 if (llvm::Function *existing = module->getFunction(thunkName))
993 6 return existing;
994
995 // Build the thunk signature: the target's signature with an additional leading capture-struct pointer
996
1/2
✓ Branch 10 → 11 taken 26 times.
✗ Branch 10 → 85 not taken.
26 const llvm::FunctionType *targetType = target->getFunctionType();
997 26 std::vector<llvm::Type *> paramTypes;
998
1/2
✓ Branch 12 → 13 taken 26 times.
✗ Branch 12 → 83 not taken.
26 paramTypes.reserve(targetType->getNumParams() + 1);
999
2/4
✓ Branch 13 → 14 taken 26 times.
✗ Branch 13 → 66 not taken.
✓ Branch 14 → 15 taken 26 times.
✗ Branch 14 → 66 not taken.
26 paramTypes.push_back(builder.getPtrTy()); // Ignored captures pointer
1000
1/2
✓ Branch 21 → 22 taken 26 times.
✗ Branch 21 → 67 not taken.
52 paramTypes.insert(paramTypes.end(), targetType->param_begin(), targetType->param_end());
1001
1/2
✓ Branch 25 → 26 taken 26 times.
✗ Branch 25 → 69 not taken.
26 llvm::FunctionType *thunkType = llvm::FunctionType::get(targetType->getReturnType(), paramTypes, targetType->isVarArg());
1002
1003
2/4
✓ Branch 26 → 27 taken 26 times.
✗ Branch 26 → 70 not taken.
✓ Branch 27 → 28 taken 26 times.
✗ Branch 27 → 70 not taken.
26 llvm::Function *thunk = llvm::Function::Create(thunkType, llvm::Function::PrivateLinkage, thunkName, module);
1004 26 thunk->setDSOLocal(true);
1005
1/2
✓ Branch 29 → 30 taken 26 times.
✗ Branch 29 → 83 not taken.
26 addCommonFctAttrs(thunk);
1006
1007 // Save insert markers, because we emit the thunk body in the middle of generating another function
1008 26 llvm::BasicBlock *bOrig = builder.GetInsertBlock();
1009 26 llvm::BasicBlock *allocaInsertBlockOrig = allocaInsertBlock;
1010 26 llvm::AllocaInst *allocaInsertInstOrig = allocaInsertInst;
1011
1012
2/4
✓ Branch 33 → 34 taken 26 times.
✗ Branch 33 → 73 not taken.
✓ Branch 34 → 35 taken 26 times.
✗ Branch 34 → 71 not taken.
26 llvm::BasicBlock *bEntry = createBlock("entry");
1013
1/2
✓ Branch 37 → 38 taken 26 times.
✗ Branch 37 → 83 not taken.
26 switchToBlock(bEntry, thunk);
1014
1015 // Forward all arguments except the leading (ignored) captures pointer
1016 26 std::vector<llvm::Value *> fwdArgs;
1017
1/2
✓ Branch 39 → 40 taken 26 times.
✗ Branch 39 → 81 not taken.
26 fwdArgs.reserve(targetType->getNumParams());
1018
2/2
✓ Branch 45 → 41 taken 28 times.
✓ Branch 45 → 46 taken 26 times.
54 for (size_t i = 1; i < thunk->arg_size(); i++)
1019
2/4
✓ Branch 41 → 42 taken 28 times.
✗ Branch 41 → 77 not taken.
✓ Branch 42 → 43 taken 28 times.
✗ Branch 42 → 77 not taken.
28 fwdArgs.push_back(thunk->getArg(i));
1020
3/6
✓ Branch 46 → 47 taken 26 times.
✗ Branch 46 → 80 not taken.
✓ Branch 48 → 49 taken 26 times.
✗ Branch 48 → 78 not taken.
✓ Branch 49 → 50 taken 26 times.
✗ Branch 49 → 78 not taken.
26 llvm::CallInst *call = builder.CreateCall(target, fwdArgs);
1021
2/2
✓ Branch 52 → 53 taken 18 times.
✓ Branch 52 → 54 taken 8 times.
26 if (targetType->getReturnType()->isVoidTy())
1022
1/2
✓ Branch 53 → 55 taken 18 times.
✗ Branch 53 → 81 not taken.
18 builder.CreateRetVoid();
1023 else
1024
1/2
✓ Branch 54 → 55 taken 8 times.
✗ Branch 54 → 81 not taken.
8 builder.CreateRet(call);
1025
1026 // Restore insert markers
1027
1/2
✓ Branch 55 → 56 taken 26 times.
✗ Branch 55 → 81 not taken.
26 builder.SetInsertPoint(bOrig);
1028 26 blockAlreadyTerminated = false;
1029 26 allocaInsertBlock = allocaInsertBlockOrig;
1030 26 allocaInsertInst = allocaInsertInstOrig;
1031
1032 26 return thunk;
1033 32 }
1034
1035 219 llvm::Value *IRGenerator::buildFatFctPtr(Scope *bodyScope, llvm::Type *capturesStructType, llvm::Value *lambda) {
1036 // Create capture struct if required
1037 219 llvm::Value *capturesPtr = nullptr;
1038 // Byte size of the owned capture struct. This is only non-zero if the captures live in a dedicated
1039 // (stack-allocated) struct that the std Lambda type needs to relocate to the heap to take ownership.
1040 // A single capture stored inline in the capturePtr slot, or no captures at all, leaves this at 0.
1041 219 uint64_t captureStructSize = 0;
1042
2/2
✓ Branch 2 → 3 taken 76 times.
✓ Branch 2 → 65 taken 143 times.
219 if (capturesStructType != nullptr) {
1043
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 76 times.
76 assert(bodyScope != nullptr);
1044 // If we have a single capture of ptr type, we can directly store it into the fat ptr. Otherwise, we need a stack allocated
1045 // struct to store the captures in a memory-efficient manner and store a pointer to that struct to the fat ptr.
1046
2/2
✓ Branch 6 → 7 taken 36 times.
✓ Branch 6 → 26 taken 40 times.
76 if (capturesStructType->isPointerTy()) {
1047 36 const CaptureMap &captures = bodyScope->symbolTable.captures;
1048
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 36 times.
36 assert(captures.size() == 1);
1049 36 const Capture &capture = captures.begin()->second;
1050
2/2
✓ Branch 13 → 14 taken 26 times.
✓ Branch 13 → 24 taken 10 times.
36 if (capture.getMode() == BY_VALUE) {
1051 26 llvm::Type *varType = capture.capturedSymbol->getQualType().toLLVMType(sourceFile);
1052
2/4
✓ Branch 19 → 20 taken 26 times.
✗ Branch 19 → 100 not taken.
✓ Branch 20 → 21 taken 26 times.
✗ Branch 20 → 100 not taken.
26 capturesPtr = insertLoad(varType, getAddress(capture.capturedSymbol));
1053 } else {
1054 10 capturesPtr = getAddress(capture.capturedSymbol);
1055 }
1056 } else {
1057
2/4
✓ Branch 28 → 29 taken 40 times.
✗ Branch 28 → 108 not taken.
✓ Branch 29 → 30 taken 40 times.
✗ Branch 29 → 106 not taken.
40 capturesPtr = insertAlloca(capturesStructType, CAPTURES_PARAM_NAME);
1058
2/4
✓ Branch 33 → 34 taken 40 times.
✗ Branch 33 → 112 not taken.
✓ Branch 34 → 35 taken 40 times.
✗ Branch 34 → 112 not taken.
40 captureStructSize = module->getDataLayout().getTypeAllocSize(capturesStructType);
1059 40 size_t captureIdx = 0;
1060
5/8
✓ Branch 35 → 36 taken 40 times.
✗ Branch 35 → 125 not taken.
✓ Branch 36 → 37 taken 40 times.
✗ Branch 36 → 125 not taken.
✓ Branch 37 → 38 taken 40 times.
✗ Branch 37 → 125 not taken.
✓ Branch 63 → 39 taken 90 times.
✓ Branch 63 → 64 taken 40 times.
130 for (const auto &capture : bodyScope->symbolTable.captures | std::views::values) {
1061 90 const SymbolTableEntry *capturedEntry = capture.capturedSymbol;
1062 // Get address or value of captured variable, depending on the capturing mode
1063
1/2
✓ Branch 40 → 41 taken 90 times.
✗ Branch 40 → 125 not taken.
90 llvm::Value *capturedValue = getAddress(capturedEntry);
1064
1/2
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 90 times.
90 assert(capturedValue != nullptr);
1065
3/4
✓ Branch 43 → 44 taken 90 times.
✗ Branch 43 → 125 not taken.
✓ Branch 44 → 45 taken 72 times.
✓ Branch 44 → 54 taken 18 times.
90 if (capture.getMode() == BY_VALUE) {
1066
2/4
✓ Branch 45 → 46 taken 72 times.
✗ Branch 45 → 125 not taken.
✓ Branch 46 → 47 taken 72 times.
✗ Branch 46 → 125 not taken.
72 llvm::Type *captureType = capturedEntry->getQualType().toLLVMType(sourceFile);
1067
1/2
✓ Branch 50 → 51 taken 72 times.
✗ Branch 50 → 113 not taken.
72 capturedValue = insertLoad(captureType, capturedValue);
1068 }
1069 // Store it in the capture struct
1070
1/2
✓ Branch 57 → 58 taken 90 times.
✗ Branch 57 → 119 not taken.
90 llvm::Value *captureAddress = insertStructGEP(capturesStructType, capturesPtr, captureIdx);
1071
1/2
✓ Branch 60 → 61 taken 90 times.
✗ Branch 60 → 125 not taken.
90 insertStore(capturedValue, captureAddress);
1072 90 captureIdx++;
1073 }
1074 }
1075 }
1076
1077 // Create fat pointer
1078
2/4
✓ Branch 67 → 68 taken 219 times.
✗ Branch 67 → 128 not taken.
✓ Branch 68 → 69 taken 219 times.
✗ Branch 68 → 126 not taken.
438 llvm::Value *fatFctPtr = insertAlloca(llvmTypes.lambdaFatPtrType, "fat.ptr");
1079
1/2
✓ Branch 74 → 75 taken 219 times.
✗ Branch 74 → 132 not taken.
219 llvm::Value *fctPtr = insertStructGEP(llvmTypes.lambdaFatPtrType, fatFctPtr, 0);
1080 219 insertStore(lambda, fctPtr);
1081
1/2
✓ Branch 81 → 82 taken 219 times.
✗ Branch 81 → 138 not taken.
219 llvm::Value *capturePtr = insertStructGEP(llvmTypes.lambdaFatPtrType, fatFctPtr, 1);
1082 // The uniform lambda calling convention always loads and passes this slot, so it must hold a defined value even
1083 // when there are no captures. A null pointer is passed to (and ignored by) non-capturing targets.
1084
2/2
✓ Branch 84 → 85 taken 143 times.
✓ Branch 84 → 88 taken 76 times.
219 insertStore(capturesPtr != nullptr ? capturesPtr : llvm::ConstantPointerNull::get(builder.getPtrTy()), capturePtr);
1085
1/2
✓ Branch 93 → 94 taken 219 times.
✗ Branch 93 → 144 not taken.
219 llvm::Value *captureSizePtr = insertStructGEP(llvmTypes.lambdaFatPtrType, fatFctPtr, 2);
1086 219 insertStore(builder.getInt64(captureStructSize), captureSizePtr);
1087
1088 219 return fatFctPtr;
1089 }
1090
1091 76 llvm::Type *IRGenerator::buildCapturesContainerType(const CaptureMap &captures) const {
1092
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 76 times.
76 assert(!captures.empty());
1093
1094 // If we have only one capture that is a ptr, we can just use that ptr type
1095 76 const Capture &capture = captures.begin()->second;
1096
11/14
✓ Branch 8 → 9 taken 44 times.
✓ Branch 8 → 15 taken 32 times.
✓ Branch 9 → 10 taken 44 times.
✗ Branch 9 → 48 not taken.
✓ Branch 10 → 11 taken 44 times.
✗ Branch 10 → 48 not taken.
✓ Branch 11 → 12 taken 16 times.
✓ Branch 11 → 14 taken 28 times.
✓ Branch 12 → 13 taken 16 times.
✗ Branch 12 → 48 not taken.
✓ Branch 13 → 14 taken 8 times.
✓ Branch 13 → 15 taken 8 times.
✓ Branch 16 → 17 taken 36 times.
✓ Branch 16 → 19 taken 40 times.
76 if (captures.size() == 1 && (capture.capturedSymbol->getQualType().isPtr() || capture.getMode() == BY_REFERENCE))
1097
1/2
✓ Branch 17 → 18 taken 36 times.
✗ Branch 17 → 48 not taken.
36 return builder.getPtrTy();
1098
1099 // Create captures struct type
1100 40 std::vector<llvm::Type *> captureTypes;
1101
5/8
✓ Branch 19 → 20 taken 40 times.
✗ Branch 19 → 44 not taken.
✓ Branch 20 → 21 taken 40 times.
✗ Branch 20 → 44 not taken.
✓ Branch 21 → 22 taken 40 times.
✗ Branch 21 → 44 not taken.
✓ Branch 35 → 23 taken 90 times.
✓ Branch 35 → 36 taken 40 times.
130 for (const auto &c : captures | std::views::values) {
1102
3/4
✓ Branch 24 → 25 taken 90 times.
✗ Branch 24 → 44 not taken.
✓ Branch 25 → 26 taken 72 times.
✓ Branch 25 → 30 taken 18 times.
90 if (c.getMode() == BY_VALUE)
1103
3/6
✓ Branch 26 → 27 taken 72 times.
✗ Branch 26 → 42 not taken.
✓ Branch 27 → 28 taken 72 times.
✗ Branch 27 → 42 not taken.
✓ Branch 28 → 29 taken 72 times.
✗ Branch 28 → 42 not taken.
72 captureTypes.push_back(c.capturedSymbol->getQualType().toLLVMType(sourceFile));
1104 else
1105
2/4
✓ Branch 30 → 31 taken 18 times.
✗ Branch 30 → 43 not taken.
✓ Branch 31 → 32 taken 18 times.
✗ Branch 31 → 43 not taken.
18 captureTypes.push_back(builder.getPtrTy());
1106 }
1107
1/2
✓ Branch 37 → 38 taken 40 times.
✗ Branch 37 → 45 not taken.
40 return llvm::StructType::get(context, captureTypes);
1108 40 }
1109
1110 76 void IRGenerator::unpackCapturesToLocalVariables(const CaptureMap &captures, llvm::Value *val, llvm::Type *structType) {
1111
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 76 times.
76 assert(!captures.empty());
1112 // If we have only one capture that is a ptr, we can just load the ptr
1113 76 const Capture &firstCapture = captures.begin()->second;
1114
8/8
✓ Branch 8 → 9 taken 44 times.
✓ Branch 8 → 15 taken 32 times.
✓ Branch 11 → 12 taken 16 times.
✓ Branch 11 → 14 taken 28 times.
✓ Branch 13 → 14 taken 8 times.
✓ Branch 13 → 15 taken 8 times.
✓ Branch 16 → 17 taken 36 times.
✓ Branch 16 → 22 taken 40 times.
76 if (captures.size() == 1 && (firstCapture.capturedSymbol->getQualType().isPtr() || firstCapture.getMode() == BY_REFERENCE)) {
1115 // Interpret capturesPtr as ptr to the first and only capture
1116 36 llvm::Value *captureAddress = val;
1117 36 pushAddress(firstCapture.capturedSymbol, captureAddress);
1118 // Generate debug info
1119
2/4
✓ Branch 18 → 19 taken 36 times.
✗ Branch 18 → 51 not taken.
✓ Branch 19 → 20 taken 36 times.
✗ Branch 19 → 49 not taken.
36 diGenerator.generateLocalVarDebugInfo(firstCapture.getName(), captureAddress);
1120 } else {
1121 // Interpret capturesPtr as ptr to the captures struct
1122
2/4
✓ Branch 25 → 26 taken 40 times.
✗ Branch 25 → 52 not taken.
✓ Branch 26 → 27 taken 40 times.
✗ Branch 26 → 52 not taken.
40 llvm::Value *capturesPtr = insertLoad(builder.getPtrTy(), val);
1123
1124 40 size_t captureIdx = 0;
1125
2/2
✓ Branch 46 → 31 taken 90 times.
✓ Branch 46 → 47 taken 40 times.
130 for (const auto &[name, capture] : captures) {
1126
5/8
✓ Branch 34 → 35 taken 90 times.
✗ Branch 34 → 63 not taken.
✓ Branch 35 → 36 taken 18 times.
✓ Branch 35 → 37 taken 72 times.
✓ Branch 36 → 38 taken 18 times.
✗ Branch 36 → 63 not taken.
✓ Branch 37 → 38 taken 72 times.
✗ Branch 37 → 63 not taken.
90 const std::string valueName = capture.getMode() == BY_REFERENCE ? name + ".addr" : name;
1127
1/2
✓ Branch 38 → 39 taken 90 times.
✗ Branch 38 → 61 not taken.
90 llvm::Value *captureAddress = insertStructGEP(structType, capturesPtr, captureIdx, valueName);
1128
1/2
✓ Branch 39 → 40 taken 90 times.
✗ Branch 39 → 61 not taken.
90 pushAddress(capture.capturedSymbol, captureAddress);
1129 // Generate debug info
1130
2/4
✓ Branch 40 → 41 taken 90 times.
✗ Branch 40 → 60 not taken.
✓ Branch 41 → 42 taken 90 times.
✗ Branch 41 → 58 not taken.
90 diGenerator.generateLocalVarDebugInfo(capture.getName(), captureAddress);
1131 90 captureIdx++;
1132 90 }
1133 }
1134 76 }
1135
1136 } // namespace spice::compiler
1137