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