GCC Code Coverage Report


Directory: ../
File: src/irgenerator/GenValues.cpp
Date: 2024-12-24 01:17:15
Exec Total Coverage
Lines: 541 564 95.9%
Functions: 13 13 100.0%
Branches: 701 1250 56.1%

Line Branch Exec Source
1 // Copyright (c) 2021-2024 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
9 #include <llvm/IR/Module.h>
10
11 namespace spice::compiler {
12
13 10317 std::any IRGenerator::visitValue(const ValueNode *node) {
14 10317 diGenerator.setSourceLocation(node);
15
16 // Function call
17
2/2
✓ Branch 0 taken 9264 times.
✓ Branch 1 taken 1053 times.
10317 if (node->fctCall)
18 9264 return visit(node->fctCall);
19
20 // Array initialization
21
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 1007 times.
1053 if (node->arrayInitialization)
22 46 return visit(node->arrayInitialization);
23
24 // Struct instantiation
25
2/2
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 845 times.
1007 if (node->structInstantiation)
26 162 return visit(node->structInstantiation);
27
28 // Lambda function
29
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 840 times.
845 if (node->lambdaFunc)
30 5 return visit(node->lambdaFunc);
31
32 // Lambda procedure
33
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 814 times.
840 if (node->lambdaProc)
34 26 return visit(node->lambdaProc);
35
36 // Lambda expression
37
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 813 times.
814 if (node->lambdaExpr)
38 1 return visit(node->lambdaExpr);
39
40
1/2
✓ Branch 0 taken 813 times.
✗ Branch 1 not taken.
813 if (node->isNil) {
41 // Retrieve type of the nil constant
42
2/4
✓ Branch 1 taken 813 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 813 times.
✗ Branch 5 not taken.
813 const auto nilType = any_cast<llvm::Type *>(visit(node->nilType));
43 // Create constant nil value
44 813 llvm::Constant *nilValue = llvm::Constant::getNullValue(nilType);
45 // Return it
46
1/2
✓ Branch 1 taken 813 times.
✗ Branch 2 not taken.
1626 return LLVMExprResult{.constant = nilValue};
47 }
48
49 throw CompilerError(UNHANDLED_BRANCH, "Value fall-through"); // GCOV_EXCL_LINE
50 }
51
52 9706 std::any IRGenerator::visitConstant(const ConstantNode *node) {
53
3/6
✓ Branch 1 taken 9706 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 9706 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 9706 times.
✗ Branch 9 not taken.
9706 return getConst(node->getCompileTimeValue(), node->getEvaluatedSymbolType(manIdx), node);
54 }
55
56 9264 std::any IRGenerator::visitFctCall(const FctCallNode *node) {
57
1/2
✓ Branch 1 taken 9264 times.
✗ Branch 2 not taken.
9264 diGenerator.setSourceLocation(node);
58
59
1/2
✓ Branch 1 taken 9264 times.
✗ Branch 2 not taken.
9264 const FctCallNode::FctCallData &data = node->data.at(manIdx);
60
61 9264 Function *spiceFunc = data.callee;
62 9264 std::string mangledName;
63
2/2
✓ Branch 1 taken 9228 times.
✓ Branch 2 taken 36 times.
9264 if (!data.isFctPtrCall())
64
1/2
✓ Branch 1 taken 9228 times.
✗ Branch 2 not taken.
9228 mangledName = spiceFunc->getMangledName();
65 9264 std::vector<llvm::Value *> argValues;
66
67 // Get entry of the first fragment
68 9264 SymbolTableEntry *firstFragEntry = currentScope->lookup(node->functionNameFragments.front());
69
70 // Get this type
71 9264 llvm::Value *thisPtr = nullptr;
72
2/2
✓ Branch 1 taken 4485 times.
✓ Branch 2 taken 4779 times.
9264 if (data.isMethodCall()) {
73
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4485 times.
4485 assert(!data.isCtorCall());
74
75 // Retrieve entry of the first fragment
76
2/4
✓ Branch 1 taken 4485 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4485 times.
✗ Branch 5 not taken.
4485 const QualType baseType = firstFragEntry->getQualType().getBase();
77
3/6
✓ Branch 0 taken 4485 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4485 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4485 times.
✗ Branch 6 not taken.
4485 assert(firstFragEntry != nullptr && baseType.isOneOf({TY_STRUCT, TY_INTERFACE}));
78
1/2
✓ Branch 1 taken 4485 times.
✗ Branch 2 not taken.
4485 Scope *structScope = baseType.getBodyScope();
79
80 // Get address of the referenced variable / struct instance
81
1/2
✓ Branch 1 taken 4485 times.
✗ Branch 2 not taken.
4485 thisPtr = firstFragEntry->getAddress();
82
83 // Auto de-reference 'this' pointer
84
1/2
✓ Branch 1 taken 4485 times.
✗ Branch 2 not taken.
4485 QualType firstFragmentType = firstFragEntry->getQualType();
85
1/2
✓ Branch 1 taken 4485 times.
✗ Branch 2 not taken.
4485 autoDeReferencePtr(thisPtr, firstFragmentType);
86
1/2
✓ Branch 1 taken 4485 times.
✗ Branch 2 not taken.
4485 llvm::Type *structTy = baseType.toLLVMType(sourceFile);
87
88 // Traverse through structs - the first fragment is already looked up and the last one is the function name
89
2/2
✓ Branch 1 taken 624 times.
✓ Branch 2 taken 4485 times.
5109 for (size_t i = 1; i < node->functionNameFragments.size() - 1; i++) {
90
2/4
✓ Branch 1 taken 624 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 624 times.
✗ Branch 5 not taken.
624 const std::string identifier = node->functionNameFragments.at(i);
91 // Retrieve field entry
92 624 SymbolTableEntry *fieldEntry = structScope->lookupStrict(identifier);
93
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 624 times.
624 assert(fieldEntry != nullptr);
94
1/2
✓ Branch 1 taken 624 times.
✗ Branch 2 not taken.
624 QualType fieldEntryType = fieldEntry->getQualType();
95
3/6
✓ Branch 1 taken 624 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 624 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 624 times.
624 assert(fieldEntryType.getBase().isOneOf({TY_STRUCT, TY_INTERFACE}));
96 // Get struct type and scope
97
2/4
✓ Branch 1 taken 624 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 624 times.
✗ Branch 5 not taken.
624 structScope = fieldEntryType.getBase().getBodyScope();
98
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 624 times.
624 assert(structScope != nullptr);
99 // Get address of field
100
2/4
✓ Branch 1 taken 624 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 624 times.
✗ Branch 5 not taken.
624 llvm::Value *indices[2] = {builder.getInt64(0), builder.getInt32(fieldEntry->orderIndex)};
101
2/4
✓ Branch 1 taken 624 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 624 times.
✗ Branch 6 not taken.
624 thisPtr = insertInBoundsGEP(structTy, thisPtr, indices);
102 // Auto de-reference pointer and get new struct type
103
1/2
✓ Branch 1 taken 624 times.
✗ Branch 2 not taken.
624 autoDeReferencePtr(thisPtr, fieldEntryType);
104
2/4
✓ Branch 1 taken 624 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 624 times.
✗ Branch 5 not taken.
624 structTy = fieldEntryType.getBase().toLLVMType(sourceFile);
105 624 }
106
107 // Add 'this' pointer to the front of the argument list
108
1/2
✓ Branch 1 taken 4485 times.
✗ Branch 2 not taken.
4485 argValues.push_back(thisPtr);
109 }
110
111
2/2
✓ Branch 1 taken 1144 times.
✓ Branch 2 taken 8120 times.
9264 if (data.isCtorCall()) {
112
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1144 times.
1144 assert(!data.isMethodCall());
113
114
1/2
✓ Branch 1 taken 1144 times.
✗ Branch 2 not taken.
1144 llvm::Type *thisType = spiceFunc->thisType.toLLVMType(sourceFile);
115
2/4
✓ Branch 1 taken 1144 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1144 times.
✗ Branch 5 not taken.
1144 thisPtr = insertAlloca(thisType);
116
117 // Add 'this' pointer to the front of the argument list
118
1/2
✓ Branch 1 taken 1144 times.
✗ Branch 2 not taken.
1144 argValues.push_back(thisPtr);
119 }
120
121 // If we have a lambda call that takes captures, add them to the argument list
122 9264 llvm::Value *fctPtr = nullptr;
123
2/2
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 9228 times.
9264 if (data.isFctPtrCall()) {
124
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
36 llvm::Value *fatPtr = firstFragEntry->getAddress();
125 // Load fctPtr
126
3/6
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 36 times.
✗ Branch 9 not taken.
36 llvm::StructType *fatStructType = llvm::StructType::get(context, {builder.getPtrTy(), builder.getPtrTy()});
127
2/4
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
36 fctPtr = insertStructGEP(fatStructType, fatPtr, 0);
128
4/6
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 26 times.
36 if (firstFragEntry->getQualType().hasLambdaCaptures()) {
129 // Load captures struct
130
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
20 llvm::Value *capturesPtrPtr = insertStructGEP(fatStructType, fatPtr, 1);
131
3/6
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
10 llvm::Value *capturesPtr = insertLoad(builder.getPtrTy(), capturesPtrPtr, false, CAPTURES_PARAM_NAME);
132 // Add captures to argument list
133
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 argValues.push_back(capturesPtr);
134 }
135 }
136
137 // Get arg values
138
2/2
✓ Branch 0 taken 6784 times.
✓ Branch 1 taken 2480 times.
9264 if (node->hasArgs) {
139
1/2
✓ Branch 2 taken 6784 times.
✗ Branch 3 not taken.
6784 argValues.reserve(node->argLst->args.size());
140
1/2
✓ Branch 1 taken 6784 times.
✗ Branch 2 not taken.
6784 const std::vector<AssignExprNode *> args = node->argLst->args;
141 const QualTypeList paramSTypes =
142
6/10
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 6762 times.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 22 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 22 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 6762 times.
✗ Branch 14 not taken.
6784 data.isFctPtrCall() ? firstFragEntry->getQualType().getBase().getFunctionParamTypes() : spiceFunc->getParamTypes();
143
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 6784 times.
6784 assert(paramSTypes.size() == args.size());
144
2/2
✓ Branch 1 taken 10196 times.
✓ Branch 2 taken 6784 times.
16980 for (size_t i = 0; i < args.size(); i++) {
145
1/2
✓ Branch 1 taken 10196 times.
✗ Branch 2 not taken.
10196 AssignExprNode *argNode = args.at(i);
146
1/2
✓ Branch 1 taken 10196 times.
✗ Branch 2 not taken.
10196 const QualType &expectedSTy = paramSTypes.at(i);
147
1/2
✓ Branch 1 taken 10196 times.
✗ Branch 2 not taken.
10196 const QualType &actualSTy = argNode->getEvaluatedSymbolType(manIdx);
148
149 10981 const auto matchFct = [](const QualType &lhsTy, const QualType &rhsTy) {
150
4/4
✓ Branch 1 taken 798 times.
✓ Branch 2 taken 10183 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 797 times.
10981 return lhsTy.matches(rhsTy, false, true, true) || lhsTy.matchesInterfaceImplementedByStruct(rhsTy);
151 };
152
153 // If the arrays are both of size -1 or 0, they are both pointers and do not need to be cast implicitly
154
3/4
✓ Branch 1 taken 10196 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9399 times.
✓ Branch 4 taken 797 times.
10196 if (matchFct(expectedSTy, actualSTy)) {
155 // Resolve address if actual type is reference, otherwise value
156
5/8
✓ Branch 1 taken 9399 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 317 times.
✓ Branch 4 taken 9082 times.
✓ Branch 6 taken 317 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 9082 times.
✗ Branch 10 not taken.
9399 llvm::Value *argValue = actualSTy.isRef() ? resolveAddress(argNode) : resolveValue(argNode);
157
1/2
✓ Branch 1 taken 9399 times.
✗ Branch 2 not taken.
9399 argValues.push_back(argValue);
158
8/12
✓ Branch 1 taken 797 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 686 times.
✓ Branch 4 taken 111 times.
✓ Branch 6 taken 686 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 686 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 686 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 686 times.
✓ Branch 14 taken 111 times.
797 } else if (expectedSTy.isRef() && matchFct(expectedSTy.getContained(), actualSTy)) { // Matches with ref
159
1/2
✓ Branch 1 taken 686 times.
✗ Branch 2 not taken.
686 llvm::Value *argAddress = resolveAddress(argNode);
160
1/2
✓ Branch 1 taken 686 times.
✗ Branch 2 not taken.
686 argValues.push_back(argAddress);
161
8/12
✓ Branch 1 taken 111 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 99 times.
✓ Branch 4 taken 12 times.
✓ Branch 6 taken 99 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 99 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 99 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 99 times.
✓ Branch 14 taken 12 times.
111 } else if (actualSTy.isRef() && matchFct(expectedSTy, actualSTy.getContained())) { // Matches with ref
162
1/2
✓ Branch 1 taken 99 times.
✗ Branch 2 not taken.
99 llvm::Value *argAddress = resolveValue(argNode);
163
1/2
✓ Branch 1 taken 99 times.
✗ Branch 2 not taken.
99 argValues.push_back(argAddress);
164 } else { // Need implicit cast
165
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 llvm::Value *argAddress = resolveAddress(argNode);
166
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 argValues.push_back(doImplicitCast(argAddress, expectedSTy, actualSTy));
167 }
168 }
169 6784 }
170
171 // Retrieve return and param types
172
1/2
✓ Branch 1 taken 9264 times.
✗ Branch 2 not taken.
9264 QualType returnSType(TY_DYN);
173 9264 QualTypeList paramSTypes;
174
2/2
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 9228 times.
9264 if (data.isFctPtrCall()) {
175
4/6
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 26 times.
36 if (firstFragEntry->getQualType().isBase(TY_FUNCTION))
176
3/6
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
10 returnSType = firstFragEntry->getQualType().getBase().getFunctionReturnType();
177
3/6
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 36 times.
✗ Branch 8 not taken.
36 paramSTypes = firstFragEntry->getQualType().getBase().getFunctionParamTypes();
178 } else {
179 9228 returnSType = spiceFunc->returnType;
180
1/2
✓ Branch 1 taken 9228 times.
✗ Branch 2 not taken.
9228 paramSTypes = spiceFunc->getParamTypes();
181 }
182
183 // Function is not defined in the current module -> declare it
184 llvm::FunctionType *fctType;
185
3/4
✓ Branch 2 taken 9264 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7619 times.
✓ Branch 5 taken 1645 times.
9264 if (llvm::Function *fct = module->getFunction(mangledName)) {
186
1/2
✓ Branch 1 taken 7619 times.
✗ Branch 2 not taken.
7619 fctType = fct->getFunctionType();
187 } else {
188 // Get returnType
189
1/2
✓ Branch 1 taken 1645 times.
✗ Branch 2 not taken.
1645 llvm::Type *returnType = builder.getVoidTy();
190
3/4
✓ Branch 1 taken 1645 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1021 times.
✓ Branch 4 taken 624 times.
1645 if (!returnSType.is(TY_DYN))
191
1/2
✓ Branch 1 taken 1021 times.
✗ Branch 2 not taken.
1021 returnType = returnSType.toLLVMType(sourceFile);
192
193 // Get arg types
194 1645 std::vector<llvm::Type *> argTypes;
195
6/6
✓ Branch 1 taken 763 times.
✓ Branch 2 taken 882 times.
✓ Branch 4 taken 224 times.
✓ Branch 5 taken 539 times.
✓ Branch 6 taken 1106 times.
✓ Branch 7 taken 539 times.
1645 if (data.isMethodCall() || data.isCtorCall())
196
2/4
✓ Branch 1 taken 1106 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1106 times.
✗ Branch 5 not taken.
1106 argTypes.push_back(builder.getPtrTy()); // This pointer
197
8/10
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 1609 times.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 36 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 10 times.
✓ Branch 10 taken 26 times.
✓ Branch 11 taken 10 times.
✓ Branch 12 taken 1635 times.
1645 if (data.isFctPtrCall() && firstFragEntry->getQualType().hasLambdaCaptures())
198
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 argTypes.push_back(builder.getPtrTy()); // Capture pointer
199
2/2
✓ Branch 4 taken 1419 times.
✓ Branch 5 taken 1645 times.
3064 for (const QualType &paramType : paramSTypes)
200
2/4
✓ Branch 1 taken 1419 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1419 times.
✗ Branch 5 not taken.
1419 argTypes.push_back(paramType.toLLVMType(sourceFile));
201
202
1/2
✓ Branch 2 taken 1645 times.
✗ Branch 3 not taken.
1645 fctType = llvm::FunctionType::get(returnType, argTypes, false);
203
7/8
✓ Branch 1 taken 1609 times.
✓ Branch 2 taken 36 times.
✓ Branch 4 taken 1609 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1601 times.
✓ Branch 7 taken 8 times.
✓ Branch 8 taken 1601 times.
✓ Branch 9 taken 44 times.
1645 if (!data.isFctPtrCall() && !data.isVirtualMethodCall())
204
1/2
✓ Branch 2 taken 1601 times.
✗ Branch 3 not taken.
1601 module->getOrInsertFunction(mangledName, fctType);
205 1645 }
206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9264 times.
9264 assert(fctType != nullptr);
207
208 llvm::CallInst *result;
209
3/4
✓ Branch 1 taken 9264 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 9256 times.
9264 if (data.isVirtualMethodCall()) {
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 assert(data.callee->isVirtual);
211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 assert(thisPtr != nullptr);
212 // Load VTable
213
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
8 llvm::Value *vtablePtr = insertLoad(builder.getPtrTy(), thisPtr, false, "vtable.addr");
214 8 const size_t vtableIndex = data.callee->vtableIndex;
215 // Lookup function pointer in VTable
216
4/8
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 8 times.
✗ Branch 12 not taken.
16 fctPtr = insertInBoundsGEP(builder.getPtrTy(), vtablePtr, builder.getInt64(vtableIndex), "vfct.addr");
217
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
8 llvm::Value *fct = insertLoad(builder.getPtrTy(), fctPtr, false, "fct");
218
219 // Generate function call
220
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
8 result = builder.CreateCall({fctType, fct}, argValues);
221
2/2
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 9220 times.
9256 } else if (data.isFctPtrCall()) {
222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 assert(firstFragEntry != nullptr);
223
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
36 QualType firstFragType = firstFragEntry->getQualType();
224
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if (!fctPtr)
225 fctPtr = firstFragEntry->getAddress();
226
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
36 autoDeReferencePtr(fctPtr, firstFragType);
227
3/6
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 36 times.
✗ Branch 8 not taken.
36 llvm::Value *fct = insertLoad(builder.getPtrTy(), fctPtr, false, "fct");
228
229 // Generate function call
230
2/4
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 36 times.
✗ Branch 7 not taken.
36 result = builder.CreateCall({fctType, fct}, argValues);
231 } else {
232 // Get callee function
233
1/2
✓ Branch 2 taken 9220 times.
✗ Branch 3 not taken.
9220 llvm::Function *callee = module->getFunction(mangledName);
234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9220 times.
9220 assert(callee != nullptr);
235
236 // Generate function call
237
3/6
✓ Branch 1 taken 9220 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 9220 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 9220 times.
✗ Branch 9 not taken.
9220 result = builder.CreateCall(callee, argValues);
238 }
239
240
8/10
✓ Branch 1 taken 4779 times.
✓ Branch 2 taken 4485 times.
✓ Branch 4 taken 3635 times.
✓ Branch 5 taken 1144 times.
✓ Branch 7 taken 3635 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 3635 times.
✓ Branch 11 taken 5629 times.
✓ Branch 12 taken 3635 times.
9264 if (data.isMethodCall() || data.isCtorCall() || data.isVirtualMethodCall()) {
241
1/2
✓ Branch 1 taken 5629 times.
✗ Branch 2 not taken.
5629 llvm::Type *thisType = data.thisType.toLLVMType(sourceFile);
242
1/2
✓ Branch 1 taken 5629 times.
✗ Branch 2 not taken.
5629 result->addParamAttr(0, llvm::Attribute::NoUndef);
243
1/2
✓ Branch 1 taken 5629 times.
✗ Branch 2 not taken.
5629 result->addParamAttr(0, llvm::Attribute::NonNull);
244
3/6
✓ Branch 2 taken 5629 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5629 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 5629 times.
✗ Branch 9 not taken.
5629 result->addDereferenceableParamAttr(0, module->getDataLayout().getTypeStoreSize(thisType));
245
3/6
✓ Branch 2 taken 5629 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5629 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 5629 times.
✗ Branch 9 not taken.
5629 result->addParamAttr(0, llvm::Attribute::getWithAlignment(context, module->getDataLayout().getABITypeAlign(thisType)));
246 }
247
248 // Attach address to anonymous symbol to keep track of de-allocation
249 9264 SymbolTableEntry *anonymousSymbol = nullptr;
250 9264 llvm::Value *resultPtr = nullptr;
251
7/8
✓ Branch 1 taken 9264 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8682 times.
✓ Branch 4 taken 582 times.
✓ Branch 6 taken 1144 times.
✓ Branch 7 taken 7538 times.
✓ Branch 8 taken 1726 times.
✓ Branch 9 taken 7538 times.
9264 if (returnSType.is(TY_STRUCT) || data.isCtorCall()) {
252
1/2
✓ Branch 1 taken 1726 times.
✗ Branch 2 not taken.
1726 anonymousSymbol = currentScope->symbolTable.lookupAnonymous(node->codeLoc);
253
2/2
✓ Branch 0 taken 381 times.
✓ Branch 1 taken 1345 times.
1726 if (anonymousSymbol != nullptr) {
254
2/2
✓ Branch 1 taken 152 times.
✓ Branch 2 taken 229 times.
381 if (data.isCtorCall()) {
255
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 anonymousSymbol->updateAddress(thisPtr);
256 } else {
257
2/4
✓ Branch 1 taken 229 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 229 times.
✗ Branch 6 not taken.
229 resultPtr = insertAlloca(result->getType());
258
1/2
✓ Branch 1 taken 229 times.
✗ Branch 2 not taken.
229 insertStore(result, resultPtr);
259
1/2
✓ Branch 1 taken 229 times.
✗ Branch 2 not taken.
229 anonymousSymbol->updateAddress(resultPtr);
260 }
261 }
262 }
263
264 // In case this is a constructor call, return the thisPtr as pointer
265
2/2
✓ Branch 1 taken 1144 times.
✓ Branch 2 taken 8120 times.
9264 if (data.isCtorCall())
266
1/2
✓ Branch 1 taken 1144 times.
✗ Branch 2 not taken.
2288 return LLVMExprResult{.ptr = thisPtr, .refPtr = resultPtr, .entry = anonymousSymbol};
267
268 // In case this is a callee, returning a reference, return the address
269
3/4
✓ Branch 1 taken 8120 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 373 times.
✓ Branch 4 taken 7747 times.
8120 if (returnSType.isRef())
270
1/2
✓ Branch 1 taken 373 times.
✗ Branch 2 not taken.
746 return LLVMExprResult{.ptr = result, .refPtr = resultPtr, .entry = anonymousSymbol};
271
272 // Otherwise return the value
273
1/2
✓ Branch 1 taken 7747 times.
✗ Branch 2 not taken.
15494 return LLVMExprResult{.value = result, .ptr = resultPtr, .entry = anonymousSymbol};
274 9264 }
275
276 46 std::any IRGenerator::visitArrayInitialization(const ArrayInitializationNode *node) {
277 // Return immediately if the initialization is empty
278
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (node->actualSize == 0)
279 return LLVMExprResult{.node = node};
280
281 // Visit array items
282 46 bool canBeConstant = true;
283 46 std::vector<LLVMExprResult> itemResults;
284
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 itemResults.reserve(node->actualSize);
285
2/2
✓ Branch 5 taken 294 times.
✓ Branch 6 taken 46 times.
340 for (const AssignExprNode *itemNode : node->itemLst->args) {
286
2/4
✓ Branch 1 taken 294 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 294 times.
✗ Branch 5 not taken.
294 auto item = std::any_cast<LLVMExprResult>(visit(itemNode));
287 294 canBeConstant &= item.constant != nullptr;
288 294 item.node = itemNode;
289
1/2
✓ Branch 1 taken 294 times.
✗ Branch 2 not taken.
294 itemResults.push_back(item);
290 }
291
292 // Get LLVM type of item and array
293
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
46 assert(!itemResults.empty());
294
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 const QualType &firstItemSTy = node->itemLst->args.front()->getEvaluatedSymbolType(manIdx);
295
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 llvm::Type *itemType = firstItemSTy.toLLVMType(sourceFile);
296
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 llvm::ArrayType *arrayType = llvm::ArrayType::get(itemType, node->actualSize);
297
298
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 2 times.
46 if (canBeConstant) { // All items are constants, so we can create a global constant array
299 // Collect constants
300 44 std::vector<llvm::Constant *> constants;
301
2/2
✓ Branch 5 taken 289 times.
✓ Branch 6 taken 44 times.
333 for (const LLVMExprResult &exprResult : itemResults) {
302 // Delete potential constant globals, that were already created a layer below
303
2/2
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 267 times.
289 if (exprResult.constant->getType()->isArrayTy())
304
3/6
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 22 times.
✗ Branch 8 not taken.
22 module->getNamedGlobal(exprResult.ptr->getName())->eraseFromParent();
305
1/2
✓ Branch 1 taken 289 times.
✗ Branch 2 not taken.
289 constants.push_back(exprResult.constant);
306 }
307
308 // Create global array
309
1/2
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
44 llvm::Constant *constantArray = llvm::ConstantArray::get(arrayType, constants);
310
2/4
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
44 llvm::Value *arrayAddr = createGlobalConst(ANON_GLOBAL_ARRAY_NAME, constantArray);
311
312
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 return LLVMExprResult{.constant = constantArray, .ptr = arrayAddr};
313 44 } else { // We have non-immediate values as items, so we need to take normal arrays as fallback
314
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 llvm::Value *arrayAddr = insertAlloca(arrayType);
315
316 // Retrieve address of first item
317
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
2 llvm::Value *firstItemAddress = insertInBoundsGEP(arrayType, arrayAddr, builder.getInt64(0));
318
319 // Store all array items at their corresponding offsets
320 2 llvm::Value *currentItemAddress = firstItemAddress;
321
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 2 times.
7 for (size_t i = 0; i < itemResults.size(); i++) {
322 5 LLVMExprResult &exprResult = itemResults[i];
323
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 llvm::Value *itemValue = resolveValue(exprResult.node, exprResult);
324 // Retrieve current item address
325
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if (i >= 1)
326
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
6 currentItemAddress = insertInBoundsGEP(itemType, currentItemAddress, builder.getInt64(1));
327 // Store the item value
328
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
5 const bool storeVolatile = exprResult.entry != nullptr && exprResult.entry->isVolatile;
329
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 insertStore(itemValue, currentItemAddress, storeVolatile);
330 }
331
332
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 return LLVMExprResult{.ptr = arrayAddr};
333 }
334 46 }
335
336 162 std::any IRGenerator::visitStructInstantiation(const StructInstantiationNode *node) {
337 // Get struct object
338
1/2
✓ Branch 1 taken 162 times.
✗ Branch 2 not taken.
162 const Struct *spiceStruct = node->instantiatedStructs.at(manIdx);
339
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 162 times.
162 assert(spiceStruct != nullptr);
340 162 const QualTypeList &fieldTypes = spiceStruct->fieldTypes;
341
342 // Can only be constant if none of the fields is of type reference
343
1/2
✓ Branch 1 taken 162 times.
✗ Branch 2 not taken.
162 bool canBeConstant = !spiceStruct->hasReferenceFields();
344
345 // Get struct type
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 162 times.
162 assert(spiceStruct->entry != nullptr);
347
2/4
✓ Branch 1 taken 162 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 162 times.
✗ Branch 5 not taken.
162 const auto structType = reinterpret_cast<llvm::StructType *>(spiceStruct->entry->getQualType().toLLVMType(sourceFile));
348
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 162 times.
162 assert(structType != nullptr);
349
350
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 152 times.
162 if (!node->fieldLst) {
351
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 llvm::Constant *constantStruct = getDefaultValueForSymbolType(spiceStruct->entry->getQualType());
352
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
20 return LLVMExprResult{.constant = constantStruct};
353 }
354
355 // Visit struct field values
356 152 std::vector<LLVMExprResult> fieldValueResults;
357
1/2
✓ Branch 2 taken 152 times.
✗ Branch 3 not taken.
152 fieldValueResults.reserve(spiceStruct->fieldTypes.size());
358
2/2
✓ Branch 4 taken 230 times.
✓ Branch 5 taken 152 times.
382 for (const AssignExprNode *fieldValueNode : node->fieldLst->args) {
359
2/4
✓ Branch 1 taken 230 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 230 times.
✗ Branch 5 not taken.
230 auto fieldValue = std::any_cast<LLVMExprResult>(visit(fieldValueNode));
360 230 fieldValue.node = fieldValueNode;
361
1/2
✓ Branch 1 taken 230 times.
✗ Branch 2 not taken.
230 fieldValueResults.push_back(fieldValue);
362 230 canBeConstant &= fieldValue.constant != nullptr;
363 }
364
365
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 134 times.
152 if (canBeConstant) { // All field values are constants, so we can create a global constant struct instantiation
366 // Collect constants
367 18 std::vector<llvm::Constant *> constants;
368 // For each interface a nullptr
369
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
18 for (const QualType &interfaceType : spiceStruct->interfaceTypes)
370 constants.push_back(getDefaultValueForSymbolType(interfaceType));
371 // Constant value for each field
372
2/2
✓ Branch 5 taken 41 times.
✓ Branch 6 taken 18 times.
59 for (const LLVMExprResult &exprResult : fieldValueResults)
373
1/2
✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
41 constants.push_back(exprResult.constant);
374
375 // Create global constant struct
376
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 llvm::Constant *constantStruct = llvm::ConstantStruct::get(structType, constants);
377
378
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 return LLVMExprResult{.constant = constantStruct};
379 18 } else { // We have at least one non-immediate value, so we need to take normal struct instantiation as fallback
380
2/4
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 134 times.
✗ Branch 5 not taken.
134 llvm::Value *structAddr = insertAlloca(structType);
381 134 const size_t interfaceCount = spiceStruct->interfaceTypes.size();
382 134 const size_t fieldCount = spiceStruct->fieldTypes.size();
383 134 size_t i = 0;
384
385 // Store interface values at their corresponding offsets
386
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134 times.
134 for (; i < interfaceCount; i++) {
387 const QualType &interfaceType = spiceStruct->interfaceTypes.at(i);
388 // Get field value
389 llvm::Value *itemValue = getDefaultValueForSymbolType(interfaceType);
390 // Get field address
391 llvm::Value *currentFieldAddress = insertStructGEP(structType, structAddr, i);
392 // Store the item value
393 insertStore(itemValue, currentFieldAddress);
394 }
395
396 // Store all field values at their corresponding offsets
397
2/2
✓ Branch 0 taken 189 times.
✓ Branch 1 taken 134 times.
323 for (; i < interfaceCount + fieldCount; i++) {
398
1/2
✓ Branch 1 taken 189 times.
✗ Branch 2 not taken.
189 LLVMExprResult &exprResult = fieldValueResults.at(i);
399 // Get field value
400
6/10
✓ Branch 1 taken 189 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 189 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 185 times.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 185 times.
✗ Branch 13 not taken.
189 llvm::Value *itemValue = fieldTypes.at(i).isRef() ? resolveAddress(exprResult) : resolveValue(exprResult.node, exprResult);
401 // Get field address
402
2/4
✓ Branch 1 taken 189 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 189 times.
✗ Branch 5 not taken.
189 llvm::Value *currentFieldAddress = insertStructGEP(structType, structAddr, i);
403 // Store the item value
404
3/4
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 122 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 67 times.
189 const bool storeVolatile = exprResult.entry != nullptr && exprResult.entry->isVolatile;
405
1/2
✓ Branch 1 taken 189 times.
✗ Branch 2 not taken.
189 insertStore(itemValue, currentFieldAddress, storeVolatile);
406 }
407
408 // Attach address to anonymous symbol to keep track of de-allocation
409
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 SymbolTableEntry *returnSymbol = currentScope->symbolTable.lookupAnonymous(node->codeLoc);
410
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 125 times.
134 if (returnSymbol != nullptr)
411
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 returnSymbol->updateAddress(structAddr);
412
413
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
268 return LLVMExprResult{.ptr = structAddr};
414 }
415 152 }
416
417 5 std::any IRGenerator::visitLambdaFunc(const LambdaFuncNode *node) {
418
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 Function spiceFunc = node->manifestations.at(manIdx);
419 5 ParamInfoList paramInfoList;
420 5 std::vector<llvm::Type *> paramTypes;
421
422 // Change scope
423
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId());
424
425 // If there are captures, we pass them in a struct as the first function argument
426 5 const CaptureMap &captures = bodyScope->symbolTable.captures;
427 5 const bool hasCaptures = !captures.empty();
428 5 llvm::Type *capturesStructType = nullptr;
429
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (hasCaptures) {
430 // Create captures struct type
431
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 capturesStructType = buildCapturesContainerType(captures);
432 // Add the captures struct as first parameter
433
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 paramInfoList.emplace_back(CAPTURES_PARAM_NAME, nullptr);
434
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 paramTypes.push_back(builder.getPtrTy()); // The capture struct is always passed as pointer
435 }
436
437 // Visit parameters
438 5 size_t argIdx = 0;
439
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 if (node->hasParams) {
440 4 const size_t numOfParams = spiceFunc.paramList.size();
441
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 paramInfoList.reserve(numOfParams);
442
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 paramTypes.reserve(numOfParams);
443
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 for (; argIdx < numOfParams; argIdx++) {
444
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 const DeclStmtNode *param = node->paramLst->params.at(argIdx);
445 // Get symbol table entry of param
446
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 SymbolTableEntry *paramSymbol = currentScope->lookupStrict(param->varName);
447
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 assert(paramSymbol != nullptr);
448 // Retrieve type of param
449
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
6 llvm::Type *paramType = spiceFunc.getParamTypes().at(argIdx).toLLVMType(sourceFile);
450 // Add it to the lists
451
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 paramInfoList.emplace_back(param->varName, paramSymbol);
452
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 paramTypes.push_back(paramType);
453 }
454 }
455
456 // Get return type
457
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 llvm::Type *returnType = spiceFunc.returnType.toLLVMType(sourceFile);
458
459 // Create function or implement declared function
460
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 spiceFunc.mangleSuffix = "." + std::to_string(manIdx);
461
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 const std::string mangledName = spiceFunc.getMangledName();
462
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 llvm::FunctionType *funcType = llvm::FunctionType::get(returnType, paramTypes, false);
463
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 module->getOrInsertFunction(mangledName, funcType);
464
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 llvm::Function *lambda = module->getFunction(mangledName);
465
466 // Set attributes to function
467 5 lambda->setDSOLocal(true);
468
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 lambda->setLinkage(llvm::Function::PrivateLinkage);
469
470 // In case of captures, add attribute to captures argument
471
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (hasCaptures) {
472
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 lambda->addParamAttr(0, llvm::Attribute::NoUndef);
473
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 lambda->addParamAttr(0, llvm::Attribute::NonNull);
474
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
2 lambda->addDereferenceableParamAttr(0, module->getDataLayout().getPointerSize());
475 }
476
477 // Add debug info
478
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 diGenerator.generateFunctionDebugInfo(lambda, &spiceFunc, true);
479
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 diGenerator.setSourceLocation(node);
480
481 // Save alloca insert markers
482 5 llvm::BasicBlock *allocaInsertBlockOrig = allocaInsertBlock;
483 5 llvm::Instruction *allocaInsertInstOrig = allocaInsertInst;
484 5 llvm::BasicBlock *bOrig = builder.GetInsertBlock();
485
486 // Create entry block
487
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 llvm::BasicBlock *bEntry = createBlock();
488
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 switchToBlock(bEntry, lambda);
489
490 // Reset alloca insert markers to this block
491 5 allocaInsertBlock = bEntry;
492 5 allocaInsertInst = nullptr;
493
494 // Declare result variable
495
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
15 SymbolTableEntry *resultEntry = currentScope->lookupStrict(RETURN_VARIABLE_NAME);
496
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 assert(resultEntry != nullptr);
497
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 llvm::Value *resultAddr = insertAlloca(returnType, RETURN_VARIABLE_NAME);
498
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 resultEntry->updateAddress(resultAddr);
499 // Generate debug info
500
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 diGenerator.generateLocalVarDebugInfo(RETURN_VARIABLE_NAME, resultAddr);
501
502 // Store function argument values
503 5 llvm::Value *captureStructPtrPtr = nullptr;
504
3/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 5 times.
13 for (auto &arg : lambda->args()) {
505 // Get parameter info
506 8 const size_t argNumber = arg.getArgNo();
507
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 auto [paramName, paramSymbol] = paramInfoList.at(argNumber);
508 // Allocate space for it
509
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 llvm::Type *paramType = funcType->getParamType(argNumber);
510
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 llvm::Value *paramAddress = insertAlloca(paramType, paramName);
511 // Update the symbol table entry
512
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
8 const bool isCapturesStruct = hasCaptures && argNumber == 0;
513
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (isCapturesStruct)
514 2 captureStructPtrPtr = paramAddress;
515 else
516
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 paramSymbol->updateAddress(paramAddress);
517 // Store the value at the new address
518
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 insertStore(&arg, paramAddress);
519 // Generate debug info
520
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if (!isCapturesStruct)
521
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 diGenerator.generateLocalVarDebugInfo(paramName, paramAddress, argNumber + 1);
522 8 }
523
524 // Store the default values for optional function args
525
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 if (node->paramLst) {
526
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 const std::vector<DeclStmtNode *> params = node->paramLst->params;
527
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 for (; argIdx < params.size(); argIdx++)
528 visit(params.at(argIdx));
529 4 }
530
531 // Extract captures from captures struct
532
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (hasCaptures) {
533
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 assert(!paramInfoList.empty());
534
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 unpackCapturesToLocalVariables(captures, captureStructPtrPtr, capturesStructType);
535 }
536
537 // Visit body
538
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 visit(node->body);
539
540 // Create return statement if the block is not terminated yet
541
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (!blockAlreadyTerminated) {
542 llvm::Value *result = insertLoad(returnType, resultEntry->getAddress());
543 builder.CreateRet(result);
544 }
545
546 // Pop capture addresses
547
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (hasCaptures)
548
5/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 4 times.
✓ Branch 13 taken 2 times.
6 for (const auto &capture : captures | std::views::values)
549
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 capture.capturedSymbol->popAddress();
550
551 // Conclude debug info for function
552
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 diGenerator.concludeFunctionDebugInfo();
553
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 diGenerator.setSourceLocation(node);
554
555 // Restore alloca insert markers
556
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 builder.SetInsertPoint(bOrig);
557 5 blockAlreadyTerminated = false;
558 5 allocaInsertBlock = allocaInsertBlockOrig;
559 5 allocaInsertInst = allocaInsertInstOrig;
560
561 // Change back to original scope
562 5 currentScope = currentScope->parent;
563
564 // Verify function
565
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 verifyFunction(lambda, node->codeLoc);
566
567 // Captures, create a struct { <fct-ptr>, <capture struct ptr> }
568
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 llvm::Value *result = buildFatFctPtr(bodyScope, capturesStructType, lambda);
569
570
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 return LLVMExprResult{.ptr = result, .node = node};
571 5 }
572
573 26 std::any IRGenerator::visitLambdaProc(const LambdaProcNode *node) {
574
2/4
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
26 Function spiceFunc = node->manifestations.at(manIdx);
575 26 ParamInfoList paramInfoList;
576 26 std::vector<llvm::Type *> paramTypes;
577
578 // Change scope
579
2/4
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
26 Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId());
580
581 // If there are captures, we pass them in a struct as the first function argument
582 26 const CaptureMap &captures = bodyScope->symbolTable.captures;
583 26 const bool hasCaptures = !captures.empty();
584 26 llvm::Type *capturesStructType = nullptr;
585
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 15 times.
26 if (hasCaptures) {
586 // Create captures struct type
587
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 capturesStructType = buildCapturesContainerType(captures);
588 // Add the captures struct as first parameter
589
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 paramInfoList.emplace_back(CAPTURES_PARAM_NAME, nullptr);
590
2/4
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
11 paramTypes.push_back(builder.getPtrTy()); // The captures struct is always passed as pointer
591 }
592
593 // Visit parameters
594 26 size_t argIdx = 0;
595
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 21 times.
26 if (node->hasParams) {
596 5 const size_t numOfParams = spiceFunc.paramList.size();
597
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 paramInfoList.reserve(numOfParams);
598
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 paramTypes.reserve(numOfParams);
599
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 5 times.
12 for (; argIdx < numOfParams; argIdx++) {
600
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 const DeclStmtNode *param = node->paramLst->params.at(argIdx);
601 // Get symbol table entry of param
602
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 SymbolTableEntry *paramSymbol = currentScope->lookupStrict(param->varName);
603
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 assert(paramSymbol != nullptr);
604 // Retrieve type of param
605
3/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
7 llvm::Type *paramType = spiceFunc.getParamTypes().at(argIdx).toLLVMType(sourceFile);
606 // Add it to the lists
607
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 paramInfoList.emplace_back(param->varName, paramSymbol);
608
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 paramTypes.push_back(paramType);
609 }
610 }
611
612 // Create function or implement declared function
613
2/4
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
26 spiceFunc.mangleSuffix = "." + std::to_string(manIdx);
614
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 const std::string mangledName = spiceFunc.getMangledName();
615
2/4
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 26 times.
✗ Branch 6 not taken.
26 llvm::FunctionType *funcType = llvm::FunctionType::get(builder.getVoidTy(), paramTypes, false);
616
1/2
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
26 module->getOrInsertFunction(mangledName, funcType);
617
1/2
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
26 llvm::Function *lambda = module->getFunction(mangledName);
618
619 // Set attributes to function
620 26 lambda->setDSOLocal(true);
621
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 lambda->setLinkage(llvm::Function::PrivateLinkage);
622
623 // In case of captures, add attribute to captures argument
624
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 15 times.
26 if (hasCaptures) {
625
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 lambda->addParamAttr(0, llvm::Attribute::NoUndef);
626
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 lambda->addParamAttr(0, llvm::Attribute::NonNull);
627
2/4
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
11 lambda->addDereferenceableParamAttr(0, module->getDataLayout().getPointerSize());
628 }
629
630 // Add debug info
631
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 diGenerator.generateFunctionDebugInfo(lambda, &spiceFunc, true);
632
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 diGenerator.setSourceLocation(node);
633
634 // Save alloca insert markers
635 26 llvm::BasicBlock *allocaInsertBlockOrig = allocaInsertBlock;
636 26 llvm::Instruction *allocaInsertInstOrig = allocaInsertInst;
637 26 llvm::BasicBlock *bOrig = builder.GetInsertBlock();
638
639 // Create entry block
640
2/4
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
26 llvm::BasicBlock *bEntry = createBlock();
641
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 switchToBlock(bEntry, lambda);
642
643 // Reset alloca insert markers to this block
644 26 allocaInsertBlock = bEntry;
645 26 allocaInsertInst = nullptr;
646
647 // Save values of parameters to locals
648 26 llvm::Value *captureStructPtrPtr = nullptr;
649
3/4
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 26 times.
44 for (auto &arg : lambda->args()) {
650 // Get information about the parameter
651 18 const size_t argNumber = arg.getArgNo();
652
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 auto [paramName, paramSymbol] = paramInfoList.at(argNumber);
653 // Allocate space for it
654
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 llvm::Type *paramType = funcType->getParamType(argNumber);
655
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 llvm::Value *paramAddress = insertAlloca(paramType, paramName);
656 // Update the symbol table entry
657
4/4
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 2 times.
18 const bool isCapturesStruct = hasCaptures && argNumber == 0;
658
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 7 times.
18 if (isCapturesStruct)
659 11 captureStructPtrPtr = paramAddress;
660 else
661
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 paramSymbol->updateAddress(paramAddress);
662 // Store the value at the new address
663
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 insertStore(&arg, paramAddress);
664 // Generate debug info
665
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 11 times.
18 if (!isCapturesStruct)
666
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 diGenerator.generateLocalVarDebugInfo(paramName, paramAddress, argNumber + 1);
667 18 }
668
669 // Store the default values for optional function args
670
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 21 times.
26 if (node->paramLst) {
671
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 const std::vector<DeclStmtNode *> params = node->paramLst->params;
672
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 for (; argIdx < params.size(); argIdx++)
673 visit(params.at(argIdx));
674 5 }
675
676 // Extract captures from captures struct
677
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 15 times.
26 if (hasCaptures) {
678
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 assert(!paramInfoList.empty());
679
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 unpackCapturesToLocalVariables(captures, captureStructPtrPtr, capturesStructType);
680 }
681
682 // Visit body
683
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 visit(node->body);
684
685 // Create return statement if the block is not terminated yet
686
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 if (!blockAlreadyTerminated)
687
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 builder.CreateRetVoid();
688
689 // Pop capture addresses
690
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 15 times.
26 if (hasCaptures)
691
5/8
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 18 times.
✓ Branch 13 taken 11 times.
29 for (const auto &capture : captures | std::views::values)
692
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 capture.capturedSymbol->popAddress();
693
694 // Conclude debug info for function
695
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 diGenerator.concludeFunctionDebugInfo();
696
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 diGenerator.setSourceLocation(node);
697
698 // Restore alloca insert markers
699
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 builder.SetInsertPoint(bOrig);
700 26 blockAlreadyTerminated = false;
701 26 allocaInsertBlock = allocaInsertBlockOrig;
702 26 allocaInsertInst = allocaInsertInstOrig;
703
704 // Change back to original scope
705 26 currentScope = currentScope->parent;
706
707 // Verify function
708
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 verifyFunction(lambda, node->codeLoc);
709
710 // Create a struct { <fct-ptr>, <capture struct ptr> }
711
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 llvm::Value *result = buildFatFctPtr(bodyScope, capturesStructType, lambda);
712
713
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
52 return LLVMExprResult{.ptr = result, .node = node};
714 26 }
715
716 1 std::any IRGenerator::visitLambdaExpr(const LambdaExprNode *node) {
717
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 const Function &spiceFunc = node->manifestations.at(manIdx);
718 1 ParamInfoList paramInfoList;
719 1 std::vector<llvm::Type *> paramTypes;
720
721 // Change scope
722
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId());
723
724 // If there are captures, we pass them in a struct as the first function argument
725 1 const CaptureMap &captures = bodyScope->symbolTable.captures;
726 1 const bool hasCaptures = !captures.empty();
727 1 llvm::Type *capturesStructType = nullptr;
728
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (hasCaptures) {
729 // Create captures struct type
730 capturesStructType = buildCapturesContainerType(captures);
731 // Add the captures struct as first parameter
732 paramInfoList.emplace_back(CAPTURES_PARAM_NAME, nullptr);
733 paramTypes.push_back(builder.getPtrTy()); // The capture struct is always passed as pointer
734 }
735
736 // Visit parameters
737 1 size_t argIdx = 0;
738
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (node->hasParams) {
739 1 const size_t numOfParams = spiceFunc.paramList.size();
740
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 paramInfoList.reserve(numOfParams);
741
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 paramTypes.reserve(numOfParams);
742
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (; argIdx < numOfParams; argIdx++) {
743
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const DeclStmtNode *param = node->paramLst->params.at(argIdx);
744 // Get symbol table entry of param
745
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 SymbolTableEntry *paramSymbol = currentScope->lookupStrict(param->varName);
746
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(paramSymbol != nullptr);
747 // Retrieve type of param
748
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
2 llvm::Type *paramType = spiceFunc.getParamTypes().at(argIdx).toLLVMType(sourceFile);
749 // Add it to the lists
750
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 paramInfoList.emplace_back(param->varName, paramSymbol);
751
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 paramTypes.push_back(paramType);
752 }
753 }
754
755 // Get return type
756
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 llvm::Type *returnType = builder.getVoidTy();
757
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (spiceFunc.isFunction())
758
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 returnType = spiceFunc.returnType.toLLVMType(sourceFile);
759
760 // Create function or implement declared function
761
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 const std::string mangledName = spiceFunc.getMangledName();
762
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 llvm::FunctionType *funcType = llvm::FunctionType::get(returnType, paramTypes, false);
763
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 module->getOrInsertFunction(mangledName, funcType);
764
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 llvm::Function *lambda = module->getFunction(mangledName);
765
766 // Set attributes to function
767 1 lambda->setDSOLocal(true);
768
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 lambda->setLinkage(llvm::Function::PrivateLinkage);
769
770 // In case of captures, add attribute to captures argument
771
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (hasCaptures) {
772 lambda->addParamAttr(0, llvm::Attribute::NoUndef);
773 lambda->addParamAttr(0, llvm::Attribute::NonNull);
774 lambda->addDereferenceableParamAttr(0, module->getDataLayout().getPointerSize());
775 }
776
777 // Add debug info
778
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 diGenerator.generateFunctionDebugInfo(lambda, &spiceFunc, true);
779
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 diGenerator.setSourceLocation(node);
780
781 // Save alloca insert markers
782 1 llvm::BasicBlock *allocaInsertBlockOrig = allocaInsertBlock;
783 1 llvm::Instruction *allocaInsertInstOrig = allocaInsertInst;
784 1 llvm::BasicBlock *bOrig = builder.GetInsertBlock();
785
786 // Create entry block
787
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 llvm::BasicBlock *bEntry = createBlock();
788
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 switchToBlock(bEntry, lambda);
789
790 // Reset alloca insert markers to this block
791 1 allocaInsertBlock = bEntry;
792 1 allocaInsertInst = nullptr;
793
794 // Save values of parameters to locals
795 1 llvm::Value *captureStructPtrPtr = nullptr;
796
3/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 1 times.
3 for (auto &arg : lambda->args()) {
797 // Get information about the parameter
798 2 const size_t argNumber = arg.getArgNo();
799
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 auto [paramName, paramSymbol] = paramInfoList.at(argNumber);
800 // Allocate space for it
801
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 llvm::Type *paramType = funcType->getParamType(argNumber);
802
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 llvm::Value *paramAddress = insertAlloca(paramType, paramName);
803 // Update the symbol table entry
804
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 const bool isCapturesStruct = hasCaptures && argNumber == 0;
805
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (isCapturesStruct)
806 captureStructPtrPtr = paramAddress;
807 else
808
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 paramSymbol->updateAddress(paramAddress);
809 // Store the value at the new address
810
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 insertStore(&arg, paramAddress);
811 // Generate debug info
812
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!isCapturesStruct)
813
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 diGenerator.generateLocalVarDebugInfo(paramName, paramAddress, argNumber + 1);
814 2 }
815
816 // Store the default values for optional function args
817
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (node->paramLst) {
818
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 const std::vector<DeclStmtNode *> params = node->paramLst->params;
819
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 for (; argIdx < params.size(); argIdx++)
820 visit(params.at(argIdx));
821 1 }
822
823 // Extract captures from captures struct
824
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (hasCaptures) {
825 assert(!paramInfoList.empty());
826 unpackCapturesToLocalVariables(captures, captureStructPtrPtr, capturesStructType);
827 }
828
829 // Visit lambda expression
830
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 llvm::Value *exprResult = resolveValue(node->lambdaExpr);
831
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 builder.CreateRet(exprResult);
832
833 // Pop capture addresses
834
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (hasCaptures)
835 for (const auto &val : captures | std::views::values)
836 val.capturedSymbol->popAddress();
837
838 // Conclude debug info for function
839
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 diGenerator.concludeFunctionDebugInfo();
840
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 diGenerator.setSourceLocation(node);
841
842 // Restore alloca insert markers
843
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 builder.SetInsertPoint(bOrig);
844 1 blockAlreadyTerminated = false;
845 1 allocaInsertBlock = allocaInsertBlockOrig;
846 1 allocaInsertInst = allocaInsertInstOrig;
847
848 // Change back to original scope
849 1 currentScope = currentScope->parent;
850
851 // Verify function
852
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 verifyFunction(lambda, node->codeLoc);
853 // Create a struct { <fct-ptr>, <capture struct ptr> }
854
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 llvm::Value *result = buildFatFctPtr(bodyScope, capturesStructType, lambda);
855
856
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 return LLVMExprResult{.ptr = result, .node = node};
857 1 }
858
859 1633 std::any IRGenerator::visitDataType(const DataTypeNode *node) {
860 // Only set the source location if this is not the root scope
861
6/8
✓ Branch 0 taken 924 times.
✓ Branch 1 taken 709 times.
✓ Branch 2 taken 916 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 916 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 916 times.
✗ Branch 7 not taken.
1633 if (currentScope != rootScope && !node->isParamType && !node->isReturnType && !node->isFieldType)
862
1/2
✓ Branch 1 taken 916 times.
✗ Branch 2 not taken.
916 diGenerator.setSourceLocation(node);
863 // Retrieve symbol type
864
1/2
✓ Branch 1 taken 1633 times.
✗ Branch 2 not taken.
1633 const QualType symbolType = node->getEvaluatedSymbolType(manIdx);
865
2/4
✓ Branch 1 taken 1633 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1633 times.
1633 assert(!symbolType.is(TY_DYN)); // Symbol type should not be dyn anymore at this point
866
2/4
✓ Branch 1 taken 1633 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1633 times.
✗ Branch 5 not taken.
1633 return symbolType.toLLVMType(sourceFile);
867 }
868
869 36 llvm::Value *IRGenerator::buildFatFctPtr(Scope *bodyScope, llvm::Type *capturesStructType, llvm::Value *lambda) {
870 // Create capture struct if required
871 36 llvm::Value *capturesPtr = nullptr;
872
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 23 times.
36 if (capturesStructType != nullptr) {
873
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 assert(bodyScope != nullptr);
874 // If we have a single capture of ptr type, we can directly store it into the fat ptr. Otherwise, we need a stack allocated
875 // struct to store the captures in a memory-efficient manner and store a pointer to that struct to the fat ptr.
876
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 9 times.
13 if (capturesStructType->isPointerTy()) {
877 4 const CaptureMap &captures = bodyScope->symbolTable.captures;
878
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 assert(captures.size() == 1);
879 4 const Capture &capture = captures.begin()->second;
880
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
4 if (capture.getMode() == BY_VALUE) {
881 1 llvm::Type *varType = capture.capturedSymbol->getQualType().toLLVMType(sourceFile);
882
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
2 capturesPtr = insertLoad(varType, capture.capturedSymbol->getAddress());
883 } else {
884 3 capturesPtr = capture.capturedSymbol->getAddress();
885 }
886 } else {
887
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
9 capturesPtr = insertAlloca(capturesStructType, CAPTURES_PARAM_NAME);
888 9 size_t captureIdx = 0;
889
5/8
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 18 times.
✓ Branch 12 taken 9 times.
27 for (const auto &capture : bodyScope->symbolTable.captures | std::views::values) {
890 18 const SymbolTableEntry *capturedEntry = capture.capturedSymbol;
891 // Get address or value of captured variable, depending on the capturing mode
892
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 llvm::Value *capturedValue = capturedEntry->getAddress();
893
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 assert(capturedValue != nullptr);
894
3/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✓ Branch 4 taken 3 times.
18 if (capture.getMode() == BY_VALUE) {
895
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 llvm::Type *captureType = capturedEntry->getQualType().toLLVMType(sourceFile);
896
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
30 capturedValue = insertLoad(captureType, capturedValue);
897 }
898 // Store it in the capture struct
899
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 llvm::Value *captureAddress = insertStructGEP(capturesStructType, capturesPtr, captureIdx);
900
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 insertStore(capturedValue, captureAddress);
901 18 captureIdx++;
902 }
903 }
904 }
905
906 // Create fat ptr struct type if not exists yet
907
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 19 times.
36 if (!llvmTypes.fatPtrType)
908
3/6
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 17 times.
✗ Branch 9 not taken.
17 llvmTypes.fatPtrType = llvm::StructType::get(context, {builder.getPtrTy(), builder.getPtrTy()});
909
910 // Create fat pointer
911
2/4
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
72 llvm::Value *fatFctPtr = insertAlloca(llvmTypes.fatPtrType, "fat.ptr");
912
2/4
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
36 llvm::Value *fctPtr = insertStructGEP(llvmTypes.fatPtrType, fatFctPtr, 0);
913 36 insertStore(lambda, fctPtr);
914
2/4
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
36 llvm::Value *capturePtr = insertStructGEP(llvmTypes.fatPtrType, fatFctPtr, 1);
915
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 13 times.
36 insertStore(capturesPtr != nullptr ? capturesPtr : llvm::PoisonValue::get(builder.getPtrTy()), capturePtr);
916
917 36 return fatFctPtr;
918 }
919
920 13 llvm::Type *IRGenerator::buildCapturesContainerType(const CaptureMap &captures) const {
921
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 assert(!captures.empty());
922
923 // If we have only one capture that is a ptr, we can just use that ptr type
924 13 const Capture &capture = captures.begin()->second;
925
10/14
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 9 times.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 2 times.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 2 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 4 times.
✓ Branch 17 taken 9 times.
13 if (captures.size() == 1 && (capture.capturedSymbol->getQualType().isPtr() || capture.getMode() == BY_REFERENCE))
926
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 return builder.getPtrTy();
927
928 // Create captures struct type
929 9 std::vector<llvm::Type *> captureTypes;
930
5/8
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 18 times.
✓ Branch 13 taken 9 times.
27 for (const auto &c : captures | std::views::values) {
931
3/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✓ Branch 4 taken 3 times.
18 if (c.getMode() == BY_VALUE)
932
3/6
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 15 times.
✗ Branch 8 not taken.
15 captureTypes.push_back(c.capturedSymbol->getQualType().toLLVMType(sourceFile));
933 else
934
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 captureTypes.push_back(builder.getPtrTy());
935 }
936
1/2
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
9 return llvm::StructType::get(context, captureTypes);
937 9 }
938
939 13 void IRGenerator::unpackCapturesToLocalVariables(const CaptureMap &captures, llvm::Value *val, llvm::Type *structType) {
940
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 assert(!captures.empty());
941 // If we have only one capture that is a ptr, we can just load the ptr
942 13 const Capture &capture = captures.begin()->second;
943
7/8
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 9 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 9 times.
13 if (captures.size() == 1 && (capture.capturedSymbol->getQualType().isPtr() || capture.getMode() == BY_REFERENCE)) {
944 // Interpret capturesPtr as ptr to the first and only capture
945 4 llvm::Value *captureAddress = val;
946 4 capture.capturedSymbol->pushAddress(captureAddress);
947 // Generate debug info
948
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 diGenerator.generateLocalVarDebugInfo(capture.getName(), captureAddress);
949 } else {
950 // Interpret capturesPtr as ptr to the captures struct
951
3/6
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
9 llvm::Value *capturesPtr = insertLoad(builder.getPtrTy(), val);
952
953 9 size_t captureIdx = 0;
954
2/2
✓ Branch 7 taken 18 times.
✓ Branch 8 taken 9 times.
27 for (const auto &[name, c] : captures) {
955
5/8
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 15 times.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 15 times.
✗ Branch 10 not taken.
18 const std::string valueName = c.getMode() == BY_REFERENCE ? name + ".addr" : name;
956
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 llvm::Value *captureAddress = insertStructGEP(structType, capturesPtr, captureIdx, valueName);
957
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 c.capturedSymbol->pushAddress(captureAddress);
958 // Generate debug info
959
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 diGenerator.generateLocalVarDebugInfo(c.getName(), captureAddress);
960 18 captureIdx++;
961 18 }
962 }
963 13 }
964
965 } // namespace spice::compiler
966