GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 96.8% 629 / 1 / 651
Functions: 100.0% 16 / 0 / 16
Branches: 57.1% 773 / 4 / 1358

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