GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 97.7% 476 / 7 / 494
Functions: 96.2% 51 / 0 / 53
Branches: 62.4% 520 / 24 / 858

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 5633 IRGenerator::IRGenerator(GlobalResourceManager &resourceManager, SourceFile *sourceFile)
20 5633 : CompilerPass(resourceManager, sourceFile), context(cliOptions.useLTO ? resourceManager.ltoContext : sourceFile->context),
21
1/2
✓ Branch 8 → 9 taken 5633 times.
✗ Branch 8 → 81 not taken.
5633 builder(sourceFile->builder), module(sourceFile->llvmModule.get()), conversionManager(sourceFile, this),
22
6/10
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 6 taken 5629 times.
✓ Branch 9 → 10 taken 5633 times.
✗ Branch 9 → 81 not taken.
✓ Branch 10 → 11 taken 5633 times.
✗ Branch 10 → 81 not taken.
✓ Branch 11 → 12 taken 5633 times.
✗ Branch 11 → 79 not taken.
✓ Branch 15 → 16 taken 5633 times.
✗ Branch 15 → 75 not taken.
11266 stdFunctionManager(sourceFile, resourceManager, module) {
23 // Attach information to the module
24
1/2
✓ Branch 19 → 20 taken 5633 times.
✗ Branch 19 → 54 not taken.
5633 module->setTargetTriple(cliOptions.targetTriple);
25
2/4
✓ Branch 23 → 24 taken 5633 times.
✗ Branch 23 → 57 not taken.
✓ Branch 24 → 25 taken 5633 times.
✗ Branch 24 → 55 not taken.
5633 module->setDataLayout(sourceFile->targetMachine->createDataLayout());
26
2/2
✓ Branch 26 → 27 taken 2 times.
✓ Branch 26 → 29 taken 5631 times.
5633 if (cliOptions.outputContainer == OutputContainer::SHARED_LIBRARY) {
27
1/2
✓ Branch 27 → 28 taken 2 times.
✗ Branch 27 → 67 not taken.
2 module->setPICLevel(llvm::PICLevel::SmallPIC);
28
1/2
✓ Branch 28 → 31 taken 2 times.
✗ Branch 28 → 67 not taken.
2 module->setPIELevel(llvm::PIELevel::Default);
29 } else {
30
1/2
✓ Branch 29 → 30 taken 5631 times.
✗ Branch 29 → 67 not taken.
5631 module->setPICLevel(llvm::PICLevel::BigPIC);
31
1/2
✓ Branch 30 → 31 taken 5631 times.
✗ Branch 30 → 67 not taken.
5631 module->setPIELevel(llvm::PIELevel::Large);
32 }
33
1/2
✓ Branch 31 → 32 taken 5633 times.
✗ Branch 31 → 67 not taken.
5633 module->setUwtable(llvm::UWTableKind::Default);
34
3/4
✓ Branch 32 → 33 taken 2 times.
✓ Branch 32 → 34 taken 5631 times.
✓ Branch 35 → 36 taken 5633 times.
✗ Branch 35 → 67 not taken.
5633 module->setFramePointer(cliOptions.keepFramePointers ? llvm::FramePointerKind::All : llvm::FramePointerKind::None);
35
36 // Add module identifier metadata
37
2/4
✓ Branch 36 → 37 taken 5633 times.
✗ Branch 36 → 58 not taken.
✓ Branch 37 → 38 taken 5633 times.
✗ Branch 37 → 58 not taken.
5633 llvm::NamedMDNode *identifierMetadata = module->getOrInsertNamedMetadata("llvm.ident");
38
3/6
✓ Branch 39 → 40 taken 5633 times.
✗ Branch 39 → 59 not taken.
✓ Branch 41 → 42 taken 5633 times.
✗ Branch 41 → 59 not taken.
✓ Branch 42 → 43 taken 5633 times.
✗ Branch 42 → 59 not taken.
5633 identifierMetadata->addOperand(llvm::MDNode::get(context, llvm::MDString::get(context, PRODUCER_STRING)));
39
40 // Initialize common LLVM types
41
4/8
✓ Branch 43 → 44 taken 5633 times.
✗ Branch 43 → 62 not taken.
✓ Branch 44 → 45 taken 5633 times.
✗ Branch 44 → 62 not taken.
✓ Branch 45 → 46 taken 5633 times.
✗ Branch 45 → 62 not taken.
✓ Branch 47 → 48 taken 5633 times.
✗ Branch 47 → 62 not taken.
5633 llvmTypes.lambdaFatPtrType = llvm::StructType::get(context, {builder.getPtrTy(), builder.getPtrTy(), builder.getInt64Ty()});
42
43 // Initialize debug info generator
44
2/2
✓ Branch 48 → 49 taken 2843 times.
✓ Branch 48 → 53 taken 2790 times.
5633 if (cliOptions.instrumentation.generateDebugInfo)
45
2/4
✓ Branch 49 → 50 taken 2843 times.
✗ Branch 49 → 66 not taken.
✓ Branch 50 → 51 taken 2843 times.
✗ Branch 50 → 64 not taken.
2843 diGenerator.initialize(sourceFile->fileName, sourceFile->fileDir);
46 5633 }
47
48 5633 std::any IRGenerator::visitEntry(const EntryNode *node) {
49 // Generate IR
50
1/2
✓ Branch 2 → 3 taken 5633 times.
✗ Branch 2 → 28 not taken.
5633 visitChildren(node);
51
52 // Generate test main if required
53
4/4
✓ Branch 4 → 5 taken 947 times.
✓ Branch 4 → 7 taken 4686 times.
✓ Branch 5 → 6 taken 10 times.
✓ Branch 5 → 7 taken 937 times.
5633 if (sourceFile->isMainFile && cliOptions.generateTestMain)
54 10 generateTestMain();
55
56 // Execute deferred VTable initializations
57
2/2
✓ Branch 21 → 9 taken 4522 times.
✓ Branch 21 → 22 taken 5633 times.
15788 for (DeferredLogic &deferredVTableInit : deferredVTableInitializations)
58
1/2
✓ Branch 11 → 12 taken 4522 times.
✗ Branch 11 → 29 not taken.
4522 deferredVTableInit.execute();
59
60 // Finalize debug info generator
61 5633 diGenerator.finalize();
62
63 // Verify module
64 5633 verifyModule(node->codeLoc);
65
66
1/2
✓ Branch 24 → 25 taken 5633 times.
✗ Branch 24 → 30 not taken.
11266 return nullptr;
67 }
68
69 246084 llvm::AllocaInst *IRGenerator::insertAlloca(llvm::Type *llvmType, const std::string &varName) {
70
2/2
✓ Branch 2 → 3 taken 160162 times.
✓ Branch 2 → 8 taken 85922 times.
246084 if (allocaInsertInst != nullptr) { // If there is already an alloca inst, insert right after that
71
2/4
✓ Branch 3 → 4 taken 160162 times.
✗ Branch 3 → 19 not taken.
✓ Branch 4 → 5 taken 160162 times.
✗ Branch 4 → 19 not taken.
160162 llvm::AllocaInst *allocaInst = builder.CreateAlloca(llvmType, nullptr, varName);
72 160162 allocaInst->dropLocation(); // Part of prologue
73 160162 allocaInst->moveAfter(allocaInsertInst);
74 160162 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 85922 llvm::BasicBlock *currentBlock = builder.GetInsertBlock();
78 85922 builder.SetInsertPoint(allocaInsertBlock, allocaInsertBlock->begin());
79
80 // Allocate the size of the given LLVM type
81
2/4
✓ Branch 11 → 12 taken 85922 times.
✗ Branch 11 → 20 not taken.
✓ Branch 12 → 13 taken 85922 times.
✗ Branch 12 → 20 not taken.
85922 allocaInsertInst = builder.CreateAlloca(llvmType, nullptr, varName);
82 85922 allocaInsertInst->dropLocation(); // Part of prologue
83
84 // Restore old basic block
85 85922 builder.SetInsertPoint(currentBlock);
86 }
87
88 // Insert lifetime start marker
89
2/2
✓ Branch 15 → 16 taken 1657 times.
✓ Branch 15 → 17 taken 244427 times.
246084 if (cliOptions.useLifetimeMarkers)
90 1657 builder.CreateLifetimeStart(allocaInsertInst);
91
92 246084 return allocaInsertInst;
93 }
94
95 196859 llvm::AllocaInst *IRGenerator::insertAlloca(const QualType &qualType, const std::string &varName) {
96 196859 llvm::Type *llvmType = qualType.toLLVMType(sourceFile);
97 196859 llvm::AllocaInst *alloca = insertAlloca(llvmType, varName);
98
99 // Insert type metadata
100
2/2
✓ Branch 4 → 5 taken 8 times.
✓ Branch 4 → 6 taken 196851 times.
196859 if (cliOptions.useTBAAMetadata)
101 8 mdGenerator.generateTypeMetadata(allocaInsertInst, qualType);
102
103 196859 return alloca;
104 }
105
106 587172 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 587172 times.
587172 assert(ptr->getType()->isPointerTy());
109
2/4
✓ Branch 6 → 7 taken 587172 times.
✗ Branch 6 → 11 not taken.
✓ Branch 7 → 8 taken 587172 times.
✗ Branch 7 → 11 not taken.
587172 return builder.CreateLoad(llvmType, ptr, isVolatile, varName);
110 }
111
112 505713 llvm::LoadInst *IRGenerator::insertLoad(const QualType &qualType, llvm::Value *ptr, bool isVolatile, const std::string &varName) {
113 505713 llvm::Type *llvmType = qualType.toLLVMType(sourceFile);
114 505713 llvm::LoadInst *load = insertLoad(llvmType, ptr, isVolatile, varName);
115
2/2
✓ Branch 4 → 5 taken 10 times.
✓ Branch 4 → 6 taken 505703 times.
505713 if (cliOptions.useTBAAMetadata)
116 10 mdGenerator.generateTBAAMetadata(load, qualType);
117 505713 return load;
118 }
119
120 308985 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 308985 times.
308985 assert(ptr->getType()->isPointerTy());
122 308985 return builder.CreateStore(val, ptr, isVolatile);
123 }
124
125 87479 void IRGenerator::insertStore(llvm::Value *val, llvm::Value *ptr, const QualType &qualType, bool isVolatile) {
126 87479 llvm::StoreInst *store = insertStore(val, ptr, isVolatile);
127
2/2
✓ Branch 3 → 4 taken 8 times.
✓ Branch 3 → 5 taken 87471 times.
87479 if (cliOptions.useTBAAMetadata)
128 8 mdGenerator.generateTBAAMetadata(store, qualType);
129 87479 }
130
131 169304 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 169304 times.
169304 assert(basePtr->getType()->isPointerTy());
134
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 169304 times.
169304 assert(!indices.empty());
135
4/6
✓ Branch 4 → 5 taken 167592 times.
✓ Branch 4 → 7 taken 154922 times.
✓ Branch 6 → 7 taken 167592 times.
✗ Branch 6 → 8 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 169304 times.
491818 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 169304 times.
✗ Branch 12 → 17 not taken.
✓ Branch 13 → 14 taken 169304 times.
✗ Branch 13 → 17 not taken.
169304 return builder.CreateInBoundsGEP(type, basePtr, indices, varName);
142 }
143
144 59414 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 59414 times.
59414 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 19472 times.
✓ Branch 6 → 8 taken 39942 times.
59414 if (index == 0)
150 19472 return basePtr;
151
152 // Insert GEP
153
2/4
✓ Branch 8 → 9 taken 39942 times.
✗ Branch 8 → 13 not taken.
✓ Branch 9 → 10 taken 39942 times.
✗ Branch 9 → 13 not taken.
39942 return builder.CreateStructGEP(type, basePtr, index, varName);
154 }
155
156 331997 llvm::Value *IRGenerator::resolveValue(const ExprNode *node) {
157 // Visit the given AST node
158
2/4
✓ Branch 2 → 3 taken 331997 times.
✗ Branch 2 → 11 not taken.
✓ Branch 3 → 4 taken 331997 times.
✗ Branch 3 → 9 not taken.
331997 auto exprResult = any_cast<LLVMExprResult>(visit(node));
159
1/2
✓ Branch 5 → 6 taken 331997 times.
✗ Branch 5 → 12 not taken.
663994 return resolveValue(node, exprResult);
160 }
161
162 367751 llvm::Value *IRGenerator::resolveValue(const ExprNode *node, LLVMExprResult &exprResult) {
163 367751 return resolveValue(node->getEvaluatedSymbolType(manIdx), exprResult);
164 }
165
166 715245 llvm::Value *IRGenerator::resolveValue(const QualType &qualType, LLVMExprResult &exprResult) {
167 // Check if the value is already present
168
2/2
✓ Branch 2 → 3 taken 224016 times.
✓ Branch 2 → 4 taken 491229 times.
715245 if (exprResult.value != nullptr)
169 224016 return exprResult.value;
170
171 // Check if a constant is present
172
2/2
✓ Branch 4 → 5 taken 150050 times.
✓ Branch 4 → 7 taken 341179 times.
491229 if (exprResult.constant != nullptr) {
173 150050 materializeConstant(exprResult);
174 150050 return exprResult.value;
175 }
176
177
3/4
✓ Branch 7 → 8 taken 4457 times.
✓ Branch 7 → 10 taken 336722 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 4457 times.
341179 assert(exprResult.ptr != nullptr || exprResult.refPtr != nullptr);
178
179 // De-reference if reference type
180
4/4
✓ Branch 10 → 11 taken 321500 times.
✓ Branch 10 → 13 taken 19679 times.
✓ Branch 11 → 12 taken 22 times.
✓ Branch 11 → 13 taken 321478 times.
341179 const bool isVolatile = exprResult.entry && exprResult.entry->isVolatile;
181
4/4
✓ Branch 14 → 15 taken 4477 times.
✓ Branch 14 → 24 taken 336702 times.
✓ Branch 15 → 16 taken 4457 times.
✓ Branch 15 → 24 taken 20 times.
341179 if (exprResult.refPtr != nullptr && exprResult.ptr == nullptr)
182
2/4
✓ Branch 19 → 20 taken 4457 times.
✗ Branch 19 → 34 not taken.
✓ Branch 20 → 21 taken 4457 times.
✗ Branch 20 → 34 not taken.
4457 exprResult.ptr = insertLoad(builder.getPtrTy(), exprResult.refPtr, isVolatile);
183
184 // Load the value from the pointer
185
1/2
✓ Branch 24 → 25 taken 341179 times.
✗ Branch 24 → 46 not taken.
341179 const QualType referencedType = qualType.removeReferenceWrapper();
186
1/2
✓ Branch 28 → 29 taken 341179 times.
✗ Branch 28 → 40 not taken.
341179 exprResult.value = insertLoad(referencedType, exprResult.ptr, isVolatile);
187
188 341179 return exprResult.value;
189 }
190
191 34208 llvm::Value *IRGenerator::resolveAddress(const ASTNode *node) {
192 // Visit the given AST node
193
2/4
✓ Branch 2 → 3 taken 34208 times.
✗ Branch 2 → 11 not taken.
✓ Branch 3 → 4 taken 34208 times.
✗ Branch 3 → 9 not taken.
34208 auto exprResult = any_cast<LLVMExprResult>(visit(node));
194
1/2
✓ Branch 5 → 6 taken 34208 times.
✗ Branch 5 → 12 not taken.
68416 return resolveAddress(exprResult);
195 }
196
197 266520 llvm::Value *IRGenerator::resolveAddress(LLVMExprResult &exprResult) {
198 // Check if an address is already present
199
2/2
✓ Branch 2 → 3 taken 218072 times.
✓ Branch 2 → 4 taken 48448 times.
266520 if (exprResult.ptr != nullptr)
200 218072 return exprResult.ptr;
201
202 // Check if the reference address is already present
203
3/4
✓ Branch 4 → 5 taken 38014 times.
✓ Branch 4 → 7 taken 10434 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 38014 times.
48448 const bool isVolatile = exprResult.entry && exprResult.entry->isVolatile;
204
3/4
✓ Branch 8 → 9 taken 39952 times.
✓ Branch 8 → 18 taken 8496 times.
✓ Branch 9 → 10 taken 39952 times.
✗ Branch 9 → 18 not taken.
48448 if (exprResult.refPtr != nullptr && exprResult.ptr == nullptr) {
205
2/4
✓ Branch 13 → 14 taken 39952 times.
✗ Branch 13 → 35 not taken.
✓ Branch 14 → 15 taken 39952 times.
✗ Branch 14 → 35 not taken.
39952 exprResult.ptr = insertLoad(builder.getPtrTy(), exprResult.refPtr, isVolatile);
206 39952 return exprResult.ptr;
207 }
208
209 // If not, store the value or constant
210 8496 materializeConstant(exprResult);
211
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 8496 times.
8496 assert(exprResult.value != nullptr);
212
7/12
✓ Branch 21 → 22 taken 12 times.
✓ Branch 21 → 23 taken 8484 times.
✓ Branch 22 → 26 taken 12 times.
✗ Branch 22 → 43 not taken.
✓ Branch 25 → 26 taken 8484 times.
✗ Branch 25 → 43 not taken.
✓ Branch 27 → 28 taken 8496 times.
✗ Branch 27 → 41 not taken.
✓ Branch 29 → 30 taken 8484 times.
✓ Branch 29 → 32 taken 12 times.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 46 not taken.
16980 exprResult.ptr = insertAlloca(exprResult.value->getType(), exprResult.entry ? exprResult.entry->name : "");
213 8496 insertStore(exprResult.value, exprResult.ptr, isVolatile);
214
215 8496 return exprResult.ptr;
216 }
217
218 /**
219 * Pack a scalar compile-time constant (integer, float, or a null pointer/aggregate) into a raw byte-array constant of
220 * the given type, using the target's endianness. This is used to embed a union's default field value into its
221 * byte-buffer payload without needing a bitcast between unrelated LLVM types (which is not something opaque-pointer-
222 * era LLVM supports between arbitrary aggregate/scalar types).
223 *
224 * Non-null pointer-shaped constants (e.g. a string literal's global address) cannot be represented this way, since
225 * their real value is only known at link/load time (a relocation) and a plain byte array cannot carry one.
226 *
227 * @param value Compile-time constant to pack
228 * @param byteArrayType Target byte-array type (element type i8)
229 * @return Packed byte-array constant
230 */
231 4 llvm::Constant *IRGenerator::packConstantAsByteArray(llvm::Constant *value, llvm::ArrayType *byteArrayType) const {
232
2/4
✓ Branch 3 → 4 taken 4 times.
✗ Branch 3 → 77 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 4 times.
4 assert(byteArrayType->getElementType()->isIntegerTy(8));
233 4 const uint64_t numBytes = byteArrayType->getNumElements();
234
235 // A null/zero constant (e.g. a null pointer, or a zero-initialized aggregate) packs trivially as all-zero bytes.
236
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 11 taken 4 times.
4 if (value->isNullValue())
237 return llvm::Constant::getNullValue(byteArrayType);
238
239 4 llvm::APInt bits;
240
2/4
✓ Branch 12 → 13 taken 4 times.
✗ Branch 12 → 75 not taken.
✓ Branch 13 → 14 taken 4 times.
✗ Branch 13 → 16 not taken.
4 if (const auto *constantInt = llvm::dyn_cast<llvm::ConstantInt>(value)) {
241
1/2
✓ Branch 15 → 31 taken 4 times.
✗ Branch 15 → 75 not taken.
4 bits = constantInt->getValue();
242 } else if (const auto *constantFP = llvm::dyn_cast<llvm::ConstantFP>(value)) {
243 bits = constantFP->getValueAPF().bitcastToAPInt();
244 } else {
245 // See the function comment: this is a rare case in practice (most union field defaults are numeric), so it is
246 // deliberately unsupported for now rather than silently producing a wrong default value.
247 throw CompilerError(INTERNAL_ERROR, "Unsupported default value for a union field of this type"); // GCOV_EXCL_LINE
248 }
249
1/2
✓ Branch 31 → 32 taken 4 times.
✗ Branch 31 → 66 not taken.
4 bits = bits.zext(numBytes * 8);
250
251 4 const bool isLittleEndian = module->getDataLayout().isLittleEndian();
252
1/2
✓ Branch 38 → 39 taken 4 times.
✗ Branch 38 → 67 not taken.
8 std::vector<uint8_t> bytes(numBytes);
253
2/2
✓ Branch 49 → 41 taken 32 times.
✓ Branch 49 → 50 taken 4 times.
36 for (uint64_t i = 0; i < numBytes; i++) {
254
1/2
✓ Branch 41 → 42 taken 32 times.
✗ Branch 41 → 43 not taken.
32 const uint64_t byteIndex = isLittleEndian ? i : numBytes - 1 - i;
255
2/4
✓ Branch 44 → 45 taken 32 times.
✗ Branch 44 → 72 not taken.
✓ Branch 45 → 46 taken 32 times.
✗ Branch 45 → 70 not taken.
32 bytes[byteIndex] = static_cast<uint8_t>(bits.extractBits(8, i * 8).getZExtValue());
256 }
257
258
1/2
✓ Branch 50 → 51 taken 4 times.
✗ Branch 50 → 73 not taken.
4 return llvm::ConstantDataArray::get(context, bytes);
259 4 }
260
261 14225 llvm::Constant *IRGenerator::getDefaultValueForSymbolType(const QualType &symbolType) { // NOLINT(misc-no-recursion)
262 // Double
263
2/2
✓ Branch 3 → 4 taken 92 times.
✓ Branch 3 → 9 taken 14133 times.
14225 if (symbolType.is(TY_DOUBLE))
264
2/4
✓ Branch 4 → 5 taken 92 times.
✗ Branch 4 → 174 not taken.
✓ Branch 5 → 6 taken 92 times.
✗ Branch 5 → 172 not taken.
92 return llvm::ConstantFP::get(context, llvm::APFloat(0.0));
265
266 // Int
267
2/2
✓ Branch 10 → 11 taken 2109 times.
✓ Branch 10 → 13 taken 12024 times.
14133 if (symbolType.is(TY_INT))
268 2109 return builder.getInt32(0);
269
270 // Short
271
2/2
✓ Branch 14 → 15 taken 32 times.
✓ Branch 14 → 17 taken 11992 times.
12024 if (symbolType.is(TY_SHORT))
272 32 return builder.getInt16(0);
273
274 // Long
275
2/2
✓ Branch 18 → 19 taken 2547 times.
✓ Branch 18 → 21 taken 9445 times.
11992 if (symbolType.is(TY_LONG))
276 2547 return builder.getInt64(0);
277
278 // Byte or char
279
3/4
✓ Branch 21 → 22 taken 9445 times.
✗ Branch 21 → 175 not taken.
✓ Branch 22 → 23 taken 294 times.
✓ Branch 22 → 25 taken 9151 times.
9445 if (symbolType.isOneOf({TY_BYTE, TY_CHAR}))
280 294 return builder.getInt8(0);
281
282 // String
283
2/2
✓ Branch 26 → 27 taken 1957 times.
✓ Branch 26 → 35 taken 7194 times.
9151 if (symbolType.is(TY_STRING)) {
284
3/6
✓ Branch 27 → 28 taken 1957 times.
✗ Branch 27 → 177 not taken.
✓ Branch 28 → 29 taken 1957 times.
✗ Branch 28 → 176 not taken.
✓ Branch 29 → 30 taken 1957 times.
✗ Branch 29 → 176 not taken.
1957 llvm::GlobalVariable *globalString = builder.CreateGlobalString("", "");
285
1/2
✓ Branch 30 → 31 taken 1957 times.
✗ Branch 30 → 34 not taken.
1957 if (cliOptions.comparableOutput)
286
2/4
✓ Branch 31 → 32 taken 1957 times.
✗ Branch 31 → 178 not taken.
✓ Branch 32 → 33 taken 1957 times.
✗ Branch 32 → 178 not taken.
1957 globalString->setAlignment(llvm::Align(4));
287 1957 return globalString;
288 }
289
290 // Bool
291
2/2
✓ Branch 36 → 37 taken 368 times.
✓ Branch 36 → 39 taken 6826 times.
7194 if (symbolType.is(TY_BOOL))
292 368 return builder.getFalse();
293
294 // Pointer or reference
295
3/4
✓ Branch 39 → 40 taken 6826 times.
✗ Branch 39 → 179 not taken.
✓ Branch 40 → 41 taken 4922 times.
✓ Branch 40 → 44 taken 1904 times.
6826 if (symbolType.isOneOf({TY_PTR, TY_REF}))
296 4922 return llvm::Constant::getNullValue(builder.getPtrTy());
297
298 // Array
299
2/2
✓ Branch 45 → 46 taken 202 times.
✓ Branch 45 → 61 taken 1702 times.
1904 if (symbolType.isArray()) {
300 // Get array size
301
1/2
✓ Branch 46 → 47 taken 202 times.
✗ Branch 46 → 188 not taken.
202 const size_t arraySize = symbolType.getArraySize();
302
303 // Get default value for item
304
2/4
✓ Branch 47 → 48 taken 202 times.
✗ Branch 47 → 180 not taken.
✓ Branch 48 → 49 taken 202 times.
✗ Branch 48 → 180 not taken.
202 llvm::Constant *defaultItemValue = getDefaultValueForSymbolType(symbolType.getContained());
305
306 // Retrieve array and item type
307
2/4
✓ Branch 49 → 50 taken 202 times.
✗ Branch 49 → 181 not taken.
✓ Branch 50 → 51 taken 202 times.
✗ Branch 50 → 181 not taken.
202 llvm::Type *itemType = symbolType.getContained().toLLVMType(sourceFile);
308
1/2
✓ Branch 51 → 52 taken 202 times.
✗ Branch 51 → 188 not taken.
202 llvm::ArrayType *arrayType = llvm::ArrayType::get(itemType, arraySize);
309
310 // Create a constant array with n times the default value
311
1/2
✓ Branch 54 → 55 taken 202 times.
✗ Branch 54 → 182 not taken.
404 const std::vector itemConstants(arraySize, defaultItemValue);
312
1/2
✓ Branch 57 → 58 taken 202 times.
✗ Branch 57 → 185 not taken.
202 return llvm::ConstantArray::get(arrayType, itemConstants);
313 202 }
314
315 // Function or procedure
316
3/4
✓ Branch 61 → 62 taken 1702 times.
✗ Branch 61 → 189 not taken.
✓ Branch 62 → 63 taken 146 times.
✓ Branch 62 → 70 taken 1556 times.
1702 if (symbolType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) {
317
2/4
✓ Branch 63 → 64 taken 146 times.
✗ Branch 63 → 190 not taken.
✓ Branch 64 → 65 taken 146 times.
✗ Branch 64 → 190 not taken.
146 llvm::Constant *ptrDefaultValue = getDefaultValueForSymbolType(QualType(TY_PTR));
318 146 llvm::Constant *sizeDefaultValue = builder.getInt64(0);
319
1/2
✓ Branch 67 → 68 taken 146 times.
✗ Branch 67 → 191 not taken.
146 return llvm::ConstantStruct::get(llvmTypes.lambdaFatPtrType, {ptrDefaultValue, ptrDefaultValue, sizeDefaultValue});
320 }
321
322 // Struct
323
2/2
✓ Branch 71 → 72 taken 1456 times.
✓ Branch 71 → 111 taken 100 times.
1556 if (symbolType.is(TY_STRUCT)) {
324 // Retrieve field count
325
1/2
✓ Branch 72 → 73 taken 1456 times.
✗ Branch 72 → 198 not taken.
1456 Scope *structScope = symbolType.getBodyScope();
326
1/2
✗ Branch 73 → 74 not taken.
✓ Branch 73 → 75 taken 1456 times.
1456 assert(structScope != nullptr);
327
1/2
✓ Branch 75 → 76 taken 1456 times.
✗ Branch 75 → 198 not taken.
1456 const size_t fieldCount = structScope->getFieldCount();
328
329 // Get default values for all fields of the struct
330 1456 std::vector<llvm::Constant *> fieldConstants;
331
1/2
✓ Branch 76 → 77 taken 1456 times.
✗ Branch 76 → 196 not taken.
1456 fieldConstants.reserve(fieldCount);
332
333 // Add default value for each struct field
334
2/2
✓ Branch 103 → 78 taken 4156 times.
✓ Branch 103 → 104 taken 1456 times.
5612 for (size_t i = 0; i < fieldCount; i++) {
335 // Get entry of the field
336
1/2
✗ Branch 78 → 79 not taken.
✓ Branch 78 → 81 taken 4156 times.
4156 const SymbolTableEntry *fieldEntry = structScope->lookupField(i);
337
3/6
✓ Branch 84 → 85 taken 4156 times.
✗ Branch 84 → 88 not taken.
✓ Branch 85 → 86 taken 4156 times.
✗ Branch 85 → 194 not taken.
✓ Branch 86 → 87 taken 4156 times.
✗ Branch 86 → 88 not taken.
4156 assert(fieldEntry != nullptr && fieldEntry->isField());
338
339 // Retrieve default field value
340 llvm::Constant *defaultFieldValue;
341
5/6
✓ Branch 89 → 90 taken 4156 times.
✗ Branch 89 → 91 not taken.
✓ Branch 92 → 93 taken 4110 times.
✓ Branch 92 → 98 taken 46 times.
✓ Branch 93 → 94 taken 3600 times.
✓ Branch 93 → 98 taken 510 times.
4156 if (const auto fieldNode = dynamic_cast<FieldNode *>(fieldEntry->declNode); fieldNode && fieldNode->defaultValue)
342
3/6
✓ Branch 94 → 95 taken 3600 times.
✗ Branch 94 → 194 not taken.
✓ Branch 95 → 96 taken 3600 times.
✗ Branch 95 → 193 not taken.
✓ Branch 96 → 97 taken 3600 times.
✗ Branch 96 → 193 not taken.
3600 defaultFieldValue = getConst(fieldNode->defaultValue->getCompileTimeValue(manIdx), fieldEntry->getQualType(), fieldNode);
343 else
344
2/4
✓ Branch 98 → 99 taken 556 times.
✗ Branch 98 → 194 not taken.
✓ Branch 99 → 100 taken 556 times.
✗ Branch 99 → 194 not taken.
556 defaultFieldValue = getDefaultValueForSymbolType(fieldEntry->getQualType());
345
346
1/2
✓ Branch 101 → 102 taken 4156 times.
✗ Branch 101 → 194 not taken.
4156 fieldConstants.push_back(defaultFieldValue);
347 }
348
349
2/4
✓ Branch 104 → 105 taken 1456 times.
✗ Branch 104 → 196 not taken.
✓ Branch 105 → 106 taken 1456 times.
✗ Branch 105 → 196 not taken.
1456 const auto structType = llvm::cast<llvm::StructType>(symbolType.toLLVMType(sourceFile));
350
1/2
✓ Branch 107 → 108 taken 1456 times.
✗ Branch 107 → 195 not taken.
1456 return llvm::ConstantStruct::get(structType, fieldConstants);
351 1456 }
352
353 // Union
354
2/2
✓ Branch 112 → 113 taken 52 times.
✓ Branch 112 → 155 taken 48 times.
100 if (symbolType.is(TY_UNION)) {
355 52 Scope *unionScope = symbolType.getBodyScope();
356
1/2
✗ Branch 114 → 115 not taken.
✓ Branch 114 → 116 taken 52 times.
52 assert(unionScope != nullptr);
357 52 const size_t fieldCount = unionScope->getFieldCount();
358
359 // Find the (at most one) field with a default value. Tag 0 is reserved for the "unset" state (see the matching
360 // comment in IRGenerator::visitPostfixUnaryExpr's TY_UNION branch), so a field's tag is its index shifted by one.
361 52 uint32_t defaultFieldTag = 0; // Sentinel: no field is active ("unset" state)
362 52 llvm::Constant *defaultFieldValue = nullptr;
363
2/2
✓ Branch 139 → 118 taken 280 times.
✓ Branch 139 → 140 taken 48 times.
328 for (size_t i = 0; i < fieldCount; i++) {
364
1/2
✓ Branch 118 → 119 taken 280 times.
✗ Branch 118 → 121 not taken.
280 const SymbolTableEntry *fieldEntry = unionScope->lookupField(i);
365
2/4
✓ Branch 124 → 125 taken 280 times.
✗ Branch 124 → 128 not taken.
✓ Branch 126 → 127 taken 280 times.
✗ Branch 126 → 128 not taken.
280 assert(fieldEntry != nullptr && fieldEntry->isField());
366
1/2
✓ Branch 129 → 130 taken 280 times.
✗ Branch 129 → 131 not taken.
280 const auto fieldNode = dynamic_cast<FieldNode *>(fieldEntry->declNode);
367
3/4
✓ Branch 132 → 133 taken 280 times.
✗ Branch 132 → 138 not taken.
✓ Branch 133 → 134 taken 4 times.
✓ Branch 133 → 138 taken 276 times.
280 if (fieldNode && fieldNode->defaultValue) {
368 4 defaultFieldTag = static_cast<uint32_t>(i) + 1;
369
2/4
✓ Branch 135 → 136 taken 4 times.
✗ Branch 135 → 199 not taken.
✓ Branch 136 → 137 taken 4 times.
✗ Branch 136 → 199 not taken.
4 defaultFieldValue = getConst(fieldNode->defaultValue->getCompileTimeValue(manIdx), fieldEntry->getQualType(), fieldNode);
370 4 break;
371 }
372 }
373
374 52 const auto unionType = llvm::cast<llvm::StructType>(symbolType.toLLVMType(sourceFile));
375
376 // With no default field, tag 0 and an all-zero payload is exactly the "unset" state, so the whole constant
377 // collapses to a plain zero value. This also makes such a union eligible for zero-initialized (.bss) storage
378 // when used as a global, rather than requiring an explicit non-zero initializer.
379
2/2
✓ Branch 142 → 143 taken 48 times.
✓ Branch 142 → 145 taken 4 times.
52 if (!defaultFieldValue)
380 48 return llvm::Constant::getNullValue(unionType);
381
382 4 llvm::Constant *tagConstant = builder.getInt32(defaultFieldTag);
383 4 llvm::Constant *alignPadConstant = llvm::Constant::getNullValue(unionType->getElementType(1));
384 4 auto *payloadType = llvm::cast<llvm::ArrayType>(unionType->getElementType(2));
385 4 llvm::Constant *payloadConstant = packConstantAsByteArray(defaultFieldValue, payloadType);
386
387
1/2
✓ Branch 152 → 153 taken 4 times.
✗ Branch 152 → 200 not taken.
4 return llvm::ConstantStruct::get(unionType, {tagConstant, alignPadConstant, payloadConstant});
388 }
389
390 // Interface
391
1/2
✓ Branch 156 → 157 taken 48 times.
✗ Branch 156 → 163 not taken.
48 if (symbolType.is(TY_INTERFACE)) {
392 48 const auto structType = llvm::cast<llvm::StructType>(symbolType.toLLVMType(sourceFile));
393 48 return llvm::ConstantStruct::get(structType, llvm::Constant::getNullValue(builder.getPtrTy()));
394 }
395
396 throw CompilerError(INTERNAL_ERROR, "Cannot determine default value for symbol type"); // GCOV_EXCL_LINE
397 }
398
399 140722 llvm::Constant *IRGenerator::getConst(const CompileTimeValue &compileTimeValue, const QualType &type, const ASTNode *node) const {
400
2/2
✓ Branch 3 → 4 taken 3858 times.
✓ Branch 3 → 9 taken 136864 times.
140722 if (type.is(TY_DOUBLE))
401
2/4
✓ Branch 4 → 5 taken 3858 times.
✗ Branch 4 → 56 not taken.
✓ Branch 5 → 6 taken 3858 times.
✗ Branch 5 → 54 not taken.
3858 return llvm::ConstantFP::get(context, llvm::APFloat(compileTimeValue.doubleValue));
402
403
2/2
✓ Branch 10 → 11 taken 22310 times.
✓ Branch 10 → 13 taken 114554 times.
136864 if (type.is(TY_INT))
404 22310 return builder.getInt32(compileTimeValue.intValue);
405
406
2/2
✓ Branch 14 → 15 taken 2660 times.
✓ Branch 14 → 17 taken 111894 times.
114554 if (type.is(TY_SHORT))
407 2660 return builder.getInt16(compileTimeValue.shortValue);
408
409
2/2
✓ Branch 18 → 19 taken 63199 times.
✓ Branch 18 → 21 taken 48695 times.
111894 if (type.is(TY_LONG))
410 63199 return builder.getInt64(compileTimeValue.longValue);
411
412
3/4
✓ Branch 21 → 22 taken 48695 times.
✗ Branch 21 → 57 not taken.
✓ Branch 22 → 23 taken 13309 times.
✓ Branch 22 → 25 taken 35386 times.
48695 if (type.isOneOf({TY_BYTE, TY_CHAR}))
413 13309 return builder.getInt8(compileTimeValue.charValue);
414
415
2/2
✓ Branch 26 → 27 taken 23534 times.
✓ Branch 26 → 36 taken 11852 times.
35386 if (type.is(TY_STRING)) {
416 23534 const std::string &stringValue = resourceManager.compileTimeStringValues.at(compileTimeValue.stringValueOffset);
417
2/4
✓ Branch 30 → 31 taken 23534 times.
✗ Branch 30 → 60 not taken.
✓ Branch 31 → 32 taken 23534 times.
✗ Branch 31 → 58 not taken.
70602 return createGlobalStringConst(ANON_GLOBAL_STRING_NAME, stringValue, node->codeLoc);
418 }
419
420
2/2
✓ Branch 37 → 38 taken 10738 times.
✓ Branch 37 → 40 taken 1114 times.
11852 if (type.is(TY_BOOL))
421 10738 return builder.getInt1(compileTimeValue.boolValue);
422
423
1/2
✓ Branch 41 → 42 taken 1114 times.
✗ Branch 41 → 45 not taken.
1114 if (type.is(TY_PTR))
424 1114 return llvm::Constant::getNullValue(builder.getPtrTy());
425
426 throw CompilerError(UNHANDLED_BRANCH, "Constant fall-through"); // GCOV_EXCL_LINE
427 }
428
429 274396 llvm::BasicBlock *IRGenerator::createBlock(const std::string &blockName /*=""*/) const {
430
2/4
✓ Branch 2 → 3 taken 274396 times.
✗ Branch 2 → 7 not taken.
✓ Branch 3 → 4 taken 274396 times.
✗ Branch 3 → 7 not taken.
274396 return llvm::BasicBlock::Create(context, blockName);
431 }
432
433 274396 void IRGenerator::switchToBlock(llvm::BasicBlock *block, llvm::Function *parentFct /*=nullptr*/) {
434
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 274396 times.
274396 assert(block->getParent() == nullptr); // Ensure that the block was not added to a function already
435 // If no parent function were passed, use the current function
436
2/2
✓ Branch 5 → 6 taken 187526 times.
✓ Branch 5 → 8 taken 86870 times.
274396 if (!parentFct)
437 187526 parentFct = builder.GetInsertBlock()->getParent();
438 // Append block to current function
439 274396 parentFct->insert(parentFct->end(), block);
440 // Set insert point to the block
441 274396 builder.SetInsertPoint(block);
442 274396 blockAlreadyTerminated = false;
443 274396 }
444
445 5001 void IRGenerator::terminateBlock(const StmtLstNode *stmtLstNode) {
446 5001 generateScopeCleanup(stmtLstNode);
447 5001 blockAlreadyTerminated = true;
448 5001 }
449
450 98048 void IRGenerator::insertJump(llvm::BasicBlock *targetBlock) {
451
2/2
✓ Branch 2 → 3 taken 32615 times.
✓ Branch 2 → 4 taken 65433 times.
98048 if (blockAlreadyTerminated)
452 32615 return;
453 65433 builder.CreateBr(targetBlock);
454 65433 blockAlreadyTerminated = true;
455 }
456
457 80974 void IRGenerator::insertCondJump(llvm::Value *condition, llvm::BasicBlock *trueBlock, llvm::BasicBlock *falseBlock,
458 Likelihood likelihood /*=UNSPECIFIED*/) {
459
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 80974 times.
80974 if (blockAlreadyTerminated)
460 return;
461 80974 llvm::CondBrInst *jumpInst = builder.CreateCondBr(condition, trueBlock, falseBlock);
462 80974 blockAlreadyTerminated = true;
463
464
2/2
✓ Branch 5 → 6 taken 11141 times.
✓ Branch 5 → 7 taken 69833 times.
80974 if (likelihood != Likelihood::UNSPECIFIED)
465 11141 mdGenerator.generateBranchWeightsMetadata(jumpInst, likelihood);
466 }
467
468 86844 void IRGenerator::verifyFunction(const llvm::Function *fct, const CodeLoc &codeLoc) const {
469 // Skip the verifying step if the verifier was disabled manually or debug info is emitted
470
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 86844 times.
86844 if (cliOptions.disableVerifier)
471 return;
472
473 // Verify function
474 86844 std::string output;
475
1/2
✓ Branch 5 → 6 taken 86844 times.
✗ Branch 5 → 20 not taken.
86844 llvm::raw_string_ostream oss(output);
476 if (llvm::verifyFunction(*fct, &oss)) // LCOV_EXCL_LINE
477 throw CompilerError(codeLoc, INVALID_FUNCTION, output); // LCOV_EXCL_LINE
478 86844 }
479
480 5633 void IRGenerator::verifyModule(const CodeLoc &codeLoc) const {
481 // Skip the verifying step if the verifier was disabled manually or debug info is emitted
482
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 5633 times.
5633 if (cliOptions.disableVerifier)
483 return;
484
485 // Verify module
486 5633 std::string output;
487
1/2
✓ Branch 5 → 6 taken 5633 times.
✗ Branch 5 → 20 not taken.
5633 llvm::raw_string_ostream oss(output);
488 if (llvm::verifyModule(*module, &oss)) // LCOV_EXCL_LINE
489 throw CompilerError(codeLoc, INVALID_MODULE, output); // LCOV_EXCL_LINE
490 5633 }
491
492 47933 LLVMExprResult IRGenerator::doAssignment(const ASTNode *lhsNode, const ExprNode *rhsNode, const ASTNode *node) {
493 // Get entry of left side
494
2/4
✓ Branch 2 → 3 taken 47933 times.
✗ Branch 2 → 18 not taken.
✓ Branch 3 → 4 taken 47933 times.
✗ Branch 3 → 16 not taken.
47933 auto exprResult = std::any_cast<LLVMExprResult>(visit(lhsNode));
495 47933 const SymbolTableEntry *entry = exprResult.entry;
496
7/10
✓ Branch 5 → 6 taken 42766 times.
✓ Branch 5 → 10 taken 5167 times.
✓ Branch 6 → 7 taken 42766 times.
✗ Branch 6 → 19 not taken.
✓ Branch 7 → 8 taken 42766 times.
✗ Branch 7 → 19 not taken.
✓ Branch 8 → 9 taken 2742 times.
✓ Branch 8 → 10 taken 40024 times.
✓ Branch 10 → 11 taken 45191 times.
✗ Branch 10 → 19 not taken.
47933 llvm::Value *lhsAddress = entry != nullptr && entry->getQualType().isRef() ? exprResult.refPtr : resolveAddress(exprResult);
497
1/2
✓ Branch 12 → 13 taken 47933 times.
✗ Branch 12 → 19 not taken.
95866 return doAssignment(lhsAddress, entry, rhsNode, node);
498 }
499
500 107981 LLVMExprResult IRGenerator::doAssignment(llvm::Value *lhsAddress, const SymbolTableEntry *lhsEntry, const ExprNode *rhsNode,
501 const ASTNode *node, bool isDecl) {
502 // Get symbol type of right side
503
1/2
✓ Branch 2 → 3 taken 107981 times.
✗ Branch 2 → 13 not taken.
107981 const QualType &rhsSType = rhsNode->getEvaluatedSymbolType(manIdx);
504
2/4
✓ Branch 3 → 4 taken 107981 times.
✗ Branch 3 → 12 not taken.
✓ Branch 4 → 5 taken 107981 times.
✗ Branch 4 → 10 not taken.
107981 auto rhs = std::any_cast<LLVMExprResult>(visit(rhsNode));
505
1/2
✓ Branch 6 → 7 taken 107981 times.
✗ Branch 6 → 13 not taken.
215962 return doAssignment(lhsAddress, lhsEntry, rhs, rhsSType, node, isDecl);
506 }
507
508 109174 LLVMExprResult IRGenerator::doAssignment(llvm::Value *lhsAddress, const SymbolTableEntry *lhsEntry, LLVMExprResult &rhs,
509 const QualType &rhsSType, const ASTNode *node, bool isDecl) {
510 // Deduce some information about the assignment
511
4/4
✓ Branch 2 → 3 taken 104007 times.
✓ Branch 2 → 7 taken 5167 times.
✓ Branch 5 → 6 taken 8837 times.
✓ Branch 5 → 7 taken 95170 times.
109174 const bool isRefAssign = lhsEntry != nullptr && lhsEntry->getQualType().isRef();
512 // A non-temporary struct value assigned by value needs a deep copy. This holds whether the destination is a direct
513 // lvalue or the value behind an already-bound reference (assign-through): the binding cases of a reference assignment
514 // (declaration/initial field ref/return value) all return early above before this is consumed, so the remaining
515 // reference assignments are assign-throughs that must copy into the referent instead of shallow-copying (which would
516 // alias the rhs' owned members and double-free).
517
6/8
✓ Branch 8 → 9 taken 109174 times.
✗ Branch 8 → 264 not taken.
✓ Branch 9 → 10 taken 109174 times.
✗ Branch 9 → 264 not taken.
✓ Branch 10 → 11 taken 19584 times.
✓ Branch 10 → 14 taken 89590 times.
✓ Branch 12 → 13 taken 3295 times.
✓ Branch 12 → 14 taken 16289 times.
109174 const bool needsCopy = rhsSType.removeReferenceWrapper().is(TY_STRUCT) && !rhs.isTemporary();
518
519
2/2
✓ Branch 15 → 16 taken 8837 times.
✓ Branch 15 → 57 taken 100337 times.
109174 if (isRefAssign) {
520
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 8837 times.
8837 assert(lhsEntry != nullptr);
521
2/2
✓ Branch 18 → 19 taken 6095 times.
✓ Branch 18 → 33 taken 2742 times.
8837 if (isDecl) { // Reference gets initially assigned
522 // Store lhs pointer to rhs
523
2/4
✓ Branch 22 → 23 taken 6095 times.
✗ Branch 22 → 265 not taken.
✓ Branch 23 → 24 taken 6095 times.
✗ Branch 23 → 265 not taken.
6095 llvm::Value *refAddress = insertAlloca(builder.getPtrTy());
524 6095 updateAddress(lhsEntry, refAddress);
525
526 // Generate debug info for variable declaration
527 6095 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, refAddress);
528
529 // Get address of right side
530 6095 llvm::Value *rhsAddress = resolveAddress(rhs);
531
1/2
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 6095 times.
6095 assert(rhsAddress != nullptr);
532 6095 insertStore(rhsAddress, refAddress);
533
534 6095 return LLVMExprResult{.value = rhsAddress, .ptr = refAddress, .entry = lhsEntry};
535 }
536
537 // Reference to reference assignment (only for struct fields that are not initialized yet)
538 // These are only allowed inside a ctor body. In other cases, the value of the reference gets assigned, not the ref itself.
539
6/8
✓ Branch 33 → 34 taken 2011 times.
✓ Branch 33 → 40 taken 731 times.
✓ Branch 35 → 36 taken 2003 times.
✓ Branch 35 → 40 taken 8 times.
✓ Branch 36 → 37 taken 2003 times.
✗ Branch 36 → 40 not taken.
✓ Branch 38 → 39 taken 2003 times.
✗ Branch 38 → 40 not taken.
2742 const bool isInitialFieldRefAssign = isInCtorBody && rhsSType.isRef() && rhs.entry && lhsEntry->isField();
540 // Assigning the result variable
541 2742 const bool isReturnValAssign = lhsEntry->name == RETURN_VARIABLE_NAME;
542
4/4
✓ Branch 42 → 43 taken 739 times.
✓ Branch 42 → 44 taken 2003 times.
✓ Branch 43 → 44 taken 24 times.
✓ Branch 43 → 49 taken 715 times.
2742 if (isInitialFieldRefAssign || isReturnValAssign) {
543 // Get address of right side
544 2027 llvm::Value *referencedAddress = resolveAddress(rhs);
545
1/2
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 47 taken 2027 times.
2027 assert(referencedAddress != nullptr);
546
547 // Store the rhs* to the lhs**
548 2027 insertStore(referencedAddress, lhsAddress);
549
550 2027 return LLVMExprResult{.value = referencedAddress, .ptr = lhsAddress, .entry = lhsEntry};
551 }
552
553 // Load referenced address
554
2/4
✓ Branch 52 → 53 taken 715 times.
✗ Branch 52 → 271 not taken.
✓ Branch 53 → 54 taken 715 times.
✗ Branch 53 → 271 not taken.
715 lhsAddress = insertLoad(builder.getPtrTy(), lhsAddress);
555 }
556
557
8/8
✓ Branch 57 → 58 taken 55146 times.
✓ Branch 57 → 63 taken 45906 times.
✓ Branch 59 → 60 taken 6473 times.
✓ Branch 59 → 63 taken 48673 times.
✓ Branch 61 → 62 taken 6451 times.
✓ Branch 61 → 63 taken 22 times.
✓ Branch 64 → 65 taken 6451 times.
✓ Branch 64 → 86 taken 94601 times.
101052 if (isDecl && rhsSType.is(TY_STRUCT) && rhs.isTemporary()) {
558
1/2
✗ Branch 65 → 66 not taken.
✓ Branch 65 → 67 taken 6451 times.
6451 assert(lhsEntry != nullptr);
559
1/2
✓ Branch 67 → 68 taken 6451 times.
✗ Branch 67 → 283 not taken.
6451 llvm::Value *rhsAddress = resolveAddress(rhs);
560 // Only adopt the temporary's storage directly (temp stealing) unless it is a phi merging two independently
561 // materialized pointers (e.g. a ternary whose branches each own their own storage). Lifetime markers require
562 // their operand to be a real alloca, so stealing such a phi here would emit an invalid llvm.lifetime.end
563 // under --sanitizer=address. Any other pointer (a plain alloca, or one derived from it via GEP/load, as in
564 // the foreach-loop item extraction below) denotes a single, stable piece of storage and is safe to adopt.
565 // The temporary's ownership fully transfers to lhsEntry either way (its underlying storage is never
566 // separately destructed), so falling back to a shallow copy into a dedicated alloca is exactly as correct,
567 // just gives up the pointer-adoption optimization for the phi case.
568
3/4
✓ Branch 68 → 69 taken 6451 times.
✗ Branch 68 → 283 not taken.
✓ Branch 69 → 70 taken 6439 times.
✓ Branch 69 → 73 taken 12 times.
6451 if (!llvm::isa<llvm::PHINode>(rhsAddress)) {
569 // Directly set the address to the lhs entry (temp stealing)
570
1/2
✓ Branch 70 → 71 taken 6439 times.
✗ Branch 70 → 283 not taken.
6439 updateAddress(lhsEntry, rhsAddress);
571 6439 rhs.entry = lhsEntry;
572 // Generate debug info for variable declaration
573
1/2
✓ Branch 71 → 72 taken 6439 times.
✗ Branch 71 → 283 not taken.
6439 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, rhsAddress);
574 6439 return rhs;
575 }
576 // Size the copy off lhsEntry's own type, not rhsSType: some callers (e.g. the foreach-loop item
577 // extraction below) pass a struct rhsSType only to steer this branch, while rhs itself resolves (through
578 // refPtr indirection) to a differently-typed value; lhsEntry's type is always the authoritative one here.
579
1/2
✓ Branch 73 → 74 taken 12 times.
✗ Branch 73 → 283 not taken.
12 const QualType &lhsQualType = lhsEntry->getQualType();
580
1/2
✓ Branch 77 → 78 taken 12 times.
✗ Branch 77 → 277 not taken.
12 llvm::Value *lhsAddr = insertAlloca(lhsQualType);
581
1/2
✓ Branch 80 → 81 taken 12 times.
✗ Branch 80 → 283 not taken.
12 updateAddress(lhsEntry, lhsAddr);
582
1/2
✓ Branch 81 → 82 taken 12 times.
✗ Branch 81 → 283 not taken.
12 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, lhsAddr);
583
2/4
✓ Branch 82 → 83 taken 12 times.
✗ Branch 82 → 283 not taken.
✓ Branch 83 → 84 taken 12 times.
✗ Branch 83 → 283 not taken.
12 generateShallowCopy(rhsAddress, lhsQualType.toLLVMType(sourceFile), lhsAddr, lhsEntry->isVolatile);
584 12 rhs.ptr = lhsAddr;
585 12 rhs.entry = lhsEntry;
586 12 return rhs;
587 }
588
589 // Allocate new memory if the lhs address does not exist
590
2/2
✓ Branch 86 → 87 taken 48250 times.
✓ Branch 86 → 98 taken 46351 times.
94601 if (!lhsAddress) {
591
1/2
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 48250 times.
48250 assert(lhsEntry != nullptr);
592
2/4
✓ Branch 92 → 93 taken 48250 times.
✗ Branch 92 → 284 not taken.
✓ Branch 93 → 94 taken 48250 times.
✗ Branch 93 → 284 not taken.
48250 lhsAddress = insertAlloca(lhsEntry->getQualType());
593 48250 updateAddress(lhsEntry, lhsAddress);
594 // Generate debug info for variable declaration
595 48250 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, lhsAddress);
596 }
597
598 // 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
599
10/10
✓ Branch 98 → 99 taken 89434 times.
✓ Branch 98 → 107 taken 5167 times.
✓ Branch 101 → 102 taken 20218 times.
✓ Branch 101 → 107 taken 69216 times.
✓ Branch 103 → 104 taken 16 times.
✓ Branch 103 → 107 taken 20202 times.
✓ Branch 105 → 106 taken 6 times.
✓ Branch 105 → 107 taken 10 times.
✓ Branch 108 → 109 taken 6 times.
✓ Branch 108 → 124 taken 94595 times.
94601 if (lhsEntry && lhsEntry->getQualType().isPtr() && rhsSType.isArray() && rhsSType.getArraySize() != ARRAY_SIZE_UNKNOWN) {
600 // Get address of right side
601
1/2
✓ Branch 109 → 110 taken 6 times.
✗ Branch 109 → 297 not taken.
6 llvm::Value *rhsAddress = resolveAddress(rhs);
602
1/2
✗ Branch 110 → 111 not taken.
✓ Branch 110 → 112 taken 6 times.
6 assert(rhsAddress != nullptr);
603
1/2
✓ Branch 112 → 113 taken 6 times.
✗ Branch 112 → 297 not taken.
6 llvm::Type *elementTy = rhsSType.toLLVMType(sourceFile);
604
2/4
✓ Branch 113 → 114 taken 6 times.
✗ Branch 113 → 297 not taken.
✓ Branch 114 → 115 taken 6 times.
✗ Branch 114 → 297 not taken.
6 llvm::Value *indices[2] = {builder.getInt64(0), builder.getInt32(0)};
605
1/2
✓ Branch 119 → 120 taken 6 times.
✗ Branch 119 → 290 not taken.
6 llvm::Value *firstItemAddress = insertInBoundsGEP(elementTy, rhsAddress, indices);
606
1/2
✓ Branch 122 → 123 taken 6 times.
✗ Branch 122 → 297 not taken.
6 insertStore(firstItemAddress, lhsAddress);
607 6 return LLVMExprResult{.value = rhsAddress, .ptr = lhsAddress, .entry = lhsEntry};
608 }
609
610 // Handle operator overloads
611
6/6
✓ Branch 124 → 125 taken 45906 times.
✓ Branch 124 → 128 taken 48689 times.
✓ Branch 126 → 127 taken 1176 times.
✓ Branch 126 → 128 taken 44730 times.
✓ Branch 129 → 130 taken 1176 times.
✓ Branch 129 → 146 taken 93419 times.
94595 if (!isDecl && conversionManager.callsOverloadedOpFct(node, DEFAULT_OP_IDX)) {
612 1176 ResolverFct lhsV = [&] { return static_cast<llvm::Value *>(nullptr); };
613
1/2
✓ Branch 131 → 132 taken 1176 times.
✗ Branch 131 → 298 not taken.
1176 ResolverFct rhsV = [&] { return resolveValue(rhsSType, rhs); };
614 2352 ResolverFct lhsP = [&] { return lhsAddress; };
615 2352 ResolverFct rhsP = [&] { return resolveAddress(rhs); };
616 1176 return conversionManager.callOperatorOverloadFct<2>(node, {lhsV, lhsP, rhsV, rhsP}, DEFAULT_OP_IDX);
617 1176 }
618
619 // Check if we need to copy the rhs to the lhs. This happens for structs
620
2/2
✓ Branch 146 → 147 taken 1223 times.
✓ Branch 146 → 232 taken 92196 times.
93419 if (needsCopy) {
621 // Get address of right side
622
1/2
✓ Branch 147 → 148 taken 1223 times.
✗ Branch 147 → 374 not taken.
1223 llvm::Value *rhsAddress = resolveAddress(rhs);
623
1/2
✗ Branch 148 → 149 not taken.
✓ Branch 148 → 150 taken 1223 times.
1223 assert(rhsAddress != nullptr);
624
625 // If the lhs already holds an initialized, non-trivially-destructible struct, its old value must be
626 // destructed before the copy overwrites it, otherwise its owning members (heap pointers, strings, ...)
627 // would leak. The typechecker only sets a dtor in exactly those cases. To stay correct for a self-
628 // assignment like 'a = a', the destruct + copy are skipped entirely when both sides share the address
629 // (the assignment is a no-op in that case, and destructing first would corrupt the value to copy from).
630
1/2
✓ Branch 150 → 151 taken 1223 times.
✗ Branch 150 → 152 not taken.
1223 const auto *assignNode = dynamic_cast<const AssignExprNode *>(node);
631
3/4
✓ Branch 153 → 154 taken 1201 times.
✓ Branch 153 → 156 taken 22 times.
✓ Branch 154 → 155 taken 1201 times.
✗ Branch 154 → 374 not taken.
1223 const Function *lhsDtor = assignNode ? assignNode->lhsDtorFct.at(manIdx) : nullptr;
632 1223 llvm::BasicBlock *bCopyEnd = nullptr;
633
2/2
✓ Branch 157 → 158 taken 52 times.
✓ Branch 157 → 178 taken 1171 times.
1223 if (lhsDtor != nullptr) {
634
2/4
✓ Branch 160 → 161 taken 52 times.
✗ Branch 160 → 318 not taken.
✓ Branch 161 → 162 taken 52 times.
✗ Branch 161 → 316 not taken.
104 llvm::BasicBlock *bCopy = createBlock("assign.copy");
635
2/4
✓ Branch 166 → 167 taken 52 times.
✗ Branch 166 → 324 not taken.
✓ Branch 167 → 168 taken 52 times.
✗ Branch 167 → 322 not taken.
52 bCopyEnd = createBlock("assign.copy.end");
636
3/6
✓ Branch 170 → 171 taken 52 times.
✗ Branch 170 → 328 not taken.
✓ Branch 171 → 172 taken 52 times.
✗ Branch 171 → 328 not taken.
✓ Branch 172 → 173 taken 52 times.
✗ Branch 172 → 328 not taken.
52 insertCondJump(builder.CreateICmpEQ(lhsAddress, rhsAddress), bCopyEnd, bCopy);
637
1/2
✓ Branch 173 → 174 taken 52 times.
✗ Branch 173 → 374 not taken.
52 switchToBlock(bCopy);
638
1/2
✓ Branch 175 → 176 taken 52 times.
✗ Branch 175 → 329 not taken.
52 generateCtorOrDtorCall(lhsAddress, lhsDtor, {});
639 }
640
641
2/4
✓ Branch 178 → 179 taken 1223 times.
✗ Branch 178 → 332 not taken.
✓ Branch 179 → 180 taken 1223 times.
✗ Branch 179 → 332 not taken.
1223 const QualType rhsSTypeNonRef = rhsSType.removeReferenceWrapper().toNonConst();
642
3/4
✓ Branch 180 → 181 taken 1223 times.
✗ Branch 180 → 374 not taken.
✓ Branch 181 → 182 taken 988 times.
✓ Branch 181 → 198 taken 235 times.
1223 if (rhsSTypeNonRef.isTriviallyCopyable(node)) {
643 // Create shallow copy
644
1/2
✓ Branch 182 → 183 taken 988 times.
✗ Branch 182 → 340 not taken.
988 llvm::Type *rhsType = rhsSTypeNonRef.toLLVMType(sourceFile);
645
3/10
✓ Branch 183 → 184 taken 988 times.
✗ Branch 183 → 185 not taken.
✓ Branch 184 → 188 taken 988 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 988 times.
✗ Branch 333 → 334 not taken.
✗ Branch 333 → 336 not taken.
988 const std::string copyName = lhsEntry ? lhsEntry->name : "";
646
3/6
✓ Branch 191 → 192 taken 988 times.
✗ Branch 191 → 194 not taken.
✗ Branch 192 → 193 not taken.
✓ Branch 192 → 194 taken 988 times.
✓ Branch 195 → 196 taken 988 times.
✗ Branch 195 → 338 not taken.
988 generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry && lhsEntry->isVolatile);
647 988 } else {
648 // Check if we have a copy ctor
649
1/2
✓ Branch 198 → 199 taken 235 times.
✗ Branch 198 → 373 not taken.
235 Scope *structScope = rhsSTypeNonRef.getBodyScope();
650
2/4
✓ Branch 199 → 200 taken 235 times.
✗ Branch 199 → 345 not taken.
✓ Branch 204 → 205 taken 235 times.
✗ Branch 204 → 341 not taken.
705 const ArgList args = {{rhsSTypeNonRef.toConstRef(node), rhs.isTemporary()}};
651
2/4
✓ Branch 208 → 209 taken 235 times.
✗ Branch 208 → 349 not taken.
✓ Branch 209 → 210 taken 235 times.
✗ Branch 209 → 347 not taken.
235 const Function *copyCtor = FunctionManager::lookup(structScope, CTOR_FUNCTION_NAME, rhsSTypeNonRef, args, true);
652
1/2
✓ Branch 212 → 213 taken 235 times.
✗ Branch 212 → 221 not taken.
235 if (copyCtor != nullptr) {
653 // Call copy ctor
654
2/4
✓ Branch 215 → 216 taken 235 times.
✗ Branch 215 → 355 not taken.
✓ Branch 216 → 217 taken 235 times.
✗ Branch 216 → 353 not taken.
470 generateCtorOrDtorCall(lhsAddress, copyCtor, {rhsAddress});
655 } else {
656 const std::string structName = rhsSTypeNonRef.getName();
657 const std::string msg = "Cannot copy struct '" + structName + "', as it is not trivially copyable and has no copy ctor";
658 throw SemanticError(node, COPY_CTOR_REQUIRED, msg);
659 }
660 235 }
661
662 // Close the self-assignment guard
663
2/2
✓ Branch 228 → 229 taken 52 times.
✓ Branch 228 → 231 taken 1171 times.
1223 if (bCopyEnd != nullptr) {
664
1/2
✓ Branch 229 → 230 taken 52 times.
✗ Branch 229 → 374 not taken.
52 insertJump(bCopyEnd);
665
1/2
✓ Branch 230 → 231 taken 52 times.
✗ Branch 230 → 374 not taken.
52 switchToBlock(bCopyEnd);
666 }
667 1223 return LLVMExprResult{.ptr = lhsAddress, .entry = lhsEntry};
668 }
669
670 // Optimization: If we have the address of both sides, we can do a memcpy instead of loading and storing the value
671 92196 llvm::Value *rhsValue = nullptr;
672
8/8
✓ Branch 233 → 234 taken 4887 times.
✓ Branch 233 → 237 taken 87309 times.
✓ Branch 234 → 235 taken 4731 times.
✓ Branch 234 → 237 taken 156 times.
✓ Branch 235 → 236 taken 4717 times.
✓ Branch 235 → 237 taken 14 times.
✓ Branch 238 → 239 taken 4717 times.
✓ Branch 238 → 260 taken 87479 times.
92196 if (rhsSType.is(TY_STRUCT) && rhs.value == nullptr && rhs.constant == nullptr) {
673 // Create shallow copy
674
2/4
✓ Branch 239 → 240 taken 4717 times.
✗ Branch 239 → 375 not taken.
✓ Branch 240 → 241 taken 4717 times.
✗ Branch 240 → 375 not taken.
4717 const QualType rhsSTypeNonRef = rhsSType.removeReferenceWrapper().toNonConst();
675
1/2
✓ Branch 241 → 242 taken 4717 times.
✗ Branch 241 → 383 not taken.
4717 llvm::Type *rhsType = rhsSTypeNonRef.toLLVMType(sourceFile);
676
1/2
✓ Branch 242 → 243 taken 4717 times.
✗ Branch 242 → 383 not taken.
4717 llvm::Value *rhsAddress = resolveAddress(rhs);
677
1/2
✗ Branch 243 → 244 not taken.
✓ Branch 243 → 245 taken 4717 times.
4717 assert(rhsAddress != nullptr);
678
6/10
✓ Branch 245 → 246 taken 3817 times.
✓ Branch 245 → 247 taken 900 times.
✓ Branch 246 → 250 taken 3817 times.
✗ Branch 246 → 376 not taken.
✓ Branch 249 → 250 taken 900 times.
✗ Branch 249 → 376 not taken.
✓ Branch 250 → 251 taken 900 times.
✓ Branch 250 → 253 taken 3817 times.
✗ Branch 376 → 377 not taken.
✗ Branch 376 → 379 not taken.
5617 const std::string copyName = lhsEntry ? lhsEntry->name : "";
679
4/6
✓ Branch 253 → 254 taken 3817 times.
✓ Branch 253 → 256 taken 900 times.
✗ Branch 254 → 255 not taken.
✓ Branch 254 → 256 taken 3817 times.
✓ Branch 257 → 258 taken 4717 times.
✗ Branch 257 → 381 not taken.
4717 generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry && lhsEntry->isVolatile);
680 4717 } else {
681 // We can load the value from the right side and store it to the left side
682 // Retrieve value of the right side
683 87479 rhsValue = resolveValue(rhsSType, rhs);
684 // Store the value to the address
685 87479 insertStore(rhsValue, lhsAddress, rhsSType);
686 }
687
688 92196 return LLVMExprResult{.value = rhsValue, .ptr = lhsAddress, .entry = lhsEntry};
689
5/14
✓ Branch 134 → 135 taken 1176 times.
✗ Branch 134 → 301 not taken.
✓ Branch 135 → 136 taken 1176 times.
✗ Branch 135 → 301 not taken.
✓ Branch 136 → 137 taken 1176 times.
✗ Branch 136 → 301 not taken.
✓ Branch 137 → 138 taken 1176 times.
✗ Branch 137 → 301 not taken.
✓ Branch 138 → 139 taken 1176 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.
1176 }
690
691 6319 void IRGenerator::generateShallowCopy(llvm::Value *oldAddress, llvm::Type *varType, llvm::Value *targetAddress,
692 bool isVolatile) const {
693 // Retrieve size to copy
694
1/2
✓ Branch 3 → 4 taken 6319 times.
✗ Branch 3 → 19 not taken.
6319 const llvm::TypeSize typeSize = module->getDataLayout().getTypeAllocSize(varType);
695
696 // Create values for memcpy intrinsic
697
2/4
✓ Branch 4 → 5 taken 6319 times.
✗ Branch 4 → 19 not taken.
✓ Branch 5 → 6 taken 6319 times.
✗ Branch 5 → 19 not taken.
6319 llvm::Value *structSize = builder.getInt64(typeSize);
698
1/2
✓ Branch 6 → 7 taken 6319 times.
✗ Branch 6 → 19 not taken.
6319 llvm::Value *copyVolatile = builder.getInt1(isVolatile);
699
700 // Call memcpy intrinsic to execute the shallow copy
701
1/2
✓ Branch 7 → 8 taken 6319 times.
✗ Branch 7 → 19 not taken.
6319 llvm::Function *memcpyFct = stdFunctionManager.getMemcpyIntrinsic();
702
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 6319 times.
6319 assert(targetAddress != nullptr);
703
3/6
✓ Branch 10 → 11 taken 6319 times.
✗ Branch 10 → 18 not taken.
✓ Branch 12 → 13 taken 6319 times.
✗ Branch 12 → 15 not taken.
✓ Branch 13 → 14 taken 6319 times.
✗ Branch 13 → 15 not taken.
6319 builder.CreateCall(memcpyFct, {targetAddress, oldAddress, structSize, copyVolatile});
704 6319 }
705
706 221455 void IRGenerator::autoDeReferencePtr(llvm::Value *&ptr, QualType &symbolType) {
707
6/6
✓ Branch 12 → 13 taken 231962 times.
✓ Branch 12 → 15 taken 154027 times.
✓ Branch 14 → 15 taken 10507 times.
✓ Branch 14 → 16 taken 221455 times.
✓ Branch 17 → 3 taken 164534 times.
✓ Branch 17 → 18 taken 221455 times.
385989 while (symbolType.isPtr() || symbolType.isRef()) {
708
1/2
✓ Branch 6 → 7 taken 164534 times.
✗ Branch 6 → 19 not taken.
164534 ptr = insertLoad(symbolType, ptr);
709
1/2
✓ Branch 9 → 10 taken 164534 times.
✗ Branch 9 → 25 not taken.
164534 symbolType = symbolType.getContained();
710 }
711 221455 }
712
713 630 llvm::GlobalVariable *IRGenerator::createGlobalConst(const std::string &baseName, llvm::Constant *constant) const {
714 // Get unused name
715
1/2
✓ Branch 2 → 3 taken 630 times.
✗ Branch 2 → 19 not taken.
630 const std::string globalName = getUnusedGlobalName(baseName);
716 // Create global
717
1/2
✓ Branch 5 → 6 taken 630 times.
✗ Branch 5 → 15 not taken.
630 module->getOrInsertGlobal(globalName, constant->getType());
718
1/2
✓ Branch 7 → 8 taken 630 times.
✗ Branch 7 → 16 not taken.
630 llvm::GlobalVariable *global = module->getNamedGlobal(globalName);
719 // Set initializer to the given constant
720
1/2
✓ Branch 8 → 9 taken 630 times.
✗ Branch 8 → 17 not taken.
630 global->setInitializer(constant);
721 630 global->setConstant(true);
722
1/2
✓ Branch 10 → 11 taken 630 times.
✗ Branch 10 → 17 not taken.
630 global->setLinkage(llvm::GlobalValue::PrivateLinkage);
723 630 global->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
724 630 return global;
725 630 }
726
727 26263 llvm::GlobalVariable *IRGenerator::createGlobalStringConst(const std::string &baseName, const std::string &value) const {
728 // Get unused name
729
1/2
✓ Branch 2 → 3 taken 26263 times.
✗ Branch 2 → 21 not taken.
26263 const std::string globalName = getUnusedGlobalName(baseName);
730 // Create global
731
2/4
✓ Branch 3 → 4 taken 26263 times.
✗ Branch 3 → 16 not taken.
✓ Branch 5 → 6 taken 26263 times.
✗ Branch 5 → 15 not taken.
26263 builder.CreateGlobalString(value, globalName, 0, module);
732
1/2
✓ Branch 7 → 8 taken 26263 times.
✗ Branch 7 → 17 not taken.
26263 llvm::GlobalVariable *global = module->getNamedGlobal(globalName);
733 // If the output should be comparable, fix alignment to 4 bytes
734
1/2
✓ Branch 8 → 9 taken 26263 times.
✗ Branch 8 → 12 not taken.
26263 if (cliOptions.comparableOutput)
735
2/4
✓ Branch 9 → 10 taken 26263 times.
✗ Branch 9 → 18 not taken.
✓ Branch 10 → 11 taken 26263 times.
✗ Branch 10 → 18 not taken.
26263 global->setAlignment(llvm::Align(4));
736 26263 return global;
737 26263 }
738
739 26263 llvm::GlobalVariable *IRGenerator::createGlobalStringConst(const std::string &baseName, const std::string &value,
740 const CodeLoc &codeLoc) const {
741 26263 llvm::GlobalVariable *global = createGlobalStringConst(baseName, value);
742 // Create debug info
743
2/2
✓ Branch 3 → 4 taken 13196 times.
✓ Branch 3 → 10 taken 13067 times.
26263 if (cliOptions.instrumentation.generateDebugInfo)
744
3/6
✓ Branch 5 → 6 taken 13196 times.
✗ Branch 5 → 14 not taken.
✓ Branch 6 → 7 taken 13196 times.
✗ Branch 6 → 14 not taken.
✓ Branch 7 → 8 taken 13196 times.
✗ Branch 7 → 12 not taken.
13196 diGenerator.generateGlobalStringDebugInfo(global, global->getName().str(), value.length(), codeLoc);
745 26263 return global;
746 }
747
748 42953 std::string IRGenerator::getUnusedGlobalName(const std::string &baseName) const {
749 // Find an unused global name
750 42953 std::string globalName;
751 42953 unsigned int suffixNumber = 0;
752 do {
753
1/2
✓ Branch 5 → 6 taken 1652490 times.
✗ Branch 5 → 15 not taken.
1652490 globalName = baseName + std::to_string(suffixNumber);
754 1652490 suffixNumber++;
755
3/4
✓ Branch 10 → 11 taken 1652490 times.
✗ Branch 10 → 19 not taken.
✓ Branch 11 → 12 taken 1609537 times.
✓ Branch 11 → 13 taken 42953 times.
1652490 } while (module->getNamedGlobal(globalName) != nullptr);
756 42953 return globalName;
757 }
758
759 158546 void IRGenerator::materializeConstant(LLVMExprResult &exprResult) {
760 // Skip results, that do not contain a constant or already have a value
761
3/4
✓ Branch 2 → 3 taken 152826 times.
✓ Branch 2 → 4 taken 5720 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 152826 times.
158546 if (exprResult.value != nullptr || exprResult.constant == nullptr)
762 5720 return;
763
764 // Default case: the value to the constant
765 152826 exprResult.value = exprResult.constant;
766 }
767
768 103353 bool IRGenerator::isSymbolDSOLocal(bool isPublic) const {
769 // If we are compiling a shared library and export the global symbol, we need to drop dso_local
770 // because it may be interposed by other shared objects by the dynamic linker.
771
3/4
✓ Branch 2 → 3 taken 90957 times.
✓ Branch 2 → 4 taken 12396 times.
✓ Branch 3 → 4 taken 90957 times.
✗ Branch 3 → 5 not taken.
103353 return !(isPublic && cliOptions.outputContainer == OutputContainer::SHARED_LIBRARY);
772 }
773
774 91665 llvm::GlobalValue::LinkageTypes IRGenerator::getSymbolLinkageType(bool isPublic) const {
775
2/2
✓ Branch 2 → 3 taken 79467 times.
✓ Branch 2 → 4 taken 12198 times.
91665 return isPublic ? llvm::GlobalValue::ExternalLinkage : llvm::GlobalValue::InternalLinkage;
776 }
777
778 13566 llvm::GlobalValue::LinkageTypes IRGenerator::getVTableLinkageType(bool isPublic) const {
779 // VTables, type infos and type info names are ODR entities that may legitimately be emitted in more than one
780 // translation unit (e.g. an interface that is defined in one file and used from several importing files). Giving
781 // them weak ODR linkage lets the linker coalesce the duplicates. On ELF this pairs with the comdat group below; on
782 // MachO, which has no comdat support, the weak/coalesced linkage is what prevents a duplicate-symbol error.
783
2/2
✓ Branch 2 → 3 taken 13368 times.
✓ Branch 2 → 4 taken 198 times.
13566 return isPublic ? llvm::GlobalValue::WeakODRLinkage : llvm::GlobalValue::PrivateLinkage;
784 }
785
786 13566 void IRGenerator::attachComdatToSymbol(llvm::GlobalVariable *global, const std::string &comdatName, bool isPublic) const {
787 // MachO does not support comdat annotations
788
6/6
✓ Branch 2 → 3 taken 13368 times.
✓ Branch 2 → 6 taken 198 times.
✓ Branch 4 → 5 taken 13338 times.
✓ Branch 4 → 6 taken 30 times.
✓ Branch 7 → 8 taken 13338 times.
✓ Branch 7 → 12 taken 228 times.
13566 if (isPublic && cliOptions.targetTriple.getObjectFormat() != llvm::Triple::MachO)
789
2/4
✓ Branch 9 → 10 taken 13338 times.
✗ Branch 9 → 13 not taken.
✓ Branch 10 → 11 taken 13338 times.
✗ Branch 10 → 13 not taken.
13338 global->setComdat(module->getOrInsertComdat(comdatName));
790 13566 }
791
792 /**
793 * Attach the function attributes that all functions we emit have in common.
794 *
795 * Spice does not know exceptions and we never emit landing pads, so none of our functions can unwind. We still request
796 * an unwind table, so that debuggers and profilers are able to produce correct stack traces.
797 *
798 * Frame pointers are off by default and are requested per function by '--keep-frame-pointers'. This attribute is the
799 * only thing the backend reads. The 'frame-pointer' module flag that IRGenerator's constructor sets from the same
800 * option covers a different set of functions - Function::createWithDefaultAttr() stamps it onto the functions LLVM
801 * itself synthesizes, such as the sanitizer module ctors - so both have to be set, and the module flag alone changes
802 * nothing about the functions we emit: x86-64 Linux then omits the frame pointer at every optimization level, and
803 * AArch64 spills the frame record but never links the chain up.
804 *
805 * Nothing is emitted in the default case, rather than an explicit "none", which is exactly what LLVM does for its own
806 * synthesized functions. It also leaves the decision to the backend, so targets whose ABI mandates a frame pointer
807 * (e.g. AArch64 on Darwin) keep theirs.
808 *
809 * The size levels are not communicated to LLVM by the pass pipeline alone - since Os and Oz both select the O2
810 * pipeline, 'optsize' and 'minsize' on the individual function are what actually distinguishes them. Without 'minsize',
811 * Oz is indistinguishable from Os.
812 *
813 * @param fct Function to attach the attributes to
814 * @param isAlwaysInline Whether the function was declared as inline
815 */
816 86870 void IRGenerator::addCommonFctAttrs(llvm::Function *fct, bool isAlwaysInline) const {
817 86870 fct->addFnAttr(llvm::Attribute::NoUnwind);
818 86870 fct->addFnAttr(llvm::Attribute::getWithUWTableKind(context, llvm::UWTableKind::Default));
819
2/2
✓ Branch 5 → 6 taken 4 times.
✓ Branch 5 → 10 taken 86866 times.
86870 if (cliOptions.keepFramePointers)
820
3/6
✓ Branch 6 → 7 taken 4 times.
✗ Branch 6 → 21 not taken.
✓ Branch 7 → 8 taken 4 times.
✗ Branch 7 → 20 not taken.
✓ Branch 8 → 9 taken 4 times.
✗ Branch 8 → 20 not taken.
4 fct->addFnAttr("frame-pointer", "all");
821
822 // Explicitly inlined functions must not be marked as 'optnone', because that is incompatible with 'alwaysinline'.
823 // This matches the behavior of other frontends: an inline request is honored, even at O0.
824
2/2
✓ Branch 10 → 11 taken 19494 times.
✓ Branch 10 → 12 taken 67376 times.
86870 if (isAlwaysInline) {
825 19494 fct->addFnAttr(llvm::Attribute::AlwaysInline);
826
2/2
✓ Branch 12 → 13 taken 67276 times.
✓ Branch 12 → 15 taken 100 times.
67376 } else if (cliOptions.optLevel == OptLevel::O0) {
827 67276 fct->addFnAttr(llvm::Attribute::OptimizeNone);
828 67276 fct->addFnAttr(llvm::Attribute::NoInline); // 'optnone' requires 'noinline'
829 }
830
831
2/2
✓ Branch 15 → 16 taken 4 times.
✓ Branch 15 → 19 taken 86866 times.
86870 if (cliOptions.optLevel >= OptLevel::Os) {
832 4 fct->addFnAttr(llvm::Attribute::OptimizeForSize);
833
2/2
✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 19 taken 2 times.
4 if (cliOptions.optLevel == OptLevel::Oz)
834 2 fct->addFnAttr(llvm::Attribute::MinSize);
835 }
836 86870 }
837
838 563544 llvm::Value *IRGenerator::getAddress(const SymbolTableEntry *entry) {
839
1/2
✓ Branch 2 → 3 taken 563544 times.
✗ Branch 2 → 18 not taken.
563544 const auto it = addressMap.find(entry);
840
5/6
✓ Branch 5 → 6 taken 562378 times.
✓ Branch 5 → 9 taken 1166 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 562378 times.
✓ Branch 11 → 12 taken 1166 times.
✓ Branch 11 → 13 taken 562378 times.
563544 if (it == addressMap.end() || it->second.empty())
841 1166 return nullptr;
842 562378 return it->second.top();
843 }
844
845 370998 void IRGenerator::updateAddress(const SymbolTableEntry *entry, llvm::Value *address) {
846
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 370998 times.
370998 assert(address != nullptr);
847
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 370998 times.
370998 assert(address->getType()->isPointerTy());
848 370998 auto &stack = addressMap[entry];
849
2/2
✓ Branch 10 → 11 taken 290510 times.
✓ Branch 10 → 12 taken 80488 times.
370998 if (stack.empty())
850 290510 stack.push(address);
851 else
852 80488 stack.top() = address;
853 370998 }
854
855 126 void IRGenerator::pushAddress(const SymbolTableEntry *entry, llvm::Value *address) {
856
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 126 times.
126 assert(address != nullptr);
857 126 addressMap[entry].push(address);
858 126 }
859
860 126 void IRGenerator::popAddress(const SymbolTableEntry *entry) {
861
1/2
✓ Branch 2 → 3 taken 126 times.
✗ Branch 2 → 14 not taken.
126 auto it = addressMap.find(entry);
862
2/4
✓ Branch 5 → 6 taken 126 times.
✗ Branch 5 → 10 not taken.
✓ Branch 8 → 9 taken 126 times.
✗ Branch 8 → 10 not taken.
126 assert(it != addressMap.end() && !it->second.empty());
863 126 it->second.pop();
864 126 }
865
866 14634 llvm::Function *IRGenerator::getLLVMFunction(const Function *spiceFunc) {
867
1/2
✓ Branch 2 → 3 taken 14634 times.
✗ Branch 2 → 12 not taken.
14634 const auto it = llvmFunctions.find(spiceFunc);
868
2/2
✓ Branch 5 → 6 taken 5898 times.
✓ Branch 5 → 8 taken 8736 times.
29268 return it != llvmFunctions.end() ? it->second : nullptr;
869 }
870
871 83848 void IRGenerator::setLLVMFunction(const Function *spiceFunc, llvm::Function *llvmFunction) {
872
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 83848 times.
83848 assert(llvmFunction != nullptr);
873 83848 llvmFunctions[spiceFunc] = llvmFunction;
874 83848 }
875
876 10989 std::string IRGenerator::getIRString(llvm::Module *llvmModule, const CliOptions &cliOptions) {
877
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 10989 times.
10989 assert(llvmModule != nullptr); // Make sure the module hasn't been moved away
878
3/4
✓ Branch 4 → 5 taken 10989 times.
✗ Branch 4 → 7 not taken.
✓ Branch 5 → 6 taken 10777 times.
✓ Branch 5 → 7 taken 212 times.
10989 const bool eliminateTarget = cliOptions.comparableOutput && cliOptions.isNativeTarget;
879
880 // Backup target triple and data layout
881
1/2
✓ Branch 9 → 10 taken 10989 times.
✗ Branch 9 → 45 not taken.
10989 const llvm::Triple targetTriple = llvmModule->getTargetTriple();
882
1/2
✓ Branch 11 → 12 taken 10989 times.
✗ Branch 11 → 43 not taken.
10989 const std::string targetDataLayout = llvmModule->getDataLayoutStr();
883 // Remove target triple and data layout
884
2/2
✓ Branch 12 → 13 taken 10777 times.
✓ Branch 12 → 19 taken 212 times.
10989 if (eliminateTarget) {
885 10777 llvmModule->setTargetTriple(llvm::Triple());
886
2/4
✓ Branch 16 → 17 taken 10777 times.
✗ Branch 16 → 34 not taken.
✓ Branch 17 → 18 taken 10777 times.
✗ Branch 17 → 34 not taken.
10777 llvmModule->setDataLayout("");
887 }
888
889 // Get IR string
890 10989 std::string output;
891
1/2
✓ Branch 20 → 21 taken 10989 times.
✗ Branch 20 → 39 not taken.
10989 llvm::raw_string_ostream oss(output);
892
1/2
✓ Branch 21 → 22 taken 10989 times.
✗ Branch 21 → 37 not taken.
10989 llvmModule->print(oss, nullptr);
893
894 // Restore target triple and data layout
895
2/2
✓ Branch 22 → 23 taken 10777 times.
✓ Branch 22 → 29 taken 212 times.
10989 if (eliminateTarget) {
896
1/2
✓ Branch 23 → 24 taken 10777 times.
✗ Branch 23 → 35 not taken.
10777 llvmModule->setTargetTriple(targetTriple);
897
1/2
✓ Branch 27 → 28 taken 10777 times.
✗ Branch 27 → 36 not taken.
10777 llvmModule->setDataLayout(targetDataLayout);
898 }
899
900 10989 return output;
901 10989 }
902
903 /**
904 * Returns the operator function list for the current manifestation and the given node
905 *
906 * @param node Node to retrieve the op fct pointer list from
907 * @return Op fct pointer list
908 */
909 182938 const std::vector<const Function *> &IRGenerator::getOpFctPointers(const ASTNode *node) const {
910
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 182938 times.
182938 assert(node->getOpFctPointers()->size() > manIdx);
911 182938 return node->getOpFctPointers()->at(manIdx);
912 }
913
914 } // namespace spice::compiler
915