GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 97.8% 483 / 7 / 501
Functions: 96.3% 52 / 0 / 54
Branches: 62.4% 525 / 24 / 866

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 5803 IRGenerator::IRGenerator(GlobalResourceManager &resourceManager, SourceFile *sourceFile)
21 5803 : CompilerPass(resourceManager, sourceFile), context(cliOptions.useLTO ? resourceManager.ltoContext : sourceFile->context),
22
1/2
✓ Branch 8 → 9 taken 5803 times.
✗ Branch 8 → 83 not taken.
5803 builder(sourceFile->builder), module(sourceFile->llvmModule.get()), conversionManager(sourceFile, this),
23
6/10
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 6 taken 5799 times.
✓ Branch 9 → 10 taken 5803 times.
✗ Branch 9 → 83 not taken.
✓ Branch 10 → 11 taken 5803 times.
✗ Branch 10 → 83 not taken.
✓ Branch 11 → 12 taken 5803 times.
✗ Branch 11 → 81 not taken.
✓ Branch 15 → 16 taken 5803 times.
✗ Branch 15 → 77 not taken.
11606 stdFunctionManager(sourceFile, resourceManager, module) {
24 // Attach information to the module
25
1/2
✓ Branch 19 → 20 taken 5803 times.
✗ Branch 19 → 56 not taken.
5803 module->setTargetTriple(cliOptions.targetTriple);
26
2/4
✓ Branch 23 → 24 taken 5803 times.
✗ Branch 23 → 59 not taken.
✓ Branch 24 → 25 taken 5802 times.
✗ Branch 24 → 57 not taken.
5803 module->setDataLayout(sourceFile->targetMachine->createDataLayout());
27
2/2
✓ Branch 26 → 27 taken 2 times.
✓ Branch 26 → 29 taken 5801 times.
5803 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 5801 times.
✗ Branch 29 → 69 not taken.
5801 module->setPICLevel(llvm::PICLevel::BigPIC);
32
1/2
✓ Branch 30 → 31 taken 5801 times.
✗ Branch 30 → 69 not taken.
5801 module->setPIELevel(llvm::PIELevel::Large);
33 }
34
1/2
✓ Branch 31 → 32 taken 5803 times.
✗ Branch 31 → 69 not taken.
5803 module->setUwtable(llvm::UWTableKind::Default);
35
3/4
✓ Branch 32 → 33 taken 2 times.
✓ Branch 32 → 34 taken 5801 times.
✓ Branch 35 → 36 taken 5803 times.
✗ Branch 35 → 69 not taken.
5803 module->setFramePointer(cliOptions.keepFramePointers ? llvm::FramePointerKind::All : llvm::FramePointerKind::None);
36
37 // Add module identifier metadata
38
2/4
✓ Branch 36 → 37 taken 5803 times.
✗ Branch 36 → 60 not taken.
✓ Branch 37 → 38 taken 5803 times.
✗ Branch 37 → 60 not taken.
5803 llvm::NamedMDNode *identifierMetadata = module->getOrInsertNamedMetadata("llvm.ident");
39
3/6
✓ Branch 39 → 40 taken 5803 times.
✗ Branch 39 → 61 not taken.
✓ Branch 41 → 42 taken 5803 times.
✗ Branch 41 → 61 not taken.
✓ Branch 42 → 43 taken 5803 times.
✗ Branch 42 → 61 not taken.
5803 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 5803 times.
✗ Branch 43 → 64 not taken.
✓ Branch 44 → 45 taken 5803 times.
✗ Branch 44 → 64 not taken.
✓ Branch 45 → 46 taken 5803 times.
✗ Branch 45 → 64 not taken.
✓ Branch 47 → 48 taken 5803 times.
✗ Branch 47 → 64 not taken.
5803 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 2929 times.
✓ Branch 50 → 55 taken 2874 times.
11606 if (cliOptions.instrumentation.emitsDebugInfo())
46
2/4
✓ Branch 51 → 52 taken 2929 times.
✗ Branch 51 → 68 not taken.
✓ Branch 52 → 53 taken 2929 times.
✗ Branch 52 → 66 not taken.
2929 diGenerator.initialize(sourceFile->fileName, sourceFile->fileDir);
47 5803 }
48
49 5803 std::any IRGenerator::visitEntry(const EntryNode *node) {
50 // Generate IR
51
1/2
✓ Branch 2 → 3 taken 5803 times.
✗ Branch 2 → 28 not taken.
5803 visitChildren(node);
52
53 // Generate test main if required
54
4/4
✓ Branch 4 → 5 taken 967 times.
✓ Branch 4 → 7 taken 4836 times.
✓ Branch 5 → 6 taken 10 times.
✓ Branch 5 → 7 taken 957 times.
5803 if (sourceFile->isMainFile && cliOptions.generateTestMain)
55 10 generateTestMain();
56
57 // Execute deferred VTable initializations
58
2/2
✓ Branch 21 → 9 taken 4594 times.
✓ Branch 21 → 22 taken 5803 times.
16200 for (DeferredLogic &deferredVTableInit : deferredVTableInitializations)
59
1/2
✓ Branch 11 → 12 taken 4594 times.
✗ Branch 11 → 29 not taken.
4594 deferredVTableInit.execute();
60
61 // Finalize debug info generator
62 5803 diGenerator.finalize();
63
64 // Verify module
65 5803 verifyModule(node->codeLoc);
66
67
1/2
✓ Branch 24 → 25 taken 5803 times.
✗ Branch 24 → 30 not taken.
11606 return nullptr;
68 }
69
70 252218 llvm::AllocaInst *IRGenerator::insertAlloca(llvm::Type *llvmType, const std::string &varName) {
71
2/2
✓ Branch 2 → 3 taken 164382 times.
✓ Branch 2 → 8 taken 87836 times.
252218 if (allocaInsertInst != nullptr) { // If there is already an alloca inst, insert right after that
72
2/4
✓ Branch 3 → 4 taken 164382 times.
✗ Branch 3 → 19 not taken.
✓ Branch 4 → 5 taken 164382 times.
✗ Branch 4 → 19 not taken.
164382 llvm::AllocaInst *allocaInst = builder.CreateAlloca(llvmType, nullptr, varName);
73 164382 allocaInst->dropLocation(); // Part of prologue
74 164382 allocaInst->moveAfter(allocaInsertInst);
75 164382 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 87836 llvm::BasicBlock *currentBlock = builder.GetInsertBlock();
79 87836 builder.SetInsertPoint(allocaInsertBlock, allocaInsertBlock->begin());
80
81 // Allocate the size of the given LLVM type
82
2/4
✓ Branch 11 → 12 taken 87836 times.
✗ Branch 11 → 20 not taken.
✓ Branch 12 → 13 taken 87836 times.
✗ Branch 12 → 20 not taken.
87836 allocaInsertInst = builder.CreateAlloca(llvmType, nullptr, varName);
83 87836 allocaInsertInst->dropLocation(); // Part of prologue
84
85 // Restore old basic block
86 87836 builder.SetInsertPoint(currentBlock);
87 }
88
89 // Insert lifetime start marker
90
2/2
✓ Branch 15 → 16 taken 1657 times.
✓ Branch 15 → 17 taken 250561 times.
252218 if (cliOptions.useLifetimeMarkers)
91 1657 builder.CreateLifetimeStart(allocaInsertInst);
92
93 252218 return allocaInsertInst;
94 }
95
96 201335 llvm::AllocaInst *IRGenerator::insertAlloca(const QualType &qualType, const std::string &varName) {
97 201335 llvm::Type *llvmType = qualType.toLLVMType(sourceFile);
98 201335 llvm::AllocaInst *alloca = insertAlloca(llvmType, varName);
99
100 // Insert type metadata
101
2/2
✓ Branch 4 → 5 taken 8 times.
✓ Branch 4 → 6 taken 201327 times.
201335 if (cliOptions.useTBAAMetadata)
102 8 mdGenerator.generateTypeMetadata(allocaInsertInst, qualType);
103
104 201335 return alloca;
105 }
106
107 613523 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 613523 times.
613523 assert(ptr->getType()->isPointerTy());
110
2/4
✓ Branch 6 → 7 taken 613523 times.
✗ Branch 6 → 11 not taken.
✓ Branch 7 → 8 taken 613523 times.
✗ Branch 7 → 11 not taken.
613523 return builder.CreateLoad(llvmType, ptr, isVolatile, varName);
111 }
112
113 519097 llvm::LoadInst *IRGenerator::insertLoad(const QualType &qualType, llvm::Value *ptr, bool isVolatile, const std::string &varName) {
114 519097 llvm::Type *llvmType = qualType.toLLVMType(sourceFile);
115 519097 llvm::LoadInst *load = insertLoad(llvmType, ptr, isVolatile, varName);
116
2/2
✓ Branch 4 → 5 taken 10 times.
✓ Branch 4 → 6 taken 519087 times.
519097 if (cliOptions.useTBAAMetadata)
117 10 mdGenerator.generateTBAAMetadata(load, qualType);
118 519097 return load;
119 }
120
121 316941 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 316941 times.
316941 assert(ptr->getType()->isPointerTy());
123 316941 return builder.CreateStore(val, ptr, isVolatile);
124 }
125
126 89915 void IRGenerator::insertStore(llvm::Value *val, llvm::Value *ptr, const QualType &qualType, bool isVolatile) {
127 89915 llvm::StoreInst *store = insertStore(val, ptr, isVolatile);
128
2/2
✓ Branch 3 → 4 taken 8 times.
✓ Branch 3 → 5 taken 89907 times.
89915 if (cliOptions.useTBAAMetadata)
129 8 mdGenerator.generateTBAAMetadata(store, qualType);
130 89915 }
131
132 173588 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 173588 times.
173588 assert(basePtr->getType()->isPointerTy());
135
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 173588 times.
173588 assert(!indices.empty());
136
4/6
✓ Branch 4 → 5 taken 171908 times.
✓ Branch 4 → 7 taken 158878 times.
✓ Branch 6 → 7 taken 171908 times.
✗ Branch 6 → 8 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 173588 times.
504374 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 173588 times.
✗ Branch 12 → 17 not taken.
✓ Branch 13 → 14 taken 173588 times.
✗ Branch 13 → 17 not taken.
173588 return builder.CreateInBoundsGEP(type, basePtr, indices, varName);
143 }
144
145 60946 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 60946 times.
60946 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 19872 times.
✓ Branch 6 → 8 taken 41074 times.
60946 if (index == 0)
151 19872 return basePtr;
152
153 // Insert GEP
154
2/4
✓ Branch 8 → 9 taken 41074 times.
✗ Branch 8 → 13 not taken.
✓ Branch 9 → 10 taken 41074 times.
✗ Branch 9 → 13 not taken.
41074 return builder.CreateStructGEP(type, basePtr, index, varName);
155 }
156
157 341663 llvm::Value *IRGenerator::resolveValue(const ExprNode *node) {
158 // Visit the given AST node
159
2/4
✓ Branch 2 → 3 taken 341663 times.
✗ Branch 2 → 11 not taken.
✓ Branch 3 → 4 taken 341663 times.
✗ Branch 3 → 9 not taken.
341663 auto exprResult = any_cast<LLVMExprResult>(visit(node));
160
1/2
✓ Branch 5 → 6 taken 341663 times.
✗ Branch 5 → 12 not taken.
683326 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 68401 llvm::Value *IRGenerator::resolveValueInExprScope(const ExprNode *expr) {
171
1/2
✓ Branch 2 → 3 taken 68401 times.
✗ Branch 2 → 12 not taken.
68401 const ExprScopeHandle exprScopeHandle(this, expr);
172
1/2
✓ Branch 3 → 4 taken 68401 times.
✗ Branch 3 → 10 not taken.
68401 llvm::Value *value = resolveValue(expr);
173
2/2
✓ Branch 5 → 6 taken 68393 times.
✓ Branch 5 → 7 taken 8 times.
68401 if (const Scope *exprScope = exprScopeHandle.getExprScope())
174
1/2
✓ Branch 6 → 7 taken 68393 times.
✗ Branch 6 → 10 not taken.
68393 generateTemporariesCleanup(exprScope, expr);
175 68401 return value;
176 68401 }
177
178 378471 llvm::Value *IRGenerator::resolveValue(const ExprNode *node, LLVMExprResult &exprResult) {
179 378471 return resolveValue(node->getEvaluatedSymbolType(manIdx), exprResult);
180 }
181
182 735487 llvm::Value *IRGenerator::resolveValue(const QualType &qualType, LLVMExprResult &exprResult) {
183 // Check if the value is already present
184
2/2
✓ Branch 2 → 3 taken 230530 times.
✓ Branch 2 → 4 taken 504957 times.
735487 if (exprResult.value != nullptr)
185 230530 return exprResult.value;
186
187 // Check if a constant is present
188
2/2
✓ Branch 4 → 5 taken 154488 times.
✓ Branch 4 → 7 taken 350469 times.
504957 if (exprResult.constant != nullptr) {
189 154488 materializeConstant(exprResult);
190 154488 return exprResult.value;
191 }
192
193
3/4
✓ Branch 7 → 8 taken 4565 times.
✓ Branch 7 → 10 taken 345904 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 4565 times.
350469 assert(exprResult.ptr != nullptr || exprResult.refPtr != nullptr);
194
195 // De-reference if reference type
196
4/4
✓ Branch 10 → 11 taken 330242 times.
✓ Branch 10 → 13 taken 20227 times.
✓ Branch 11 → 12 taken 22 times.
✓ Branch 11 → 13 taken 330220 times.
350469 const bool isVolatile = exprResult.entry && exprResult.entry->isVolatile;
197
4/4
✓ Branch 14 → 15 taken 4585 times.
✓ Branch 14 → 24 taken 345884 times.
✓ Branch 15 → 16 taken 4565 times.
✓ Branch 15 → 24 taken 20 times.
350469 if (exprResult.refPtr != nullptr && exprResult.ptr == nullptr)
198
2/4
✓ Branch 19 → 20 taken 4565 times.
✗ Branch 19 → 34 not taken.
✓ Branch 20 → 21 taken 4565 times.
✗ Branch 20 → 34 not taken.
4565 exprResult.ptr = insertLoad(builder.getPtrTy(), exprResult.refPtr, isVolatile);
199
200 // Load the value from the pointer
201
1/2
✓ Branch 24 → 25 taken 350469 times.
✗ Branch 24 → 46 not taken.
350469 const QualType referencedType = qualType.removeReferenceWrapper();
202
1/2
✓ Branch 28 → 29 taken 350469 times.
✗ Branch 28 → 40 not taken.
350469 exprResult.value = insertLoad(referencedType, exprResult.ptr, isVolatile);
203
204 350469 return exprResult.value;
205 }
206
207 35008 llvm::Value *IRGenerator::resolveAddress(const ASTNode *node) {
208 // Visit the given AST node
209
2/4
✓ Branch 2 → 3 taken 35008 times.
✗ Branch 2 → 11 not taken.
✓ Branch 3 → 4 taken 35008 times.
✗ Branch 3 → 9 not taken.
35008 auto exprResult = any_cast<LLVMExprResult>(visit(node));
210
1/2
✓ Branch 5 → 6 taken 35008 times.
✗ Branch 5 → 12 not taken.
70016 return resolveAddress(exprResult);
211 }
212
213 274042 llvm::Value *IRGenerator::resolveAddress(LLVMExprResult &exprResult) {
214 // Check if an address is already present
215
2/2
✓ Branch 2 → 3 taken 224284 times.
✓ Branch 2 → 4 taken 49758 times.
274042 if (exprResult.ptr != nullptr)
216 224284 return exprResult.ptr;
217
218 // Check if the reference address is already present
219
3/4
✓ Branch 4 → 5 taken 38872 times.
✓ Branch 4 → 7 taken 10886 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 38872 times.
49758 const bool isVolatile = exprResult.entry && exprResult.entry->isVolatile;
220
3/4
✓ Branch 8 → 9 taken 40868 times.
✓ Branch 8 → 18 taken 8890 times.
✓ Branch 9 → 10 taken 40868 times.
✗ Branch 9 → 18 not taken.
49758 if (exprResult.refPtr != nullptr && exprResult.ptr == nullptr) {
221
2/4
✓ Branch 13 → 14 taken 40868 times.
✗ Branch 13 → 35 not taken.
✓ Branch 14 → 15 taken 40868 times.
✗ Branch 14 → 35 not taken.
40868 exprResult.ptr = insertLoad(builder.getPtrTy(), exprResult.refPtr, isVolatile);
222 40868 return exprResult.ptr;
223 }
224
225 // If not, store the value or constant
226 8890 materializeConstant(exprResult);
227
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 8890 times.
8890 assert(exprResult.value != nullptr);
228
7/12
✓ Branch 21 → 22 taken 12 times.
✓ Branch 21 → 23 taken 8878 times.
✓ Branch 22 → 26 taken 12 times.
✗ Branch 22 → 43 not taken.
✓ Branch 25 → 26 taken 8878 times.
✗ Branch 25 → 43 not taken.
✓ Branch 27 → 28 taken 8890 times.
✗ Branch 27 → 41 not taken.
✓ Branch 29 → 30 taken 8878 times.
✓ Branch 29 → 32 taken 12 times.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 46 not taken.
17768 exprResult.ptr = insertAlloca(exprResult.value->getType(), exprResult.entry ? exprResult.entry->name : "");
229 8890 insertStore(exprResult.value, exprResult.ptr, isVolatile);
230
231 8890 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 14755 llvm::Constant *IRGenerator::getDefaultValueForSymbolType(const QualType &symbolType) { // NOLINT(misc-no-recursion)
278 // Double
279
2/2
✓ Branch 3 → 4 taken 92 times.
✓ Branch 3 → 9 taken 14663 times.
14755 if (symbolType.is(TY_DOUBLE))
280
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));
281
282 // Int
283
2/2
✓ Branch 10 → 11 taken 2189 times.
✓ Branch 10 → 13 taken 12474 times.
14663 if (symbolType.is(TY_INT))
284 2189 return builder.getInt32(0);
285
286 // Short
287
2/2
✓ Branch 14 → 15 taken 32 times.
✓ Branch 14 → 17 taken 12442 times.
12474 if (symbolType.is(TY_SHORT))
288 32 return builder.getInt16(0);
289
290 // Long
291
2/2
✓ Branch 18 → 19 taken 2657 times.
✓ Branch 18 → 21 taken 9785 times.
12442 if (symbolType.is(TY_LONG))
292 2657 return builder.getInt64(0);
293
294 // Byte or char
295
3/4
✓ Branch 21 → 22 taken 9785 times.
✗ Branch 21 → 175 not taken.
✓ Branch 22 → 23 taken 316 times.
✓ Branch 22 → 25 taken 9469 times.
9785 if (symbolType.isOneOf({TY_BYTE, TY_CHAR}))
296 316 return builder.getInt8(0);
297
298 // String
299
2/2
✓ Branch 26 → 27 taken 2037 times.
✓ Branch 26 → 35 taken 7432 times.
9469 if (symbolType.is(TY_STRING)) {
300
3/6
✓ Branch 27 → 28 taken 2037 times.
✗ Branch 27 → 177 not taken.
✓ Branch 28 → 29 taken 2037 times.
✗ Branch 28 → 176 not taken.
✓ Branch 29 → 30 taken 2037 times.
✗ Branch 29 → 176 not taken.
2037 llvm::GlobalVariable *globalString = builder.CreateGlobalString("", "");
301
1/2
✓ Branch 30 → 31 taken 2037 times.
✗ Branch 30 → 34 not taken.
2037 if (cliOptions.comparableOutput)
302
2/4
✓ Branch 31 → 32 taken 2037 times.
✗ Branch 31 → 178 not taken.
✓ Branch 32 → 33 taken 2037 times.
✗ Branch 32 → 178 not taken.
2037 globalString->setAlignment(llvm::Align(4));
303 2037 return globalString;
304 }
305
306 // Bool
307
2/2
✓ Branch 36 → 37 taken 386 times.
✓ Branch 36 → 39 taken 7046 times.
7432 if (symbolType.is(TY_BOOL))
308 386 return builder.getFalse();
309
310 // Pointer or reference
311
3/4
✓ Branch 39 → 40 taken 7046 times.
✗ Branch 39 → 179 not taken.
✓ Branch 40 → 41 taken 5052 times.
✓ Branch 40 → 44 taken 1994 times.
7046 if (symbolType.isOneOf({TY_PTR, TY_REF}))
312 5052 return llvm::Constant::getNullValue(builder.getPtrTy());
313
314 // Array
315
2/2
✓ Branch 45 → 46 taken 238 times.
✓ Branch 45 → 61 taken 1756 times.
1994 if (symbolType.isArray()) {
316 // Get array size
317
1/2
✓ Branch 46 → 47 taken 238 times.
✗ Branch 46 → 188 not taken.
238 const size_t arraySize = symbolType.getArraySize();
318
319 // Get default value for item
320
2/4
✓ Branch 47 → 48 taken 238 times.
✗ Branch 47 → 180 not taken.
✓ Branch 48 → 49 taken 238 times.
✗ Branch 48 → 180 not taken.
238 llvm::Constant *defaultItemValue = getDefaultValueForSymbolType(symbolType.getContained());
321
322 // Retrieve array and item type
323
2/4
✓ Branch 49 → 50 taken 238 times.
✗ Branch 49 → 181 not taken.
✓ Branch 50 → 51 taken 238 times.
✗ Branch 50 → 181 not taken.
238 llvm::Type *itemType = symbolType.getContained().toLLVMType(sourceFile);
324
1/2
✓ Branch 51 → 52 taken 238 times.
✗ Branch 51 → 188 not taken.
238 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 238 times.
✗ Branch 54 → 182 not taken.
476 const std::vector itemConstants(arraySize, defaultItemValue);
328
1/2
✓ Branch 57 → 58 taken 238 times.
✗ Branch 57 → 185 not taken.
238 return llvm::ConstantArray::get(arrayType, itemConstants);
329 238 }
330
331 // Function or procedure
332
3/4
✓ Branch 61 → 62 taken 1756 times.
✗ Branch 61 → 189 not taken.
✓ Branch 62 → 63 taken 154 times.
✓ Branch 62 → 70 taken 1602 times.
1756 if (symbolType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) {
333
2/4
✓ Branch 63 → 64 taken 154 times.
✗ Branch 63 → 190 not taken.
✓ Branch 64 → 65 taken 154 times.
✗ Branch 64 → 190 not taken.
154 llvm::Constant *ptrDefaultValue = getDefaultValueForSymbolType(QualType(TY_PTR));
334 154 llvm::Constant *sizeDefaultValue = builder.getInt64(0);
335
1/2
✓ Branch 67 → 68 taken 154 times.
✗ Branch 67 → 191 not taken.
154 return llvm::ConstantStruct::get(llvmTypes.lambdaFatPtrType, {ptrDefaultValue, ptrDefaultValue, sizeDefaultValue});
336 }
337
338 // Struct
339
2/2
✓ Branch 71 → 72 taken 1502 times.
✓ Branch 71 → 111 taken 100 times.
1602 if (symbolType.is(TY_STRUCT)) {
340 // Retrieve field count
341
1/2
✓ Branch 72 → 73 taken 1502 times.
✗ Branch 72 → 198 not taken.
1502 Scope *structScope = symbolType.getBodyScope();
342
1/2
✗ Branch 73 → 74 not taken.
✓ Branch 73 → 75 taken 1502 times.
1502 assert(structScope != nullptr);
343
1/2
✓ Branch 75 → 76 taken 1502 times.
✗ Branch 75 → 198 not taken.
1502 const size_t fieldCount = structScope->getFieldCount();
344
345 // Get default values for all fields of the struct
346 1502 std::vector<llvm::Constant *> fieldConstants;
347
1/2
✓ Branch 76 → 77 taken 1502 times.
✗ Branch 76 → 196 not taken.
1502 fieldConstants.reserve(fieldCount);
348
349 // Add default value for each struct field
350
2/2
✓ Branch 103 → 78 taken 4328 times.
✓ Branch 103 → 104 taken 1502 times.
5830 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 4328 times.
4328 const SymbolTableEntry *fieldEntry = structScope->lookupField(i);
353
3/6
✓ Branch 84 → 85 taken 4328 times.
✗ Branch 84 → 88 not taken.
✓ Branch 85 → 86 taken 4328 times.
✗ Branch 85 → 194 not taken.
✓ Branch 86 → 87 taken 4328 times.
✗ Branch 86 → 88 not taken.
4328 assert(fieldEntry != nullptr && fieldEntry->isField());
354
355 // Retrieve default field value
356 llvm::Constant *defaultFieldValue;
357
5/6
✓ Branch 89 → 90 taken 4328 times.
✗ Branch 89 → 91 not taken.
✓ Branch 92 → 93 taken 4282 times.
✓ Branch 92 → 98 taken 46 times.
✓ Branch 93 → 94 taken 3656 times.
✓ Branch 93 → 98 taken 626 times.
4328 if (const auto fieldNode = dynamic_cast<FieldNode *>(fieldEntry->declNode); fieldNode && fieldNode->defaultValue)
358
3/6
✓ Branch 94 → 95 taken 3656 times.
✗ Branch 94 → 194 not taken.
✓ Branch 95 → 96 taken 3656 times.
✗ Branch 95 → 193 not taken.
✓ Branch 96 → 97 taken 3656 times.
✗ Branch 96 → 193 not taken.
3656 defaultFieldValue = getConst(fieldNode->defaultValue->getCompileTimeValue(manIdx), fieldEntry->getQualType(), fieldNode);
359 else
360
2/4
✓ Branch 98 → 99 taken 672 times.
✗ Branch 98 → 194 not taken.
✓ Branch 99 → 100 taken 672 times.
✗ Branch 99 → 194 not taken.
672 defaultFieldValue = getDefaultValueForSymbolType(fieldEntry->getQualType());
361
362
1/2
✓ Branch 101 → 102 taken 4328 times.
✗ Branch 101 → 194 not taken.
4328 fieldConstants.push_back(defaultFieldValue);
363 }
364
365
2/4
✓ Branch 104 → 105 taken 1502 times.
✗ Branch 104 → 196 not taken.
✓ Branch 105 → 106 taken 1502 times.
✗ Branch 105 → 196 not taken.
1502 const auto structType = llvm::cast<llvm::StructType>(symbolType.toLLVMType(sourceFile));
366
1/2
✓ Branch 107 → 108 taken 1502 times.
✗ Branch 107 → 195 not taken.
1502 return llvm::ConstantStruct::get(structType, fieldConstants);
367 1502 }
368
369 // Union
370
2/2
✓ Branch 112 → 113 taken 52 times.
✓ Branch 112 → 155 taken 48 times.
100 if (symbolType.is(TY_UNION)) {
371 52 Scope *unionScope = symbolType.getBodyScope();
372
1/2
✗ Branch 114 → 115 not taken.
✓ Branch 114 → 116 taken 52 times.
52 assert(unionScope != nullptr);
373 52 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 52 uint32_t defaultFieldTag = 0; // Sentinel: no field is active ("unset" state)
378 52 llvm::Constant *defaultFieldValue = nullptr;
379
2/2
✓ Branch 139 → 118 taken 280 times.
✓ Branch 139 → 140 taken 48 times.
328 for (size_t i = 0; i < fieldCount; i++) {
380
1/2
✓ Branch 118 → 119 taken 280 times.
✗ Branch 118 → 121 not taken.
280 const SymbolTableEntry *fieldEntry = unionScope->lookupField(i);
381
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());
382
1/2
✓ Branch 129 → 130 taken 280 times.
✗ Branch 129 → 131 not taken.
280 const auto fieldNode = dynamic_cast<FieldNode *>(fieldEntry->declNode);
383
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) {
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 52 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 48 times.
✓ Branch 142 → 145 taken 4 times.
52 if (!defaultFieldValue)
396 48 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 48 times.
✗ Branch 156 → 163 not taken.
48 if (symbolType.is(TY_INTERFACE)) {
408 48 const auto structType = llvm::cast<llvm::StructType>(symbolType.toLLVMType(sourceFile));
409 48 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 145218 llvm::Constant *IRGenerator::getConst(const CompileTimeValue &compileTimeValue, const QualType &type, const ASTNode *node) const {
416
2/2
✓ Branch 3 → 4 taken 3858 times.
✓ Branch 3 → 9 taken 141360 times.
145218 if (type.is(TY_DOUBLE))
417
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));
418
419
2/2
✓ Branch 10 → 11 taken 22984 times.
✓ Branch 10 → 13 taken 118376 times.
141360 if (type.is(TY_INT))
420 22984 return builder.getInt32(compileTimeValue.intValue);
421
422
2/2
✓ Branch 14 → 15 taken 2684 times.
✓ Branch 14 → 17 taken 115692 times.
118376 if (type.is(TY_SHORT))
423 2684 return builder.getInt16(compileTimeValue.shortValue);
424
425
2/2
✓ Branch 18 → 19 taken 64817 times.
✓ Branch 18 → 21 taken 50875 times.
115692 if (type.is(TY_LONG))
426 64817 return builder.getInt64(compileTimeValue.longValue);
427
428
3/4
✓ Branch 21 → 22 taken 50875 times.
✗ Branch 21 → 57 not taken.
✓ Branch 22 → 23 taken 14045 times.
✓ Branch 22 → 25 taken 36830 times.
50875 if (type.isOneOf({TY_BYTE, TY_CHAR}))
429 14045 return builder.getInt8(compileTimeValue.charValue);
430
431
2/2
✓ Branch 26 → 27 taken 24390 times.
✓ Branch 26 → 36 taken 12440 times.
36830 if (type.is(TY_STRING)) {
432 24390 const std::string &stringValue = resourceManager.compileTimeStringValues.at(compileTimeValue.stringValueOffset);
433
2/4
✓ Branch 30 → 31 taken 24390 times.
✗ Branch 30 → 60 not taken.
✓ Branch 31 → 32 taken 24390 times.
✗ Branch 31 → 58 not taken.
73170 return createGlobalStringConst(ANON_GLOBAL_STRING_NAME, stringValue, node->codeLoc);
434 }
435
436
2/2
✓ Branch 37 → 38 taken 11308 times.
✓ Branch 37 → 40 taken 1132 times.
12440 if (type.is(TY_BOOL))
437 11308 return builder.getInt1(compileTimeValue.boolValue);
438
439
1/2
✓ Branch 41 → 42 taken 1132 times.
✗ Branch 41 → 45 not taken.
1132 if (type.is(TY_PTR))
440 1132 return llvm::Constant::getNullValue(builder.getPtrTy());
441
442 throw CompilerError(UNHANDLED_BRANCH, "Constant fall-through"); // GCOV_EXCL_LINE
443 }
444
445 282520 llvm::BasicBlock *IRGenerator::createBlock(const std::string &blockName /*=""*/) const {
446
2/4
✓ Branch 2 → 3 taken 282520 times.
✗ Branch 2 → 7 not taken.
✓ Branch 3 → 4 taken 282520 times.
✗ Branch 3 → 7 not taken.
282520 return llvm::BasicBlock::Create(context, blockName);
447 }
448
449 282520 void IRGenerator::switchToBlock(llvm::BasicBlock *block, llvm::Function *parentFct /*=nullptr*/) {
450
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 282520 times.
282520 assert(block->getParent() == nullptr); // Ensure that the block was not added to a function already
451 // If no parent function were passed, use the current function
452
2/2
✓ Branch 5 → 6 taken 193670 times.
✓ Branch 5 → 8 taken 88850 times.
282520 if (!parentFct)
453 193670 parentFct = builder.GetInsertBlock()->getParent();
454 // Append block to current function
455 282520 parentFct->insert(parentFct->end(), block);
456 // Set insert point to the block
457 282520 builder.SetInsertPoint(block);
458 282520 blockAlreadyTerminated = false;
459 282520 }
460
461 5121 void IRGenerator::terminateBlock(const StmtLstNode *stmtLstNode) {
462 5121 generateScopeCleanup(stmtLstNode);
463 5121 blockAlreadyTerminated = true;
464 5121 }
465
466 101444 void IRGenerator::insertJump(llvm::BasicBlock *targetBlock) {
467
2/2
✓ Branch 2 → 3 taken 33739 times.
✓ Branch 2 → 4 taken 67705 times.
101444 if (blockAlreadyTerminated)
468 33739 return;
469 67705 builder.CreateBr(targetBlock);
470 67705 blockAlreadyTerminated = true;
471 }
472
473 83666 void IRGenerator::insertCondJump(llvm::Value *condition, llvm::BasicBlock *trueBlock, llvm::BasicBlock *falseBlock,
474 Likelihood likelihood /*=UNSPECIFIED*/) {
475
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 83666 times.
83666 if (blockAlreadyTerminated)
476 return;
477 83666 llvm::CondBrInst *jumpInst = builder.CreateCondBr(condition, trueBlock, falseBlock);
478 83666 blockAlreadyTerminated = true;
479
480
2/2
✓ Branch 5 → 6 taken 11311 times.
✓ Branch 5 → 7 taken 72355 times.
83666 if (likelihood != Likelihood::UNSPECIFIED)
481 11311 mdGenerator.generateBranchWeightsMetadata(jumpInst, likelihood);
482 }
483
484 88788 void IRGenerator::verifyFunction(const llvm::Function *fct, const CodeLoc &codeLoc) const {
485 // Skip the verifying step if the verifier was disabled manually or debug info is emitted
486
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 88788 times.
88788 if (cliOptions.disableVerifier)
487 return;
488
489 // Verify function
490 88788 std::string output;
491
1/2
✓ Branch 5 → 6 taken 88788 times.
✗ Branch 5 → 20 not taken.
88788 llvm::raw_string_ostream oss(output);
492 if (llvm::verifyFunction(*fct, &oss)) // LCOV_EXCL_LINE
493 throw CompilerError(codeLoc, INVALID_FUNCTION, output); // LCOV_EXCL_LINE
494 88788 }
495
496 5803 void IRGenerator::verifyModule(const CodeLoc &codeLoc) const {
497 // Skip the verifying step if the verifier was disabled manually or debug info is emitted
498
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 5803 times.
5803 if (cliOptions.disableVerifier)
499 return;
500
501 // Verify module
502 5803 std::string output;
503
1/2
✓ Branch 5 → 6 taken 5803 times.
✗ Branch 5 → 20 not taken.
5803 llvm::raw_string_ostream oss(output);
504 if (llvm::verifyModule(*module, &oss)) // LCOV_EXCL_LINE
505 throw CompilerError(codeLoc, INVALID_MODULE, output); // LCOV_EXCL_LINE
506 5803 }
507
508 49299 LLVMExprResult IRGenerator::doAssignment(const ASTNode *lhsNode, const ExprNode *rhsNode, const ASTNode *node) {
509 // Get entry of left side
510
2/4
✓ Branch 2 → 3 taken 49299 times.
✗ Branch 2 → 18 not taken.
✓ Branch 3 → 4 taken 49299 times.
✗ Branch 3 → 16 not taken.
49299 auto exprResult = std::any_cast<LLVMExprResult>(visit(lhsNode));
511 49299 const SymbolTableEntry *entry = exprResult.entry;
512
7/10
✓ Branch 5 → 6 taken 43974 times.
✓ Branch 5 → 10 taken 5325 times.
✓ Branch 6 → 7 taken 43974 times.
✗ Branch 6 → 19 not taken.
✓ Branch 7 → 8 taken 43974 times.
✗ Branch 7 → 19 not taken.
✓ Branch 8 → 9 taken 2798 times.
✓ Branch 8 → 10 taken 41176 times.
✓ Branch 10 → 11 taken 46501 times.
✗ Branch 10 → 19 not taken.
49299 llvm::Value *lhsAddress = entry != nullptr && entry->getQualType().isRef() ? exprResult.refPtr : resolveAddress(exprResult);
513
1/2
✓ Branch 12 → 13 taken 49299 times.
✗ Branch 12 → 19 not taken.
98598 return doAssignment(lhsAddress, entry, rhsNode, node);
514 }
515
516 110959 LLVMExprResult IRGenerator::doAssignment(llvm::Value *lhsAddress, const SymbolTableEntry *lhsEntry, const ExprNode *rhsNode,
517 const ASTNode *node, bool isDecl) {
518 // Get symbol type of right side
519
1/2
✓ Branch 2 → 3 taken 110959 times.
✗ Branch 2 → 13 not taken.
110959 const QualType &rhsSType = rhsNode->getEvaluatedSymbolType(manIdx);
520
2/4
✓ Branch 3 → 4 taken 110959 times.
✗ Branch 3 → 12 not taken.
✓ Branch 4 → 5 taken 110959 times.
✗ Branch 4 → 10 not taken.
110959 auto rhs = std::any_cast<LLVMExprResult>(visit(rhsNode));
521
1/2
✓ Branch 6 → 7 taken 110959 times.
✗ Branch 6 → 13 not taken.
221918 return doAssignment(lhsAddress, lhsEntry, rhs, rhsSType, node, isDecl);
522 }
523
524 112210 LLVMExprResult IRGenerator::doAssignment(llvm::Value *lhsAddress, const SymbolTableEntry *lhsEntry, LLVMExprResult &rhs,
525 const QualType &rhsSType, const ASTNode *node, bool isDecl) {
526 // Deduce some information about the assignment
527
4/4
✓ Branch 2 → 3 taken 106885 times.
✓ Branch 2 → 7 taken 5325 times.
✓ Branch 5 → 6 taken 9039 times.
✓ Branch 5 → 7 taken 97846 times.
112210 const bool isRefAssign = lhsEntry != nullptr && lhsEntry->getQualType().isRef();
528 // A non-temporary struct value assigned by value needs a deep copy. This holds whether the destination is a direct
529 // lvalue or the value behind an already-bound reference (assign-through): the binding cases of a reference assignment
530 // (declaration/initial field ref/return value) all return early above before this is consumed, so the remaining
531 // reference assignments are assign-throughs that must copy into the referent instead of shallow-copying (which would
532 // alias the rhs' owned members and double-free).
533
6/8
✓ Branch 8 → 9 taken 112210 times.
✗ Branch 8 → 264 not taken.
✓ Branch 9 → 10 taken 112210 times.
✗ Branch 9 → 264 not taken.
✓ Branch 10 → 11 taken 20200 times.
✓ Branch 10 → 14 taken 92010 times.
✓ Branch 12 → 13 taken 3371 times.
✓ Branch 12 → 14 taken 16829 times.
112210 const bool needsCopy = rhsSType.removeReferenceWrapper().is(TY_STRUCT) && !rhs.isTemporary();
534
535
2/2
✓ Branch 15 → 16 taken 9039 times.
✓ Branch 15 → 57 taken 103171 times.
112210 if (isRefAssign) {
536
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 9039 times.
9039 assert(lhsEntry != nullptr);
537
2/2
✓ Branch 18 → 19 taken 6241 times.
✓ Branch 18 → 33 taken 2798 times.
9039 if (isDecl) { // Reference gets initially assigned
538 // Store lhs pointer to rhs
539
2/4
✓ Branch 22 → 23 taken 6241 times.
✗ Branch 22 → 265 not taken.
✓ Branch 23 → 24 taken 6241 times.
✗ Branch 23 → 265 not taken.
6241 llvm::Value *refAddress = insertAlloca(builder.getPtrTy());
540 6241 updateAddress(lhsEntry, refAddress);
541
542 // Generate debug info for variable declaration
543 6241 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, refAddress);
544
545 // Get address of right side
546 6241 llvm::Value *rhsAddress = resolveAddress(rhs);
547
1/2
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 6241 times.
6241 assert(rhsAddress != nullptr);
548 6241 insertStore(rhsAddress, refAddress);
549
550 6241 return LLVMExprResult{.value = rhsAddress, .ptr = refAddress, .entry = lhsEntry};
551 }
552
553 // Reference to reference assignment (only for struct fields that are not initialized yet)
554 // These are only allowed inside a ctor body. In other cases, the value of the reference gets assigned, not the ref itself.
555
6/8
✓ Branch 33 → 34 taken 2047 times.
✓ Branch 33 → 40 taken 751 times.
✓ Branch 35 → 36 taken 2039 times.
✓ Branch 35 → 40 taken 8 times.
✓ Branch 36 → 37 taken 2039 times.
✗ Branch 36 → 40 not taken.
✓ Branch 38 → 39 taken 2039 times.
✗ Branch 38 → 40 not taken.
2798 const bool isInitialFieldRefAssign = isInCtorBody && rhsSType.isRef() && rhs.entry && lhsEntry->isField();
556 // Assigning the result variable
557 2798 const bool isReturnValAssign = lhsEntry->name == RETURN_VARIABLE_NAME;
558
4/4
✓ Branch 42 → 43 taken 759 times.
✓ Branch 42 → 44 taken 2039 times.
✓ Branch 43 → 44 taken 24 times.
✓ Branch 43 → 49 taken 735 times.
2798 if (isInitialFieldRefAssign || isReturnValAssign) {
559 // Get address of right side
560 2063 llvm::Value *referencedAddress = resolveAddress(rhs);
561
1/2
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 47 taken 2063 times.
2063 assert(referencedAddress != nullptr);
562
563 // Store the rhs* to the lhs**
564 2063 insertStore(referencedAddress, lhsAddress);
565
566 2063 return LLVMExprResult{.value = referencedAddress, .ptr = lhsAddress, .entry = lhsEntry};
567 }
568
569 // Load referenced address
570
2/4
✓ Branch 52 → 53 taken 735 times.
✗ Branch 52 → 271 not taken.
✓ Branch 53 → 54 taken 735 times.
✗ Branch 53 → 271 not taken.
735 lhsAddress = insertLoad(builder.getPtrTy(), lhsAddress);
571 }
572
573
8/8
✓ Branch 57 → 58 taken 56670 times.
✓ Branch 57 → 63 taken 47236 times.
✓ Branch 59 → 60 taken 6727 times.
✓ Branch 59 → 63 taken 49943 times.
✓ Branch 61 → 62 taken 6705 times.
✓ Branch 61 → 63 taken 22 times.
✓ Branch 64 → 65 taken 6705 times.
✓ Branch 64 → 86 taken 97201 times.
103906 if (isDecl && rhsSType.is(TY_STRUCT) && rhs.isTemporary()) {
574
1/2
✗ Branch 65 → 66 not taken.
✓ Branch 65 → 67 taken 6705 times.
6705 assert(lhsEntry != nullptr);
575
1/2
✓ Branch 67 → 68 taken 6705 times.
✗ Branch 67 → 283 not taken.
6705 llvm::Value *rhsAddress = resolveAddress(rhs);
576 // Only adopt the temporary's storage directly (temp stealing) unless it is a phi merging two independently
577 // materialized pointers (e.g. a ternary whose branches each own their own storage). Lifetime markers require
578 // their operand to be a real alloca, so stealing such a phi here would emit an invalid llvm.lifetime.end
579 // under --sanitizer=address. Any other pointer (a plain alloca, or one derived from it via GEP/load, as in
580 // the foreach-loop item extraction below) denotes a single, stable piece of storage and is safe to adopt.
581 // The temporary's ownership fully transfers to lhsEntry either way (its underlying storage is never
582 // separately destructed), so falling back to a shallow copy into a dedicated alloca is exactly as correct,
583 // just gives up the pointer-adoption optimization for the phi case.
584
3/4
✓ Branch 68 → 69 taken 6705 times.
✗ Branch 68 → 283 not taken.
✓ Branch 69 → 70 taken 6693 times.
✓ Branch 69 → 73 taken 12 times.
6705 if (!llvm::isa<llvm::PHINode>(rhsAddress)) {
585 // Directly set the address to the lhs entry (temp stealing)
586
1/2
✓ Branch 70 → 71 taken 6693 times.
✗ Branch 70 → 283 not taken.
6693 updateAddress(lhsEntry, rhsAddress);
587 6693 rhs.entry = lhsEntry;
588 // Generate debug info for variable declaration
589
1/2
✓ Branch 71 → 72 taken 6693 times.
✗ Branch 71 → 283 not taken.
6693 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, rhsAddress);
590 6693 return rhs;
591 }
592 // Size the copy off lhsEntry's own type, not rhsSType: some callers (e.g. the foreach-loop item
593 // extraction below) pass a struct rhsSType only to steer this branch, while rhs itself resolves (through
594 // refPtr indirection) to a differently-typed value; lhsEntry's type is always the authoritative one here.
595
1/2
✓ Branch 73 → 74 taken 12 times.
✗ Branch 73 → 283 not taken.
12 const QualType &lhsQualType = lhsEntry->getQualType();
596
1/2
✓ Branch 77 → 78 taken 12 times.
✗ Branch 77 → 277 not taken.
12 llvm::Value *lhsAddr = insertAlloca(lhsQualType);
597
1/2
✓ Branch 80 → 81 taken 12 times.
✗ Branch 80 → 283 not taken.
12 updateAddress(lhsEntry, lhsAddr);
598
1/2
✓ Branch 81 → 82 taken 12 times.
✗ Branch 81 → 283 not taken.
12 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, lhsAddr);
599
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);
600 12 rhs.ptr = lhsAddr;
601 12 rhs.entry = lhsEntry;
602 12 return rhs;
603 }
604
605 // Allocate new memory if the lhs address does not exist
606
2/2
✓ Branch 86 → 87 taken 49494 times.
✓ Branch 86 → 98 taken 47707 times.
97201 if (!lhsAddress) {
607
1/2
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 49494 times.
49494 assert(lhsEntry != nullptr);
608
2/4
✓ Branch 92 → 93 taken 49494 times.
✗ Branch 92 → 284 not taken.
✓ Branch 93 → 94 taken 49494 times.
✗ Branch 93 → 284 not taken.
49494 lhsAddress = insertAlloca(lhsEntry->getQualType());
609 49494 updateAddress(lhsEntry, lhsAddress);
610 // Generate debug info for variable declaration
611 49494 diGenerator.generateLocalVarDebugInfo(lhsEntry->name, lhsAddress);
612 }
613
614 // 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
615
10/10
✓ Branch 98 → 99 taken 91876 times.
✓ Branch 98 → 107 taken 5325 times.
✓ Branch 101 → 102 taken 20560 times.
✓ Branch 101 → 107 taken 71316 times.
✓ Branch 103 → 104 taken 16 times.
✓ Branch 103 → 107 taken 20544 times.
✓ Branch 105 → 106 taken 6 times.
✓ Branch 105 → 107 taken 10 times.
✓ Branch 108 → 109 taken 6 times.
✓ Branch 108 → 124 taken 97195 times.
97201 if (lhsEntry && lhsEntry->getQualType().isPtr() && rhsSType.isArray() && rhsSType.getArraySize() != ARRAY_SIZE_UNKNOWN) {
616 // Get address of right side
617
1/2
✓ Branch 109 → 110 taken 6 times.
✗ Branch 109 → 297 not taken.
6 llvm::Value *rhsAddress = resolveAddress(rhs);
618
1/2
✗ Branch 110 → 111 not taken.
✓ Branch 110 → 112 taken 6 times.
6 assert(rhsAddress != nullptr);
619
1/2
✓ Branch 112 → 113 taken 6 times.
✗ Branch 112 → 297 not taken.
6 llvm::Type *elementTy = rhsSType.toLLVMType(sourceFile);
620
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)};
621
1/2
✓ Branch 119 → 120 taken 6 times.
✗ Branch 119 → 290 not taken.
6 llvm::Value *firstItemAddress = insertInBoundsGEP(elementTy, rhsAddress, indices);
622
1/2
✓ Branch 122 → 123 taken 6 times.
✗ Branch 122 → 297 not taken.
6 insertStore(firstItemAddress, lhsAddress);
623 6 return LLVMExprResult{.value = rhsAddress, .ptr = lhsAddress, .entry = lhsEntry};
624 }
625
626 // Handle operator overloads
627
6/6
✓ Branch 124 → 125 taken 47236 times.
✓ Branch 124 → 128 taken 49959 times.
✓ Branch 126 → 127 taken 1264 times.
✓ Branch 126 → 128 taken 45972 times.
✓ Branch 129 → 130 taken 1264 times.
✓ Branch 129 → 146 taken 95931 times.
97195 if (!isDecl && conversionManager.callsOverloadedOpFct(node, DEFAULT_OP_IDX)) {
628 1264 ResolverFct lhsV = [&] { return static_cast<llvm::Value *>(nullptr); };
629
1/2
✓ Branch 131 → 132 taken 1264 times.
✗ Branch 131 → 298 not taken.
1264 ResolverFct rhsV = [&] { return resolveValue(rhsSType, rhs); };
630 2528 ResolverFct lhsP = [&] { return lhsAddress; };
631 2528 ResolverFct rhsP = [&] { return resolveAddress(rhs); };
632 1264 return conversionManager.callOperatorOverloadFct<2>(node, {lhsV, lhsP, rhsV, rhsP}, DEFAULT_OP_IDX);
633 1264 }
634
635 // Check if we need to copy the rhs to the lhs. This happens for structs
636
2/2
✓ Branch 146 → 147 taken 1255 times.
✓ Branch 146 → 232 taken 94676 times.
95931 if (needsCopy) {
637 // Get address of right side
638
1/2
✓ Branch 147 → 148 taken 1255 times.
✗ Branch 147 → 374 not taken.
1255 llvm::Value *rhsAddress = resolveAddress(rhs);
639
1/2
✗ Branch 148 → 149 not taken.
✓ Branch 148 → 150 taken 1255 times.
1255 assert(rhsAddress != nullptr);
640
641 // If the lhs already holds an initialized, non-trivially-destructible struct, its old value must be
642 // destructed before the copy overwrites it, otherwise its owning members (heap pointers, strings, ...)
643 // would leak. The typechecker only sets a dtor in exactly those cases. To stay correct for a self-
644 // assignment like 'a = a', the destruct + copy are skipped entirely when both sides share the address
645 // (the assignment is a no-op in that case, and destructing first would corrupt the value to copy from).
646
1/2
✓ Branch 150 → 151 taken 1255 times.
✗ Branch 150 → 152 not taken.
1255 const auto *assignNode = dynamic_cast<const AssignExprNode *>(node);
647
3/4
✓ Branch 153 → 154 taken 1233 times.
✓ Branch 153 → 156 taken 22 times.
✓ Branch 154 → 155 taken 1233 times.
✗ Branch 154 → 374 not taken.
1255 const Function *lhsDtor = assignNode ? assignNode->lhsDtorFct.at(manIdx) : nullptr;
648 1255 llvm::BasicBlock *bCopyEnd = nullptr;
649
2/2
✓ Branch 157 → 158 taken 56 times.
✓ Branch 157 → 178 taken 1199 times.
1255 if (lhsDtor != nullptr) {
650
2/4
✓ Branch 160 → 161 taken 56 times.
✗ Branch 160 → 318 not taken.
✓ Branch 161 → 162 taken 56 times.
✗ Branch 161 → 316 not taken.
112 llvm::BasicBlock *bCopy = createBlock("assign.copy");
651
2/4
✓ Branch 166 → 167 taken 56 times.
✗ Branch 166 → 324 not taken.
✓ Branch 167 → 168 taken 56 times.
✗ Branch 167 → 322 not taken.
56 bCopyEnd = createBlock("assign.copy.end");
652
3/6
✓ Branch 170 → 171 taken 56 times.
✗ Branch 170 → 328 not taken.
✓ Branch 171 → 172 taken 56 times.
✗ Branch 171 → 328 not taken.
✓ Branch 172 → 173 taken 56 times.
✗ Branch 172 → 328 not taken.
56 insertCondJump(builder.CreateICmpEQ(lhsAddress, rhsAddress), bCopyEnd, bCopy);
653
1/2
✓ Branch 173 → 174 taken 56 times.
✗ Branch 173 → 374 not taken.
56 switchToBlock(bCopy);
654
1/2
✓ Branch 175 → 176 taken 56 times.
✗ Branch 175 → 329 not taken.
56 generateCtorOrDtorCall(lhsAddress, lhsDtor, {});
655 }
656
657
2/4
✓ Branch 178 → 179 taken 1255 times.
✗ Branch 178 → 332 not taken.
✓ Branch 179 → 180 taken 1255 times.
✗ Branch 179 → 332 not taken.
1255 const QualType rhsSTypeNonRef = rhsSType.removeReferenceWrapper().toNonConst();
658
3/4
✓ Branch 180 → 181 taken 1255 times.
✗ Branch 180 → 374 not taken.
✓ Branch 181 → 182 taken 1016 times.
✓ Branch 181 → 198 taken 239 times.
1255 if (rhsSTypeNonRef.isTriviallyCopyable(node)) {
659 // Create shallow copy
660
1/2
✓ Branch 182 → 183 taken 1016 times.
✗ Branch 182 → 340 not taken.
1016 llvm::Type *rhsType = rhsSTypeNonRef.toLLVMType(sourceFile);
661
3/10
✓ Branch 183 → 184 taken 1016 times.
✗ Branch 183 → 185 not taken.
✓ Branch 184 → 188 taken 1016 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 1016 times.
✗ Branch 333 → 334 not taken.
✗ Branch 333 → 336 not taken.
1016 const std::string copyName = lhsEntry ? lhsEntry->name : "";
662
3/6
✓ Branch 191 → 192 taken 1016 times.
✗ Branch 191 → 194 not taken.
✗ Branch 192 → 193 not taken.
✓ Branch 192 → 194 taken 1016 times.
✓ Branch 195 → 196 taken 1016 times.
✗ Branch 195 → 338 not taken.
1016 generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry && lhsEntry->isVolatile);
663 1016 } else {
664 // Check if we have a copy ctor
665
1/2
✓ Branch 198 → 199 taken 239 times.
✗ Branch 198 → 373 not taken.
239 Scope *structScope = rhsSTypeNonRef.getBodyScope();
666
2/4
✓ Branch 199 → 200 taken 239 times.
✗ Branch 199 → 345 not taken.
✓ Branch 204 → 205 taken 239 times.
✗ Branch 204 → 341 not taken.
717 const ArgList args = {{rhsSTypeNonRef.toConstRef(node), rhs.isTemporary()}};
667
2/4
✓ Branch 208 → 209 taken 239 times.
✗ Branch 208 → 349 not taken.
✓ Branch 209 → 210 taken 239 times.
✗ Branch 209 → 347 not taken.
239 const Function *copyCtor = FunctionManager::lookup(structScope, CTOR_FUNCTION_NAME, rhsSTypeNonRef, args, true);
668
1/2
✓ Branch 212 → 213 taken 239 times.
✗ Branch 212 → 221 not taken.
239 if (copyCtor != nullptr) {
669 // Call copy ctor
670
2/4
✓ Branch 215 → 216 taken 239 times.
✗ Branch 215 → 355 not taken.
✓ Branch 216 → 217 taken 239 times.
✗ Branch 216 → 353 not taken.
478 generateCtorOrDtorCall(lhsAddress, copyCtor, {rhsAddress});
671 } else {
672 const std::string structName = rhsSTypeNonRef.getName();
673 const std::string msg = "Cannot copy struct '" + structName + "', as it is not trivially copyable and has no copy ctor";
674 throw SemanticError(node, COPY_CTOR_REQUIRED, msg);
675 }
676 239 }
677
678 // Close the self-assignment guard
679
2/2
✓ Branch 228 → 229 taken 56 times.
✓ Branch 228 → 231 taken 1199 times.
1255 if (bCopyEnd != nullptr) {
680
1/2
✓ Branch 229 → 230 taken 56 times.
✗ Branch 229 → 374 not taken.
56 insertJump(bCopyEnd);
681
1/2
✓ Branch 230 → 231 taken 56 times.
✗ Branch 230 → 374 not taken.
56 switchToBlock(bCopyEnd);
682 }
683 1255 return LLVMExprResult{.ptr = lhsAddress, .entry = lhsEntry};
684 }
685
686 // Optimization: If we have the address of both sides, we can do a memcpy instead of loading and storing the value
687 94676 llvm::Value *rhsValue = nullptr;
688
8/8
✓ Branch 233 → 234 taken 4979 times.
✓ Branch 233 → 237 taken 89697 times.
✓ Branch 234 → 235 taken 4775 times.
✓ Branch 234 → 237 taken 204 times.
✓ Branch 235 → 236 taken 4761 times.
✓ Branch 235 → 237 taken 14 times.
✓ Branch 238 → 239 taken 4761 times.
✓ Branch 238 → 260 taken 89915 times.
94676 if (rhsSType.is(TY_STRUCT) && rhs.value == nullptr && rhs.constant == nullptr) {
689 // Create shallow copy
690
2/4
✓ Branch 239 → 240 taken 4761 times.
✗ Branch 239 → 375 not taken.
✓ Branch 240 → 241 taken 4761 times.
✗ Branch 240 → 375 not taken.
4761 const QualType rhsSTypeNonRef = rhsSType.removeReferenceWrapper().toNonConst();
691
1/2
✓ Branch 241 → 242 taken 4761 times.
✗ Branch 241 → 383 not taken.
4761 llvm::Type *rhsType = rhsSTypeNonRef.toLLVMType(sourceFile);
692
1/2
✓ Branch 242 → 243 taken 4761 times.
✗ Branch 242 → 383 not taken.
4761 llvm::Value *rhsAddress = resolveAddress(rhs);
693
1/2
✗ Branch 243 → 244 not taken.
✓ Branch 243 → 245 taken 4761 times.
4761 assert(rhsAddress != nullptr);
694
6/10
✓ Branch 245 → 246 taken 3835 times.
✓ Branch 245 → 247 taken 926 times.
✓ Branch 246 → 250 taken 3835 times.
✗ Branch 246 → 376 not taken.
✓ Branch 249 → 250 taken 926 times.
✗ Branch 249 → 376 not taken.
✓ Branch 250 → 251 taken 926 times.
✓ Branch 250 → 253 taken 3835 times.
✗ Branch 376 → 377 not taken.
✗ Branch 376 → 379 not taken.
5687 const std::string copyName = lhsEntry ? lhsEntry->name : "";
695
4/6
✓ Branch 253 → 254 taken 3835 times.
✓ Branch 253 → 256 taken 926 times.
✗ Branch 254 → 255 not taken.
✓ Branch 254 → 256 taken 3835 times.
✓ Branch 257 → 258 taken 4761 times.
✗ Branch 257 → 381 not taken.
4761 generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry && lhsEntry->isVolatile);
696 4761 } else {
697 // We can load the value from the right side and store it to the left side
698 // Retrieve value of the right side
699 89915 rhsValue = resolveValue(rhsSType, rhs);
700 // Store the value to the address
701 89915 insertStore(rhsValue, lhsAddress, rhsSType);
702 }
703
704 94676 return LLVMExprResult{.value = rhsValue, .ptr = lhsAddress, .entry = lhsEntry};
705
5/14
✓ Branch 134 → 135 taken 1264 times.
✗ Branch 134 → 301 not taken.
✓ Branch 135 → 136 taken 1264 times.
✗ Branch 135 → 301 not taken.
✓ Branch 136 → 137 taken 1264 times.
✗ Branch 136 → 301 not taken.
✓ Branch 137 → 138 taken 1264 times.
✗ Branch 137 → 301 not taken.
✓ Branch 138 → 139 taken 1264 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.
1264 }
706
707 6425 void IRGenerator::generateShallowCopy(llvm::Value *oldAddress, llvm::Type *varType, llvm::Value *targetAddress,
708 bool isVolatile) const {
709 // Retrieve size to copy
710
1/2
✓ Branch 3 → 4 taken 6425 times.
✗ Branch 3 → 19 not taken.
6425 const llvm::TypeSize typeSize = module->getDataLayout().getTypeAllocSize(varType);
711
712 // Create values for memcpy intrinsic
713
2/4
✓ Branch 4 → 5 taken 6425 times.
✗ Branch 4 → 19 not taken.
✓ Branch 5 → 6 taken 6425 times.
✗ Branch 5 → 19 not taken.
6425 llvm::Value *structSize = builder.getInt64(typeSize);
714
1/2
✓ Branch 6 → 7 taken 6425 times.
✗ Branch 6 → 19 not taken.
6425 llvm::Value *copyVolatile = builder.getInt1(isVolatile);
715
716 // Call memcpy intrinsic to execute the shallow copy
717
1/2
✓ Branch 7 → 8 taken 6425 times.
✗ Branch 7 → 19 not taken.
6425 llvm::Function *memcpyFct = stdFunctionManager.getMemcpyIntrinsic();
718
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 6425 times.
6425 assert(targetAddress != nullptr);
719
3/6
✓ Branch 10 → 11 taken 6425 times.
✗ Branch 10 → 18 not taken.
✓ Branch 12 → 13 taken 6425 times.
✗ Branch 12 → 15 not taken.
✓ Branch 13 → 14 taken 6425 times.
✗ Branch 13 → 15 not taken.
6425 builder.CreateCall(memcpyFct, {targetAddress, oldAddress, structSize, copyVolatile});
720 6425 }
721
722 226879 void IRGenerator::autoDeReferencePtr(llvm::Value *&ptr, QualType &symbolType) {
723
6/6
✓ Branch 12 → 13 taken 237692 times.
✓ Branch 12 → 15 taken 157815 times.
✓ Branch 14 → 15 taken 10813 times.
✓ Branch 14 → 16 taken 226879 times.
✓ Branch 17 → 3 taken 168628 times.
✓ Branch 17 → 18 taken 226879 times.
395507 while (symbolType.isPtr() || symbolType.isRef()) {
724
1/2
✓ Branch 6 → 7 taken 168628 times.
✗ Branch 6 → 19 not taken.
168628 ptr = insertLoad(symbolType, ptr);
725
1/2
✓ Branch 9 → 10 taken 168628 times.
✗ Branch 9 → 25 not taken.
168628 symbolType = symbolType.getContained();
726 }
727 226879 }
728
729 632 llvm::GlobalVariable *IRGenerator::createGlobalConst(const std::string &baseName, llvm::Constant *constant) const {
730 // Get unused name
731
1/2
✓ Branch 2 → 3 taken 632 times.
✗ Branch 2 → 19 not taken.
632 const std::string globalName = getUnusedGlobalName(baseName);
732 // Create global
733
1/2
✓ Branch 5 → 6 taken 632 times.
✗ Branch 5 → 15 not taken.
632 module->getOrInsertGlobal(globalName, constant->getType());
734
1/2
✓ Branch 7 → 8 taken 632 times.
✗ Branch 7 → 16 not taken.
632 llvm::GlobalVariable *global = module->getNamedGlobal(globalName);
735 // Set initializer to the given constant
736
1/2
✓ Branch 8 → 9 taken 632 times.
✗ Branch 8 → 17 not taken.
632 global->setInitializer(constant);
737 632 global->setConstant(true);
738
1/2
✓ Branch 10 → 11 taken 632 times.
✗ Branch 10 → 17 not taken.
632 global->setLinkage(llvm::GlobalValue::PrivateLinkage);
739 632 global->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
740 632 return global;
741 632 }
742
743 27145 llvm::GlobalVariable *IRGenerator::createGlobalStringConst(const std::string &baseName, const std::string &value) const {
744 // Get unused name
745
1/2
✓ Branch 2 → 3 taken 27145 times.
✗ Branch 2 → 21 not taken.
27145 const std::string globalName = getUnusedGlobalName(baseName);
746 // Create global
747
2/4
✓ Branch 3 → 4 taken 27145 times.
✗ Branch 3 → 16 not taken.
✓ Branch 5 → 6 taken 27145 times.
✗ Branch 5 → 15 not taken.
27145 builder.CreateGlobalString(value, globalName, 0, module);
748
1/2
✓ Branch 7 → 8 taken 27145 times.
✗ Branch 7 → 17 not taken.
27145 llvm::GlobalVariable *global = module->getNamedGlobal(globalName);
749 // If the output should be comparable, fix alignment to 4 bytes
750
1/2
✓ Branch 8 → 9 taken 27145 times.
✗ Branch 8 → 12 not taken.
27145 if (cliOptions.comparableOutput)
751
2/4
✓ Branch 9 → 10 taken 27145 times.
✗ Branch 9 → 18 not taken.
✓ Branch 10 → 11 taken 27145 times.
✗ Branch 10 → 18 not taken.
27145 global->setAlignment(llvm::Align(4));
752 27145 return global;
753 27145 }
754
755 27145 llvm::GlobalVariable *IRGenerator::createGlobalStringConst(const std::string &baseName, const std::string &value,
756 const CodeLoc &codeLoc) const {
757 27145 llvm::GlobalVariable *global = createGlobalStringConst(baseName, value);
758 // Create debug info
759
2/2
✓ Branch 5 → 6 taken 283 times.
✓ Branch 5 → 12 taken 26862 times.
54290 if (cliOptions.instrumentation.emitsFullDebugInfo())
760
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);
761 27145 return global;
762 }
763
764 44097 std::string IRGenerator::getUnusedGlobalName(const std::string &baseName) const {
765 // Find an unused global name
766 44097 std::string globalName;
767 44097 unsigned int suffixNumber = 0;
768 do {
769
1/2
✓ Branch 5 → 6 taken 1667850 times.
✗ Branch 5 → 15 not taken.
1667850 globalName = baseName + std::to_string(suffixNumber);
770 1667850 suffixNumber++;
771
3/4
✓ Branch 10 → 11 taken 1667850 times.
✗ Branch 10 → 19 not taken.
✓ Branch 11 → 12 taken 1623753 times.
✓ Branch 11 → 13 taken 44097 times.
1667850 } while (module->getNamedGlobal(globalName) != nullptr);
772 44097 return globalName;
773 }
774
775 163378 void IRGenerator::materializeConstant(LLVMExprResult &exprResult) {
776 // Skip results, that do not contain a constant or already have a value
777
3/4
✓ Branch 2 → 3 taken 157456 times.
✓ Branch 2 → 4 taken 5922 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 157456 times.
163378 if (exprResult.value != nullptr || exprResult.constant == nullptr)
778 5922 return;
779
780 // Default case: the value to the constant
781 157456 exprResult.value = exprResult.constant;
782 }
783
784 105735 bool IRGenerator::isSymbolDSOLocal(bool isPublic) const {
785 // If we are compiling a shared library and export the global symbol, we need to drop dso_local
786 // because it may be interposed by other shared objects by the dynamic linker.
787
3/4
✓ Branch 2 → 3 taken 92759 times.
✓ Branch 2 → 4 taken 12976 times.
✓ Branch 3 → 4 taken 92759 times.
✗ Branch 3 → 5 not taken.
105735 return !(isPublic && cliOptions.outputContainer == OutputContainer::SHARED_LIBRARY);
788 }
789
790 93879 llvm::GlobalValue::LinkageTypes IRGenerator::getSymbolLinkageType(bool isPublic) const {
791
2/2
✓ Branch 2 → 3 taken 81101 times.
✓ Branch 2 → 4 taken 12778 times.
93879 return isPublic ? llvm::GlobalValue::ExternalLinkage : llvm::GlobalValue::InternalLinkage;
792 }
793
794 13782 llvm::GlobalValue::LinkageTypes IRGenerator::getVTableLinkageType(bool isPublic) const {
795 // VTables, type infos and type info names are ODR entities that may legitimately be emitted in more than one
796 // translation unit (e.g. an interface that is defined in one file and used from several importing files). Giving
797 // them weak ODR linkage lets the linker coalesce the duplicates. On ELF this pairs with the comdat group below; on
798 // MachO, which has no comdat support, the weak/coalesced linkage is what prevents a duplicate-symbol error.
799
2/2
✓ Branch 2 → 3 taken 13584 times.
✓ Branch 2 → 4 taken 198 times.
13782 return isPublic ? llvm::GlobalValue::WeakODRLinkage : llvm::GlobalValue::PrivateLinkage;
800 }
801
802 13782 void IRGenerator::attachComdatToSymbol(llvm::GlobalVariable *global, const std::string &comdatName, bool isPublic) const {
803 // MachO does not support comdat annotations
804
6/6
✓ Branch 2 → 3 taken 13584 times.
✓ Branch 2 → 6 taken 198 times.
✓ Branch 4 → 5 taken 13554 times.
✓ Branch 4 → 6 taken 30 times.
✓ Branch 7 → 8 taken 13554 times.
✓ Branch 7 → 12 taken 228 times.
13782 if (isPublic && cliOptions.targetTriple.getObjectFormat() != llvm::Triple::MachO)
805
2/4
✓ Branch 9 → 10 taken 13554 times.
✗ Branch 9 → 13 not taken.
✓ Branch 10 → 11 taken 13554 times.
✗ Branch 10 → 13 not taken.
13554 global->setComdat(module->getOrInsertComdat(comdatName));
806 13782 }
807
808 /**
809 * Attach the function attributes that all functions we emit have in common.
810 *
811 * Spice does not know exceptions and we never emit landing pads, so none of our functions can unwind. We still request
812 * an unwind table, so that debuggers and profilers are able to produce correct stack traces.
813 *
814 * Frame pointers are off by default and are requested per function by '--keep-frame-pointers'. This attribute is the
815 * only thing the backend reads. The 'frame-pointer' module flag that IRGenerator's constructor sets from the same
816 * option covers a different set of functions - Function::createWithDefaultAttr() stamps it onto the functions LLVM
817 * itself synthesizes, such as the sanitizer module ctors - so both have to be set, and the module flag alone changes
818 * nothing about the functions we emit: x86-64 Linux then omits the frame pointer at every optimization level, and
819 * AArch64 spills the frame record but never links the chain up.
820 *
821 * Nothing is emitted in the default case, rather than an explicit "none", which is exactly what LLVM does for its own
822 * synthesized functions. It also leaves the decision to the backend, so targets whose ABI mandates a frame pointer
823 * (e.g. AArch64 on Darwin) keep theirs.
824 *
825 * The size levels are not communicated to LLVM by the pass pipeline alone - since Os and Oz both select the O2
826 * pipeline, 'optsize' and 'minsize' on the individual function are what actually distinguishes them. Without 'minsize',
827 * Oz is indistinguishable from Os.
828 *
829 * @param fct Function to attach the attributes to
830 * @param isAlwaysInline Whether the function was declared as inline
831 */
832 88850 void IRGenerator::addCommonFctAttrs(llvm::Function *fct, bool isAlwaysInline) const {
833 88850 fct->addFnAttr(llvm::Attribute::NoUnwind);
834 88850 fct->addFnAttr(llvm::Attribute::getWithUWTableKind(context, llvm::UWTableKind::Default));
835
2/2
✓ Branch 5 → 6 taken 4 times.
✓ Branch 5 → 10 taken 88846 times.
88850 if (cliOptions.keepFramePointers)
836
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");
837
838 // Explicitly inlined functions must not be marked as 'optnone', because that is incompatible with 'alwaysinline'.
839 // This matches the behavior of other frontends: an inline request is honored, even at O0.
840
2/2
✓ Branch 10 → 11 taken 20032 times.
✓ Branch 10 → 12 taken 68818 times.
88850 if (isAlwaysInline) {
841 20032 fct->addFnAttr(llvm::Attribute::AlwaysInline);
842
2/2
✓ Branch 12 → 13 taken 68718 times.
✓ Branch 12 → 15 taken 100 times.
68818 } else if (cliOptions.optLevel == OptLevel::O0) {
843 68718 fct->addFnAttr(llvm::Attribute::OptimizeNone);
844 68718 fct->addFnAttr(llvm::Attribute::NoInline); // 'optnone' requires 'noinline'
845 }
846
847
2/2
✓ Branch 15 → 16 taken 4 times.
✓ Branch 15 → 19 taken 88846 times.
88850 if (cliOptions.optLevel >= OptLevel::Os) {
848 4 fct->addFnAttr(llvm::Attribute::OptimizeForSize);
849
2/2
✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 19 taken 2 times.
4 if (cliOptions.optLevel == OptLevel::Oz)
850 2 fct->addFnAttr(llvm::Attribute::MinSize);
851 }
852 88850 }
853
854 579210 llvm::Value *IRGenerator::getAddress(const SymbolTableEntry *entry) {
855
1/2
✓ Branch 2 → 3 taken 579210 times.
✗ Branch 2 → 18 not taken.
579210 const auto it = addressMap.find(entry);
856
5/6
✓ Branch 5 → 6 taken 577962 times.
✓ Branch 5 → 9 taken 1248 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 577962 times.
✓ Branch 11 → 12 taken 1248 times.
✓ Branch 11 → 13 taken 577962 times.
579210 if (it == addressMap.end() || it->second.empty())
857 1248 return nullptr;
858 577962 return it->second.top();
859 }
860
861 380090 void IRGenerator::updateAddress(const SymbolTableEntry *entry, llvm::Value *address) {
862
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 380090 times.
380090 assert(address != nullptr);
863
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 380090 times.
380090 assert(address->getType()->isPointerTy());
864 380090 auto &stack = addressMap[entry];
865
2/2
✓ Branch 10 → 11 taken 297692 times.
✓ Branch 10 → 12 taken 82398 times.
380090 if (stack.empty())
866 297692 stack.push(address);
867 else
868 82398 stack.top() = address;
869 380090 }
870
871 126 void IRGenerator::pushAddress(const SymbolTableEntry *entry, llvm::Value *address) {
872
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 126 times.
126 assert(address != nullptr);
873 126 addressMap[entry].push(address);
874 126 }
875
876 126 void IRGenerator::popAddress(const SymbolTableEntry *entry) {
877
1/2
✓ Branch 2 → 3 taken 126 times.
✗ Branch 2 → 14 not taken.
126 auto it = addressMap.find(entry);
878
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());
879 126 it->second.pop();
880 126 }
881
882 14794 llvm::Function *IRGenerator::getLLVMFunction(const Function *spiceFunc) {
883
1/2
✓ Branch 2 → 3 taken 14794 times.
✗ Branch 2 → 12 not taken.
14794 const auto it = llvmFunctions.find(spiceFunc);
884
2/2
✓ Branch 5 → 6 taken 5998 times.
✓ Branch 5 → 8 taken 8796 times.
29588 return it != llvmFunctions.end() ? it->second : nullptr;
885 }
886
887 85724 void IRGenerator::setLLVMFunction(const Function *spiceFunc, llvm::Function *llvmFunction) {
888
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 85724 times.
85724 assert(llvmFunction != nullptr);
889 85724 llvmFunctions[spiceFunc] = llvmFunction;
890 85724 }
891
892 11306 std::string IRGenerator::getIRString(llvm::Module *llvmModule, const CliOptions &cliOptions) {
893
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 11306 times.
11306 assert(llvmModule != nullptr); // Make sure the module hasn't been moved away
894
3/4
✓ Branch 4 → 5 taken 11306 times.
✗ Branch 4 → 7 not taken.
✓ Branch 5 → 6 taken 11094 times.
✓ Branch 5 → 7 taken 212 times.
11306 const bool eliminateTarget = cliOptions.comparableOutput && cliOptions.isNativeTarget;
895
896 // Backup target triple and data layout
897
1/2
✓ Branch 9 → 10 taken 11306 times.
✗ Branch 9 → 45 not taken.
11306 const llvm::Triple targetTriple = llvmModule->getTargetTriple();
898
1/2
✓ Branch 11 → 12 taken 11306 times.
✗ Branch 11 → 43 not taken.
11306 const std::string targetDataLayout = llvmModule->getDataLayoutStr();
899 // Remove target triple and data layout
900
2/2
✓ Branch 12 → 13 taken 11094 times.
✓ Branch 12 → 19 taken 212 times.
11306 if (eliminateTarget) {
901 11094 llvmModule->setTargetTriple(llvm::Triple());
902
2/4
✓ Branch 16 → 17 taken 11094 times.
✗ Branch 16 → 34 not taken.
✓ Branch 17 → 18 taken 11094 times.
✗ Branch 17 → 34 not taken.
11094 llvmModule->setDataLayout("");
903 }
904
905 // Get IR string
906 11306 std::string output;
907
1/2
✓ Branch 20 → 21 taken 11306 times.
✗ Branch 20 → 39 not taken.
11306 llvm::raw_string_ostream oss(output);
908
1/2
✓ Branch 21 → 22 taken 11306 times.
✗ Branch 21 → 37 not taken.
11306 llvmModule->print(oss, nullptr);
909
910 // Restore target triple and data layout
911
2/2
✓ Branch 22 → 23 taken 11094 times.
✓ Branch 22 → 29 taken 212 times.
11306 if (eliminateTarget) {
912
1/2
✓ Branch 23 → 24 taken 11094 times.
✗ Branch 23 → 35 not taken.
11094 llvmModule->setTargetTriple(targetTriple);
913
1/2
✓ Branch 27 → 28 taken 11094 times.
✗ Branch 27 → 36 not taken.
11094 llvmModule->setDataLayout(targetDataLayout);
914 }
915
916 11306 return output;
917 11306 }
918
919 /**
920 * Returns the operator function list for the current manifestation and the given node
921 *
922 * @param node Node to retrieve the op fct pointer list from
923 * @return Op fct pointer list
924 */
925 188548 const std::vector<const Function *> &IRGenerator::getOpFctPointers(const ASTNode *node) const {
926
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 188548 times.
188548 assert(node->getOpFctPointers()->size() > manIdx);
927 188548 return node->getOpFctPointers()->at(manIdx);
928 }
929
930 } // namespace spice::compiler
931