GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 97.8% 489 / 7 / 507
Functions: 96.3% 52 / 0 / 54
Branches: 62.6% 541 / 24 / 888

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