GCC Code Coverage Report


Directory: ../
File: src/irgenerator/GenImplicit.cpp
Date: 2024-11-22 23:10:59
Exec Total Coverage
Lines: 353 405 87.2%
Functions: 20 21 95.2%
Branches: 491 1042 47.1%

Line Branch Exec Source
1 // Copyright (c) 2021-2024 ChilliBits. All rights reserved.
2
3 #include "IRGenerator.h"
4
5 #include <SourceFile.h>
6 #include <ast/ASTNodes.h>
7 #include <ast/Attributes.h>
8 #include <driver/Driver.h>
9 #include <global/GlobalResourceManager.h>
10 #include <model/Function.h>
11 #include <symboltablebuilder/SymbolTableBuilder.h>
12
13 #include <llvm/IR/Module.h>
14
15 namespace spice::compiler {
16
17 // String placeholders for builtin testing output
18 static const char *const TEST_ALL_START_MSG = "[==========] Running %d test(s) from %d source file(s)\n";
19 static const char *const TEST_ALL_END_MSG = "[==========] Ran %d test(s) from %d source file(s)\n";
20 static const char *const TEST_FILE_START_MSG = "[----------] Running %d test(s) from %s\n";
21 static const char *const TEST_FILE_END_MSG = "[----------] Ran %d test(s) from %s\n\n";
22 static const char *const TEST_CASE_RUN_MSG = "[ RUN ] %s\n";
23 static const char *const TEST_CASE_SUCCESS_MSG = "\033[1m\033[32m[ PASSED ]\033[0m\033[22m %s\n";
24 static const char *const TEST_CASE_FAILED_MSG = "\033[1m\033[31m[ FAILED ]\033[0m\033[22m %s\n";
25 static const char *const TEST_CASE_SKIPPED_MSG = "\033[1m\033[33m[ SKIPPED ]\033[0m\033[22m %s\n";
26
27 12 llvm::Value *IRGenerator::doImplicitCast(llvm::Value *src, QualType dstSTy, QualType srcSTy) {
28
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 assert(srcSTy != dstSTy); // We only need to cast implicitly, if the types do not match exactly
29
30 // Unpack the pointers until a pointer of another type is met
31 12 size_t loadCounter = 0;
32
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 while (srcSTy.isPtr()) {
33 src = insertLoad(srcSTy.toLLVMType(sourceFile), src);
34 srcSTy = srcSTy.getContained();
35 dstSTy = dstSTy.getContained();
36 loadCounter++;
37 }
38 // GEP or bit-cast
39
3/6
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
12 if (dstSTy.isArray() && srcSTy.isArray()) { // Special case that is used for passing arrays as pointer to functions
40
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 llvm::Value *indices[2] = {builder.getInt64(0), builder.getInt32(0)};
41
3/6
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
24 src = insertInBoundsGEP(srcSTy.toLLVMType(sourceFile), src, indices);
42 } else {
43 src = insertLoad(srcSTy.toLLVMType(sourceFile), src);
44 src = builder.CreateBitCast(src, dstSTy.toLLVMType(sourceFile));
45 }
46 // Pack the pointers together again
47
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 for (; loadCounter > 0; loadCounter--) {
48 llvm::Value *newActualArg = insertAlloca(src->getType());
49 insertStore(src, newActualArg);
50 src = newActualArg;
51 }
52 12 return src;
53 }
54
55 17870 void IRGenerator::generateScopeCleanup(const StmtLstNode *node) const {
56 // Do not clean up if the block is already terminated
57
2/2
✓ Branch 0 taken 6268 times.
✓ Branch 1 taken 11602 times.
17870 if (blockAlreadyTerminated)
58 6268 return;
59
60 // Call all dtor functions
61 11602 const auto &[dtorFunctionsToCall, heapVarsToFree] = node->resourcesToCleanup.at(manIdx);
62
2/2
✓ Branch 6 taken 669 times.
✓ Branch 7 taken 11602 times.
12271 for (auto [entry, dtor] : dtorFunctionsToCall)
63
1/2
✓ Branch 2 taken 669 times.
✗ Branch 3 not taken.
669 generateCtorOrDtorCall(entry, dtor, {});
64
65 // Deallocate all heap variables that go out of scope and are currently owned
66
2/2
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 11602 times.
11605 for (const SymbolTableEntry *entry : heapVarsToFree)
67
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 generateDeallocCall(entry->getAddress());
68
69 // Generate lifetime end markers
70
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11602 times.
11602 if (cliOptions.useLifetimeMarkers) {
71 for (const SymbolTableEntry *var : currentScope->getVarsGoingOutOfScope()) {
72 llvm::Value *address = var->getAddress();
73 if (address == nullptr)
74 continue;
75 const uint64_t sizeInBytes = module->getDataLayout().getTypeAllocSize(var->getQualType().toLLVMType(sourceFile));
76 builder.CreateLifetimeEnd(address, builder.getInt64(sizeInBytes));
77 }
78 }
79 }
80
81 llvm::Value *IRGenerator::generateFctCall(const Function *fct, const std::vector<llvm::Value *> &args) const {
82 // Retrieve metadata for the function
83 const std::string mangledName = fct->getMangledName();
84
85 // Function is not defined in the current module -> declare it
86 if (!module->getFunction(mangledName)) {
87 std::vector<llvm::Type *> paramTypes;
88 for (const llvm::Value *argValue : args)
89 paramTypes.push_back(argValue->getType());
90 llvm::Type *returnType = fct->returnType.toLLVMType(sourceFile);
91 llvm::FunctionType *fctType = llvm::FunctionType::get(returnType, paramTypes, false);
92 module->getOrInsertFunction(mangledName, fctType);
93 }
94
95 // Get callee function
96 llvm::Function *callee = module->getFunction(mangledName);
97 assert(callee != nullptr);
98
99 // Generate function call
100 return builder.CreateCall(callee, args);
101 }
102
103 1062 void IRGenerator::generateProcCall(const Function *proc, std::vector<llvm::Value *> &args) const {
104 // Retrieve metadata for the function
105
1/2
✓ Branch 1 taken 1062 times.
✗ Branch 2 not taken.
1062 const std::string mangledName = proc->getMangledName();
106
107 // Function is not defined in the current module -> declare it
108
3/4
✓ Branch 2 taken 1062 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 290 times.
✓ Branch 5 taken 772 times.
1062 if (!module->getFunction(mangledName)) {
109 290 std::vector<llvm::Type *> paramTypes;
110
2/2
✓ Branch 4 taken 347 times.
✓ Branch 5 taken 290 times.
637 for (const llvm::Value *argValue : args)
111
1/2
✓ Branch 2 taken 347 times.
✗ Branch 3 not taken.
347 paramTypes.push_back(argValue->getType());
112
2/4
✓ Branch 2 taken 290 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 290 times.
✗ Branch 6 not taken.
290 llvm::FunctionType *fctType = llvm::FunctionType::get(builder.getVoidTy(), paramTypes, false);
113
1/2
✓ Branch 2 taken 290 times.
✗ Branch 3 not taken.
290 module->getOrInsertFunction(mangledName, fctType);
114 290 }
115
116 // Get callee function
117
1/2
✓ Branch 2 taken 1062 times.
✗ Branch 3 not taken.
1062 llvm::Function *callee = module->getFunction(mangledName);
118
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1062 times.
1062 assert(callee != nullptr);
119
120 // Generate function call
121
3/6
✓ Branch 1 taken 1062 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1062 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1062 times.
✗ Branch 9 not taken.
1062 builder.CreateCall(callee, args);
122 1062 }
123
124 1020 void IRGenerator::generateCtorOrDtorCall(const SymbolTableEntry *entry, const Function *ctorOrDtor,
125 const std::vector<llvm::Value *> &args) const {
126 // Retrieve address of the struct variable. For fields this is the 'this' variable, otherwise use the normal address
127 llvm::Value *structAddr;
128
2/2
✓ Branch 1 taken 319 times.
✓ Branch 2 taken 701 times.
1020 if (entry->isField()) {
129 // Take 'this' var as base pointer
130
1/2
✓ Branch 1 taken 319 times.
✗ Branch 2 not taken.
957 const SymbolTableEntry *thisVar = currentScope->lookupStrict(THIS_VARIABLE_NAME);
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 319 times.
319 assert(thisVar != nullptr);
132
7/14
✓ Branch 1 taken 319 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 319 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 319 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 319 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 319 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 319 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 319 times.
✗ Branch 18 not taken.
319 assert(thisVar->getQualType().isPtr() && thisVar->getQualType().getContained().is(TY_STRUCT));
133
3/6
✓ Branch 1 taken 319 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 319 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 319 times.
✗ Branch 8 not taken.
319 llvm::Type *thisType = thisVar->getQualType().getContained().toLLVMType(sourceFile);
134
4/8
✓ Branch 1 taken 319 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 319 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 319 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 319 times.
✗ Branch 11 not taken.
319 llvm::Value *thisPtr = insertLoad(builder.getPtrTy(), thisVar->getAddress());
135 // Add field offset
136
2/4
✓ Branch 1 taken 319 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 319 times.
✗ Branch 5 not taken.
319 llvm::Value *indices[2] = {builder.getInt64(0), builder.getInt32(entry->orderIndex)};
137
2/4
✓ Branch 1 taken 319 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 319 times.
✗ Branch 6 not taken.
638 structAddr = insertInBoundsGEP(thisType, thisPtr, indices);
138 } else {
139 701 structAddr = entry->getAddress();
140 }
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1020 times.
1020 assert(structAddr != nullptr);
142 1020 generateCtorOrDtorCall(structAddr, ctorOrDtor, args);
143 1020 }
144
145 1062 void IRGenerator::generateCtorOrDtorCall(llvm::Value *structAddr, const Function *ctorOrDtor,
146 const std::vector<llvm::Value *> &args) const {
147 // Build parameter list
148
1/2
✓ Branch 1 taken 1062 times.
✗ Branch 2 not taken.
2124 std::vector<llvm::Value *> argValues = {structAddr};
149
1/2
✓ Branch 5 taken 1062 times.
✗ Branch 6 not taken.
1062 argValues.insert(argValues.end(), args.begin(), args.end());
150
151 // Generate function call
152
1/2
✓ Branch 1 taken 1062 times.
✗ Branch 2 not taken.
1062 generateProcCall(ctorOrDtor, argValues);
153 1062 }
154
155 54 void IRGenerator::generateDeallocCall(llvm::Value *variableAddress) const {
156 // Abort if the address is not set. This can happen when leaving the scope of a dtor, which already freed the heap memory
157
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 53 times.
54 if (!variableAddress)
158 1 return;
159
160 // In case of string runtime, call free manually. Otherwise, use the memory_rt implementation of sDealloc()
161
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 53 times.
106 if (sourceFile->isStringRT()) {
162 llvm::Function *freeFct = stdFunctionManager.getFreeFct();
163 builder.CreateCall(freeFct, variableAddress);
164 } else {
165 53 llvm::Function *deallocFct = stdFunctionManager.getDeallocBytePtrRefFct();
166
3/6
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 53 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 53 times.
✗ Branch 9 not taken.
53 builder.CreateCall(deallocFct, variableAddress);
167 }
168 }
169
170 2 llvm::Function *IRGenerator::generateImplicitFunction(const std::function<void()> &generateBody, const Function *spiceFunc) {
171 // Only focus on method procedures
172
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const ASTNode *node = spiceFunc->entry->declNode;
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(spiceFunc->isFunction());
174
175 // Only generate if used
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!spiceFunc->used)
177 return nullptr;
178
179 // Retrieve return type
180
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 llvm::Type *returnType = spiceFunc->returnType.toLLVMType(sourceFile);
181
182 // Get 'this' entry
183 2 std::vector<llvm::Type *> paramTypes;
184
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 SymbolTableEntry *thisEntry = nullptr;
185
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (spiceFunc->isMethod()) {
186 thisEntry = spiceFunc->bodyScope->lookupStrict(THIS_VARIABLE_NAME);
187 assert(thisEntry != nullptr);
188 paramTypes.push_back(builder.getPtrTy());
189 }
190
191 // Get parameter types
192
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
2 for (const auto &[qualType, isOptional] : spiceFunc->paramList) {
193 assert(!isOptional);
194 paramTypes.push_back(qualType.toLLVMType(sourceFile));
195 }
196
197 // Get function linkage
198
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 const bool isPublic = spiceFunc->entry->getQualType().isPublic();
199
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 const llvm::GlobalValue::LinkageTypes linkage = isPublic ? llvm::Function::ExternalLinkage : llvm::Function::PrivateLinkage;
200
201 // Create function
202
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const std::string mangledName = spiceFunc->getMangledName();
203
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 llvm::FunctionType *fctType = llvm::FunctionType::get(returnType, paramTypes, false);
204
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 llvm::Function *fct = llvm::Function::Create(fctType, llvm::Function::ExternalLinkage, mangledName, module);
205
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 fct->setLinkage(linkage);
206
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 fct->setDoesNotRecurse();
207
208 // Set attributes to 'this' param
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (spiceFunc->isMethod()) {
210 fct->addParamAttr(0, llvm::Attribute::NoUndef);
211 fct->addParamAttr(0, llvm::Attribute::NonNull);
212 assert(thisEntry != nullptr);
213 llvm::Type *structType = thisEntry->getQualType().getContained().toLLVMType(sourceFile);
214 assert(structType != nullptr);
215 fct->addDereferenceableParamAttr(0, module->getDataLayout().getTypeStoreSize(structType));
216 fct->addParamAttr(0, llvm::Attribute::getWithAlignment(context, module->getDataLayout().getABITypeAlign(structType)));
217 }
218
219 // Add debug info
220
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 diGenerator.generateFunctionDebugInfo(fct, spiceFunc);
221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (node != nullptr)
222 diGenerator.setSourceLocation(node);
223
224 // Change to body scope
225
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 changeToScope(spiceFunc->getSignature(false), ScopeType::FUNC_PROC_BODY);
226
227 // Create entry block
228
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 llvm::BasicBlock *bEntry = createBlock();
229
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 switchToBlock(bEntry, fct);
230
231 // Reset alloca insert markers to this block
232 2 allocaInsertBlock = bEntry;
233
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 allocaInsertInst = nullptr;
234
235 // Store first argument to 'this' symbol
236
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (spiceFunc->isMethod()) {
237 assert(thisEntry != nullptr);
238 // Allocate space for the parameter
239 llvm::Value *thisAddress = insertAlloca(paramTypes.front(), THIS_VARIABLE_NAME);
240 // Update the symbol table entry
241 thisEntry->updateAddress(thisAddress);
242 // Store the value at the new address
243 insertStore(fct->arg_begin(), thisAddress);
244 // Generate debug info
245 diGenerator.generateLocalVarDebugInfo(THIS_VARIABLE_NAME, thisAddress, 1);
246 }
247
248 // Generate body
249
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 generateBody();
250
251 // Conclude debug info for function
252
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 diGenerator.concludeFunctionDebugInfo();
253
254 // Verify function
255 // Use the code location of the declaration node if available. Otherwise, (e.g. in case of test main) use an artificial code loc
256
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 const CodeLoc codeLoc = node != nullptr ? node->codeLoc : CodeLoc(1, 1, sourceFile);
257
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 verifyFunction(fct, codeLoc);
258
259 // Change to parent scope
260
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 changeToParentScope(ScopeType::FUNC_PROC_BODY);
261
262 2 return fct;
263 2 }
264
265 190 llvm::Function *IRGenerator::generateImplicitProcedure(const std::function<void()> &generateBody, const Function *spiceProc) {
266 // Only focus on method procedures
267 190 const ASTNode *node = spiceProc->entry->declNode;
268
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 190 times.
190 assert(node != nullptr);
269
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 190 times.
190 assert(spiceProc->isProcedure());
270
271 // Only generate if used
272
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 134 times.
190 if (!spiceProc->used)
273 56 return nullptr;
274
275 // Get 'this' entry
276 134 std::vector<llvm::Type *> paramTypes;
277
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 SymbolTableEntry *thisEntry = nullptr;
278
1/2
✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
134 if (spiceProc->isMethod()) {
279
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
402 thisEntry = spiceProc->bodyScope->lookupStrict(THIS_VARIABLE_NAME);
280
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134 times.
134 assert(thisEntry != nullptr);
281
2/4
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 134 times.
✗ Branch 5 not taken.
134 paramTypes.push_back(builder.getPtrTy());
282 }
283
284 // Get parameter types
285
2/2
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 134 times.
161 for (const auto &[qualType, isOptional] : spiceProc->paramList) {
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 assert(!isOptional);
287
2/4
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
27 paramTypes.push_back(qualType.toLLVMType(sourceFile));
288 }
289
290 // Get function linkage
291
2/4
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 134 times.
✗ Branch 5 not taken.
134 const bool isPublic = spiceProc->entry->getQualType().isPublic();
292
1/2
✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
134 const llvm::GlobalValue::LinkageTypes linkage = isPublic ? llvm::Function::ExternalLinkage : llvm::Function::PrivateLinkage;
293
294 // Create function
295
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 const std::string mangledName = spiceProc->getMangledName();
296
2/4
✓ Branch 2 taken 134 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 134 times.
✗ Branch 6 not taken.
134 llvm::FunctionType *fctType = llvm::FunctionType::get(builder.getVoidTy(), paramTypes, false);
297
2/4
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 134 times.
✗ Branch 5 not taken.
134 llvm::Function *fct = llvm::Function::Create(fctType, llvm::Function::ExternalLinkage, mangledName, module);
298
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 fct->setLinkage(linkage);
299
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 fct->setDoesNotRecurse();
300
301 // Set attributes to 'this' param
302
1/2
✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
134 if (spiceProc->isMethod()) {
303
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 fct->addParamAttr(0, llvm::Attribute::NoUndef);
304
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 fct->addParamAttr(0, llvm::Attribute::NonNull);
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134 times.
134 assert(thisEntry != nullptr);
306
3/6
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 134 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 134 times.
✗ Branch 8 not taken.
134 llvm::Type *structType = thisEntry->getQualType().getContained().toLLVMType(sourceFile);
307
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134 times.
134 assert(structType != nullptr);
308
3/6
✓ Branch 2 taken 134 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 134 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 134 times.
✗ Branch 9 not taken.
134 fct->addDereferenceableParamAttr(0, module->getDataLayout().getTypeStoreSize(structType));
309
3/6
✓ Branch 2 taken 134 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 134 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 134 times.
✗ Branch 9 not taken.
134 fct->addParamAttr(0, llvm::Attribute::getWithAlignment(context, module->getDataLayout().getABITypeAlign(structType)));
310 }
311
312 // Add debug info
313
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 diGenerator.generateFunctionDebugInfo(fct, spiceProc);
314
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 diGenerator.setSourceLocation(node);
315
316 // Change to body scope
317
2/4
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 134 times.
✗ Branch 5 not taken.
134 changeToScope(spiceProc->getSignature(false), ScopeType::FUNC_PROC_BODY);
318
319 // Create entry block
320
2/4
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 134 times.
✗ Branch 5 not taken.
134 llvm::BasicBlock *bEntry = createBlock();
321
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 switchToBlock(bEntry, fct);
322
323 // Reset alloca insert markers to this block
324 134 allocaInsertBlock = bEntry;
325
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 allocaInsertInst = nullptr;
326
327 // Store first argument to 'this' symbol
328
1/2
✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
134 if (spiceProc->isMethod()) {
329
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134 times.
134 assert(thisEntry != nullptr);
330 // Allocate space for the parameter
331
2/4
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 134 times.
✗ Branch 6 not taken.
134 llvm::Value *thisAddress = insertAlloca(paramTypes.front(), THIS_VARIABLE_NAME);
332 // Update the symbol table entry
333
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 thisEntry->updateAddress(thisAddress);
334 // Store the value at the new address
335
2/4
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 134 times.
✗ Branch 5 not taken.
134 insertStore(fct->arg_begin(), thisAddress);
336 // Generate debug info
337
2/4
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 134 times.
✗ Branch 5 not taken.
402 diGenerator.generateLocalVarDebugInfo(THIS_VARIABLE_NAME, thisAddress, 1);
338 }
339
340 // Generate body
341
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 generateBody();
342
343 // Create return instruction
344
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 builder.CreateRetVoid();
345
346 // Conclude debug info for function
347
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 diGenerator.concludeFunctionDebugInfo();
348
349 // Verify function
350
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 verifyFunction(fct, node->codeLoc);
351
352 // Change to parent scope
353
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
134 changeToParentScope(ScopeType::FUNC_PROC_BODY);
354
355 134 return fct;
356 134 }
357
358 997 void IRGenerator::generateCtorBodyPreamble(Scope *bodyScope) {
359 // Retrieve struct scope
360 997 Scope *structScope = bodyScope->parent;
361
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 997 times.
997 assert(structScope != nullptr);
362
363 // Get struct address
364
1/2
✓ Branch 1 taken 997 times.
✗ Branch 2 not taken.
2991 const SymbolTableEntry *thisEntry = bodyScope->lookupStrict(THIS_VARIABLE_NAME);
365
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 997 times.
997 assert(thisEntry != nullptr);
366
1/2
✓ Branch 1 taken 997 times.
✗ Branch 2 not taken.
997 llvm::Value *thisPtrPtr = thisEntry->getAddress();
367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 997 times.
997 assert(thisPtrPtr != nullptr);
368 997 llvm::Value *thisPtr = nullptr;
369
2/4
✓ Branch 1 taken 997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 997 times.
✗ Branch 5 not taken.
997 const QualType structSymbolType = thisEntry->getQualType().getBase();
370
1/2
✓ Branch 1 taken 997 times.
✗ Branch 2 not taken.
997 llvm::Type *structType = structSymbolType.toLLVMType(sourceFile);
371
372 // Store VTable to first struct field if required
373
1/2
✓ Branch 1 taken 997 times.
✗ Branch 2 not taken.
997 const Struct *spiceStruct = structSymbolType.getStruct(nullptr);
374
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 997 times.
997 assert(spiceStruct != nullptr);
375
2/2
✓ Branch 0 taken 178 times.
✓ Branch 1 taken 819 times.
997 if (spiceStruct->vTableData.vtable != nullptr) {
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 178 times.
178 assert(spiceStruct->vTableData.vtableType != nullptr);
377 // Store VTable to field address at index 0
378
3/6
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 178 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 178 times.
✗ Branch 8 not taken.
178 thisPtr = insertLoad(builder.getPtrTy(), thisPtrPtr);
379
3/6
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 178 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 178 times.
✗ Branch 8 not taken.
178 llvm::Value *indices[3] = {builder.getInt64(0), builder.getInt32(0), builder.getInt32(2)};
380
2/4
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 178 times.
✗ Branch 6 not taken.
178 llvm::Value *gepResult = insertInBoundsGEP(spiceStruct->vTableData.vtableType, spiceStruct->vTableData.vtable, indices);
381
1/2
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
178 insertStore(gepResult, thisPtr);
382 }
383
384
1/2
✓ Branch 1 taken 997 times.
✗ Branch 2 not taken.
997 const size_t fieldCount = structScope->getFieldCount();
385
2/2
✓ Branch 0 taken 2610 times.
✓ Branch 1 taken 997 times.
3607 for (size_t i = 0; i < fieldCount; i++) {
386
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2610 times.
2610 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
387
3/6
✓ Branch 0 taken 2610 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 2610 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2610 times.
✗ Branch 6 not taken.
2610 assert(fieldSymbol != nullptr && fieldSymbol->isField());
388
2/2
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 2468 times.
2610 if (fieldSymbol->isImplicitField)
389 142 continue;
390
391 // Call ctor for struct fields
392
1/2
✓ Branch 1 taken 2468 times.
✗ Branch 2 not taken.
2468 const QualType &fieldType = fieldSymbol->getQualType();
393
1/2
✓ Branch 0 taken 2468 times.
✗ Branch 1 not taken.
2468 const auto fieldNode = spice_pointer_cast<FieldNode *>(fieldSymbol->declNode);
394
3/4
✓ Branch 1 taken 2468 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 276 times.
✓ Branch 4 taken 2192 times.
2468 if (fieldType.is(TY_STRUCT)) {
395 // Lookup ctor function and call if available
396
1/2
✓ Branch 1 taken 276 times.
✗ Branch 2 not taken.
276 Scope *matchScope = fieldType.getBodyScope();
397
4/6
✓ Branch 2 taken 276 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 276 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 233 times.
✓ Branch 10 taken 43 times.
828 if (const Function *ctorFunction = FunctionManager::lookup(matchScope, CTOR_FUNCTION_NAME, fieldType, {}, false))
398
1/2
✓ Branch 2 taken 233 times.
✗ Branch 3 not taken.
233 generateCtorOrDtorCall(fieldSymbol, ctorFunction, {});
399
400 276 continue;
401 276 }
402
403 // Store default field values
404
5/8
✓ Branch 1 taken 2192 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1974 times.
✓ Branch 4 taken 218 times.
✓ Branch 5 taken 1974 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2192 times.
✗ Branch 8 not taken.
2192 if (fieldNode->defaultValue() != nullptr || cliOptions.buildMode == DEBUG) {
405 // Retrieve field address
406
2/2
✓ Branch 0 taken 759 times.
✓ Branch 1 taken 1433 times.
2192 if (!thisPtr)
407
3/6
✓ Branch 1 taken 759 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 759 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 759 times.
✗ Branch 8 not taken.
1518 thisPtr = insertLoad(builder.getPtrTy(), thisPtrPtr);
408
2/4
✓ Branch 1 taken 2192 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2192 times.
✗ Branch 5 not taken.
2192 llvm::Value *indices[2] = {builder.getInt64(0), builder.getInt32(i)};
409
2/4
✓ Branch 1 taken 2192 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 2192 times.
✗ Branch 6 not taken.
2192 llvm::Value *fieldAddress = insertInBoundsGEP(structType, thisPtr, indices);
410 // Retrieve default value
411 llvm::Value *value;
412
3/4
✓ Branch 1 taken 2192 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 218 times.
✓ Branch 4 taken 1974 times.
2192 if (fieldNode->defaultValue() != nullptr) {
413 // To resolve the default value, we need to temporarily change to the manifestation of the current struct instantiation
414 218 const size_t oldManIdx = manIdx; // Save manifestation index
415 218 manIdx = spiceStruct->manifestationIndex;
416
2/4
✓ Branch 1 taken 218 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 218 times.
✗ Branch 5 not taken.
218 value = resolveValue(fieldNode->defaultValue());
417 218 manIdx = oldManIdx; // Restore manifestation index
418 } else {
419
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1974 times.
1974 assert(cliOptions.buildMode == DEBUG);
420
1/2
✓ Branch 1 taken 1974 times.
✗ Branch 2 not taken.
1974 value = getDefaultValueForSymbolType(fieldType);
421 }
422 // Store default value
423
1/2
✓ Branch 1 taken 2192 times.
✗ Branch 2 not taken.
2192 insertStore(value, fieldAddress);
424 }
425 }
426 997 }
427
428 27 void IRGenerator::generateDefaultCtor(const Function *ctorFunction) {
429
3/6
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 27 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 27 times.
✗ Branch 6 not taken.
27 assert(ctorFunction->implicitDefault && ctorFunction->name == CTOR_FUNCTION_NAME);
430 51 const std::function<void()> generateBody = [&] { generateCtorBodyPreamble(ctorFunction->bodyScope); };
431
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 generateImplicitProcedure(generateBody, ctorFunction);
432 27 }
433
434 27 void IRGenerator::generateCopyCtorBodyPreamble(const Function *copyCtorFunction) {
435 // Retrieve struct scope
436 27 Scope *structScope = copyCtorFunction->bodyScope->parent;
437
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 assert(structScope != nullptr);
438
439 // Get struct address
440
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
81 const SymbolTableEntry *thisEntry = copyCtorFunction->bodyScope->lookupStrict(THIS_VARIABLE_NAME);
441
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 assert(thisEntry != nullptr);
442 27 llvm::Value *thisPtrPtr = thisEntry->getAddress();
443
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 assert(thisPtrPtr != nullptr);
444 27 llvm::Value *thisPtr = nullptr;
445
3/6
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 27 times.
✗ Branch 8 not taken.
27 llvm::Type *structType = thisEntry->getQualType().getBase().toLLVMType(sourceFile);
446
447 // Retrieve the value of the original struct, which is the only function paramenter
448 27 llvm::Value *originalThisPtr = builder.GetInsertBlock()->getParent()->getArg(1);
449
450 27 const size_t fieldCount = structScope->getFieldCount();
451
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 27 times.
145 for (size_t i = 0; i < fieldCount; i++) {
452
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 118 times.
118 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
453
3/6
✓ Branch 0 taken 118 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 118 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 118 times.
✗ Branch 6 not taken.
118 assert(fieldSymbol != nullptr && fieldSymbol->isField());
454
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 115 times.
118 if (fieldSymbol->isImplicitField)
455 45 continue;
456
457 // Retrieve the address of the original field (copy source)
458
2/4
✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 115 times.
✗ Branch 5 not taken.
115 llvm::Value *indices[2] = {builder.getInt64(0), builder.getInt32(i)};
459
2/4
✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 115 times.
✗ Branch 6 not taken.
115 llvm::Value *originalFieldAddress = insertInBoundsGEP(structType, originalThisPtr, indices);
460
461
1/2
✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
115 const QualType &fieldType = fieldSymbol->getQualType();
462
463 // Call copy ctor for struct fields
464
3/4
✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
✓ Branch 4 taken 77 times.
115 if (fieldType.is(TY_STRUCT)) {
465 // Lookup copy ctor function and call if available
466
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 Scope *matchScope = fieldType.getBodyScope();
467
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 38 times.
✗ Branch 6 not taken.
114 const ArgList args = {{fieldType.toConstRef(nullptr), false /* we have the field as storage */}};
468
4/6
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 35 times.
✓ Branch 8 taken 3 times.
76 if (const Function *copyCtorFct = FunctionManager::lookup(matchScope, CTOR_FUNCTION_NAME, fieldType, args, false))
469
2/4
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35 times.
✗ Branch 5 not taken.
105 generateCtorOrDtorCall(fieldSymbol, copyCtorFct, {originalFieldAddress});
470 38 continue;
471 38 }
472
473 // Retrieve the address of the new field (copy dest)
474
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 59 times.
77 if (!thisPtr)
475
3/6
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 18 times.
✗ Branch 8 not taken.
36 thisPtr = insertLoad(builder.getPtrTy(), thisPtrPtr);
476
2/4
✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 77 times.
✗ Branch 6 not taken.
77 llvm::Value *fieldAddress = insertInBoundsGEP(structType, thisPtr, indices);
477
478 // For owning heap fields, copy the underlying heap storage
479
3/4
✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 73 times.
77 if (fieldType.isHeap()) {
480
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 assert(fieldType.isPtr());
481
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 llvm::Type *pointeeType = fieldType.getContained().toLLVMType(sourceFile);
482
483 // Retrieve original heap address
484
3/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
8 llvm::Value *originalHeapAddress = insertLoad(builder.getPtrTy(), originalFieldAddress);
485
486 // Insert check for nullptr
487
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
8 llvm::BasicBlock *bThen = createBlock("nullptrcheck.then");
488
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 llvm::BasicBlock *bExit = createBlock("nullptrcheck.exit");
489
4/8
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
4 llvm::Value *condValue = builder.CreateICmpNE(originalHeapAddress, llvm::Constant::getNullValue(builder.getPtrTy()));
490
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 insertCondJump(condValue, bThen, bExit);
491
492 // Fill then block
493
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 switchToBlock(bThen);
494
495 // Allocate new space on the heap
496
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 llvm::Function *unsafeAllocFct = stdFunctionManager.getAllocUnsafeLongFct();
497
2/4
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
4 const size_t typeSizeInBytes = module->getDataLayout().getTypeSizeInBits(pointeeType) / 8;
498
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 llvm::ConstantInt *typeSize = builder.getInt64(typeSizeInBytes);
499
3/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
4 llvm::Value *newHeapAddress = builder.CreateCall(unsafeAllocFct, {typeSize});
500
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 insertStore(newHeapAddress, fieldAddress);
501
502 // Copy data from the old heap storage to the new one
503
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 generateShallowCopy(originalHeapAddress, pointeeType, newHeapAddress, false);
504
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 insertJump(bExit);
505
506 // Switch to exit block
507
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 switchToBlock(bExit);
508
509 4 continue;
510 4 }
511
512 // Shallow copy
513
1/2
✓ Branch 1 taken 73 times.
✗ Branch 2 not taken.
73 llvm::Type *type = fieldType.toLLVMType(sourceFile);
514
1/2
✓ Branch 1 taken 73 times.
✗ Branch 2 not taken.
73 generateShallowCopy(originalFieldAddress, type, fieldAddress, false);
515 }
516 27 }
517
518 79 void IRGenerator::generateDefaultCopyCtor(const Function *copyCtorFunction) {
519
3/6
✓ Branch 0 taken 79 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 79 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 79 times.
✗ Branch 6 not taken.
79 assert(copyCtorFunction->implicitDefault && copyCtorFunction->name == CTOR_FUNCTION_NAME);
520 106 const std::function<void()> generateBody = [&] { generateCopyCtorBodyPreamble(copyCtorFunction); };
521
1/2
✓ Branch 1 taken 79 times.
✗ Branch 2 not taken.
79 generateImplicitProcedure(generateBody, copyCtorFunction);
522 79 }
523
524 83 void IRGenerator::generateDtorBodyPreamble(const Function *dtorFunction) const {
525 // Retrieve struct scope
526 83 Scope *structScope = dtorFunction->bodyScope->parent;
527
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 assert(structScope != nullptr);
528
529 // Get struct address
530
1/2
✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
249 const SymbolTableEntry *thisEntry = dtorFunction->bodyScope->lookupStrict(THIS_VARIABLE_NAME);
531
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 assert(thisEntry != nullptr);
532 83 llvm::Value *thisPtrPtr = thisEntry->getAddress();
533
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 assert(thisPtrPtr != nullptr);
534 83 llvm::Value *thisPtr = nullptr;
535
3/6
✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 83 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 83 times.
✗ Branch 8 not taken.
83 llvm::Type *structType = thisEntry->getQualType().getBase().toLLVMType(sourceFile);
536
537 83 const size_t fieldCount = structScope->getFieldCount();
538
2/2
✓ Branch 0 taken 323 times.
✓ Branch 1 taken 83 times.
406 for (size_t i = 0; i < fieldCount; i++) {
539
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 323 times.
323 const SymbolTableEntry *fieldSymbol = structScope->lookupField(i);
540
2/4
✓ Branch 0 taken 323 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 323 times.
✗ Branch 4 not taken.
323 assert(fieldSymbol != nullptr && fieldSymbol->isField());
541
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 281 times.
323 if (fieldSymbol->isImplicitField)
542 42 continue;
543
544 // Call dtor for struct fields
545 281 const QualType &fieldType = fieldSymbol->getQualType();
546
2/2
✓ Branch 1 taken 57 times.
✓ Branch 2 taken 224 times.
281 if (fieldType.is(TY_STRUCT)) {
547 // Lookup dtor function and generate call if found
548
5/8
✓ Branch 2 taken 57 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 57 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 57 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 51 times.
✓ Branch 13 taken 6 times.
171 if (const Function *dtorFct = FunctionManager::lookup(fieldType.getBodyScope(), DTOR_FUNCTION_NAME, fieldType, {}, false))
549
1/2
✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
51 generateCtorOrDtorCall(fieldSymbol, dtorFct, {});
550 57 continue;
551 57 }
552
553 // Deallocate fields, that are stored on the heap
554
2/2
✓ Branch 1 taken 51 times.
✓ Branch 2 taken 173 times.
224 if (fieldType.isHeap()) {
555 // Retrieve field address
556
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 4 times.
51 if (!thisPtr)
557
3/6
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 47 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 47 times.
✗ Branch 8 not taken.
94 thisPtr = insertLoad(builder.getPtrTy(), thisPtrPtr);
558
2/4
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 51 times.
✗ Branch 5 not taken.
51 llvm::Value *indices[2] = {builder.getInt64(0), builder.getInt32(i)};
559
2/4
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 51 times.
✗ Branch 6 not taken.
51 llvm::Value *fieldAddress = insertInBoundsGEP(structType, thisPtr, indices);
560 // Call dealloc function
561
1/2
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
51 generateDeallocCall(fieldAddress);
562 }
563 }
564 83 }
565
566 84 void IRGenerator::generateDefaultDtor(const Function *dtorFunction) {
567
3/6
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 84 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 84 times.
✗ Branch 6 not taken.
84 assert(dtorFunction->implicitDefault && dtorFunction->name == DTOR_FUNCTION_NAME);
568 167 const std::function<void()> generateBody = [&] { generateDtorBodyPreamble(dtorFunction); };
569
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 generateImplicitProcedure(generateBody, dtorFunction);
570 84 }
571
572 2 void IRGenerator::generateTestMain() {
573 // Collect all test functions
574 2 std::vector<const std::vector<const Function *> *> tests;
575
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 3 times.
✓ Branch 13 taken 2 times.
5 for (const auto &sourceFile : resourceManager.sourceFiles | std::views::values)
576
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (!sourceFile->testFunctions.empty())
577
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 tests.push_back(&sourceFile->testFunctions);
578
579 // Prepare printf function
580
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 llvm::Function *printfFct = stdFunctionManager.getPrintfFct();
581
582 // Prepare success and error messages
583
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.
8 llvm::Constant *allStartMsg = createGlobalStringConst("allStartMsg", TEST_ALL_START_MSG, *rootScope->codeLoc);
584
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.
8 llvm::Constant *allEndMsg = createGlobalStringConst("allEndMsg", TEST_ALL_END_MSG, *rootScope->codeLoc);
585
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.
8 llvm::Constant *fileStartMsg = createGlobalStringConst("fileStartMsg", TEST_FILE_START_MSG, *rootScope->codeLoc);
586
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.
8 llvm::Constant *fileEndMsg = createGlobalStringConst("fileEndMsg", TEST_FILE_END_MSG, *rootScope->codeLoc);
587
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.
8 llvm::Constant *runMsg = createGlobalStringConst("runMsg", TEST_CASE_RUN_MSG, *rootScope->codeLoc);
588
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.
8 llvm::Constant *successMsg = createGlobalStringConst("successMsg", TEST_CASE_SUCCESS_MSG, *rootScope->codeLoc);
589
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.
8 llvm::Constant *errorMsg = createGlobalStringConst("errorMsg", TEST_CASE_FAILED_MSG, *rootScope->codeLoc);
590
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.
8 llvm::Constant *skippedMsg = createGlobalStringConst("skippedMsg", TEST_CASE_SKIPPED_MSG, *rootScope->codeLoc);
591
592 // Prepare entry for test main
593
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 QualType functionType(TY_FUNCTION);
594
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 functionType.setSpecifiers(TypeSpecifiers::of(TY_FUNCTION));
595
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 functionType.makePublic();
596
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 SymbolTableEntry entry(MAIN_FUNCTION_NAME, functionType, rootScope, nullptr, 0, false);
597
598 // Prepare test main function
599
3/6
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
6 Function testMain(MAIN_FUNCTION_NAME, &entry, QualType(TY_DYN), QualType(TY_INT), {}, {}, nullptr);
600 2 testMain.used = true; // Mark as used to prevent removal
601 2 testMain.implicitDefault = true;
602 2 testMain.mangleFunctionName = false;
603
604 // Prepare scope
605
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 rootScope->createChildScope(testMain.getSignature(false), ScopeType::FUNC_PROC_BODY, nullptr);
606
607 // Generate
608 const std::function<void()> generateBody = [&] {
609 // Prepare result variable
610
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 llvm::Type *i32Ty = builder.getInt32Ty();
611
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 llvm::Value *overallResult = insertAlloca(i32Ty, RETURN_VARIABLE_NAME);
612
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 insertStore(builder.getTrue(), overallResult);
613
614 // Print start message
615 3 const auto accFct = [&](size_t sum, const std::vector<const Function *> *innerVector) { return sum + innerVector->size(); };
616 2 const size_t totalTestCount = std::accumulate(tests.begin(), tests.end(), 0, accFct);
617
5/10
✓ 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.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 2 times.
✗ Branch 16 not taken.
2 builder.CreateCall(printfFct, {allStartMsg, builder.getInt32(totalTestCount), builder.getInt32(tests.size())});
618
619 // Generate a call to each test function
620
2/2
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 2 times.
5 for (const std::vector<const Function *> *testSuite : tests) {
621 // Print test suite prologue
622
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 const std::string fileName = testSuite->front()->bodyScope->sourceFile->fileName;
623
3/6
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
6 llvm::Constant *fileNameValue = createGlobalStringConst("fileName", fileName, testSuite->front()->getDeclCodeLoc());
624
4/8
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
3 builder.CreateCall(printfFct, {fileStartMsg, builder.getInt32(testSuite->size()), fileNameValue});
625
626
3/4
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 8 times.
✓ Branch 9 taken 3 times.
11 for (const Function *testFunction : *testSuite) {
627
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 assert(testFunction->isNormalFunction());
628
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 assert(testFunction->paramList.empty());
629
630 // Retrieve attribute list for the test function
631
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 assert(testFunction->declNode->isFctOrProcDef());
632
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 const auto fctDefNode = spice_pointer_cast<FctDefBaseNode *>(testFunction->declNode);
633
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 assert(fctDefNode->attrs() != nullptr);
634
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 const AttrLstNode *attrs = fctDefNode->attrs()->attrLst();
635
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
16 assert(attrs->getAttrValueByName(ATTR_TEST)->boolValue); // The test attribute must be present
636
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
16 const CompileTimeValue *testSkipAttr = attrs->getAttrValueByName(ATTR_TEST_SKIP);
637
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
8 const bool skipTest = testSkipAttr && testSkipAttr->boolValue;
638
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
16 const CompileTimeValue *testNameAttr = attrs->getAttrValueByName(ATTR_TEST_NAME);
639
640 // Prepare test name
641
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 std::stringstream testName;
642
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 testName << testFunction->name;
643
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (testNameAttr)
644
4/8
✓ 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.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
1 testName << " (" << resourceManager.compileTimeStringValues.at(testNameAttr->stringValueOffset) << ")";
645
646 // Print test case run message
647
4/8
✓ 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.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
24 llvm::Constant *testNameValue = createGlobalStringConst("testName", testName.str(), testFunction->getDeclCodeLoc());
648
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
8 builder.CreateCall(printfFct, {runMsg, testNameValue});
649
650
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (skipTest) {
651 // Print test case skip message
652
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 builder.CreateCall(printfFct, {skippedMsg, testNameValue});
653 1 continue;
654 }
655
656 // Test function is not defined in the current module -> declare it
657
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 const std::string mangledName = testFunction->getMangledName();
658
3/4
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 5 times.
7 if (!module->getFunction(mangledName)) {
659
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 assert(testFunction->returnType.is(TY_BOOL));
660
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 assert(testFunction->paramList.empty());
661
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
2 llvm::FunctionType *fctType = llvm::FunctionType::get(builder.getInt1Ty(), {}, false);
662
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 module->getOrInsertFunction(mangledName, fctType);
663 }
664
665 // Call test function
666
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 llvm::Function *callee = module->getFunction(mangledName);
667
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 assert(callee != nullptr);
668
3/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
7 llvm::Value *testCaseResult = builder.CreateCall(callee);
669
670 // Update result variable
671
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 llvm::Value *oldResult = insertLoad(i32Ty, overallResult);
672
4/8
✓ 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.
✓ Branch 10 taken 7 times.
✗ Branch 11 not taken.
7 llvm::Value *newResult = builder.CreateAnd(oldResult, builder.CreateZExt(testCaseResult, i32Ty));
673
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 insertStore(newResult, overallResult);
674
675 // Print test case result message
676
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
7 llvm::Value *message = builder.CreateSelect(testCaseResult, successMsg, errorMsg);
677
3/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
7 builder.CreateCall(printfFct, {message, testNameValue});
678
2/2
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 1 times.
8 }
679
680 // Print test suite epilogue
681
4/8
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
3 builder.CreateCall(printfFct, {fileEndMsg, builder.getInt32(testSuite->size()), fileNameValue});
682 3 }
683
684 // Print end message
685
5/10
✓ 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.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 2 times.
✗ Branch 16 not taken.
2 builder.CreateCall(printfFct, {allEndMsg, builder.getInt32(totalTestCount), builder.getInt32(tests.size())});
686
687 // Return result
688
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 llvm::Value *finalResult = insertLoad(i32Ty, overallResult);
689
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 builder.CreateRet(finalResult);
690
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 };
691
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 generateImplicitFunction(generateBody, &testMain);
692 2 }
693
694 } // namespace spice::compiler
695