GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 97.0% 619 / 1 / 639
Functions: 100.0% 16 / 0 / 16
Branches: 57.0% 753 / 4 / 1326

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