GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 97.0% 613 / 1 / 633
Functions: 100.0% 16 / 0 / 16
Branches: 57.0% 752 / 4 / 1324

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