GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 98.2% 437 / 6 / 451
Functions: 96.2% 50 / 0 / 52
Branches: 63.0% 481 / 20 / 784

src/irgenerator/IRGenerator.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include "IRGenerator.h"
4
5 #include <SourceFile.h>
6 #include <driver/Driver.h>
7 #include <global/GlobalResourceManager.h>
8 #include <model/Function.h>
9 #include <symboltablebuilder/SymbolTableBuilder.h>
10 #include <typechecker/FunctionManager.h>
11
12 #include <llvm/IR/Module.h>
13 #include <llvm/IR/Verifier.h>
14
15 namespace spice::compiler {
16
17 const std::string PRODUCER_STRING = "spice version " + std::string(SPICE_VERSION) + " (https://github.com/spicelang/spice)";
18
19 2544 IRGenerator::IRGenerator(GlobalResourceManager &resourceManager, SourceFile *sourceFile)
20 2544 : CompilerPass(resourceManager, sourceFile), context(cliOptions.useLTO ? resourceManager.ltoContext : sourceFile->context),
21
1/2
✓ Branch 8 → 9 taken 2544 times.
✗ Branch 8 → 78 not taken.
2544 builder(sourceFile->builder), module(sourceFile->llvmModule.get()), conversionManager(sourceFile, this),
22
6/10
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 6 taken 2540 times.
✓ Branch 9 → 10 taken 2544 times.
✗ Branch 9 → 78 not taken.
✓ Branch 10 → 11 taken 2544 times.
✗ Branch 10 → 78 not taken.
✓ Branch 11 → 12 taken 2544 times.
✗ Branch 11 → 76 not taken.
✓ Branch 15 → 16 taken 2544 times.
✗ Branch 15 → 72 not taken.
5088 stdFunctionManager(sourceFile, resourceManager, module) {
23 // Attach information to the module
24
1/2
✓ Branch 19 → 20 taken 2544 times.
✗ Branch 19 → 51 not taken.
2544 module->setTargetTriple(cliOptions.targetTriple);
25
2/4
✓ Branch 23 → 24 taken 2544 times.
✗ Branch 23 → 54 not taken.
✓ Branch 24 → 25 taken 2544 times.
✗ Branch 24 → 52 not taken.
2544 module->setDataLayout(sourceFile->targetMachine->createDataLayout());
26
2/2
✓ Branch 26 → 27 taken 1 time.
✓ Branch 26 → 29 taken 2543 times.
2544 if (cliOptions.outputContainer == OutputContainer::SHARED_LIBRARY) {
27
1/2
✓ Branch 27 → 28 taken 1 time.
✗ Branch 27 → 64 not taken.
1 module->setPICLevel(llvm::PICLevel::SmallPIC);
28
1/2
✓ Branch 28 → 31 taken 1 time.
✗ Branch 28 → 64 not taken.
1 module->setPIELevel(llvm::PIELevel::Default);
29 } else {
30
1/2
✓ Branch 29 → 30 taken 2543 times.
✗ Branch 29 → 64 not taken.
2543 module->setPICLevel(llvm::PICLevel::BigPIC);
31
1/2
✓ Branch 30 → 31 taken 2543 times.
✗ Branch 30 → 64 not taken.
2543 module->setPIELevel(llvm::PIELevel::Large);
32 }
33
1/2
✓ Branch 31 → 32 taken 2544 times.
✗ Branch 31 → 64 not taken.
2544 module->setUwtable(llvm::UWTableKind::Default);
34
1/2
✓ Branch 32 → 33 taken 2544 times.
✗ Branch 32 → 64 not taken.
2544 module->setFramePointer(llvm::FramePointerKind::All);
35
36 // Add module identifier metadata
37
2/4
✓ Branch 33 → 34 taken 2544 times.
✗ Branch 33 → 55 not taken.
✓ Branch 34 → 35 taken 2544 times.
✗ Branch 34 → 55 not taken.
2544 llvm::NamedMDNode *identifierMetadata = module->getOrInsertNamedMetadata("llvm.ident");
38
3/6
✓ Branch 36 → 37 taken 2544 times.
✗ Branch 36 → 56 not taken.
✓ Branch 38 → 39 taken 2544 times.
✗ Branch 38 → 56 not taken.
✓ Branch 39 → 40 taken 2544 times.
✗ Branch 39 → 56 not taken.
2544 identifierMetadata->addOperand(llvm::MDNode::get(context, llvm::MDString::get(context, PRODUCER_STRING)));
39
40 // Initialize common LLVM types
41
4/8
✓ Branch 40 → 41 taken 2544 times.
✗ Branch 40 → 59 not taken.
✓ Branch 41 → 42 taken 2544 times.
✗ Branch 41 → 59 not taken.
✓ Branch 42 → 43 taken 2544 times.
✗ Branch 42 → 59 not taken.
✓ Branch 44 → 45 taken 2544 times.
✗ Branch 44 → 59 not taken.
2544 llvmTypes.lambdaFatPtrType = llvm::StructType::get(context, {builder.getPtrTy(), builder.getPtrTy(), builder.getInt64Ty()});
42
43 // Initialize debug info generator
44
2/2
✓ Branch 45 → 46 taken 59 times.
✓ Branch 45 → 50 taken 2485 times.
2544 if (cliOptions.instrumentation.generateDebugInfo)
45
2/4
✓ Branch 46 → 47 taken 59 times.
✗ Branch 46 → 63 not taken.
✓ Branch 47 → 48 taken 59 times.
✗ Branch 47 → 61 not taken.
59 diGenerator.initialize(sourceFile->fileName, sourceFile->fileDir);
46 2544 }
47
48 2544 std::any IRGenerator::visitEntry(const EntryNode *node) {
49 // Generate IR
50
1/2
✓ Branch 2 → 3 taken 2544 times.
✗ Branch 2 → 28 not taken.
2544 visitChildren(node);
51
52 // Generate test main if required
53
4/4
✓ Branch 4 → 5 taken 456 times.
✓ Branch 4 → 7 taken 2088 times.
✓ Branch 5 → 6 taken 4 times.
✓ Branch 5 → 7 taken 452 times.
2544 if (sourceFile->isMainFile && cliOptions.generateTestMain)
54 4 generateTestMain();
55
56 // Execute deferred VTable initializations
57
2/2
✓ Branch 21 → 9 taken 2272 times.
✓ Branch 21 → 22 taken 2544 times.
7360 for (DeferredLogic &deferredVTableInit : deferredVTableInitializations)
58
1/2
✓ Branch 11 → 12 taken 2272 times.
✗ Branch 11 → 29 not taken.
2272 deferredVTableInit.execute();
59
60 // Finalize debug info generator
61 2544 diGenerator.finalize();
62
63 // Verify module
64 2544 verifyModule(node->codeLoc);
65
66
1/2
✓ Branch 24 → 25 taken 2544 times.
✗ Branch 24 → 30 not taken.
5088 return nullptr;
67 }
68
69 139583 llvm::AllocaInst *IRGenerator::insertAlloca(llvm::Type *llvmType, const std::string &varName) {
70
2/2
✓ Branch 2 → 3 taken 99134 times.
✓ Branch 2 → 8 taken 40449 times.
139583 if (allocaInsertInst != nullptr) { // If there is already an alloca inst, insert right after that
71
2/4
✓ Branch 3 → 4 taken 99134 times.
✗ Branch 3 → 19 not taken.
✓ Branch 4 → 5 taken 99134 times.
✗ Branch 4 → 19 not taken.
99134 llvm::AllocaInst *allocaInst = builder.CreateAlloca(llvmType, nullptr, varName);
72 99134 allocaInst->dropLocation(); // Part of prologue
73 99134 allocaInst->moveAfter(allocaInsertInst);
74 99134 allocaInsertInst = allocaInst;
75 } else { // This is the first alloca inst in the current function -> insert at the entry block
76 // Save current basic block and move insert cursor to entry block of the current function
77 40449 llvm::BasicBlock *currentBlock = builder.GetInsertBlock();
78 40449 builder.SetInsertPoint(allocaInsertBlock, allocaInsertBlock->begin());
79
80 // Allocate the size of the given LLVM type
81
2/4
✓ Branch 11 → 12 taken 40449 times.
✗ Branch 11 → 20 not taken.
✓ Branch 12 → 13 taken 40449 times.
✗ Branch 12 → 20 not taken.
40449 allocaInsertInst = builder.CreateAlloca(llvmType, nullptr, varName);
82 40449 allocaInsertInst->dropLocation(); // Part of prologue
83
84 // Restore old basic block
85 40449 builder.SetInsertPoint(currentBlock);
86 }
87
88 // Insert lifetime start marker
89
2/2
✓ Branch 15 → 16 taken 1808 times.
✓ Branch 15 → 17 taken 137775 times.
139583 if (cliOptions.useLifetimeMarkers)
90 1808 builder.CreateLifetimeStart(allocaInsertInst);
91
92 139583 return allocaInsertInst;
93 }
94
95 116329 llvm::AllocaInst *IRGenerator::insertAlloca(const QualType &qualType, const std::string &varName) {
96 116329 llvm::Type *llvmType = qualType.toLLVMType(sourceFile);
97 116329 llvm::AllocaInst *alloca = insertAlloca(llvmType, varName);
98
99 // Insert type metadata
100
2/2
✓ Branch 4 → 5 taken 9 times.
✓ Branch 4 → 6 taken 116320 times.
116329 if (cliOptions.useTBAAMetadata)
101 9 mdGenerator.generateTypeMetadata(allocaInsertInst, qualType);
102
103 116329 return alloca;
104 }
105
106 277280 llvm::LoadInst *IRGenerator::insertLoad(llvm::Type *llvmType, llvm::Value *ptr, bool isVolatile,
107 const std::string &varName) const {
108
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 277280 times.
277280 assert(ptr->getType()->isPointerTy());
109
2/4
✓ Branch 6 → 7 taken 277280 times.
✗ Branch 6 → 11 not taken.
✓ Branch 7 → 8 taken 277280 times.
✗ Branch 7 → 11 not taken.
277280 return builder.CreateLoad(llvmType, ptr, isVolatile, varName);
110 }
111
112 237969 llvm::LoadInst *IRGenerator::insertLoad(const QualType &qualType, llvm::Value *ptr, bool isVolatile, const std::string &varName) {
113 237969 llvm::Type *llvmType = qualType.toLLVMType(sourceFile);
114 237969 llvm::LoadInst *load = insertLoad(llvmType, ptr, isVolatile, varName);
115
2/2
✓ Branch 4 → 5 taken 6 times.
✓ Branch 4 → 6 taken 237963 times.
237969 if (cliOptions.useTBAAMetadata)
116 6 mdGenerator.generateTBAAMetadata(load, qualType);
117 237969 return load;
118 }
119
120 144855 llvm::StoreInst *IRGenerator::insertStore(llvm::Value *val, llvm::Value *ptr, bool isVolatile) const {
121
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 144855 times.
144855 assert(ptr->getType()->isPointerTy());
122 144855 return builder.CreateStore(val, ptr, isVolatile);
123 }
124
125 43308 void IRGenerator::insertStore(llvm::Value *val, llvm::Value *ptr, const QualType &qualType, bool isVolatile) {
126 43308 llvm::StoreInst *store = insertStore(val, ptr, isVolatile);
127
2/2
✓ Branch 3 → 4 taken 9 times.
✓ Branch 3 → 5 taken 43299 times.
43308 if (cliOptions.useTBAAMetadata)
128 9 mdGenerator.generateTBAAMetadata(store, qualType);
129 43308 }
130
131 79369 llvm::Value *IRGenerator::insertInBoundsGEP(llvm::Type *type, llvm::Value *basePtr, llvm::ArrayRef<llvm::Value *> indices,
132 const std::string &varName) const {
133
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 79369 times.
79369 assert(basePtr->getType()->isPointerTy());
134
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 79369 times.
79369 assert(!indices.empty());
135
4/6
✓ Branch 4 → 5 taken 78528 times.
✓ Branch 4 → 7 taken 72274 times.
✓ Branch 6 → 7 taken 78528 times.
✗ Branch 6 → 8 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 79369 times.
230171 assert(std::ranges::all_of(indices, [](const llvm::Value *index) {
136 const llvm::Type *indexType = index->getType();
137 return indexType->isIntegerTy(32) || indexType->isIntegerTy(64);
138 }));
139
140 // Insert GEP
141
2/4
✓ Branch 12 → 13 taken 79369 times.
✗ Branch 12 → 17 not taken.
✓ Branch 13 → 14 taken 79369 times.
✗ Branch 13 → 17 not taken.
79369 return builder.CreateInBoundsGEP(type, basePtr, indices, varName);
142 }
143
144 27419 llvm::Value *IRGenerator::insertStructGEP(llvm::Type *type, llvm::Value *basePtr, unsigned int index,
145 const std::string &varName) const {
146
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 27419 times.
27419 assert(basePtr->getType()->isPointerTy());
147
148 // If we use index 0 we can use the base pointer directly
149
2/2
✓ Branch 6 → 7 taken 9115 times.
✓ Branch 6 → 8 taken 18304 times.
27419 if (index == 0)
150 9115 return basePtr;
151
152 // Insert GEP
153
2/4
✓ Branch 8 → 9 taken 18304 times.
✗ Branch 8 → 13 not taken.
✓ Branch 9 → 10 taken 18304 times.
✗ Branch 9 → 13 not taken.
18304 return builder.CreateStructGEP(type, basePtr, index, varName);
154 }
155
156 157036 llvm::Value *IRGenerator::resolveValue(const ExprNode *node) {
157 // Visit the given AST node
158
2/4
✓ Branch 2 → 3 taken 157036 times.
✗ Branch 2 → 11 not taken.
✓ Branch 3 → 4 taken 157036 times.
✗ Branch 3 → 9 not taken.
157036 auto exprResult = any_cast<LLVMExprResult>(visit(node));
159
1/2
✓ Branch 5 → 6 taken 157036 times.
✗ Branch 5 → 12 not taken.
314072 return resolveValue(node, exprResult);
160 }
161
162 172491 llvm::Value *IRGenerator::resolveValue(const ExprNode *node, LLVMExprResult &exprResult) {
163 172491 return resolveValue(node->getEvaluatedSymbolType(manIdx), exprResult);
164 }
165
166 340866 llvm::Value *IRGenerator::resolveValue(const QualType &qualType, LLVMExprResult &exprResult) {
167 // Check if the value is already present
168
2/2
✓ Branch 2 → 3 taken 107880 times.
✓ Branch 2 → 4 taken 232986 times.
340866 if (exprResult.value != nullptr)
169 107880 return exprResult.value;
170
171 // Check if a constant is present
172
2/2
✓ Branch 4 → 5 taken 72203 times.
✓ Branch 4 → 7 taken 160783 times.
232986 if (exprResult.constant != nullptr) {
173 72203 materializeConstant(exprResult);
174 72203 return exprResult.value;
175 }
176
177
3/4
✓ Branch 7 → 8 taken 2213 times.
✓ Branch 7 → 10 taken 158570 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 2213 times.
160783 assert(exprResult.ptr != nullptr || exprResult.refPtr != nullptr);
178
179 // De-reference if reference type
180
4/4
✓ Branch 10 → 11 taken 151102 times.
✓ Branch 10 → 13 taken 9681 times.
✓ Branch 11 → 12 taken 7 times.
✓ Branch 11 → 13 taken 151095 times.
160783 const bool isVolatile = exprResult.entry && exprResult.entry->isVolatile;
181
4/4
✓ Branch 14 → 15 taken 2223 times.
✓ Branch 14 → 24 taken 158560 times.
✓ Branch 15 → 16 taken 2213 times.
✓ Branch 15 → 24 taken 10 times.
160783 if (exprResult.refPtr != nullptr && exprResult.ptr == nullptr)
182
2/4
✓ Branch 19 → 20 taken 2213 times.
✗ Branch 19 → 34 not taken.
✓ Branch 20 → 21 taken 2213 times.
✗ Branch 20 → 34 not taken.
2213 exprResult.ptr = insertLoad(builder.getPtrTy(), exprResult.refPtr, isVolatile);
183
184 // Load the value from the pointer
185
1/2
✓ Branch 24 → 25 taken 160783 times.
✗ Branch 24 → 46 not taken.
160783 const QualType referencedType = qualType.removeReferenceWrapper();
186
1/2
✓ Branch 28 → 29 taken 160783 times.
✗ Branch 28 → 40 not taken.
160783 exprResult.value = insertLoad(referencedType, exprResult.ptr, isVolatile);
187
188 160783 return exprResult.value;
189 }
190
191 17877 llvm::Value *IRGenerator::resolveAddress(const ASTNode *node) {
192 // Visit the given AST node
193
2/4
✓ Branch 2 → 3 taken 17877 times.
✗ Branch 2 → 11 not taken.
✓ Branch 3 → 4 taken 17877 times.
✗ Branch 3 → 9 not taken.
17877 auto exprResult = any_cast<LLVMExprResult>(visit(node));
194
1/2
✓ Branch 5 → 6 taken 17877 times.
✗ Branch 5 → 12 not taken.
35754 return resolveAddress(exprResult);
195 }
196
197 126551 llvm::Value *IRGenerator::resolveAddress(LLVMExprResult &exprResult) {
198 // Check if an address is already present
199
2/2
✓ Branch 2 → 3 taken 103360 times.
✓ Branch 2 → 4 taken 23191 times.
126551 if (exprResult.ptr != nullptr)
200 103360 return exprResult.ptr;
201
202 // Check if the reference address is already present
203
3/4
✓ Branch 4 → 5 taken 17950 times.
✓ Branch 4 → 7 taken 5241 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 17950 times.
23191 const bool isVolatile = exprResult.entry && exprResult.entry->isVolatile;
204
3/4
✓ Branch 8 → 9 taken 18915 times.
✓ Branch 8 → 18 taken 4276 times.
✓ Branch 9 → 10 taken 18915 times.
✗ Branch 9 → 18 not taken.
23191 if (exprResult.refPtr != nullptr && exprResult.ptr == nullptr) {
205
2/4
✓ Branch 13 → 14 taken 18915 times.
✗ Branch 13 → 35 not taken.
✓ Branch 14 → 15 taken 18915 times.
✗ Branch 14 → 35 not taken.
18915 exprResult.ptr = insertLoad(builder.getPtrTy(), exprResult.refPtr, isVolatile);
206 18915 return exprResult.ptr;
207 }
208
209 // If not, store the value or constant
210 4276 materializeConstant(exprResult);
211
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 4276 times.
4276 assert(exprResult.value != nullptr);
212
7/12
✓ Branch 21 → 22 taken 6 times.
✓ Branch 21 → 23 taken 4270 times.
✓ Branch 22 → 26 taken 6 times.
✗ Branch 22 → 43 not taken.
✓ Branch 25 → 26 taken 4270 times.
✗ Branch 25 → 43 not taken.
✓ Branch 27 → 28 taken 4276 times.
✗ Branch 27 → 41 not taken.
✓ Branch 29 → 30 taken 4270 times.
✓ Branch 29 → 32 taken 6 times.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 46 not taken.
8546 exprResult.ptr = insertAlloca(exprResult.value->getType(), exprResult.entry ? exprResult.entry->name : "");
213 4276 insertStore(exprResult.value, exprResult.ptr, isVolatile);
214
215 4276 return exprResult.ptr;
216 }
217
218 5665 llvm::Constant *IRGenerator::getDefaultValueForSymbolType(const QualType &symbolType) { // NOLINT(misc-no-recursion)
219 // Double
220
2/2
✓ Branch 3 → 4 taken 46 times.
✓ Branch 3 → 9 taken 5619 times.
5665 if (symbolType.is(TY_DOUBLE))
221
2/4
✓ Branch 4 → 5 taken 46 times.
✗ Branch 4 → 129 not taken.
✓ Branch 5 → 6 taken 46 times.
✗ Branch 5 → 127 not taken.
46 return llvm::ConstantFP::get(context, llvm::APFloat(0.0));
222
223 // Int
224
2/2
✓ Branch 10 → 11 taken 980 times.
✓ Branch 10 → 13 taken 4639 times.
5619 if (symbolType.is(TY_INT))
225 980 return builder.getInt32(0);
226
227 // Short
228
2/2
✓ Branch 14 → 15 taken 14 times.
✓ Branch 14 → 17 taken 4625 times.
4639 if (symbolType.is(TY_SHORT))
229 14 return builder.getInt16(0);
230
231 // Long
232
2/2
✓ Branch 18 → 19 taken 919 times.
✓ Branch 18 → 21 taken 3706 times.
4625 if (symbolType.is(TY_LONG))
233 919 return builder.getInt64(0);
234
235 // Byte or char
236
3/4
✓ Branch 21 → 22 taken 3706 times.
✗ Branch 21 → 130 not taken.
✓ Branch 22 → 23 taken 131 times.
✓ Branch 22 → 25 taken 3575 times.
3706 if (symbolType.isOneOf({TY_BYTE, TY_CHAR}))
237 131 return builder.getInt8(0);
238
239 // String
240
2/2
✓ Branch 26 → 27 taken 921 times.
✓ Branch 26 → 35 taken 2654 times.
3575 if (symbolType.is(TY_STRING)) {
241
3/6
✓ Branch 27 → 28 taken 921 times.
✗ Branch 27 → 132 not taken.
✓ Branch 28 → 29 taken 921 times.
✗ Branch 28 → 131 not taken.
✓ Branch 29 → 30 taken 921 times.
✗ Branch 29 → 131 not taken.
921 llvm::GlobalVariable *globalString = builder.CreateGlobalString("", "");
242
1/2
✓ Branch 30 → 31 taken 921 times.
✗ Branch 30 → 34 not taken.
921 if (cliOptions.comparableOutput)
243
2/4
✓ Branch 31 → 32 taken 921 times.
✗ Branch 31 → 133 not taken.
✓ Branch 32 → 33 taken 921 times.
✗ Branch 32 → 133 not taken.
921 globalString->setAlignment(llvm::Align(4));
244 921 return globalString;
245 }
246
247 // Bool
248
2/2
✓ Branch 36 → 37 taken 133 times.
✓ Branch 36 → 39 taken 2521 times.
2654 if (symbolType.is(TY_BOOL))
249 133 return builder.getFalse();
250
251 // Pointer or reference
252
3/4
✓ Branch 39 → 40 taken 2521 times.
✗ Branch 39 → 134 not taken.
✓ Branch 40 → 41 taken 2302 times.
✓ Branch 40 → 44 taken 219 times.
2521 if (symbolType.isOneOf({TY_PTR, TY_REF}))
253 2302 return llvm::Constant::getNullValue(builder.getPtrTy());
254
255 // Array
256
2/2
✓ Branch 45 → 46 taken 93 times.
✓ Branch 45 → 61 taken 126 times.
219 if (symbolType.isArray()) {
257 // Get array size
258
1/2
✓ Branch 46 → 47 taken 93 times.
✗ Branch 46 → 143 not taken.
93 const size_t arraySize = symbolType.getArraySize();
259
260 // Get default value for item
261
2/4
✓ Branch 47 → 48 taken 93 times.
✗ Branch 47 → 135 not taken.
✓ Branch 48 → 49 taken 93 times.
✗ Branch 48 → 135 not taken.
93 llvm::Constant *defaultItemValue = getDefaultValueForSymbolType(symbolType.getContained());
262
263 // Retrieve array and item type
264
2/4
✓ Branch 49 → 50 taken 93 times.
✗ Branch 49 → 136 not taken.
✓ Branch 50 → 51 taken 93 times.
✗ Branch 50 → 136 not taken.
93 llvm::Type *itemType = symbolType.getContained().toLLVMType(sourceFile);
265
1/2
✓ Branch 51 → 52 taken 93 times.
✗ Branch 51 → 143 not taken.
93 llvm::ArrayType *arrayType = llvm::ArrayType::get(itemType, arraySize);
266
267 // Create a constant array with n times the default value
268
1/2
✓ Branch 54 → 55 taken 93 times.
✗ Branch 54 → 137 not taken.
186 const std::vector itemConstants(arraySize, defaultItemValue);
269
1/2
✓ Branch 57 → 58 taken 93 times.
✗ Branch 57 → 140 not taken.
93 return llvm::ConstantArray::get(arrayType, itemConstants);
270 93 }
271
272 // Function or procedure
273
3/4
✓ Branch 61 → 62 taken 126 times.
✗ Branch 61 → 144 not taken.
✓ Branch 62 → 63 taken 68 times.
✓ Branch 62 → 70 taken 58 times.
126 if (symbolType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) {
274
2/4
✓ Branch 63 → 64 taken 68 times.
✗ Branch 63 → 145 not taken.
✓ Branch 64 → 65 taken 68 times.
✗ Branch 64 → 145 not taken.
68 llvm::Constant *ptrDefaultValue = getDefaultValueForSymbolType(QualType(TY_PTR));
275 68 llvm::Constant *sizeDefaultValue = builder.getInt64(0);
276
1/2
✓ Branch 67 → 68 taken 68 times.
✗ Branch 67 → 146 not taken.
68 return llvm::ConstantStruct::get(llvmTypes.lambdaFatPtrType, {ptrDefaultValue, ptrDefaultValue, sizeDefaultValue});
277 }
278
279 // Struct
280
2/2
✓ Branch 71 → 72 taken 57 times.
✓ Branch 71 → 110 taken 1 time.
58 if (symbolType.is(TY_STRUCT)) {
281 // Retrieve field count
282
1/2
✓ Branch 72 → 73 taken 57 times.
✗ Branch 72 → 153 not taken.
57 Scope *structScope = symbolType.getBodyScope();
283
1/2
✗ Branch 73 → 74 not taken.
✓ Branch 73 → 75 taken 57 times.
57 assert(structScope != nullptr);
284
1/2
✓ Branch 75 → 76 taken 57 times.
✗ Branch 75 → 153 not taken.
57 const size_t fieldCount = structScope->getFieldCount();
285
286 // Get default values for all fields of the struct
287 57 std::vector<llvm::Constant *> fieldConstants;
288
1/2
✓ Branch 76 → 77 taken 57 times.
✗ Branch 76 → 151 not taken.
57 fieldConstants.reserve(fieldCount);
289
290 // Add default value for each struct field
291
2/2
✓ Branch 102 → 78 taken 117 times.
✓ Branch 102 → 103 taken 57 times.
174 for (size_t i = 0; i < fieldCount; i++) {
292 // Get entry of the field
293
1/2
✗ Branch 78 → 79 not taken.
✓ Branch 78 → 80 taken 117 times.
117 const SymbolTableEntry *fieldEntry = structScope->lookupField(i);
294
3/6
✓ Branch 83 → 84 taken 117 times.
✗ Branch 83 → 87 not taken.
✓ Branch 84 → 85 taken 117 times.
✗ Branch 84 → 149 not taken.
✓ Branch 85 → 86 taken 117 times.
✗ Branch 85 → 87 not taken.
117 assert(fieldEntry != nullptr && fieldEntry->isField());
295
296 // Retrieve default field value
297 llvm::Constant *defaultFieldValue;
298
4/6
✓ Branch 88 → 89 taken 117 times.
✗ Branch 88 → 90 not taken.
✓ Branch 91 → 92 taken 117 times.
✗ Branch 91 → 97 not taken.
✓ Branch 92 → 93 taken 8 times.
✓ Branch 92 → 97 taken 109 times.
117 if (const auto fieldNode = dynamic_cast<FieldNode *>(fieldEntry->declNode); fieldNode && fieldNode->defaultValue)
299
3/6
✓ Branch 93 → 94 taken 8 times.
✗ Branch 93 → 149 not taken.
✓ Branch 94 → 95 taken 8 times.
✗ Branch 94 → 148 not taken.
✓ Branch 95 → 96 taken 8 times.
✗ Branch 95 → 148 not taken.
8 defaultFieldValue = getConst(fieldNode->defaultValue->getCompileTimeValue(manIdx), fieldEntry->getQualType(), fieldNode);
300 else
301
2/4
✓ Branch 97 → 98 taken 109 times.
✗ Branch 97 → 149 not taken.
✓ Branch 98 → 99 taken 109 times.
✗ Branch 98 → 149 not taken.
109 defaultFieldValue = getDefaultValueForSymbolType(fieldEntry->getQualType());
302
303
1/2
✓ Branch 100 → 101 taken 117 times.
✗ Branch 100 → 149 not taken.
117 fieldConstants.push_back(defaultFieldValue);
304 }
305
306
2/4
✓ Branch 103 → 104 taken 57 times.
✗ Branch 103 → 151 not taken.
✓ Branch 104 → 105 taken 57 times.
✗ Branch 104 → 151 not taken.
57 const auto structType = llvm::cast<llvm::StructType>(symbolType.toLLVMType(sourceFile));
307
1/2
✓ Branch 106 → 107 taken 57 times.
✗ Branch 106 → 150 not taken.
57 return llvm::ConstantStruct::get(structType, fieldConstants);
308 57 }
309
310 // Interface
311
1/2
✓ Branch 111 → 112 taken 1 time.
✗ Branch 111 → 118 not taken.
1 if (symbolType.is(TY_INTERFACE)) {
312 1 const auto structType = llvm::cast<llvm::StructType>(symbolType.toLLVMType(sourceFile));
313 1 return llvm::ConstantStruct::get(structType, llvm::Constant::getNullValue(builder.getPtrTy()));
314 }
315
316 throw CompilerError(INTERNAL_ERROR, "Cannot determine default value for symbol type"); // GCOV_EXCL_LINE
317 }
318
319 66480 llvm::Constant *IRGenerator::getConst(const CompileTimeValue &compileTimeValue, const QualType &type, const ASTNode *node) const {
320
2/2
✓ Branch 3 → 4 taken 1943 times.
✓ Branch 3 → 9 taken 64537 times.
66480 if (type.is(TY_DOUBLE))
321
2/4
✓ Branch 4 → 5 taken 1943 times.
✗ Branch 4 → 56 not taken.
✓ Branch 5 → 6 taken 1943 times.
✗ Branch 5 → 54 not taken.
1943 return llvm::ConstantFP::get(context, llvm::APFloat(compileTimeValue.doubleValue));
322
323
2/2
✓ Branch 10 → 11 taken 10355 times.
✓ Branch 10 → 13 taken 54182 times.
64537 if (type.is(TY_INT))
324 10355 return builder.getInt32(compileTimeValue.intValue);
325
326
2/2
✓ Branch 14 → 15 taken 1347 times.
✓ Branch 14 → 17 taken 52835 times.
54182 if (type.is(TY_SHORT))
327 1347 return builder.getInt16(compileTimeValue.shortValue);
328
329
2/2
✓ Branch 18 → 19 taken 30115 times.
✓ Branch 18 → 21 taken 22720 times.
52835 if (type.is(TY_LONG))
330 30115 return builder.getInt64(compileTimeValue.longValue);
331
332
3/4
✓ Branch 21 → 22 taken 22720 times.
✗ Branch 21 → 57 not taken.
✓ Branch 22 → 23 taken 6502 times.
✓ Branch 22 → 25 taken 16218 times.
22720 if (type.isOneOf({TY_BYTE, TY_CHAR}))
333 6502 return builder.getInt8(compileTimeValue.charValue);
334
335
2/2
✓ Branch 26 → 27 taken 10984 times.
✓ Branch 26 → 36 taken 5234 times.
16218 if (type.is(TY_STRING)) {
336 10984 const std::string &stringValue = resourceManager.compileTimeStringValues.at(compileTimeValue.stringValueOffset);
337
2/4
✓ Branch 30 → 31 taken 10984 times.
✗ Branch 30 → 60 not taken.
✓ Branch 31 → 32 taken 10984 times.
✗ Branch 31 → 58 not taken.
32952 return createGlobalStringConst(ANON_GLOBAL_STRING_NAME, stringValue, node->codeLoc);
338 }
339
340
2/2
✓ Branch 37 → 38 taken 5232 times.
✓ Branch 37 → 40 taken 2 times.
5234 if (type.is(TY_BOOL))
341 5232 return builder.getInt1(compileTimeValue.boolValue);
342
343
1/2
✓ Branch 41 → 42 taken 2 times.
✗ Branch 41 → 45 not taken.
2 if (type.is(TY_PTR))
344 2 return llvm::Constant::getNullValue(builder.getPtrTy());
345
346 throw CompilerError(UNHANDLED_BRANCH, "Constant fall-through"); // GCOV_EXCL_LINE
347 }
348
349 130713 llvm::BasicBlock *IRGenerator::createBlock(const std::string &blockName /*=""*/) const {
350
2/4
✓ Branch 2 → 3 taken 130713 times.
✗ Branch 2 → 7 not taken.
✓ Branch 3 → 4 taken 130713 times.
✗ Branch 3 → 7 not taken.
130713 return llvm::BasicBlock::Create(context, blockName);
351 }
352
353 130713 void IRGenerator::switchToBlock(llvm::BasicBlock *block, llvm::Function *parentFct /*=nullptr*/) {
354
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 130713 times.
130713 assert(block->getParent() == nullptr); // Ensure that the block was not added to a function already
355 // If no parent function were passed, use the current function
356
2/2
✓ Branch 5 → 6 taken 90089 times.
✓ Branch 5 → 8 taken 40624 times.
130713 if (!parentFct)
357 90089 parentFct = builder.GetInsertBlock()->getParent();
358 // Append block to current function
359 130713 parentFct->insert(parentFct->end(), block);
360 // Set insert point to the block
361 130713 builder.SetInsertPoint(block);
362 130713 blockAlreadyTerminated = false;
363 130713 }
364
365 2478 void IRGenerator::terminateBlock(const StmtLstNode *stmtLstNode) {
366 2478 generateScopeCleanup(stmtLstNode);
367 2478 blockAlreadyTerminated = true;
368 2478 }
369
370 46950 void IRGenerator::insertJump(llvm::BasicBlock *targetBlock) {
371
2/2
✓ Branch 2 → 3 taken 16063 times.
✓ Branch 2 → 4 taken 30887 times.
46950 if (blockAlreadyTerminated)
372 16063 return;
373 30887 builder.CreateBr(targetBlock);
374 30887 blockAlreadyTerminated = true;
375 }
376
377 38916 void IRGenerator::insertCondJump(llvm::Value *condition, llvm::BasicBlock *trueBlock, llvm::BasicBlock *falseBlock,
378 Likelihood likelihood /*=UNSPECIFIED*/) {
379
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 38916 times.
38916 if (blockAlreadyTerminated)
380 return;
381 38916 llvm::CondBrInst *jumpInst = builder.CreateCondBr(condition, trueBlock, falseBlock);
382 38916 blockAlreadyTerminated = true;
383
384
2/2
✓ Branch 5 → 6 taken 5502 times.
✓ Branch 5 → 7 taken 33414 times.
38916 if (likelihood != Likelihood::UNSPECIFIED)
385 5502 mdGenerator.generateBranchWeightsMetadata(jumpInst, likelihood);
386 }
387
388 40611 void IRGenerator::verifyFunction(const llvm::Function *fct, const CodeLoc &codeLoc) const {
389 // Skip the verifying step if the verifier was disabled manually or debug info is emitted
390
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 40611 times.
40611 if (cliOptions.disableVerifier)
391 return;
392
393 // Verify function
394 40611 std::string output;
395
1/2
✓ Branch 5 → 6 taken 40611 times.
✗ Branch 5 → 20 not taken.
40611 llvm::raw_string_ostream oss(output);
396 if (llvm::verifyFunction(*fct, &oss)) // LCOV_EXCL_LINE
397 throw CompilerError(codeLoc, INVALID_FUNCTION, output); // LCOV_EXCL_LINE
398 40611 }
399
400 2544 void IRGenerator::verifyModule(const CodeLoc &codeLoc) const {
401 // Skip the verifying step if the verifier was disabled manually or debug info is emitted
402
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2544 times.
2544 if (cliOptions.disableVerifier)
403 return;
404
405 // Verify module
406 2544 std::string output;
407
1/2
✓ Branch 5 → 6 taken 2544 times.
✗ Branch 5 → 20 not taken.
2544 llvm::raw_string_ostream oss(output);
408 if (llvm::verifyModule(*module, &oss)) // LCOV_EXCL_LINE
409 throw CompilerError(codeLoc, INVALID_MODULE, output); // LCOV_EXCL_LINE
410 2544 }
411
412 23113 LLVMExprResult IRGenerator::doAssignment(const ASTNode *lhsNode, const ExprNode *rhsNode, const ASTNode *node) {
413 // Get entry of left side
414
2/4
✓ Branch 2 → 3 taken 23113 times.
✗ Branch 2 → 18 not taken.
✓ Branch 3 → 4 taken 23113 times.
✗ Branch 3 → 16 not taken.
23113 auto exprResult = std::any_cast<LLVMExprResult>(visit(lhsNode));
415 23113 const SymbolTableEntry *entry = exprResult.entry;
416
7/10
✓ Branch 5 → 6 taken 21016 times.
✓ Branch 5 → 10 taken 2097 times.
✓ Branch 6 → 7 taken 21016 times.
✗ Branch 6 → 19 not taken.
✓ Branch 7 → 8 taken 21016 times.
✗ Branch 7 → 19 not taken.
✓ Branch 8 → 9 taken 1378 times.
✓ Branch 8 → 10 taken 19638 times.
✓ Branch 10 → 11 taken 21735 times.
✗ Branch 10 → 19 not taken.
23113 llvm::Value *lhsAddress = entry != nullptr && entry->getQualType().isRef() ? exprResult.refPtr : resolveAddress(exprResult);
417
1/2
✓ Branch 12 → 13 taken 23113 times.
✗ Branch 12 → 19 not taken.
46226 return doAssignment(lhsAddress, entry, rhsNode, node);
418 }
419
420 51834 LLVMExprResult IRGenerator::doAssignment(llvm::Value *lhsAddress, const SymbolTableEntry *lhsEntry, const ExprNode *rhsNode,
421 const ASTNode *node, bool isDecl) {
422 // Get symbol type of right side
423
1/2
✓ Branch 2 → 3 taken 51834 times.
✗ Branch 2 → 13 not taken.
51834 const QualType &rhsSType = rhsNode->getEvaluatedSymbolType(manIdx);
424
2/4
✓ Branch 3 → 4 taken 51834 times.
✗ Branch 3 → 12 not taken.
✓ Branch 4 → 5 taken 51834 times.
✗ Branch 4 → 10 not taken.
51834 auto rhs = std::any_cast<LLVMExprResult>(visit(rhsNode));
425
1/2
✓ Branch 6 → 7 taken 51834 times.
✗ Branch 6 → 13 not taken.
103668 return doAssignment(lhsAddress, lhsEntry, rhs, rhsSType, node, isDecl);
426 }
427
428 52429 LLVMExprResult IRGenerator::doAssignment(llvm::Value *lhsAddress, const SymbolTableEntry *lhsEntry, LLVMExprResult &rhs,
429 const QualType &rhsSType, const ASTNode *node, bool isDecl) {
430 // Deduce some information about the assignment
431
4/4
✓ Branch 2 → 3 taken 50332 times.
✓ Branch 2 → 7 taken 2097 times.
✓ Branch 5 → 6 taken 3564 times.
✓ Branch 5 → 7 taken 46768 times.
52429 const bool isRefAssign = lhsEntry != nullptr && lhsEntry->getQualType().isRef();
432 // A non-temporary struct value assigned by value needs a deep copy. This holds whether the destination is a direct
433 // lvalue or the value behind an already-bound reference (assign-through): the binding cases of a reference assignment
434 // (declaration/initial field ref/return value) all return early above before this is consumed, so the remaining
435 // reference assignments are assign-throughs that must copy into the referent instead of shallow-copying (which would
436 // alias the rhs' owned members and double-free).
437
6/8
✓ Branch 8 → 9 taken 52429 times.
✗ Branch 8 → 264 not taken.
✓ Branch 9 → 10 taken 52429 times.
✗ Branch 9 → 264 not taken.
✓ Branch 10 → 11 taken 8526 times.
✓ Branch 10 → 14 taken 43903 times.
✓ Branch 12 → 13 taken 1657 times.
✓ Branch 12 → 14 taken 6869 times.
52429 const bool needsCopy = rhsSType.removeReferenceWrapper().is(TY_STRUCT) && !rhs.isTemporary();
438
439
2/2
✓ Branch 15 → 16 taken 3564 times.
✓ Branch 15 → 57 taken 48865 times.
52429 if (isRefAssign) {
440
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 3564 times.
3564 assert(lhsEntry != nullptr);
441
2/2
✓ Branch 18 → 19 taken 2186 times.
✓ Branch 18 → 33 taken 1378 times.
3564 if (isDecl) { // Reference gets initially assigned
442 // Store lhs pointer to rhs
443
2/4
✓ Branch 22 → 23 taken 2186 times.
✗ Branch 22 → 265 not taken.
✓ Branch 23 → 24 taken 2186 times.
✗ Branch 23 → 265 not taken.
2186 llvm::Value *refAddress = insertAlloca(builder.getPtrTy());
444 2186 updateAddress(lhsEntry, refAddress);
445
446 // Generate debug info for variable declaration
447 2186 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, refAddress);
448
449 // Get address of right side
450 2186 llvm::Value *rhsAddress = resolveAddress(rhs);
451
1/2
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 2186 times.
2186 assert(rhsAddress != nullptr);
452 2186 insertStore(rhsAddress, refAddress);
453
454 2186 return LLVMExprResult{.value = rhsAddress, .ptr = refAddress, .entry = lhsEntry};
455 }
456
457 // Reference to reference assignment (only for struct fields that are not initialized yet)
458 // These are only allowed inside a ctor body. In other cases, the value of the reference gets assigned, not the ref itself.
459
6/8
✓ Branch 33 → 34 taken 1010 times.
✓ Branch 33 → 40 taken 368 times.
✓ Branch 35 → 36 taken 1006 times.
✓ Branch 35 → 40 taken 4 times.
✓ Branch 36 → 37 taken 1006 times.
✗ Branch 36 → 40 not taken.
✓ Branch 38 → 39 taken 1006 times.
✗ Branch 38 → 40 not taken.
1378 const bool isInitialFieldRefAssign = isInCtorBody && rhsSType.isRef() && rhs.entry && lhsEntry->isField();
460 // Assigning the result variable
461 1378 const bool isReturnValAssign = lhsEntry->name == RETURN_VARIABLE_NAME;
462
4/4
✓ Branch 42 → 43 taken 372 times.
✓ Branch 42 → 44 taken 1006 times.
✓ Branch 43 → 44 taken 11 times.
✓ Branch 43 → 49 taken 361 times.
1378 if (isInitialFieldRefAssign || isReturnValAssign) {
463 // Get address of right side
464 1017 llvm::Value *referencedAddress = resolveAddress(rhs);
465
1/2
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 47 taken 1017 times.
1017 assert(referencedAddress != nullptr);
466
467 // Store the rhs* to the lhs**
468 1017 insertStore(referencedAddress, lhsAddress);
469
470 1017 return LLVMExprResult{.value = referencedAddress, .ptr = lhsAddress, .entry = lhsEntry};
471 }
472
473 // Load referenced address
474
2/4
✓ Branch 52 → 53 taken 361 times.
✗ Branch 52 → 271 not taken.
✓ Branch 53 → 54 taken 361 times.
✗ Branch 53 → 271 not taken.
361 lhsAddress = insertLoad(builder.getPtrTy(), lhsAddress);
475 }
476
477
8/8
✓ Branch 57 → 58 taken 27130 times.
✓ Branch 57 → 63 taken 22096 times.
✓ Branch 59 → 60 taken 3297 times.
✓ Branch 59 → 63 taken 23833 times.
✓ Branch 61 → 62 taken 3286 times.
✓ Branch 61 → 63 taken 11 times.
✓ Branch 64 → 65 taken 3286 times.
✓ Branch 64 → 86 taken 45940 times.
49226 if (isDecl && rhsSType.is(TY_STRUCT) && rhs.isTemporary()) {
478
1/2
✗ Branch 65 → 66 not taken.
✓ Branch 65 → 67 taken 3286 times.
3286 assert(lhsEntry != nullptr);
479
1/2
✓ Branch 67 → 68 taken 3286 times.
✗ Branch 67 → 283 not taken.
3286 llvm::Value *rhsAddress = resolveAddress(rhs);
480 // Only adopt the temporary's storage directly (temp stealing) unless it is a phi merging two independently
481 // materialized pointers (e.g. a ternary whose branches each own their own storage). Lifetime markers require
482 // their operand to be a real alloca, so stealing such a phi here would emit an invalid llvm.lifetime.end
483 // under --sanitizer=address. Any other pointer (a plain alloca, or one derived from it via GEP/load, as in
484 // the foreach-loop item extraction below) denotes a single, stable piece of storage and is safe to adopt.
485 // The temporary's ownership fully transfers to lhsEntry either way (its underlying storage is never
486 // separately destructed), so falling back to a shallow copy into a dedicated alloca is exactly as correct,
487 // just gives up the pointer-adoption optimization for the phi case.
488
3/4
✓ Branch 68 → 69 taken 3286 times.
✗ Branch 68 → 283 not taken.
✓ Branch 69 → 70 taken 3280 times.
✓ Branch 69 → 73 taken 6 times.
3286 if (!llvm::isa<llvm::PHINode>(rhsAddress)) {
489 // Directly set the address to the lhs entry (temp stealing)
490
1/2
✓ Branch 70 → 71 taken 3280 times.
✗ Branch 70 → 283 not taken.
3280 updateAddress(lhsEntry, rhsAddress);
491 3280 rhs.entry = lhsEntry;
492 // Generate debug info for variable declaration
493
1/2
✓ Branch 71 → 72 taken 3280 times.
✗ Branch 71 → 283 not taken.
3280 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, rhsAddress);
494 3280 return rhs;
495 }
496 // Size the copy off lhsEntry's own type, not rhsSType: some callers (e.g. the foreach-loop item
497 // extraction below) pass a struct rhsSType only to steer this branch, while rhs itself resolves (through
498 // refPtr indirection) to a differently-typed value; lhsEntry's type is always the authoritative one here.
499
1/2
✓ Branch 73 → 74 taken 6 times.
✗ Branch 73 → 283 not taken.
6 const QualType &lhsQualType = lhsEntry->getQualType();
500
1/2
✓ Branch 77 → 78 taken 6 times.
✗ Branch 77 → 277 not taken.
6 llvm::Value *lhsAddr = insertAlloca(lhsQualType);
501
1/2
✓ Branch 80 → 81 taken 6 times.
✗ Branch 80 → 283 not taken.
6 updateAddress(lhsEntry, lhsAddr);
502
1/2
✓ Branch 81 → 82 taken 6 times.
✗ Branch 81 → 283 not taken.
6 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, lhsAddr);
503
2/4
✓ Branch 82 → 83 taken 6 times.
✗ Branch 82 → 283 not taken.
✓ Branch 83 → 84 taken 6 times.
✗ Branch 83 → 283 not taken.
6 generateShallowCopy(rhsAddress, lhsQualType.toLLVMType(sourceFile), lhsAddr, lhsEntry->isVolatile);
504 6 rhs.ptr = lhsAddr;
505 6 rhs.entry = lhsEntry;
506 6 return rhs;
507 }
508
509 // Allocate new memory if the lhs address does not exist
510
2/2
✓ Branch 86 → 87 taken 23621 times.
✓ Branch 86 → 98 taken 22319 times.
45940 if (!lhsAddress) {
511
1/2
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 23621 times.
23621 assert(lhsEntry != nullptr);
512
2/4
✓ Branch 92 → 93 taken 23621 times.
✗ Branch 92 → 284 not taken.
✓ Branch 93 → 94 taken 23621 times.
✗ Branch 93 → 284 not taken.
23621 lhsAddress = insertAlloca(lhsEntry->getQualType());
513 23621 updateAddress(lhsEntry, lhsAddress);
514 // Generate debug info for variable declaration
515 23621 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, lhsAddress);
516 }
517
518 // Check if we try to assign an array by value to a pointer. Here we have to store the address of the first element to the lhs
519
10/10
✓ Branch 98 → 99 taken 43843 times.
✓ Branch 98 → 107 taken 2097 times.
✓ Branch 101 → 102 taken 10078 times.
✓ Branch 101 → 107 taken 33765 times.
✓ Branch 103 → 104 taken 7 times.
✓ Branch 103 → 107 taken 10071 times.
✓ Branch 105 → 106 taken 2 times.
✓ Branch 105 → 107 taken 5 times.
✓ Branch 108 → 109 taken 2 times.
✓ Branch 108 → 124 taken 45938 times.
45940 if (lhsEntry && lhsEntry->getQualType().isPtr() && rhsSType.isArray() && rhsSType.getArraySize() != ARRAY_SIZE_UNKNOWN) {
520 // Get address of right side
521
1/2
✓ Branch 109 → 110 taken 2 times.
✗ Branch 109 → 297 not taken.
2 llvm::Value *rhsAddress = resolveAddress(rhs);
522
1/2
✗ Branch 110 → 111 not taken.
✓ Branch 110 → 112 taken 2 times.
2 assert(rhsAddress != nullptr);
523
1/2
✓ Branch 112 → 113 taken 2 times.
✗ Branch 112 → 297 not taken.
2 llvm::Type *elementTy = rhsSType.toLLVMType(sourceFile);
524
2/4
✓ Branch 113 → 114 taken 2 times.
✗ Branch 113 → 297 not taken.
✓ Branch 114 → 115 taken 2 times.
✗ Branch 114 → 297 not taken.
2 llvm::Value *indices[2] = {builder.getInt64(0), builder.getInt32(0)};
525
1/2
✓ Branch 119 → 120 taken 2 times.
✗ Branch 119 → 290 not taken.
2 llvm::Value *firstItemAddress = insertInBoundsGEP(elementTy, rhsAddress, indices);
526
1/2
✓ Branch 122 → 123 taken 2 times.
✗ Branch 122 → 297 not taken.
2 insertStore(firstItemAddress, lhsAddress);
527 2 return LLVMExprResult{.value = rhsAddress, .ptr = lhsAddress, .entry = lhsEntry};
528 }
529
530 // Handle operator overloads
531
6/6
✓ Branch 124 → 125 taken 22096 times.
✓ Branch 124 → 128 taken 23842 times.
✓ Branch 126 → 127 taken 503 times.
✓ Branch 126 → 128 taken 21593 times.
✓ Branch 129 → 130 taken 503 times.
✓ Branch 129 → 146 taken 45435 times.
45938 if (!isDecl && conversionManager.callsOverloadedOpFct(node, DEFAULT_OP_IDX)) {
532 503 ResolverFct lhsV = [&] { return static_cast<llvm::Value *>(nullptr); };
533
1/2
✓ Branch 131 → 132 taken 503 times.
✗ Branch 131 → 298 not taken.
503 ResolverFct rhsV = [&] { return resolveValue(rhsSType, rhs); };
534 1006 ResolverFct lhsP = [&] { return lhsAddress; };
535 1006 ResolverFct rhsP = [&] { return resolveAddress(rhs); };
536 503 return conversionManager.callOperatorOverloadFct<2>(node, {lhsV, lhsP, rhsV, rhsP}, DEFAULT_OP_IDX);
537 503 }
538
539 // Check if we need to copy the rhs to the lhs. This happens for structs
540
2/2
✓ Branch 146 → 147 taken 647 times.
✓ Branch 146 → 232 taken 44788 times.
45435 if (needsCopy) {
541 // Get address of right side
542
1/2
✓ Branch 147 → 148 taken 647 times.
✗ Branch 147 → 374 not taken.
647 llvm::Value *rhsAddress = resolveAddress(rhs);
543
1/2
✗ Branch 148 → 149 not taken.
✓ Branch 148 → 150 taken 647 times.
647 assert(rhsAddress != nullptr);
544
545 // If the lhs already holds an initialized, non-trivially-destructible struct, its old value must be
546 // destructed before the copy overwrites it, otherwise its owning members (heap pointers, strings, ...)
547 // would leak. The typechecker only sets a dtor in exactly those cases. To stay correct for a self-
548 // assignment like 'a = a', the destruct + copy are skipped entirely when both sides share the address
549 // (the assignment is a no-op in that case, and destructing first would corrupt the value to copy from).
550
1/2
✓ Branch 150 → 151 taken 647 times.
✗ Branch 150 → 152 not taken.
647 const auto *assignNode = dynamic_cast<const AssignExprNode *>(node);
551
3/4
✓ Branch 153 → 154 taken 636 times.
✓ Branch 153 → 156 taken 11 times.
✓ Branch 154 → 155 taken 636 times.
✗ Branch 154 → 374 not taken.
647 const Function *lhsDtor = assignNode ? assignNode->lhsDtorFct.at(manIdx) : nullptr;
552 647 llvm::BasicBlock *bCopyEnd = nullptr;
553
2/2
✓ Branch 157 → 158 taken 19 times.
✓ Branch 157 → 178 taken 628 times.
647 if (lhsDtor != nullptr) {
554
2/4
✓ Branch 160 → 161 taken 19 times.
✗ Branch 160 → 318 not taken.
✓ Branch 161 → 162 taken 19 times.
✗ Branch 161 → 316 not taken.
38 llvm::BasicBlock *bCopy = createBlock("assign.copy");
555
2/4
✓ Branch 166 → 167 taken 19 times.
✗ Branch 166 → 324 not taken.
✓ Branch 167 → 168 taken 19 times.
✗ Branch 167 → 322 not taken.
19 bCopyEnd = createBlock("assign.copy.end");
556
3/6
✓ Branch 170 → 171 taken 19 times.
✗ Branch 170 → 328 not taken.
✓ Branch 171 → 172 taken 19 times.
✗ Branch 171 → 328 not taken.
✓ Branch 172 → 173 taken 19 times.
✗ Branch 172 → 328 not taken.
19 insertCondJump(builder.CreateICmpEQ(lhsAddress, rhsAddress), bCopyEnd, bCopy);
557
1/2
✓ Branch 173 → 174 taken 19 times.
✗ Branch 173 → 374 not taken.
19 switchToBlock(bCopy);
558
1/2
✓ Branch 175 → 176 taken 19 times.
✗ Branch 175 → 329 not taken.
19 generateCtorOrDtorCall(lhsAddress, lhsDtor, {});
559 }
560
561
2/4
✓ Branch 178 → 179 taken 647 times.
✗ Branch 178 → 332 not taken.
✓ Branch 179 → 180 taken 647 times.
✗ Branch 179 → 332 not taken.
647 const QualType rhsSTypeNonRef = rhsSType.removeReferenceWrapper().toNonConst();
562
3/4
✓ Branch 180 → 181 taken 647 times.
✗ Branch 180 → 374 not taken.
✓ Branch 181 → 182 taken 489 times.
✓ Branch 181 → 198 taken 158 times.
647 if (rhsSTypeNonRef.isTriviallyCopyable(node)) {
563 // Create shallow copy
564
1/2
✓ Branch 182 → 183 taken 489 times.
✗ Branch 182 → 340 not taken.
489 llvm::Type *rhsType = rhsSTypeNonRef.toLLVMType(sourceFile);
565
3/10
✓ Branch 183 → 184 taken 489 times.
✗ Branch 183 → 185 not taken.
✓ Branch 184 → 188 taken 489 times.
✗ Branch 184 → 333 not taken.
✗ Branch 187 → 188 not taken.
✗ Branch 187 → 333 not taken.
✗ Branch 188 → 189 not taken.
✓ Branch 188 → 191 taken 489 times.
✗ Branch 333 → 334 not taken.
✗ Branch 333 → 336 not taken.
489 const std::string copyName = lhsEntry ? lhsEntry->name : "";
566
3/6
✓ Branch 191 → 192 taken 489 times.
✗ Branch 191 → 194 not taken.
✗ Branch 192 → 193 not taken.
✓ Branch 192 → 194 taken 489 times.
✓ Branch 195 → 196 taken 489 times.
✗ Branch 195 → 338 not taken.
489 generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry && lhsEntry->isVolatile);
567 489 } else {
568 // Check if we have a copy ctor
569
1/2
✓ Branch 198 → 199 taken 158 times.
✗ Branch 198 → 373 not taken.
158 Scope *structScope = rhsSTypeNonRef.getBodyScope();
570
2/4
✓ Branch 199 → 200 taken 158 times.
✗ Branch 199 → 345 not taken.
✓ Branch 204 → 205 taken 158 times.
✗ Branch 204 → 341 not taken.
474 const ArgList args = {{rhsSTypeNonRef.toConstRef(node), rhs.isTemporary()}};
571
2/4
✓ Branch 208 → 209 taken 158 times.
✗ Branch 208 → 349 not taken.
✓ Branch 209 → 210 taken 158 times.
✗ Branch 209 → 347 not taken.
158 const Function *copyCtor = FunctionManager::lookup(structScope, CTOR_FUNCTION_NAME, rhsSTypeNonRef, args, true);
572
1/2
✓ Branch 212 → 213 taken 158 times.
✗ Branch 212 → 221 not taken.
158 if (copyCtor != nullptr) {
573 // Call copy ctor
574
2/4
✓ Branch 215 → 216 taken 158 times.
✗ Branch 215 → 355 not taken.
✓ Branch 216 → 217 taken 158 times.
✗ Branch 216 → 353 not taken.
316 generateCtorOrDtorCall(lhsAddress, copyCtor, {rhsAddress});
575 } else {
576 const std::string structName = rhsSTypeNonRef.getName();
577 const std::string msg = "Cannot copy struct '" + structName + "', as it is not trivially copyable and has no copy ctor";
578 throw SemanticError(node, COPY_CTOR_REQUIRED, msg);
579 }
580 158 }
581
582 // Close the self-assignment guard
583
2/2
✓ Branch 228 → 229 taken 19 times.
✓ Branch 228 → 231 taken 628 times.
647 if (bCopyEnd != nullptr) {
584
1/2
✓ Branch 229 → 230 taken 19 times.
✗ Branch 229 → 374 not taken.
19 insertJump(bCopyEnd);
585
1/2
✓ Branch 230 → 231 taken 19 times.
✗ Branch 230 → 374 not taken.
19 switchToBlock(bCopyEnd);
586 }
587 647 return LLVMExprResult{.ptr = lhsAddress, .entry = lhsEntry};
588 }
589
590 // Optimization: If we have the address of both sides, we can do a memcpy instead of loading and storing the value
591 44788 llvm::Value *rhsValue = nullptr;
592
8/8
✓ Branch 233 → 234 taken 2023 times.
✓ Branch 233 → 237 taken 42765 times.
✓ Branch 234 → 235 taken 1936 times.
✓ Branch 234 → 237 taken 87 times.
✓ Branch 235 → 236 taken 1929 times.
✓ Branch 235 → 237 taken 7 times.
✓ Branch 238 → 239 taken 1929 times.
✓ Branch 238 → 260 taken 42859 times.
44788 if (rhsSType.is(TY_STRUCT) && rhs.value == nullptr && rhs.constant == nullptr) {
593 // Create shallow copy
594
2/4
✓ Branch 239 → 240 taken 1929 times.
✗ Branch 239 → 375 not taken.
✓ Branch 240 → 241 taken 1929 times.
✗ Branch 240 → 375 not taken.
1929 const QualType rhsSTypeNonRef = rhsSType.removeReferenceWrapper().toNonConst();
595
1/2
✓ Branch 241 → 242 taken 1929 times.
✗ Branch 241 → 383 not taken.
1929 llvm::Type *rhsType = rhsSTypeNonRef.toLLVMType(sourceFile);
596
1/2
✓ Branch 242 → 243 taken 1929 times.
✗ Branch 242 → 383 not taken.
1929 llvm::Value *rhsAddress = resolveAddress(rhs);
597
1/2
✗ Branch 243 → 244 not taken.
✓ Branch 243 → 245 taken 1929 times.
1929 assert(rhsAddress != nullptr);
598
6/10
✓ Branch 245 → 246 taken 1918 times.
✓ Branch 245 → 247 taken 11 times.
✓ Branch 246 → 250 taken 1918 times.
✗ Branch 246 → 376 not taken.
✓ Branch 249 → 250 taken 11 times.
✗ Branch 249 → 376 not taken.
✓ Branch 250 → 251 taken 11 times.
✓ Branch 250 → 253 taken 1918 times.
✗ Branch 376 → 377 not taken.
✗ Branch 376 → 379 not taken.
1940 const std::string copyName = lhsEntry ? lhsEntry->name : "";
599
4/6
✓ Branch 253 → 254 taken 1918 times.
✓ Branch 253 → 256 taken 11 times.
✗ Branch 254 → 255 not taken.
✓ Branch 254 → 256 taken 1918 times.
✓ Branch 257 → 258 taken 1929 times.
✗ Branch 257 → 381 not taken.
1929 generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry && lhsEntry->isVolatile);
600 1929 } else {
601 // We can load the value from the right side and store it to the left side
602 // Retrieve value of the right side
603 42859 rhsValue = resolveValue(rhsSType, rhs);
604 // Store the value to the address
605 42859 insertStore(rhsValue, lhsAddress, rhsSType);
606 }
607
608 44788 return LLVMExprResult{.value = rhsValue, .ptr = lhsAddress, .entry = lhsEntry};
609
5/14
✓ Branch 134 → 135 taken 503 times.
✗ Branch 134 → 301 not taken.
✓ Branch 135 → 136 taken 503 times.
✗ Branch 135 → 301 not taken.
✓ Branch 136 → 137 taken 503 times.
✗ Branch 136 → 301 not taken.
✓ Branch 137 → 138 taken 503 times.
✗ Branch 137 → 301 not taken.
✓ Branch 138 → 139 taken 503 times.
✗ Branch 138 → 299 not taken.
✗ Branch 301 → 302 not taken.
✗ Branch 301 → 305 not taken.
✗ Branch 303 → 304 not taken.
✗ Branch 303 → 305 not taken.
503 }
610
611 2720 void IRGenerator::generateShallowCopy(llvm::Value *oldAddress, llvm::Type *varType, llvm::Value *targetAddress,
612 bool isVolatile) const {
613 // Retrieve size to copy
614
1/2
✓ Branch 3 → 4 taken 2720 times.
✗ Branch 3 → 19 not taken.
2720 const llvm::TypeSize typeSize = module->getDataLayout().getTypeAllocSize(varType);
615
616 // Create values for memcpy intrinsic
617
2/4
✓ Branch 4 → 5 taken 2720 times.
✗ Branch 4 → 19 not taken.
✓ Branch 5 → 6 taken 2720 times.
✗ Branch 5 → 19 not taken.
2720 llvm::Value *structSize = builder.getInt64(typeSize);
618
1/2
✓ Branch 6 → 7 taken 2720 times.
✗ Branch 6 → 19 not taken.
2720 llvm::Value *copyVolatile = builder.getInt1(isVolatile);
619
620 // Call memcpy intrinsic to execute the shallow copy
621
1/2
✓ Branch 7 → 8 taken 2720 times.
✗ Branch 7 → 19 not taken.
2720 llvm::Function *memcpyFct = stdFunctionManager.getMemcpyIntrinsic();
622
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 2720 times.
2720 assert(targetAddress != nullptr);
623
3/6
✓ Branch 10 → 11 taken 2720 times.
✗ Branch 10 → 18 not taken.
✓ Branch 12 → 13 taken 2720 times.
✗ Branch 12 → 15 not taken.
✓ Branch 13 → 14 taken 2720 times.
✗ Branch 13 → 15 not taken.
2720 builder.CreateCall(memcpyFct, {targetAddress, oldAddress, structSize, copyVolatile});
624 2720 }
625
626 104683 void IRGenerator::autoDeReferencePtr(llvm::Value *&ptr, QualType &symbolType) {
627
6/6
✓ Branch 12 → 13 taken 109245 times.
✓ Branch 12 → 15 taken 72624 times.
✓ Branch 14 → 15 taken 4562 times.
✓ Branch 14 → 16 taken 104683 times.
✓ Branch 17 → 3 taken 77186 times.
✓ Branch 17 → 18 taken 104683 times.
181869 while (symbolType.isPtr() || symbolType.isRef()) {
628
1/2
✓ Branch 6 → 7 taken 77186 times.
✗ Branch 6 → 19 not taken.
77186 ptr = insertLoad(symbolType, ptr);
629
1/2
✓ Branch 9 → 10 taken 77186 times.
✗ Branch 9 → 25 not taken.
77186 symbolType = symbolType.getContained();
630 }
631 104683 }
632
633 314 llvm::GlobalVariable *IRGenerator::createGlobalConst(const std::string &baseName, llvm::Constant *constant) const {
634 // Get unused name
635
1/2
✓ Branch 2 → 3 taken 314 times.
✗ Branch 2 → 19 not taken.
314 const std::string globalName = getUnusedGlobalName(baseName);
636 // Create global
637
1/2
✓ Branch 5 → 6 taken 314 times.
✗ Branch 5 → 15 not taken.
314 module->getOrInsertGlobal(globalName, constant->getType());
638
1/2
✓ Branch 7 → 8 taken 314 times.
✗ Branch 7 → 16 not taken.
314 llvm::GlobalVariable *global = module->getNamedGlobal(globalName);
639 // Set initializer to the given constant
640
1/2
✓ Branch 8 → 9 taken 314 times.
✗ Branch 8 → 17 not taken.
314 global->setInitializer(constant);
641 314 global->setConstant(true);
642
1/2
✓ Branch 10 → 11 taken 314 times.
✗ Branch 10 → 17 not taken.
314 global->setLinkage(llvm::GlobalValue::PrivateLinkage);
643 314 global->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
644 314 return global;
645 314 }
646
647 12509 llvm::GlobalVariable *IRGenerator::createGlobalStringConst(const std::string &baseName, const std::string &value) const {
648 // Get unused name
649
1/2
✓ Branch 2 → 3 taken 12509 times.
✗ Branch 2 → 21 not taken.
12509 const std::string globalName = getUnusedGlobalName(baseName);
650 // Create global
651
2/4
✓ Branch 3 → 4 taken 12509 times.
✗ Branch 3 → 16 not taken.
✓ Branch 5 → 6 taken 12509 times.
✗ Branch 5 → 15 not taken.
12509 builder.CreateGlobalString(value, globalName, 0, module);
652
1/2
✓ Branch 7 → 8 taken 12509 times.
✗ Branch 7 → 17 not taken.
12509 llvm::GlobalVariable *global = module->getNamedGlobal(globalName);
653 // If the output should be comparable, fix alignment to 4 bytes
654
1/2
✓ Branch 8 → 9 taken 12509 times.
✗ Branch 8 → 12 not taken.
12509 if (cliOptions.comparableOutput)
655
2/4
✓ Branch 9 → 10 taken 12509 times.
✗ Branch 9 → 18 not taken.
✓ Branch 10 → 11 taken 12509 times.
✗ Branch 10 → 18 not taken.
12509 global->setAlignment(llvm::Align(4));
656 12509 return global;
657 12509 }
658
659 12509 llvm::GlobalVariable *IRGenerator::createGlobalStringConst(const std::string &baseName, const std::string &value,
660 const CodeLoc &codeLoc) const {
661 12509 llvm::GlobalVariable *global = createGlobalStringConst(baseName, value);
662 // Create debug info
663
2/2
✓ Branch 3 → 4 taken 135 times.
✓ Branch 3 → 10 taken 12374 times.
12509 if (cliOptions.instrumentation.generateDebugInfo)
664
3/6
✓ Branch 5 → 6 taken 135 times.
✗ Branch 5 → 14 not taken.
✓ Branch 6 → 7 taken 135 times.
✗ Branch 6 → 14 not taken.
✓ Branch 7 → 8 taken 135 times.
✗ Branch 7 → 12 not taken.
135 diGenerator.generateGlobalStringDebugInfo(global, global->getName().str(), value.length(), codeLoc);
665 12509 return global;
666 }
667
668 20798 std::string IRGenerator::getUnusedGlobalName(const std::string &baseName) const {
669 // Find an unused global name
670 20798 std::string globalName;
671 20798 unsigned int suffixNumber = 0;
672 do {
673
1/2
✓ Branch 5 → 6 taken 825546 times.
✗ Branch 5 → 15 not taken.
825546 globalName = baseName + std::to_string(suffixNumber);
674 825546 suffixNumber++;
675
3/4
✓ Branch 10 → 11 taken 825546 times.
✗ Branch 10 → 19 not taken.
✓ Branch 11 → 12 taken 804748 times.
✓ Branch 11 → 13 taken 20798 times.
825546 } while (module->getNamedGlobal(globalName) != nullptr);
676 20798 return globalName;
677 }
678
679 76479 void IRGenerator::materializeConstant(LLVMExprResult &exprResult) {
680 // Skip results, that do not contain a constant or already have a value
681
3/4
✓ Branch 2 → 3 taken 73631 times.
✓ Branch 2 → 4 taken 2848 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 73631 times.
76479 if (exprResult.value != nullptr || exprResult.constant == nullptr)
682 2848 return;
683
684 // Default case: the value to the constant
685 73631 exprResult.value = exprResult.constant;
686 }
687
688 48657 bool IRGenerator::isSymbolDSOLocal(bool isPublic) const {
689 // If we are compiling a shared library and export the global symbol, we need to drop dso_local
690 // because it may be interposed by other shared objects by the dynamic linker.
691
3/4
✓ Branch 2 → 3 taken 43377 times.
✓ Branch 2 → 4 taken 5280 times.
✓ Branch 3 → 4 taken 43377 times.
✗ Branch 3 → 5 not taken.
48657 return !(isPublic && cliOptions.outputContainer == OutputContainer::SHARED_LIBRARY);
692 }
693
694 42771 llvm::GlobalValue::LinkageTypes IRGenerator::getSymbolLinkageType(bool isPublic) const {
695
2/2
✓ Branch 2 → 3 taken 37590 times.
✓ Branch 2 → 4 taken 5181 times.
42771 return isPublic ? llvm::GlobalValue::ExternalLinkage : llvm::GlobalValue::InternalLinkage;
696 }
697
698 6816 llvm::GlobalValue::LinkageTypes IRGenerator::getVTableLinkageType(bool isPublic) const {
699 // VTables, type infos and type info names are ODR entities that may legitimately be emitted in more than one
700 // translation unit (e.g. an interface that is defined in one file and used from several importing files). Giving
701 // them weak ODR linkage lets the linker coalesce the duplicates. On ELF this pairs with the comdat group below; on
702 // MachO, which has no comdat support, the weak/coalesced linkage is what prevents a duplicate-symbol error.
703
2/2
✓ Branch 2 → 3 taken 6717 times.
✓ Branch 2 → 4 taken 99 times.
6816 return isPublic ? llvm::GlobalValue::WeakODRLinkage : llvm::GlobalValue::PrivateLinkage;
704 }
705
706 6816 void IRGenerator::attachComdatToSymbol(llvm::GlobalVariable *global, const std::string &comdatName, bool isPublic) const {
707 // MachO does not support comdat annotations
708
6/6
✓ Branch 2 → 3 taken 6717 times.
✓ Branch 2 → 6 taken 99 times.
✓ Branch 4 → 5 taken 6702 times.
✓ Branch 4 → 6 taken 15 times.
✓ Branch 7 → 8 taken 6702 times.
✓ Branch 7 → 12 taken 114 times.
6816 if (isPublic && cliOptions.targetTriple.getObjectFormat() != llvm::Triple::MachO)
709
2/4
✓ Branch 9 → 10 taken 6702 times.
✗ Branch 9 → 13 not taken.
✓ Branch 10 → 11 taken 6702 times.
✗ Branch 10 → 13 not taken.
6702 global->setComdat(module->getOrInsertComdat(comdatName));
710 6816 }
711
712 /**
713 * Attach the function attributes that all functions we emit have in common.
714 *
715 * Spice does not know exceptions and we never emit landing pads, so none of our functions can unwind. We still request
716 * an unwind table, so that debuggers and profilers are able to produce correct stack traces.
717 *
718 * The size levels are not communicated to LLVM by the pass pipeline alone - since Os and Oz both select the O2
719 * pipeline, 'optsize' and 'minsize' on the individual function are what actually distinguishes them. Without 'minsize',
720 * Oz is indistinguishable from Os.
721 *
722 * @param fct Function to attach the attributes to
723 * @param isAlwaysInline Whether the function was declared as inline
724 */
725 40624 void IRGenerator::addCommonFctAttrs(llvm::Function *fct, bool isAlwaysInline) const {
726 40624 fct->addFnAttr(llvm::Attribute::NoUnwind);
727 40624 fct->addFnAttr(llvm::Attribute::getWithUWTableKind(context, llvm::UWTableKind::Default));
728
729 // Explicitly inlined functions must not be marked as 'optnone', because that is incompatible with 'alwaysinline'.
730 // This matches the behavior of other frontends: an inline request is honored, even at O0.
731
2/2
✓ Branch 5 → 6 taken 8035 times.
✓ Branch 5 → 7 taken 32589 times.
40624 if (isAlwaysInline) {
732 8035 fct->addFnAttr(llvm::Attribute::AlwaysInline);
733
2/2
✓ Branch 7 → 8 taken 32538 times.
✓ Branch 7 → 10 taken 51 times.
32589 } else if (cliOptions.optLevel == OptLevel::O0) {
734 32538 fct->addFnAttr(llvm::Attribute::OptimizeNone);
735 32538 fct->addFnAttr(llvm::Attribute::NoInline); // 'optnone' requires 'noinline'
736 }
737
738
2/2
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 14 taken 40622 times.
40624 if (cliOptions.optLevel >= OptLevel::Os) {
739 2 fct->addFnAttr(llvm::Attribute::OptimizeForSize);
740
2/2
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 1 time.
2 if (cliOptions.optLevel == OptLevel::Oz)
741 1 fct->addFnAttr(llvm::Attribute::MinSize);
742 }
743 40624 }
744
745 268862 llvm::Value *IRGenerator::getAddress(const SymbolTableEntry *entry) {
746
1/2
✓ Branch 2 → 3 taken 268862 times.
✗ Branch 2 → 18 not taken.
268862 const auto it = addressMap.find(entry);
747
5/6
✓ Branch 5 → 6 taken 268264 times.
✓ Branch 5 → 9 taken 598 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 268264 times.
✓ Branch 11 → 12 taken 598 times.
✓ Branch 11 → 13 taken 268264 times.
268862 if (it == addressMap.end() || it->second.empty())
748 598 return nullptr;
749 268264 return it->second.top();
750 }
751
752 198204 void IRGenerator::updateAddress(const SymbolTableEntry *entry, llvm::Value *address) {
753
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 198204 times.
198204 assert(address != nullptr);
754
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 198204 times.
198204 assert(address->getType()->isPointerTy());
755 198204 auto &stack = addressMap[entry];
756
2/2
✓ Branch 10 → 11 taken 159304 times.
✓ Branch 10 → 12 taken 38900 times.
198204 if (stack.empty())
757 159304 stack.push(address);
758 else
759 38900 stack.top() = address;
760 198204 }
761
762 60 void IRGenerator::pushAddress(const SymbolTableEntry *entry, llvm::Value *address) {
763
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 60 times.
60 assert(address != nullptr);
764 60 addressMap[entry].push(address);
765 60 }
766
767 60 void IRGenerator::popAddress(const SymbolTableEntry *entry) {
768
1/2
✓ Branch 2 → 3 taken 60 times.
✗ Branch 2 → 14 not taken.
60 auto it = addressMap.find(entry);
769
2/4
✓ Branch 5 → 6 taken 60 times.
✗ Branch 5 → 10 not taken.
✓ Branch 8 → 9 taken 60 times.
✗ Branch 8 → 10 not taken.
60 assert(it != addressMap.end() && !it->second.empty());
770 60 it->second.pop();
771 60 }
772
773 7346 llvm::Function *IRGenerator::getLLVMFunction(const Function *spiceFunc) {
774
1/2
✓ Branch 2 → 3 taken 7346 times.
✗ Branch 2 → 12 not taken.
7346 const auto it = llvmFunctions.find(spiceFunc);
775
2/2
✓ Branch 5 → 6 taken 2964 times.
✓ Branch 5 → 8 taken 4382 times.
14692 return it != llvmFunctions.end() ? it->second : nullptr;
776 }
777
778 39140 void IRGenerator::setLLVMFunction(const Function *spiceFunc, llvm::Function *llvmFunction) {
779
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 39140 times.
39140 assert(llvmFunction != nullptr);
780 39140 llvmFunctions[spiceFunc] = llvmFunction;
781 39140 }
782
783 4844 std::string IRGenerator::getIRString(llvm::Module *llvmModule, const CliOptions &cliOptions) {
784
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 4844 times.
4844 assert(llvmModule != nullptr); // Make sure the module hasn't been moved away
785
3/4
✓ Branch 4 → 5 taken 4844 times.
✗ Branch 4 → 7 not taken.
✓ Branch 5 → 6 taken 4746 times.
✓ Branch 5 → 7 taken 98 times.
4844 const bool eliminateTarget = cliOptions.comparableOutput && cliOptions.isNativeTarget;
786
787 // Backup target triple and data layout
788
1/2
✓ Branch 9 → 10 taken 4844 times.
✗ Branch 9 → 45 not taken.
4844 const llvm::Triple targetTriple = llvmModule->getTargetTriple();
789
1/2
✓ Branch 11 → 12 taken 4844 times.
✗ Branch 11 → 43 not taken.
4844 const std::string targetDataLayout = llvmModule->getDataLayoutStr();
790 // Remove target triple and data layout
791
2/2
✓ Branch 12 → 13 taken 4746 times.
✓ Branch 12 → 19 taken 98 times.
4844 if (eliminateTarget) {
792 4746 llvmModule->setTargetTriple(llvm::Triple());
793
2/4
✓ Branch 16 → 17 taken 4746 times.
✗ Branch 16 → 34 not taken.
✓ Branch 17 → 18 taken 4746 times.
✗ Branch 17 → 34 not taken.
4746 llvmModule->setDataLayout("");
794 }
795
796 // Get IR string
797 4844 std::string output;
798
1/2
✓ Branch 20 → 21 taken 4844 times.
✗ Branch 20 → 39 not taken.
4844 llvm::raw_string_ostream oss(output);
799
1/2
✓ Branch 21 → 22 taken 4844 times.
✗ Branch 21 → 37 not taken.
4844 llvmModule->print(oss, nullptr);
800
801 // Restore target triple and data layout
802
2/2
✓ Branch 22 → 23 taken 4746 times.
✓ Branch 22 → 29 taken 98 times.
4844 if (eliminateTarget) {
803
1/2
✓ Branch 23 → 24 taken 4746 times.
✗ Branch 23 → 35 not taken.
4746 llvmModule->setTargetTriple(targetTriple);
804
1/2
✓ Branch 27 → 28 taken 4746 times.
✗ Branch 27 → 36 not taken.
4746 llvmModule->setDataLayout(targetDataLayout);
805 }
806
807 4844 return output;
808 4844 }
809
810 /**
811 * Returns the operator function list for the current manifestation and the given node
812 *
813 * @param node Node to retrieve the op fct pointer list from
814 * @return Op fct pointer list
815 */
816 87874 const std::vector<const Function *> &IRGenerator::getOpFctPointers(const ASTNode *node) const {
817
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 87874 times.
87874 assert(node->getOpFctPointers()->size() > manIdx);
818 87874 return node->getOpFctPointers()->at(manIdx);
819 }
820
821 } // namespace spice::compiler
822