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