GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
4243 / 4509, 38 excl
94.1%
Branches:
1396 / 1608, 502 excl
86.8%
Functions:
234 / 242
96.7%

irgenerator/ir-generator.spice
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 // Std imports
4 import "std/type/any";
5 import "std/data/vector";
6 import "std/data/stack";
7 import "std/data/unordered-map";
8 import "std/data/pair";
9 import "std/text/stringstream";
10 import "std/type/type-conversion";
11 import "std/type/types";
12 import "std/bindings/llvm/llvm" as llvm;
13
14 // Own imports
15 import "@/exception/fatal-error";
16 import "@/compiler-pass";
17 import "@/driver";
18 import "@/source-file";
19 import "@/ast/ast-nodes";
20 import "@/ast/attributes";
21 import "@/ast/abstract-ast-visitor-intf";
22 import "@/exception/compiler-error";
23 import "@/exception/semantic-error";
24 import "@/global/global-resource-manager";
25 import "@/global/runtime-module-manager";
26 import "@/irgenerator/abi-info";
27 import "@/irgenerator/debug-info-generator";
28 import "@/irgenerator/llvm-expr-result";
29 import "@/irgenerator/metadata-generator";
30 import "@/irgenerator/name-mangling";
31 import "@/irgenerator/op-rule-conversion-manager";
32 import "@/irgenerator/std-function-manager";
33 import "@/model/function" as model; // Aliased, because Function clashes with llvm::Function
34 import "@/model/generic-type";
35 import "@/model/interface";
36 import "@/model/struct";
37 import "@/model/struct-base";
38 import "@/reader/code-loc";
39 import "@/symboltablebuilder/capture";
40 import "@/symboltablebuilder/qual-type";
41 import "@/symboltablebuilder/scope";
42 import "@/symboltablebuilder/scope-handle";
43 import "@/symboltablebuilder/super-type";
44 import "@/symboltablebuilder/symbol-table";
45 import "@/symboltablebuilder/symbol-table-entry";
46 import "@/symboltablebuilder/type-chain";
47 import "@/symboltablebuilder/type-qualifiers";
48 import "@/typechecker/builtin-functions";
49 import "@/typechecker/function-manager";
50
51 // Constants
52 public const string ANON_GLOBAL_STRING_NAME = "anon.string.";
53 public const string ANON_GLOBAL_ARRAY_NAME = "anon.array.";
54 public const string CAPTURES_PARAM_NAME = "captures";
55 const int EXIT_FAILURE = 1;
56
57 // String placeholders for builtin testing output
58 const string TEST_ALL_START_MSG = "[==========] Running %d test(s) from %d source file(s)\n";
59 const string TEST_ALL_END_MSG = "[==========] Ran %d test(s) from %d source file(s)\n";
60 const string TEST_FILE_START_MSG = "[----------] Running %d test(s) from %s\n";
61 const string TEST_FILE_END_MSG = "[----------] Ran %d test(s) from %s\n\n";
62 const string TEST_CASE_RUN_MSG = "[ RUN ] %s\n";
63 const string TEST_CASE_SUCCESS_MSG = "\033[1m\033[32m[ PASSED ]\033[0m\033[22m %s\n";
64 const string TEST_CASE_FAILED_MSG = "\033[1m\033[31m[ FAILED ]\033[0m\033[22m %s\n";
65 const string TEST_CASE_SKIPPED_MSG = "\033[1m\033[33m[ SKIPPED ]\033[0m\033[22m %s\n";
66
67 /**
68 * Get the producer string, that is attached to the generated modules
69 *
70 * @return Producer string
71 */
72 3622 public f<String> getProducerString() {
73 3622 const string version = __get_build_var<string>("SPICE_VERSION", "dev");
74 7244 return String("spice version ") + version + " (https://github.com/spicelang/spice)";
75 3622 }
76
77 public type Likelihood enum {
78 UNSPECIFIED,
79 LIKELY,
80 UNLIKELY
81 }
82
83 3499 public type CommonLLVMTypes struct {
84 public llvm::Type lambdaFatPtrType
85 }
86
87 // A break/continue target: the scope to run cleanup (dtor calls) up to (inclusive) before jumping, since the
88 // jump skips the normal fall-through cleanup of enclosing scopes, paired with the block to jump to.
89 public type BreakContinueTarget struct {
90 public Scope* scope
91 public llvm::BasicBlock block
92 }
93
94 // Name and symbol table entry of a parameter
95 226113 public type ParamInfo struct {
96 public String name
97 public const SymbolTableEntry* entry
98 }
99
100 public type ParamInfoList alias Vector<ParamInfo>;
101
102 /**
103 * Generates the LLVM IR of a source file.
104 *
105 * Spice requires all methods of a struct to live in the file of the struct, so this file is split into sections, that are
106 * named after the generated constructs (top level definitions, statements, expressions, ...).
107 *
108 * Notes:
109 * - The LLVM C API offers no way to set the dso_local flag of global values, so the generated IR has no dso_local markers.
110 * This does not change the generated code, since the target machine assumes dso_local for all symbols of the executable.
111 * - Implicit functions and procedures are generated by a prologue and an epilogue, that surround the body generation at the
112 * call site.
113 * - If an instruction takes two operands, which both emit IR (e.g. the lhs and rhs of an addition), the rhs is emitted first.
114 */
115 3499 public type IRGenerator struct : IAbstractAstVisitor {
116 compose public CompilerPass compilerPass
117 public llvm::LLVMContext* context
118 public llvm::IRBuilder* builder
119 public llvm::Module* module
120 ABIInfo abiInfo
121 public OpRuleConversionManager conversionManager
122 public StdFunctionManager stdFunctionManager
123 public DebugInfoGenerator diGenerator
124 public MetadataGenerator mdGenerator
125 public CommonLLVMTypes llvmTypes
126 Vector<BreakContinueTarget> breakTargets
127 Vector<BreakContinueTarget> continueTargets
128 Stack<llvm::BasicBlock> fallthroughBlocks
129 llvm::BasicBlock allocaInsertBlock
130 llvm::Value allocaInsertInst
131 3499 bool blockAlreadyTerminated = false
132 3499 bool isInCtorBody = false
133 Vector<const StructBase*> deferredVTableInitializations
134 // IR-side state: separate from semantic objects to keep the type-checker model clean. Both maps are keyed by the address of
135 // the symbol table entry / function, since pointers cannot be hashed.
136 UnorderedMap<long, Stack<llvm::Value>> addressMap
137 UnorderedMap<long, llvm::Function> llvmFunctions
138 }
139
140 3499 public p IRGenerator.ctor(GlobalResourceManager& resourceManager, SourceFile* sourceFile) {
141 3499 this.compilerPass = CompilerPass(resourceManager, sourceFile);
142 3499 llvm::LLVMContext& llvmContext = sourceFile.getLLVMContext();
143 3499 this.context = &llvmContext;
144 3499 this.builder = &sourceFile.builder;
145 3499 this.module = &sourceFile.llvmModule;
146 3499 this.abiInfo = ABIInfo(this.cliOptions.targetTriple, this.module, this.context);
147 3499 this.stdFunctionManager = StdFunctionManager(sourceFile, this.module, this);
148 3499 this.conversionManager = OpRuleConversionManager(this);
149 3499 this.diGenerator = DebugInfoGenerator(this);
150 3499 this.mdGenerator = MetadataGenerator(this);
151
152 // Attach information to the module
153 3499 this.module.setTargetTriple(this.cliOptions.targetTriple);
154 3499 this.module.setDataLayout(sourceFile.targetMachine.createDataLayout());
155
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3498 times.
3499 if this.cliOptions.outputContainer == OutputContainer::SHARED_LIBRARY {
156 1 this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MIN, "PIC Level", 1); // llvm::PICLevel::SmallPIC
157 1 this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MAX, "PIE Level", 0); // llvm::PIELevel::Default
158 1 } else {
159 3498 this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MIN, "PIC Level", 2); // llvm::PICLevel::BigPIC
160 3498 this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MAX, "PIE Level", 2); // llvm::PIELevel::Large
161 3498 }
162 3499 this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MAX, "uwtable", 2); // llvm::UWTableKind::Default
163 // llvm::FramePointerKind::All or llvm::FramePointerKind::None
164 3499 this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MAX, "frame-pointer", this.cliOptions.keepFramePointers ? 2 : 0);
165
166 // Add module identifier metadata
167 3499 this.module.addNamedStringMetadata("llvm.ident", getProducerString());
168
169 // Initialize common LLVM types
170 3499 Vector<llvm::Type> fatPtrFieldTypes;
171 3499 fatPtrFieldTypes.pushBack(this.builder.getPtrTy());
172 3499 fatPtrFieldTypes.pushBack(this.builder.getPtrTy());
173 3499 fatPtrFieldTypes.pushBack(this.builder.getInt64Ty());
174 3499 this.llvmTypes.lambdaFatPtrType = llvm::getStructType(*this.context, fatPtrFieldTypes);
175
176 // Initialize debug info generator
177
2/2
✓ Branch 0 taken 3376 times.
✓ Branch 1 taken 123 times.
3499 if this.cliOptions.instrumentation.emitsDebugInfo() {
178 123 this.diGenerator.initialize(sourceFile.fileName, sourceFile.fileDir);
179 123 }
180 3499 }
181
182 // ============================================================= Visit ===========================================================
183
184 // Visit a node, which evaluates to an expression result
185 552915 f<LLVMExprResult> IRGenerator.visitExpr(IVisitable* node) {
186 552915 Any visitResult = this.visit(node);
187 1105830 return visitResult.get<LLVMExprResult>();
188 552915 }
189
190 // Visit a node, which evaluates to a constant (e.g. a constant node)
191 78074 f<llvm::Value> IRGenerator.visitConst(IVisitable* node) {
192 78074 Any visitResult = this.visit(node);
193 156148 return visitResult.get<llvm::Value>();
194 78074 }
195
196 // Visit a node, which evaluates to a type (e.g. a data type node)
197 10048 f<llvm::Type> IRGenerator.visitType(IVisitable* node) {
198 10048 Any visitResult = this.visit(node);
199 20096 return visitResult.get<llvm::Type>();
200 10048 }
201
202 // Return value of a visit method, that evaluates to an expression result
203 615342 f<Any> exprResultAny(const LLVMExprResult& exprResult) {
204 615342 return Any(exprResult);
205 }
206
207 // Get the evaluated symbol type of an expression node, that is stored as IVisitable*
208 723193 f<QualType> IRGenerator.getEvaluatedType(IVisitable* node) {
209 723193 ASTExprNode* exprNode = asExprNode(getASTNode(node));
210 723193 return exprNode.getEvaluatedSymbolType(this.manIdx);
211 }
212
213 // Check if an expression node, that is stored as IVisitable*, has a compile time value
214 9792 f<bool> IRGenerator.hasCompileTimeValue(IVisitable* node) {
215 9792 ASTNode* astNode = getASTNode(node);
216 9792 return astNode.hasCompileTimeValue(this.manIdx);
217 }
218
219 // Get the compile time value of an expression node, that is stored as IVisitable*
220 3822 f<CompileTimeValue> IRGenerator.getCompileTimeValue(IVisitable* node) {
221 3822 ASTNode* astNode = getASTNode(node);
222 3822 return astNode.getCompileTimeValue(this.manIdx);
223 }
224
225 507170 f<long> getEntryKey(const SymbolTableEntry* entry) {
226 unsafe {
227 507170 return cast<long>(entry);
228 }
229 }
230
231 46641 f<long> getFunctionKey(const model::Function* function) {
232 unsafe {
233 46641 return cast<long>(function);
234 }
235 }
236
237 // Abort the compilation with a compiler error
238 ✗ p raiseCompilerError(const CodeLoc& codeLoc, CompilerErrorType errorType, const String& message) {
239 ✗ const CompilerError compilerError = CompilerError(codeLoc, errorType, message.getRaw());
240 ✗ abortWithError(compilerError.getMessage());
241 ✗ }
242
243 // Abort the compilation with a semantic error
244 ✗ p raiseSemanticError(const ASTNode* node, SemanticErrorType errorType, const String& message) {
245 ✗ const SemanticError semanticError = SemanticError(node, errorType, message.getRaw());
246 ✗ abortWithError(semanticError.getMessage());
247 ✗ }
248
249 // Integer constants. The values are masked to the bit width, so that negative values of signed types are represented
250 // correctly.
251 8874 f<llvm::Value> IRGenerator.getInt8(long value) {
252 8874 return llvm::getConstInt(this.builder.getInt8Ty(), cast<unsigned long>(value) & 255ul);
253 }
254
255 2253 f<llvm::Value> IRGenerator.getInt16(long value) {
256 2253 return llvm::getConstInt(this.builder.getInt16Ty(), cast<unsigned long>(value) & 65535ul);
257 }
258
259 107383 f<llvm::Value> IRGenerator.getInt32(long value) {
260 107383 return llvm::getConstInt(this.builder.getInt32Ty(), cast<unsigned long>(value) & 4294967295ul);
261 }
262
263 123315 f<llvm::Value> IRGenerator.getInt64(long value) {
264 123315 return llvm::getConstInt(this.builder.getInt64Ty(), cast<unsigned long>(value));
265 }
266
267 7639 f<llvm::Value> IRGenerator.getInt1(bool value) {
268
2/2
✓ Branch 0 taken 2228 times.
✓ Branch 1 taken 5411 times.
7639 return value ? this.builder.getTrue() : this.builder.getFalse();
269 }
270
271 // ======================================================== IR generator =========================================================
272
273 3499 public f<Any> IRGenerator.visitEntry(ASTEntryNode* node) {
274 // Generate IR
275 3499 this.visitChildren(cast<ASTNode*>(node));
276
277 // Generate test main if required
278
4/4
✓ Branch 0 taken 2739 times.
✓ Branch 1 taken 760 times.
✓ Branch 2 taken 3490 times.
✓ Branch 3 taken 9 times.
3499 if this.sourceFile.isMainFile && this.cliOptions.generateTestMain {
279 9 this.generateTestMain();
280 9 }
281
282 // Execute deferred VTable initializations
283
2/2
✓ Branch 0 taken 3499 times.
✓ Branch 1 taken 1554 times.
5053 foreach const StructBase* spiceStruct : this.deferredVTableInitializations {
284 1554 this.generateVTableInitializer(spiceStruct);
285 1554 }
286
287 // Finalize debug info generator
288 3499 this.diGenerator.finalize();
289
290 // Verify module
291 3499 this.verifyModule(node.codeLoc);
292
293 6998 return Any();
294 3499 }
295
296 128764 public f<llvm::Value> IRGenerator.insertAlloca(llvm::Type llvmType, const String& varName = String()) {
297
4/4
✓ Branch 0 taken 22282 times.
✓ Branch 1 taken 776 times.
✓ Branch 2 taken 62233 times.
✓ Branch 3 taken 43473 times.
128764 if !this.allocaInsertInst.isNull() { // If there is already an alloca inst, insert right after that
298 // The alloca belongs after the last alloca. The LLVM C API cannot move instructions, so the alloca is created at its
299 // final position right away
300 // Positioning the builder before an instruction also takes over its debug location, so save and restore it
301 84515 const llvm::BasicBlock currentBlock = this.builder.getInsertBlock();
302 84515 const llvm::Metadata currentDebugLoc = this.builder.getCurrentDebugLocation();
303 84515 const llvm::Value nextInst = this.allocaInsertInst.getNextInstruction();
304
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 22275 times.
✓ Branch 2 taken 636 times.
✓ Branch 3 taken 61597 times.
84515 if nextInst.isNull() {
305 643 this.builder.setInsertPoint(this.allocaInsertBlock);
306 643 } else {
307 83872 this.builder.setInsertPointBefore(this.allocaInsertBlock, nextInst);
308 83872 }
309 84515 llvm::Value allocaInst = this.builder.createAlloca(llvmType, varName.getRaw());
310 84515 allocaInst.dropLocation(); // Part of prologue
311 84515 this.allocaInsertInst = allocaInst;
312 84515 this.builder.setInsertPoint(currentBlock);
313 84515 this.builder.setCurrentDebugLocation(currentDebugLoc);
314 84515 } else { // This is the first alloca inst in the current function -> insert at the entry block
315 // Save current basic block and debug location and move insert cursor to entry block of the current function
316 44249 const llvm::BasicBlock currentBlock = this.builder.getInsertBlock();
317 44249 const llvm::Metadata currentDebugLoc = this.builder.getCurrentDebugLocation();
318 44249 const llvm::Value firstInst = this.allocaInsertBlock.getFirstInstruction();
319
4/4
✓ Branch 0 taken 739 times.
✓ Branch 1 taken 37 times.
✓ Branch 2 taken 43393 times.
✓ Branch 3 taken 80 times.
44249 if firstInst.isNull() {
320 44132 this.builder.setInsertPoint(this.allocaInsertBlock);
321 44132 } else {
322 117 this.builder.setInsertPointBefore(this.allocaInsertBlock, firstInst);
323 117 }
324
325 // Allocate the size of the given LLVM type
326 44249 this.allocaInsertInst = this.builder.createAlloca(llvmType, varName.getRaw());
327 44249 this.allocaInsertInst.dropLocation(); // Part of prologue
328
329 // Restore old basic block and debug location
330 44249 this.builder.setInsertPoint(currentBlock);
331 44249 this.builder.setCurrentDebugLocation(currentDebugLoc);
332 44249 }
333
334 // Insert lifetime start marker
335
4/4
✓ Branch 0 taken 22457 times.
✓ Branch 1 taken 601 times.
✓ Branch 2 taken 102443 times.
✓ Branch 3 taken 3263 times.
128764 if this.cliOptions.useLifetimeMarkers {
336 3864 this.createLifetimeMarker("llvm.lifetime.start", this.allocaInsertInst);
337 3864 }
338
339 151822 return this.allocaInsertInst;
340 23058 }
341
342 102792 public f<llvm::Value> IRGenerator.insertAlloca(const QualType& qualType, const String& varName = String()) {
343 102792 const llvm::Type llvmType = qualType.toLLVMType(this.sourceFile);
344 102792 const llvm::Value alloca = this.insertAlloca(llvmType, varName);
345
346 // Insert type metadata
347
3/4
✓ Branch 0 taken 27020 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 75760 times.
✗ Branch 3 not taken.
102792 if this.cliOptions.useTBAAMetadata {
348 12 this.mdGenerator.generateTypeMetadata(this.allocaInsertInst, qualType);
349 12 }
350
351 129824 return alloca;
352 27032 }
353
354 // Like builder.CreateLifetimeStart/CreateLifetimeEnd
355 5322 p IRGenerator.createLifetimeMarker(string intrinsicName, llvm::Value ptr) {
356 5322 Vector<llvm::Type> overloadTypes;
357 5322 overloadTypes.pushBack(this.builder.getPtrTy());
358 5322 const llvm::Function markerFct = this.module.getIntrinsicDeclaration(intrinsicName, overloadTypes);
359 5322 Vector<llvm::Value> args;
360 5322 args.pushBack(ptr);
361 5322 this.builder.createCall(markerFct, args);
362 5322 }
363
364 333216 public f<llvm::Value> IRGenerator.insertLoad(llvm::Type llvmType, llvm::Value ptr, bool isVolatile = false, const String& varName = String()) {
365 333216 const llvm::Type ptrType = ptr.getType();
366 333216 assert ptrType.isPointerTy();
367 377415 return this.builder.createLoad(ptr, llvmType, varName.getRaw(), isVolatile);
368 44199 }
369
370 288559 public f<llvm::Value> IRGenerator.insertLoad(const QualType& qualType, llvm::Value ptr, bool isVolatile = false, const String& varName = String()) {
371 288559 const llvm::Type llvmType = qualType.toLLVMType(this.sourceFile);
372 288559 const llvm::Value load = this.insertLoad(llvmType, ptr, isVolatile, varName);
373
3/6
✓ Branch 0 taken 85749 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 202798 times.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
288559 if this.cliOptions.useTBAAMetadata {
374 12 this.mdGenerator.generateTBAAMetadata(load, qualType);
375 12 }
376 577118 return load;
377 288559 }
378
379 166893 #[ignoreUnusedReturnValue]
380 166893 public f<llvm::Value> IRGenerator.insertStore(llvm::Value val, llvm::Value ptr, bool isVolatile = false) {
381 166893 const llvm::Type ptrType = ptr.getType();
382 166893 assert ptrType.isPointerTy();
383 166893 return this.builder.createStore(val, ptr, isVolatile);
384 }
385
386 50417 public p IRGenerator.insertStore(llvm::Value val, llvm::Value ptr, const QualType& qualType, bool isVolatile = false) {
387 50417 const llvm::Value store = this.insertStore(val, ptr, isVolatile);
388
2/4
✓ Branch 0 taken 50405 times.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
50417 if this.cliOptions.useTBAAMetadata {
389 12 this.mdGenerator.generateTBAAMetadata(store, qualType);
390 12 }
391 50417 }
392
393 93553 public f<llvm::Value> IRGenerator.insertInBoundsGEP(llvm::Type ty, llvm::Value basePtr, const Vector<llvm::Value>& indices, const String& varName = String()) {
394 93553 const llvm::Type basePtrType = basePtr.getType();
395 93553 assert basePtrType.isPointerTy();
396 93553 assert !indices.isEmpty();
397
4/4
✓ Branch 0 taken 6791 times.
✓ Branch 1 taken 2810 times.
✓ Branch 2 taken 171344 times.
✓ Branch 3 taken 90743 times.
271688 foreach const llvm::Value& index : indices {
398 178135 const llvm::Type indexType = index.getType();
399 178135 assert indexType.isIntegerTy(32) || indexType.isIntegerTy(64);
400 178135 }
401
402 // Insert GEP
403 96363 return this.builder.createInBoundsGEP(ty, basePtr, indices, varName.getRaw());
404 2810 }
405
406 10162 public f<llvm::Value> IRGenerator.insertInBoundsGEP(llvm::Type ty, llvm::Value basePtr, llvm::Value index, const String& varName = String()) {
407 10162 Vector<llvm::Value> indices;
408 10162 indices.pushBack(index);
409 20324 return this.insertInBoundsGEP(ty, basePtr, indices, varName);
410 10162 }
411
412 27114 public f<llvm::Value> IRGenerator.insertStructGEP(llvm::Type ty, llvm::Value basePtr, unsigned int index, const String& varName = String()) {
413 27114 const llvm::Type basePtrType = basePtr.getType();
414 27114 assert basePtrType.isPointerTy();
415
416 // If we use index 0 we can use the base pointer directly
417
4/4
✓ Branch 0 taken 19136 times.
✓ Branch 1 taken 7735 times.
✓ Branch 2 taken 119 times.
✓ Branch 3 taken 124 times.
27114 if index == 0 {
418 15594 return basePtr;
419 }
420
421 // Insert GEP
422 38391 return this.builder.createStructGEP(ty, basePtr, index, varName.getRaw());
423 26871 }
424
425 187458 #[ignoreUnusedReturnValue]
426 187458 public f<llvm::Value> IRGenerator.resolveValue(IVisitable* node) {
427 // Visit the given AST node
428 187458 LLVMExprResult exprResult = this.visitExpr(node);
429 187458 return this.resolveValue(node, exprResult);
430 }
431
432 /**
433 * Resolve the value of an expression in its expression scope, if it has one, and destruct the temporaries of the expression right
434 * afterwards. The value is already loaded at that point, so it does not depend on the temporaries anymore.
435 *
436 * @param expr Expression to resolve the value of
437 * @return Value of the expression
438 */
439 39531 public f<llvm::Value> IRGenerator.resolveValueInExprScope(IVisitable* expr) {
440 39531 ExprScopeHandle exprScopeHandle = ExprScopeHandle(&this.compilerPass, expr);
441 39531 const llvm::Value value = this.resolveValue(expr);
442 39531 const Scope* exprScope = exprScopeHandle.getExprScope();
443
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 39519 times.
39531 if exprScope != nil<Scope*> {
444 39519 this.generateTemporariesCleanup(exprScope, getASTNode(expr));
445 39519 }
446 79062 return value;
447 39531 }
448
449 209990 #[ignoreUnusedReturnValue]
450 209990 public f<llvm::Value> IRGenerator.resolveValue(IVisitable* node, LLVMExprResult& exprResult) {
451 209990 return this.resolveValue(this.getEvaluatedType(node), exprResult);
452 }
453
454 414754 #[ignoreUnusedReturnValue]
455 414754 public f<llvm::Value> IRGenerator.resolveValue(const QualType& qualType, LLVMExprResult& exprResult) {
456 // Check if the value is already present
457
2/2
✓ Branch 0 taken 127469 times.
✓ Branch 1 taken 287285 times.
414754 if !exprResult.value.isNull() {
458 127469 return exprResult.value;
459 }
460
461 // Check if a constant is present
462
2/2
✓ Branch 0 taken 84475 times.
✓ Branch 1 taken 202810 times.
287285 if !exprResult.constant.isNull() {
463 84475 materializeConstant(exprResult);
464 84475 return exprResult.value;
465 }
466
467 202810 assert !exprResult.ptr.isNull() || !exprResult.refPtr.isNull();
468
469 // De-reference if reference type
470
2/2
✓ Branch 0 taken 12864 times.
✓ Branch 1 taken 189946 times.
202810 const bool isVolatile = exprResult.entry != nil<const SymbolTableEntry*> && exprResult.entry.isVolatile;
471
4/4
✓ Branch 0 taken 200160 times.
✓ Branch 1 taken 2650 times.
✓ Branch 2 taken 200174 times.
✓ Branch 3 taken 2636 times.
202810 if !exprResult.refPtr.isNull() && exprResult.ptr.isNull() {
472 2636 exprResult.ptr = this.insertLoad(this.builder.getPtrTy(), exprResult.refPtr, isVolatile);
473 2636 }
474
475 // Load the value from the pointer
476 202810 const QualType referencedType = qualType.removeReferenceWrapper();
477 202810 exprResult.value = this.insertLoad(referencedType, exprResult.ptr, isVolatile);
478
479 202810 return exprResult.value;
480 }
481
482 16813 #[ignoreUnusedReturnValue]
483 16813 public f<llvm::Value> IRGenerator.resolveAddress(IVisitable* node) {
484 // Visit the given AST node
485 16813 LLVMExprResult exprResult = this.visitExpr(node);
486 16813 return this.resolveAddress(exprResult);
487 }
488
489 141191 #[ignoreUnusedReturnValue]
490 141191 public f<llvm::Value> IRGenerator.resolveAddress(LLVMExprResult& exprResult) {
491 // Check if an address is already present
492
2/2
✓ Branch 0 taken 119032 times.
✓ Branch 1 taken 22159 times.
141191 if !exprResult.ptr.isNull() {
493 119032 return exprResult.ptr;
494 }
495
496 // Check if the reference address is already present
497
2/2
✓ Branch 0 taken 2389 times.
✓ Branch 1 taken 19770 times.
22159 const bool isVolatile = exprResult.entry != nil<const SymbolTableEntry*> && exprResult.entry.isVolatile;
498
4/4
✓ Branch 0 taken 1632 times.
✓ Branch 1 taken 20527 times.
✓ Branch 2 taken 20527 times.
✓ Branch 3 taken 1632 times.
22159 if !exprResult.refPtr.isNull() && exprResult.ptr.isNull() {
499 20527 exprResult.ptr = this.insertLoad(this.builder.getPtrTy(), exprResult.refPtr, isVolatile);
500 20527 return exprResult.ptr;
501 }
502
503 // If not, store the value or constant
504 1632 materializeConstant(exprResult);
505 1632 assert !exprResult.value.isNull();
506
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1628 times.
1632 const String varName = exprResult.entry != nil<const SymbolTableEntry*> ? exprResult.entry.name : String();
507 1632 exprResult.ptr = this.insertAlloca(exprResult.value.getType(), varName);
508 1632 this.insertStore(exprResult.value, exprResult.ptr, isVolatile);
509
510 3264 return exprResult.ptr;
511 1632 }
512
513 // Resolve a resolver, see Resolver
514 9790 public f<llvm::Value> IRGenerator.resolve(const Resolver& resolver) {
515
5/7
✗ Branch 0 not taken.
✓ Branch 1 taken 1341 times.
✓ Branch 2 taken 6847 times.
✓ Branch 3 taken 896 times.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 678 times.
9790 switch resolver.kind {
516 ✗ case ResolverKind::NONE: { return llvm::Value(); }
517 1341 case ResolverKind::VALUE: { return this.resolveValue(resolver.qualType, *resolver.exprResult); }
518 6847 case ResolverKind::ADDRESS: { return this.resolveAddress(*resolver.exprResult); }
519 896 case ResolverKind::EXPR_VALUE: { return this.resolveValue(resolver.expr); }
520 28 case ResolverKind::EXPR_ADDRESS: { return this.resolveAddress(resolver.expr); }
521 678 case ResolverKind::FIXED: { return resolver.fixedValue; }
522 }
523 ✗ }
524
525 /**
526 * Pack a scalar compile-time constant (integer, float, or a null pointer/aggregate) into a raw byte-array constant of
527 * the given type, using the target's endianness. This is used to embed a union's default field value into its
528 * byte-buffer payload without needing a bitcast between unrelated LLVM types (which is not something opaque-pointer-
529 * era LLVM supports between arbitrary aggregate/scalar types).
530 *
531 * Non-null pointer-shaped constants (e.g. a string literal's global address) cannot be represented this way, since
532 * their real value is only known at link/load time (a relocation) and a plain byte array cannot carry one.
533 *
534 * @param value Compile-time constant to pack
535 * @param byteArrayType Target byte-array type (element type i8)
536 * @return Packed byte-array constant
537 */
538 2 f<llvm::Value> IRGenerator.packConstantAsByteArray(llvm::Value value, llvm::Type byteArrayType) {
539 2 const llvm::Type elementType = byteArrayType.getArrayElementType();
540 2 assert elementType.isIntegerTy(8);
541 2 const unsigned long numBytes = byteArrayType.getArrayNumElements();
542
543 // A null/zero constant (e.g. a null pointer, or a zero-initialized aggregate) packs trivially as all-zero bytes.
544
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if value.isNullValue() {
545 ✗ return llvm::getNullValue(byteArrayType);
546 }
547
548 // Spice constants have at most 64 bits, so this works on the plain 64 bit value instead of an arbitrary precision integer.
549 2 unsigned long bits = 0l;
550
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if value.isConstantInt() {
551 2 bits = value.getZExtValue();
552
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
2 } else if value.isConstantFP() {
553 ✗ const double doubleValue = value.getDoubleValue();
554 unsafe {
555 ✗ const unsigned long* bitsPtr = cast<unsigned long*>(&doubleValue);
556 ✗ bits = *bitsPtr;
557 }
558 ✗ } else {
559 // See the function comment: this is a rare case in practice (most union field defaults are numeric), so it is
560 // deliberately unsupported for now rather than silently producing a wrong default value.
561 − panic(Error("Unsupported default value for a union field of this type")); // LCOV_EXCL_LINE
562 ✗ }
563
564 2 const bool isLittleEndian = this.module.isLittleEndian();
565 2 Vector<char> bytes;
566
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
18 for unsigned long i = 0l; i < numBytes; i++ {
567 16 bytes.pushBack('\0');
568 16 }
569
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
18 for unsigned long i = 0l; i < numBytes; i++ {
570
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 const unsigned long byteIndex = isLittleEndian ? i : numBytes - 1l - i;
571
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 const unsigned long byteValue = i < 8l ? (bits >> (i * 8l)) & 255ul : 0ul;
572 16 char& byteSlot = bytes.get(byteIndex);
573 16 byteSlot = cast<char>(byteValue);
574 16 }
575
576 4 return llvm::getConstByteArray(*this.context, bytes);
577 2 }
578
579 7539 public f<llvm::Value> IRGenerator.getDefaultValueForSymbolType(const QualType& symbolType) {
580 // Double
581
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 7485 times.
7539 if symbolType.is(SuperType::TY_DOUBLE) {
582 54 return llvm::getConstDouble(this.builder.getDoubleTy(), 0.0);
583 }
584
585 // Int
586
2/2
✓ Branch 0 taken 1430 times.
✓ Branch 1 taken 6055 times.
7485 if symbolType.is(SuperType::TY_INT) {
587 1430 return this.getInt32(0l);
588 }
589
590 // Short
591
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 6021 times.
6055 if symbolType.is(SuperType::TY_SHORT) {
592 34 return this.getInt16(0l);
593 }
594
595 // Long
596
2/2
✓ Branch 0 taken 1224 times.
✓ Branch 1 taken 4797 times.
6021 if symbolType.is(SuperType::TY_LONG) {
597 1224 return this.getInt64(0l);
598 }
599
600 // Byte or char
601
4/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 4761 times.
✓ Branch 2 taken 117 times.
✓ Branch 3 taken 4680 times.
4797 if symbolType.is(SuperType::TY_BYTE) || symbolType.is(SuperType::TY_CHAR) {
602 117 return this.getInt8(0l);
603 }
604
605 // String
606
2/2
✓ Branch 0 taken 1234 times.
✓ Branch 1 taken 3446 times.
4680 if symbolType.is(SuperType::TY_STRING) {
607 1234 llvm::Value globalString = this.module.createGlobalString(String(""), "");
608
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1234 times.
1234 if this.cliOptions.comparableOutput {
609 1234 globalString.setAlignment(4);
610 1234 }
611 2468 return globalString;
612 1234 }
613
614 // Bool
615
2/2
✓ Branch 0 taken 216 times.
✓ Branch 1 taken 3230 times.
3446 if symbolType.is(SuperType::TY_BOOL) {
616 216 return this.builder.getFalse();
617 }
618
619 // Pointer or reference
620
4/4
✓ Branch 0 taken 1507 times.
✓ Branch 1 taken 1723 times.
✓ Branch 2 taken 2225 times.
✓ Branch 3 taken 1005 times.
3230 if symbolType.is(SuperType::TY_PTR) || symbolType.is(SuperType::TY_REF) {
621 2225 return llvm::getNullValue(this.builder.getPtrTy());
622 }
623
624 // Array
625
2/2
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 876 times.
1005 if symbolType.isArray() {
626 // Get array size
627 129 const unsigned long arraySize = cast<unsigned long>(symbolType.getArraySize());
628
629 // Get default value for item
630 129 const QualType itemSymbolType = symbolType.getContained();
631 129 const llvm::Value defaultItemValue = this.getDefaultValueForSymbolType(itemSymbolType);
632
633 // Retrieve array and item type
634 129 const llvm::Type itemType = itemSymbolType.toLLVMType(this.sourceFile);
635
636 // Create a constant array with n times the default value
637 129 Vector<llvm::Value> itemConstants;
638
2/2
✓ Branch 0 taken 120573 times.
✓ Branch 1 taken 129 times.
120702 for unsigned long i = 0l; i < arraySize; i++ {
639 120573 itemConstants.pushBack(defaultItemValue);
640 120573 }
641 258 return llvm::getConstArray(itemType, itemConstants);
642 129 }
643
644 // Function or procedure
645
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 872 times.
✓ Branch 2 taken 153 times.
✓ Branch 3 taken 723 times.
876 if symbolType.is(SuperType::TY_FUNCTION) || symbolType.is(SuperType::TY_PROCEDURE) {
646 153 const llvm::Value ptrDefaultValue = this.getDefaultValueForSymbolType(QualType(SuperType::TY_PTR));
647 153 const llvm::Value sizeDefaultValue = this.getInt64(0l);
648 153 Vector<llvm::Value> fieldValues;
649 153 fieldValues.pushBack(ptrDefaultValue);
650 153 fieldValues.pushBack(ptrDefaultValue);
651 153 fieldValues.pushBack(sizeDefaultValue);
652 306 return llvm::getConstNamedStruct(this.llvmTypes.lambdaFatPtrType, fieldValues);
653 153 }
654
655 // Struct
656
2/2
✓ Branch 0 taken 668 times.
✓ Branch 1 taken 55 times.
723 if symbolType.is(SuperType::TY_STRUCT) {
657 // Retrieve field count
658 668 Scope* structScope = symbolType.getBodyScope();
659 668 assert structScope != nil<Scope*>;
660 668 const unsigned long fieldCount = structScope.getFieldCount();
661
662 // Get default values for all fields of the struct
663 668 Vector<llvm::Value> fieldConstants;
664 668 fieldConstants.reserve(fieldCount + 1l);
665
666 // Add the VTable address point for the synthesized vtable pointer if required
667 668 const Struct* spiceStruct = symbolType.getStruct(nil<ASTNode*>);
668 668 assert spiceStruct != nil<const Struct*>;
669
2/2
✓ Branch 0 taken 666 times.
✓ Branch 1 taken 2 times.
668 if spiceStruct.hasSynthesizedVTablePtr() {
670 2 fieldConstants.pushBack(this.getVTableAddressPoint(&spiceStruct.structBase));
671 2 }
672
673 // Add default value for each struct field
674
2/2
✓ Branch 0 taken 668 times.
✓ Branch 1 taken 1856 times.
2524 for unsigned long i = 0l; i < fieldCount; i++ {
675 // Get entry of the field
676 1856 const SymbolTableEntry* fieldEntry = structScope.lookupField(cast<unsigned int>(i));
677 1856 assert fieldEntry != nil<const SymbolTableEntry*> && fieldEntry.isField();
678
679 // Retrieve default field value
680 1856 llvm::Value defaultFieldValue;
681 1856 const ASTFieldNode* fieldNode = getFieldNode(fieldEntry);
682
4/4
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 1823 times.
✓ Branch 2 taken 1463 times.
✓ Branch 3 taken 393 times.
1856 if fieldNode != nil<const ASTFieldNode*> && fieldNode.defaultValue != nil<IVisitable*> {
683 1463 const CompileTimeValue defaultValue = this.getCompileTimeValue(fieldNode.defaultValue);
684 1463 defaultFieldValue = this.getConst(defaultValue, fieldEntry.getQualType(), cast<ASTNode*>(fieldNode));
685 1463 } else {
686 393 defaultFieldValue = this.getDefaultValueForSymbolType(fieldEntry.getQualType());
687 393 }
688
689 1856 fieldConstants.pushBack(defaultFieldValue);
690 1856 }
691
692 668 const llvm::Type structType = symbolType.toLLVMType(this.sourceFile);
693 1336 return llvm::getConstNamedStruct(structType, fieldConstants);
694 668 }
695
696 // Union
697
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 34 times.
55 if symbolType.is(SuperType::TY_UNION) {
698 21 Scope* unionScope = symbolType.getBodyScope();
699 21 assert unionScope != nil<Scope*>;
700 21 const unsigned long fieldCount = unionScope.getFieldCount();
701
702 // Find the (at most one) field with a default value. Tag 0 is reserved for the "unset" state (see the matching
703 // comment in IRGenerator::visitPostfixUnaryExpr's TY_UNION branch), so a field's tag is its index shifted by one.
704 21 unsigned int defaultFieldTag = 0; // Sentinel: no field is active ("unset" state)
705 21 llvm::Value defaultFieldValue;
706
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 48 times.
67 for unsigned long i = 0l; i < fieldCount; i++ {
707 48 const SymbolTableEntry* fieldEntry = unionScope.lookupField(cast<unsigned int>(i));
708 48 assert fieldEntry != nil<const SymbolTableEntry*> && fieldEntry.isField();
709 48 const ASTFieldNode* fieldNode = getFieldNode(fieldEntry);
710
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 46 times.
48 if fieldNode != nil<const ASTFieldNode*> && fieldNode.defaultValue != nil<IVisitable*> {
711 2 defaultFieldTag = cast<unsigned int>(i) + 1;
712 2 const CompileTimeValue defaultValue = this.getCompileTimeValue(fieldNode.defaultValue);
713 2 defaultFieldValue = this.getConst(defaultValue, fieldEntry.getQualType(), cast<ASTNode*>(fieldNode));
714 2 break;
715 }
716 46 }
717
718 21 const llvm::Type unionType = symbolType.toLLVMType(this.sourceFile);
719
720 // With no default field, tag 0 and an all-zero payload is exactly the "unset" state, so the whole constant
721 // collapses to a plain zero value. This also makes such a union eligible for zero-initialized (.bss) storage
722 // when used as a global, rather than requiring an explicit non-zero initializer.
723
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 2 times.
21 if defaultFieldValue.isNull() {
724 19 return llvm::getNullValue(unionType);
725 }
726
727 2 const llvm::Value tagConstant = this.getInt32(cast<long>(defaultFieldTag));
728 2 const llvm::Value alignPadConstant = llvm::getNullValue(unionType.getStructElementType(1));
729 2 const llvm::Type payloadType = unionType.getStructElementType(2);
730 2 const llvm::Value payloadConstant = this.packConstantAsByteArray(defaultFieldValue, payloadType);
731
732 2 Vector<llvm::Value> fieldValues;
733 2 fieldValues.pushBack(tagConstant);
734 2 fieldValues.pushBack(alignPadConstant);
735 2 fieldValues.pushBack(payloadConstant);
736 4 return llvm::getConstNamedStruct(unionType, fieldValues);
737 2 }
738
739 // Interface
740
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 if symbolType.is(SuperType::TY_INTERFACE) {
741 34 const llvm::Type structType = symbolType.toLLVMType(this.sourceFile);
742 34 Vector<llvm::Value> fieldValues;
743 34 fieldValues.pushBack(llvm::getNullValue(this.builder.getPtrTy()));
744 68 return llvm::getConstNamedStruct(structType, fieldValues);
745 34 }
746
747 − panic(Error("Cannot determine default value for symbol type")); // LCOV_EXCL_LINE
748 }
749
750 // Get the field node, a field entry is declared by, if it is declared by a field node
751 1904 f<const ASTFieldNode*> getFieldNode(const SymbolTableEntry* fieldEntry) {
752 1904 const ASTNode* declNode = fieldEntry.declNode;
753
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1904 times.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 1871 times.
1904 if declNode == nil<const ASTNode*> || declNode.kind != ASTNodeKind::FIELD {
754 33 return nil<const ASTFieldNode*>;
755 }
756 1871 return downcast<ASTFieldNode>(declNode);
757 }
758
759 80651 f<llvm::Value> IRGenerator.getConst(const CompileTimeValue& compileTimeValue, const QualType& qualType, const ASTNode* node) {
760
2/2
✓ Branch 0 taken 1331 times.
✓ Branch 1 taken 79320 times.
80651 if qualType.is(SuperType::TY_DOUBLE) {
761 1331 return llvm::getConstDouble(this.builder.getDoubleTy(), compileTimeValue.getDoubleValue());
762 }
763
764
2/2
✓ Branch 0 taken 14268 times.
✓ Branch 1 taken 65052 times.
79320 if qualType.is(SuperType::TY_INT) {
765 14268 return this.getInt32(cast<long>(compileTimeValue.getIntValue()));
766 }
767
768
2/2
✓ Branch 0 taken 2219 times.
✓ Branch 1 taken 62833 times.
65052 if qualType.is(SuperType::TY_SHORT) {
769 2219 return this.getInt16(cast<long>(compileTimeValue.getShortValue()));
770 }
771
772
2/2
✓ Branch 0 taken 37117 times.
✓ Branch 1 taken 25716 times.
62833 if qualType.is(SuperType::TY_LONG) {
773 37117 return this.getInt64(compileTimeValue.getLongValue());
774 }
775
776
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 25716 times.
✓ Branch 2 taken 8757 times.
✓ Branch 3 taken 16959 times.
25716 if qualType.is(SuperType::TY_BYTE) || qualType.is(SuperType::TY_CHAR) {
777 8757 return this.getInt8(cast<long>(cast<int>(compileTimeValue.getCharValue())));
778 }
779
780
2/2
✓ Branch 0 taken 10969 times.
✓ Branch 1 taken 5990 times.
16959 if qualType.is(SuperType::TY_STRING) {
781 10969 const String& stringValue = this.resourceManager.compileTimeStringValues.get(compileTimeValue.getStringValueOffset());
782 21938 return this.createGlobalStringConst(String(ANON_GLOBAL_STRING_NAME), stringValue, node.codeLoc);
783 10969 }
784
785
2/2
✓ Branch 0 taken 5503 times.
✓ Branch 1 taken 487 times.
5990 if qualType.is(SuperType::TY_BOOL) {
786 5503 return this.getInt1(compileTimeValue.getBoolValue());
787 }
788
789
2/2
✓ Branch 0 taken 481 times.
✓ Branch 1 taken 6 times.
487 if qualType.is(SuperType::TY_PTR) {
790 481 return llvm::getNullValue(this.builder.getPtrTy());
791 }
792
793 // The only compile-time value of a function or procedure type is nil, which is represented as a null fat pointer
794
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 if qualType.is(SuperType::TY_FUNCTION) || qualType.is(SuperType::TY_PROCEDURE) {
795 6 return llvm::getNullValue(this.llvmTypes.lambdaFatPtrType);
796 }
797
798 − panic(Error("Constant fall-through")); // LCOV_EXCL_LINE
799 }
800
801 153300 f<llvm::BasicBlock> IRGenerator.createBlock(const String& blockName = String()) {
802 198044 return llvm::BasicBlock(*this.context, blockName.getRaw());
803 44744 }
804
805 153300 p IRGenerator.switchToBlock(llvm::BasicBlock block, llvm::Function parentFct = llvm::Function()) {
806 153300 assert !block.hasParent(); // Ensure that the block was not added to a function already
807 // If no parent function were passed, use the current function
808
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 108513 times.
✓ Branch 2 taken 44787 times.
✗ Branch 3 not taken.
153300 if parentFct.isNull() {
809 108513 const llvm::BasicBlock insertBlock = this.builder.getInsertBlock();
810 108513 parentFct = insertBlock.getParent();
811 108513 }
812 // Append block to current function
813 153300 parentFct.pushBack(block);
814 // Set insert point to the block
815 153300 this.builder.setInsertPoint(block);
816 153300 this.blockAlreadyTerminated = false;
817 153300 }
818
819 2933 p IRGenerator.terminateBlock(ASTStmtLstNode* stmtLstNode) {
820 2933 this.generateScopeCleanup(stmtLstNode);
821 2933 this.blockAlreadyTerminated = true;
822 2933 }
823
824 58912 p IRGenerator.insertJump(llvm::BasicBlock targetBlock) {
825
2/2
✓ Branch 0 taken 20045 times.
✓ Branch 1 taken 38867 times.
58912 if this.blockAlreadyTerminated {
826 20045 return;
827 }
828 38867 this.builder.createBr(targetBlock);
829 38867 this.blockAlreadyTerminated = true;
830 38867 }
831
832 46760 p IRGenerator.insertCondJump(llvm::Value condition, llvm::BasicBlock trueBlock, llvm::BasicBlock falseBlock, Likelihood likelihood = Likelihood::UNSPECIFIED) {
833
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 41849 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4911 times.
46760 if this.blockAlreadyTerminated {
834 ✗ return;
835 }
836 46760 const llvm::Value jumpInst = this.builder.createCondBr(condition, trueBlock, falseBlock);
837 46760 this.blockAlreadyTerminated = true;
838
839
2/4
✓ Branch 0 taken 41849 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4911 times.
46760 if likelihood != Likelihood::UNSPECIFIED {
840 4911 this.mdGenerator.generateBranchWeightsMetadata(jumpInst, likelihood);
841 4911 }
842 46760 }
843
844 44744 p IRGenerator.verifyFunction(llvm::Function fct, const CodeLoc& codeLoc) {
845 // Skip the verifying step if the verifier was disabled manually or debug info is emitted
846
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44744 times.
44744 if this.cliOptions.disableVerifier {
847 ✗ return;
848 }
849
850 // Verify function. The LLVM C API does not return the output of the function verifier, so it is printed to stderr
851 − if llvm::verifyFunction(fct, llvm::LLVMVerifierFailureAction::PrintMessageAction) { // LCOV_EXCL_LINE
852 − raiseCompilerError(codeLoc, CompilerErrorType::INVALID_FUNCTION, String("Function verification failed")); // LCOV_EXCL_LINE
853 ✗ }
854 44744 }
855
856 3499 p IRGenerator.verifyModule(const CodeLoc& codeLoc) {
857 // Skip the verifying step if the verifier was disabled manually or debug info is emitted
858
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3499 times.
3499 if this.cliOptions.disableVerifier {
859 ✗ return;
860 }
861
862 // Verify module
863 3499 String output;
864 − if this.module.verify(output) { // LCOV_EXCL_LINE
865 − raiseCompilerError(codeLoc, CompilerErrorType::INVALID_MODULE, output); // LCOV_EXCL_LINE
866 ✗ }
867 3499 }
868
869 26703 #[ignoreUnusedReturnValue]
870 26703 f<LLVMExprResult> IRGenerator.doAssignment(IVisitable* lhsNode, IVisitable* rhsNode, ASTNode* node) {
871 // Get entry of left side
872 26703 LLVMExprResult exprResult = this.visitExpr(lhsNode);
873 26703 const SymbolTableEntry* entry = exprResult.entry;
874 26703 llvm::Value lhsAddress;
875
4/4
✓ Branch 0 taken 3745 times.
✓ Branch 1 taken 22958 times.
✓ Branch 2 taken 1205 times.
✓ Branch 3 taken 25498 times.
26703 if entry != nil<const SymbolTableEntry*> && isRefEntry(entry) {
876 1205 lhsAddress = exprResult.refPtr;
877 1205 } else {
878 25498 lhsAddress = this.resolveAddress(exprResult);
879 25498 }
880 26703 return this.doAssignment(lhsAddress, entry, rhsNode, node);
881 }
882
883 59750 #[ignoreUnusedReturnValue]
884 59750 f<LLVMExprResult> IRGenerator.doAssignment(llvm::Value lhsAddress, const SymbolTableEntry* lhsEntry, IVisitable* rhsNode, ASTNode* node, bool isDecl = false) {
885 // Get symbol type of right side
886 59750 const QualType rhsSType = this.getEvaluatedType(rhsNode);
887 59750 LLVMExprResult rhs = this.visitExpr(rhsNode);
888 59750 return this.doAssignment(lhsAddress, lhsEntry, rhs, rhsSType, node, isDecl);
889 }
890
891 60208 #[ignoreUnusedReturnValue]
892 60208 f<LLVMExprResult> IRGenerator.doAssignment(llvm::Value lhsAddress, const SymbolTableEntry* lhsEntry, LLVMExprResult& rhs, const QualType& rhsSType, ASTNode* node, bool isDecl) {
893 // Deduce some information about the assignment
894
2/2
✓ Branch 0 taken 3745 times.
✓ Branch 1 taken 56463 times.
60208 const bool isRefAssign = lhsEntry != nil<const SymbolTableEntry*> && isRefEntry(lhsEntry);
895 // A non-temporary struct value assigned by value needs a deep copy. This holds whether the destination is a direct
896 // lvalue or the value behind an already-bound reference (assign-through): the binding cases of a reference assignment
897 // (declaration/initial field ref/return value) all return early above before this is consumed, so the remaining
898 // reference assignments are assign-throughs that must copy into the referent instead of shallow-copying (which would
899 // alias the rhs' owned members and double-free).
900 // A reference rhs never denotes a temporary that could be stolen, even without an entry (e.g. a call returning a
901 // reference), because the referenced value is owned elsewhere.
902 60208 const QualType rhsSTypeNoRef = rhsSType.removeReferenceWrapper();
903
4/4
✓ Branch 0 taken 51487 times.
✓ Branch 1 taken 8721 times.
✓ Branch 2 taken 3164 times.
✓ Branch 3 taken 5557 times.
60208 const bool needsCopy = rhsSTypeNoRef.is(SuperType::TY_STRUCT) && (rhsSType.isRef() || !rhs.isTemporary());
904
905
2/2
✓ Branch 0 taken 55840 times.
✓ Branch 1 taken 4368 times.
60208 if isRefAssign {
906 4368 assert lhsEntry != nil<const SymbolTableEntry*>;
907
2/2
✓ Branch 0 taken 3163 times.
✓ Branch 1 taken 1205 times.
4368 if isDecl { // Reference gets initially assigned
908 // Store lhs pointer to rhs
909 3163 const llvm::Value refAddress = this.insertAlloca(this.builder.getPtrTy());
910 3163 this.updateAddress(lhsEntry, refAddress);
911
912 // Generate debug info for variable declaration
913 3163 this.diGenerator.generateLocalVarDebugInfo(lhsEntry.name, refAddress);
914
915 // Get address of right side
916 3163 const llvm::Value rhsAddress = this.resolveAddress(rhs);
917 3163 assert !rhsAddress.isNull();
918 3163 this.insertStore(rhsAddress, refAddress);
919
920 3163 LLVMExprResult res;
921 3163 res.value = rhsAddress;
922 3163 res.ptr = refAddress;
923 3163 res.entry = lhsEntry;
924 3163 return res;
925 }
926
927 // Reference to reference assignment (only for struct fields that are not initialized yet)
928 // These are only allowed inside a ctor body, for direct fields of 'this'. In other cases, the value of the reference gets
929 // assigned, not the ref itself.
930 1205 const bool isFieldOfThis = isAssignToDirectFieldOfThis(node);
931
6/8
✓ Branch 0 taken 705 times.
✓ Branch 1 taken 500 times.
✓ Branch 2 taken 703 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 703 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 703 times.
1205 const bool isInitialFieldRefAssign = this.isInCtorBody && isFieldOfThis && rhsSType.isRef() && rhs.entry != nil<const SymbolTableEntry*> && lhsEntry.isField();
932 // Assigning the result variable
933 1205 const bool isReturnValAssign = lhsEntry.name == RETURN_VARIABLE_NAME;
934
4/4
✓ Branch 0 taken 703 times.
✓ Branch 1 taken 502 times.
✓ Branch 2 taken 705 times.
✓ Branch 3 taken 500 times.
1205 if isInitialFieldRefAssign || isReturnValAssign {
935 // Get address of right side
936 705 const llvm::Value referencedAddress = this.resolveAddress(rhs);
937 705 assert !referencedAddress.isNull();
938
939 // Store the rhs* to the lhs**
940 705 this.insertStore(referencedAddress, lhsAddress);
941
942 705 LLVMExprResult res;
943 705 res.value = referencedAddress;
944 705 res.ptr = lhsAddress;
945 705 res.entry = lhsEntry;
946 705 return res;
947 }
948
949 // Load referenced address
950 500 lhsAddress = this.insertLoad(this.builder.getPtrTy(), lhsAddress);
951 500 }
952
953
6/6
✓ Branch 0 taken 30342 times.
✓ Branch 1 taken 25998 times.
✓ Branch 2 taken 27053 times.
✓ Branch 3 taken 3289 times.
✓ Branch 4 taken 3285 times.
✓ Branch 5 taken 53055 times.
56340 if isDecl && rhsSType.is(SuperType::TY_STRUCT) && rhs.isTemporary() {
954 3285 assert lhsEntry != nil<const SymbolTableEntry*>;
955 3285 const llvm::Value rhsAddress = this.resolveAddress(rhs);
956 // Only adopt the temporary's storage directly (temp stealing) unless it is a phi merging two independently
957 // materialized pointers (e.g. a ternary whose branches each own their own storage). Lifetime markers require
958 // their operand to be a real alloca, so stealing such a phi here would emit an invalid llvm.lifetime.end
959 // under --sanitizer=address. Any other pointer (a plain alloca, or one derived from it via GEP/load, as in
960 // the foreach-loop item extraction below) denotes a single, stable piece of storage and is safe to adopt.
961 // The temporary's ownership fully transfers to lhsEntry either way (its underlying storage is never
962 // separately destructed), so falling back to a shallow copy into a dedicated alloca is exactly as correct,
963 // just gives up the pointer-adoption optimization for the phi case.
964
2/2
✓ Branch 0 taken 3264 times.
✓ Branch 1 taken 21 times.
3285 if !rhsAddress.isPHINode() {
965 // Directly set the address to the lhs entry (temp stealing)
966 3264 this.updateAddress(lhsEntry, rhsAddress);
967 3264 rhs.entry = lhsEntry;
968 // Generate debug info for variable declaration
969 3264 this.diGenerator.generateLocalVarDebugInfo(lhsEntry.name, rhsAddress);
970 3264 return rhs;
971 }
972 // Size the copy off lhsEntry's own type, not rhsSType: some callers (e.g. the foreach-loop item
973 // extraction below) pass a struct rhsSType only to steer this branch, while rhs itself resolves (through
974 // refPtr indirection) to a differently-typed value; lhsEntry's type is always the authoritative one here.
975 21 const QualType& lhsQualType = lhsEntry.getQualType();
976 21 const llvm::Value lhsAddr = this.insertAlloca(lhsQualType);
977 21 this.updateAddress(lhsEntry, lhsAddr);
978 21 this.diGenerator.generateLocalVarDebugInfo(lhsEntry.name, lhsAddr);
979 21 this.generateShallowCopy(rhsAddress, lhsQualType.toLLVMType(this.sourceFile), lhsAddr, lhsEntry.isVolatile);
980 21 rhs.ptr = lhsAddr;
981 21 rhs.entry = lhsEntry;
982 21 return rhs;
983 }
984
985 // Allocate new memory if the lhs address does not exist
986
2/2
✓ Branch 0 taken 26072 times.
✓ Branch 1 taken 26983 times.
53055 if lhsAddress.isNull() {
987 26983 assert lhsEntry != nil<const SymbolTableEntry*>;
988 26983 lhsAddress = this.insertAlloca(lhsEntry.getQualType());
989 26983 this.updateAddress(lhsEntry, lhsAddress);
990 // Generate debug info for variable declaration
991 26983 this.diGenerator.generateLocalVarDebugInfo(lhsEntry.name, lhsAddress);
992 26983 }
993
994 // 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
995
8/8
✓ Branch 0 taken 49310 times.
✓ Branch 1 taken 3745 times.
✓ Branch 2 taken 10237 times.
✓ Branch 3 taken 39073 times.
✓ Branch 4 taken 10225 times.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 53050 times.
53055 if lhsEntry != nil<const SymbolTableEntry*> && isPtrEntry(lhsEntry) && rhsSType.isArray() && cast<unsigned long>(rhsSType.getArraySize()) != ARRAY_SIZE_UNKNOWN {
996 // Get address of right side
997 5 const llvm::Value rhsAddress = this.resolveAddress(rhs);
998 5 assert !rhsAddress.isNull();
999 5 const llvm::Type elementTy = rhsSType.toLLVMType(this.sourceFile);
1000 5 Vector<llvm::Value> indices;
1001 5 indices.pushBack(this.getInt64(0l));
1002 5 indices.pushBack(this.getInt32(0l));
1003 5 const llvm::Value firstItemAddress = this.insertInBoundsGEP(elementTy, rhsAddress, indices);
1004 5 this.insertStore(firstItemAddress, lhsAddress);
1005 5 LLVMExprResult res;
1006 5 res.value = rhsAddress;
1007 5 res.ptr = lhsAddress;
1008 5 res.entry = lhsEntry;
1009 10 return res;
1010 5 }
1011
1012 // Handle operator overloads
1013
4/4
✓ Branch 0 taken 27052 times.
✓ Branch 1 taken 25998 times.
✓ Branch 2 taken 678 times.
✓ Branch 3 taken 52372 times.
53050 if !isDecl && this.conversionManager.callsOverloadedOpFct(node, DEFAULT_OP_IDX) {
1014 678 const Resolver lhsV = noneResolver();
1015 678 const Resolver rhsV = valueResolver(rhsSType, &rhs);
1016 678 const Resolver lhsP = fixedResolver(lhsAddress);
1017 678 const Resolver rhsP = addressResolver(&rhs);
1018 678 const OperandResolvers resolvers = OperandResolvers(lhsV, lhsP, rhsV, rhsP);
1019 1356 return this.conversionManager.callOperatorOverloadFct(node, resolvers, 2l, DEFAULT_OP_IDX);
1020 678 }
1021
1022 // Check if we need to copy the rhs to the lhs. This happens for structs
1023
2/2
✓ Branch 0 taken 715 times.
✓ Branch 1 taken 51657 times.
52372 if needsCopy {
1024 // Get address of right side
1025 715 const llvm::Value rhsAddress = this.resolveAddress(rhs);
1026 715 assert !rhsAddress.isNull();
1027
1028 // If the lhs already holds an initialized, non-trivially-destructible struct, its old value must be
1029 // destructed before the copy overwrites it, otherwise its owning members (heap pointers, strings, ...)
1030 // would leak. The typechecker only sets a dtor in exactly those cases. To stay correct for a self-
1031 // assignment like 'a = a', the destruct + copy are skipped entirely when both sides share the address
1032 // (the assignment is a no-op in that case, and destructing first would corrupt the value to copy from).
1033 715 const model::Function* lhsDtor = this.getLhsDtorFct(node);
1034 715 llvm::BasicBlock bCopyEnd;
1035
2/2
✓ Branch 0 taken 689 times.
✓ Branch 1 taken 26 times.
715 if lhsDtor != nil<const model::Function*> {
1036 26 const llvm::BasicBlock bCopy = this.createBlock(String("assign.copy"));
1037 26 bCopyEnd = this.createBlock(String("assign.copy.end"));
1038 26 const llvm::Value selfAssign = this.builder.createICmp(llvm::LLVMIntPredicate::EQ, lhsAddress, rhsAddress);
1039 26 this.insertCondJump(selfAssign, bCopyEnd, bCopy);
1040 26 this.switchToBlock(bCopy);
1041 26 const Vector<llvm::Value> noArgs;
1042 26 this.generateCtorOrDtorCall(lhsAddress, lhsDtor, noArgs);
1043 26 }
1044
1045 715 const QualType rhsSTypeNonRef = rhsSTypeNoRef.toNonConst();
1046
2/2
✓ Branch 0 taken 636 times.
✓ Branch 1 taken 79 times.
715 if rhsSTypeNonRef.isTriviallyCopyable(node) {
1047 // Create shallow copy
1048 636 const llvm::Type rhsType = rhsSTypeNonRef.toLLVMType(this.sourceFile);
1049
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 636 times.
636 this.generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry != nil<const SymbolTableEntry*> && lhsEntry.isVolatile);
1050 636 } else {
1051 // Check if we have a copy ctor
1052 79 Scope* structScope = rhsSTypeNonRef.getBodyScope();
1053 79 ArgList args;
1054 79 args.pushBack(Pair<QualType, bool>(rhsSTypeNonRef.toConstRef(node), rhs.isTemporary()));
1055 79 const model::Function* copyCtor = lookupFunction(structScope, String(CTOR_FUNCTION_NAME), rhsSTypeNonRef, args, true);
1056
1/2
✓ Branch 0 taken 79 times.
✗ Branch 1 not taken.
79 if copyCtor != nil<const model::Function*> {
1057 // Call copy ctor
1058 79 Vector<llvm::Value> ctorArgs;
1059 79 ctorArgs.pushBack(rhsAddress);
1060 79 this.generateCtorOrDtorCall(lhsAddress, copyCtor, ctorArgs);
1061 79 } else {
1062 ✗ const String structName = rhsSTypeNonRef.getName();
1063 ✗ const String msg = "Cannot copy struct '" + structName + "', as it is not trivially copyable and has no copy ctor";
1064 ✗ raiseSemanticError(node, SemanticErrorType::COPY_CTOR_REQUIRED, msg);
1065 ✗ }
1066 79 }
1067
1068 // Close the self-assignment guard
1069
2/2
✓ Branch 0 taken 689 times.
✓ Branch 1 taken 26 times.
715 if !bCopyEnd.isNull() {
1070 26 this.insertJump(bCopyEnd);
1071 26 this.switchToBlock(bCopyEnd);
1072 26 }
1073 715 LLVMExprResult res;
1074 715 res.ptr = lhsAddress;
1075 715 res.entry = lhsEntry;
1076 715 return res;
1077 }
1078
1079 // If a temporary struct is moved into an already initialized lhs (temp stealing), the old value of the lhs must be
1080 // destructed before it gets overwritten, otherwise its owning members would leak. The typechecker only sets a dtor in
1081 // exactly those cases. The rhs is already evaluated at this point and, being a temporary, never aliases the lhs.
1082
4/4
✓ Branch 0 taken 50403 times.
✓ Branch 1 taken 1254 times.
✓ Branch 2 taken 50403 times.
✓ Branch 3 taken 1254 times.
51657 if rhsSType.is(SuperType::TY_STRUCT) && rhs.isTemporary() {
1083 1254 const model::Function* lhsDtor = this.getLhsDtorFct(node);
1084
2/2
✓ Branch 0 taken 1237 times.
✓ Branch 1 taken 17 times.
1254 if lhsDtor != nil<const model::Function*> {
1085 17 const Vector<llvm::Value> noArgs;
1086 17 this.generateCtorOrDtorCall(lhsAddress, lhsDtor, noArgs);
1087 17 }
1088 1254 }
1089
1090 // Optimization: If we have the address of both sides, we can do a memcpy instead of loading and storing the value
1091 51657 llvm::Value rhsValue;
1092
6/6
✓ Branch 0 taken 1254 times.
✓ Branch 1 taken 50403 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1250 times.
✓ Branch 4 taken 1240 times.
✓ Branch 5 taken 50417 times.
51657 if rhsSType.is(SuperType::TY_STRUCT) && rhs.value.isNull() && rhs.constant.isNull() {
1093 // Create shallow copy
1094 1240 const QualType rhsSTypeNonRef = rhsSTypeNoRef.toNonConst();
1095 1240 const llvm::Type rhsType = rhsSTypeNonRef.toLLVMType(this.sourceFile);
1096 1240 const llvm::Value rhsAddress = this.resolveAddress(rhs);
1097 1240 assert !rhsAddress.isNull();
1098
2/2
✓ Branch 0 taken 620 times.
✓ Branch 1 taken 620 times.
1240 this.generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry != nil<const SymbolTableEntry*> && lhsEntry.isVolatile);
1099 1240 } else {
1100 // We can load the value from the right side and store it to the left side
1101 // Retrieve value of the right side
1102 50417 rhsValue = this.resolveValue(rhsSType, rhs);
1103 // Store the value to the address
1104 50417 this.insertStore(rhsValue, lhsAddress, rhsSType);
1105 50417 }
1106
1107 51657 LLVMExprResult res;
1108 51657 res.value = rhsValue;
1109 51657 res.ptr = lhsAddress;
1110 51657 res.entry = lhsEntry;
1111 51657 return res;
1112 }
1113
1114 // Check if the given node is an assignment to a direct field of 'this'
1115 1205 f<bool> isAssignToDirectFieldOfThis(const ASTNode* node) {
1116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1205 times.
1205 if node.kind != ASTNodeKind::ASSIGN_EXPR {
1117 ✗ return false;
1118 }
1119 1205 const ASTAssignExprNode* assignNode = downcast<ASTAssignExprNode>(node);
1120 1205 return isDirectFieldOfThis(getASTNode(assignNode.lhs));
1121 }
1122
1123 // Check if the type of the given entry is a reference
1124 79421 f<bool> isRefEntry(const SymbolTableEntry* entry) {
1125 79421 const QualType& qualType = entry.getQualType();
1126 79421 return qualType.isRef();
1127 }
1128
1129 // Check if the type of the given entry is a pointer
1130 49310 f<bool> isPtrEntry(const SymbolTableEntry* entry) {
1131 49310 const QualType& qualType = entry.getQualType();
1132 49310 return qualType.isPtr();
1133 }
1134
1135 // Get the dtor of the lhs of an assignment, if the lhs gets overwritten
1136 1969 f<const model::Function*> IRGenerator.getLhsDtorFct(ASTNode* node) {
1137
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 1861 times.
1969 if node.kind != ASTNodeKind::ASSIGN_EXPR {
1138 108 return nil<const model::Function*>;
1139 }
1140 1861 const ASTAssignExprNode* assignNode = downcast<ASTAssignExprNode>(node);
1141 1861 return assignNode.lhsDtorFct.get(this.manIdx);
1142 }
1143
1144 2136 p IRGenerator.generateShallowCopy(llvm::Value oldAddress, llvm::Type varType, llvm::Value targetAddress, bool isVolatile) {
1145 // Retrieve size to copy
1146 2136 const unsigned long typeSize = this.module.getTypeAllocSize(varType);
1147
1148 // Create values for memcpy intrinsic
1149 2136 const llvm::Value structSize = this.builder.getInt64(typeSize);
1150 2136 const llvm::Value copyVolatile = this.getInt1(isVolatile);
1151
1152 // Call memcpy intrinsic to execute the shallow copy
1153 2136 const llvm::Function memcpyFct = this.stdFunctionManager.getMemcpyIntrinsic();
1154 2136 assert !targetAddress.isNull();
1155 2136 Vector<llvm::Value> args;
1156 2136 args.pushBack(targetAddress);
1157 2136 args.pushBack(oldAddress);
1158 2136 args.pushBack(structSize);
1159 2136 args.pushBack(copyVolatile);
1160 2136 this.builder.createCall(memcpyFct, args);
1161 2136 }
1162
1163 112163 p IRGenerator.autoDeReferencePtr(llvm::Value& ptr, QualType& symbolType) {
1164
4/4
✓ Branch 0 taken 79838 times.
✓ Branch 1 taken 118074 times.
✓ Branch 2 taken 85749 times.
✓ Branch 3 taken 112163 times.
197912 while symbolType.isPtr() || symbolType.isRef() {
1165 85749 ptr = this.insertLoad(symbolType, ptr);
1166 85749 symbolType = symbolType.getContained();
1167 85749 }
1168 112163 }
1169
1170 152 f<llvm::Value> IRGenerator.createGlobalConst(const String& baseName, llvm::Value constant) {
1171 // Get unused name
1172 152 const String globalName = this.getUnusedGlobalName(baseName);
1173 // Create global
1174 152 llvm::Value global = this.module.getOrInsertGlobal(globalName.getRaw(), constant.getType());
1175 // Set initializer to the given constant
1176 152 global.setInitializer(constant);
1177 152 global.setConstant(true);
1178 152 global.setLinkage(llvm::LLVMLinkage::PrivateLinkage);
1179 152 global.setUnnamedAddr(llvm::LLVMUnnamedAddr::GlobalUnnamedAddr);
1180 304 return global;
1181 152 }
1182
1183 13426 f<llvm::Value> IRGenerator.createGlobalStringConst(const String& baseName, const String& value) {
1184 // Get unused name
1185 13426 const String globalName = this.getUnusedGlobalName(baseName);
1186 // Create global
1187 13426 llvm::Value global = this.module.createGlobalString(value, globalName.getRaw());
1188 // If the output should be comparable, fix alignment to 4 bytes
1189
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13426 times.
13426 if this.cliOptions.comparableOutput {
1190 13426 global.setAlignment(4);
1191 13426 }
1192 26852 return global;
1193 13426 }
1194
1195 13426 f<llvm::Value> IRGenerator.createGlobalStringConst(const String& baseName, const String& value, const CodeLoc& codeLoc) {
1196 13426 const llvm::Value global = this.createGlobalStringConst(baseName, value);
1197 // Create debug info
1198
2/2
✓ Branch 0 taken 13176 times.
✓ Branch 1 taken 250 times.
13426 if this.cliOptions.instrumentation.emitsFullDebugInfo() {
1199 250 this.diGenerator.generateGlobalStringDebugInfo(global, global.getName(), value.getLength(), codeLoc);
1200 250 }
1201 13426 return global;
1202 }
1203
1204 21333 f<String> IRGenerator.getUnusedGlobalName(const String& baseName) {
1205 // Find an unused global name
1206 21333 String globalName;
1207 21333 unsigned int suffixNumber = 0;
1208 21333 do {
1209 515283 globalName = baseName + toString(suffixNumber);
1210 515283 suffixNumber++;
1211 515283 const llvm::Value existingGlobal = this.module.getNamedGlobal(globalName.getRaw());
1212
2/2
✓ Branch 0 taken 493950 times.
✓ Branch 1 taken 21333 times.
515283 if existingGlobal.isNull() {
1213 21333 break;
1214 }
1215
1/2
✓ Branch 0 taken 493950 times.
✗ Branch 1 not taken.
515283 } while true;
1216 21333 return globalName;
1217 }
1218
1219 86107 p materializeConstant(LLVMExprResult& exprResult) {
1220 // Skip results, that do not contain a constant or already have a value
1221
4/4
✓ Branch 0 taken 217 times.
✓ Branch 1 taken 85890 times.
✓ Branch 2 taken 217 times.
✓ Branch 3 taken 85890 times.
86107 if !exprResult.value.isNull() || exprResult.constant.isNull() {
1222 217 return;
1223 }
1224
1225 // Default case: the value to the constant
1226 85890 exprResult.value = exprResult.constant;
1227 85890 }
1228
1229 48043 f<llvm::LLVMLinkage> IRGenerator.getSymbolLinkageType(bool isPublic) {
1230
2/2
✓ Branch 0 taken 40875 times.
✓ Branch 1 taken 7168 times.
48043 return isPublic ? llvm::LLVMLinkage::ExternalLinkage : llvm::LLVMLinkage::InternalLinkage;
1231 }
1232
1233 4662 f<llvm::LLVMLinkage> IRGenerator.getVTableLinkageType(bool isPublic) {
1234 // VTables, type infos and type info names are ODR entities that may legitimately be emitted in more than one
1235 // translation unit (e.g. an interface that is defined in one file and used from several importing files). Giving
1236 // them weak ODR linkage lets the linker coalesce the duplicates. On ELF this pairs with the comdat group below; on
1237 // MachO, which has no comdat support, the weak/coalesced linkage is what prevents a duplicate-symbol error.
1238
2/2
✓ Branch 0 taken 4470 times.
✓ Branch 1 taken 192 times.
4662 return isPublic ? llvm::LLVMLinkage::WeakODRLinkage : llvm::LLVMLinkage::PrivateLinkage;
1239 }
1240
1241 4662 p IRGenerator.attachComdatToSymbol(llvm::Value global, const String& comdatName, bool isPublic) {
1242 // MachO does not support comdat annotations
1243
4/4
✓ Branch 0 taken 192 times.
✓ Branch 1 taken 4470 times.
✓ Branch 2 taken 222 times.
✓ Branch 3 taken 4440 times.
4662 if isPublic && !this.cliOptions.targetTriple.isMachO() {
1244 4440 global.setComdat(this.module.getOrInsertComdat(comdatName.getRaw()));
1245 4440 }
1246 4662 }
1247
1248 /**
1249 * Attach the function attributes that all functions we emit have in common.
1250 *
1251 * Spice does not know exceptions and we never emit landing pads, so none of our functions can unwind. We still request
1252 * an unwind table, so that debuggers and profilers are able to produce correct stack traces.
1253 *
1254 * Frame pointers are off by default and are requested per function by '--keep-frame-pointers'. This attribute is the
1255 * only thing the backend reads. The 'frame-pointer' module flag that IRGenerator's constructor sets from the same
1256 * option covers a different set of functions - Function::createWithDefaultAttr() stamps it onto the functions LLVM
1257 * itself synthesizes, such as the sanitizer module ctors - so both have to be set, and the module flag alone changes
1258 * nothing about the functions we emit: x86-64 Linux then omits the frame pointer at every optimization level, and
1259 * AArch64 spills the frame record but never links the chain up.
1260 *
1261 * Nothing is emitted in the default case, rather than an explicit "none", which is exactly what LLVM does for its own
1262 * synthesized functions. It also leaves the decision to the backend, so targets whose ABI mandates a frame pointer
1263 * (e.g. AArch64 on Darwin) keep theirs.
1264 *
1265 * '--no-red-zone' adds 'noredzone' to every function, like Clang's '-mno-red-zone'. Leaf functions then allocate their
1266 * stack frame instead of using the area below the stack pointer, which the x86_64 SysV ABI reserves for them. Kernel
1267 * code needs this, because an interrupt without a stack switch pushes its frame onto the current stack. Targets without
1268 * a red zone, like AArch64, are not affected. Functions and procedures can override the option with the
1269 * 'core.compiler.redZone' attribute.
1270 *
1271 * The size levels are not communicated to LLVM by the pass pipeline alone - since Os and Oz both select the O2
1272 * pipeline, 'optsize' and 'minsize' on the individual function are what actually distinguishes them. Without 'minsize',
1273 * Oz is indistinguishable from Os.
1274 *
1275 * @param fct Function to attach the attributes to
1276 * @param isAlwaysInline Whether the function was declared as inline
1277 * @param attrs Attributes of the function or procedure definition, if any
1278 */
1279 44787 p IRGenerator.addCommonFctAttrs(llvm::Function fct, bool isAlwaysInline = false, IVisitable* attrs = nil<IVisitable*>) {
1280 44787 fct.addFnAttr("nounwind");
1281 44787 fct.addFnAttr("uwtable", 2l); // llvm::UWTableKind::Default
1282
4/4
✓ Branch 0 taken 1655 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 43128 times.
✓ Branch 3 taken 2 times.
44787 if this.cliOptions.keepFramePointers {
1283 4 fct.addFnStringAttr("frame-pointer", "all");
1284 4 }
1285
4/4
✓ Branch 0 taken 1653 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 43124 times.
✓ Branch 3 taken 6 times.
44787 if this.isRedZoneDisabled(attrs) {
1286 10 fct.addFnAttr("noredzone");
1287 10 }
1288
1289 // Explicitly inlined functions must not be marked as 'optnone', because that is incompatible with 'alwaysinline'.
1290 // This matches the behavior of other frontends: an inline request is honored, even at O0.
1291
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1657 times.
✓ Branch 2 taken 11445 times.
✓ Branch 3 taken 31685 times.
44787 if isAlwaysInline {
1292 11445 fct.addFnAttr("alwaysinline");
1293
4/4
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 1609 times.
✓ Branch 2 taken 402 times.
✓ Branch 3 taken 31283 times.
44787 } else if this.cliOptions.optLevel == OptLevel::O0 {
1294 32892 fct.addFnAttr("optnone");
1295 32892 fct.addFnAttr("noinline"); // 'optnone' requires 'noinline'
1296 33342 }
1297
1298
3/4
✓ Branch 0 taken 1654 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 43130 times.
✗ Branch 3 not taken.
44787 if this.cliOptions.optLevel >= OptLevel::Os {
1299 3 fct.addFnAttr("optsize");
1300
2/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if this.cliOptions.optLevel == OptLevel::Oz {
1301 2 fct.addFnAttr("minsize");
1302 2 }
1303 3 }
1304 44787 }
1305
1306 /**
1307 * Check whether a function must not use the red zone: with '--no-red-zone', unless the function allows it with the
1308 * 'core.compiler.redZone' attribute, or without the option, if the attribute forbids it
1309 *
1310 * @param attrs Attributes of the function or procedure definition, if any
1311 * @return Whether the function gets the 'noredzone' attribute
1312 */
1313 44787 f<bool> IRGenerator.isRedZoneDisabled(IVisitable* attrs) {
1314
2/2
✓ Branch 0 taken 43579 times.
✓ Branch 1 taken 1208 times.
44787 if attrs != nil<IVisitable*> {
1315 1208 const ASTTopLevelDefAttrNode* topLevelDefAttrs = downcast<ASTTopLevelDefAttrNode>(attrs);
1316 1208 const String redZoneAttrName = String(ATTR_CORE_COMPILER_RED_ZONE);
1317
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1204 times.
1208 if topLevelDefAttrs.attrLst.hasAttr(redZoneAttrName) {
1318 4 const CompileTimeValue redZoneAttrValue = topLevelDefAttrs.attrLst.getAttrValueByName(redZoneAttrName);
1319 8 return !redZoneAttrValue.getBoolValue();
1320 }
1321 1208 }
1322 44783 return this.cliOptions.noRedZone;
1323 }
1324
1325 311597 public f<llvm::Value> IRGenerator.getAddress(const SymbolTableEntry* entry) {
1326 311597 const long key = getEntryKey(entry);
1327
2/2
✓ Branch 0 taken 1420 times.
✓ Branch 1 taken 310177 times.
311597 if !this.addressMap.contains(key) {
1328 1420 return llvm::Value();
1329 }
1330 310177 Stack<llvm::Value>& stack = this.addressMap.get(key);
1331
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 310174 times.
310177 if stack.isEmpty() {
1332 3 return llvm::Value();
1333 }
1334 310174 return stack.top();
1335 }
1336
1337 195389 public p IRGenerator.updateAddress(const SymbolTableEntry* entry, llvm::Value address) {
1338 195389 assert !address.isNull();
1339 195389 const llvm::Type addressType = address.getType();
1340 195389 assert addressType.isPointerTy();
1341 195389 const long key = getEntryKey(entry);
1342
2/2
✓ Branch 0 taken 41606 times.
✓ Branch 1 taken 153783 times.
195389 if !this.addressMap.contains(key) {
1343 153783 Stack<llvm::Value> newStack;
1344 153783 this.addressMap.upsert(key, newStack);
1345 153783 }
1346 195389 Stack<llvm::Value>& stack = this.addressMap.get(key);
1347
2/2
✓ Branch 0 taken 153783 times.
✓ Branch 1 taken 41606 times.
195389 if !stack.isEmpty() {
1348 41606 stack.pop();
1349 41606 }
1350 195389 stack.push(address);
1351 195389 }
1352
1353 92 public p IRGenerator.pushAddress(const SymbolTableEntry* entry, llvm::Value address) {
1354 92 assert !address.isNull();
1355 92 const long key = getEntryKey(entry);
1356
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 3 times.
92 if !this.addressMap.contains(key) {
1357 3 Stack<llvm::Value> newStack;
1358 3 this.addressMap.upsert(key, newStack);
1359 3 }
1360 92 Stack<llvm::Value>& stack = this.addressMap.get(key);
1361 92 stack.push(address);
1362 92 }
1363
1364 92 public p IRGenerator.popAddress(const SymbolTableEntry* entry) {
1365 92 const long key = getEntryKey(entry);
1366 92 assert this.addressMap.contains(key);
1367 92 Stack<llvm::Value>& stack = this.addressMap.get(key);
1368 92 assert !stack.isEmpty();
1369 92 stack.pop();
1370 92 }
1371
1372 3511 public f<llvm::Function> IRGenerator.getLLVMFunction(const model::Function* spiceFunc) {
1373 3511 const long key = getFunctionKey(spiceFunc);
1374
2/2
✓ Branch 0 taken 1755 times.
✓ Branch 1 taken 1756 times.
3511 if !this.llvmFunctions.contains(key) {
1375 1755 return llvm::Function();
1376 }
1377 1756 return this.llvmFunctions.get(key);
1378 }
1379
1380 43130 public p IRGenerator.setLLVMFunction(const model::Function* spiceFunc, llvm::Function llvmFunction) {
1381 43130 assert !llvmFunction.isNull();
1382 43130 this.llvmFunctions.upsert(getFunctionKey(spiceFunc), llvmFunction);
1383 43130 }
1384
1385 /**
1386 * Get the information, how a value of the given type is returned from a function on the current target
1387 *
1388 * @param returnType Return type of the function, dyn for procedures
1389 * @return Return ABI info
1390 */
1391 339382 public f<ReturnABIInfo> IRGenerator.getReturnABIInfo(const QualType& returnType) {
1392
2/2
✓ Branch 0 taken 99787 times.
✓ Branch 1 taken 239595 times.
339382 if returnType.is(SuperType::TY_DYN) {
1393 99787 return ReturnABIInfo{ReturnABIKind::DIRECT, this.builder.getVoidTy(), llvm::Type()};
1394 }
1395 239595 llvm::Type llvmType = returnType.toLLVMType(this.sourceFile);
1396 // Types with a copy ctor or dtor are non-trivial for the purpose of calls and therefore returned via memory
1397
2/2
✓ Branch 0 taken 205502 times.
✓ Branch 1 taken 34093 times.
239595 const bool isNonTrivial = llvmType.isAggregateType() &&
1398
2/2
✓ Branch 0 taken 21318 times.
✓ Branch 1 taken 12775 times.
239595 (!returnType.isTriviallyCopyable(nil<const ASTNode*>) || !returnType.isTriviallyDestructible(nil<const ASTNode*>));
1399 239595 return this.abiInfo.classifyReturnType(llvmType, isNonTrivial);
1400 }
1401
1402 /**
1403 * Get the LLVM function type for a function with the given return and param types, with the return value lowered to the
1404 * calling convention of the target. If the value is returned via memory, the sret pointer is the first parameter.
1405 *
1406 * @param returnType Return type of the function, dyn for procedures
1407 * @param paramTypes LLVM types of the parameters, including the 'this' pointer for methods
1408 * @param isVarArg Variadic function or not
1409 * @return Lowered LLVM function type
1410 */
1411 63125 public f<llvm::Type> IRGenerator.getFunctionType(const QualType& returnType, const Vector<llvm::Type>& paramTypes, bool isVarArg = false) {
1412 63125 const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType);
1413
4/4
✓ Branch 0 taken 1159 times.
✓ Branch 1 taken 56944 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 5018 times.
63125 if returnABI.isCoerced() {
1414 1163 return llvm::getFunctionType(returnABI.coercedType, paramTypes, isVarArg);
1415 }
1416
4/4
✓ Branch 0 taken 5820 times.
✓ Branch 1 taken 51124 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 5016 times.
61962 if returnABI.isIndirect() {
1417 5822 Vector<llvm::Type> loweredParamTypes;
1418 5822 loweredParamTypes.pushBack(this.builder.getPtrTy());
1419
4/4
✓ Branch 0 taken 7789 times.
✓ Branch 1 taken 5820 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
13613 foreach const llvm::Type& paramType : paramTypes {
1420 7791 loweredParamTypes.pushBack(paramType);
1421 7791 }
1422 11644 return llvm::getFunctionType(this.builder.getVoidTy(), loweredParamTypes, isVarArg);
1423 5822 }
1424 56140 return llvm::getFunctionType(returnABI.memoryType, paramTypes, isVarArg);
1425 }
1426
1427 /**
1428 * Get the index of the first actual parameter of a function with the given return type. It is shifted by one, if the
1429 * function has an sret parameter.
1430 *
1431 * @param returnType Return type of the function, dyn for procedures
1432 * @return Index of the first actual parameter
1433 */
1434 115598 public f<unsigned int> IRGenerator.getArgOffset(const QualType& returnType) {
1435 115598 const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType);
1436 115598 return returnABI.isIndirect() ? 1 : 0;
1437 }
1438
1439 /**
1440 * Insert a call to a function, that returns a value of the given type, lowered to the calling convention of the target.
1441 * If the value is returned via memory or coerced, its address is returned via resultAddr. Otherwise, resultAddr is
1442 * set to a null value and the call itself is the returned value.
1443 *
1444 * @param fctType Lowered function type of the callee
1445 * @param callee Callee
1446 * @param args Arguments, not including the sret pointer
1447 * @param returnType Return type of the callee, dyn for procedures
1448 * @param resultAddr Address of the returned value, if it is returned via memory or coerced
1449 * @param sretAddr Memory to return the value to, if it is returned via memory. A new temporary is used, if null
1450 * @return Call instruction
1451 */
1452 72909 public f<llvm::Value> IRGenerator.insertCall(llvm::Type fctType, llvm::Value callee, const Vector<llvm::Value>& args,
1453 72909 const QualType& returnType, llvm::Value& resultAddr, llvm::Value sretAddr = llvm::Value()) {
1454 72909 const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType);
1455 72909 resultAddr = llvm::Value();
1456 72909 Vector<llvm::Value> loweredArgs;
1457
4/4
✓ Branch 0 taken 62798 times.
✓ Branch 1 taken 4681 times.
✓ Branch 2 taken 4793 times.
✓ Branch 3 taken 637 times.
72909 if returnABI.isIndirect() {
1458
3/4
✓ Branch 0 taken 4681 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 544 times.
✓ Branch 3 taken 93 times.
5318 resultAddr = sretAddr.isNull() ? this.insertAlloca(returnABI.memoryType) : sretAddr;
1459 5318 loweredArgs.pushBack(resultAddr);
1460 5318 }
1461
4/4
✓ Branch 0 taken 122389 times.
✓ Branch 1 taken 67479 times.
✓ Branch 2 taken 10320 times.
✓ Branch 3 taken 5430 times.
205618 foreach const llvm::Value& arg : args {
1462 132709 loweredArgs.pushBack(arg);
1463 132709 }
1464
1465 72909 llvm::Value callInst = this.builder.createCall(fctType, callee, loweredArgs);
1466
1467
4/4
✓ Branch 0 taken 4681 times.
✓ Branch 1 taken 62798 times.
✓ Branch 2 taken 637 times.
✓ Branch 3 taken 4793 times.
72909 if returnABI.isIndirect() {
1468 5318 this.addSRetCallParamAttrs(callInst, returnABI.memoryType);
1469
4/4
✓ Branch 0 taken 62601 times.
✓ Branch 1 taken 197 times.
✓ Branch 2 taken 4744 times.
✓ Branch 3 taken 49 times.
72909 } else if returnABI.isCoerced() {
1470 // Store the coerced value to memory, which can be accessed as the actual return type
1471 246 resultAddr = this.insertCoercionAlloca(returnABI.memoryType, returnABI.coercedType);
1472 246 this.insertStore(callInst, resultAddr);
1473 67591 }
1474 145818 return callInst;
1475 72909 }
1476
1477 /**
1478 * Insert a call to the given function, that returns a value of the given type (see the overload above)
1479 */
1480 5430 public f<llvm::Value> IRGenerator.insertCall(llvm::Function callee, const Vector<llvm::Value>& args, const QualType& returnType,
1481 5430 llvm::Value& resultAddr, llvm::Value sretAddr = llvm::Value()) {
1482 5430 return this.insertCall(callee.getFunctionType(), callee.asValue(), args, returnType, resultAddr, sretAddr);
1483 }
1484
1485 /**
1486 * Insert a return instruction for the current function, lowered to the calling convention of the target
1487 *
1488 * @param returnValue Value to return or a null value. For functions with an sret parameter, a null value means, that the
1489 * value was already stored to the sret memory
1490 */
1491 42093 public p IRGenerator.insertReturn(llvm::Value returnValue) {
1492 42093 const llvm::BasicBlock currentBlock = this.builder.getInsertBlock();
1493 42093 llvm::Function function = currentBlock.getParent();
1494
1495 // Store the value to the memory of the caller
1496 42093 const llvm::Value sretArg = getSRetArg(function);
1497
2/2
✓ Branch 0 taken 5319 times.
✓ Branch 1 taken 36774 times.
42093 if !sretArg.isNull() {
1498
2/2
✓ Branch 0 taken 917 times.
✓ Branch 1 taken 4402 times.
5319 if !returnValue.isNull() {
1499 4402 this.insertStore(returnValue, sretArg);
1500 4402 }
1501 5319 this.builder.createRetVoid();
1502 5319 return;
1503 }
1504
1505
2/2
✓ Branch 0 taken 4077 times.
✓ Branch 1 taken 32697 times.
36774 if returnValue.isNull() {
1506 4077 this.builder.createRetVoid();
1507 4077 return;
1508 }
1509
1510 // Coerce the value to the return type via memory
1511 32697 const llvm::Type returnType = function.getReturnType();
1512 32697 const llvm::Type valueType = returnValue.getType();
1513
2/2
✓ Branch 0 taken 31558 times.
✓ Branch 1 taken 1139 times.
32697 if !valueType.isSameAs(returnType) {
1514 1139 const llvm::Value coercionAddr = this.insertCoercionAlloca(valueType, returnType);
1515 1139 this.insertStore(returnValue, coercionAddr);
1516 1139 returnValue = this.insertLoad(returnType, coercionAddr);
1517 1139 }
1518 32697 this.builder.createRet(returnValue);
1519 32697 }
1520
1521 /**
1522 * Allocate memory, that can hold a value of both given types
1523 *
1524 * @param llvmType Actual type of the value
1525 * @param coercedType Type, the value is coerced to
1526 * @return Memory address
1527 */
1528 1385 f<llvm::Value> IRGenerator.insertCoercionAlloca(llvm::Type llvmType, llvm::Type coercedType) {
1529 1385 const bool isCoercedTypeLarger = this.module.getTypeAllocSize(coercedType) > this.module.getTypeAllocSize(llvmType);
1530
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 1355 times.
1385 llvm::Value alloca = this.insertAlloca(isCoercedTypeLarger ? coercedType : llvmType);
1531 1385 const unsigned int typeAlign = this.module.getPrefTypeAlign(llvmType);
1532 1385 const unsigned int coercedTypeAlign = this.module.getPrefTypeAlign(coercedType);
1533
2/2
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 1318 times.
1385 alloca.setAlignment(typeAlign > coercedTypeAlign ? typeAlign : coercedTypeAlign);
1534 1385 return alloca;
1535 }
1536
1537 /**
1538 * Get the sret parameter of the given function
1539 *
1540 * @param function LLVM function
1541 * @return sret parameter or a null value, if the function does not return via memory
1542 */
1543 85299 public f<llvm::Value> getSRetArg(llvm::Function function) {
1544
2/2
✓ Branch 0 taken 74207 times.
✓ Branch 1 taken 11092 times.
85299 if !function.hasParamAttr(0, "sret") {
1545 74207 return llvm::Value();
1546 }
1547 11092 return function.getArg(0);
1548 }
1549
1550 5814 public p IRGenerator.addSRetParamAttrs(llvm::Function function, llvm::Type llvmType) {
1551 5814 function.addParamAttr(0, "dead_on_unwind");
1552 5814 function.addParamAttr(0, "noalias");
1553 5814 function.addParamAttr(0, "writable");
1554 5814 function.addParamTypeAttr(0, "sret", llvmType);
1555 5814 function.addParamAttr(0, "align", cast<unsigned long>(this.module.getABITypeAlign(llvmType)));
1556 5814 }
1557
1558 5319 public p IRGenerator.addSRetCallParamAttrs(llvm::Value callInst, llvm::Type llvmType) {
1559 5319 callInst.addCallParamAttr(0, "dead_on_unwind");
1560 5319 callInst.addCallParamAttr(0, "writable");
1561 5319 callInst.addCallParamTypeAttr(0, "sret", llvmType);
1562 5319 callInst.addCallParamAttr(0, "align", cast<unsigned long>(this.module.getABITypeAlign(llvmType)));
1563 5319 }
1564
1565 /**
1566 * Print the given module to a string
1567 *
1568 * @param llvmModule LLVM module
1569 * @param cliOptions Cli options
1570 * @return IR string
1571 */
1572 7000 public f<String> getIRString(llvm::Module* llvmModule, const CliOptions& cliOptions) {
1573
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7000 times.
7000 const bool eliminateTarget = cliOptions.comparableOutput && cliOptions.isNativeTarget;
1574
1575 // Backup target triple and data layout
1576 7000 const String targetTriple = llvmModule.getTargetTripleString();
1577 7000 const String targetDataLayout = llvmModule.getDataLayoutString();
1578 // Remove target triple and data layout
1579
2/2
✓ Branch 0 taken 224 times.
✓ Branch 1 taken 6776 times.
7000 if eliminateTarget {
1580 6776 llvmModule.setTargetTripleString("");
1581 6776 llvmModule.setDataLayoutString("");
1582 6776 }
1583
1584 // Get IR string
1585 7000 const String output = llvmModule.printToString();
1586
1587 // Restore target triple and data layout
1588
2/2
✓ Branch 0 taken 224 times.
✓ Branch 1 taken 6776 times.
7000 if eliminateTarget {
1589 6776 llvmModule.setTargetTripleString(targetTriple.getRaw());
1590 6776 llvmModule.setDataLayoutString(targetDataLayout.getRaw());
1591 6776 }
1592
1593 14000 return output;
1594 7000 }
1595
1596 /**
1597 * Returns the operator function list for the current manifestation and the given node
1598 *
1599 * @param node Node to retrieve the op fct pointer list from
1600 * @return Op fct pointer list
1601 */
1602 124696 public f<const Vector<const model::Function*>&> IRGenerator.getOpFctPointers(ASTNode* node) {
1603 124696 const Vector<Vector<const model::Function*>>* opFctPointers = node.getOpFctPointers();
1604 124696 assert opFctPointers.getSize() > this.manIdx;
1605 124696 return opFctPointers.get(this.manIdx);
1606 }
1607
1608 // ==================================================== Top level definitions ====================================================
1609
1610 751 public f<Any> IRGenerator.visitMainFctDef(ASTMainFctDefNode* node) {
1611 // Ignore main function definitions if this is not the main source file
1612
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 751 times.
751 if !this.sourceFile.isMainFile {
1613 ✗ return Any();
1614 }
1615
1616 // Do not generate main function if it is explicitly specified
1617
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 748 times.
751 if this.cliOptions.noEntryFct {
1618 3 return Any();
1619 }
1620
1621 // Change scope to function scope
1622 748 this.currentScope = node.bodyScope;
1623 748 assert this.currentScope != nil<Scope*>;
1624
1625 // Visit parameters
1626 748 ParamInfoList paramInfoList;
1627 748 QualTypeList paramSymbolTypes;
1628 748 Vector<llvm::Type> paramTypes;
1629
2/2
✓ Branch 0 taken 741 times.
✓ Branch 1 taken 7 times.
748 if node.takesArgs {
1630 7 const ASTParamLstNode* paramLst = downcast<ASTParamLstNode>(node.paramLst);
1631 7 const unsigned long numOfParams = paramLst.params.getSize();
1632 7 paramInfoList.reserve(numOfParams);
1633 7 paramSymbolTypes.reserve(numOfParams);
1634 7 paramTypes.reserve(numOfParams);
1635
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 14 times.
21 foreach ASTDeclStmtNode* param : paramLst.params {
1636 // Get symbol table entry of param
1637 14 const SymbolTableEntry* paramSymbol = node.bodyScope.lookupStrict(param.varName);
1638 14 assert paramSymbol != nil<const SymbolTableEntry*>;
1639 // Retrieve type of param
1640 14 const QualType paramSymbolType = paramSymbol.getQualType();
1641 // Add it to the lists
1642 14 paramInfoList.pushBack(ParamInfo{param.varName, paramSymbol});
1643 14 paramSymbolTypes.pushBack(paramSymbolType);
1644 14 paramTypes.pushBack(paramSymbolType.getParamLLVMType(this.sourceFile));
1645 14 }
1646 7 }
1647
1648 // Build the function
1649 748 const llvm::Type returnType = this.builder.getInt32Ty();
1650 748 const llvm::Type fctType = llvm::getFunctionType(returnType, paramTypes, false);
1651 748 llvm::Function fct = this.module.createFunction(fctType, llvm::LLVMLinkage::ExternalLinkage, MAIN_FUNCTION_NAME);
1652 // ToDo: Set dso_local as soon as the LLVM C API allows it
1653
1654 // Add function attributes. The main function is the entry point, so nobody can call or inline it.
1655 748 fct.addFnAttr("mustprogress");
1656 748 fct.addFnAttr("noinline");
1657 748 fct.addFnAttr("norecurse");
1658 748 this.addCommonFctAttrs(fct);
1659 748 this.enableFunctionInstrumentation(fct);
1660
1661 // Add return value attributes
1662 748 fct.addRetAttr("noundef");
1663
1664 // Add debug info
1665
2/2
✓ Branch 0 taken 726 times.
✓ Branch 1 taken 22 times.
748 if this.cliOptions.instrumentation.emitsDebugInfo() {
1666 22 const model::Function spiceFunc = createMainFunction(node.entry, paramSymbolTypes, cast<ASTNode*>(node));
1667 22 this.diGenerator.generateFunctionDebugInfo(fct, &spiceFunc);
1668 22 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
1669 22 }
1670
1671 // Create entry block
1672 748 const llvm::BasicBlock bEntry = this.createBlock();
1673 748 this.switchToBlock(bEntry, fct);
1674
1675 // Reset alloca insert markers to this block
1676 748 this.allocaInsertBlock = bEntry;
1677 748 this.allocaInsertInst = llvm::Value();
1678
1679 // Result variable
1680 748 const SymbolTableEntry* resultEntry = this.currentScope.lookupStrict(String(RETURN_VARIABLE_NAME));
1681 748 assert resultEntry != nil<const SymbolTableEntry*>;
1682
2/2
✓ Branch 0 taken 740 times.
✓ Branch 1 taken 8 times.
748 if resultEntry.isInitialized() {
1683 // Allocate result variable
1684 8 const llvm::Value resultAddr = this.insertAlloca(QualType(SuperType::TY_INT), String(RETURN_VARIABLE_NAME));
1685 // Update the symbol table entry
1686 8 this.updateAddress(resultEntry, resultAddr);
1687 // Generate debug info
1688 8 this.diGenerator.generateLocalVarDebugInfo(String(RETURN_VARIABLE_NAME), resultAddr);
1689 8 }
1690
1691 // Store function argument values
1692 748 const unsigned int argCount = fct.argSize();
1693
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 748 times.
762 for unsigned int argNumber = 0; argNumber < argCount; argNumber++ {
1694 14 const llvm::Value arg = fct.getArg(argNumber);
1695 // Get information about the parameter
1696 14 const ParamInfo& paramInfo = paramInfoList.get(argNumber);
1697 14 const SymbolTableEntry* paramSymbol = paramInfo.entry;
1698 14 assert paramSymbol != nil<const SymbolTableEntry*>;
1699 // Decayed array params already carry the address of the array, so they do not need a local copy
1700
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if this.bindDecayedArrayParam(arg, argNumber, paramInfo.name, paramSymbol) {
1701 ✗ continue;
1702 }
1703 // Allocate space for it
1704 14 const llvm::Value paramAddress = this.insertAlloca(paramSymbol.getQualType(), paramInfo.name);
1705 // Update the symbol table entry
1706 14 this.updateAddress(paramSymbol, paramAddress);
1707 // Generate debug info
1708 14 this.diGenerator.generateLocalVarDebugInfo(paramInfo.name, paramAddress, cast<unsigned long>(argNumber) + 1l);
1709 // Store the value at the new address
1710 14 this.insertStore(arg, paramAddress);
1711 14 }
1712
1713 // Visit function body
1714 748 this.visit(node.body);
1715
1716 // Create return statement if the block is not terminated yet
1717
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 680 times.
748 if !this.blockAlreadyTerminated {
1718 680 llvm::Value res = this.getInt32(0l);
1719
2/2
✓ Branch 0 taken 672 times.
✓ Branch 1 taken 8 times.
680 if resultEntry.isInitialized() {
1720 8 res = this.insertLoad(fct.getReturnType(), this.getAddress(resultEntry));
1721 8 }
1722 680 this.builder.createRet(res);
1723 680 }
1724
1725 // Conclude debug info for function
1726 748 this.diGenerator.concludeFunctionDebugInfo();
1727
1728 // Verify function
1729 748 this.verifyFunction(fct, node.codeLoc);
1730
1731 // Change back to root scope
1732 748 this.currentScope = this.rootScope;
1733 748 assert this.currentScope != nil<Scope*>;
1734
1735 1496 return Any();
1736 748 }
1737
1738 26857 public f<Any> IRGenerator.visitFctDef(ASTFctDefNode* node) {
1739 26857 return this.visitFctOrProcDef(cast<ASTNode*>(node), &node.node, true, false);
1740 }
1741
1742 16732 public f<Any> IRGenerator.visitProcDef(ASTProcDefNode* node) {
1743 16732 return this.visitFctOrProcDef(cast<ASTNode*>(node), &node.node, false, node.isCtor);
1744 }
1745
1746 /**
1747 * Shared implementation of visitFctDef and visitProcDef. The differences are marked below.
1748 *
1749 * @param baseNode Function or procedure definition node
1750 * @param node Function definition base of the node
1751 * @param isFunction Function or procedure
1752 * @param isCtor Whether the procedure is a constructor
1753 */
1754 43589 f<Any> IRGenerator.visitFctOrProcDef(ASTNode* baseNode, ASTFctDefBaseNode* node, bool isFunction, bool isCtor) {
1755 // Loop through manifestations
1756 43589 this.manIdx = 0l; // Reset the symbolTypeIndex
1757
2/2
✓ Branch 0 taken 58193 times.
✓ Branch 1 taken 43589 times.
101782 foreach const model::Function* manifestation : node.manifestations {
1758 58193 assert manifestation.entry != nil<SymbolTableEntry*>;
1759
1760 // Check if the manifestation is substantiated or not public and not used by anybody
1761 58193 const QualType& entryType = manifestation.entry.getQualType();
1762 58193 const bool isPublic = isExportedFunction(manifestation, this.cliOptions.generateTestMain);
1763
6/6
✓ Branch 0 taken 15022 times.
✓ Branch 1 taken 43171 times.
✓ Branch 2 taken 37998 times.
✓ Branch 3 taken 5173 times.
✓ Branch 4 taken 15063 times.
✓ Branch 5 taken 43130 times.
58193 if !manifestation.isFullySubstantiated() || (!isPublic && !manifestation.used) {
1764 15063 this.manIdx++; // Increment symbolTypeIndex
1765 15063 continue;
1766 }
1767
2/2
✓ Branch 0 taken 26545 times.
✓ Branch 1 taken 16585 times.
43130 if !isFunction {
1768 16585 assert manifestation.alreadyTypeChecked;
1769 16585 }
1770
1771 // Change to struct scope or to the interface scope for default methods
1772
2/2
✓ Branch 0 taken 16953 times.
✓ Branch 1 taken 26177 times.
43130 if manifestation.isMethod() {
1773 26177 const QualType& thisType = manifestation.thisType;
1774 26177 const QualTypeList noTemplateTypes;
1775
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 26171 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 26171 times.
26177 const String scopeName = manifestation.isInterfaceDefaultMethod ? getInterfaceScopeName(thisType.getSubType(), noTemplateTypes) : getStructScopeName(thisType.getSubType(), thisType.getTemplateTypes());
1776 26177 this.currentScope = this.currentScope.getChildScope(scopeName);
1777 26177 assert this.currentScope != nil<Scope*>;
1778 26177 }
1779
1780 // Change scope
1781 43130 this.currentScope = this.currentScope.getChildScope(manifestation.getScopeName());
1782 43130 assert this.currentScope != nil<Scope*>;
1783
1784 // Get 'this' entry
1785 43130 ParamInfoList paramInfoList;
1786 43130 Vector<llvm::Type> paramTypes;
1787
2/2
✓ Branch 0 taken 16953 times.
✓ Branch 1 taken 26177 times.
43130 if manifestation.isMethod() {
1788 26177 const SymbolTableEntry* thisEntry = this.currentScope.lookupStrict(String(THIS_VARIABLE_NAME));
1789 26177 assert thisEntry != nil<const SymbolTableEntry*>;
1790 26177 paramInfoList.pushBack(ParamInfo{String(THIS_VARIABLE_NAME), thisEntry});
1791 26177 paramTypes.pushBack(this.builder.getPtrTy());
1792 26177 }
1793
1794 // Visit parameters
1795 43130 unsigned long argIdx = 0l;
1796
2/2
✓ Branch 0 taken 12116 times.
✓ Branch 1 taken 31014 times.
43130 if node.hasParams {
1797 31014 const ASTParamLstNode* paramLst = downcast<ASTParamLstNode>(node.paramLst);
1798 31014 const unsigned long numOfParams = manifestation.paramList.getSize();
1799 31014 const QualTypeList manifestationParamTypes = manifestation.getParamTypes();
1800 31014 paramInfoList.reserve(numOfParams);
1801 31014 paramTypes.reserve(numOfParams);
1802
2/2
✓ Branch 0 taken 31014 times.
✓ Branch 1 taken 48929 times.
79943 while argIdx < numOfParams {
1803 48929 const ASTDeclStmtNode* param = paramLst.params.get(argIdx);
1804 // Get symbol table entry of param
1805 48929 const SymbolTableEntry* paramSymbol = this.currentScope.lookupStrict(param.varName);
1806 48929 assert paramSymbol != nil<const SymbolTableEntry*>;
1807 48929 const QualType& paramSymbolType = manifestationParamTypes.get(argIdx);
1808 // Retrieve type of param
1809 48929 const llvm::Type paramType = paramSymbolType.getParamLLVMType(this.sourceFile);
1810 // Add it to the lists
1811 48929 paramInfoList.pushBack(ParamInfo{param.varName, paramSymbol});
1812 48929 paramTypes.pushBack(paramType);
1813 48929 argIdx++;
1814 48929 }
1815 31014 }
1816
1817 // Get return type
1818 43130 llvm::Type returnType = this.builder.getVoidTy();
1819
2/2
✓ Branch 0 taken 26545 times.
✓ Branch 1 taken 16585 times.
43130 const QualType returnSType = isFunction ? manifestation.returnType : QualType(SuperType::TY_DYN);
1820
2/2
✓ Branch 0 taken 16585 times.
✓ Branch 1 taken 26545 times.
43130 if isFunction {
1821 26545 returnType = manifestation.returnType.toLLVMType(this.sourceFile);
1822 26545 }
1823 43130 const unsigned int argOffset = this.getArgOffset(returnSType);
1824
1825 // Create function or implement declared function
1826 43130 const String mangledName = manifestation.getMangledName();
1827 43130 const llvm::Type fctType = this.getFunctionType(returnSType, paramTypes);
1828 43130 this.module.getOrInsertFunction(mangledName.getRaw(), fctType);
1829 43130 llvm::Function func = this.module.getFunction(mangledName.getRaw());
1830 43130 this.updateAddress(node.entry, func.asValue());
1831 43130 this.setLLVMFunction(manifestation, func);
1832 43130 assert func.isEmpty();
1833
1834 // Set attributes to function
1835 // ToDo: Set dso_local as soon as the LLVM C API allows it (not for public symbols of a shared library)
1836 43130 func.setLinkage(this.getSymbolLinkageType(isPublic));
1837 43130 this.addCommonFctAttrs(func, entryType.isInline(), node.attrs);
1838 43130 this.enableFunctionInstrumentation(func);
1839 // Set attributes to function parameters and return value
1840 43130 this.setParamAttrs(func, paramInfoList, argOffset);
1841
2/2
✓ Branch 0 taken 16585 times.
✓ Branch 1 taken 26545 times.
43130 if isFunction {
1842 26545 this.setFunctionReturnValAttrs(func, manifestation.returnType);
1843 26545 }
1844
1845 // Add debug info
1846 43130 this.diGenerator.generateFunctionDebugInfo(func, manifestation);
1847 43130 this.diGenerator.setSourceLocation(baseNode);
1848
1849 // Create entry block
1850 43130 const llvm::BasicBlock bEntry = this.createBlock();
1851 43130 this.switchToBlock(bEntry, func);
1852
1853 // Reset alloca insert markers to this block
1854 43130 this.allocaInsertBlock = bEntry;
1855 43130 this.allocaInsertInst = llvm::Value();
1856
1857 // Result variable
1858 43130 const SymbolTableEntry* resultEntry = nil<const SymbolTableEntry*>;
1859
2/2
✓ Branch 0 taken 16585 times.
✓ Branch 1 taken 26545 times.
43130 if isFunction {
1860 26545 resultEntry = this.currentScope.lookupStrict(String(RETURN_VARIABLE_NAME));
1861 26545 assert resultEntry != nil<const SymbolTableEntry*>;
1862
2/2
✓ Branch 0 taken 25437 times.
✓ Branch 1 taken 1108 times.
26545 if resultEntry.isInitialized() {
1863 // Allocate result variable. If the value is returned via memory, the result variable lives in the caller's memory
1864 1108 llvm::Value resultAddr = getSRetArg(func);
1865
2/2
✓ Branch 0 taken 458 times.
✓ Branch 1 taken 650 times.
1108 if resultAddr.isNull() {
1866 650 resultAddr = this.insertAlloca(manifestation.returnType, String(RETURN_VARIABLE_NAME));
1867 650 }
1868 // Update the symbol table entry
1869 1108 this.updateAddress(resultEntry, resultAddr);
1870 // Generate debug info
1871 1108 this.diGenerator.generateLocalVarDebugInfo(String(RETURN_VARIABLE_NAME), resultAddr);
1872 1108 }
1873 26545 }
1874
1875 // Store function argument values. The sret parameter is skipped
1876 43130 const unsigned int argCount = func.argSize() - argOffset;
1877
2/2
✓ Branch 0 taken 75106 times.
✓ Branch 1 taken 43130 times.
118236 for unsigned int argNumber = 0; argNumber < argCount; argNumber++ {
1878 75106 const llvm::Value arg = func.getArg(argOffset + argNumber);
1879 // Get information about the parameter
1880 75106 const ParamInfo& paramInfo = paramInfoList.get(argNumber);
1881 75106 const SymbolTableEntry* paramSymbol = paramInfo.entry;
1882 75106 assert paramSymbol != nil<const SymbolTableEntry*>;
1883 // Decayed array params already carry the address of the array, so they do not need a local copy
1884
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 75088 times.
75106 if this.bindDecayedArrayParam(arg, argNumber, paramInfo.name, paramSymbol) {
1885 18 continue;
1886 }
1887 // Allocate space for it
1888 75088 const llvm::Value paramAddress = this.insertAlloca(paramSymbol.getQualType(), paramInfo.name);
1889 // Update the symbol table entry
1890 75088 this.updateAddress(paramSymbol, paramAddress);
1891 // Set source location
1892 75088 this.diGenerator.setSourceLocation(paramSymbol.declNode);
1893 // Generate debug info to declare variable
1894 75088 this.diGenerator.generateLocalVarDebugInfo(paramInfo.name, paramAddress, cast<unsigned long>(argNumber) + 1l);
1895 // Store the value at the new address
1896 75088 this.insertStore(arg, paramAddress);
1897 75088 }
1898
1899 // Store the default values for optional function args
1900
2/2
✓ Branch 0 taken 12116 times.
✓ Branch 1 taken 31014 times.
43130 if node.paramLst != nil<IVisitable*> {
1901 31014 const ASTParamLstNode* paramLst = downcast<ASTParamLstNode>(node.paramLst);
1902
2/2
✓ Branch 0 taken 2465 times.
✓ Branch 1 taken 31014 times.
33479 while argIdx < paramLst.params.getSize() {
1903 2465 ASTDeclStmtNode* param = paramLst.params.get(argIdx);
1904 2465 this.visit(cast<IVisitable*>(param));
1905 2465 argIdx++;
1906 2465 }
1907 31014 }
1908
1909 // Generate special ctor preamble before generating the body to store VTable, default field values, etc. if required
1910
2/2
✓ Branch 0 taken 37758 times.
✓ Branch 1 taken 5372 times.
43130 if isCtor {
1911 5372 this.isInCtorBody = true;
1912 5372 this.generateCtorBodyPreamble(this.currentScope);
1913 5372 }
1914
1915 // Visit function body
1916 43130 this.visit(node.body);
1917
1918
2/2
✓ Branch 0 taken 37758 times.
✓ Branch 1 taken 5372 times.
43130 if isCtor {
1919 5372 this.isInCtorBody = false;
1920 5372 }
1921
1922 // Create return statement if the block is not terminated yet
1923
2/2
✓ Branch 0 taken 25717 times.
✓ Branch 1 taken 17413 times.
43130 if !this.blockAlreadyTerminated {
1924
2/2
✓ Branch 0 taken 1141 times.
✓ Branch 1 taken 16272 times.
17413 if isFunction {
1925 1141 llvm::Value res = this.getDefaultValueForSymbolType(manifestation.returnType);
1926
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1107 times.
1141 if resultEntry.isInitialized() {
1927 1107 const llvm::Value resultAddr = this.getAddress(resultEntry);
1928
2/2
✓ Branch 0 taken 458 times.
✓ Branch 1 taken 649 times.
1107 res = resultAddr.isSameAs(getSRetArg(func)) ? llvm::Value() : this.insertLoad(returnType, resultAddr);
1929 1107 }
1930 1141 this.insertReturn(res);
1931 1141 } else {
1932 16272 this.builder.createRetVoid();
1933 16272 }
1934 17413 }
1935
1936 // Conclude debug info for function
1937 43130 this.diGenerator.concludeFunctionDebugInfo();
1938
1939 // Verify function
1940 43130 this.verifyFunction(func, baseNode.codeLoc);
1941
1942 // Change to root scope
1943 43130 this.currentScope = this.rootScope;
1944
1945 43130 this.manIdx++; // Increment symbolTypeIndex
1946 43130 }
1947 43589 this.manIdx = 0l; // Reset the symbolTypeIndex
1948
1949 // Ensure that we are at the root scope again
1950 43589 assert this.currentScope == this.rootScope;
1951
1952 43589 return Any();
1953 }
1954
1955 /**
1956 * Bind a function argument that carries a decayed array to its parameter symbol. Since the argument already is the
1957 * address of the array, it can be used as the address of the parameter directly, without allocating a local copy.
1958 *
1959 * @param arg Argument of the LLVM function
1960 * @param argNumber Number of the argument
1961 * @param paramName Name of the parameter
1962 * @param paramSymbol Symbol table entry of the parameter
1963 * @return Whether the argument was bound (false if the parameter does not carry a decayed array)
1964 */
1965 75371 f<bool> IRGenerator.bindDecayedArrayParam(llvm::Value arg, unsigned int argNumber, const String& paramName, const SymbolTableEntry* paramSymbol) {
1966
2/2
✓ Branch 0 taken 75235 times.
✓ Branch 1 taken 136 times.
75371 if paramSymbol == nil<const SymbolTableEntry*> {
1967 136 return false;
1968 }
1969 75235 const QualType& paramType = paramSymbol.getQualType();
1970
2/2
✓ Branch 0 taken 75215 times.
✓ Branch 1 taken 20 times.
75235 if !paramType.isDecayedArray() {
1971 75215 return false;
1972 }
1973
1974 20 arg.setName(paramName.getRaw());
1975 20 this.updateAddress(paramSymbol, arg);
1976 // Set source location and generate debug info to declare the variable
1977 20 this.diGenerator.setSourceLocation(paramSymbol.declNode);
1978 20 this.diGenerator.generateLocalVarDebugInfo(paramName, arg, cast<unsigned long>(argNumber) + 1l);
1979 20 return true;
1980 }
1981
1982 /**
1983 * Materialize an argument that is passed to a decayed array parameter, so that the callee receives the address of a
1984 * writable array.
1985 *
1986 * Two cases need fixing up: if the argument was produced as the array itself instead of its address, it has to be put
1987 * into memory. And if it was produced as the address of a read-only constant - array literals are emitted as global
1988 * constants - it has to be copied into a mutable stack slot, because the callee may assign through the parameter. The
1989 * latter matches how C frontends materialize array compound literals.
1990 *
1991 * @param argValue Value that was produced for the argument
1992 * @param paramType Type of the parameter
1993 * @return Address of the array to hand to the callee
1994 */
1995 26 public f<llvm::Value> IRGenerator.materializeDecayedArrayArg(llvm::Value argValue, const QualType& paramType) {
1996 26 assert paramType.isDecayedArray();
1997 26 const llvm::Type paramArrayType = paramType.toLLVMType(this.sourceFile);
1998
1999 // The array itself was produced (e.g. as the result of an implicit cast) -> put it into memory
2000 26 const llvm::Type argType = argValue.getType();
2001
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if !argType.isPointerTy() {
2002 ✗ const llvm::Value argAddress = this.insertAlloca(paramArrayType, String("arg.decay"));
2003 ✗ this.insertStore(argValue, argAddress);
2004 ✗ return argAddress;
2005 ✗ }
2006
2007 // Look through constant offsets, because the implicit array-to-array cast GEPs into the global constant
2008 26 const llvm::Value global = argValue.stripInBoundsConstantOffsets();
2009
4/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 9 times.
26 if !global.isGlobalVariable() || !global.isConstant() {
2010 17 return argValue;
2011 }
2012
2013 // Copy the constant into a mutable stack slot. Function matching does not enforce that the argument array has the
2014 // same size as the parameter array, so copy the smaller of the two to never read past the end of the global.
2015 9 const llvm::Type globalType = global.getValueType();
2016 9 const bool globalIsSmaller = this.module.getTypeAllocSize(globalType) < this.module.getTypeAllocSize(paramArrayType);
2017 9 const llvm::Value argAddress = this.insertAlloca(paramArrayType, String("arg.decay"));
2018
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 this.generateShallowCopy(argValue, globalIsSmaller ? globalType : paramArrayType, argAddress, false);
2019 18 return argAddress;
2020 9 }
2021
2022 43130 p IRGenerator.setParamAttrs(llvm::Function function, const ParamInfoList& paramInfo, unsigned int argOffset = 0) {
2023 43130 assert cast<unsigned long>(function.argSize()) == cast<unsigned long>(argOffset) + paramInfo.getSize();
2024
2/2
✓ Branch 0 taken 75106 times.
✓ Branch 1 taken 43130 times.
118236 for unsigned int paramIdx = 0; paramIdx < cast<unsigned int>(paramInfo.getSize()); paramIdx++ {
2025 75106 const unsigned int i = argOffset + paramIdx;
2026 75106 const ParamInfo& info = paramInfo.get(paramIdx);
2027 75106 const QualType& paramType = info.entry.getQualType();
2028
2029 // NoUndef attribute
2030 75106 function.addParamAttr(i, "noundef");
2031
2032
2/2
✓ Branch 0 taken 47332 times.
✓ Branch 1 taken 27774 times.
75106 if paramType.isPtr() {
2033 27774 const QualType pointeeSType = paramType.getContained();
2034 27774 const llvm::Type pointeeType = pointeeSType.toLLVMType(this.sourceFile);
2035 27774 assert !pointeeType.isNull();
2036 // NonNull and dereferenceable attributes. Only 'this' is guaranteed to point to a valid object, other pointers may be nil
2037
2/2
✓ Branch 0 taken 1597 times.
✓ Branch 1 taken 26177 times.
27774 if info.name == THIS_VARIABLE_NAME {
2038 26177 function.addParamAttr(i, "nonnull");
2039 26177 function.addDereferenceableParamAttr(i, this.module.getTypeStoreSize(pointeeType));
2040 26177 }
2041 // Alignment attribute
2042 27774 function.addParamAttr(i, "align", cast<unsigned long>(this.module.getABITypeAlign(pointeeType)));
2043 27774 }
2044
2045 // ZExt or SExt attribute
2046 75106 const string extAttrKind = this.getExtAttrKindForType(paramType);
2047
2/2
✓ Branch 0 taken 66093 times.
✓ Branch 1 taken 9013 times.
75106 if extAttrKind != nil<string> {
2048 9013 function.addParamAttr(i, extAttrKind);
2049 9013 }
2050 75106 }
2051 43130 }
2052
2053 31563 p IRGenerator.setFunctionReturnValAttrs(llvm::Function function, const QualType& returnType) {
2054
2/2
✓ Branch 0 taken 658 times.
✓ Branch 1 taken 30905 times.
31563 if returnType.is(SuperType::TY_DYN) {
2055 658 return;
2056 }
2057
2058 // Values, that are returned via memory, have attributes on the sret parameter. Coerced values might contain padding
2059 30905 const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType);
2060
2/2
✓ Branch 0 taken 3735 times.
✓ Branch 1 taken 27170 times.
30905 if returnABI.isIndirect() {
2061 3735 this.addSRetParamAttrs(function, returnABI.memoryType);
2062 3735 return;
2063 }
2064
2/2
✓ Branch 0 taken 1066 times.
✓ Branch 1 taken 26104 times.
27170 if returnABI.isCoerced() {
2065 1066 return;
2066 }
2067
2068 // NoUndef attribute
2069 26104 function.addRetAttr("noundef");
2070 // ZExt or SExt attribute
2071 26104 const string extAttrKind = this.getExtAttrKindForType(returnType);
2072
2/2
✓ Branch 0 taken 14746 times.
✓ Branch 1 taken 11358 times.
26104 if extAttrKind != nil<string> {
2073 11358 function.addRetAttr(extAttrKind);
2074 11358 }
2075 26104 }
2076
2077 // Get the name of the ext attribute kind (zeroext/signext) for the given type, or nil if none is required (llvm::Attribute::None)
2078 271211 f<string> IRGenerator.getExtAttrKindForType(const QualType& qualType) {
2079
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 271211 times.
271211 if qualType.is(SuperType::TY_DYN) {
2080 ✗ return nil<string>;
2081 }
2082
2083 271211 const llvm::Type llvmType = qualType.toLLVMType(this.sourceFile);
2084 271211 const unsigned int maxExtWidth = this.cliOptions.targetTriple.isPPC() ? 64 : 32;
2085
4/4
✓ Branch 0 taken 166364 times.
✓ Branch 1 taken 104847 times.
✓ Branch 2 taken 44479 times.
✓ Branch 3 taken 226732 times.
271211 if llvmType.isIntegerTy() && llvmType.getIntegerBitWidth() < maxExtWidth {
2086 44479 return qualType.isSigned() ? "signext" : "zeroext";
2087 }
2088 226732 return nil<string>;
2089 }
2090
2091 2983 public f<Any> IRGenerator.visitStructDef(ASTStructDefNode* node) {
2092 // Get all substantiated structs which result from this struct def
2093 2983 Vector<Struct*> manifestations = node.structManifestations;
2094
2095 // Sort the manifestations to prevent generating the struct types in the wrong order (in case of dependencies between
2096 // structs). Insertion sort, the lists are small
2097
2/2
✓ Branch 0 taken 2095 times.
✓ Branch 1 taken 2983 times.
5078 for unsigned long i = 1l; i < manifestations.getSize(); i++ {
2098 2095 Struct* current = manifestations.get(i);
2099 2095 unsigned long j = i;
2100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2095 times.
2095 while j > 0l {
2101 2095 Struct* previous = manifestations.get(j - 1l);
2102
1/2
✓ Branch 0 taken 2095 times.
✗ Branch 1 not taken.
2095 if previous.manifestationIndex <= current.manifestationIndex {
2103 2095 break;
2104 }
2105 ✗ Struct*& slot = manifestations.get(j);
2106 ✗ slot = previous;
2107 ✗ j--;
2108 ✗ }
2109 2095 Struct*& targetSlot = manifestations.get(j);
2110 2095 targetSlot = current;
2111 2095 }
2112
2113
2/2
✓ Branch 0 taken 5078 times.
✓ Branch 1 taken 2983 times.
8061 foreach Struct* spiceStruct : manifestations {
2114 // Skip structs, that are not fully substantiated
2115
2/2
✓ Branch 0 taken 1079 times.
✓ Branch 1 taken 3999 times.
5078 if !spiceStruct.structBase.isFullySubstantiated() {
2116 1079 continue;
2117 }
2118
2119 // Do not generate this struct if it is private and used by nobody
2120 3999 const QualType& structEntryType = spiceStruct.entry.getQualType();
2121
4/4
✓ Branch 0 taken 3992 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3996 times.
3999 if !spiceStruct.used && !structEntryType.isPublic() {
2122 3 continue;
2123 }
2124
2125 // Change scope to struct scope, specific to substantiation
2126 3996 this.currentScope = spiceStruct.scope;
2127 3996 assert this.currentScope != nil<Scope*>;
2128
2129 // Set LLVM type to the struct entry
2130 3996 const SymbolTableEntry* structEntry = spiceStruct.entry;
2131 3996 assert structEntry != nil<const SymbolTableEntry*>;
2132
2133 // Generate VTable if required
2134
2/2
✓ Branch 0 taken 3124 times.
✓ Branch 1 taken 872 times.
3996 if node.emitVTable {
2135 872 this.generateVTable(&spiceStruct.structBase);
2136 872 this.deferredVTableInitializations.pushBack(&spiceStruct.structBase);
2137 872 }
2138
2139 // Generate default ctor if required
2140 3996 const QualType& thisType = structEntry.getQualType();
2141 3996 const ArgList noArgs;
2142 3996 const model::Function* ctorFunc = lookupFunction(this.currentScope, String(CTOR_FUNCTION_NAME), thisType, noArgs, true);
2143
4/4
✓ Branch 0 taken 2701 times.
✓ Branch 1 taken 1295 times.
✓ Branch 2 taken 3835 times.
✓ Branch 3 taken 161 times.
3996 if ctorFunc != nil<const model::Function*> && ctorFunc.implicitDefault {
2144 161 this.generateDefaultCtor(ctorFunc);
2145 161 }
2146
2147 // Generate default copy ctor if required
2148 3996 ArgList args;
2149 3996 args.pushBack(Pair<QualType, bool>(thisType.toConstRef(cast<ASTNode*>(node)), false /* always non-temporary */));
2150 3996 const model::Function* copyCtorFunc = lookupFunction(this.currentScope, String(CTOR_FUNCTION_NAME), thisType, args, true);
2151
4/4
✓ Branch 0 taken 2766 times.
✓ Branch 1 taken 1230 times.
✓ Branch 2 taken 3296 times.
✓ Branch 3 taken 700 times.
3996 if copyCtorFunc != nil<const model::Function*> && copyCtorFunc.implicitDefault {
2152 700 this.generateDefaultCopyCtor(copyCtorFunc);
2153 700 }
2154
2155 // Generate default move ctor if required. We can't use FunctionManager::lookup with a non-const ref arg
2156 // here, because the lookup permits const-param-to-non-const-arg matching ("constify") and may return the
2157 // copy ctor as a false positive. findMoveCtor scans the manifestations directly and only matches the
2158 // strict move ctor signature.
2159 3996 const model::Function* moveCtorFunc = findMoveCtor(this.currentScope);
2160
4/4
✓ Branch 0 taken 3933 times.
✓ Branch 1 taken 63 times.
✓ Branch 2 taken 3975 times.
✓ Branch 3 taken 21 times.
3996 if moveCtorFunc != nil<const model::Function*> && moveCtorFunc.implicitDefault {
2161 21 this.generateDefaultMoveCtor(moveCtorFunc);
2162 21 }
2163
2164 // Generate default dtor if required
2165 3996 const model::Function* dtorFunc = lookupFunction(this.currentScope, String(DTOR_FUNCTION_NAME), thisType, noArgs, true);
2166
4/4
✓ Branch 0 taken 2693 times.
✓ Branch 1 taken 1303 times.
✓ Branch 2 taken 3443 times.
✓ Branch 3 taken 553 times.
3996 if dtorFunc != nil<const model::Function*> && dtorFunc.implicitDefault {
2167 553 this.generateDefaultDtor(dtorFunc);
2168 553 }
2169
2170 // Return to root scope
2171 3996 this.currentScope = this.rootScope;
2172 3996 assert this.currentScope != nil<Scope*>;
2173 3996 }
2174
2175 5966 return Any();
2176 2983 }
2177
2178 291 public f<Any> IRGenerator.visitInterfaceDef(ASTInterfaceDefNode* node) {
2179 // Get all substantiated structs which result from this struct def
2180 291 Vector<Interface*> manifestations = node.interfaceManifestations;
2181
2182 // Sort the manifestations to prevent generating the struct types in the wrong order (in case of dependencies between
2183 // structs). Insertion sort, the lists are small
2184
2/2
✓ Branch 0 taken 636 times.
✓ Branch 1 taken 291 times.
927 for unsigned long i = 1l; i < manifestations.getSize(); i++ {
2185 636 Interface* current = manifestations.get(i);
2186 636 unsigned long j = i;
2187
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 636 times.
636 while j > 0l {
2188 636 Interface* previous = manifestations.get(j - 1l);
2189
1/2
✓ Branch 0 taken 636 times.
✗ Branch 1 not taken.
636 if previous.manifestationIndex <= current.manifestationIndex {
2190 636 break;
2191 }
2192 ✗ Interface*& slot = manifestations.get(j);
2193 ✗ slot = previous;
2194 ✗ j--;
2195 ✗ }
2196 636 Interface*& targetSlot = manifestations.get(j);
2197 636 targetSlot = current;
2198 636 }
2199
2200
2/2
✓ Branch 0 taken 927 times.
✓ Branch 1 taken 291 times.
1218 foreach Interface* spiceInterface : manifestations {
2201 // Skip interfaces, that are not fully substantiated
2202
2/2
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 683 times.
927 if !spiceInterface.structBase.isFullySubstantiated() {
2203 244 continue;
2204 }
2205
2206 // Do not generate this interface if it is private and used by nobody
2207 683 const QualType& interfaceEntryType = spiceInterface.entry.getQualType();
2208
4/4
✓ Branch 0 taken 682 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 682 times.
683 if !spiceInterface.used && !interfaceEntryType.isPublic() {
2209 1 continue;
2210 }
2211
2212 // Generate VTable information
2213 682 this.generateVTable(&spiceInterface.structBase);
2214 682 this.deferredVTableInitializations.pushBack(&spiceInterface.structBase);
2215 682 }
2216
2217 582 return Any();
2218 291 }
2219
2220 15 public f<Any> IRGenerator.visitUnionDef(ASTUnionDefNode* _) {
2221 15 return Any(); // Noop (unions carry no per-definition IR work; their LLVM type is materialized lazily on first use)
2222 }
2223
2224 436 public f<Any> IRGenerator.visitEnumDef(ASTEnumDefNode* _) {
2225 436 return Any(); // Noop (enums are high-level semantic-only structures)
2226 }
2227
2228 2893 public f<Any> IRGenerator.visitGenericTypeDef(ASTGenericTypeDefNode* _) {
2229 2893 return Any(); // Noop (generic types are high-level semantic-only structures)
2230 }
2231
2232 153 public f<Any> IRGenerator.visitAliasDef(ASTAliasDefNode* _) {
2233 153 return Any(); // Noop (alias definitions are high-level semantic-only structures)
2234 }
2235
2236 4183 public f<Any> IRGenerator.visitGlobalVarDef(ASTGlobalVarDefNode* node) {
2237 // Retrieve some information about the variable
2238 4183 assert node.entry != nil<SymbolTableEntry*>;
2239 4183 const QualType& entryType = node.entry.getQualType();
2240 4183 const bool isPublic = entryType.isPublic();
2241 4183 const bool isConst = entryType.isConst();
2242
2243 // Get correct type and linkage type. Use the type of the entry, because the data type might be dyn
2244 4183 const llvm::Type varType = entryType.toLLVMType(this.sourceFile);
2245
2246 // Create global var
2247 4183 const llvm::Value varAddress = this.module.getOrInsertGlobal(node.varName.getRaw(), varType);
2248 4183 llvm::Value var = this.module.getNamedGlobal(node.varName.getRaw());
2249 // Set some attributes, based on the given information
2250 4183 var.setConstant(isConst);
2251 4183 var.setLinkage(this.getSymbolLinkageType(isPublic));
2252 // ToDo: Set dso_local as soon as the LLVM C API allows it (not for public symbols of a shared library)
2253
2254 // Set initializer
2255
2/2
✓ Branch 0 taken 4177 times.
✓ Branch 1 taken 6 times.
4183 if node.hasValue { // Set the constant value as variable initializer
2256 4177 const llvm::Value constantValue = this.visitConst(cast<IVisitable*>(node.constant));
2257 4177 var.setInitializer(constantValue);
2258 4177 } else {
2259 // Set the default value as variable initializer. This is required in every build mode, because a global without
2260 // initializer would only be an external declaration. Zero-initialized globals live in .bss, so this is for free.
2261 6 const llvm::Value constantValue = this.getDefaultValueForSymbolType(entryType);
2262 6 var.setInitializer(constantValue);
2263 6 }
2264
2265 4183 this.updateAddress(node.entry, varAddress);
2266
2267 // Add debug info
2268 4183 this.diGenerator.generateGlobalVarDebugInfo(var, node.entry);
2269
2270 4183 return Any();
2271 }
2272
2273 5018 public f<Any> IRGenerator.visitExtDecl(ASTExtDeclNode* node) {
2274 5018 const model::Function* spiceFunc = node.extFunction;
2275 5018 assert spiceFunc != nil<const model::Function*>;
2276 5018 const unsigned int argOffset = this.getArgOffset(spiceFunc.returnType);
2277
2278 // Get arg types
2279 5018 const QualTypeList paramTypes = spiceFunc.getParamTypes();
2280 5018 Vector<llvm::Type> argTypes;
2281 5018 argTypes.reserve(paramTypes.getSize());
2282
2/2
✓ Branch 0 taken 10685 times.
✓ Branch 1 taken 5018 times.
15703 foreach const QualType& paramType : paramTypes {
2283 10685 argTypes.pushBack(paramType.getParamLLVMType(this.sourceFile));
2284 10685 }
2285
2286 // Declare function
2287 5018 bool isVarArg = false;
2288
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 4947 times.
5018 if node.argTypeLst != nil<IVisitable*> {
2289 4947 const ASTTypeLstWithEllipsisNode* argTypeLst = downcast<ASTTypeLstWithEllipsisNode>(node.argTypeLst);
2290 4947 isVarArg = argTypeLst.hasEllipsis;
2291 4947 }
2292 5018 const llvm::Type functionType = this.getFunctionType(spiceFunc.returnType, argTypes, isVarArg);
2293 5018 const String mangledName = spiceFunc.getMangledName();
2294 5018 this.module.getOrInsertFunction(mangledName.getRaw(), functionType);
2295 5018 llvm::Function fct = this.module.getFunction(mangledName.getRaw());
2296
2297 // The external function is assumed not to unwind into Spice code
2298 5018 fct.addFnAttr("nounwind");
2299
2300 // Add noundef attribute to all parameters and, for sub-32-bit integers, the ABI-required sext/zext attribute
2301
2/2
✓ Branch 0 taken 10685 times.
✓ Branch 1 taken 5018 times.
15703 for unsigned int i = 0; i < cast<unsigned int>(paramTypes.getSize()); i++ {
2302 10685 fct.addParamAttr(argOffset + i, "noundef");
2303 10685 const string extAttrKind = this.getExtAttrKindForType(paramTypes.get(i));
2304
2/2
✓ Branch 0 taken 10663 times.
✓ Branch 1 taken 22 times.
10685 if extAttrKind != nil<string> {
2305 22 fct.addParamAttr(argOffset + i, extAttrKind);
2306 22 }
2307 10685 }
2308
2309 // Add the same noundef/sext/zext treatment to the return value
2310 5018 this.setFunctionReturnValAttrs(fct, spiceFunc.returnType);
2311
2312 // If the function should be imported as dll, add the dll attribute
2313
2/2
✓ Branch 0 taken 5017 times.
✓ Branch 1 taken 1 time.
5018 if node.attrs != nil<IVisitable*> {
2314 1 const ASTTopLevelDefAttrNode* attrs = downcast<ASTTopLevelDefAttrNode>(node.attrs);
2315 1 const String dllAttrName = String(ATTR_CORE_LINKER_DLL);
2316
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if attrs.attrLst.hasAttr(dllAttrName) {
2317 ✗ const CompileTimeValue dllAttrValue = attrs.attrLst.getAttrValueByName(dllAttrName);
2318
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if dllAttrValue.getBoolValue() {
2319 ✗ fct.setDLLStorageClass(llvm::LLVMDLLStorageClass::DLLImport);
2320 ✗ }
2321 ✗ }
2322 1 }
2323
2324 10036 return Any();
2325 5018 }
2326
2327 // ===================================================== Control structures ======================================================
2328
2329 11993 public f<Any> IRGenerator.visitUnsafeBlock(ASTUnsafeBlockNode* node) {
2330 // Change scope
2331 11993 ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::UNSAFE_BODY, cast<ASTNode*>(node));
2332
2333 // Visit instructions in the block
2334 11993 this.visit(node.body);
2335
2336 23986 return Any();
2337 11993 }
2338
2339 5024 public f<Any> IRGenerator.visitForLoop(ASTForLoopNode* node) {
2340 // Create blocks
2341 5024 const String codeLine = node.codeLoc.toPrettyLine();
2342 5024 const llvm::BasicBlock bHead = this.createBlock("for.head." + codeLine);
2343 5024 const llvm::BasicBlock bBody = this.createBlock("for.body." + codeLine);
2344 5024 const llvm::BasicBlock bTail = this.createBlock("for.tail." + codeLine);
2345 5024 const llvm::BasicBlock bExit = this.createBlock("for.exit." + codeLine);
2346
2347 // Change to head scope
2348 5024 ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::LOOP_HEAD, cast<ASTNode*>(node));
2349 5024 Scope* bodyScope = this.currentScope.getChildScope(node.getBodyScopeId());
2350
2351 // Save the break/continue targets, paired with the scope to clean up to when jumping there. The head scope is cleaned up in
2352 // the exit block, so break does not clean it up itself
2353 5024 this.breakTargets.pushBack(BreakContinueTarget{bodyScope, bExit});
2354 5024 this.continueTargets.pushBack(BreakContinueTarget{bodyScope, bTail});
2355
2356 // Init statement
2357 5024 this.visit(node.initDecl);
2358 // Create jump from original to head block
2359 5024 this.insertJump(bHead);
2360
2361 // Switch to head block
2362 5024 this.switchToBlock(bHead);
2363 // Condition evaluation
2364 5024 const llvm::Value condValue = this.resolveValueInExprScope(node.condAssign);
2365 // Create conditional jump from head to body or exit block
2366 5024 this.insertCondJump(condValue, bBody, bExit);
2367
2368 // Switch to body block
2369 5024 this.switchToBlock(bBody);
2370 // Visit body
2371 5024 ScopeHandle bodyScopeHandle = ScopeHandle(&this.compilerPass, bodyScope, ScopeType::FOR_BODY);
2372 5024 this.visit(node.body);
2373 5024 bodyScopeHandle.leaveScopeEarly();
2374 // Create jump from body to tail block
2375 5024 this.insertJump(bTail);
2376
2377 // Switch to tail block
2378 5024 this.switchToBlock(bTail);
2379 // Inc statement
2380 {
2381 5024 ExprScopeHandle exprScopeHandle = ExprScopeHandle(&this.compilerPass, node.incAssign);
2382 5024 this.visit(node.incAssign);
2383 5024 const Scope* exprScope = exprScopeHandle.getExprScope();
2384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5024 times.
5024 if exprScope != nil<Scope*> {
2385 5024 this.generateTemporariesCleanup(exprScope, getASTNode(node.incAssign));
2386 5024 }
2387 5024 }
2388 // Create jump from tail to head
2389 5024 this.insertJump(bHead);
2390
2391 // Switch to exit block
2392 5024 this.switchToBlock(bExit);
2393 // Clean up the head scope, e.g. destruct the loop variable
2394 5024 this.generateLoopHeadCleanup(cast<ASTNode*>(node), this.currentScope);
2395
2396 // Pop break/continue targets
2397 5024 this.breakTargets.popBack();
2398 5024 this.continueTargets.popBack();
2399
2400 10048 return Any();
2401 5024 }
2402
2403 438 public f<Any> IRGenerator.visitForeachLoop(ASTForeachLoopNode* node) {
2404 // Create blocks
2405 438 const String codeLine = node.codeLoc.toPrettyLine();
2406 438 const llvm::BasicBlock bHead = this.createBlock("foreach.head." + codeLine);
2407 438 const llvm::BasicBlock bBody = this.createBlock("foreach.body." + codeLine);
2408 438 const llvm::BasicBlock bTail = this.createBlock("foreach.tail." + codeLine);
2409 438 const llvm::BasicBlock bExit = this.createBlock("foreach.exit." + codeLine);
2410
2411 // Change to head scope
2412 438 ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::LOOP_HEAD, cast<ASTNode*>(node));
2413 438 Scope* bodyScope = this.currentScope.getChildScope(node.getBodyScopeId());
2414
2415 // Save the break/continue targets, paired with the scope to clean up to when jumping there. The head scope is cleaned up in
2416 // the exit block, so break does not clean it up itself
2417 438 this.breakTargets.pushBack(BreakContinueTarget{bodyScope, bExit});
2418 438 this.continueTargets.pushBack(BreakContinueTarget{bodyScope, bTail});
2419
2420 // Resolve iterator
2421 438 const ForeachLoopData& data = node.data.get(this.manIdx);
2422 438 IVisitable* iteratorAssignNode = node.iteratorAssign;
2423 438 llvm::Value iteratorPtr;
2424
2/2
✓ Branch 0 taken 400 times.
✓ Branch 1 taken 38 times.
438 if data.getIteratorFct != nil<model::Function*> { // The iteratorAssignExpr is of type Iterable
2425 400 const llvm::Value iterablePtr = this.resolveAddress(iteratorAssignNode);
2426
2427 400 llvm::Value iterator;
2428 400 llvm::Value iteratorAddr;
2429 400 const QualTypeList getIteratorParamTypes = data.getIteratorFct.getParamTypes();
2430 400 bool isArrayIterable = false;
2431
2/2
✓ Branch 0 taken 393 times.
✓ Branch 1 taken 7 times.
400 if !data.getIteratorFct.isMethod() {
2432 7 const QualType& firstParamType = getIteratorParamTypes.front();
2433 7 isArrayIterable = firstParamType.isArray();
2434 7 }
2435
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 393 times.
400 if isArrayIterable { // Array as iterable
2436 // Call iterate() function from std/iterator/array-iterator
2437 7 const llvm::Function iterateFct = this.stdFunctionManager.getIterateFct(data.getIteratorFct);
2438 7 const QualType iterableType = this.getEvaluatedType(iteratorAssignNode);
2439 7 const unsigned long arraySize = cast<unsigned long>(iterableType.getArraySize());
2440 7 assert arraySize > 0l;
2441 7 Vector<llvm::Value> args;
2442 7 args.pushBack(iterablePtr);
2443 7 args.pushBack(this.builder.getInt64(arraySize));
2444 7 iterator = this.insertCall(iterateFct, args, data.getIteratorFct.returnType, iteratorAddr);
2445 7 } else { // Struct as iterable
2446 // Call .getIterator() on iterable
2447 393 const llvm::Function getIteratorFct = this.stdFunctionManager.getIteratorFct(data.getIteratorFct);
2448 393 Vector<llvm::Value> args;
2449 393 args.pushBack(iterablePtr);
2450 393 iterator = this.insertCall(getIteratorFct, args, data.getIteratorFct.returnType, iteratorAddr);
2451 393 }
2452
2453 // Resolve address of iterator. If it is returned via memory or coerced, it already lives in memory
2454 400 LLVMExprResult callResult;
2455
1/2
✓ Branch 0 taken 400 times.
✗ Branch 1 not taken.
400 if !iteratorAddr.isNull() {
2456 400 callResult.ptr = iteratorAddr;
2457 400 } else {
2458 ✗ callResult.value = iterator;
2459 ✗ }
2460 400 callResult.node = iteratorAssignNode;
2461 400 iteratorPtr = this.resolveAddress(callResult);
2462
2463 // If an anonymous symbol exists, set its address
2464 400 const SymbolTableEntry* returnSymbol = this.currentScope.symbolTable.lookupAnonymous(getASTNode(iteratorAssignNode));
2465
2/2
✓ Branch 0 taken 399 times.
✓ Branch 1 taken 1 time.
400 if returnSymbol != nil<const SymbolTableEntry*> {
2466 1 this.updateAddress(returnSymbol, iteratorPtr);
2467 1 }
2468 400 } else { // The iteratorAssignExpr is of type Iterator
2469 38 iteratorPtr = this.resolveAddress(iteratorAssignNode);
2470 38 }
2471
2472 // Check we have an idx
2473 438 const bool hasIdx = node.idxVarDecl != nil<IVisitable*>;
2474 // Retrieve item ref type
2475
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 408 times.
438 if hasIdx {
2476 30 assert data.getIdxFct != nil<model::Function*>;
2477 30 } else {
2478 408 assert data.getFct != nil<model::Function*>;
2479 408 }
2480
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 408 times.
438 const QualType itemRefSTy = hasIdx ? data.getIdxFct.returnType : data.getFct.returnType;
2481
2482 // Visit idx variable declaration if required
2483 438 const SymbolTableEntry* idxEntry = nil<const SymbolTableEntry*>;
2484 438 llvm::Value idxAddress;
2485
2/2
✓ Branch 0 taken 408 times.
✓ Branch 1 taken 30 times.
438 if hasIdx {
2486 30 this.visit(node.idxVarDecl);
2487 // Get address of idx variable
2488 30 const ASTDeclStmtNode* idxDeclNode = downcast<ASTDeclStmtNode>(node.idxVarDecl);
2489 30 idxEntry = idxDeclNode.entries.get(this.manIdx);
2490 30 idxAddress = this.getAddress(idxEntry);
2491 30 assert !idxAddress.isNull();
2492 30 }
2493
2494 // Create jump from original to head block
2495 438 this.insertJump(bHead);
2496
2497 // Switch to head block
2498 438 this.switchToBlock(bHead);
2499 438 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
2500 // Call .isValid() on iterator
2501 438 assert data.isValidFct != nil<model::Function*>;
2502 438 const llvm::Function isValidFct = this.stdFunctionManager.getIteratorIsValidFct(data.isValidFct);
2503 438 Vector<llvm::Value> iteratorArgs;
2504 438 iteratorArgs.pushBack(iteratorPtr);
2505 438 const llvm::Value isValid = this.builder.createCall(isValidFct, iteratorArgs);
2506 // Create conditional jump from head to body or exit block
2507 438 this.insertCondJump(isValid, bBody, bExit);
2508
2509 // Switch to body block
2510 438 this.switchToBlock(bBody);
2511 // Change to body scope
2512 438 ScopeHandle bodyScopeHandle = ScopeHandle(&this.compilerPass, bodyScope, ScopeType::FOREACH_BODY);
2513
2514 // Visit item variable declaration. The item belongs to the body, so its lifetime begins anew in each round
2515 438 this.visit(node.itemVarDecl);
2516 // Get address of item variable
2517 438 const ASTDeclStmtNode* itemDeclNode = downcast<ASTDeclStmtNode>(node.itemVarDecl);
2518 438 const SymbolTableEntry* itemEntry = itemDeclNode.entries.get(this.manIdx);
2519 438 const llvm::Value itemAddress = this.getAddress(itemEntry);
2520 438 assert !itemAddress.isNull();
2521
2522 // Get the current iterator values
2523 438 LLVMExprResult itemResult;
2524
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 408 times.
438 if hasIdx {
2525 30 const QualType& pairSTy = data.getIdxFct.returnType;
2526 30 const llvm::Type pairTy = pairSTy.toLLVMType(this.sourceFile);
2527 // Call .getIdx() on iterator. If the pair is returned via memory or coerced, it already lives in memory
2528 30 assert data.getIdxFct != nil<model::Function*>;
2529 30 const llvm::Function getIdxFct = this.stdFunctionManager.getIteratorGetIdxFct(data.getIdxFct);
2530 30 llvm::Value pairPtr;
2531 30 llvm::Value pair = this.insertCall(getIdxFct, iteratorArgs, pairSTy, pairPtr);
2532
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 if pairPtr.isNull() {
2533 ✗ pairPtr = this.insertAlloca(pairSTy, String("pair.addr"));
2534 ✗ pair.setName("pair");
2535 ✗ this.insertStore(pair, pairPtr);
2536 ✗ }
2537 // Store idx to idx var
2538 30 const llvm::Value idxAddrInPair = this.insertStructGEP(pairTy, pairPtr, 0, String("idx.addr"));
2539 30 LLVMExprResult idxResult;
2540 30 idxResult.ptr = idxAddrInPair;
2541 30 assert !idxAddress.isNull() && idxEntry != nil<const SymbolTableEntry*>;
2542 30 this.doAssignment(idxAddress, idxEntry, idxResult, QualType(SuperType::TY_LONG), cast<ASTNode*>(node), true);
2543 // Store item to item var
2544 30 itemResult.refPtr = this.insertStructGEP(pairTy, pairPtr, 1, String("item.addr"));
2545 30 } else {
2546 // Call .get() on iterator
2547 408 assert data.getFct != nil<model::Function*>;
2548 408 const llvm::Function getFct = this.stdFunctionManager.getIteratorGetFct(data.getFct);
2549 408 itemResult.ptr = this.builder.createCall(getFct, iteratorArgs);
2550 408 }
2551
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 428 times.
438 if data.calledItemCopyCtor != nil<model::Function*> {
2552 // Call copy ctor
2553 10 const llvm::Value rhsAddress = this.resolveAddress(itemResult);
2554 10 assert !rhsAddress.isNull();
2555 10 Vector<llvm::Value> ctorArgs;
2556 10 ctorArgs.pushBack(rhsAddress);
2557 10 this.generateCtorOrDtorCall(itemEntry, data.calledItemCopyCtor, ctorArgs);
2558 10 } else {
2559 // Perform normal assignment
2560 428 this.doAssignment(itemAddress, itemEntry, itemResult, itemRefSTy, cast<ASTNode*>(node), true);
2561 428 }
2562 // Visit body
2563 438 this.visit(node.body);
2564 // Leave body scope
2565 438 bodyScopeHandle.leaveScopeEarly();
2566 // Create jump from body to tail block
2567 438 this.insertJump(bTail);
2568
2569 // Switch to tail block
2570 438 this.switchToBlock(bTail);
2571 438 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
2572 // Call .next() on iterator
2573 438 assert data.nextFct != nil<model::Function*>;
2574 438 const llvm::Function nextFct = this.stdFunctionManager.getIteratorNextFct(data.nextFct);
2575 438 this.builder.createCall(nextFct, iteratorArgs);
2576 // Create jump from tail to head block
2577 438 this.insertJump(bHead);
2578
2579 // Switch to exit block
2580 438 this.switchToBlock(bExit);
2581 // Clean up the head scope, e.g. end the lifetime of the index variable
2582 438 this.generateLoopHeadCleanup(cast<ASTNode*>(node), this.currentScope);
2583
2584 // Pop break/continue targets
2585 438 this.breakTargets.popBack();
2586 438 this.continueTargets.popBack();
2587
2588 876 return Any();
2589 438 }
2590
2591 1796 public f<Any> IRGenerator.visitWhileLoop(ASTWhileLoopNode* node) {
2592 // Create blocks
2593 1796 const String codeLine = node.codeLoc.toPrettyLine();
2594 1796 const llvm::BasicBlock bHead = this.createBlock("while.head." + codeLine);
2595 1796 const llvm::BasicBlock bBody = this.createBlock("while.body." + codeLine);
2596 1796 const llvm::BasicBlock bExit = this.createBlock("while.exit." + codeLine);
2597
2598 // Change scope
2599 1796 ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::WHILE_BODY, cast<ASTNode*>(node));
2600
2601 // Save the break/continue targets, paired with the scope to clean up to when jumping there
2602 1796 this.breakTargets.pushBack(BreakContinueTarget{this.currentScope, bExit});
2603 1796 this.continueTargets.pushBack(BreakContinueTarget{this.currentScope, bHead});
2604
2605 // Jump to head block
2606 1796 this.insertJump(bHead);
2607
2608 // Switch to head block
2609 1796 this.switchToBlock(bHead);
2610 // Evaluate condition
2611 1796 const llvm::Value condValue = this.resolveValueInExprScope(node.condition);
2612 // Jump to body or exit block, depending on the condition
2613 1796 this.insertCondJump(condValue, bBody, bExit);
2614
2615 // Switch to body block
2616 1796 this.switchToBlock(bBody);
2617 // Visit body
2618 1796 this.visit(node.body);
2619 // Create jump to head block
2620 1796 this.insertJump(bHead);
2621
2622 // Switch to exit block
2623 1796 this.switchToBlock(bExit);
2624
2625 // Pop break/continue targets
2626 1796 this.breakTargets.popBack();
2627 1796 this.continueTargets.popBack();
2628
2629 3592 return Any();
2630 1796 }
2631
2632 81 public f<Any> IRGenerator.visitDoWhileLoop(ASTDoWhileLoopNode* node) {
2633 // Create blocks
2634 81 const String codeLine = node.codeLoc.toPrettyLine();
2635 81 const llvm::BasicBlock bBody = this.createBlock("dowhile.body." + codeLine);
2636 81 const llvm::BasicBlock bFoot = this.createBlock("dowhile.foot." + codeLine);
2637 81 const llvm::BasicBlock bExit = this.createBlock("dowhile.exit." + codeLine);
2638
2639 // Change scope
2640 81 ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::WHILE_BODY, cast<ASTNode*>(node));
2641
2642 // Save the break/continue targets, paired with the scope to clean up to when jumping there
2643 81 this.breakTargets.pushBack(BreakContinueTarget{this.currentScope, bExit});
2644 81 this.continueTargets.pushBack(BreakContinueTarget{this.currentScope, bFoot});
2645
2646 // Jump to body block
2647 81 this.insertJump(bBody);
2648
2649 // Switch to body block
2650 81 this.switchToBlock(bBody);
2651 // Visit body
2652 81 this.visit(node.body);
2653 // Create jump to foot block
2654 81 this.insertJump(bFoot);
2655
2656 // Switch to head block
2657 81 this.switchToBlock(bFoot);
2658 // Evaluate condition
2659 81 const llvm::Value condValue = this.resolveValueInExprScope(node.condition);
2660 // Jump to body or exit block, depending on the condition
2661 81 this.insertCondJump(condValue, bBody, bExit);
2662
2663 // Switch to exit block
2664 81 this.switchToBlock(bExit);
2665
2666 // Pop break/continue targets
2667 81 this.breakTargets.popBack();
2668 81 this.continueTargets.popBack();
2669
2670 162 return Any();
2671 81 }
2672
2673 26515 public f<Any> IRGenerator.visitIfStmt(ASTIfStmtNode* node) {
2674 // If we have a compile time decision, only evaluate the respective branch
2675 26515 const bool doCompileThenBranch = node.doCompileThenBranch(this.manIdx);
2676 26515 const bool doCompileElseBranch = node.doCompileElseBranch(this.manIdx);
2677
4/4
✓ Branch 0 taken 145 times.
✓ Branch 1 taken 26370 times.
✓ Branch 2 taken 236 times.
✓ Branch 3 taken 26279 times.
26515 if doCompileThenBranch && !doCompileElseBranch {
2678 236 ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::IF_ELSE_BODY, cast<ASTNode*>(node));
2679 236 this.visit(node.thenBody);
2680 472 return Any(this.builder.getTrue());
2681 236 }
2682
4/4
✓ Branch 0 taken 26134 times.
✓ Branch 1 taken 145 times.
✓ Branch 2 taken 145 times.
✓ Branch 3 taken 26134 times.
26279 if !doCompileThenBranch && doCompileElseBranch {
2683
2/2
✓ Branch 0 taken 143 times.
✓ Branch 1 taken 2 times.
145 if node.elseStmt != nil<IVisitable*> {
2684 2 this.visit(node.elseStmt);
2685 2 }
2686 145 return Any(this.builder.getFalse());
2687 }
2688
2689 // Create blocks
2690 26134 const String codeLine = node.codeLoc.toPrettyLine();
2691 26134 const llvm::BasicBlock bThen = this.createBlock("if.then." + codeLine);
2692 26134 llvm::BasicBlock bElse;
2693
2/2
✓ Branch 0 taken 24832 times.
✓ Branch 1 taken 1302 times.
26134 if node.elseStmt != nil<IVisitable*> {
2694 1302 bElse = this.createBlock("if.else." + codeLine);
2695 1302 }
2696 26134 const llvm::BasicBlock bExit = this.createBlock("if.exit." + codeLine);
2697
2698 // Change scope
2699 26134 ScopeHandle scopeHandle = ScopeHandle(this, node.getScopeId(), ScopeType::IF_ELSE_BODY, cast<ASTNode*>(node));
2700
2701 // Retrieve condition value
2702 26134 const llvm::Value condValue = this.resolveValueInExprScope(node.condition);
2703 // Check if condition is fulfilled
2704
2/2
✓ Branch 0 taken 1302 times.
✓ Branch 1 taken 24832 times.
26134 this.insertCondJump(condValue, bThen, node.elseStmt != nil<IVisitable*> ? bElse : bExit);
2705
2706 // Switch to then block
2707 26134 this.switchToBlock(bThen);
2708 // Visit then body
2709 26134 this.visit(node.thenBody);
2710 // Create jump from then to end block
2711 26134 this.insertJump(bExit);
2712
2713 // Change scope back
2714 26134 scopeHandle.leaveScopeEarly();
2715
2716
2/2
✓ Branch 0 taken 24832 times.
✓ Branch 1 taken 1302 times.
26134 if node.elseStmt != nil<IVisitable*> {
2717 // Switch to else block
2718 1302 this.switchToBlock(bElse);
2719 // Visit else block
2720 1302 this.visit(node.elseStmt);
2721 // Create jump from else to end block
2722 1302 this.insertJump(bExit);
2723 1302 }
2724
2725 // Switch to exit block
2726 26134 this.switchToBlock(bExit);
2727
2728 // Return conditional value as result for the 'if' stmt
2729 52268 return Any(condValue);
2730 26134 }
2731
2732 1304 public f<Any> IRGenerator.visitElseStmt(ASTElseStmtNode* node) {
2733
2/2
✓ Branch 0 taken 550 times.
✓ Branch 1 taken 754 times.
1304 if node.ifStmt != nil<IVisitable*> { // It is an else if branch
2734 550 this.visit(node.ifStmt);
2735 550 } else { // It is an else branch
2736 // Change scope
2737 754 ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::IF_ELSE_BODY, cast<ASTNode*>(node));
2738
2739 // Generate IR for nested statements
2740 754 this.visit(node.body);
2741 754 }
2742
2743 1304 return Any();
2744 }
2745
2746 30 public f<Any> IRGenerator.visitSwitchStmt(ASTSwitchStmtNode* node) {
2747 // Create blocks
2748 30 Vector<llvm::BasicBlock> bCases;
2749 30 bCases.reserve(node.caseBranches.getSize());
2750
2/2
✓ Branch 0 taken 183 times.
✓ Branch 1 taken 30 times.
213 foreach IVisitable* caseBranch : node.caseBranches {
2751 183 const CodeLoc& caseBranchCodeLoc = caseBranch.getCodeLoc();
2752 183 bCases.pushBack(this.createBlock("switch.case." + caseBranchCodeLoc.toPrettyLine()));
2753 183 }
2754 30 llvm::BasicBlock bDefault;
2755
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 18 times.
30 if node.hasDefaultBranch {
2756 18 const CodeLoc& defaultBranchCodeLoc = node.defaultBranch.getCodeLoc();
2757 18 bDefault = this.createBlock("switch.default." + defaultBranchCodeLoc.toPrettyLine());
2758 18 }
2759 30 const String codeLine = node.codeLoc.toPrettyLine();
2760 30 const llvm::BasicBlock bExit = this.createBlock("switch.exit." + codeLine);
2761
2762 // Save the break target. The scope to clean up to is set by each case/default branch as it is visited below,
2763 // since it changes per branch (there is no single scope spanning the whole switch statement).
2764 30 this.breakTargets.pushBack(BreakContinueTarget{nil<Scope*>, bExit});
2765
2766 // Visit switch expression
2767 30 const llvm::Value exprValue = this.resolveValue(node.assignExpr);
2768
2769 // Generate switch instruction
2770
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 12 times.
30 llvm::Value switchInst = this.builder.createSwitch(exprValue, node.hasDefaultBranch ? bDefault : bExit, cast<unsigned int>(node.caseBranches.getSize()));
2771
2772 // Generate case branches
2773
2/2
✓ Branch 0 taken 183 times.
✓ Branch 1 taken 30 times.
213 for unsigned long i = 0l; i < node.caseBranches.getSize(); i++ {
2774 183 const ASTCaseBranchNode* caseBranch = downcast<ASTCaseBranchNode>(node.caseBranches.get(i));
2775
2776 // Push fallthrough block
2777 183 llvm::BasicBlock bFallthrough = bDefault;
2778
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 153 times.
183 if i + 1l < node.caseBranches.getSize() {
2779 153 bFallthrough = bCases.get(i + 1l);
2780 153 }
2781 183 this.fallthroughBlocks.push(bFallthrough);
2782
2783 // Switch to case block
2784 183 this.switchToBlock(bCases.get(i));
2785
2786 // Visit case body
2787 183 this.visit(node.caseBranches.get(i));
2788
2789 // Create jump from case to exit block
2790 183 this.insertJump(bExit);
2791
2792 // Pop fallthrough block
2793 183 this.fallthroughBlocks.pop();
2794
2795 // Add case to switch instruction
2796
2/2
✓ Branch 0 taken 188 times.
✓ Branch 1 taken 183 times.
371 foreach IVisitable* caseConstantNode : caseBranch.caseConstants {
2797 188 const llvm::Value caseValue = this.visitConst(caseConstantNode);
2798 188 switchInst.addCase(caseValue, bCases.get(i));
2799 188 }
2800 183 }
2801
2802 // Generate default branch
2803
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 18 times.
30 if node.hasDefaultBranch {
2804 // Switch to default block
2805 18 this.switchToBlock(bDefault);
2806
2807 // Visit default body
2808 18 this.visit(node.defaultBranch);
2809
2810 // Create jump from default to exit block
2811 18 this.insertJump(bExit);
2812 18 }
2813
2814 // Switch to exit block
2815 30 this.switchToBlock(bExit);
2816
2817 // Pop break target
2818 30 this.breakTargets.popBack();
2819
2820 60 return Any();
2821 30 }
2822
2823 183 public f<Any> IRGenerator.visitCaseBranch(ASTCaseBranchNode* node) {
2824 // Change to case body scope
2825 183 ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::CASE_BODY);
2826
2827 // A 'break' within this branch targets the enclosing switch; point it at this branch's own scope
2828 183 BreakContinueTarget& breakTarget = this.breakTargets.back();
2829 183 breakTarget.scope = this.currentScope;
2830
2831 // Visit case body
2832 183 this.visit(node.body);
2833
2834 366 return Any();
2835 183 }
2836
2837 18 public f<Any> IRGenerator.visitDefaultBranch(ASTDefaultBranchNode* node) {
2838 // Change to default body scope
2839 18 ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::DEFAULT_BODY);
2840
2841 // A 'break' within this branch targets the enclosing switch; point it at this branch's own scope
2842 18 BreakContinueTarget& breakTarget = this.breakTargets.back();
2843 18 breakTarget.scope = this.currentScope;
2844
2845 // Visit case body
2846 18 this.visit(node.body);
2847
2848 36 return Any();
2849 18 }
2850
2851 126 public f<Any> IRGenerator.visitAnonymousBlockStmt(ASTAnonymousBlockStmtNode* node) {
2852 // Change scope
2853 126 node.bodyScope.parent = this.currentScope; // Needed for nested scopes in generic functions
2854 126 node.bodyScope.symbolTable.parent = &this.currentScope.symbolTable; // Needed for nested scopes in generic functions
2855 126 ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::ANONYMOUS_BLOCK_BODY, cast<ASTNode*>(node));
2856
2857 // Visit instructions in the block
2858 126 this.visit(node.body);
2859
2860 252 return Any();
2861 126 }
2862
2863 // ========================================================= Statements ==========================================================
2864
2865 90793 public f<Any> IRGenerator.visitStmtLst(ASTStmtLstNode* node) {
2866 // Generate instructions in the scope
2867
2/2
✓ Branch 0 taken 90785 times.
✓ Branch 1 taken 180612 times.
271397 foreach IVisitable* stmt : node.statements {
2868 // Check if we can cancel generating instructions for this code branch
2869 180612 const ASTStmtNode* stmtNode = asStmtNode(getASTNode(stmt));
2870
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 180606 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 180604 times.
180612 if this.blockAlreadyTerminated || stmtNode.unreachable {
2871 8 break;
2872 }
2873
2874 // Set source location for debug info
2875 180604 this.diGenerator.setSourceLocation(getASTNode(stmt));
2876
2877 // Visit child
2878 180604 this.visit(stmt);
2879 180604 }
2880
2881 // Generate cleanup code of this scope, e.g. dtor calls for struct instances
2882 90793 this.generateScopeCleanup(node);
2883
2884 90793 return Any();
2885 }
2886
2887 ✗ public f<Any> IRGenerator.visitTypeAltsLst(ASTTypeAltsLstNode* _) {
2888 ✗ return Any(); // Noop
2889 }
2890
2891 35014 public f<Any> IRGenerator.visitDeclStmt(ASTDeclStmtNode* node) {
2892 // Get variable entry
2893 35014 const SymbolTableEntry* varEntry = node.entries.get(this.manIdx);
2894 35014 const DeclStmtData& data = node.data.get(this.manIdx);
2895 35014 assert varEntry != nil<const SymbolTableEntry*>;
2896 35014 const QualType varSymbolType = varEntry.getQualType();
2897
2898 // Get LLVM type of variable
2899 35014 const llvm::Type varTy = varSymbolType.toLLVMType(this.sourceFile);
2900
2901 // Check if the declaration is with an assignment or the default value
2902 35014 llvm::Value varAddress;
2903
2/2
✓ Branch 0 taken 33160 times.
✓ Branch 1 taken 1854 times.
35014 if node.hasAssignment { // Assignment
2904
2/2
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 33047 times.
33160 if data.calledCopyCtor != nil<model::Function*> {
2905 // Allocate memory
2906 113 varAddress = this.insertAlloca(varTy);
2907 113 this.updateAddress(varEntry, varAddress);
2908 // Generate debug info for variable declaration
2909 113 this.diGenerator.generateLocalVarDebugInfo(node.varName, varAddress);
2910 // Call copy ctor
2911 113 const llvm::Value rhsAddress = this.resolveAddress(node.assignExpr);
2912 113 assert !rhsAddress.isNull();
2913 113 Vector<llvm::Value> ctorArgs;
2914 113 ctorArgs.pushBack(rhsAddress);
2915 113 this.generateCtorOrDtorCall(varEntry, data.calledCopyCtor, ctorArgs);
2916 113 } else {
2917 // Assign rhs to lhs
2918 33047 const LLVMExprResult assignResult = this.doAssignment(varAddress, varEntry, node.assignExpr, cast<ASTNode*>(node), true);
2919 33047 assert assignResult.entry == varEntry;
2920 33047 varAddress = this.getAddress(varEntry);
2921 33047 this.updateAddress(varEntry, varAddress);
2922 33047 }
2923 33160 } else { // Default value
2924 // Allocate memory
2925 1854 varAddress = this.insertAlloca(varTy);
2926 1854 this.updateAddress(varEntry, varAddress);
2927
2928 // Generate debug info for variable declaration
2929 1854 this.diGenerator.generateLocalVarDebugInfo(node.varName, varAddress);
2930
2931
2/2
✓ Branch 0 taken 1004 times.
✓ Branch 1 taken 850 times.
1854 if data.calledInitCtor != nil<model::Function*> {
2932 // Call no-args constructor
2933 1004 const Vector<llvm::Value> noArgs;
2934 1004 this.generateCtorOrDtorCall(varEntry, data.calledInitCtor, noArgs);
2935 // A union with no default field must start in the "unset" tag state, and a union with a default field must
2936 // start with that field active. Either way, the tag bits carry real runtime safety meaning (unlike a struct's
2937 // all-zero debug-only default), so they must be initialized unconditionally, in every build mode.
2938
6/6
✓ Branch 0 taken 438 times.
✓ Branch 1 taken 412 times.
✓ Branch 2 taken 408 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 442 times.
✓ Branch 5 taken 408 times.
1854 } else if !node.isForEachItem && (this.cliOptions.buildMode != BuildMode::RELEASE || varSymbolType.is(SuperType::TY_UNION)) {
2939 408 assert !data.isCtorCallRequired;
2940 // Retrieve default value for lhs symbol type and store it
2941 408 const llvm::Value defaultValue = this.getDefaultValueForSymbolType(varSymbolType);
2942 408 this.insertStore(defaultValue, varAddress);
2943 850 }
2944 1854 }
2945 35014 assert !varAddress.isNull();
2946
2947 // Attach the variable name to the LLVM value.
2948 35014 varAddress.setName(varEntry.name.getRaw());
2949
2950 35014 return Any();
2951 }
2952
2953 ✗ public f<Any> IRGenerator.visitQualifierLst(ASTQualifierLstNode* _) {
2954 ✗ return Any(); // Noop
2955 }
2956
2957 1431 public f<Any> IRGenerator.visitModAttr(ASTModAttrNode* _) {
2958 1431 return Any(); // Noop
2959 }
2960
2961 ✗ public f<Any> IRGenerator.visitTopLevelDefinitionAttr(ASTTopLevelDefAttrNode* _) {
2962 ✗ return Any(); // Noop
2963 }
2964
2965 188 public f<Any> IRGenerator.visitCaseConstant(ASTCaseConstantNode* node) {
2966
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 95 times.
188 if node.constant != nil<ASTConstantNode*> {
2967 93 return this.visit(cast<IVisitable*>(node.constant));
2968 }
2969
2970 95 const SymbolTableEntry* constantEntry = node.entry;
2971 95 const CompileTimeValue compileTimeValue = constantEntry.declNode.getCompileTimeValue(this.manIdx);
2972 95 const QualType evaluatedType = this.getEvaluatedType(cast<IVisitable*>(node));
2973 95 return Any(this.getConst(compileTimeValue, evaluatedType, cast<ASTNode*>(node)));
2974 }
2975
2976 40855 public f<Any> IRGenerator.visitReturnStmt(ASTReturnStmtNode* node) {
2977 // If the value is returned via memory, it is directly put into the memory of the caller
2978 40855 const llvm::BasicBlock currentBlock = this.builder.getInsertBlock();
2979 40855 const llvm::Value sretArg = getSRetArg(currentBlock.getParent());
2980
2981 40855 llvm::Value returnValue;
2982
2/2
✓ Branch 0 taken 36773 times.
✓ Branch 1 taken 4082 times.
40855 if node.hasReturnValue { // Return value is attached to the return statement
2983 36773 IVisitable* returnExpr = node.assignExpr;
2984 36773 const ReturnStmtData& data = node.data.get(this.manIdx);
2985
2/2
✓ Branch 0 taken 364 times.
✓ Branch 1 taken 36409 times.
36773 if data.calledCopyCtor != nil<model::Function*> {
2986 // Perform a copy
2987 364 const llvm::Value originalAddress = this.resolveAddress(returnExpr);
2988 364 const llvm::Type returnTy = data.returnType.toLLVMType(this.sourceFile);
2989
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 364 times.
364 const llvm::Value newAddress = sretArg.isNull() ? this.insertAlloca(returnTy) : sretArg;
2990 364 Vector<llvm::Value> ctorArgs;
2991 364 ctorArgs.pushBack(originalAddress);
2992 364 this.generateCtorOrDtorCall(newAddress, data.calledCopyCtor, ctorArgs);
2993
1/2
✓ Branch 0 taken 364 times.
✗ Branch 1 not taken.
364 if sretArg.isNull() {
2994 ✗ returnValue = this.insertLoad(returnTy, newAddress);
2995 ✗ }
2996 364 } else {
2997
2/2
✓ Branch 0 taken 2020 times.
✓ Branch 1 taken 34389 times.
36409 returnValue = data.returnType.isRef() ? this.resolveAddress(returnExpr) : this.resolveValue(returnExpr);
2998 36409 }
2999 36773 } else { // Try to load result variable value
3000 4082 const SymbolTableEntry* resultEntry = this.currentScope.lookupResultVar();
3001
2/2
✓ Branch 0 taken 4077 times.
✓ Branch 1 taken 5 times.
4082 if resultEntry != nil<const SymbolTableEntry*> {
3002 5 const llvm::Value returnValueAddr = this.getAddress(resultEntry);
3003 // The result variable of functions, that return via memory, already lives in the memory of the caller
3004
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if !returnValueAddr.isSameAs(sretArg) {
3005 3 const QualType& resultType = resultEntry.getQualType();
3006 3 const llvm::Type resultSTy = resultType.toLLVMType(this.sourceFile);
3007 3 returnValue = this.insertLoad(resultSTy, returnValueAddr);
3008 3 }
3009 5 }
3010 4082 }
3011
3012 // Clean up all scopes between here and the enclosing function/procedure/lambda body, then terminate the block. A local
3013 // variable, that is returned without a copy, is destructed by the caller
3014 40855 this.generateScopeCleanupUpTo(cast<ASTNode*>(node), this.currentScope.getFunctionScope(), node.returnedLocals.get(this.manIdx));
3015 40855 this.blockAlreadyTerminated = true;
3016
3017 // Create return instruction. The cleanup code is attributed to the closing braces of the scopes, the return to this statement
3018 40855 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3019 40855 this.insertReturn(returnValue);
3020
3021 40855 return Any();
3022 }
3023
3024 724 public f<Any> IRGenerator.visitBreakStmt(ASTBreakStmtNode* node) {
3025 724 const BreakContinueTarget target = this.breakTargets.get(this.breakTargets.getSize() - cast<unsigned long>(node.breakTimes));
3026
3027 // Clean up all scopes between here and the loop/switch statement we are breaking out of
3028 724 this.generateScopeCleanupUpTo(cast<ASTNode*>(node), target.scope);
3029
3030 // Jump to destination block. The cleanup code is attributed to the closing braces of the scopes, the jump to this statement
3031 724 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3032 724 this.insertJump(target.block);
3033
3034 724 return Any();
3035 }
3036
3037 1366 public f<Any> IRGenerator.visitContinueStmt(ASTContinueStmtNode* node) {
3038 1366 const BreakContinueTarget target = this.continueTargets.get(this.continueTargets.getSize() - cast<unsigned long>(node.continueTimes));
3039
3040 // Clean up all scopes between here and the loop statement we are continuing
3041 1366 this.generateScopeCleanupUpTo(cast<ASTNode*>(node), target.scope);
3042
3043 // Jump to destination block. The cleanup code is attributed to the closing braces of the scopes, the jump to this statement
3044 1366 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3045 1366 this.insertJump(target.block);
3046
3047 1366 return Any();
3048 }
3049
3050 10 public f<Any> IRGenerator.visitFallthroughStmt(ASTFallthroughStmtNode* node) {
3051 // Clean up all scopes between here and the body of the case branch we are falling through from
3052 10 Scope* caseBodyScope = this.currentScope;
3053
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
13 while caseBodyScope.scopeType != ScopeType::CASE_BODY {
3054 3 caseBodyScope = caseBodyScope.parent;
3055 3 }
3056 10 this.generateScopeCleanupUpTo(cast<ASTNode*>(node), caseBodyScope);
3057
3058 // Jump to destination block. The cleanup code is attributed to the closing braces of the scopes, the jump to this statement
3059 10 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3060 10 this.insertJump(this.fallthroughBlocks.top());
3061
3062 10 return Any();
3063 }
3064
3065 // Generate the body of a failed assertion
3066 4787 p IRGenerator.generateAssertBody(ASTAssertStmtNode* node) {
3067 // Create constant for error message. It is the format string of the fprintf call below, so the condition's source text
3068 // must not be mistaken for conversion specifiers
3069 4787 String expressionString = node.expressionString;
3070 4787 expressionString.replaceAll("%", "%%");
3071 4787 const String errorMsg = "Assertion failed: Condition '" + expressionString + "' evaluated to false.\n";
3072 4787 const String globalName = this.getUnusedGlobalName(String(ANON_GLOBAL_STRING_NAME));
3073 4787 llvm::Value globalString = this.module.createGlobalString(errorMsg, globalName.getRaw());
3074 // If the output should be comparable, fix alignment to 4 bytes
3075
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4787 times.
4787 if this.cliOptions.comparableOutput {
3076 4787 globalString.setAlignment(4);
3077 4787 }
3078 // Print the error message to stderr, like panic does
3079 4787 const llvm::Function fprintfFct = this.stdFunctionManager.getFPrintfFct();
3080 4787 Vector<llvm::Value> fprintfArgs;
3081 4787 fprintfArgs.pushBack(this.getStdErrValue());
3082 4787 fprintfArgs.pushBack(globalString);
3083 4787 this.builder.createCall(fprintfFct, fprintfArgs);
3084 // Print the stack trace of the failed assertion
3085
1/2
✓ Branch 0 taken 4787 times.
✗ Branch 1 not taken.
4787 if this.cliOptions.printsStackTraceOnAbort() {
3086 ✗ Vector<llvm::Value> dumpArgs;
3087 ✗ dumpArgs.pushBack(this.builder.getTrue());
3088 ✗ dumpArgs.pushBack(this.builder.getFalse());
3089 ✗ this.builder.createCall(this.stdFunctionManager.getDumpStacktraceFct(), dumpArgs);
3090 ✗ }
3091 // Generate call to exit()
3092 4787 const llvm::Function exitFct = this.stdFunctionManager.getExitFct();
3093 4787 Vector<llvm::Value> exitArgs;
3094 4787 exitArgs.pushBack(this.getInt32(cast<long>(EXIT_FAILURE)));
3095 4787 this.builder.createCall(exitFct, exitArgs);
3096 // Create unreachable instruction
3097 4787 this.builder.createUnreachable();
3098 4787 this.blockAlreadyTerminated = true;
3099 4787 }
3100
3101 4803 public f<Any> IRGenerator.visitAssertStmt(ASTAssertStmtNode* node) {
3102 // Do not generate assertions in release mode
3103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4803 times.
4803 if this.cliOptions.buildMode == BuildMode::RELEASE {
3104 ✗ return Any();
3105 }
3106
3107 // If we have a compile time decision, only evaluate the respective branch
3108
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4783 times.
4803 if this.hasCompileTimeValue(node.assignExpr) {
3109 20 const CompileTimeValue compileTimeValue = this.getCompileTimeValue(node.assignExpr);
3110
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
20 if !compileTimeValue.getBoolValue() {
3111 4 this.generateAssertBody(node);
3112 4 }
3113 20 return Any();
3114 }
3115
3116 // Create blocks
3117 4783 const String codeLine = node.codeLoc.toPrettyLine();
3118 4783 const llvm::BasicBlock bThen = this.createBlock("assert.then." + codeLine);
3119 4783 const llvm::BasicBlock bExit = this.createBlock("assert.exit." + codeLine);
3120
3121 // Visit the assignExpr
3122 4783 const llvm::Value condValue = this.resolveValue(node.assignExpr);
3123
3124 // Create condition check
3125 4783 this.insertCondJump(condValue, bExit, bThen, Likelihood::LIKELY);
3126
3127 // Switch to then block
3128 4783 this.switchToBlock(bThen);
3129 4783 this.generateAssertBody(node);
3130
3131 // Switch to exit block
3132 4783 this.switchToBlock(bExit);
3133
3134 9566 return Any();
3135 4783 }
3136
3137 // ========================================================= Expressions =========================================================
3138
3139 29380 public f<Any> IRGenerator.visitAssignExpr(ASTAssignExprNode* node) {
3140 // Visit ternary expression
3141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29380 times.
29380 if node.ternaryExpr != nil<IVisitable*> {
3142 ✗ return this.visit(node.ternaryExpr);
3143 }
3144
3145 29380 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3146
3147 // Assign or compound assign operation
3148
1/2
✓ Branch 0 taken 29380 times.
✗ Branch 1 not taken.
29380 if node.op != AssignOp::OP_NONE {
3149 29380 IVisitable* lhsNode = node.lhs;
3150 29380 IVisitable* rhsNode = node.rhs;
3151 29380 ASTNode* baseNode = cast<ASTNode*>(node);
3152
3153 // Normal assignment
3154
2/2
✓ Branch 0 taken 26703 times.
✓ Branch 1 taken 2677 times.
29380 if node.op == AssignOp::OP_ASSIGN {
3155 26703 return exprResultAny(this.doAssignment(lhsNode, rhsNode, baseNode));
3156 }
3157
3158 // Compound assignment
3159 // Get symbol types of left and right side
3160 2677 const QualType lhsSTy = this.getEvaluatedType(lhsNode);
3161 2677 const QualType rhsSTy = this.getEvaluatedType(rhsNode);
3162
3163 // Retrieve rhs
3164 2677 LLVMExprResult rhs = this.visitExpr(rhsNode);
3165 // Retrieve lhs
3166 2677 LLVMExprResult lhs = this.visitExpr(lhsNode);
3167
3168 2677 LLVMExprResult res;
3169
10/11
✓ Branch 0 taken 1917 times.
✓ Branch 1 taken 92 times.
✓ Branch 2 taken 159 times.
✓ Branch 3 taken 106 times.
✓ Branch 4 taken 61 times.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 19 times.
✓ Branch 7 taken 30 times.
✓ Branch 8 taken 32 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 249 times.
2677 switch node.op {
3170 1917 case AssignOp::OP_PLUS_EQUAL: { res = this.conversionManager.getPlusEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); }
3171 92 case AssignOp::OP_MINUS_EQUAL: { res = this.conversionManager.getMinusEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); }
3172 159 case AssignOp::OP_MUL_EQUAL: { res = this.conversionManager.getMulEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); }
3173 106 case AssignOp::OP_DIV_EQUAL: { res = this.conversionManager.getDivEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); }
3174 61 case AssignOp::OP_REM_EQUAL: { res = this.conversionManager.getRemEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); }
3175 12 case AssignOp::OP_SHL_EQUAL: { res = this.conversionManager.getSHLEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); }
3176 19 case AssignOp::OP_SHR_EQUAL: { res = this.conversionManager.getSHREqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); }
3177 30 case AssignOp::OP_AND_EQUAL: { res = this.conversionManager.getAndEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); }
3178 32 case AssignOp::OP_OR_EQUAL: { res = this.conversionManager.getOrEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); }
3179 249 case AssignOp::OP_XOR_EQUAL: { res = this.conversionManager.getXorEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); }
3180 − default: { panic(Error("Assign op fall-through")); } // LCOV_EXCL_LINE
3181 }
3182
3183
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2677 times.
2677 if !res.ptr.isNull() { // The operation allocated more memory
3184
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if lhs.entry != nil<const SymbolTableEntry*> {
3185 ✗ this.updateAddress(lhs.entry, res.ptr);
3186 ✗ }
3187
2/2
✓ Branch 0 taken 845 times.
✓ Branch 1 taken 1832 times.
2677 } else if !res.value.isNull() { // The operation only updated the value
3188 // Store the result
3189 1832 lhs.value = res.value;
3190
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 1773 times.
1832 this.insertStore(lhs.value, lhs.ptr, lhs.entry != nil<const SymbolTableEntry*> && lhs.entry.isVolatile);
3191 2677 }
3192 2677 return exprResultAny(lhs);
3193 }
3194
3195 // This is a fallthrough case -> throw an error
3196 − panic(Error("AssignStmt fall-through")); // LCOV_EXCL_LINE
3197 }
3198
3199 // Enter the expression scope of the given expression, if it has one
3200 3688 f<Scope*> IRGenerator.enterExprScope(IVisitable* expr) {
3201 3688 Scope* exprScope = this.currentScope.getChildScope(getExprScopeId(expr));
3202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3688 times.
3688 if exprScope != nil<Scope*> {
3203 3688 this.compilerPass.changeToScope(exprScope, ScopeType::EXPR_BODY);
3204 3688 }
3205 7376 return exprScope;
3206 3688 }
3207
3208 // Leave the given expression scope, if it exists
3209 3690 p IRGenerator.leaveExprScope(Scope* exprScope) {
3210
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3688 times.
3690 if exprScope != nil<Scope*> {
3211 3688 this.compilerPass.changeToParentScope(ScopeType::EXPR_BODY);
3212 3688 }
3213 3690 }
3214
3215 2206 public f<Any> IRGenerator.visitTernaryExpr(ASTTernaryExprNode* node) {
3216 // Check if only one operand is present -> loop through
3217
1/2
✓ Branch 0 taken 2206 times.
✗ Branch 1 not taken.
2206 if node.falseExpr == nil<IVisitable*> {
3218 ✗ return this.visit(node.condition);
3219 }
3220
3221 2206 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3222
3223 // It is a ternary
3224 // Retrieve the condition value
3225 2206 const llvm::Value condValue = this.resolveValue(node.condition);
3226
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2204 times.
2206 IVisitable* trueNode = node.isShortened ? node.condition : node.trueExpr;
3227 2206 IVisitable* falseNode = node.falseExpr;
3228 2206 const TernaryExprData& data = node.data.get(this.manIdx);
3229
3230 2206 llvm::Value resultValue;
3231 2206 llvm::Value resultPtr;
3232 2206 const SymbolTableEntry* anonymousSymbol = nil<const SymbolTableEntry*>;
3233
4/4
✓ Branch 0 taken 1760 times.
✓ Branch 1 taken 446 times.
✓ Branch 2 taken 361 times.
✓ Branch 3 taken 1845 times.
2206 if this.hasCompileTimeValue(trueNode) && this.hasCompileTimeValue(falseNode) {
3234 // If both are constants, we can simply emit a selection instruction
3235 361 const llvm::Value trueValue = this.resolveValue(trueNode);
3236 361 const llvm::Value falseValue = this.resolveValue(falseNode);
3237 361 resultValue = this.builder.createSelect(condValue, trueValue, falseValue);
3238 361 } else {
3239 // We have at least one non-constant value, use branching to not perform both sides
3240 1845 const String codeLoc = node.codeLoc.toPrettyLineAndColumn();
3241 1845 llvm::BasicBlock condTrue = this.createBlock("cond.true." + codeLoc);
3242 1845 llvm::BasicBlock condFalse = this.createBlock("cond.false." + codeLoc);
3243 1845 const llvm::BasicBlock condExit = this.createBlock("cond.exit." + codeLoc);
3244
3245 // Jump from original block to true or false block, depending on condition
3246 1845 this.insertCondJump(condValue, condTrue, condFalse);
3247
3248 // Fill true block
3249 1845 this.switchToBlock(condTrue);
3250 1845 const QualType resultType = this.getEvaluatedType(cast<IVisitable*>(node));
3251 1845 llvm::Value trueValue;
3252 1845 llvm::Value truePtr;
3253 // The true branch has an expression scope for its temporaries, except for shortened ternaries (the condition is the branch)
3254 1845 Scope* trueScope = nil<Scope*>;
3255
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1843 times.
1845 if !node.isShortened {
3256 1843 trueScope = this.enterExprScope(trueNode);
3257 1843 }
3258
4/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1828 times.
✓ Branch 2 taken 122 times.
✓ Branch 3 taken 1723 times.
1845 if data.falseSideCallsCopyCtor || resultType.isRef() { // both sides or only the false side needs copy ctor call
3259 122 truePtr = this.resolveAddress(trueNode);
3260
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1714 times.
1845 } else if data.trueSideCallsCopyCtor { // only true side needs copy ctor call
3261 9 const llvm::Value originalPtr = this.resolveAddress(trueNode);
3262 // Allocate storage for the copy using the ternary's own result type, not trueNode's own evaluated type:
3263 // trueNode may be a reference (e.g. a 'const T&' parameter), whose LLVM type is just a pointer and would
3264 // undersize this alloca for the struct value the copy ctor is about to write into it.
3265 9 truePtr = this.insertAlloca(resultType);
3266 9 Vector<llvm::Value> ctorArgs;
3267 9 ctorArgs.pushBack(originalPtr);
3268 9 this.generateCtorOrDtorCall(truePtr, data.calledCopyCtor, ctorArgs);
3269 9 } else { // neither true nor false side need copy ctor call
3270 1714 trueValue = this.resolveValue(trueNode);
3271 1723 }
3272 1845 this.leaveExprScope(trueScope);
3273 // Set the true block to the current insert point, since it could have changed in the meantime
3274 1845 condTrue = this.builder.getInsertBlock();
3275 1845 this.insertJump(condExit);
3276
3277 // Fill false block
3278 1845 this.switchToBlock(condFalse);
3279 1845 llvm::Value falseValue;
3280 1845 llvm::Value falsePtr;
3281 // The false branch has an expression scope for its temporaries
3282 1845 Scope* falseScope = this.enterExprScope(falseNode);
3283
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1836 times.
✓ Branch 2 taken 114 times.
✓ Branch 3 taken 1731 times.
1845 if data.trueSideCallsCopyCtor || resultType.isRef() { // both sides or only the true side needs copy ctor call
3284 114 falsePtr = this.resolveAddress(falseNode);
3285
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1714 times.
1845 } else if data.falseSideCallsCopyCtor { // only false side needs copy ctor call
3286 17 const llvm::Value originalPtr = this.resolveAddress(falseNode);
3287 // See the analogous truePtr allocation above: use the ternary's result type, not falseNode's own
3288 // (possibly reference-qualified) evaluated type.
3289 17 falsePtr = this.insertAlloca(resultType);
3290 17 Vector<llvm::Value> ctorArgs;
3291 17 ctorArgs.pushBack(originalPtr);
3292 17 this.generateCtorOrDtorCall(falsePtr, data.calledCopyCtor, ctorArgs);
3293 17 } else { // neither true nor false side need copy ctor call
3294 1714 falseValue = this.resolveValue(falseNode);
3295 1731 }
3296 1845 this.leaveExprScope(falseScope);
3297 // Set the true block to the current insert point, since it could have changed in the meantime
3298 1845 condFalse = this.builder.getInsertBlock();
3299 1845 this.insertJump(condExit);
3300
3301 // Fill the exit block
3302 1845 this.switchToBlock(condExit);
3303
6/6
✓ Branch 0 taken 1836 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 1819 times.
✓ Branch 4 taken 131 times.
✓ Branch 5 taken 1714 times.
1845 if data.trueSideCallsCopyCtor || data.falseSideCallsCopyCtor || resultType.isRef() { // one side calls copy ctor
3304 131 llvm::Value phiInst = this.builder.createPhi(this.builder.getPtrTy(), "cond.result");
3305 131 phiInst.addIncoming(truePtr, condTrue);
3306 131 phiInst.addIncoming(falsePtr, condFalse);
3307
3/4
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 131 times.
131 if data.trueSideCallsCopyCtor && data.falseSideCallsCopyCtor { // both sides need copy ctor call
3308 ✗ resultPtr = this.insertAlloca(resultType);
3309 ✗ Vector<llvm::Value> ctorArgs;
3310 ✗ ctorArgs.pushBack(phiInst);
3311 ✗ this.generateCtorOrDtorCall(resultPtr, data.calledCopyCtor, ctorArgs);
3312 ✗ } else {
3313 131 resultPtr = phiInst;
3314 131 }
3315 131 } else { // neither true nor false side calls copy ctor
3316 1714 assert !trueValue.isNull();
3317 1714 llvm::Value phiInst = this.builder.createPhi(resultType.toLLVMType(this.sourceFile), "cond.result");
3318 1714 phiInst.addIncoming(trueValue, condTrue);
3319 1714 phiInst.addIncoming(falseValue, condFalse);
3320 1714 resultValue = phiInst;
3321 1714 }
3322
3323 // If we have an anonymous symbol for this ternary expr, make sure that it has an address to reference.
3324 1845 anonymousSymbol = this.currentScope.symbolTable.lookupAnonymous(cast<ASTNode*>(node));
3325
2/2
✓ Branch 0 taken 1804 times.
✓ Branch 1 taken 41 times.
1845 if anonymousSymbol != nil<const SymbolTableEntry*> {
3326
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 2 times.
41 if resultPtr.isNull() {
3327 2 resultPtr = this.insertAlloca(anonymousSymbol.getQualType());
3328 2 this.insertStore(resultValue, resultPtr);
3329 2 }
3330 41 this.updateAddress(anonymousSymbol, resultPtr);
3331 41 }
3332
3333 // Destruct the temporaries of the branch that was evaluated. This must happen here, after the result was copied out of the
3334 // branch and only for the branch that was taken, since the temporaries of the other one were never constructed.
3335
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1843 times.
1845 const bool trueNeedsCleanup = trueScope != nil<Scope*> && !trueScope.temporaryDtorsToCall.isEmpty();
3336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1845 times.
1845 const bool falseNeedsCleanup = falseScope != nil<Scope*> && !falseScope.temporaryDtorsToCall.isEmpty();
3337
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1843 times.
✓ Branch 2 taken 1842 times.
✓ Branch 3 taken 3 times.
1845 if trueNeedsCleanup || falseNeedsCleanup {
3338 3 const llvm::BasicBlock bCleanTrue = this.createBlock("cond.cleanup.true." + codeLoc);
3339 3 const llvm::BasicBlock bCleanFalse = this.createBlock("cond.cleanup.false." + codeLoc);
3340 3 const llvm::BasicBlock bCleanExit = this.createBlock("cond.cleanup.exit." + codeLoc);
3341 3 this.insertCondJump(condValue, bCleanTrue, bCleanFalse);
3342
3343 3 this.switchToBlock(bCleanTrue);
3344
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3 if trueNeedsCleanup {
3345 2 this.generateTemporariesCleanup(trueScope, getASTNode(trueNode));
3346 2 }
3347 3 this.insertJump(bCleanExit);
3348
3349 3 this.switchToBlock(bCleanFalse);
3350
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 if falseNeedsCleanup {
3351 1 this.generateTemporariesCleanup(falseScope, getASTNode(falseNode));
3352 1 }
3353 3 this.insertJump(bCleanExit);
3354
3355 3 this.switchToBlock(bCleanExit);
3356 3 }
3357 1845 }
3358
3359 2206 LLVMExprResult res;
3360 2206 res.value = resultValue;
3361 2206 res.ptr = resultPtr;
3362 2206 res.entry = anonymousSymbol;
3363 2206 return exprResultAny(res);
3364 }
3365
3366 // Shared implementation of visitLogicalOrExpr and visitLogicalAndExpr
3367 5307 f<Any> IRGenerator.visitLogicalExpr(ASTNode* node, const Vector<IVisitable*>& operands, bool isOr) {
3368 5307 this.diGenerator.setSourceLocation(node);
3369
3370 // Create exit block for short-circuiting
3371 5307 const String codeLoc = node.codeLoc.toPrettyLineAndColumn();
3372 5307 const String prefix = String(isOr ? "lor" : "land");
3373 5307 const llvm::BasicBlock bExit = this.createBlock(prefix + ".exit." + codeLoc);
3374
3375 // Visit the first operand
3376 5307 const llvm::Value firstOperandValue = this.resolveValue(operands.front());
3377
3378 // Prepare an array for value-to-block-mapping
3379 5307 Vector<llvm::BasicBlock> shortCircuitBlocks;
3380 5307 Vector<llvm::Value> shortCircuitValues;
3381 5307 shortCircuitBlocks.reserve(operands.getSize());
3382 5307 shortCircuitValues.reserve(operands.getSize());
3383 // The first element is the first operand value with the original block
3384 5307 shortCircuitBlocks.pushBack(this.builder.getInsertBlock());
3385 5307 shortCircuitValues.pushBack(firstOperandValue);
3386 // Create a block for each additional operand and save it to the mapping
3387
2/2
✓ Branch 0 taken 6496 times.
✓ Branch 1 taken 5307 times.
11803 for unsigned long i = 1l; i < operands.getSize(); i++ {
3388 6496 shortCircuitBlocks.pushBack(this.createBlock(prefix + "." + toString(i) + "." + codeLoc));
3389 6496 shortCircuitValues.pushBack(llvm::Value());
3390 6496 }
3391 // Create conditional jump to the exit block if the first operand was true (or), otherwise to the next block
3392
2/2
✓ Branch 0 taken 3375 times.
✓ Branch 1 taken 1932 times.
5307 if isOr {
3393 3375 this.insertCondJump(firstOperandValue, bExit, shortCircuitBlocks.get(1l));
3394 3375 } else {
3395 1932 this.insertCondJump(firstOperandValue, shortCircuitBlocks.get(1l), bExit);
3396 1932 }
3397
3398 // Create block for each operand
3399
2/2
✓ Branch 0 taken 6496 times.
✓ Branch 1 taken 5307 times.
11803 for unsigned long i = 1l; i < operands.getSize(); i++ {
3400 // Switch to the next block
3401 6496 this.switchToBlock(shortCircuitBlocks.get(i));
3402 // Evaluate operand and save the result in the mapping. The temporaries of the operand are destructed within its block, since
3403 // they only exist if the operand was evaluated
3404 6496 llvm::Value& operandValue = shortCircuitValues.get(i);
3405 6496 operandValue = this.resolveValueInExprScope(operands.get(i));
3406 // Replace the array entry with the current insert block, since the insert block could have changed in the meantime
3407 6496 llvm::BasicBlock& operandBlock = shortCircuitBlocks.get(i);
3408 6496 operandBlock = this.builder.getInsertBlock();
3409 // Check if there are more blocks to process
3410
2/2
✓ Branch 0 taken 5307 times.
✓ Branch 1 taken 1189 times.
6496 if i == operands.getSize() - 1l {
3411 // Insert a simple jump to the exit block for the last block
3412 5307 this.insertJump(bExit);
3413
2/2
✓ Branch 0 taken 954 times.
✓ Branch 1 taken 235 times.
6496 } else if isOr {
3414 // Create conditional jump to the exit block if the operand was true, otherwise to the next block
3415 954 this.insertCondJump(operandValue, bExit, shortCircuitBlocks.get(i + 1l));
3416 954 } else {
3417 // Create conditional jump to the next block if the operand was true, otherwise to the exit block
3418 235 this.insertCondJump(operandValue, shortCircuitBlocks.get(i + 1l), bExit);
3419 1189 }
3420 6496 }
3421
3422 // Get the result with the phi node
3423 5307 this.switchToBlock(bExit);
3424 5307 llvm::Value res = this.builder.createPhi(firstOperandValue.getType(), isOr ? "lor_phi" : "land_phi");
3425
2/2
✓ Branch 0 taken 11803 times.
✓ Branch 1 taken 5307 times.
17110 for unsigned long i = 0l; i < shortCircuitBlocks.getSize(); i++ {
3426 11803 res.addIncoming(shortCircuitValues.get(i), shortCircuitBlocks.get(i));
3427 11803 }
3428
3429 // Return the result
3430 10614 return exprResultAny(exprResultWithValue(res));
3431 5307 }
3432
3433 3375 public f<Any> IRGenerator.visitLogicalOrExpr(ASTLogicalOrExprNode* node) {
3434 // Check if only one operand is present -> loop through
3435
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3375 times.
3375 if node.operands.getSize() == 1l {
3436 ✗ return this.visit(node.operands.front());
3437 }
3438
3439 // It is a logical or expression
3440 3375 return this.visitLogicalExpr(cast<ASTNode*>(node), node.operands, true);
3441 }
3442
3443 1932 public f<Any> IRGenerator.visitLogicalAndExpr(ASTLogicalAndExprNode* node) {
3444 // Check if only one operand is present -> loop through
3445
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1932 times.
1932 if node.operands.getSize() == 1l {
3446 ✗ return this.visit(node.operands.front());
3447 }
3448
3449 // It is a logical and expression
3450 1932 return this.visitLogicalExpr(cast<ASTNode*>(node), node.operands, false);
3451 }
3452
3453 // Kinds of bitwise expressions
3454 type BitwiseExprKind enum {
3455 OR,
3456 XOR,
3457 AND
3458 }
3459
3460 // Shared implementation of visitBitwiseOrExpr, visitBitwiseXorExpr and visitBitwiseAndExpr
3461 808 f<Any> IRGenerator.visitBitwiseExpr(ASTNode* node, const Vector<IVisitable*>& operands, BitwiseExprKind kind) {
3462 808 this.diGenerator.setSourceLocation(node);
3463
3464 // Evaluate first operand
3465 808 IVisitable* lhsNode = operands.front();
3466 808 const QualType lhsSTy = this.getEvaluatedType(lhsNode);
3467 808 LLVMExprResult res = this.visitExpr(lhsNode);
3468
3469 // Evaluate all additional operands
3470
2/2
✓ Branch 0 taken 816 times.
✓ Branch 1 taken 808 times.
1624 for unsigned long i = 1l; i < operands.getSize(); i++ {
3471 // Evaluate the operand
3472 816 IVisitable* rhsNode = operands.get(i);
3473 816 const QualType rhsSTy = this.getEvaluatedType(rhsNode);
3474 816 LLVMExprResult rhs = this.visitExpr(rhsNode);
3475
3/4
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 429 times.
✓ Branch 3 taken 305 times.
816 switch kind {
3476 429 case BitwiseExprKind::OR: { res = this.conversionManager.getBitwiseOrInst(node, res, lhsSTy, rhs, rhsSTy, i - 1l); }
3477 82 case BitwiseExprKind::XOR: { res = this.conversionManager.getBitwiseXorInst(node, res, lhsSTy, rhs, rhsSTy, i - 1l); }
3478 305 case BitwiseExprKind::AND: { res = this.conversionManager.getBitwiseAndInst(node, res, lhsSTy, rhs, rhsSTy, i - 1l); }
3479 }
3480 816 }
3481
3482 // Return result
3483 808 return exprResultAny(res);
3484 }
3485
3486 425 public f<Any> IRGenerator.visitBitwiseOrExpr(ASTBitwiseOrExprNode* node) {
3487 // Check if only one operand is present -> loop through
3488
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 425 times.
425 if node.operands.getSize() == 1l {
3489 ✗ return this.visit(node.operands.front());
3490 }
3491 // It is a bitwise or expression
3492 425 return this.visitBitwiseExpr(cast<ASTNode*>(node), node.operands, BitwiseExprKind::OR);
3493 }
3494
3495 80 public f<Any> IRGenerator.visitBitwiseXorExpr(ASTBitwiseXorExprNode* node) {
3496 // Check if only one operand is present -> loop through
3497
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if node.operands.getSize() == 1l {
3498 ✗ return this.visit(node.operands.front());
3499 }
3500 // It is a bitwise xor expression
3501 80 return this.visitBitwiseExpr(cast<ASTNode*>(node), node.operands, BitwiseExprKind::XOR);
3502 }
3503
3504 303 public f<Any> IRGenerator.visitBitwiseAndExpr(ASTBitwiseAndExprNode* node) {
3505 // Check if only one operand is present -> loop through
3506
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 303 times.
303 if node.operands.getSize() == 1l {
3507 ✗ return this.visit(node.operands.front());
3508 }
3509 // It is a bitwise and expression
3510 303 return this.visitBitwiseExpr(cast<ASTNode*>(node), node.operands, BitwiseExprKind::AND);
3511 }
3512
3513 27520 public f<Any> IRGenerator.visitEqualityExpr(ASTEqualityExprNode* node) {
3514 // Check if only one operand is present -> loop through
3515
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27520 times.
27520 if node.operands.getSize() == 1l {
3516 ✗ return this.visit(node.operands.front());
3517 }
3518
3519 27520 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3520
3521 // It is an equality expression
3522 // Evaluate lhs
3523 27520 IVisitable* lhsNode = node.operands.get(0l);
3524 27520 const QualType lhsSTy = this.getEvaluatedType(lhsNode);
3525 27520 LLVMExprResult res = this.visitExpr(lhsNode);
3526
3527 // Evaluate rhs
3528 27520 IVisitable* rhsNode = node.operands.get(1l);
3529 27520 const QualType rhsSTy = this.getEvaluatedType(rhsNode);
3530 27520 LLVMExprResult rhs = this.visitExpr(rhsNode);
3531
3532 // Retrieve the result value, based on the exact operator
3533 27520 ASTNode* baseNode = cast<ASTNode*>(node);
3534
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 22765 times.
✓ Branch 2 taken 4755 times.
27520 switch node.op {
3535 22765 case EqualityOp::OP_EQUAL: { res = this.conversionManager.getEqualInst(baseNode, res, lhsSTy, rhs, rhsSTy); }
3536 4755 case EqualityOp::OP_NOT_EQUAL: { res = this.conversionManager.getNotEqualInst(baseNode, res, lhsSTy, rhs, rhsSTy); }
3537 − default: { panic(Error("EqualityExpr fall-through")); } // LCOV_EXCL_LINE
3538 }
3539
3540 // Return the result
3541 27520 return exprResultAny(res);
3542 }
3543
3544 16919 public f<Any> IRGenerator.visitRelationalExpr(ASTRelationalExprNode* node) {
3545 // Check if only one operand is present -> loop through
3546
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16919 times.
16919 if node.operands.getSize() == 1l {
3547 ✗ return this.visit(node.operands.front());
3548 }
3549
3550 16919 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3551
3552 // It is a relational expression
3553 // Evaluate lhs
3554 16919 IVisitable* lhsNode = node.operands.get(0l);
3555 16919 const QualType lhsSTy = this.getEvaluatedType(lhsNode);
3556 16919 LLVMExprResult res = this.visitExpr(lhsNode);
3557
3558 // Evaluate rhs
3559 16919 IVisitable* rhsNode = node.operands.get(1l);
3560 16919 const QualType rhsSTy = this.getEvaluatedType(rhsNode);
3561 16919 LLVMExprResult rhs = this.visitExpr(rhsNode);
3562
3563 // Retrieve the result value, based on the exact operator
3564 16919 ASTNode* baseNode = cast<ASTNode*>(node);
3565
4/5
✓ Branch 0 taken 6953 times.
✓ Branch 1 taken 3636 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2181 times.
✓ Branch 4 taken 4149 times.
16919 switch node.op {
3566 6953 case RelationalOp::OP_LESS: { res = this.conversionManager.getLessInst(baseNode, res, lhsSTy, rhs, rhsSTy); }
3567 3636 case RelationalOp::OP_GREATER: { res = this.conversionManager.getGreaterInst(baseNode, res, lhsSTy, rhs, rhsSTy); }
3568 2181 case RelationalOp::OP_LESS_EQUAL: { res = this.conversionManager.getLessEqualInst(baseNode, res, lhsSTy, rhs, rhsSTy); }
3569 4149 case RelationalOp::OP_GREATER_EQUAL: { res = this.conversionManager.getGreaterEqualInst(baseNode, res, lhsSTy, rhs, rhsSTy); }
3570 − default: { panic(Error("EqualityExpr fall-through")); } // LCOV_EXCL_LINE
3571 }
3572
3573 // Return the result
3574 16919 return exprResultAny(res);
3575 }
3576
3577 753 public f<Any> IRGenerator.visitShiftExpr(ASTShiftExprNode* node) {
3578 // Check if only one operand is present -> loop through
3579
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 753 times.
753 if node.operands.getSize() == 1l {
3580 ✗ return this.visit(node.operands.front());
3581 }
3582
3583 753 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3584
3585 // It is a shift expression
3586 // Evaluate first operand
3587 753 IVisitable* lhsNode = node.operands.front();
3588 753 QualType lhsSTy = this.getEvaluatedType(lhsNode);
3589 753 LLVMExprResult lhs = this.visitExpr(lhsNode);
3590
3591 753 ASTNode* baseNode = cast<ASTNode*>(node);
3592 753 ShiftOpQueue opQueue = node.opQueue;
3593 753 unsigned long operandIndex = 1l;
3594
2/2
✓ Branch 0 taken 986 times.
✓ Branch 1 taken 753 times.
1739 while !opQueue.isEmpty() {
3595 986 const unsigned long operatorIndex = operandIndex - 1l;
3596 // Evaluate next operand
3597 986 IVisitable* rhsNode = node.operands.get(operandIndex);
3598 986 operandIndex++;
3599 986 assert rhsNode != nil<IVisitable*>;
3600 986 const QualType rhsSTy = this.getEvaluatedType(rhsNode);
3601 986 LLVMExprResult rhs = this.visitExpr(rhsNode);
3602
3603 // Retrieve the result, based on the exact operator
3604 986 const Pair<ShiftOp, QualType>& opPair = opQueue.front();
3605 986 const ShiftOp currentOp = opPair.getFirst();
3606
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 666 times.
✓ Branch 2 taken 320 times.
986 switch currentOp {
3607 666 case ShiftOp::OP_SHIFT_LEFT: { lhs = this.conversionManager.getShiftLeftInst(baseNode, lhs, lhsSTy, rhs, rhsSTy, operatorIndex); }
3608 320 case ShiftOp::OP_SHIFT_RIGHT: { lhs = this.conversionManager.getShiftRightInst(baseNode, lhs, lhsSTy, rhs, rhsSTy, operatorIndex); }
3609 − default: { panic(Error("AdditiveExpr fall-through")); } // LCOV_EXCL_LINE
3610 }
3611
3612 // Retrieve the new lhs symbol type
3613 986 lhsSTy = opPair.getSecond();
3614
3615 986 opQueue.pop();
3616 986 }
3617
3618 // Return the result
3619 1506 return exprResultAny(lhs);
3620 753 }
3621
3622 15404 public f<Any> IRGenerator.visitAdditiveExpr(ASTAdditiveExprNode* node) {
3623 // Check if only one operand is present -> loop through
3624
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15404 times.
15404 if node.operands.getSize() == 1l {
3625 ✗ return this.visit(node.operands.front());
3626 }
3627
3628 15404 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3629
3630 // It is an additive expression
3631 // Evaluate first operand
3632 15404 IVisitable* lhsNode = node.operands.get(0l);
3633 15404 QualType lhsSTy = this.getEvaluatedType(lhsNode);
3634 15404 LLVMExprResult lhs = this.visitExpr(lhsNode);
3635
3636 15404 ASTNode* baseNode = cast<ASTNode*>(node);
3637 15404 AdditiveOpQueue opQueue = node.opQueue;
3638 15404 unsigned long operandIndex = 1l;
3639
2/2
✓ Branch 0 taken 17083 times.
✓ Branch 1 taken 15404 times.
32487 while !opQueue.isEmpty() {
3640 17083 const unsigned long operatorIndex = operandIndex - 1l;
3641 // Evaluate next operand
3642 17083 IVisitable* rhsNode = node.operands.get(operandIndex);
3643 17083 operandIndex++;
3644 17083 assert rhsNode != nil<IVisitable*>;
3645 17083 const QualType rhsSTy = this.getEvaluatedType(rhsNode);
3646 17083 LLVMExprResult rhs = this.visitExpr(rhsNode);
3647
3648 // Retrieve the result, based on the exact operator
3649 17083 const Pair<AdditiveOp, QualType>& opPair = opQueue.front();
3650 17083 const AdditiveOp currentOp = opPair.getFirst();
3651
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 10363 times.
✓ Branch 2 taken 6720 times.
17083 switch currentOp {
3652 10363 case AdditiveOp::OP_PLUS: { lhs = this.conversionManager.getPlusInst(baseNode, lhs, lhsSTy, rhs, rhsSTy, operatorIndex); }
3653 6720 case AdditiveOp::OP_MINUS: { lhs = this.conversionManager.getMinusInst(baseNode, lhs, lhsSTy, rhs, rhsSTy, operatorIndex); }
3654 − default: { panic(Error("AdditiveExpr fall-through")); } // LCOV_EXCL_LINE
3655 }
3656
3657 // Retrieve the new lhs symbol type
3658 17083 lhsSTy = opPair.getSecond();
3659
3660 17083 opQueue.pop();
3661 17083 }
3662
3663 // Return the result
3664 30808 return exprResultAny(lhs);
3665 15404 }
3666
3667 2867 public f<Any> IRGenerator.visitMultiplicativeExpr(ASTMultiplicativeExprNode* node) {
3668 // Check if only one operand is present -> loop through
3669
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2867 times.
2867 if node.operands.getSize() == 1l {
3670 ✗ return this.visit(node.operands.front());
3671 }
3672
3673 2867 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3674
3675 // It is an additive expression
3676 // Evaluate first operand
3677 2867 IVisitable* lhsNode = node.operands.get(0l);
3678 2867 QualType lhsSTy = this.getEvaluatedType(lhsNode);
3679 2867 LLVMExprResult res = this.visitExpr(lhsNode);
3680
3681 2867 ASTNode* baseNode = cast<ASTNode*>(node);
3682 2867 MultiplicativeOpQueue opQueue = node.opQueue;
3683 2867 unsigned long operandIndex = 1l;
3684
2/2
✓ Branch 0 taken 2867 times.
✓ Branch 1 taken 2957 times.
5824 while !opQueue.isEmpty() {
3685 2957 const unsigned long operatorIndex = operandIndex - 1l;
3686 // Evaluate next operand
3687 2957 IVisitable* rhsNode = node.operands.get(operandIndex);
3688 2957 operandIndex++;
3689 2957 assert rhsNode != nil<IVisitable*>;
3690 2957 const QualType rhsSTy = this.getEvaluatedType(rhsNode);
3691 2957 LLVMExprResult rhs = this.visitExpr(rhsNode);
3692
3693 // Retrieve the result, based on the exact operator
3694 2957 const Pair<MultiplicativeOp, QualType>& opPair = opQueue.front();
3695 2957 const MultiplicativeOp currentOp = opPair.getFirst();
3696
3/4
✓ Branch 0 taken 339 times.
✓ Branch 1 taken 2409 times.
✓ Branch 2 taken 209 times.
✗ Branch 3 not taken.
2957 switch currentOp {
3697 2409 case MultiplicativeOp::OP_MUL: { res = this.conversionManager.getMulInst(baseNode, res, lhsSTy, rhs, rhsSTy, operatorIndex); }
3698 339 case MultiplicativeOp::OP_DIV: { res = this.conversionManager.getDivInst(baseNode, res, lhsSTy, rhs, rhsSTy, operatorIndex); }
3699 209 case MultiplicativeOp::OP_REM: { res = this.conversionManager.getRemInst(baseNode, res, lhsSTy, rhs, rhsSTy); }
3700 − default: { panic(Error("MultiplicativeExpr fall-through")); } // LCOV_EXCL_LINE
3701 }
3702
3703 // Retrieve the new lhs symbol type
3704 2957 lhsSTy = opPair.getSecond();
3705 2957 opQueue.pop();
3706 2957 }
3707
3708 // Return the result
3709 5734 return exprResultAny(res);
3710 2867 }
3711
3712 11775 public f<Any> IRGenerator.visitCastExpr(ASTCastExprNode* node) {
3713 // Check if only one operand is present -> loop through
3714
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11775 times.
11775 if !node.isCast {
3715 ✗ return this.visit(node.prefixUnaryExpr);
3716 }
3717
3718 11775 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3719
3720 // It is a cast expression
3721 // Retrieve target symbol type
3722 11775 const QualType targetSTy = this.getEvaluatedType(cast<IVisitable*>(node));
3723
3724 // Evaluate rhs
3725 11775 IVisitable* rhsNode = node.assignExpr;
3726 11775 const QualType rhsSTy = this.getEvaluatedType(rhsNode);
3727 11775 LLVMExprResult rhs = this.visitExpr(rhsNode);
3728
3729 // Retrieve the result value
3730 11775 const LLVMExprResult res = this.conversionManager.getCastInst(cast<ASTNode*>(node), targetSTy, rhs, rhsSTy);
3731
3732 // Return the result
3733 11775 return exprResultAny(res);
3734 }
3735
3736 7154 public f<Any> IRGenerator.visitPrefixUnaryExpr(ASTPrefixUnaryExprNode* node) {
3737 // If no operator is applied, simply visit the atomic expression
3738
1/2
✓ Branch 0 taken 7154 times.
✗ Branch 1 not taken.
7154 if node.op == PrefixUnaryOp::OP_NONE {
3739 ✗ return this.visit(node.postfixUnaryExpr);
3740 }
3741
3742 7154 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3743
3744 // Evaluate lhs
3745 7154 IVisitable* lhsNode = node.prefixUnaryExpr;
3746 7154 const QualType lhsSTy = this.getEvaluatedType(lhsNode);
3747 7154 LLVMExprResult lhs = this.visitExpr(lhsNode);
3748 7154 ASTNode* baseNode = cast<ASTNode*>(node);
3749
3750
7/8
✓ Branch 0 taken 948 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 3609 times.
✓ Branch 4 taken 33 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 966 times.
✓ Branch 7 taken 1543 times.
7154 switch node.op {
3751 case PrefixUnaryOp::OP_MINUS: {
3752 // Execute operation
3753 948 lhs = this.conversionManager.getPrefixMinusInst(baseNode, lhs, lhsSTy);
3754
3755 // This operator can not work in-place, so we need additional memory
3756 948 lhs.ptr = this.insertAlloca(lhs.value.getType());
3757
3758 // Store the new value
3759 948 this.insertStore(lhs.value, lhs.ptr);
3760 948 }
3761 case PrefixUnaryOp::OP_PLUS_PLUS: {
3762 // Execute operation
3763 16 const LLVMExprResult res = this.conversionManager.getPrefixPlusPlusInst(baseNode, lhs, lhsSTy);
3764 16 lhs.value = res.value;
3765
3766 // If this operation happens on a volatile variable, store the value directly
3767
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 if lhs.entry != nil<const SymbolTableEntry*> && lhs.entry.isVolatile {
3768 ✗ this.insertStore(lhs.value, lhs.ptr, true);
3769 ✗ }
3770
3771 // Save to the existing address if possible, otherwise (e.g. for literals) allocate new space
3772
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2 times.
16 if lhs.ptr.isNull() {
3773 2 lhs.ptr = this.insertAlloca(lhs.value.getType());
3774 2 }
3775
3776 // Store the new value
3777 16 this.insertStore(lhs.value, lhs.ptr);
3778 16 }
3779 case PrefixUnaryOp::OP_MINUS_MINUS: {
3780 // Execute operation
3781 39 const LLVMExprResult res = this.conversionManager.getPrefixMinusMinusInst(baseNode, lhs, lhsSTy);
3782 39 lhs.value = res.value;
3783
3784 // If this operation happens on a volatile variable, store the value directly
3785
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 if lhs.entry != nil<const SymbolTableEntry*> && lhs.entry.isVolatile {
3786 ✗ this.insertStore(lhs.value, lhs.ptr, true);
3787 ✗ }
3788
3789 // Save to the existing address if possible, otherwise (e.g. for literals) allocate new space
3790
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 2 times.
39 if lhs.ptr.isNull() {
3791 2 lhs.ptr = this.insertAlloca(lhs.value.getType());
3792 2 }
3793
3794 // Store the new value
3795 39 this.insertStore(lhs.value, lhs.ptr);
3796 39 }
3797 case PrefixUnaryOp::OP_NOT: {
3798 // Execute operation
3799 3609 lhs = this.conversionManager.getPrefixNotInst(baseNode, lhs, lhsSTy);
3800 3609 }
3801 case PrefixUnaryOp::OP_BITWISE_NOT: {
3802 // Execute operation
3803 33 lhs = this.conversionManager.getPrefixBitwiseNotInst(baseNode, lhs, lhsSTy);
3804 33 }
3805 case PrefixUnaryOp::OP_DEREFERENCE: {
3806 // If only .refPtr is filled, we can't simply rewire the fields, but need to actually perform a load.
3807 // For that we can use resolveValue().
3808
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 946 times.
966 const bool onlyRefPtrIsFilled = lhs.value.isNull() && lhs.ptr.isNull();
3809 // Rewire the fields
3810
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 903 times.
966 const llvm::Value newRefPtr = onlyRefPtrIsFilled ? this.resolveValue(lhsNode, lhs) : lhs.ptr;
3811 966 const llvm::Value newPtr = lhs.value;
3812 966 LLVMExprResult newLhs;
3813 966 newLhs.ptr = newPtr;
3814 966 newLhs.refPtr = newRefPtr;
3815 966 lhs = newLhs;
3816 966 }
3817 case PrefixUnaryOp::OP_ADDRESS_OF: {
3818 // If only .value is filled, we can't simply rewire the fields, but need to actually perform alloca + store.
3819 // For that we can use resolveAddress().
3820
2/2
✓ Branch 0 taken 1312 times.
✓ Branch 1 taken 231 times.
1543 const bool onlyValueIsFilled = lhs.ptr.isNull() && lhs.refPtr.isNull();
3821 // Rewire the fields
3822
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1539 times.
1543 const llvm::Value newValue = onlyValueIsFilled ? this.resolveAddress(lhs) : lhs.ptr;
3823 1543 const llvm::Value newPtr = lhs.refPtr;
3824 1543 LLVMExprResult newLhs;
3825 1543 newLhs.value = newValue;
3826 1543 newLhs.ptr = newPtr;
3827 1543 lhs = newLhs;
3828 1543 }
3829 default: {
3830 − panic(Error("PrefixUnaryExpr fall-through")); // LCOV_EXCL_LINE
3831 }
3832 }
3833
3834 7154 return exprResultAny(lhs);
3835 }
3836
3837 102178 public f<Any> IRGenerator.visitPostfixUnaryExpr(ASTPostfixUnaryExprNode* node) {
3838 // If no operator is applied, simply visit the atomic expression
3839
1/2
✓ Branch 0 taken 102178 times.
✗ Branch 1 not taken.
102178 if node.op == PostfixUnaryOp::OP_NONE {
3840 ✗ return this.visit(node.atomicExpr);
3841 }
3842
3843 102178 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
3844
3845 // Evaluate lhs
3846 102178 IVisitable* lhsNode = node.postfixUnaryExpr;
3847 102178 QualType lhsSTy = this.getEvaluatedType(lhsNode);
3848 102178 LLVMExprResult lhs = this.visitExpr(lhsNode);
3849 102178 ASTNode* baseNode = cast<ASTNode*>(node);
3850
3851
5/6
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12559 times.
✓ Branch 3 taken 80646 times.
✓ Branch 4 taken 8143 times.
✓ Branch 5 taken 737 times.
102178 switch node.op {
3852 case PostfixUnaryOp::OP_SUBSCRIPT: {
3853 12559 IVisitable* indexExpr = node.subscriptIndexExpr;
3854
3855 // Check if we need to generate a call to an overloaded operator function
3856
2/2
✓ Branch 0 taken 924 times.
✓ Branch 1 taken 11635 times.
12559 if this.conversionManager.callsOverloadedOpFct(baseNode, 0l) {
3857 924 const Resolver lhsV = valueResolver(lhsSTy, &lhs);
3858 924 const Resolver lhsP = addressResolver(&lhs);
3859 924 const Resolver idxV = exprValueResolver(indexExpr);
3860 924 const Resolver idxP = exprAddressResolver(indexExpr);
3861 924 const OperandResolvers resolvers = OperandResolvers(lhsV, lhsP, idxV, idxP);
3862 924 lhs = this.conversionManager.callOperatorOverloadFct(baseNode, resolvers, 2l, 0l);
3863 924 } else {
3864 11635 lhsSTy = lhsSTy.removeReferenceWrapper();
3865
3866 // Get the index value
3867 11635 const llvm::Value indexValue = this.resolveValue(indexExpr);
3868 // Come up with the address
3869
4/4
✓ Branch 0 taken 9856 times.
✓ Branch 1 taken 1779 times.
✓ Branch 2 taken 1559 times.
✓ Branch 3 taken 10076 times.
11635 if lhsSTy.isArray() && cast<unsigned long>(lhsSTy.getArraySize()) != ARRAY_SIZE_UNKNOWN { // Array
3870 // Make sure the address is present
3871 1559 this.resolveAddress(lhs);
3872
3873 // Calculate address of array item
3874 1559 const llvm::Type lhsTy = lhsSTy.toLLVMType(this.sourceFile);
3875 1559 Vector<llvm::Value> indices;
3876 1559 indices.pushBack(this.getInt64(0l));
3877 1559 indices.pushBack(indexValue);
3878 1559 lhs.ptr = this.insertInBoundsGEP(lhsTy, lhs.ptr, indices);
3879 1559 } else { // Pointer
3880 // Now the pointer is the value
3881 10076 lhs.ptr = this.resolveValue(lhsNode, lhs);
3882
3883 10076 const QualType lhsContainedSTy = lhsSTy.getContained();
3884 10076 const llvm::Type lhsTy = lhsContainedSTy.toLLVMType(this.sourceFile);
3885 // Calculate address of pointer item
3886 10076 lhs.ptr = this.insertInBoundsGEP(lhsTy, lhs.ptr, indexValue);
3887 10076 }
3888
3889 // Reset value and entry
3890 11635 lhs.value = llvm::Value();
3891 11635 lhs.entry = nil<const SymbolTableEntry*>;
3892 11635 }
3893 12559 }
3894 case PostfixUnaryOp::OP_MEMBER_ACCESS: {
3895 // Get the address of the struct/union instance
3896 80646 this.resolveAddress(lhs);
3897 80646 lhsSTy = lhsSTy.removeReferenceWrapper();
3898
3899 // Auto de-reference pointer
3900 80646 this.autoDeReferencePtr(lhs.ptr, lhsSTy);
3901
3902 80646 const String& fieldName = node.identifier;
3903
3904 // Union field access is handled separately, since it is backed by a tagged { i32 tag, ..., payload } layout
3905 // instead of one LLVM struct member per Spice field (see Type::toLLVMType's TY_UNION branch), and needs a
3906 // runtime tag read-guard/write-update around the payload access.
3907
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 80581 times.
80646 if lhsSTy.is(SuperType::TY_UNION) {
3908 65 this.generateUnionFieldAccess(node, lhs, lhsSTy, fieldName);
3909 65 } else {
3910 80581 assert lhsSTy.is(SuperType::TY_STRUCT);
3911
3912 // Retrieve struct
3913 80581 Struct* spiceStruct = lhsSTy.getStruct(cast<ASTNode*>(node));
3914 80581 assert spiceStruct != nil<Struct*>;
3915
3916 // Retrieve field entry
3917 80581 Vector<unsigned long> indexPath;
3918 80581 lhs.entry = spiceStruct.lookupInComposedFields(fieldName, indexPath);
3919 80581 assert lhs.entry != nil<const SymbolTableEntry*>;
3920 80581 const QualType fieldSymbolType = lhs.entry.getQualType();
3921
3922 // Get address of the field in the struct instance
3923 80581 Vector<llvm::Value> indices;
3924 80581 indices.pushBack(this.getInt64(0l));
3925
2/2
✓ Branch 0 taken 80601 times.
✓ Branch 1 taken 80581 times.
161182 foreach const unsigned long index : indexPath {
3926 80601 indices.pushBack(this.getInt32(cast<long>(index)));
3927 80601 }
3928 80581 const String name = fieldName + ".addr";
3929 80581 const llvm::Value memberAddr = this.insertInBoundsGEP(lhsSTy.toLLVMType(this.sourceFile), lhs.ptr, indices, name);
3930
3931 // Set as ptr or refPtr, depending on the type
3932
2/2
✓ Branch 0 taken 905 times.
✓ Branch 1 taken 79676 times.
80581 if fieldSymbolType.isRef() {
3933 905 lhs.ptr = llvm::Value();
3934 905 lhs.refPtr = memberAddr;
3935 905 } else {
3936 79676 lhs.ptr = memberAddr;
3937 79676 lhs.refPtr = llvm::Value();
3938 79676 }
3939
3940 // Reset the value
3941 80581 lhs.value = llvm::Value();
3942 80581 }
3943 80646 }
3944 case PostfixUnaryOp::OP_PLUS_PLUS: {
3945 // Make sure a value is present
3946 8143 this.resolveValue(lhsNode, lhs);
3947
3948 // Allocate new local variable if required
3949
2/2
✓ Branch 0 taken 8139 times.
✓ Branch 1 taken 4 times.
8143 if lhs.ptr.isNull() {
3950 4 assert !lhs.value.isNull();
3951 4 lhs.ptr = this.insertAlloca(lhs.value.getType());
3952 4 }
3953
3954 // Execute operation
3955 8143 const LLVMExprResult res = this.conversionManager.getPostfixPlusPlusInst(baseNode, lhs, lhsSTy);
3956
3957 // Save the new value to the old address
3958
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 8131 times.
8143 if this.conversionManager.callsOverloadedOpFct(baseNode, 0l) {
3959 12 lhs.value = res.value;
3960 12 lhs.ptr = res.ptr;
3961 12 } else {
3962
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 8118 times.
8131 this.insertStore(res.value, lhs.ptr, lhs.entry != nil<const SymbolTableEntry*> && lhs.entry.isVolatile);
3963 8131 lhs.ptr = llvm::Value();
3964 8131 }
3965 8143 }
3966 case PostfixUnaryOp::OP_MINUS_MINUS: {
3967 // Make sure a value is present
3968 737 this.resolveValue(lhsNode, lhs);
3969
3970 // Allocate new local variable if required
3971
2/2
✓ Branch 0 taken 733 times.
✓ Branch 1 taken 4 times.
737 if lhs.ptr.isNull() {
3972 4 assert !lhs.value.isNull();
3973 4 lhs.ptr = this.insertAlloca(lhs.value.getType());
3974 4 }
3975
3976 // Execute operation
3977 737 const LLVMExprResult res = this.conversionManager.getPostfixMinusMinusInst(baseNode, lhs, lhsSTy);
3978
3979 // Save the new value to the old address
3980
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 727 times.
737 if this.conversionManager.callsOverloadedOpFct(baseNode, 0l) {
3981 10 lhs.value = res.value;
3982 10 lhs.ptr = res.ptr;
3983 10 } else {
3984
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 719 times.
727 this.insertStore(res.value, lhs.ptr, lhs.entry != nil<const SymbolTableEntry*> && lhs.entry.isVolatile);
3985 727 lhs.ptr = llvm::Value();
3986 727 }
3987 737 }
3988 case PostfixUnaryOp::OP_ERR_PROPAGATION: {
3989 93 lhs = this.generateErrorPropagation(node, lhs);
3990 93 }
3991 default: {
3992 − panic(Error("PostfixUnaryExpr fall-through")); // LCOV_EXCL_LINE
3993 }
3994 }
3995
3996 102178 return exprResultAny(lhs);
3997 }
3998
3999 // Union branch of the member access in visitPostfixUnaryExpr
4000 65 p IRGenerator.generateUnionFieldAccess(ASTPostfixUnaryExprNode* node, LLVMExprResult& lhs, const QualType& lhsSTy, const String& fieldName) {
4001 65 Scope* unionScope = lhsSTy.getBodyScope();
4002 65 const SymbolTableEntry* fieldEntry = unionScope.symbolTable.lookupStrict(fieldName);
4003 65 assert fieldEntry != nil<const SymbolTableEntry*>;
4004 65 const QualType fieldSymbolType = fieldEntry.getQualType();
4005 // Tag 0 is reserved for the "unset" state, so a field's tag is its order index shifted up by one. This makes
4006 // an all-zero union (e.g. from .bss, a generic memset, or a nested aggregate's zero default) correctly unset
4007 // rather than aliasing field 0, so the tag-mismatch guard below also catches accidental zero-initialization.
4008 65 const long fieldTagIndex = cast<long>(fieldEntry.orderIndex) + 1l;
4009
4010 65 const llvm::Type unionLLVMTy = lhsSTy.toLLVMType(this.sourceFile);
4011 65 const llvm::Value unionBasePtr = lhs.ptr;
4012 65 const llvm::Value tagAddr = this.insertStructGEP(unionLLVMTy, unionBasePtr, 0, String("tag.addr"));
4013
4014 // A plain `u.field = value` assignment is the only access that should update the tag instead of checking it:
4015 // it is the sole case where this exact node is the direct, unwrapped left-hand side of a plain '=' assignment.
4016 // Every other use (reads, compound-assign's read-then-store-back, address-of, pass-by-ref, or descending
4017 // further into a by-value struct/union field) must first prove the field is currently active.
4018 65 bool isDirectAssignTarget = false;
4019 65 const ASTNode* parent = node.parent;
4020
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 65 times.
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 31 times.
65 if parent != nil<const ASTNode*> && parent.kind == ASTNodeKind::ASSIGN_EXPR {
4021 31 const ASTAssignExprNode* assignParent = downcast<ASTAssignExprNode>(parent);
4022
2/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
31 isDirectAssignTarget = assignParent.lhs != nil<IVisitable*> && getASTNode(assignParent.lhs) == cast<ASTNode*>(node) && assignParent.op == AssignOp::OP_ASSIGN;
4023 31 }
4024
4025
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 35 times.
65 if isDirectAssignTarget {
4026 30 this.insertStore(this.getInt32(fieldTagIndex), tagAddr);
4027 30 } else {
4028 // Guard the read with a runtime tag check. This is an always-on safety check (unlike e.g. the assert
4029 // statement, which is skipped in release builds), since it protects a core invariant of the union type
4030 // itself, not an optional user-level debug assertion.
4031 35 const llvm::Value tagValue = this.insertLoad(this.builder.getInt32Ty(), tagAddr);
4032 35 const llvm::Value tagMatches = this.builder.createICmp(llvm::LLVMIntPredicate::EQ, tagValue, this.getInt32(fieldTagIndex));
4033
4034 35 const String codeLine = node.codeLoc.toPrettyLine();
4035 35 const llvm::BasicBlock bOk = this.createBlock("union.tag.ok." + codeLine);
4036 35 const llvm::BasicBlock bPanic = this.createBlock("union.tag.panic." + codeLine);
4037 35 this.insertCondJump(tagMatches, bOk, bPanic, Likelihood::LIKELY);
4038
4039 35 this.switchToBlock(bPanic);
4040 35 const llvm::Value stdErrValue = this.getStdErrValue();
4041 35 const String errorMsg = "Program panicked at " + node.codeLoc.toPrettyString() + ": active field mismatch on union field access '" + fieldName + "'\n";
4042 35 const String globalName = this.getUnusedGlobalName(String(ANON_GLOBAL_STRING_NAME));
4043 35 llvm::Value globalString = this.module.createGlobalString(errorMsg, globalName.getRaw());
4044
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if this.cliOptions.comparableOutput {
4045 35 globalString.setAlignment(4);
4046 35 }
4047 35 Vector<llvm::Value> fprintfArgs;
4048 35 fprintfArgs.pushBack(stdErrValue);
4049 35 fprintfArgs.pushBack(globalString);
4050 35 this.builder.createCall(this.stdFunctionManager.getFPrintfFct(), fprintfArgs);
4051
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 if this.cliOptions.printsStackTraceOnAbort() {
4052 ✗ Vector<llvm::Value> dumpArgs;
4053 ✗ dumpArgs.pushBack(this.builder.getTrue());
4054 ✗ dumpArgs.pushBack(this.builder.getFalse());
4055 ✗ this.builder.createCall(this.stdFunctionManager.getDumpStacktraceFct(), dumpArgs);
4056 ✗ }
4057 35 Vector<llvm::Value> exitArgs;
4058 35 exitArgs.pushBack(this.getInt32(cast<long>(EXIT_FAILURE)));
4059 35 this.builder.createCall(this.stdFunctionManager.getExitFct(), exitArgs);
4060 35 this.builder.createUnreachable();
4061
4062 35 this.switchToBlock(bOk);
4063 35 }
4064
4065 // Get address of the payload (index 2: { tag, alignPad, payload })
4066 65 const llvm::Value memberAddr = this.insertStructGEP(unionLLVMTy, unionBasePtr, 2, fieldName + ".addr");
4067
4068 // Set as ptr or refPtr, depending on the type
4069
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65 times.
65 if fieldSymbolType.isRef() {
4070 ✗ lhs.ptr = llvm::Value();
4071 ✗ lhs.refPtr = memberAddr;
4072 ✗ } else {
4073 65 lhs.ptr = memberAddr;
4074 65 lhs.refPtr = llvm::Value();
4075 65 }
4076
4077 65 lhs.entry = fieldEntry;
4078 65 lhs.value = llvm::Value();
4079 65 }
4080
4081 // Error propagation branch of visitPostfixUnaryExpr
4082 93 f<LLVMExprResult> IRGenerator.generateErrorPropagation(ASTPostfixUnaryExprNode* node, LLVMExprResult& lhs) {
4083 93 const ErrPropagationData& errPropData = node.errPropData.get(this.manIdx);
4084
4085 // Get the address of the Result<T> operand
4086 93 const llvm::Value operandPtr = this.resolveAddress(lhs);
4087 93 Vector<llvm::Value> operandArgs;
4088 93 operandArgs.pushBack(operandPtr);
4089
4090 // Call isErr() on the operand
4091 93 assert errPropData.isErrFct != nil<const model::Function*>;
4092 93 const llvm::Function isErrFct = this.stdFunctionManager.getResultIsErrFct(errPropData.isErrFct);
4093 93 const llvm::Value isErr = this.builder.createCall(isErrFct, operandArgs);
4094
4095 // Create blocks
4096 93 const String codeLine = node.codeLoc.toPrettyLine();
4097 93 const llvm::BasicBlock bThen = this.createBlock("err.prop.then." + codeLine);
4098 93 const llvm::BasicBlock bExit = this.createBlock("err.prop.exit." + codeLine);
4099 93 this.insertCondJump(isErr, bThen, bExit, Likelihood::UNLIKELY);
4100
4101 // Switch to then block: propagate the error as the enclosing function's Result<U>
4102 93 this.switchToBlock(bThen);
4103 93 assert errPropData.getErrFct != nil<const model::Function*> && errPropData.ctorFct != nil<const model::Function*>;
4104 93 const llvm::Function getErrFct = this.stdFunctionManager.getResultGetErrFct(errPropData.getErrFct);
4105 93 const llvm::Value errorPtr = this.builder.createCall(getErrFct, operandArgs);
4106 93 const llvm::Function errCtorFct = this.stdFunctionManager.getResultErrCtorFct(errPropData.ctorFct);
4107 93 Vector<llvm::Value> errCtorArgs;
4108 93 errCtorArgs.pushBack(errorPtr);
4109 // If the enclosing function returns via memory, construct the result directly in the memory of its caller
4110 93 const QualType& propagatedResultType = errPropData.ctorFct.returnType;
4111 93 const llvm::BasicBlock currentBlock = this.builder.getInsertBlock();
4112 93 const llvm::Value sretArg = getSRetArg(currentBlock.getParent());
4113 93 llvm::Value propagatedResultAddr;
4114 93 llvm::Value propagatedResult = this.insertCall(errCtorFct, errCtorArgs, propagatedResultType, propagatedResultAddr, sretArg);
4115
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
93 if !sretArg.isNull() && propagatedResultAddr.isSameAs(sretArg) {
4116 93 propagatedResult = llvm::Value();
4117
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
93 } else if !propagatedResultAddr.isNull() {
4118 ✗ propagatedResult = this.insertLoad(propagatedResultType, propagatedResultAddr);
4119 ✗ }
4120
4121 // Record this propagation hop in the error return trace, if tracing is enabled for this file
4122
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 3 times.
93 if this.sourceFile.errorReturnTracing {
4123 3 this.generateErrTraceCall(this.stdFunctionManager.getErrTracePushFct(), cast<ASTNode*>(node), "errtrace.hop.sig.", "errtrace.hop.file.");
4124 3 }
4125
4126 // Clean up all scopes between here and the enclosing function/procedure/lambda body, then return the error
4127 93 this.generateScopeCleanupUpTo(cast<ASTNode*>(node), this.currentScope.getFunctionScope());
4128 93 this.blockAlreadyTerminated = true;
4129 93 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
4130 93 this.insertReturn(propagatedResult);
4131
4132 // Switch to exit block: unwrap the payload, which becomes the value of the whole expression
4133 93 this.switchToBlock(bExit);
4134 93 assert errPropData.unwrapFct != nil<const model::Function*>;
4135 93 const llvm::Function unwrapFct = this.stdFunctionManager.getResultUnwrapFct(errPropData.unwrapFct);
4136 93 const llvm::Value unwrapped = this.builder.createCall(unwrapFct, operandArgs);
4137 186 return exprResultWithPtr(unwrapped);
4138 93 }
4139
4140 // Generate a call to an error return trace function with the signature and location of the given node
4141 8 p IRGenerator.generateErrTraceCall(llvm::Function traceFct, ASTNode* node, string sigBaseName, string fileBaseName) {
4142 8 const String enclosingSignature = node.getEnclosingFunctionSignature(this.manIdx);
4143 8 const llvm::Value signature = this.createGlobalStringConst(String(sigBaseName), enclosingSignature, node.codeLoc);
4144 8 const llvm::Value fileName = this.createGlobalStringConst(String(fileBaseName), node.codeLoc.toPrettyFilePath(), node.codeLoc);
4145 8 const llvm::Value line = this.getInt32(cast<long>(node.codeLoc.line));
4146 8 const llvm::Value column = this.getInt32(cast<long>(node.codeLoc.col));
4147 8 Vector<llvm::Value> args;
4148 8 args.pushBack(signature);
4149 8 args.pushBack(fileName);
4150 8 args.pushBack(line);
4151 8 args.pushBack(column);
4152 8 this.builder.createCall(traceFct, args);
4153 8 }
4154
4155 397941 public f<Any> IRGenerator.visitAtomicExpr(ASTAtomicExprNode* node) {
4156 // If constant
4157
2/2
✓ Branch 0 taken 73709 times.
✓ Branch 1 taken 324232 times.
397941 if node.constant != nil<ASTConstantNode*> {
4158 73709 const llvm::Value constantValue = this.visitConst(cast<IVisitable*>(node.constant));
4159 73709 return exprResultAny(exprResultWithConstant(constantValue));
4160 }
4161
4162 // If value
4163
2/2
✓ Branch 0 taken 88607 times.
✓ Branch 1 taken 235625 times.
324232 if node.value != nil<IVisitable*> {
4164 88607 return this.visit(node.value);
4165 }
4166
4167 // Is assign expression
4168
2/2
✓ Branch 0 taken 1937 times.
✓ Branch 1 taken 233688 times.
235625 if node.assignExpr != nil<IVisitable*> {
4169 1937 return this.visit(node.assignExpr);
4170 }
4171
4172 233688 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
4173
4174 // Identifier (local or global variable access)
4175 233688 assert !node.identifierFragments.isEmpty();
4176
4177 // Get symbol table entry
4178 233688 const VarAccessData& accessData = node.data.get(this.manIdx);
4179 233688 const SymbolTableEntry* entry = accessData.entry;
4180 233688 Scope* accessScope = accessData.accessScope;
4181 233688 const Capture* capture = accessData.capture;
4182 233688 assert entry != nil<const SymbolTableEntry*>;
4183 233688 assert accessScope != nil<Scope*>;
4184 233688 const QualType varSymbolType = entry.getQualType();
4185 233688 const llvm::Type varType = varSymbolType.toLLVMType(this.sourceFile);
4186
4187 // If this is a function/procedure reference, return it as value. The fat pointer must point at a thunk that
4188 // carries the (ignored) trailing capture-struct pointer, so it can be called through the uniform lambda ABI.
4189
6/6
✓ Branch 0 taken 228081 times.
✓ Branch 1 taken 5607 times.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 5582 times.
✓ Branch 4 taken 64 times.
✓ Branch 5 taken 233624 times.
233688 if entry.global && (varSymbolType.is(SuperType::TY_FUNCTION) || varSymbolType.is(SuperType::TY_PROCEDURE)) {
4190 // The referenced function might be defined later, in another source file or externally, so declare it if required.
4191 // The type checker only allows to reference functions with a single manifestation.
4192 64 Vector<model::Function*>* manifestations = entry.declNode.getFctManifestations(entry.name);
4193 64 assert manifestations.getSize() == 1l;
4194 64 const llvm::Function target = this.getOrDeclareFunction(manifestations.front());
4195 64 const llvm::Function thunk = this.getOrCreateFatFctPtrThunk(target);
4196 64 const llvm::Value fatPtr = this.buildFatFctPtr(nil<Scope*>, llvm::Type(), thunk.asValue());
4197 64 LLVMExprResult res;
4198 64 res.ptr = fatPtr;
4199 64 res.entry = entry;
4200 64 return exprResultAny(res);
4201 }
4202
4203 // Check if external global variable
4204
4/4
✓ Branch 0 taken 228081 times.
✓ Branch 1 taken 5543 times.
✓ Branch 2 taken 233295 times.
✓ Branch 3 taken 329 times.
233624 if entry.global && accessScope.isImportedBy(this.rootScope) {
4205 // External global variables need to be declared and allocated in the current module
4206 329 const llvm::Value varAddress = this.module.getOrInsertGlobal(entry.name.getRaw(), varType);
4207 329 this.updateAddress(entry, varAddress);
4208 329 }
4209
4210 // Check if enum item
4211
2/2
✓ Branch 0 taken 2282 times.
✓ Branch 1 taken 231342 times.
233624 if accessScope.scopeType == ScopeType::ENUM {
4212 2282 const ASTEnumItemNode* itemNode = downcast<ASTEnumItemNode>(entry.declNode);
4213 2282 const llvm::Value constantItemValue = llvm::getConstInt(varType, cast<unsigned long>(itemNode.itemValue));
4214 2282 LLVMExprResult res;
4215 2282 res.constant = constantItemValue;
4216 2282 res.entry = entry;
4217 2282 return exprResultAny(res);
4218 }
4219
4220 231342 const llvm::Value address = this.getAddress(entry);
4221 231342 assert !address.isNull();
4222
4223 // Load the address of the referenced variable
4224 231342 LLVMExprResult res;
4225 231342 res.entry = entry;
4226
6/6
✓ Branch 0 taken 22735 times.
✓ Branch 1 taken 208607 times.
✓ Branch 2 taken 208512 times.
✓ Branch 3 taken 95 times.
✓ Branch 4 taken 22777 times.
✓ Branch 5 taken 208565 times.
231342 if varSymbolType.isRef() || (capture != nil<const Capture*> && capture.getMode() == CapturePassMode::BY_REFERENCE) {
4227 22777 res.refPtr = address;
4228 22777 return exprResultAny(res);
4229 }
4230
4231 208565 res.ptr = address;
4232 208565 return exprResultAny(res);
4233 }
4234
4235 // =========================================================== Values ============================================================
4236
4237 88607 public f<Any> IRGenerator.visitValue(ASTValueNode* node) {
4238 88607 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
4239
4240 // Function call
4241
2/2
✓ Branch 0 taken 76601 times.
✓ Branch 1 taken 12006 times.
88607 if node.fctCall != nil<IVisitable*> {
4242 76601 return this.visit(node.fctCall);
4243 }
4244
4245 // Array initialization
4246
2/2
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 11846 times.
12006 if node.arrayInitialization != nil<IVisitable*> {
4247 160 return this.visit(node.arrayInitialization);
4248 }
4249
4250 // Struct instantiation
4251
2/2
✓ Branch 0 taken 1662 times.
✓ Branch 1 taken 10184 times.
11846 if node.structInstantiation != nil<IVisitable*> {
4252 1662 return this.visit(node.structInstantiation);
4253 }
4254
4255 // Lambda function
4256
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 10123 times.
10184 if node.lambdaFunc != nil<IVisitable*> {
4257 61 return this.visit(node.lambdaFunc);
4258 }
4259
4260 // Lambda procedure
4261
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 10052 times.
10123 if node.lambdaProc != nil<IVisitable*> {
4262 71 return this.visit(node.lambdaProc);
4263 }
4264
4265 // Lambda expression
4266
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10048 times.
10052 if node.lambdaExpr != nil<IVisitable*> {
4267 4 return this.visit(node.lambdaExpr);
4268 }
4269
4270
1/2
✓ Branch 0 taken 10048 times.
✗ Branch 1 not taken.
10048 if node.isNil {
4271 // Retrieve type of the nil constant
4272 10048 const llvm::Type nilType = this.visitType(cast<IVisitable*>(node.nilType));
4273 // Create constant nil value
4274 10048 const llvm::Value nilValue = llvm::getNullValue(nilType);
4275 // Return it
4276 10048 return exprResultAny(exprResultWithConstant(nilValue));
4277 }
4278
4279 − panic(Error("Value fall-through")); // LCOV_EXCL_LINE
4280 }
4281
4282 77979 public f<Any> IRGenerator.visitConstant(ASTConstantNode* node) {
4283
2/2
✓ Branch 0 taken 4177 times.
✓ Branch 1 taken 73802 times.
77979 if this.currentScope != this.rootScope {
4284 73802 this.diGenerator.setSourceLocation(cast<ASTNode*>(node));
4285 73802 }
4286 77979 ASTNode* baseNode = cast<ASTNode*>(node);
4287 77979 const CompileTimeValue compileTimeValue = baseNode.getCompileTimeValue(this.manIdx);
4288 77979 const QualType evaluatedType = this.getEvaluatedType(cast<IVisitable*>(node));
4289 77979 return Any(this.getConst(compileTimeValue, evaluatedType, baseNode));
4290 }
4291
4292 // Check if an argument of the given type matches the expected parameter type
4293 92982 f<bool> matchArgType(const QualType& origExpectedTy, const QualType& origActualTy) {
4294
2/2
✓ Branch 0 taken 85537 times.
✓ Branch 1 taken 7445 times.
92982 if origExpectedTy.matches(origActualTy, false, true, true) {
4295 85537 return true;
4296 }
4297
4298 // Unwrap as far as possible and remove reference wrappers if possible
4299 7445 QualType expectedTy = origExpectedTy;
4300 7445 QualType actualTy = origActualTy;
4301 7445 unwrapBoth(expectedTy, actualTy);
4302 7445 return expectedTy.matchesInterfaceImplementedByStruct(actualTy);
4303 }
4304
4305 76601 public f<Any> IRGenerator.visitFctCall(ASTFctCallNode* node) {
4306 // Check if this is a builtin call
4307
2/2
✓ Branch 0 taken 9122 times.
✓ Branch 1 taken 67479 times.
76601 if isBuiltinFunction(node.fqFunctionName) {
4308 9122 return this.visitBuiltinCall(node);
4309 }
4310
4311 67479 const FctCallData& data = node.data.get(this.manIdx);
4312
4313 67479 const model::Function* spiceFunc = data.callee;
4314 67479 assert data.isFctPtrCall() || spiceFunc != nil<const model::Function*>; // If not a function pointer call, we must have a function
4315 67479 String mangledName;
4316
2/2
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 67314 times.
67479 if !data.isFctPtrCall() {
4317 67314 mangledName = spiceFunc.getMangledName();
4318 67314 }
4319
4320 // Get entry of the first fragment
4321 67479 const SymbolTableEntry* firstFragEntry = this.currentScope.lookup(node.functionNameFragments.front());
4322
4323 // Get this type
4324 67479 Vector<llvm::Value> argValues;
4325 67479 llvm::Value thisPtr;
4326
2/2
✓ Branch 0 taken 39838 times.
✓ Branch 1 taken 27641 times.
67479 if data.isMethodCall() {
4327 27641 assert !data.isCtorCall();
4328
4329 // Retrieve entry of the first fragment
4330 27641 assert firstFragEntry != nil<const SymbolTableEntry*>;
4331 27641 const QualType& firstFragQualType = firstFragEntry.getQualType();
4332 27641 const QualType baseType = firstFragQualType.getBase();
4333 27641 assert baseType.is(SuperType::TY_STRUCT) || baseType.is(SuperType::TY_INTERFACE);
4334 27641 QualType structType = baseType;
4335
4336 // Get address of the referenced variable / struct instance
4337 27641 thisPtr = this.getAddress(firstFragEntry);
4338 27641 assert !thisPtr.isNull();
4339
4340 // Auto de-reference 'this' pointer
4341 27641 QualType firstFragmentType = firstFragEntry.getQualType();
4342 27641 this.autoDeReferencePtr(thisPtr, firstFragmentType);
4343 27641 llvm::Type structTy = baseType.toLLVMType(this.sourceFile);
4344
4345 // Traverse through structs - the first fragment is already looked up and the last one is the function name
4346
2/2
✓ Branch 0 taken 3711 times.
✓ Branch 1 taken 27641 times.
31352 for unsigned long i = 1l; i < node.functionNameFragments.getSize() - 1l; i++ {
4347 3711 const String& identifier = node.functionNameFragments.get(i);
4348 // Retrieve field entry, also looking through composed fields
4349 3711 Struct* spiceStruct = structType.getStruct(cast<ASTNode*>(node));
4350 3711 assert spiceStruct != nil<Struct*>;
4351 3711 Vector<unsigned long> indexPath;
4352 3711 const SymbolTableEntry* fieldEntry = spiceStruct.lookupInComposedFields(identifier, indexPath);
4353 3711 assert fieldEntry != nil<const SymbolTableEntry*>;
4354 3711 QualType fieldEntryType = fieldEntry.getQualType();
4355 3711 const QualType fieldBaseType = fieldEntryType.getBase();
4356 3711 assert fieldBaseType.is(SuperType::TY_STRUCT) || fieldBaseType.is(SuperType::TY_INTERFACE);
4357 // Get struct type
4358 3711 structType = fieldBaseType;
4359 // Get address of field. If the field was found directly (no composition involved), keep using the
4360 // single-index helper, since it elides the GEP entirely for fields at offset 0. Otherwise, step through
4361 // all composed fields on the way in a single GEP.
4362
2/2
✓ Branch 0 taken 3710 times.
✓ Branch 1 taken 1 time.
3711 if indexPath.getSize() == 1l {
4363 3710 thisPtr = this.insertStructGEP(structTy, thisPtr, cast<unsigned int>(indexPath.front()));
4364 3710 } else {
4365 1 Vector<llvm::Value> indices;
4366 1 indices.pushBack(this.getInt64(0l));
4367
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 foreach const unsigned long index : indexPath {
4368 2 indices.pushBack(this.getInt32(cast<long>(index)));
4369 2 }
4370 1 thisPtr = this.insertInBoundsGEP(structTy, thisPtr, indices);
4371 1 }
4372 // Auto de-reference pointer and get new struct type
4373 3711 this.autoDeReferencePtr(thisPtr, fieldEntryType);
4374 3711 const QualType newBaseType = fieldEntryType.getBase();
4375 3711 structTy = newBaseType.toLLVMType(this.sourceFile);
4376 3711 }
4377
4378 // Methods, that are inherited from an interface default method, expect a pointer to the interface part of the struct
4379
3/4
✓ Branch 0 taken 27641 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 27632 times.
✓ Branch 3 taken 9 times.
27641 if spiceFunc != nil<const model::Function*> && spiceFunc.isInheritedDefaultMethod() {
4380 9 thisPtr = this.getUpcastedStructPtr(thisPtr, spiceFunc.defaultMethod.thisType, structType);
4381 9 }
4382
4383 // Add 'this' pointer to the front of the argument list
4384 27641 argValues.pushBack(thisPtr);
4385 27641 }
4386
4387
2/2
✓ Branch 0 taken 58563 times.
✓ Branch 1 taken 8916 times.
67479 if data.isCtorCall() {
4388 8916 assert !data.isMethodCall();
4389
4390 8916 const llvm::Type thisType = spiceFunc.thisType.toLLVMType(this.sourceFile);
4391 8916 thisPtr = this.insertAlloca(thisType);
4392
4393 // Add 'this' pointer to the front of the argument list
4394 8916 argValues.pushBack(thisPtr);
4395 8916 }
4396
4397 // Every callable behind a fat function pointer uses the same calling convention: the capture-struct pointer
4398 // (fat ptr slot 1) is always passed as the trailing argument, after all real arguments, regardless of whether the
4399 // target actually captures. Non-capturing lambdas and plain function references ignore it. Passing captures last
4400 // (instead of first) keeps fat ptr slot 0 directly callable through a plain C calling convention, so a Spice
4401 // function or lambda cast to a raw pointer can be handed to a C API as a callback without shifting its arguments.
4402 67479 llvm::Value fctPtr;
4403
4404 // Get arg values
4405
2/2
✓ Branch 0 taken 16502 times.
✓ Branch 1 taken 50977 times.
67479 if node.hasArgs {
4406 50977 const Vector<IVisitable*>& args = node.argLst.args;
4407 50977 argValues.reserve(args.getSize());
4408 50977 QualTypeList paramSTypes;
4409
2/2
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 50864 times.
50977 if data.isFctPtrCall() {
4410 113 const QualType& firstFragQualType = firstFragEntry.getQualType();
4411 113 const QualType firstFragBaseType = firstFragQualType.getBase();
4412 113 paramSTypes = firstFragBaseType.getFunctionParamTypes();
4413 113 } else {
4414 50864 paramSTypes = spiceFunc.getParamTypes();
4415 50864 }
4416 50977 assert paramSTypes.getSize() == args.getSize();
4417 50977 const Vector<ArgInfo>& argInfos = node.argLst.argInfos.get(this.manIdx);
4418
2/2
✓ Branch 0 taken 85667 times.
✓ Branch 1 taken 50977 times.
136644 for unsigned long i = 0l; i < args.getSize(); i++ {
4419 85667 IVisitable* argNode = args.get(i);
4420 85667 const ArgInfo& argInfo = argInfos.get(i);
4421 85667 const model::Function* copyCtor = argInfo.copyCtor;
4422
4423 85667 const QualType& expectedSTy = paramSTypes.get(i);
4424 85667 const QualType actualSTy = this.getEvaluatedType(argNode);
4425
4426
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 85624 times.
85667 if copyCtor != nil<const model::Function*> {
4427 // By-value params of non-trivially copyable types get a copy of the argument, also if it is a reference
4428 43 const QualType actualValueSTy = actualSTy.removeReferenceWrapper();
4429 43 assert !actualValueSTy.isTriviallyCopyable(cast<ASTNode*>(node));
4430 43 const llvm::Value originalPtr = this.resolveAddress(argNode);
4431 43 const llvm::Value valueCopyPtr = this.generateArgCopy(originalPtr, actualValueSTy, copyCtor);
4432
4433 // Only external functions leave the copy to the caller. Attach the address of the copy to its anonymous symbol
4434 43 const SymbolTableEntry* anonymousSymbol = this.currentScope.symbolTable.lookupAnonymous(getASTNode(argNode), ULONG_MAX_VALUE);
4435
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 if anonymousSymbol != nil<const SymbolTableEntry*> {
4436 ✗ this.updateAddress(anonymousSymbol, valueCopyPtr);
4437 ✗ }
4438
4439 43 argValues.pushBack(this.insertLoad(actualValueSTy.toLLVMType(this.sourceFile), valueCopyPtr));
4440
2/2
✓ Branch 0 taken 78191 times.
✓ Branch 1 taken 7433 times.
85667 } else if matchArgType(expectedSTy, actualSTy) {
4441 // Resolve address if actual type is reference or a decaying array, otherwise value
4442
4/4
✓ Branch 0 taken 3354 times.
✓ Branch 1 taken 74837 times.
✓ Branch 2 taken 3378 times.
✓ Branch 3 taken 74813 times.
78191 if actualSTy.isRef() || expectedSTy.isDecayedArray() {
4443 3378 argValues.pushBack(this.resolveAddress(argNode));
4444 3378 } else {
4445 74813 argValues.pushBack(this.resolveValue(argNode));
4446 74813 }
4447
4/4
✓ Branch 0 taken 698 times.
✓ Branch 1 taken 6735 times.
✓ Branch 2 taken 6735 times.
✓ Branch 3 taken 698 times.
85624 } else if expectedSTy.isRef() && matchArgType(expectedSTy.getContained(), actualSTy) { // Matches with ref
4448 6735 const llvm::Value argAddress = this.resolveAddress(argNode);
4449 6735 argValues.pushBack(argAddress);
4450
4/4
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 623 times.
✓ Branch 2 taken 623 times.
✓ Branch 3 taken 75 times.
7433 } else if actualSTy.isRef() && matchArgType(expectedSTy, actualSTy.getContained()) { // Matches with ref
4451 623 const llvm::Value argAddress = this.resolveValue(argNode);
4452 623 argValues.pushBack(argAddress);
4453 623 } else { // Need implicit cast
4454 75 const llvm::Value argAddress = this.resolveAddress(argNode);
4455 75 argValues.pushBack(this.doImplicitCast(argAddress, expectedSTy, actualSTy));
4456 85624 }
4457
4458 // Decayed array params expect the address of a writable array, which not all of the paths above produce
4459
2/2
✓ Branch 0 taken 85643 times.
✓ Branch 1 taken 24 times.
85667 if expectedSTy.isDecayedArray() {
4460 24 llvm::Value& argValue = argValues.back();
4461 24 argValue = this.materializeDecayedArrayArg(argValue, expectedSTy);
4462 24 }
4463 85667 }
4464 50977 }
4465
4466 // Append the trailing capture-struct pointer for fat function pointer calls (see the ABI note above)
4467
2/2
✓ Branch 0 taken 67314 times.
✓ Branch 1 taken 165 times.
67479 if data.isFctPtrCall() {
4468 165 llvm::Value fatPtr = this.getAddress(firstFragEntry);
4469 // The fat pointer might be reached via pointers or references (e.g. a param of type f<int>()** or f<int>()&)
4470 165 QualType fatPtrType = firstFragEntry.getQualType();
4471 165 this.autoDeReferencePtr(fatPtr, fatPtrType);
4472 // Remember the address of the function pointer slot for the call below
4473 165 fctPtr = this.insertStructGEP(this.llvmTypes.lambdaFatPtrType, fatPtr, 0);
4474 // Load the captures pointer and add it to the argument list as the trailing argument
4475 165 const llvm::Value capturesPtrPtr = this.insertStructGEP(this.llvmTypes.lambdaFatPtrType, fatPtr, 1);
4476 165 const llvm::Value capturesPtr = this.insertLoad(this.builder.getPtrTy(), capturesPtrPtr, false, String(CAPTURES_PARAM_NAME));
4477 165 argValues.pushBack(capturesPtr);
4478 165 }
4479
4480 // Retrieve return and param types
4481 67479 QualType returnSType = QualType(SuperType::TY_DYN);
4482 67479 QualTypeList paramSTypes;
4483
2/2
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 67314 times.
67479 if data.isFctPtrCall() {
4484 165 const QualType& firstFragQualType = firstFragEntry.getQualType();
4485 165 const QualType firstFragBaseType = firstFragQualType.getBase();
4486
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 69 times.
165 if firstFragQualType.isBase(SuperType::TY_FUNCTION) {
4487 69 returnSType = firstFragBaseType.getFunctionReturnType();
4488 69 }
4489 165 paramSTypes = firstFragBaseType.getFunctionParamTypes();
4490 165 } else {
4491 67314 returnSType = spiceFunc.returnType;
4492 67314 paramSTypes = spiceFunc.getParamTypes();
4493 67314 }
4494
4495 // Function is not defined in the current module -> declare it
4496 67479 llvm::Type fctType;
4497 67479 const llvm::Function existingFct = this.module.getFunction(mangledName.getRaw());
4498
4/4
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 67314 times.
✓ Branch 2 taken 53786 times.
✓ Branch 3 taken 13693 times.
67479 if !data.isFctPtrCall() && !existingFct.isNull() {
4499 53786 fctType = existingFct.getFunctionType();
4500 53786 } else {
4501 // Get arg types
4502 13693 Vector<llvm::Type> argTypes;
4503
4/4
✓ Branch 0 taken 6997 times.
✓ Branch 1 taken 6696 times.
✓ Branch 2 taken 4678 times.
✓ Branch 3 taken 9015 times.
13693 if data.isMethodCall() || data.isCtorCall() {
4504 9015 argTypes.pushBack(this.builder.getPtrTy()); // This pointer
4505 9015 }
4506
2/2
✓ Branch 0 taken 12272 times.
✓ Branch 1 taken 13693 times.
25965 foreach const QualType& paramType : paramSTypes {
4507 12272 argTypes.pushBack(paramType.getParamLLVMType(this.sourceFile));
4508 12272 }
4509
2/2
✓ Branch 0 taken 13528 times.
✓ Branch 1 taken 165 times.
13693 if data.isFctPtrCall() {
4510 165 argTypes.pushBack(this.builder.getPtrTy()); // Trailing capture pointer (always present in the uniform lambda ABI)
4511 165 }
4512
4513 13693 fctType = this.getFunctionType(returnSType, argTypes);
4514
4/4
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 13528 times.
✓ Branch 2 taken 224 times.
✓ Branch 3 taken 13469 times.
13693 if !data.isFctPtrCall() && !data.isVirtualMethodCall() {
4515 13469 this.module.getOrInsertFunction(mangledName.getRaw(), fctType);
4516 13469 const ReturnABIInfo returnABI = this.getReturnABIInfo(returnSType);
4517
2/2
✓ Branch 0 taken 11689 times.
✓ Branch 1 taken 1780 times.
13469 if returnABI.isIndirect() {
4518 1780 this.addSRetParamAttrs(this.module.getFunction(mangledName.getRaw()), returnABI.memoryType);
4519 1780 }
4520 13469 }
4521 13693 }
4522 67479 assert !fctType.isNull();
4523
4524 // Retrieve the callee
4525 67479 llvm::Value callee;
4526
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 67415 times.
67479 if data.isVirtualMethodCall() {
4527 64 assert data.callee.isVirtual;
4528 64 assert !thisPtr.isNull();
4529 // Load VTable
4530 64 const llvm::Value vtablePtr = this.insertLoad(this.builder.getPtrTy(), thisPtr, false, String("vtable.addr"));
4531 64 const unsigned long vtableIndex = data.callee.vtableIndex;
4532 // Lookup function pointer in VTable
4533 64 fctPtr = this.insertInBoundsGEP(this.builder.getPtrTy(), vtablePtr, this.builder.getInt64(vtableIndex), String("vfct.addr"));
4534 64 callee = this.insertLoad(this.builder.getPtrTy(), fctPtr, false, String("fct"));
4535
2/2
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 67250 times.
67479 } else if data.isFctPtrCall() {
4536 165 assert !fctPtr.isNull(); // Already dereferenced above, where the captures pointer is loaded
4537 165 callee = this.insertLoad(this.builder.getPtrTy(), fctPtr, false, String("fct"));
4538 165 } else {
4539 // Get callee function
4540 67250 const llvm::Function calleeFct = this.module.getFunction(mangledName.getRaw());
4541 67250 assert !calleeFct.isNull();
4542 67250 callee = calleeFct.asValue();
4543 67415 }
4544
4545 // Generate function call. If the value is returned via memory or coerced, the call result lives in memory
4546 67479 llvm::Value resultAddr;
4547 67479 const llvm::Value callInst = this.insertCall(fctType, callee, argValues, returnSType, resultAddr);
4548
4549 // Set argument and return value attributes
4550
2/2
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 67314 times.
67479 if !data.isFctPtrCall() {
4551 67314 this.setCallArgAttrs(callInst, spiceFunc, paramSTypes, this.getArgOffset(returnSType));
4552 67314 this.setCallReturnValAttrs(callInst, returnSType);
4553 67314 }
4554
4555 // Start a fresh error return trace at this call site, since it is where a new error was just put into existence
4556
2/2
✓ Branch 0 taken 67474 times.
✓ Branch 1 taken 5 times.
67479 if node.isErrorTraceOrigin {
4557 5 this.generateErrTraceCall(this.stdFunctionManager.getErrTraceResetFct(), cast<ASTNode*>(node), "errtrace.origin.sig.", "errtrace.origin.file.");
4558 5 }
4559
4560 // Attach address to anonymous symbol to keep track of de-allocation
4561 67479 const SymbolTableEntry* anonymousSymbol = nil<const SymbolTableEntry*>;
4562 67479 llvm::Value resultPtr;
4563
4/4
✓ Branch 0 taken 4805 times.
✓ Branch 1 taken 62674 times.
✓ Branch 2 taken 53758 times.
✓ Branch 3 taken 13721 times.
67479 if returnSType.is(SuperType::TY_STRUCT) || data.isCtorCall() {
4564 13721 anonymousSymbol = this.currentScope.symbolTable.lookupAnonymous(cast<ASTNode*>(node));
4565
2/2
✓ Branch 0 taken 11627 times.
✓ Branch 1 taken 2094 times.
13721 if anonymousSymbol != nil<const SymbolTableEntry*> {
4566
2/2
✓ Branch 0 taken 1427 times.
✓ Branch 1 taken 667 times.
2094 if data.isCtorCall() {
4567 1427 this.updateAddress(anonymousSymbol, thisPtr);
4568 1427 } else {
4569 667 resultPtr = resultAddr;
4570
1/2
✓ Branch 0 taken 667 times.
✗ Branch 1 not taken.
667 if resultPtr.isNull() {
4571 ✗ resultPtr = this.insertAlloca(callInst.getType());
4572 ✗ this.insertStore(callInst, resultPtr);
4573 ✗ }
4574 667 this.updateAddress(anonymousSymbol, resultPtr);
4575 667 }
4576 2094 }
4577 13721 }
4578
4579 67479 LLVMExprResult res;
4580 67479 res.entry = anonymousSymbol;
4581
4582 // In case this is a constructor call, return the thisPtr as pointer
4583
2/2
✓ Branch 0 taken 8916 times.
✓ Branch 1 taken 58563 times.
67479 if data.isCtorCall() {
4584 8916 res.ptr = thisPtr;
4585 8916 res.refPtr = resultPtr;
4586 17832 return exprResultAny(res);
4587 }
4588
4589 // In case this is a callee, returning a reference, return the address
4590
2/2
✓ Branch 0 taken 2793 times.
✓ Branch 1 taken 55770 times.
58563 if returnSType.isRef() {
4591 2793 res.ptr = callInst;
4592 2793 res.refPtr = resultPtr;
4593 5586 return exprResultAny(res);
4594 }
4595
4596 // In case the value is returned via memory or coerced, return its address
4597
2/2
✓ Branch 0 taken 4878 times.
✓ Branch 1 taken 50892 times.
55770 if !resultAddr.isNull() {
4598 4878 res.ptr = resultAddr;
4599 9756 return exprResultAny(res);
4600 }
4601
4602 // Otherwise return the value
4603 50892 res.value = callInst;
4604 50892 res.ptr = resultPtr;
4605 101784 return exprResultAny(res);
4606 67479 }
4607
4608 67314 p IRGenerator.setCallArgAttrs(llvm::Value callInst, const model::Function* spiceFunc, const QualTypeList& paramSTypes,
4609 67314 unsigned int argOffset = 0) {
4610 67314 const bool isFctPtr = spiceFunc == nil<const model::Function*>;
4611
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 67314 times.
67314 const bool isMethod = !isFctPtr && !spiceFunc.thisType.is(SuperType::TY_DYN);
4612
4/4
✓ Branch 0 taken 36557 times.
✓ Branch 1 taken 30757 times.
✓ Branch 2 taken 36557 times.
✓ Branch 3 taken 30757 times.
67314 const unsigned long expectedParamCount = isMethod || isFctPtr ? paramSTypes.getSize() + 1l : paramSTypes.getSize();
4613
2/2
✓ Branch 0 taken 122093 times.
✓ Branch 1 taken 67314 times.
189407 for unsigned long i = 0l; i < expectedParamCount; i++ {
4614 122093 QualType paramType = QualType(SuperType::TY_INVALID);
4615
4/4
✓ Branch 0 taken 55621 times.
✓ Branch 1 taken 66472 times.
✓ Branch 2 taken 36557 times.
✓ Branch 3 taken 85536 times.
122093 if i == 0l && isMethod {
4616 36557 paramType = spiceFunc.thisType.toPtr(nil<ASTNode*>);
4617 36557 } else {
4618
2/2
✓ Branch 0 taken 27695 times.
✓ Branch 1 taken 57841 times.
85536 paramType = paramSTypes.get(isMethod ? i - 1l : i);
4619 85536 }
4620 122093 const unsigned int argNo = argOffset + cast<unsigned int>(i);
4621
4622 // NoUndef attribute
4623 122093 callInst.addCallParamAttr(argNo, "noundef");
4624
4625
2/2
✓ Branch 0 taken 69611 times.
✓ Branch 1 taken 52482 times.
122093 if paramType.isPtr() {
4626 52482 const QualType pointeeSType = paramType.getContained();
4627 52482 const llvm::Type pointeeType = pointeeSType.toLLVMType(this.sourceFile);
4628 52482 assert !pointeeType.isNull();
4629 // NonNull and dereferenceable attributes. Only 'this' is guaranteed to point to a valid object, other pointers may be nil
4630
4/4
✓ Branch 0 taken 6781 times.
✓ Branch 1 taken 45701 times.
✓ Branch 2 taken 15925 times.
✓ Branch 3 taken 36557 times.
52482 if i == 0l && isMethod {
4631 36557 callInst.addCallParamAttr(argNo, "nonnull");
4632 36557 callInst.addCallDereferenceableParamAttr(argNo, this.module.getTypeStoreSize(pointeeType));
4633 36557 }
4634 // Alignment attribute
4635 52482 callInst.addCallParamAttr(argNo, "align", cast<unsigned long>(this.module.getABITypeAlign(pointeeType)));
4636 52482 }
4637
4638 // ZExt or SExt attribute
4639 122093 const string extAttrKind = this.getExtAttrKindForType(paramType);
4640
2/2
✓ Branch 0 taken 109392 times.
✓ Branch 1 taken 12701 times.
122093 if extAttrKind != nil<string> {
4641 12701 callInst.addCallParamAttr(argNo, extAttrKind);
4642 12701 }
4643 122093 }
4644 67314 }
4645
4646 67314 p IRGenerator.setCallReturnValAttrs(llvm::Value callInst, const QualType& returnType) {
4647
2/2
✓ Branch 0 taken 25222 times.
✓ Branch 1 taken 42092 times.
67314 if returnType.is(SuperType::TY_DYN) {
4648 25222 return;
4649 }
4650
4651 // Values, that are returned via memory, have attributes on the sret argument. Coerced values might contain padding
4652 42092 const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType);
4653
2/2
✓ Branch 0 taken 4869 times.
✓ Branch 1 taken 37223 times.
42092 if !returnABI.isDirect() {
4654 4869 return;
4655 }
4656
4657 // NoUndef attribute
4658 37223 callInst.addCallRetAttr("noundef");
4659 // ZExt or SExt attribute
4660 37223 const string extAttrKind = this.getExtAttrKindForType(returnType);
4661
2/2
✓ Branch 0 taken 25838 times.
✓ Branch 1 taken 11385 times.
37223 if extAttrKind != nil<string> {
4662 11385 callInst.addCallRetAttr(extAttrKind);
4663 11385 }
4664 37223 }
4665
4666 160 public f<Any> IRGenerator.visitArrayInitialization(ASTArrayInitializationNode* node) {
4667 // Return immediately if the initialization is empty
4668
1/2
✓ Branch 0 taken 160 times.
✗ Branch 1 not taken.
160 if node.actualSize == 0l {
4669 ✗ LLVMExprResult res;
4670 ✗ res.node = cast<IVisitable*>(node);
4671 ✗ return exprResultAny(res);
4672 }
4673
4674 // Visit array items
4675 160 bool canBeConstant = true;
4676 160 Vector<LLVMExprResult> itemResults;
4677 160 itemResults.reserve(node.actualSize);
4678
2/2
✓ Branch 0 taken 1286 times.
✓ Branch 1 taken 160 times.
1446 foreach IVisitable* itemNode : node.itemLst.args {
4679 1286 LLVMExprResult item = this.visitExpr(itemNode);
4680
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1272 times.
1286 canBeConstant = canBeConstant && !item.constant.isNull();
4681 1286 item.node = itemNode;
4682 1286 itemResults.pushBack(item);
4683 1286 }
4684
4685 // Get LLVM type of item and array
4686 160 assert !itemResults.isEmpty();
4687 160 const QualType firstItemSTy = this.getEvaluatedType(node.itemLst.args.front());
4688 160 const llvm::Type itemType = firstItemSTy.toLLVMType(this.sourceFile);
4689 160 const llvm::Type arrayType = llvm::getArrayType(itemType, node.actualSize);
4690
4691
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 8 times.
160 if canBeConstant { // All items are constants, so we can create a global constant array
4692 // Collect constants
4693 152 Vector<llvm::Value> constants;
4694
2/2
✓ Branch 0 taken 1264 times.
✓ Branch 1 taken 152 times.
1416 foreach const LLVMExprResult& exprResult : itemResults {
4695 // Delete potential constant globals, that were already created a layer below
4696 1264 const llvm::Type constantType = exprResult.constant.getType();
4697
2/2
✓ Branch 0 taken 1242 times.
✓ Branch 1 taken 22 times.
1264 if constantType.isArrayTy() {
4698 22 const String nestedGlobalName = exprResult.ptr.getName();
4699 22 llvm::Value nestedGlobal = this.module.getNamedGlobal(nestedGlobalName.getRaw());
4700 22 nestedGlobal.eraseGlobalFromParent();
4701 22 }
4702 1264 constants.pushBack(exprResult.constant);
4703 1264 }
4704
4705 // Create global array
4706 152 const llvm::Value constantArray = llvm::getConstArray(itemType, constants);
4707 152 const llvm::Value arrayAddr = this.createGlobalConst(String(ANON_GLOBAL_ARRAY_NAME), constantArray);
4708
4709 152 LLVMExprResult res;
4710 152 res.constant = constantArray;
4711 152 res.ptr = arrayAddr;
4712 304 return exprResultAny(res);
4713 152 }
4714
4715 // We have non-immediate values as items, so we need to take normal arrays as fallback
4716 8 const llvm::Value arrayAddr = this.insertAlloca(arrayType);
4717
4718 // Retrieve address of first item
4719 8 const llvm::Value firstItemAddress = this.insertInBoundsGEP(arrayType, arrayAddr, this.getInt64(0l));
4720
4721 // Store all array items at their corresponding offsets
4722 8 llvm::Value currentItemAddress = firstItemAddress;
4723
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 8 times.
30 for unsigned long i = 0l; i < itemResults.getSize(); i++ {
4724 22 LLVMExprResult& exprResult = itemResults.get(i);
4725 22 const llvm::Value itemValue = this.resolveValue(exprResult.node, exprResult);
4726 // Retrieve current item address
4727
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 14 times.
22 if i >= 1l {
4728 14 currentItemAddress = this.insertInBoundsGEP(itemType, currentItemAddress, this.getInt64(1l));
4729 14 }
4730 // Store the item value
4731
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 6 times.
22 const bool storeVolatile = exprResult.entry != nil<const SymbolTableEntry*> && exprResult.entry.isVolatile;
4732 22 this.insertStore(itemValue, currentItemAddress, storeVolatile);
4733 22 }
4734
4735 16 return exprResultAny(exprResultWithPtr(arrayAddr));
4736 160 }
4737
4738 1662 public f<Any> IRGenerator.visitStructInstantiation(ASTStructInstantiationNode* node) {
4739 // Get struct object
4740 1662 const Struct* spiceStruct = node.instantiatedStructs.get(this.manIdx);
4741 1662 assert spiceStruct != nil<const Struct*>;
4742 1662 const QualTypeList& fieldTypes = spiceStruct.fieldTypes;
4743
4744 // Can only be constant if none of the fields is of type reference
4745 1662 bool canBeConstant = !spiceStruct.hasReferenceFields();
4746
4747 // Get struct type
4748 1662 assert spiceStruct.entry != nil<SymbolTableEntry*>;
4749 1662 const QualType& structEntryType = spiceStruct.entry.getQualType();
4750 1662 const llvm::Type structType = structEntryType.toLLVMType(this.sourceFile);
4751 1662 assert !structType.isNull();
4752
4753
2/2
✓ Branch 0 taken 1618 times.
✓ Branch 1 taken 44 times.
1662 if node.fieldLst == nil<ASTArgLstNode*> {
4754 44 const llvm::Value constantStruct = this.getDefaultValueForSymbolType(structEntryType);
4755 44 return exprResultAny(exprResultWithConstant(constantStruct));
4756 }
4757
4758 // Visit struct field values
4759 1618 Vector<LLVMExprResult> fieldValueResults;
4760 1618 fieldValueResults.reserve(spiceStruct.fieldTypes.getSize());
4761
2/2
✓ Branch 0 taken 3892 times.
✓ Branch 1 taken 1618 times.
5510 foreach IVisitable* fieldValueNode : node.fieldLst.args {
4762 3892 LLVMExprResult fieldValue = this.visitExpr(fieldValueNode);
4763 3892 fieldValue.node = fieldValueNode;
4764 3892 fieldValueResults.pushBack(fieldValue);
4765
2/2
✓ Branch 0 taken 2076 times.
✓ Branch 1 taken 1816 times.
3892 canBeConstant = canBeConstant && !fieldValue.constant.isNull();
4766 3892 }
4767
4768
2/2
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 1434 times.
1618 if canBeConstant { // All field values are constants, so we can create a global constant struct instantiation
4769 // Collect constants
4770 184 Vector<llvm::Value> constants;
4771 // The VTable address point for the synthesized vtable pointer
4772
2/2
✓ Branch 0 taken 178 times.
✓ Branch 1 taken 6 times.
184 if spiceStruct.hasSynthesizedVTablePtr() {
4773 6 constants.pushBack(this.getVTableAddressPoint(&spiceStruct.structBase));
4774 6 }
4775 // For each interface a nullptr
4776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 184 times.
184 foreach const QualType& interfaceType : spiceStruct.interfaceTypes {
4777 ✗ constants.pushBack(this.getDefaultValueForSymbolType(interfaceType));
4778 ✗ }
4779 // Constant value for each field
4780
2/2
✓ Branch 0 taken 364 times.
✓ Branch 1 taken 184 times.
548 foreach const LLVMExprResult& exprResult : fieldValueResults {
4781 364 constants.pushBack(exprResult.constant);
4782 364 }
4783
4784 // Create global constant struct
4785 184 const llvm::Value constantStruct = llvm::getConstNamedStruct(structType, constants);
4786
4787 368 return exprResultAny(exprResultWithConstant(constantStruct));
4788 184 }
4789
4790 // We have at least one non-immediate value, so we need to take normal struct instantiation as fallback
4791 1434 const llvm::Value structAddr = this.insertAlloca(structType);
4792 1434 const unsigned long interfaceCount = spiceStruct.interfaceTypes.getSize();
4793 1434 const unsigned long fieldCount = spiceStruct.fieldTypes.getSize();
4794 1434 unsigned long i = 0l;
4795
4796 // Store the VTable address point for the synthesized vtable pointer
4797
2/2
✓ Branch 0 taken 1428 times.
✓ Branch 1 taken 6 times.
1434 if spiceStruct.hasSynthesizedVTablePtr() {
4798 6 this.insertStore(this.getVTableAddressPoint(&spiceStruct.structBase), structAddr);
4799 6 }
4800
4801 // Store interface values at their corresponding offsets
4802
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1434 times.
1434 while i < interfaceCount {
4803 ✗ const QualType& interfaceType = spiceStruct.interfaceTypes.get(i);
4804 // Get field value
4805 ✗ const llvm::Value itemValue = this.getDefaultValueForSymbolType(interfaceType);
4806 // Get field address
4807 ✗ const llvm::Value currentFieldAddress = this.insertStructGEP(structType, structAddr, cast<unsigned int>(spiceStruct.getFieldElementIndex(i)));
4808 // Store the item value
4809 ✗ this.insertStore(itemValue, currentFieldAddress);
4810 ✗ i++;
4811 ✗ }
4812
4813 // Store all field values at their corresponding offsets
4814 1434 const Vector<model::Function*>& fieldCopyCtors = node.fieldCopyCtors.get(this.manIdx);
4815
2/2
✓ Branch 0 taken 3528 times.
✓ Branch 1 taken 1434 times.
4962 while i < interfaceCount + fieldCount {
4816 3528 LLVMExprResult& exprResult = fieldValueResults.get(i);
4817 3528 const QualType& fieldType = fieldTypes.get(i);
4818 // Get field value
4819 3528 llvm::Value itemValue;
4820 3528 const model::Function* copyCtor = fieldCopyCtors.get(i);
4821
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3519 times.
3528 if fieldType.isRef() {
4822 9 itemValue = this.resolveAddress(exprResult);
4823
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 3491 times.
3528 } else if copyCtor != nil<const model::Function*> {
4824 // Deep-copy the value into the field via its copy ctor: the source is not consumed/moved here, so the
4825 // caller keeps its own copy alive and destructs it independently - a raw value copy would alias its
4826 // owned heap resources and lead to a double-free once one of the two is destructed.
4827 28 const llvm::Value originalPtr = this.resolveAddress(exprResult);
4828 28 const llvm::Type fieldLLVMType = fieldType.toLLVMType(this.sourceFile);
4829 28 const llvm::Value fieldCopyPtr = this.insertAlloca(fieldLLVMType, String("field.copy"));
4830 28 Vector<llvm::Value> ctorArgs;
4831 28 ctorArgs.pushBack(originalPtr);
4832 28 this.generateCtorOrDtorCall(fieldCopyPtr, copyCtor, ctorArgs);
4833 28 itemValue = this.insertLoad(fieldLLVMType, fieldCopyPtr);
4834 28 } else {
4835 3491 itemValue = this.resolveValue(exprResult.node, exprResult);
4836 3519 }
4837 // Get field address
4838 3528 const llvm::Value currentFieldAddress = this.insertStructGEP(structType, structAddr, cast<unsigned int>(spiceStruct.getFieldElementIndex(i)));
4839 // Store the item value
4840
2/2
✓ Branch 0 taken 791 times.
✓ Branch 1 taken 2737 times.
3528 const bool storeVolatile = exprResult.entry != nil<const SymbolTableEntry*> && exprResult.entry.isVolatile;
4841 3528 this.insertStore(itemValue, currentFieldAddress, storeVolatile);
4842 3528 i++;
4843 3528 }
4844
4845 // Attach address to anonymous symbol to keep track of de-allocation
4846 1434 const SymbolTableEntry* returnSymbol = this.currentScope.symbolTable.lookupAnonymous(cast<ASTNode*>(node));
4847
2/2
✓ Branch 0 taken 1424 times.
✓ Branch 1 taken 10 times.
1434 if returnSymbol != nil<const SymbolTableEntry*> {
4848 10 this.updateAddress(returnSymbol, structAddr);
4849 10 }
4850
4851 2868 return exprResultAny(exprResultWithPtr(structAddr));
4852 1618 }
4853
4854 /**
4855 * Shared implementation of visitLambdaFunc, visitLambdaProc and visitLambdaExpr. The differences are marked below.
4856 *
4857 * @param node Lambda node
4858 * @param lambdaBase Lambda base node
4859 * @param spiceFunc Manifestation of the lambda (a copy, since the mangle suffix may be set)
4860 * @param isFunction Whether the lambda returns a value
4861 * @param body Body of the lambda (statement list), or nil for lambda expressions
4862 * @param lambdaExpr Expression of a lambda expression, or nil for lambda functions/procedures
4863 * @param setMangleSuffix Whether to set the mangle suffix to the manifestation index (lambda functions/procedures)
4864 * @return Fat pointer to the lambda
4865 */
4866 136 f<Any> IRGenerator.visitLambda(ASTNode* node, ASTLambdaBaseNode* lambdaBase, model::Function& spiceFunc, bool isFunction, IVisitable* body, IVisitable* lambdaExpr, bool setMangleSuffix, const String& scopeId) {
4867 136 ParamInfoList paramInfoList;
4868 136 Vector<llvm::Type> paramTypes;
4869
4870 // Change scope
4871 136 Scope* bodyScope = this.currentScope.getChildScope(scopeId);
4872 136 this.currentScope = bodyScope;
4873
4874 // Every lambda uniformly takes a trailing capture-struct pointer as its last argument, even when it captures
4875 // nothing. This keeps the calling convention of all lambdas (and plain function pointers) identical, so a lambda
4876 // can be stored, retrieved and called without the call site knowing statically whether it captures. A
4877 // non-capturing lambda simply ignores the passed (poison) pointer. Passing captures last (rather than first) keeps
4878 // fat ptr slot 0 directly callable through a plain C calling convention (e.g. when handed to a C API as a callback).
4879 136 const CaptureMap& captures = bodyScope.symbolTable.captures;
4880 136 const bool hasCaptures = !captures.isEmpty();
4881 136 llvm::Type capturesStructType;
4882
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 68 times.
136 if hasCaptures {
4883 68 capturesStructType = this.buildCapturesContainerType(captures);
4884 68 }
4885
4886 // Visit parameters
4887 136 unsigned long argIdx = 0l;
4888
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 89 times.
136 if lambdaBase.hasParams {
4889 89 const ASTParamLstNode* paramLst = downcast<ASTParamLstNode>(lambdaBase.paramLst);
4890 89 const unsigned long numOfParams = spiceFunc.paramList.getSize();
4891 89 const QualTypeList spiceFuncParamTypes = spiceFunc.getParamTypes();
4892 89 paramInfoList.reserve(numOfParams + 1l);
4893 89 paramTypes.reserve(numOfParams + 1l);
4894
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 115 times.
204 while argIdx < numOfParams {
4895 115 const ASTDeclStmtNode* param = paramLst.params.get(argIdx);
4896 // Get symbol table entry of param
4897 115 const SymbolTableEntry* paramSymbol = this.currentScope.lookupStrict(param.varName);
4898 115 assert paramSymbol != nil<const SymbolTableEntry*>;
4899 // Retrieve type of param
4900 115 const QualType& paramSType = spiceFuncParamTypes.get(argIdx);
4901 115 const llvm::Type paramType = paramSType.getParamLLVMType(this.sourceFile);
4902 // Add it to the lists
4903 115 paramInfoList.pushBack(ParamInfo{param.varName, paramSymbol});
4904 115 paramTypes.pushBack(paramType);
4905 115 argIdx++;
4906 115 }
4907 89 }
4908
4909 // Append the trailing capture-struct pointer (always passed as pointer)
4910 136 paramInfoList.pushBack(ParamInfo{String(CAPTURES_PARAM_NAME), nil<const SymbolTableEntry*>});
4911 136 paramTypes.pushBack(this.builder.getPtrTy());
4912
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 71 times.
136 const QualType returnSType = isFunction ? spiceFunc.returnType : QualType(SuperType::TY_DYN);
4913 136 const unsigned int argOffset = this.getArgOffset(returnSType);
4914 136 const unsigned int capturesArgIdx = argOffset + cast<unsigned int>(paramTypes.getSize()) - 1;
4915
4916 // Get return type
4917 136 llvm::Type returnType = this.builder.getVoidTy();
4918
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 65 times.
136 if isFunction {
4919 65 returnType = spiceFunc.returnType.toLLVMType(this.sourceFile);
4920 65 }
4921
4922 // Create function or implement declared function
4923
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 132 times.
136 if setMangleSuffix {
4924 132 spiceFunc.mangleSuffix = "." + toString(this.manIdx);
4925 132 }
4926 136 const String mangledName = spiceFunc.getMangledName();
4927 136 const llvm::Type funcType = this.getFunctionType(returnSType, paramTypes);
4928 136 this.module.getOrInsertFunction(mangledName.getRaw(), funcType);
4929 136 llvm::Function lambda = this.module.getFunction(mangledName.getRaw());
4930
4931 // Set attributes to function
4932 136 lambda.setLinkage(llvm::LLVMLinkage::InternalLinkage);
4933 136 this.addCommonFctAttrs(lambda);
4934 136 this.enableFunctionInstrumentation(lambda);
4935 136 const ReturnABIInfo returnABI = this.getReturnABIInfo(returnSType);
4936
2/2
✓ Branch 0 taken 131 times.
✓ Branch 1 taken 5 times.
136 if returnABI.isIndirect() {
4937 5 this.addSRetParamAttrs(lambda, returnABI.memoryType);
4938 5 }
4939
4940 // In case of captures, add attribute to captures argument
4941
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 68 times.
136 if hasCaptures {
4942 68 lambda.addParamAttr(capturesArgIdx, "noundef");
4943 68 lambda.addParamAttr(capturesArgIdx, "nonnull");
4944 68 lambda.addDereferenceableParamAttr(capturesArgIdx, cast<unsigned long>(this.module.getPointerSize()));
4945 68 }
4946
4947 // Add debug info
4948 136 this.diGenerator.generateFunctionDebugInfo(lambda, &spiceFunc, true);
4949 136 this.diGenerator.setSourceLocation(node);
4950
4951 // Save alloca insert markers
4952 136 const llvm::BasicBlock allocaInsertBlockOrig = this.allocaInsertBlock;
4953 136 const llvm::Value allocaInsertInstOrig = this.allocaInsertInst;
4954 136 const llvm::BasicBlock bOrig = this.builder.getInsertBlock();
4955
4956 // Create entry block
4957 136 const llvm::BasicBlock bEntry = this.createBlock();
4958 136 this.switchToBlock(bEntry, lambda);
4959
4960 // Reset alloca insert markers to this block
4961 136 this.allocaInsertBlock = bEntry;
4962 136 this.allocaInsertInst = llvm::Value();
4963
4964 // Declare result variable
4965 136 const SymbolTableEntry* resultEntry = nil<const SymbolTableEntry*>;
4966
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 132 times.
✓ Branch 2 taken 75 times.
✓ Branch 3 taken 61 times.
136 if body != nil<IVisitable*> && isFunction {
4967 61 resultEntry = this.currentScope.lookupStrict(String(RETURN_VARIABLE_NAME));
4968 61 assert resultEntry != nil<const SymbolTableEntry*>;
4969
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
61 if resultEntry.isInitialized() {
4970 // If the value is returned via memory, the result variable lives in the caller's memory
4971 ✗ llvm::Value resultAddr = getSRetArg(lambda);
4972
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if resultAddr.isNull() {
4973 ✗ resultAddr = this.insertAlloca(returnType, String(RETURN_VARIABLE_NAME));
4974 ✗ }
4975 ✗ this.updateAddress(resultEntry, resultAddr);
4976 // Generate debug info
4977 ✗ this.diGenerator.generateLocalVarDebugInfo(String(RETURN_VARIABLE_NAME), resultAddr);
4978 ✗ }
4979 61 }
4980
4981 // Store function argument values. The sret parameter is skipped
4982 136 llvm::Value captureStructPtrPtr;
4983 136 const unsigned int argCount = lambda.argSize() - argOffset;
4984 136 const Vector<llvm::Type> funcParamTypes = funcType.getParamTypes();
4985
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 136 times.
387 for unsigned int argNumber = 0; argNumber < argCount; argNumber++ {
4986 251 const llvm::Value arg = lambda.getArg(argOffset + argNumber);
4987 // Get parameter info
4988 251 const ParamInfo& paramInfo = paramInfoList.get(argNumber);
4989 251 const SymbolTableEntry* paramSymbol = paramInfo.entry;
4990 // Decayed array params already carry the address of the array, so they do not need a local copy
4991
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 249 times.
251 if this.bindDecayedArrayParam(arg, argNumber, paramInfo.name, paramSymbol) {
4992 2 continue;
4993 }
4994 // Allocate space for it
4995 249 const llvm::Type paramType = funcParamTypes.get(argOffset + argNumber);
4996 249 const llvm::Value paramAddress = this.insertAlloca(paramType, paramInfo.name);
4997 // Update the symbol table entry
4998 249 const bool isCapturesStruct = argOffset + argNumber == capturesArgIdx;
4999
2/2
✓ Branch 0 taken 136 times.
✓ Branch 1 taken 113 times.
249 if isCapturesStruct {
5000 136 captureStructPtrPtr = paramAddress;
5001 136 } else {
5002 113 this.updateAddress(paramSymbol, paramAddress);
5003 113 }
5004 // Generate debug info
5005
2/2
✓ Branch 0 taken 136 times.
✓ Branch 1 taken 113 times.
249 if !isCapturesStruct {
5006 113 this.diGenerator.generateLocalVarDebugInfo(paramInfo.name, paramAddress, cast<unsigned long>(argNumber) + 1l);
5007 113 }
5008 // Store the value at the new address
5009 249 this.insertStore(arg, paramAddress);
5010 249 }
5011
5012 // Store the default values for optional function args
5013
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 89 times.
136 if lambdaBase.paramLst != nil<IVisitable*> {
5014 89 const ASTParamLstNode* paramLst = downcast<ASTParamLstNode>(lambdaBase.paramLst);
5015
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 89 times.
89 while argIdx < paramLst.params.getSize() {
5016 ✗ this.visit(cast<IVisitable*>(paramLst.params.get(argIdx)));
5017 ✗ argIdx++;
5018 ✗ }
5019 89 }
5020
5021 // Extract captures from captures struct
5022
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 68 times.
136 if hasCaptures {
5023 68 assert !paramInfoList.isEmpty();
5024 68 this.unpackCapturesToLocalVariables(captures, captureStructPtrPtr, capturesStructType);
5025 68 }
5026
5027
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 4 times.
136 if body != nil<IVisitable*> {
5028 // Visit body
5029 132 this.visit(body);
5030
5031 // Create return statement if the block is not terminated yet
5032
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 70 times.
132 if !this.blockAlreadyTerminated {
5033
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if isFunction {
5034 ✗ llvm::Value res = this.getDefaultValueForSymbolType(spiceFunc.returnType);
5035
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if resultEntry.isInitialized() {
5036 ✗ const llvm::Value resultAddr = this.getAddress(resultEntry);
5037
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ res = resultAddr.isSameAs(getSRetArg(lambda)) ? llvm::Value() : this.insertLoad(returnType, resultAddr);
5038 ✗ }
5039 ✗ this.insertReturn(res);
5040 ✗ } else {
5041 70 this.builder.createRetVoid();
5042 70 }
5043 70 }
5044 132 } else {
5045 // Visit lambda expression
5046 4 const llvm::Value exprResult = this.resolveValue(lambdaExpr);
5047 4 this.insertReturn(exprResult);
5048 4 }
5049
5050 // Pop capture addresses
5051
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 68 times.
136 if hasCaptures {
5052
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 68 times.
160 foreach Pair<const String&, const Capture&> capture : captures {
5053 92 const Capture& captureValue = capture.getSecond();
5054 92 this.popAddress(captureValue.capturedSymbol);
5055 92 }
5056 68 }
5057
5058 // Conclude debug info for function
5059 136 this.diGenerator.concludeFunctionDebugInfo();
5060 136 this.diGenerator.setSourceLocation(node);
5061
5062 // Restore alloca insert markers
5063 136 this.builder.setInsertPoint(bOrig);
5064 136 this.blockAlreadyTerminated = false;
5065 136 this.allocaInsertBlock = allocaInsertBlockOrig;
5066 136 this.allocaInsertInst = allocaInsertInstOrig;
5067
5068 // Change back to original scope
5069 136 this.currentScope = this.currentScope.parent;
5070
5071 // Verify function
5072 136 this.verifyFunction(lambda, node.codeLoc);
5073
5074 // Captures, create a struct { <fct-ptr>, <capture struct ptr> }
5075 136 const llvm::Value res = this.buildFatFctPtr(bodyScope, capturesStructType, lambda.asValue());
5076
5077 136 LLVMExprResult exprResult;
5078 136 exprResult.ptr = res;
5079 136 exprResult.node = node.visitable;
5080 272 return exprResultAny(exprResult);
5081 136 }
5082
5083 61 public f<Any> IRGenerator.visitLambdaFunc(ASTLambdaFuncNode* node) {
5084 61 model::Function spiceFunc = node.manifestations.get(this.manIdx);
5085 122 return this.visitLambda(cast<ASTNode*>(node), &node.node, spiceFunc, true, node.body, nil<IVisitable*>, true, node.getScopeId());
5086 61 }
5087
5088 71 public f<Any> IRGenerator.visitLambdaProc(ASTLambdaProcNode* node) {
5089 71 model::Function spiceFunc = node.manifestations.get(this.manIdx);
5090 142 return this.visitLambda(cast<ASTNode*>(node), &node.node, spiceFunc, false, node.body, nil<IVisitable*>, true, node.getScopeId());
5091 71 }
5092
5093 4 public f<Any> IRGenerator.visitLambdaExpr(ASTLambdaExprNode* node) {
5094 4 model::Function spiceFunc = node.manifestations.get(this.manIdx);
5095 8 return this.visitLambda(cast<ASTNode*>(node), &node.node, spiceFunc, spiceFunc.isFunction(), nil<IVisitable*>, node.lambdaExpr, false, node.getScopeId());
5096 4 }
5097
5098 10048 public f<Any> IRGenerator.visitDataType(ASTDataTypeNode* node) {
5099 // Retrieve symbol type
5100 10048 const QualType symbolType = this.getEvaluatedType(cast<IVisitable*>(node));
5101 10048 assert !symbolType.is(SuperType::TY_DYN); // Symbol type should not be dyn anymore at this point
5102 10048 return Any(symbolType.toLLVMType(this.sourceFile));
5103 }
5104
5105 64 f<llvm::Function> IRGenerator.getOrCreateFatFctPtrThunk(llvm::Function target) {
5106 // Plain function/procedure references are stored in fat function pointers and called through the uniform lambda
5107 // calling convention, which always passes a trailing capture-struct pointer. A named function does not have that
5108 // parameter, so we wrap it in a thunk that has the extra (ignored) pointer and forwards to the real function.
5109 64 const String thunkName = target.getName() + ".fatthunk";
5110 64 const llvm::Function existing = this.module.getFunction(thunkName.getRaw());
5111
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 43 times.
64 if !existing.isNull() {
5112 42 return existing;
5113 }
5114
5115 // Build the thunk signature: the target's signature with an additional trailing capture-struct pointer
5116 43 const llvm::Type targetType = target.getFunctionType();
5117 43 const Vector<llvm::Type> targetParamTypes = targetType.getParamTypes();
5118 43 Vector<llvm::Type> paramTypes;
5119 43 paramTypes.reserve(targetParamTypes.getSize() + 1l);
5120
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 43 times.
136 foreach const llvm::Type& paramType : targetParamTypes {
5121 93 paramTypes.pushBack(paramType);
5122 93 }
5123 43 paramTypes.pushBack(this.builder.getPtrTy()); // Ignored captures pointer
5124 43 const llvm::Type thunkType = llvm::getFunctionType(targetType.getReturnType(), paramTypes, targetType.isFunctionVarArg());
5125
5126 43 const llvm::Function thunk = this.module.createFunction(thunkType, llvm::LLVMLinkage::PrivateLinkage, thunkName.getRaw());
5127 43 this.addCommonFctAttrs(thunk);
5128
5129 // Save insert markers, because we emit the thunk body in the middle of generating another function
5130 43 const llvm::BasicBlock bOrig = this.builder.getInsertBlock();
5131 43 const llvm::BasicBlock allocaInsertBlockOrig = this.allocaInsertBlock;
5132 43 const llvm::Value allocaInsertInstOrig = this.allocaInsertInst;
5133
5134 43 const llvm::BasicBlock bEntry = this.createBlock(String("entry"));
5135 43 this.switchToBlock(bEntry, thunk);
5136
5137 // Forward all arguments except the trailing (ignored) captures pointer
5138 43 Vector<llvm::Value> fwdArgs;
5139 43 fwdArgs.reserve(targetParamTypes.getSize());
5140
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 43 times.
136 for unsigned int i = 0; i < cast<unsigned int>(targetParamTypes.getSize()); i++ {
5141 93 fwdArgs.pushBack(thunk.getArg(i));
5142 93 }
5143 43 const llvm::Value call = this.builder.createCall(target, fwdArgs);
5144 // Forward the sret pointer, if the target returns via memory
5145 43 const llvm::Value targetSRetArg = getSRetArg(target);
5146
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 1 time.
43 if !targetSRetArg.isNull() {
5147 1 const llvm::Type sretType = target.getParamTypeAttr(0, "sret");
5148 1 this.addSRetParamAttrs(thunk, sretType);
5149 1 this.addSRetCallParamAttrs(call, sretType);
5150 1 }
5151 43 const llvm::Type targetReturnType = targetType.getReturnType();
5152
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 23 times.
43 if targetReturnType.isVoidTy() {
5153 20 this.builder.createRetVoid();
5154 20 } else {
5155 23 this.builder.createRet(call);
5156 23 }
5157
5158 // Restore insert markers
5159 43 this.builder.setInsertPoint(bOrig);
5160 43 this.blockAlreadyTerminated = false;
5161 43 this.allocaInsertBlock = allocaInsertBlockOrig;
5162 43 this.allocaInsertInst = allocaInsertInstOrig;
5163
5164 86 return thunk;
5165 64 }
5166
5167 200 f<llvm::Value> IRGenerator.buildFatFctPtr(Scope* bodyScope, llvm::Type capturesStructType, llvm::Value lambda) {
5168 // Create capture struct if required
5169 200 llvm::Value capturesPtr;
5170 // Byte size of the owned capture struct. This is only non-zero if the captures live in a dedicated
5171 // (stack-allocated) struct that the std Lambda type needs to relocate to the heap to take ownership.
5172 // A single capture stored inline in the capturePtr slot, or no captures at all, leaves this at 0.
5173 200 unsigned long captureStructSize = 0l;
5174
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 68 times.
200 if !capturesStructType.isNull() {
5175 68 assert bodyScope != nil<Scope*>;
5176 68 const CaptureMap& captures = bodyScope.symbolTable.captures;
5177 // If we have a single capture of ptr type, we can directly store it into the fat ptr. Otherwise, we need a stack allocated
5178 // struct to store the captures in a memory-efficient manner and store a pointer to that struct to the fat ptr.
5179
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 29 times.
68 if capturesStructType.isPointerTy() {
5180 39 assert captures.getSize() == 1l;
5181
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 39 times.
78 foreach Pair<const String&, const Capture&> captureEntry : captures {
5182 39 const Capture& capture = captureEntry.getSecond();
5183
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 22 times.
39 if capture.getMode() == CapturePassMode::BY_VALUE {
5184 22 const QualType& capturedType = capture.capturedSymbol.getQualType();
5185 22 const llvm::Type varType = capturedType.toLLVMType(this.sourceFile);
5186 22 capturesPtr = this.insertLoad(varType, this.getAddress(capture.capturedSymbol));
5187 22 } else {
5188 17 capturesPtr = this.getAddress(capture.capturedSymbol);
5189 17 }
5190 39 }
5191 39 } else {
5192 29 capturesPtr = this.insertAlloca(capturesStructType, String(CAPTURES_PARAM_NAME));
5193 29 captureStructSize = this.module.getTypeAllocSize(capturesStructType);
5194 29 unsigned int captureIdx = 0;
5195
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 53 times.
82 foreach Pair<const String&, const Capture&> captureEntry : captures {
5196 53 const Capture& capture = captureEntry.getSecond();
5197 53 const SymbolTableEntry* capturedEntry = capture.capturedSymbol;
5198 // Get address or value of captured variable, depending on the capturing mode
5199 53 llvm::Value capturedValue = this.getAddress(capturedEntry);
5200 53 assert !capturedValue.isNull();
5201
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 38 times.
53 if capture.getMode() == CapturePassMode::BY_VALUE {
5202 38 const QualType& capturedType = capturedEntry.getQualType();
5203 38 const llvm::Type captureType = capturedType.toLLVMType(this.sourceFile);
5204 38 capturedValue = this.insertLoad(captureType, capturedValue);
5205 38 }
5206 // Store it in the capture struct
5207 53 const llvm::Value captureAddress = this.insertStructGEP(capturesStructType, capturesPtr, captureIdx);
5208 53 this.insertStore(capturedValue, captureAddress);
5209 53 captureIdx++;
5210 53 }
5211 29 }
5212 68 }
5213
5214 // Create fat pointer
5215 200 const llvm::Value fatFctPtr = this.insertAlloca(this.llvmTypes.lambdaFatPtrType, String("fat.ptr"));
5216 200 const llvm::Value fctPtr = this.insertStructGEP(this.llvmTypes.lambdaFatPtrType, fatFctPtr, 0);
5217 200 this.insertStore(lambda, fctPtr);
5218 200 const llvm::Value capturePtr = this.insertStructGEP(this.llvmTypes.lambdaFatPtrType, fatFctPtr, 1);
5219 // The uniform lambda calling convention always loads and passes this slot, so it must hold a defined value even
5220 // when there are no captures. A null pointer is passed to (and ignored by) non-capturing targets.
5221
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 135 times.
200 this.insertStore(!capturesPtr.isNull() ? capturesPtr : llvm::getNullValue(this.builder.getPtrTy()), capturePtr);
5222 200 const llvm::Value captureSizePtr = this.insertStructGEP(this.llvmTypes.lambdaFatPtrType, fatFctPtr, 2);
5223 200 this.insertStore(this.builder.getInt64(captureStructSize), captureSizePtr);
5224
5225 400 return fatFctPtr;
5226 200 }
5227
5228 68 f<llvm::Type> IRGenerator.buildCapturesContainerType(const CaptureMap& captures) {
5229 68 assert !captures.isEmpty();
5230
5231 // If we have only one capture that is a ptr, we can just use that ptr type
5232
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 44 times.
68 if captures.getSize() == 1l {
5233
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 5 times.
49 foreach Pair<const String&, const Capture&> captureEntry : captures {
5234 44 const Capture& capture = captureEntry.getSecond();
5235 44 const QualType& capturedType = capture.capturedSymbol.getQualType();
5236
4/4
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 5 times.
44 if capturedType.isPtr() || capture.getMode() == CapturePassMode::BY_REFERENCE {
5237 39 return this.builder.getPtrTy();
5238 }
5239 5 }
5240 5 }
5241
5242 // Create captures struct type
5243 29 Vector<llvm::Type> captureTypes;
5244
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 29 times.
82 foreach Pair<const String&, const Capture&> captureEntry : captures {
5245 53 const Capture& c = captureEntry.getSecond();
5246
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 38 times.
53 if c.getMode() == CapturePassMode::BY_VALUE {
5247 38 const QualType& capturedType = c.capturedSymbol.getQualType();
5248 38 captureTypes.pushBack(capturedType.toLLVMType(this.sourceFile));
5249 38 } else {
5250 15 captureTypes.pushBack(this.builder.getPtrTy());
5251 15 }
5252 53 }
5253 58 return llvm::getStructType(*this.context, captureTypes);
5254 29 }
5255
5256 68 p IRGenerator.unpackCapturesToLocalVariables(const CaptureMap& captures, llvm::Value val, llvm::Type structType) {
5257 68 assert !captures.isEmpty();
5258 // Debug info attached to a computed address gets dropped by the backend, so the debug info of all captures is attached
5259 // to the stack slot of the captures ptr (val) instead. The address ops lead from there to the capture. By reference
5260 // captures hold the address of the captured variable, so they need one more deref.
5261 // If we have only one capture that is a ptr, we can just load the ptr
5262
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 44 times.
68 if captures.getSize() == 1l {
5263
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 5 times.
49 foreach Pair<const String&, const Capture&> captureEntry : captures {
5264 44 const Capture& firstCapture = captureEntry.getSecond();
5265 44 const QualType& capturedType = firstCapture.capturedSymbol.getQualType();
5266
4/4
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 5 times.
44 if capturedType.isPtr() || firstCapture.getMode() == CapturePassMode::BY_REFERENCE {
5267 // Interpret capturesPtr as ptr to the first and only capture
5268 39 const llvm::Value captureAddress = val;
5269 39 this.pushAddress(firstCapture.capturedSymbol, captureAddress);
5270 // Generate debug info
5271 39 Vector<unsigned long> addressOps;
5272
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 17 times.
39 if firstCapture.getMode() == CapturePassMode::BY_REFERENCE {
5273 17 addressOps.pushBack(llvm::DW_OP_DEREF);
5274 17 }
5275 39 this.diGenerator.generateLocalVarDebugInfo(firstCapture.getName(), val, ULONG_MAX_VALUE, addressOps);
5276 39 return;
5277 39 }
5278 5 }
5279 5 }
5280
5281 // Interpret capturesPtr as ptr to the captures struct
5282 29 const llvm::Value capturesPtr = this.insertLoad(this.builder.getPtrTy(), val);
5283
5284 29 unsigned int captureIdx = 0;
5285
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 53 times.
82 foreach Pair<const String&, const Capture&> captureEntry : captures {
5286 53 const String& name = captureEntry.getFirst();
5287 53 const Capture& capture = captureEntry.getSecond();
5288
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 38 times.
53 const String valueName = capture.getMode() == CapturePassMode::BY_REFERENCE ? name + ".addr" : name;
5289 53 const llvm::Value captureAddress = this.insertStructGEP(structType, capturesPtr, captureIdx, valueName);
5290 53 this.pushAddress(capture.capturedSymbol, captureAddress);
5291 // Generate debug info
5292 53 const unsigned long offset = this.module.getStructElementOffset(structType, captureIdx);
5293 53 Vector<unsigned long> addressOps;
5294 53 addressOps.pushBack(llvm::DW_OP_DEREF);
5295 53 addressOps.pushBack(llvm::DW_OP_PLUS_UCONST);
5296 53 addressOps.pushBack(offset);
5297
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 15 times.
53 if capture.getMode() == CapturePassMode::BY_REFERENCE {
5298 15 addressOps.pushBack(llvm::DW_OP_DEREF);
5299 15 }
5300 53 this.diGenerator.generateLocalVarDebugInfo(capture.getName(), val, ULONG_MAX_VALUE, addressOps);
5301 53 captureIdx++;
5302 53 }
5303 29 }
5304
5305 // ====================================================== Builtin functions ======================================================
5306
5307 9122 f<Any> IRGenerator.visitBuiltinCall(ASTFctCallNode* node) {
5308 // If we have a compile time value, but the computation is still there, we can simply use this constant value
5309 9122 ASTNode* baseNode = cast<ASTNode*>(node);
5310
2/2
✓ Branch 0 taken 1112 times.
✓ Branch 1 taken 8010 times.
9122 if baseNode.hasCompileTimeValue(this.manIdx) {
5311 1112 const CompileTimeValue compileTimeValue = baseNode.getCompileTimeValue(this.manIdx);
5312 1112 const QualType evaluatedType = this.getEvaluatedType(cast<IVisitable*>(node));
5313 1112 const llvm::Value value = this.getConst(compileTimeValue, evaluatedType, baseNode);
5314 1112 return exprResultAny(exprResultWithConstant(value));
5315 }
5316
5317 // If we need to perform runtime actions, call the specified IRGenerator delegate
5318 8010 const String& name = node.fqFunctionName;
5319
2/2
✓ Branch 0 taken 2337 times.
✓ Branch 1 taken 5673 times.
8010 if name == BUILTIN_FCT_NAME_PRINTF { return this.visitBuiltinPrintfCall(node); }
5320
2/2
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 5574 times.
5673 if name == BUILTIN_FCT_NAME_LEN { return this.visitBuiltinLenCall(node); }
5321
2/2
✓ Branch 0 taken 2933 times.
✓ Branch 1 taken 2641 times.
5574 if name == BUILTIN_FCT_NAME_PANIC { return this.visitBuiltinPanicCall(node); }
5322
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2636 times.
2641 if name == BUILTIN_FCT_NAME_SYSCALL { return this.visitBuiltinSyscallCall(node); }
5323
2/2
✓ Branch 0 taken 238 times.
✓ Branch 1 taken 2398 times.
2636 if name == BUILTIN_FCT_NAME_NEW { return this.visitBuiltinNewCall(node); }
5324
2/2
✓ Branch 0 taken 522 times.
✓ Branch 1 taken 1876 times.
2398 if name == BUILTIN_FCT_NAME_PLACEMENT_NEW { return this.visitBuiltinPlacementNewCall(node); }
5325
2/2
✓ Branch 0 taken 924 times.
✓ Branch 1 taken 952 times.
1876 if name == BUILTIN_FCT_NAME_ERR_TRACE_BUFFER { return this.visitBuiltinErrTraceBufferCall(node); }
5326
1/2
✓ Branch 0 taken 952 times.
✗ Branch 1 not taken.
952 if name == BUILTIN_FCT_NAME_STDERR { return this.visitBuiltinStdErrCall(node); }
5327
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if name == BUILTIN_FCT_NAME_FRAME_ADDRESS { return this.visitBuiltinFrameAddressCall(node); }
5328 − panic(Error("Builtin function not implemented!")); // LCOV_EXCL_LINE
5329 }
5330
5331 2337 f<Any> IRGenerator.visitBuiltinPrintfCall(ASTFctCallNode* node) {
5332 // Retrieve templated string
5333 2337 assert node.hasArgs;
5334 2337 IVisitable* firstArg = node.argLst.args.front();
5335 2337 const QualType firstArgType = this.getEvaluatedType(firstArg);
5336 2337 assert firstArgType.is(SuperType::TY_STRING) && this.hasCompileTimeValue(firstArg);
5337 2337 const CompileTimeValue firstArgValue = this.getCompileTimeValue(firstArg);
5338 2337 const unsigned long stringOffset = firstArgValue.getStringValueOffset();
5339 2337 const String templatedString = this.resourceManager.compileTimeStringValues.get(stringOffset);
5340
5341 // Push the template string as first argument
5342 2337 Vector<llvm::Value> printfArgs;
5343 2337 const llvm::Value templateString = this.createGlobalStringConst(String("printf.str."), templatedString, node.codeLoc);
5344 2337 printfArgs.pushBack(templateString);
5345
5346 // Collect replacement arguments
5347
2/2
✓ Branch 0 taken 2053 times.
✓ Branch 1 taken 2337 times.
4390 for unsigned long argIdx = 1l; argIdx < node.argLst.args.getSize(); argIdx++ {
5348 2053 IVisitable* arg = node.argLst.args.get(argIdx);
5349 // Retrieve type of argument
5350 2053 const QualType argSymbolType = this.getEvaluatedType(arg);
5351 2053 const QualType argBaseType = argSymbolType.getBase();
5352
5353 // Re-map some values
5354 2053 llvm::Value argVal;
5355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2053 times.
2053 if argSymbolType.isArray() {
5356 // ToDo: Check if GEP can be removed
5357 ✗ const llvm::Value argValPtr = this.resolveAddress(arg);
5358 ✗ Vector<llvm::Value> indices;
5359 ✗ indices.pushBack(this.getInt64(0l));
5360 ✗ indices.pushBack(this.getInt32(0l));
5361 ✗ const llvm::Type argType = argSymbolType.toLLVMType(this.sourceFile);
5362 ✗ argVal = this.insertInBoundsGEP(argType, argValPtr, indices);
5363
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 1945 times.
2053 } else if argBaseType.isStringObj() {
5364 108 llvm::Value argValPtr = this.resolveAddress(arg);
5365 108 const llvm::Type argBaseLLVMType = argBaseType.toLLVMType(this.sourceFile);
5366 108 argValPtr = this.insertStructGEP(argBaseLLVMType, argValPtr, 0);
5367 108 argVal = this.insertLoad(this.builder.getPtrTy(), argValPtr);
5368 108 } else {
5369 1945 argVal = this.resolveValue(arg);
5370 2053 }
5371
5372 // Extend all integer types lower than 32 bit to 32 bit
5373 2053 const QualType argTypeNoRef = argSymbolType.removeReferenceWrapper();
5374 2053 const dyn smallIntTypes = [SuperType::TY_SHORT, SuperType::TY_BYTE, SuperType::TY_CHAR, SuperType::TY_BOOL];
5375
2/2
✓ Branch 0 taken 1696 times.
✓ Branch 1 taken 357 times.
2053 if argTypeNoRef.isOneOf(smallIntTypes, len(smallIntTypes)) {
5376 357 argVal = this.builder.createIntCast(argVal, this.builder.getInt32Ty(), argTypeNoRef.isSigned());
5377 357 }
5378
5379 2053 printfArgs.pushBack(argVal);
5380 2053 }
5381
5382 // Call printf function
5383 2337 const llvm::Function printfFct = this.stdFunctionManager.getPrintfFct();
5384 2337 llvm::Value callInst = this.builder.createCall(printfFct, printfArgs);
5385
5386 // Add noundef attribute to return value and all arguments
5387 2337 callInst.addCallRetAttr("noundef");
5388
2/2
✓ Branch 0 taken 4390 times.
✓ Branch 1 taken 2337 times.
6727 for unsigned int i = 0; i < cast<unsigned int>(printfArgs.getSize()); i++ {
5389 4390 callInst.addCallParamAttr(i, "noundef");
5390 4390 }
5391
5392 4674 return exprResultAny(exprResultWithValue(callInst));
5393 2337 }
5394
5395 99 f<Any> IRGenerator.visitBuiltinLenCall(ASTFctCallNode* node) {
5396 99 assert node.fqFunctionName == BUILTIN_FCT_NAME_LEN;
5397
5398 // Check if the length is fixed and known via the symbol type
5399 99 IVisitable* argNode = node.argLst.args.front();
5400 99 QualType symbolType = this.getEvaluatedType(argNode);
5401 99 symbolType = symbolType.removeReferenceWrapper();
5402 99 assert symbolType.is(SuperType::TY_STRING);
5403
5404 99 const llvm::Function getRawLengthFct = this.stdFunctionManager.getStringGetRawLengthStringFct();
5405 99 Vector<llvm::Value> args;
5406 99 args.pushBack(this.resolveValue(argNode));
5407 99 const llvm::Value lengthValue = this.builder.createCall(getRawLengthFct, args);
5408 198 return exprResultAny(exprResultWithValue(lengthValue));
5409 99 }
5410
5411 2933 f<Any> IRGenerator.visitBuiltinPanicCall(ASTFctCallNode* node) {
5412 2933 assert node.fqFunctionName == BUILTIN_FCT_NAME_PANIC;
5413
5414 2933 const llvm::Type ptrTy = this.builder.getPtrTy();
5415
5416 // Get value for stderr
5417 2933 const llvm::Value stdErr = this.getStdErrValue();
5418
5419 // Create constant for error message
5420 2933 ASTNode* baseNode = cast<ASTNode*>(node);
5421 2933 const String codeLoc = node.codeLoc.toPrettyString();
5422 2933 const String templateStr = "Program panicked at " + codeLoc + ": %s\n" + baseNode.getErrorMessage() + "\n";
5423 2933 const String globalName = this.getUnusedGlobalName(String(ANON_GLOBAL_STRING_NAME));
5424 2933 llvm::Value globalString = this.module.createGlobalString(templateStr, globalName.getRaw());
5425
5426 // If the output should be comparable, fix alignment to 4 bytes
5427
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2933 times.
2933 if this.cliOptions.comparableOutput {
5428 2933 globalString.setAlignment(4);
5429 2933 }
5430
5431 // Get actual error message
5432 2933 assert node.hasArgs;
5433 2933 IVisitable* assignExpr = node.argLst.args.front();
5434 2933 const llvm::Value errorObjPtr = this.resolveAddress(assignExpr);
5435 2933 const QualType errorObjType = this.getEvaluatedType(assignExpr);
5436 2933 const llvm::Type errorObjTy = errorObjType.toLLVMType(this.sourceFile);
5437 2933 const llvm::Value errorMessagePtr = this.insertStructGEP(errorObjTy, errorObjPtr, 1);
5438 2933 const llvm::Value errorMessage = this.insertLoad(ptrTy, errorMessagePtr);
5439
5440 // Print the error message to stderr
5441 2933 const llvm::Function fprintfFct = this.stdFunctionManager.getFPrintfFct();
5442 2933 Vector<llvm::Value> fprintfArgs;
5443 2933 fprintfArgs.pushBack(stdErr);
5444 2933 fprintfArgs.pushBack(globalString);
5445 2933 fprintfArgs.pushBack(errorMessage);
5446 2933 this.builder.createCall(fprintfFct, fprintfArgs);
5447
5448 // Print the recorded error return trace to stderr. A no-op unless error return tracing was enabled somewhere
5449 // along the propagation chain that produced this error - see error_trace_rt.spice.
5450 2933 const Vector<llvm::Value> noArgs;
5451 2933 this.builder.createCall(this.stdFunctionManager.getErrTraceDumpFct(), noArgs);
5452
5453 // Print the stack trace of the panic. Emitted ahead of the scope cleanup, which is not part of what led to the panic.
5454
1/2
✓ Branch 0 taken 2933 times.
✗ Branch 1 not taken.
2933 if this.cliOptions.printsStackTraceOnAbort() {
5455 ✗ Vector<llvm::Value> dumpArgs;
5456 ✗ dumpArgs.pushBack(this.builder.getTrue());
5457 ✗ dumpArgs.pushBack(this.builder.getFalse());
5458 ✗ this.builder.createCall(this.stdFunctionManager.getDumpStacktraceFct(), dumpArgs);
5459 ✗ }
5460
5461 // Cleanup the scope before calling exit()
5462 // Unreachable below counts as terminator
5463 2933 this.terminateBlock(baseNode.getNextOuterStmtLst());
5464
5465 // Generate call to exit(). The cleanup code is attributed to the closing brace of the scope, the exit to the panic call
5466 2933 this.diGenerator.setSourceLocation(baseNode);
5467 2933 const llvm::Function exitFct = this.stdFunctionManager.getExitFct();
5468 2933 Vector<llvm::Value> exitArgs;
5469 2933 exitArgs.pushBack(this.getInt32(cast<long>(EXIT_FAILURE)));
5470 2933 this.builder.createCall(exitFct, exitArgs);
5471 // Create unreachable instruction
5472 2933 this.builder.createUnreachable();
5473
5474 5866 return Any();
5475 2933 }
5476
5477 5 f<Any> IRGenerator.visitBuiltinSyscallCall(ASTFctCallNode* node) {
5478 5 assert node.fqFunctionName == BUILTIN_FCT_NAME_SYSCALL;
5479
5480 // Determine the required number of operands.
5481 // (We assume at least one argument is provided: the syscall number)
5482 5 assert node.hasArgs;
5483 5 const unsigned int requiredRegs = cast<unsigned int>(node.argLst.args.getSize());
5484 5 assert requiredRegs >= 1 && requiredRegs <= 7;
5485
5486 // Create the asm and constraint strings based on the required number of registers.
5487 5 const String asmString = this.getSysCallAsmString(requiredRegs);
5488 5 const String constraints = this.getSysCallConstraintString(requiredRegs);
5489
5490 // Create the LLVM function type for the inline asm with only the needed operands.
5491 // On i386, the registers are only 32 bits wide and with six args the syscall number is passed via memory.
5492 5 const bool isX86 = this.cliOptions.targetTriple.isX86();
5493
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 const bool passSysCallNumberViaMemory = isX86 && requiredRegs == 7;
5494 5 const llvm::Type int64Ty = this.builder.getInt64Ty();
5495
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 const llvm::Type regTy = isX86 ? this.builder.getInt32Ty() : int64Ty;
5496 5 Vector<llvm::Type> argTypes;
5497
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
27 for unsigned int i = 0; i < requiredRegs; i++ {
5498
3/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
22 argTypes.pushBack(i == 0 && passSysCallNumberViaMemory ? this.builder.getPtrTy() : regTy);
5499 22 }
5500 5 const llvm::Type fctType = llvm::getFunctionType(regTy, argTypes, false);
5501 5 const llvm::Value inlineAsm = llvm::getInlineAsm(fctType, asmString, constraints, true);
5502
5503 // Build the argument list (each provided argument is converted to the register type).
5504 5 Vector<llvm::Value> argValues;
5505 5 argValues.reserve(requiredRegs);
5506
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
27 for unsigned int i = 0; i < requiredRegs; i++ {
5507 22 IVisitable* argNode = node.argLst.args.get(i);
5508 22 const QualType argType = this.getEvaluatedType(argNode);
5509
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 19 times.
22 if argType.is(SuperType::TY_PTR) || argType.is(SuperType::TY_STRING) {
5510 3 argValues.pushBack(this.builder.createPtrToInt(this.resolveValue(argNode), regTy));
5511 3 } else {
5512 19 argValues.pushBack(this.builder.createZExtOrTrunc(this.resolveValue(argNode), regTy));
5513 19 }
5514 22 }
5515
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if passSysCallNumberViaMemory {
5516 ✗ const llvm::Value sysCallNumberPtr = this.insertAlloca(regTy, String("syscall.number"));
5517 ✗ this.insertStore(argValues.front(), sysCallNumberPtr);
5518 ✗ llvm::Value& firstArg = argValues.front();
5519 ✗ firstArg = sysCallNumberPtr;
5520 ✗ }
5521
5522 // Generate the call using only the required number of arguments.
5523 5 llvm::Value call = this.builder.createCall(fctType, inlineAsm, argValues);
5524
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if passSysCallNumberViaMemory {
5525 ✗ call.addCallParamTypeAttr(0, "elementtype", regTy);
5526 ✗ }
5527 5 const llvm::Value res = this.builder.createSExtOrTrunc(call, int64Ty);
5528
5529 10 return exprResultAny(exprResultWithValue(res));
5530 5 }
5531
5532 238 f<Any> IRGenerator.visitBuiltinNewCall(ASTFctCallNode* node) {
5533 238 assert node.fqFunctionName == BUILTIN_FCT_NAME_NEW;
5534
5535 238 const FctCallData& data = node.data.get(this.manIdx);
5536 238 const QualType& templateType = data.templateTypes.front();
5537
5538 // Allocate sizeof(T) bytes on the heap
5539 238 const llvm::Type llvmType = templateType.toLLVMType(this.sourceFile);
5540 238 const unsigned long typeSize = this.module.getTypeAllocSize(llvmType);
5541 238 const llvm::Function allocFct = this.stdFunctionManager.getAllocUnsafeLongFct();
5542 238 Vector<llvm::Value> allocArgs;
5543 238 allocArgs.pushBack(this.builder.getInt64(typeSize));
5544 238 const llvm::Value targetPtr = this.builder.createCall(allocFct, allocArgs);
5545
5546 // Call the constructor if one was resolved during type checking
5547 238 const model::Function* ctor = data.callee;
5548
2/2
✓ Branch 0 taken 228 times.
✓ Branch 1 taken 10 times.
238 if ctor != nil<const model::Function*> {
5549 228 Vector<llvm::Value> ctorArgValues;
5550
2/2
✓ Branch 0 taken 214 times.
✓ Branch 1 taken 14 times.
228 if node.hasArgs {
5551 214 const QualTypeList paramTypes = ctor.getParamTypes();
5552
2/2
✓ Branch 0 taken 232 times.
✓ Branch 1 taken 214 times.
446 for unsigned long i = 0l; i < node.argLst.args.getSize(); i++ {
5553 232 IVisitable* argNode = node.argLst.args.get(i);
5554 232 const QualType& paramType = paramTypes.get(i);
5555
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 191 times.
232 if paramType.isRef() {
5556 41 ctorArgValues.pushBack(this.resolveAddress(argNode));
5557
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 190 times.
232 } else if paramType.isOwnedByValueParam(cast<ASTNode*>(node)) {
5558 1 ctorArgValues.pushBack(this.generateOwnedArgValue(this.resolveAddress(argNode), paramType));
5559 1 } else {
5560 190 ctorArgValues.pushBack(this.resolveValue(argNode));
5561 191 }
5562 232 }
5563 214 }
5564 228 this.generateCtorOrDtorCall(targetPtr, ctor, ctorArgValues);
5565 228 } else {
5566 10 llvm::Value initVal;
5567
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if node.hasArgs {
5568 5 initVal = this.resolveValue(node.argLst.args.front());
5569 5 } else {
5570 5 initVal = this.getDefaultValueForSymbolType(templateType);
5571 5 }
5572 10 this.insertStore(initVal, targetPtr);
5573 10 }
5574
5575 476 return exprResultAny(exprResultWithValue(targetPtr));
5576 238 }
5577
5578 522 f<Any> IRGenerator.visitBuiltinPlacementNewCall(ASTFctCallNode* node) {
5579 522 assert node.fqFunctionName == BUILTIN_FCT_NAME_PLACEMENT_NEW;
5580
5581 522 const FctCallData& data = node.data.get(this.manIdx);
5582
5583 // Resolve the target address from the first argument (the byte* pointer)
5584 522 const llvm::Value targetPtr = this.resolveValue(node.argLst.args.front());
5585
5586 // Call the constructor if one was resolved during type checking
5587 522 const model::Function* ctor = data.callee;
5588
2/2
✓ Branch 0 taken 277 times.
✓ Branch 1 taken 245 times.
522 if ctor != nil<const model::Function*> {
5589 277 Vector<llvm::Value> ctorArgValues;
5590 277 const QualTypeList paramTypes = ctor.getParamTypes();
5591
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 277 times.
557 for unsigned long i = 1l; i < node.argLst.args.getSize(); i++ {
5592 280 IVisitable* argNode = node.argLst.args.get(i);
5593 280 const QualType& paramType = paramTypes.get(i - 1l);
5594
2/2
✓ Branch 0 taken 274 times.
✓ Branch 1 taken 6 times.
280 if paramType.isRef() {
5595 274 ctorArgValues.pushBack(this.resolveAddress(argNode));
5596
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
280 } else if paramType.isOwnedByValueParam(cast<ASTNode*>(node)) {
5597 ✗ ctorArgValues.pushBack(this.generateOwnedArgValue(this.resolveAddress(argNode), paramType));
5598 ✗ } else {
5599 6 ctorArgValues.pushBack(this.resolveValue(argNode));
5600 6 }
5601 280 }
5602 277 this.generateCtorOrDtorCall(targetPtr, ctor, ctorArgValues);
5603 277 } else {
5604 245 const QualType& templateType = data.templateTypes.front();
5605 245 const Vector<IVisitable*>& args = node.argLst.args;
5606 245 llvm::Value initVal;
5607
1/2
✓ Branch 0 taken 245 times.
✗ Branch 1 not taken.
245 if args.getSize() > 1l {
5608 245 initVal = this.resolveValue(args.get(1l));
5609 245 } else {
5610 ✗ initVal = this.getDefaultValueForSymbolType(templateType);
5611 ✗ }
5612 245 this.insertStore(initVal, targetPtr);
5613 245 }
5614
5615 522 return exprResultAny(exprResultWithValue(targetPtr));
5616 }
5617
5618 924 f<Any> IRGenerator.visitBuiltinErrTraceBufferCall(ASTFctCallNode* node) {
5619 924 assert node.fqFunctionName == BUILTIN_FCT_NAME_ERR_TRACE_BUFFER;
5620
5621 // Get or create the thread-local ErrorTraceBuffer instance for this module. It is only ever referenced from
5622 // within error_trace_rt.spice's own wrapper procedures (all compiled into this same module), so internal
5623 // linkage is enough - no other module ever needs to see this symbol.
5624 924 const string tlsGlobalName = "__spice_errTraceBufferTLS";
5625 924 llvm::Value tlsBuffer = this.module.getNamedGlobal(tlsGlobalName);
5626
2/2
✓ Branch 0 taken 616 times.
✓ Branch 1 taken 308 times.
924 if tlsBuffer.isNull() {
5627 308 const QualType evaluatedType = this.getEvaluatedType(cast<IVisitable*>(node));
5628 308 const QualType bufferSType = evaluatedType.removeReferenceWrapper();
5629 308 const llvm::Type bufferType = bufferSType.toLLVMType(this.sourceFile);
5630 308 const llvm::Value zeroInit = llvm::getNullValue(bufferType);
5631 308 tlsBuffer = this.module.addGlobal(bufferType, tlsGlobalName);
5632 308 tlsBuffer.setConstant(false);
5633 308 tlsBuffer.setLinkage(llvm::LLVMLinkage::InternalLinkage);
5634 308 tlsBuffer.setInitializer(zeroInit);
5635 308 tlsBuffer.setThreadLocal(true);
5636 308 }
5637
5638 924 return exprResultAny(exprResultWithPtr(tlsBuffer));
5639 }
5640
5641 952 f<Any> IRGenerator.visitBuiltinStdErrCall(ASTFctCallNode* node) {
5642 952 assert node.fqFunctionName == BUILTIN_FCT_NAME_STDERR;
5643
5644 952 return exprResultAny(exprResultWithValue(this.getStdErrValue()));
5645 }
5646
5647 /**
5648 * Emits a call to LLVM's target-independent 'llvm.frameaddress' intrinsic at level 0, yielding the frame address
5649 * (saved frame-pointer slot) of whichever Spice function this builtin call is inlined into - the same value as
5650 * C's `__builtin_frame_address(0)`.
5651 *
5652 * Frame pointers are kept for every Spice function we emit (see 'frame-pointer' in IRGenerator::addCommonFctAttrs),
5653 * so the returned address is a real frame address. That does NOT make a frame-pointer chain walkable in general:
5654 * the layout of the slots around it is target- and ABI-specific, and on Windows x64 in particular there is no
5655 * saved-frame-pointer chain at all, since the frame pointer points into the middle of the frame and unwinding goes
5656 * through the .pdata/.xdata tables instead. Anything that needs an actual call stack must go through the platform
5657 * unwinder rather than walking from this value.
5658 */
5659 ✗ f<Any> IRGenerator.visitBuiltinFrameAddressCall(ASTFctCallNode* node) {
5660 ✗ assert node.fqFunctionName == BUILTIN_FCT_NAME_FRAME_ADDRESS;
5661
5662 ✗ Vector<llvm::Type> overloadTypes;
5663 ✗ overloadTypes.pushBack(this.builder.getPtrTy());
5664 ✗ const llvm::Function frameAddressFct = this.module.getIntrinsicDeclaration("llvm.frameaddress", overloadTypes);
5665 // Level 0 = this frame; LLVM does not guarantee levels > 0 work on every target.
5666 ✗ Vector<llvm::Value> args;
5667 ✗ args.pushBack(this.getInt32(0l));
5668 ✗ const llvm::Value frameAddress = this.builder.createCall(frameAddressFct, args);
5669
5670 ✗ return exprResultAny(exprResultWithValue(frameAddress));
5671 ✗ }
5672
5673 /**
5674 * Retrieves the process's stderr stream (FILE*). Spice has no syntax for declaring an external variable, so this
5675 * reaches the real libc symbol directly, the same way on every platform: an ACRT accessor call on Windows, and
5676 * the platform-specific extern global everywhere else.
5677 */
5678 8707 f<llvm::Value> IRGenerator.getStdErrValue() {
5679 8707 const llvm::Type ptrTy = this.builder.getPtrTy();
5680
5681
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 8649 times.
8707 if this.cliOptions.targetTriple.isOSWindows() {
5682 58 const llvm::Function getAcrtIOFuncFct = this.stdFunctionManager.getAcrtIOFuncFct();
5683 58 Vector<llvm::Value> args;
5684 58 args.pushBack(this.getInt32(2l)); // Constant for stderr
5685 116 return this.builder.createCall(getAcrtIOFuncFct, args);
5686 58 }
5687
5688 8649 const string globalName = this.cliOptions.targetTriple.isOSDarwin() ? "__stderrp" : "stderr";
5689 8649 llvm::Value stdErrPtr = this.module.getNamedGlobal(globalName);
5690
2/2
✓ Branch 0 taken 6971 times.
✓ Branch 1 taken 1678 times.
8649 if stdErrPtr.isNull() {
5691 1678 stdErrPtr = this.module.getOrInsertGlobal(globalName, ptrTy);
5692 1678 stdErrPtr.setLinkage(llvm::LLVMLinkage::ExternalLinkage);
5693 1678 stdErrPtr.setUnnamedAddr(llvm::LLVMUnnamedAddr::LocalUnnamedAddr);
5694 1678 stdErrPtr.setAlignment(8);
5695 1678 }
5696 8649 return this.insertLoad(ptrTy, stdErrPtr);
5697 }
5698
5699 // ===================================================== Implicit functions ======================================================
5700
5701 75 f<llvm::Value> IRGenerator.doImplicitCast(llvm::Value src, const QualType& origDstSTy, const QualType& origSrcSTy) {
5702 75 assert !origSrcSTy.matches(origDstSTy, false, false, false); // We only need to cast implicitly, if the types do not match exactly
5703 75 QualType dstSTy = origDstSTy;
5704 75 QualType srcSTy = origSrcSTy;
5705
5706 // Unpack the pointers until a pointer of another type is met
5707 75 unsigned long loadCounter = 0l;
5708
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 while srcSTy.isPtr() {
5709 ✗ src = this.insertLoad(srcSTy, src);
5710 ✗ srcSTy = srcSTy.getContained();
5711 ✗ dstSTy = dstSTy.getContained();
5712 ✗ loadCounter++;
5713 ✗ }
5714 // GEP or bit-cast
5715
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
75 if dstSTy.isArray() && srcSTy.isArray() { // Special case that is used for passing arrays as pointer to functions
5716 75 Vector<llvm::Value> indices;
5717 75 indices.pushBack(this.getInt64(0l));
5718 75 indices.pushBack(this.getInt32(0l));
5719 75 src = this.insertInBoundsGEP(srcSTy.toLLVMType(this.sourceFile), src, indices);
5720 75 } else {
5721 ✗ src = this.insertLoad(srcSTy, src);
5722 ✗ src = this.builder.createBitCast(src, dstSTy.toLLVMType(this.sourceFile));
5723 ✗ }
5724 // Pack the pointers together again
5725
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 while loadCounter > 0l {
5726 ✗ const llvm::Value newActualArg = this.insertAlloca(srcSTy);
5727 ✗ this.insertStore(src, newActualArg);
5728 ✗ src = newActualArg;
5729 ✗ loadCounter--;
5730 ✗ }
5731 75 return src;
5732 }
5733
5734 15 public f<llvm::Value> IRGenerator.getUpcastedStructPtr(llvm::Value structPtr, const QualType& dstType, const QualType& srcType) {
5735 // Adjusts a pointer to a struct instance so it points at the embedded subobject the destination type
5736 // refers to. This backs the explicit 'cast<Base*>(derived)' / 'cast<Interface*>(struct)' conversions.
5737 // When the struct carries a vtable prefix (because it implements interfaces), the composed base no longer
5738 // sits at offset 0, so the pointer has to be advanced via a GEP. For subobjects that already sit at offset
5739 // 0 (e.g. the leading interface field), insertStructGEP() with index 0 is a no-op and returns the pointer
5740 // unchanged, so no superfluous IR is emitted. If the given pair of types is not such an upcast, the
5741 // pointer is returned unchanged.
5742 15 QualType dstPointee = dstType.removeReferenceWrapper();
5743 15 QualType srcPointee = srcType.removeReferenceWrapper();
5744 // Both sides are pointers ('structPtr' is the pointer value itself), so look one level deeper
5745
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 6 times.
15 if dstPointee.isPtr() && srcPointee.isPtr() {
5746 6 dstPointee = dstPointee.getContained();
5747 6 srcPointee = srcPointee.getContained();
5748 6 }
5749 // Bail out if this is not an interface/composed-base upcast that requires a pointer adjustment
5750
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if !srcPointee.is(SuperType::TY_STRUCT) {
5751 ✗ return structPtr;
5752 }
5753
3/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if !dstPointee.matchesInterfaceImplementedByStruct(srcPointee) && !dstPointee.matchesComposedBaseOfStruct(srcPointee) {
5754 ✗ return structPtr;
5755 }
5756
5757 15 QualType walkType = srcPointee;
5758
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 17 times.
21 while !walkType.matches(dstPointee, false, true, true) {
5759 17 assert walkType.is(SuperType::TY_STRUCT);
5760 17 Scope* structScope = walkType.getBodyScope();
5761 17 assert structScope != nil<Scope*>;
5762 17 const Struct* spiceStruct = walkType.getStruct(nil<ASTNode*>);
5763 17 assert spiceStruct != nil<const Struct*>;
5764 17 const llvm::Type llvmStructType = walkType.toLLVMType(this.sourceFile);
5765 // Implicit (interface) fields are inserted before the explicit fields, so their count is the difference
5766 // between the total field count and the number of explicit fields.
5767 17 const unsigned long implicitFieldCount = structScope.getFieldCount() - spiceStruct.fieldTypes.getSize();
5768
5769 // Case 1: the destination is an interface implemented directly by the struct -> step into its implicit field
5770
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 11 times.
17 if dstPointee.is(SuperType::TY_INTERFACE) {
5771 11 bool found = false;
5772
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 for unsigned long i = 0l; i < implicitFieldCount; i++ {
5773 11 const SymbolTableEntry* implicitField = structScope.lookupField(cast<unsigned int>(i));
5774
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if dstPointee.matches(implicitField.getQualType(), false, true, true) {
5775 11 structPtr = this.insertStructGEP(llvmStructType, structPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(implicitField.orderIndex)));
5776 11 found = true;
5777 11 break;
5778 }
5779 ✗ }
5780 11 assert found;
5781 11 return structPtr;
5782 }
5783
5784 // Case 2: the destination is a composed base struct -> step into the first explicit (composed) field
5785 6 const SymbolTableEntry* baseField = structScope.lookupField(cast<unsigned int>(implicitFieldCount));
5786 6 assert baseField != nil<const SymbolTableEntry*>;
5787 6 const QualType& baseFieldType = baseField.getQualType();
5788 6 assert baseFieldType.isComposition();
5789 6 structPtr = this.insertStructGEP(llvmStructType, structPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(baseField.orderIndex)));
5790 6 walkType = baseFieldType;
5791 6 }
5792 4 return structPtr;
5793 }
5794
5795 /**
5796 * Generate cleanup code (dtor calls, deallocations, lifetime end markers) for the scope of the given statement list, which is
5797 * the current scope
5798 *
5799 * @param node Statement list of the scope
5800 * @param returnedLocal Local variable, that is handed over to the caller by a return statement. It must not be destructed.
5801 * Nil if there is none.
5802 */
5803 93726 p IRGenerator.generateScopeCleanup(ASTStmtLstNode* node, SymbolTableEntry* returnedLocal = nil<SymbolTableEntry*>) {
5804 93726 this.generateScopeCleanup(node.resourcesToCleanup.get(this.manIdx), this.currentScope, node.closingBraceCodeLoc, returnedLocal);
5805 93726 }
5806
5807 /**
5808 * Generate cleanup code (dtor calls, deallocations, lifetime end markers) for the given scope
5809 *
5810 * @param resources Resources to clean up, as determined by the type checker
5811 * @param scope Scope to clean up
5812 * @param codeLoc Code location to attribute the cleanup code to
5813 * @param returnedLocal Local variable, that is handed over to the caller by a return statement. It must not be destructed.
5814 * Nil if there is none.
5815 */
5816 171434 p IRGenerator.generateScopeCleanup(const ResourcesForManifestationToCleanup& resources, Scope* scope, const CodeLoc& codeLoc, SymbolTableEntry* returnedLocal = nil<SymbolTableEntry*>) {
5817 171434 this.diGenerator.setSourceLocation(codeLoc);
5818
5819 // Do not clean up if the block is already terminated
5820
2/2
✓ Branch 0 taken 48556 times.
✓ Branch 1 taken 122878 times.
171434 if this.blockAlreadyTerminated {
5821 48556 return;
5822 }
5823
5824 // Call all dtor functions
5825 122878 const Vector<llvm::Value> noArgs;
5826
2/2
✓ Branch 0 taken 7259 times.
✓ Branch 1 taken 122878 times.
130137 foreach const Pair<SymbolTableEntry*, model::Function*>& dtorToCall : resources.dtorFunctionsToCall {
5827
2/2
✓ Branch 0 taken 177 times.
✓ Branch 1 taken 7082 times.
7259 if dtorToCall.getFirst() != returnedLocal {
5828 7082 this.generateCtorOrDtorCall(dtorToCall.getFirst(), dtorToCall.getSecond(), noArgs);
5829 7082 }
5830 7259 }
5831
5832 // Deallocate all heap variables that go out of scope and are currently owned
5833
2/2
✓ Branch 0 taken 490 times.
✓ Branch 1 taken 122878 times.
123368 foreach const SymbolTableEntry* entry : resources.heapVarsToFree {
5834 490 this.generateDeallocCall(this.getAddress(entry));
5835 490 }
5836
5837 // Generate lifetime end markers
5838
2/2
✓ Branch 0 taken 118791 times.
✓ Branch 1 taken 4087 times.
122878 if this.cliOptions.useLifetimeMarkers {
5839 4087 const Vector<SymbolTableEntry*> varsGoingOutOfScope = scope.getVarsGoingOutOfScope();
5840
2/2
✓ Branch 0 taken 1890 times.
✓ Branch 1 taken 4087 times.
5977 foreach const SymbolTableEntry* var : varsGoingOutOfScope {
5841 1890 const llvm::Value address = this.getAddress(var);
5842 // Only allocas get a lifetime start marker and llvm.lifetime.end rejects anything else. This excludes e.g. the phi
5843 // of a ternary with a reference result, which refers to the storage of one of its operands.
5844
4/4
✓ Branch 0 taken 429 times.
✓ Branch 1 taken 1461 times.
✓ Branch 2 taken 432 times.
✓ Branch 3 taken 1458 times.
1890 if !address.isNull() && address.isAllocaInst() {
5845 1458 this.createLifetimeMarker("llvm.lifetime.end", address);
5846 1458 }
5847 1890 }
5848 4087 }
5849 122878 }
5850
5851 /**
5852 * Generate cleanup code for the head scope of a for or foreach loop, which holds the variables declared in the head of the
5853 * loop. Those live for the whole loop, so this is generated where the loop is left, not at the end of each round.
5854 *
5855 * @param loopNode For or foreach loop node
5856 * @param headScope Head scope of the loop
5857 * @param returnedLocal Local variable, that is handed over to the caller by a return statement. It must not be destructed.
5858 * Nil if there is none.
5859 */
5860 9095 p IRGenerator.generateLoopHeadCleanup(ASTNode* loopNode, Scope* headScope, SymbolTableEntry* returnedLocal = nil<SymbolTableEntry*>) {
5861 9095 assert headScope.scopeType == ScopeType::LOOP_HEAD;
5862
4/4
✓ Branch 0 taken 5024 times.
✓ Branch 1 taken 438 times.
✓ Branch 2 taken 3293 times.
✓ Branch 3 taken 340 times.
9095 if loopNode.kind == ASTNodeKind::FOR_LOOP {
5863 8317 const ASTForLoopNode* forLoopNode = downcast<ASTForLoopNode>(loopNode);
5864 8317 const ASTStmtLstNode* body = downcast<ASTStmtLstNode>(getASTNode(forLoopNode.body));
5865 8317 const ResourcesForManifestationToCleanup& resources = forLoopNode.headResourcesToCleanup.get(this.manIdx);
5866 8317 this.generateScopeCleanup(resources, headScope, body.closingBraceCodeLoc, returnedLocal);
5867 8317 } else {
5868 778 assert loopNode.kind == ASTNodeKind::FOREACH_LOOP;
5869 778 const ASTForeachLoopNode* foreachLoopNode = downcast<ASTForeachLoopNode>(loopNode);
5870 778 const ASTStmtLstNode* body = downcast<ASTStmtLstNode>(getASTNode(foreachLoopNode.body));
5871 778 const ResourcesForManifestationToCleanup& resources = foreachLoopNode.headResourcesToCleanup.get(this.manIdx);
5872 778 this.generateScopeCleanup(resources, headScope, body.closingBraceCodeLoc, returnedLocal);
5873 778 }
5874 9095 }
5875
5876 /**
5877 * Generate the dtor calls for the temporaries of an expression scope. Those were determined by the type checker.
5878 *
5879 * @param exprScope Expression scope to generate the dtor calls for
5880 * @param node Expression the scope belongs to
5881 */
5882 44561 p IRGenerator.generateTemporariesCleanup(const Scope* exprScope, ASTNode* node) {
5883
2/2
✓ Branch 0 taken 44527 times.
✓ Branch 1 taken 34 times.
44561 if exprScope.temporaryDtorsToCall.isEmpty() {
5884 44527 return;
5885 }
5886 34 this.diGenerator.setSourceLocation(node);
5887 34 const Vector<llvm::Value> noArgs;
5888
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 34 times.
69 foreach const Pair<SymbolTableEntry*, model::Function*>& dtorToCall : exprScope.temporaryDtorsToCall {
5889 35 this.generateCtorOrDtorCall(dtorToCall.getFirst(), dtorToCall.getSecond(), noArgs);
5890 35 }
5891 34 }
5892
5893 /**
5894 * Get the if, while, do-while, for or foreach statement, if the given node is part of its header, e.g. of its condition.
5895 *
5896 * The IR for a header is generated within the scope of its statement, but the header is not part of the body of the statement.
5897 * Hence, the nearest statement list of a node in the header is the one around the statement, and not the one of that scope.
5898 *
5899 * @param node Node to check
5900 * @return Statement, whose header the node is part of. Nil if the node is not part of a header
5901 */
5902 43048 f<ASTNode*> getStatementOfHeader(const ASTNode* node) {
5903 43048 ASTNode* ancestor = node.parent;
5904
1/2
✓ Branch 0 taken 43163 times.
✗ Branch 1 not taken.
43163 while ancestor != nil<ASTNode*> {
5905
2/2
✓ Branch 0 taken 43041 times.
✓ Branch 1 taken 122 times.
43163 if ancestor.kind == ASTNodeKind::STMT_LST {
5906 43041 return nil<ASTNode*>; // The node is part of a body
5907 }
5908 122 const ASTNodeKind kind = ancestor.kind;
5909
10/10
✓ Branch 0 taken 119 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 118 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 117 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 115 times.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 115 times.
122 if kind == ASTNodeKind::IF_STMT || kind == ASTNodeKind::WHILE_LOOP || kind == ASTNodeKind::DO_WHILE_LOOP || kind == ASTNodeKind::FOR_LOOP || kind == ASTNodeKind::FOREACH_LOOP {
5910 7 return ancestor;
5911 }
5912 115 ancestor = ancestor.parent;
5913 115 }
5914 ✗ return nil<ASTNode*>;
5915 }
5916
5917 /**
5918 * Generate cleanup code (dtor calls, deallocations) for every scope between the given node (exclusive) and the given
5919 * target scope (inclusive). This is required for jumps that leave more than one scope at once (e.g. break/continue/return),
5920 * since those skip the normal fall-through cleanup that visitStmtLst() generates for each of the enclosing scopes.
5921 *
5922 * The jump can also originate from the middle of an expression (the error propagation operator), which is evaluated in scopes
5923 * that have no statement list to pair with the scope. These are handled first, so that the scopes and statement lists match up
5924 * for the rest of the way.
5925 *
5926 * @param node Node the jump originates from
5927 * @param targetScope Outermost scope that is left by the jump; cleanup is generated for this scope as well
5928 * @param returnedLocal Local variable, that is handed over to the caller by the jump (a return statement). It must not be
5929 * destructed. Nil if there is none.
5930 */
5931 43048 p IRGenerator.generateScopeCleanupUpTo(ASTNode* node, const Scope* targetScope, SymbolTableEntry* returnedLocal = nil<SymbolTableEntry*>) {
5932 43048 assert targetScope != nil<const Scope*>;
5933 43048 Scope* scopeLevel = this.currentScope;
5934
5935 // Leave the expression scopes, which hold the temporaries of the expression that is evaluated. Only the ones that were already
5936 // constructed are destructed here, since the others do not have an address yet
5937
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 40855 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 2193 times.
43063 while scopeLevel.scopeType == ScopeType::EXPR_BODY {
5938 15 this.generateTemporariesCleanup(scopeLevel, node);
5939 15 scopeLevel = scopeLevel.parent;
5940 15 }
5941
5942 // Leave the scope of the statement, if the jump originates from its header. The body was not entered yet, or it was already
5943 // left, so there is nothing of it to clean up. The head of a for or foreach loop has a scope of its own though, which holds
5944 // the variables the head declares. Those are alive while the head is evaluated.
5945 43048 ASTNode* headerStatement = getStatementOfHeader(node);
5946
7/8
✓ Branch 0 taken 22844 times.
✓ Branch 1 taken 18011 times.
✓ Branch 2 taken 40855 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 97 times.
✓ Branch 5 taken 2096 times.
✓ Branch 6 taken 2186 times.
✓ Branch 7 taken 7 times.
43048 if scopeLevel != targetScope && headerStatement != nil<ASTNode*> {
5947
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 2 times.
7 if scopeLevel.scopeType == ScopeType::LOOP_HEAD {
5948 2 this.generateLoopHeadCleanup(headerStatement, scopeLevel, returnedLocal);
5949 2 }
5950 7 scopeLevel = scopeLevel.parent;
5951 7 }
5952
5953 43048 ASTStmtLstNode* scope = node.getNextOuterStmtLst();
5954
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 62015 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6598 times.
68613 while true {
5955 68613 this.generateScopeCleanup(scope.resourcesToCleanup.get(this.manIdx), scopeLevel, scope.closingBraceCodeLoc, returnedLocal);
5956
4/4
✓ Branch 0 taken 40855 times.
✓ Branch 1 taken 21160 times.
✓ Branch 2 taken 2193 times.
✓ Branch 3 taken 4405 times.
68613 if scopeLevel == targetScope {
5957 43048 break;
5958 }
5959 25565 assert scope.parent != nil<ASTNode*> && scopeLevel.parent != nil<Scope*>;
5960 25565 scopeLevel = scopeLevel.parent;
5961 // The head of a for or foreach loop is a scope of its own, which has no statement list. The loop owns the statement list
5962 // of its body, which was cleaned up just now
5963
4/4
✓ Branch 0 taken 18696 times.
✓ Branch 1 taken 2464 times.
✓ Branch 2 taken 3238 times.
✓ Branch 3 taken 1167 times.
25565 if scopeLevel.scopeType == ScopeType::LOOP_HEAD {
5964 3631 this.generateLoopHeadCleanup(scope.parent, scopeLevel, returnedLocal);
5965 3631 assert scopeLevel != targetScope && scopeLevel.parent != nil<Scope*>;
5966 3631 scopeLevel = scopeLevel.parent;
5967 3631 }
5968 25565 scope = scope.parent.getNextOuterStmtLst();
5969 25565 }
5970 43048 }
5971
5972 11816 p IRGenerator.generateFctDecl(const model::Function* fct, const Vector<llvm::Value>& args) {
5973 // Retrieve metadata for the function
5974 11816 const String mangledName = fct.getMangledName();
5975
5976 // Function is not defined in the current module -> declare it
5977 11816 const llvm::Function existingFct = this.module.getFunction(mangledName.getRaw());
5978
2/2
✓ Branch 0 taken 9387 times.
✓ Branch 1 taken 2429 times.
11816 if existingFct.isNull() {
5979 2429 Vector<llvm::Type> paramTypes;
5980
2/2
✓ Branch 0 taken 2911 times.
✓ Branch 1 taken 2429 times.
5340 foreach const llvm::Value& argValue : args {
5981 2911 paramTypes.pushBack(argValue.getType());
5982 2911 }
5983 2429 assert fct.isProcedure();
5984 2429 const llvm::Type fctType = llvm::getFunctionType(this.builder.getVoidTy(), paramTypes, false);
5985 2429 this.module.getOrInsertFunction(mangledName.getRaw(), fctType);
5986
5987
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2429 times.
2429 if fct.isMethod() {
5988 // Get callee function
5989 2429 llvm::Function callee = this.module.getFunction(mangledName.getRaw());
5990 2429 assert !callee.isNull();
5991
5992 // Set attributes to 'this' param
5993 2429 const llvm::Type structType = this.getThisStructType(fct);
5994 2429 callee.addParamAttr(0, "noundef");
5995 2429 callee.addParamAttr(0, "nonnull");
5996 2429 callee.addDereferenceableParamAttr(0, this.module.getTypeStoreSize(structType));
5997 2429 callee.addParamAttr(0, "align", cast<unsigned long>(this.module.getABITypeAlign(structType)));
5998 2429 }
5999 2429 }
6000 11816 }
6001
6002 11816 #[ignoreUnusedReturnValue]
6003 11816 f<llvm::Value> IRGenerator.generateFctCall(const model::Function* fct, const Vector<llvm::Value>& args) {
6004 // Retrieve metadata for the function
6005 11816 const String mangledName = fct.getMangledName();
6006
6007 // Get callee function
6008 11816 const llvm::Function callee = this.module.getFunction(mangledName.getRaw());
6009 11816 assert !callee.isNull();
6010
6011 // Generate function call
6012 11816 llvm::Value callInst = this.builder.createCall(callee, args);
6013
6014 // Set attributes to 'this' param
6015
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11816 times.
11816 if fct.isMethod() {
6016 11816 const llvm::Type structType = this.getThisStructType(fct);
6017 11816 callInst.addCallParamAttr(0, "noundef");
6018 11816 callInst.addCallParamAttr(0, "nonnull");
6019 11816 callInst.addCallDereferenceableParamAttr(0, this.module.getTypeStoreSize(structType));
6020 11816 callInst.addCallParamAttr(0, "align", cast<unsigned long>(this.module.getABITypeAlign(structType)));
6021 11816 }
6022
6023 23632 return callInst;
6024 11816 }
6025
6026 /**
6027 * Get the LLVM type of the struct, that the 'this' param of the given method points to
6028 *
6029 * @param method Method
6030 * @return LLVM struct type
6031 */
6032 14245 f<llvm::Type> IRGenerator.getThisStructType(const model::Function* method) {
6033 14245 const SymbolTableEntry* thisEntry = method.bodyScope.lookupStrict(String(THIS_VARIABLE_NAME));
6034 14245 assert thisEntry != nil<const SymbolTableEntry*>;
6035 14245 const QualType& thisType = thisEntry.getQualType();
6036 14245 const QualType structSType = thisType.getContained();
6037 14245 const llvm::Type structType = structSType.toLLVMType(this.sourceFile);
6038 14245 assert !structType.isNull();
6039 28490 return structType;
6040 14245 }
6041
6042 11816 p IRGenerator.generateProcDeclAndCall(const model::Function* proc, const Vector<llvm::Value>& args) {
6043 11816 this.generateFctDecl(proc, args);
6044 11816 this.generateFctCall(proc, args);
6045 11816 }
6046
6047 9249 p IRGenerator.generateCtorOrDtorCall(const SymbolTableEntry* entry, const model::Function* ctorOrDtor, const Vector<llvm::Value>& args) {
6048 // Retrieve address of the struct variable. For fields this is the 'this' variable, otherwise use the normal address
6049 9249 llvm::Value structAddr;
6050
2/2
✓ Branch 0 taken 1071 times.
✓ Branch 1 taken 8178 times.
9249 if entry.isField() {
6051 // Take 'this' var as base pointer
6052 1071 const SymbolTableEntry* thisVar = this.currentScope.lookupStrict(String(THIS_VARIABLE_NAME));
6053 1071 assert thisVar != nil<const SymbolTableEntry*>;
6054 1071 const QualType& thisVarType = thisVar.getQualType();
6055 1071 assert thisVarType.isPtr();
6056 1071 const QualType thisStructType = thisVarType.getContained();
6057 1071 assert thisStructType.is(SuperType::TY_STRUCT);
6058 1071 const Struct* spiceStruct = thisStructType.getStruct(entry.declNode);
6059 1071 assert spiceStruct != nil<const Struct*>;
6060 1071 const llvm::Type thisType = thisStructType.toLLVMType(this.sourceFile);
6061 1071 const llvm::Value thisPtr = this.insertLoad(this.builder.getPtrTy(), this.getAddress(thisVar));
6062 // Add field offset
6063 1071 structAddr = this.insertStructGEP(thisType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(entry.orderIndex)));
6064 1071 } else {
6065 8178 structAddr = this.getAddress(entry);
6066 // For optional parameter initializers we need this exception
6067
2/2
✓ Branch 0 taken 624 times.
✓ Branch 1 taken 7554 times.
8178 if structAddr.isNull() {
6068 624 return;
6069 }
6070 7554 }
6071 8625 assert !structAddr.isNull();
6072 8625 this.generateCtorOrDtorCall(structAddr, ctorOrDtor, args);
6073 8625 }
6074
6075 11816 p IRGenerator.generateCtorOrDtorCall(llvm::Value structAddr, const model::Function* ctorOrDtor, const Vector<llvm::Value>& args) {
6076 // Build parameter list
6077 11816 Vector<llvm::Value> argValues;
6078 11816 argValues.pushBack(structAddr);
6079
2/2
✓ Branch 0 taken 1574 times.
✓ Branch 1 taken 11816 times.
13390 foreach const llvm::Value& arg : args {
6080 1574 argValues.pushBack(arg);
6081 1574 }
6082
6083 // Generate function call
6084 11816 this.generateProcDeclAndCall(ctorOrDtor, argValues);
6085 11816 }
6086
6087 /**
6088 * Copy an argument for a by-value param, that owns its argument (see QualType.isOwnedByValueParam). The called function
6089 * destructs the copy.
6090 *
6091 * @param originalPtr Address of the argument
6092 * @param valueType Type of the argument without reference
6093 * @param copyCtor Copy ctor of the argument type
6094 * @return Address of the copy
6095 */
6096 46 public f<llvm::Value> IRGenerator.generateArgCopy(llvm::Value originalPtr, const QualType& valueType, const model::Function* copyCtor) {
6097 46 const llvm::Value copyPtr = this.insertAlloca(valueType.toLLVMType(this.sourceFile), String("arg.copy"));
6098 46 Vector<llvm::Value> ctorArgs;
6099 46 ctorArgs.pushBack(originalPtr);
6100 46 this.generateCtorOrDtorCall(copyPtr, copyCtor, ctorArgs);
6101 92 return copyPtr;
6102 46 }
6103
6104 /**
6105 * Produce the argument value for a by-value param, that owns its argument (see QualType.isOwnedByValueParam), for calls, that
6106 * do not resolve the copy ctor in advance, like calls of operator functions or ctor calls via __new and __placement_new.
6107 *
6108 * @param originalPtr Address of the argument
6109 * @param paramType Type of the param
6110 * @return Value of the copy, which the called function destructs
6111 */
6112 3 public f<llvm::Value> IRGenerator.generateOwnedArgValue(llvm::Value originalPtr, const QualType& paramType) {
6113 3 const QualType valueType = paramType.toNonConst();
6114 3 ArgList args;
6115 3 args.pushBack(Pair<QualType, bool>(valueType.toConstRef(nil<ASTNode*>), false));
6116 3 const model::Function* copyCtor = lookupFunction(valueType.getBodyScope(), String(CTOR_FUNCTION_NAME), valueType, args, true);
6117 3 assert copyCtor != nil<const model::Function*>; // Resolved by the type checker
6118 3 const llvm::Value copyPtr = this.generateArgCopy(originalPtr, valueType, copyCtor);
6119 6 return this.insertLoad(valueType.toLLVMType(this.sourceFile), copyPtr);
6120 3 }
6121
6122 691 p IRGenerator.generateDeallocCall(llvm::Value variableAddress) {
6123 // Abort if the address is not set. This can happen when leaving the scope of a dtor, which already freed the heap memory
6124
2/2
✓ Branch 0 taken 367 times.
✓ Branch 1 taken 324 times.
691 if variableAddress.isNull() {
6125 367 return;
6126 }
6127
6128 // In case of string runtime, call free manually. Otherwise, use the memory_rt implementation of sDealloc()
6129 324 Vector<llvm::Value> args;
6130 324 args.pushBack(variableAddress);
6131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 324 times.
324 if this.sourceFile.isStringRT() {
6132 ✗ const llvm::Function freeFct = this.stdFunctionManager.getFreeFct();
6133 ✗ this.builder.createCall(freeFct, args);
6134 ✗ } else {
6135 324 const llvm::Function deallocFct = this.stdFunctionManager.getDeallocBytePtrRefFct();
6136 324 this.builder.createCall(deallocFct, args);
6137 324 }
6138 324 }
6139
6140 /**
6141 * Prologue of an implicit function or procedure. The body is generated between this prologue and endImplicitFunction() at the
6142 * call site.
6143 *
6144 * @param spiceFunc Spice function or procedure
6145 * @param isFunction Function or procedure
6146 * @return LLVM function or nil, if the function is unused and therefore not generated
6147 */
6148 1444 f<llvm::Function> IRGenerator.beginImplicitFunction(const model::Function* spiceFunc, bool isFunction) {
6149 // Only focus on method procedures
6150 1444 const ASTNode* node = spiceFunc.entry.declNode;
6151
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1435 times.
1444 if isFunction {
6152 9 assert spiceFunc.isFunction();
6153 9 } else {
6154 1435 assert node != nil<const ASTNode*>;
6155 1435 assert spiceFunc.isProcedure();
6156 1435 }
6157
6158 // Only generate if used
6159
2/2
✓ Branch 0 taken 714 times.
✓ Branch 1 taken 730 times.
1444 if !spiceFunc.used {
6160 714 return llvm::Function();
6161 }
6162
6163 // Retrieve return type
6164 730 llvm::Type returnType = this.builder.getVoidTy();
6165
2/2
✓ Branch 0 taken 721 times.
✓ Branch 1 taken 9 times.
730 if isFunction {
6166 9 returnType = spiceFunc.returnType.toLLVMType(this.sourceFile);
6167 9 }
6168
6169 // Get 'this' entry
6170 730 Vector<llvm::Type> paramTypes;
6171 730 const SymbolTableEntry* thisEntry = nil<const SymbolTableEntry*>;
6172
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 721 times.
730 if spiceFunc.isMethod() {
6173 721 thisEntry = spiceFunc.bodyScope.lookupStrict(String(THIS_VARIABLE_NAME));
6174 721 assert thisEntry != nil<const SymbolTableEntry*>;
6175 721 paramTypes.pushBack(this.builder.getPtrTy());
6176 721 }
6177
6178 // Get parameter types
6179
2/2
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 730 times.
872 foreach const model::Param& param : spiceFunc.paramList {
6180 142 assert !param.isOptional;
6181 142 paramTypes.pushBack(param.qualType.toLLVMType(this.sourceFile));
6182 142 }
6183
6184 // Get function linkage
6185 730 const QualType& entryType = spiceFunc.entry.getQualType();
6186 730 const bool isPublic = entryType.isPublic();
6187
6188 // Create function or implement a forward declaration that an earlier call site already inserted for this mangled
6189 // name (e.g. another struct's ctor preamble calling this default member before it got its own definition here).
6190 // Unconditionally calling llvm::Function::Create would not find that declaration - LLVM auto-renames the new,
6191 // colliding GlobalValue instead of merging with it, leaving the original declaration (and every call to it)
6192 // permanently undefined.
6193 730 const String mangledName = spiceFunc.getMangledName();
6194 730 const llvm::Type fctType = llvm::getFunctionType(returnType, paramTypes, false);
6195 730 this.module.getOrInsertFunction(mangledName.getRaw(), fctType);
6196 730 llvm::Function fct = this.module.getFunction(mangledName.getRaw());
6197 730 assert fct.isEmpty();
6198 730 fct.setLinkage(this.getSymbolLinkageType(isPublic));
6199 730 fct.addFnAttr("mustprogress");
6200 730 this.addCommonFctAttrs(fct);
6201
6202 // Set attributes to 'this' param
6203
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 721 times.
730 if spiceFunc.isMethod() {
6204 721 fct.addParamAttr(0, "noundef");
6205 721 fct.addParamAttr(0, "nonnull");
6206 721 assert thisEntry != nil<const SymbolTableEntry*>;
6207 721 const QualType& thisType = thisEntry.getQualType();
6208 721 const QualType structSType = thisType.getContained();
6209 721 const llvm::Type structType = structSType.toLLVMType(this.sourceFile);
6210 721 assert !structType.isNull();
6211 721 fct.addDereferenceableParamAttr(0, this.module.getTypeStoreSize(structType));
6212 721 fct.addParamAttr(0, "align", cast<unsigned long>(this.module.getABITypeAlign(structType)));
6213 721 }
6214
6215 // Add debug info
6216 730 this.diGenerator.generateFunctionDebugInfo(fct, spiceFunc);
6217
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 721 times.
730 if node != nil<const ASTNode*> {
6218 721 this.diGenerator.setSourceLocation(node);
6219 721 }
6220
6221 // Change to body scope
6222 730 this.compilerPass.changeToScope(spiceFunc.getScopeName(), ScopeType::FUNC_PROC_BODY);
6223
6224 // Create entry block
6225 730 const llvm::BasicBlock bEntry = this.createBlock();
6226 730 this.switchToBlock(bEntry, fct);
6227
6228 // Reset alloca insert markers to this block
6229 730 this.allocaInsertBlock = bEntry;
6230 730 this.allocaInsertInst = llvm::Value();
6231
6232 // Store first argument to 'this' symbol
6233
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 721 times.
730 if spiceFunc.isMethod() {
6234 721 assert thisEntry != nil<const SymbolTableEntry*>;
6235 // Allocate space for the parameter
6236 721 const llvm::Value thisAddress = this.insertAlloca(paramTypes.front(), String(THIS_VARIABLE_NAME));
6237 // Update the symbol table entry
6238 721 this.updateAddress(thisEntry, thisAddress);
6239 // Generate debug info
6240 721 this.diGenerator.generateLocalVarDebugInfo(String(THIS_VARIABLE_NAME), thisAddress, 1l);
6241 // Store the value at the new address
6242 721 this.insertStore(fct.getArg(0), thisAddress);
6243 721 }
6244
6245 1460 return fct;
6246 730 }
6247
6248 /**
6249 * Epilogue of an implicit function or procedure, see beginImplicitFunction()
6250 *
6251 * @param spiceFunc Spice function or procedure
6252 * @param fct LLVM function
6253 * @param isFunction Function or procedure
6254 */
6255 730 p IRGenerator.endImplicitFunction(const model::Function* spiceFunc, llvm::Function fct, bool isFunction) {
6256 // Create return instruction
6257
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 721 times.
730 if !isFunction {
6258 721 this.builder.createRetVoid();
6259 721 }
6260
6261 // Conclude debug info for function
6262 730 this.diGenerator.concludeFunctionDebugInfo();
6263
6264 // Verify function
6265 // Use the code location of the declaration node if available. Otherwise, (e.g. in case of test main) use an artificial code loc
6266 730 const ASTNode* node = spiceFunc.entry.declNode;
6267
2/2
✓ Branch 0 taken 721 times.
✓ Branch 1 taken 9 times.
730 const CodeLoc codeLoc = node != nil<const ASTNode*> ? node.codeLoc : CodeLoc(1l, 1l, this.sourceFile.filePath.toString());
6268 730 this.verifyFunction(fct, codeLoc);
6269
6270 // Change to parent scope
6271 730 this.compilerPass.changeToParentScope(ScopeType::FUNC_PROC_BODY);
6272 730 }
6273
6274 5495 p IRGenerator.generateCtorBodyPreamble(Scope* bodyScope) {
6275 // Retrieve struct scope
6276 5495 Scope* structScope = bodyScope.parent;
6277 5495 assert structScope != nil<Scope*>;
6278
6279 // Get struct address
6280 5495 const SymbolTableEntry* thisEntry = bodyScope.lookupStrict(String(THIS_VARIABLE_NAME));
6281 5495 assert thisEntry != nil<const SymbolTableEntry*>;
6282 5495 const llvm::Value thisPtrPtr = this.getAddress(thisEntry);
6283 5495 assert !thisPtrPtr.isNull();
6284 5495 llvm::Value thisPtr;
6285 5495 const QualType& thisType = thisEntry.getQualType();
6286 5495 const QualType structSymbolType = thisType.getBase();
6287 5495 const llvm::Type structType = structSymbolType.toLLVMType(this.sourceFile);
6288
6289 // Store VTable to first struct field if required
6290 5495 const Struct* spiceStruct = structSymbolType.getStruct(nil<ASTNode*>);
6291 5495 assert spiceStruct != nil<const Struct*>;
6292
2/2
✓ Branch 0 taken 4325 times.
✓ Branch 1 taken 1170 times.
5495 if !spiceStruct.vTableData.vtable.isNull() {
6293 1170 assert !spiceStruct.vTableData.vtableType.isNull();
6294 // Store VTable to field address at index 0
6295 1170 thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr);
6296 1170 Vector<llvm::Value> indices;
6297 1170 indices.pushBack(this.getInt64(0l));
6298 1170 indices.pushBack(this.getInt32(0l));
6299 1170 indices.pushBack(this.getInt32(2l));
6300 1170 const llvm::Value gepResult = this.insertInBoundsGEP(spiceStruct.vTableData.vtableType, spiceStruct.vTableData.vtable, indices);
6301 1170 this.insertStore(gepResult, thisPtr);
6302 1170 }
6303
6304 5495 const unsigned long fieldCount = structScope.getFieldCount();
6305 5495 const Vector<llvm::Value> noArgs;
6306
2/2
✓ Branch 0 taken 14599 times.
✓ Branch 1 taken 5495 times.
20094 for unsigned long fieldIdx = 0l; fieldIdx < fieldCount; fieldIdx++ {
6307 14599 const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(fieldIdx));
6308 14599 assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.isField();
6309
2/2
✓ Branch 0 taken 1004 times.
✓ Branch 1 taken 13595 times.
14599 if fieldSymbol.isImplicitField {
6310 1004 continue;
6311 }
6312
6313 // Call ctor for struct fields
6314 13595 const QualType& fieldType = fieldSymbol.getQualType();
6315 13595 const ASTFieldNode* fieldNode = downcast<ASTFieldNode>(fieldSymbol.declNode);
6316
2/2
✓ Branch 0 taken 2278 times.
✓ Branch 1 taken 11317 times.
13595 if fieldType.is(SuperType::TY_STRUCT) {
6317 // Lookup ctor function and call if available
6318 2278 Scope* matchScope = fieldType.getBodyScope();
6319 2278 const ArgList noCtorArgs;
6320 2278 const model::Function* ctorFunction = lookupFunction(matchScope, String(CTOR_FUNCTION_NAME), fieldType, noCtorArgs, false);
6321
2/2
✓ Branch 0 taken 178 times.
✓ Branch 1 taken 2100 times.
2278 if ctorFunction != nil<const model::Function*> {
6322
2/2
✓ Branch 0 taken 1677 times.
✓ Branch 1 taken 423 times.
2100 if thisPtr.isNull() {
6323 423 thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr);
6324 423 }
6325 2100 const llvm::Value fieldAddress = this.insertStructGEP(structType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx)));
6326 2100 this.generateCtorOrDtorCall(fieldAddress, ctorFunction, noArgs);
6327 2100 }
6328 2278 continue;
6329 2278 }
6330
6331 // Store default field values
6332
3/4
✓ Branch 0 taken 6057 times.
✓ Branch 1 taken 5260 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11317 times.
11317 if fieldNode.defaultValue != nil<IVisitable*> || this.cliOptions.buildMode != BuildMode::RELEASE {
6333 // Retrieve field address
6334
2/2
✓ Branch 0 taken 7468 times.
✓ Branch 1 taken 3849 times.
11317 if thisPtr.isNull() {
6335 3849 thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr);
6336 3849 }
6337 11317 const llvm::Value fieldAddress = this.insertStructGEP(structType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx)));
6338 // Retrieve default value
6339 11317 llvm::Value value;
6340
2/2
✓ Branch 0 taken 6057 times.
✓ Branch 1 taken 5260 times.
11317 if fieldNode.defaultValue != nil<IVisitable*> {
6341 // To resolve the default value, we need to temporarily change to the manifestation of the current struct instantiation
6342 6057 const unsigned long oldManIdx = this.manIdx; // Save manifestation index
6343 6057 this.manIdx = spiceStruct.manifestationIndex;
6344 6057 value = this.resolveValue(fieldNode.defaultValue);
6345 6057 this.manIdx = oldManIdx; // Restore manifestation index
6346 6057 } else {
6347 5260 assert this.cliOptions.buildMode == BuildMode::DEBUG || this.cliOptions.buildMode == BuildMode::TEST;
6348 5260 value = this.getDefaultValueForSymbolType(fieldType);
6349 5260 }
6350 // Store default value
6351 11317 this.insertStore(value, fieldAddress);
6352 11317 }
6353 11317 }
6354 5495 }
6355
6356 161 p IRGenerator.generateDefaultCtor(const model::Function* ctorFunction) {
6357 161 assert ctorFunction.implicitDefault && ctorFunction.name == CTOR_FUNCTION_NAME;
6358 161 const llvm::Function fct = this.beginImplicitFunction(ctorFunction, false);
6359
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 123 times.
161 if fct.isNull() {
6360 38 return;
6361 }
6362 123 this.generateCtorBodyPreamble(ctorFunction.bodyScope);
6363 123 this.endImplicitFunction(ctorFunction, fct, false);
6364 123 }
6365
6366 133 p IRGenerator.generateCopyCtorBodyPreamble(const model::Function* copyCtorFunction) {
6367 // Retrieve struct scope
6368 133 Scope* structScope = copyCtorFunction.bodyScope.parent;
6369 133 assert structScope != nil<Scope*>;
6370
6371 // Get struct address
6372 133 const SymbolTableEntry* thisEntry = copyCtorFunction.bodyScope.lookupStrict(String(THIS_VARIABLE_NAME));
6373 133 assert thisEntry != nil<const SymbolTableEntry*>;
6374 133 const llvm::Value thisPtrPtr = this.getAddress(thisEntry);
6375 133 assert !thisPtrPtr.isNull();
6376 133 llvm::Value thisPtr;
6377 133 const QualType& thisType = thisEntry.getQualType();
6378 133 const QualType structSType = thisType.getBase();
6379 133 const llvm::Type structType = structSType.toLLVMType(this.sourceFile);
6380 133 const Struct* spiceStruct = structSType.getStruct(nil<ASTNode*>);
6381 133 assert spiceStruct != nil<const Struct*>;
6382
6383 // Retrieve the value of the original struct, which is the only function parameter
6384 133 const llvm::BasicBlock insertBlock = this.builder.getInsertBlock();
6385 133 const llvm::Function parentFct = insertBlock.getParent();
6386 133 const llvm::Value originalThisPtr = parentFct.getArg(1);
6387
6388 // The synthesized vtable pointer is no field, so take it over separately
6389
2/2
✓ Branch 0 taken 131 times.
✓ Branch 1 taken 2 times.
133 if spiceStruct.hasSynthesizedVTablePtr() {
6390 2 thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr);
6391 2 const llvm::Value vtablePtr = this.insertLoad(this.builder.getPtrTy(), originalThisPtr);
6392 2 this.insertStore(vtablePtr, thisPtr);
6393 2 }
6394
6395 133 const unsigned long fieldCount = structScope.getFieldCount();
6396
2/2
✓ Branch 0 taken 616 times.
✓ Branch 1 taken 133 times.
749 for unsigned long fieldIdx = 0l; fieldIdx < fieldCount; fieldIdx++ {
6397 616 const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(fieldIdx));
6398 616 assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.isField();
6399
6400 // Retrieve the address of the original field (copy source)
6401 616 const llvm::Value originalFieldAddress = this.insertStructGEP(structType, originalThisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx)));
6402
6403 616 const QualType& fieldType = fieldSymbol.getQualType();
6404
6405 // Call copy ctor for struct fields
6406
4/4
✓ Branch 0 taken 220 times.
✓ Branch 1 taken 396 times.
✓ Branch 2 taken 393 times.
✓ Branch 3 taken 223 times.
616 if fieldType.is(SuperType::TY_STRUCT) && !fieldType.isTriviallyCopyable(nil<ASTNode*>) {
6407 // Lookup copy ctor function and call if available
6408 393 Scope* matchScope = fieldType.getBodyScope();
6409 393 ArgList args;
6410 393 args.pushBack(Pair<QualType, bool>(fieldType.toConstRef(nil<ASTNode*>), false /* we have the field as storage */));
6411 393 const model::Function* copyCtor = lookupFunction(matchScope, String(CTOR_FUNCTION_NAME), fieldType, args, false);
6412 393 assert copyCtor != nil<const model::Function*>;
6413 393 Vector<llvm::Value> ctorArgs;
6414 393 ctorArgs.pushBack(originalFieldAddress);
6415 393 this.generateCtorOrDtorCall(fieldSymbol, copyCtor, ctorArgs);
6416 393 continue;
6417 393 }
6418
6419 // Retrieve the address of the new field (copy dest)
6420
2/2
✓ Branch 0 taken 125 times.
✓ Branch 1 taken 98 times.
223 if thisPtr.isNull() {
6421 98 thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr);
6422 98 }
6423 223 const llvm::Value fieldAddress = this.insertStructGEP(structType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx)));
6424
6425 // For owning heap fields, copy the underlying heap storage
6426
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 217 times.
223 if fieldType.isHeap() {
6427 6 assert fieldType.isPtr();
6428 6 const QualType pointeeSType = fieldType.getContained();
6429 6 const llvm::Type pointeeType = pointeeSType.toLLVMType(this.sourceFile);
6430
6431 // Retrieve original heap address
6432 6 const llvm::Value originalHeapAddress = this.insertLoad(this.builder.getPtrTy(), originalFieldAddress);
6433
6434 // Initialize the new field with nullptr, so that it stays valid if the original heap address is nullptr
6435 6 this.insertStore(llvm::getNullValue(this.builder.getPtrTy()), fieldAddress);
6436
6437 // Insert check for nullptr
6438 6 const llvm::BasicBlock bThen = this.createBlock(String("nullptrcheck.then"));
6439 6 const llvm::BasicBlock bExit = this.createBlock(String("nullptrcheck.exit"));
6440 6 const llvm::Value condValue = this.builder.createICmp(llvm::LLVMIntPredicate::NE, originalHeapAddress, llvm::getNullValue(this.builder.getPtrTy()));
6441 6 this.insertCondJump(condValue, bThen, bExit);
6442
6443 // Fill then block
6444 6 this.switchToBlock(bThen);
6445
6446 // Allocate new space on the heap
6447 6 const llvm::Function unsafeAllocFct = this.stdFunctionManager.getAllocUnsafeLongFct();
6448 6 const unsigned long typeSizeInBytes = this.module.getTypeSizeInBits(pointeeType) / 8l;
6449 6 Vector<llvm::Value> allocArgs;
6450 6 allocArgs.pushBack(this.builder.getInt64(typeSizeInBytes));
6451 6 const llvm::Value newHeapAddress = this.builder.createCall(unsafeAllocFct, allocArgs);
6452 6 this.insertStore(newHeapAddress, fieldAddress);
6453
6454 // Copy data from the old heap storage to the new one
6455 6 this.generateShallowCopy(originalHeapAddress, pointeeType, newHeapAddress, false);
6456 6 this.insertJump(bExit);
6457
6458 // Switch to exit block
6459 6 this.switchToBlock(bExit);
6460
6461 6 continue;
6462 6 }
6463
6464 // Shallow copy
6465 217 const llvm::Type fieldLLVMType = fieldType.toLLVMType(this.sourceFile);
6466 217 this.generateShallowCopy(originalFieldAddress, fieldLLVMType, fieldAddress, false);
6467 217 }
6468 133 }
6469
6470 700 p IRGenerator.generateDefaultCopyCtor(const model::Function* copyCtorFunction) {
6471 700 assert copyCtorFunction.implicitDefault && copyCtorFunction.name == CTOR_FUNCTION_NAME;
6472 700 const llvm::Function fct = this.beginImplicitFunction(copyCtorFunction, false);
6473
2/2
✓ Branch 0 taken 567 times.
✓ Branch 1 taken 133 times.
700 if fct.isNull() {
6474 567 return;
6475 }
6476 133 this.generateCopyCtorBodyPreamble(copyCtorFunction);
6477 133 this.endImplicitFunction(copyCtorFunction, fct, false);
6478 133 }
6479
6480 9 p IRGenerator.generateMoveCtorBodyPreamble(const model::Function* moveCtorFunction) {
6481 // Retrieve struct scope
6482 9 Scope* structScope = moveCtorFunction.bodyScope.parent;
6483 9 assert structScope != nil<Scope*>;
6484
6485 // Get struct address
6486 9 const SymbolTableEntry* thisEntry = moveCtorFunction.bodyScope.lookupStrict(String(THIS_VARIABLE_NAME));
6487 9 assert thisEntry != nil<const SymbolTableEntry*>;
6488 9 const llvm::Value thisPtrPtr = this.getAddress(thisEntry);
6489 9 assert !thisPtrPtr.isNull();
6490 9 llvm::Value thisPtr;
6491 9 const QualType& thisType = thisEntry.getQualType();
6492 9 const QualType structSType = thisType.getBase();
6493 9 const llvm::Type structType = structSType.toLLVMType(this.sourceFile);
6494 9 const Struct* spiceStruct = structSType.getStruct(nil<ASTNode*>);
6495 9 assert spiceStruct != nil<const Struct*>;
6496
6497 // Retrieve the value of the original (source) struct, which is the only function parameter
6498 9 const llvm::BasicBlock insertBlock = this.builder.getInsertBlock();
6499 9 const llvm::Function parentFct = insertBlock.getParent();
6500 9 const llvm::Value originalThisPtr = parentFct.getArg(1);
6501
6502 // The synthesized vtable pointer is no field, so take it over separately
6503
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
9 if spiceStruct.hasSynthesizedVTablePtr() {
6504 2 thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr);
6505 2 const llvm::Value vtablePtr = this.insertLoad(this.builder.getPtrTy(), originalThisPtr);
6506 2 this.insertStore(vtablePtr, thisPtr);
6507 2 }
6508
6509 9 const unsigned long fieldCount = structScope.getFieldCount();
6510
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 9 times.
25 for unsigned long fieldIdx = 0l; fieldIdx < fieldCount; fieldIdx++ {
6511 16 const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(fieldIdx));
6512 16 assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.isField();
6513
6514 // Retrieve the address of the original field (move source)
6515 16 const llvm::Value originalFieldAddress = this.insertStructGEP(structType, originalThisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx)));
6516
6517 16 const QualType& fieldType = fieldSymbol.getQualType();
6518
6519 // Call move ctor for struct fields if available, otherwise fall back to copy or shallow copy
6520
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 3 times.
16 if fieldType.is(SuperType::TY_STRUCT) {
6521 3 Scope* matchScope = fieldType.getBodyScope();
6522 // First try to find a move ctor (non-const ref param). We scan the manifestations directly via
6523 // findMoveCtor rather than FunctionManager::lookup with a non-const ref arg, because lookup permits
6524 // const-param-to-non-const-arg "constify" matching and may return the copy ctor as a false positive.
6525 3 const model::Function* moveCtor = findMoveCtor(matchScope);
6526
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if moveCtor != nil<const model::Function*> {
6527 3 Vector<llvm::Value> ctorArgs;
6528 3 ctorArgs.pushBack(originalFieldAddress);
6529 3 this.generateCtorOrDtorCall(fieldSymbol, moveCtor, ctorArgs);
6530 3 continue;
6531 3 }
6532 // No move ctor: fall back to copy ctor for non-trivially copyable types
6533
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if !fieldType.isTriviallyCopyable(nil<ASTNode*>) {
6534 ✗ ArgList copyArgs;
6535 ✗ copyArgs.pushBack(Pair<QualType, bool>(fieldType.toConstRef(nil<ASTNode*>), false));
6536 ✗ const model::Function* copyCtor = lookupFunction(matchScope, String(CTOR_FUNCTION_NAME), fieldType, copyArgs, false);
6537 ✗ assert copyCtor != nil<const model::Function*>;
6538 ✗ Vector<llvm::Value> ctorArgs;
6539 ✗ ctorArgs.pushBack(originalFieldAddress);
6540 ✗ this.generateCtorOrDtorCall(fieldSymbol, copyCtor, ctorArgs);
6541 ✗ continue;
6542 ✗ }
6543 ✗ }
6544
6545 // Retrieve the address of the new field (move dest)
6546
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 5 times.
13 if thisPtr.isNull() {
6547 5 thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr);
6548 5 }
6549 13 const llvm::Value fieldAddress = this.insertStructGEP(structType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx)));
6550
6551 // For owning heap fields, transfer ownership: copy the pointer to the destination, and null out the source
6552
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 7 times.
13 if fieldType.isHeap() {
6553 6 assert fieldType.isPtr();
6554
6555 // Load original heap address
6556 6 const llvm::Value originalHeapAddress = this.insertLoad(this.builder.getPtrTy(), originalFieldAddress);
6557 // Store it in the destination field
6558 6 this.insertStore(originalHeapAddress, fieldAddress);
6559 // Null out the source field so its dtor does not free the storage
6560 6 this.insertStore(llvm::getNullValue(this.builder.getPtrTy()), originalFieldAddress);
6561
6562 6 continue;
6563 }
6564
6565 // Shallow copy non-heap, non-struct (or trivially copyable struct) fields
6566 7 const llvm::Type fieldLLVMType = fieldType.toLLVMType(this.sourceFile);
6567 7 this.generateShallowCopy(originalFieldAddress, fieldLLVMType, fieldAddress, false);
6568 7 }
6569 9 }
6570
6571 21 p IRGenerator.generateDefaultMoveCtor(const model::Function* moveCtorFunction) {
6572 21 assert moveCtorFunction.implicitDefault && moveCtorFunction.name == CTOR_FUNCTION_NAME;
6573 21 const llvm::Function fct = this.beginImplicitFunction(moveCtorFunction, false);
6574
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 9 times.
21 if fct.isNull() {
6575 12 return;
6576 }
6577 9 this.generateMoveCtorBodyPreamble(moveCtorFunction);
6578 9 this.endImplicitFunction(moveCtorFunction, fct, false);
6579 9 }
6580
6581 456 p IRGenerator.generateDtorBodyPreamble(const model::Function* dtorFunction) {
6582 // Retrieve struct scope
6583 456 Scope* structScope = dtorFunction.bodyScope.parent;
6584 456 assert structScope != nil<Scope*>;
6585
6586 // Get struct address
6587 456 const SymbolTableEntry* thisEntry = dtorFunction.bodyScope.lookupStrict(String(THIS_VARIABLE_NAME));
6588 456 assert thisEntry != nil<const SymbolTableEntry*>;
6589 456 const llvm::Value thisPtrPtr = this.getAddress(thisEntry);
6590 456 assert !thisPtrPtr.isNull();
6591 456 llvm::Value thisPtr;
6592 456 const QualType& thisType = thisEntry.getQualType();
6593 456 const QualType structSType = thisType.getBase();
6594 456 const llvm::Type structType = structSType.toLLVMType(this.sourceFile);
6595 456 const Struct* spiceStruct = structSType.getStruct(nil<ASTNode*>);
6596 456 assert spiceStruct != nil<const Struct*>;
6597
6598 456 const unsigned long fieldCount = structScope.getFieldCount();
6599 456 const Vector<llvm::Value> noArgs;
6600
2/2
✓ Branch 0 taken 1410 times.
✓ Branch 1 taken 456 times.
1866 for unsigned long i = 0l; i < fieldCount; i++ {
6601 1410 const unsigned long fieldIdx = fieldCount - 1l - i; // Destruct fields in reverse order
6602 1410 const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(fieldIdx));
6603 1410 assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.isField();
6604
6605 // Call dtor for struct fields
6606 1410 const QualType& fieldType = fieldSymbol.getQualType();
6607
2/2
✓ Branch 0 taken 756 times.
✓ Branch 1 taken 654 times.
1410 if fieldType.is(SuperType::TY_STRUCT) {
6608 // Lookup dtor function and generate call if found
6609 756 const ArgList noDtorArgs;
6610 756 const model::Function* dtorFct = lookupFunction(fieldType.getBodyScope(), String(DTOR_FUNCTION_NAME), fieldType, noDtorArgs, false);
6611
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 609 times.
756 if dtorFct != nil<const model::Function*> {
6612 609 this.generateCtorOrDtorCall(fieldSymbol, dtorFct, noArgs);
6613 609 }
6614 756 continue;
6615 756 }
6616
6617 // Deallocate fields, that are stored on the heap
6618
2/2
✓ Branch 0 taken 453 times.
✓ Branch 1 taken 201 times.
654 if fieldType.isHeap() {
6619 // Retrieve field address
6620
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 178 times.
201 if thisPtr.isNull() {
6621 178 thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr);
6622 178 }
6623 201 const llvm::Value fieldAddress = this.insertStructGEP(structType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx)));
6624 // Call dealloc function
6625 201 this.generateDeallocCall(fieldAddress);
6626 201 }
6627 654 }
6628 456 }
6629
6630 553 p IRGenerator.generateDefaultDtor(const model::Function* dtorFunction) {
6631 553 assert dtorFunction.implicitDefault && dtorFunction.name == DTOR_FUNCTION_NAME;
6632 553 const llvm::Function fct = this.beginImplicitFunction(dtorFunction, false);
6633
2/2
✓ Branch 0 taken 97 times.
✓ Branch 1 taken 456 times.
553 if fct.isNull() {
6634 97 return;
6635 }
6636 456 this.generateDtorBodyPreamble(dtorFunction);
6637 456 this.endImplicitFunction(dtorFunction, fct, false);
6638 456 }
6639
6640 9 p IRGenerator.generateTestMain() {
6641 // Collect all test functions. Iterate the source files in creation order, so that the tests of the main source file run
6642 // first, followed by the ones of its dependencies in import order. The order of the source file map depends on the hash
6643 // function
6644 9 Vector<const Vector<const model::Function*>*> tests;
6645
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 9 times.
20 foreach const SourceFile* testSourceFile : this.resourceManager.sourceFilesInCreationOrder {
6646
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if !testSourceFile.testFunctions.isEmpty() {
6647 11 tests.pushBack(&testSourceFile.testFunctions);
6648 11 }
6649 11 }
6650
6651 // Prepare printf function
6652 9 const llvm::Function printfFct = this.stdFunctionManager.getPrintfFct();
6653
6654 // Prepare success and error messages
6655 9 const CodeLoc& rootCodeLoc = *this.rootScope.codeLoc;
6656 9 const llvm::Value allStartMsg = this.createGlobalStringConst(String("allStartMsg"), String(TEST_ALL_START_MSG), rootCodeLoc);
6657 9 const llvm::Value allEndMsg = this.createGlobalStringConst(String("allEndMsg"), String(TEST_ALL_END_MSG), rootCodeLoc);
6658 9 const llvm::Value fileStartMsg = this.createGlobalStringConst(String("fileStartMsg"), String(TEST_FILE_START_MSG), rootCodeLoc);
6659 9 const llvm::Value fileEndMsg = this.createGlobalStringConst(String("fileEndMsg"), String(TEST_FILE_END_MSG), rootCodeLoc);
6660 9 const llvm::Value runMsg = this.createGlobalStringConst(String("runMsg"), String(TEST_CASE_RUN_MSG), rootCodeLoc);
6661 9 const llvm::Value successMsg = this.createGlobalStringConst(String("successMsg"), String(TEST_CASE_SUCCESS_MSG), rootCodeLoc);
6662 9 const llvm::Value errorMsg = this.createGlobalStringConst(String("errorMsg"), String(TEST_CASE_FAILED_MSG), rootCodeLoc);
6663 9 const llvm::Value skippedMsg = this.createGlobalStringConst(String("skippedMsg"), String(TEST_CASE_SKIPPED_MSG), rootCodeLoc);
6664
6665 // Prepare entry for test main
6666 9 QualType functionType = QualType(SuperType::TY_FUNCTION);
6667 9 functionType.setQualifiers(getTypeQualifiersOf(SuperType::TY_FUNCTION));
6668 9 functionType.makePublic();
6669 9 SymbolTableEntry entry = SymbolTableEntry(String(MAIN_FUNCTION_NAME), functionType, this.rootScope, nil<ASTNode*>, 0l, false);
6670
6671 // Prepare test main function
6672 9 const model::ParamList noParams;
6673 9 const Vector<GenericType> noTemplateTypes;
6674 9 model::Function testMain = model::Function(String(MAIN_FUNCTION_NAME), &entry, QualType(SuperType::TY_DYN), QualType(SuperType::TY_INT), noParams, noTemplateTypes, nil<ASTNode*>);
6675 9 testMain.used = true; // Mark as used to prevent removal
6676 9 testMain.implicitDefault = true;
6677 9 testMain.mangleFunctionName = false;
6678
6679 // Prepare scope
6680 9 this.rootScope.createChildScope(testMain.getScopeName(), ScopeType::FUNC_PROC_BODY, nil<CodeLoc*>);
6681
6682 // Generate
6683 9 const llvm::Function fct = this.beginImplicitFunction(&testMain, true);
6684
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if fct.isNull() {
6685 ✗ return;
6686 }
6687
6688 // Prepare result variable
6689 9 Vector<llvm::Value> testCaseResults;
6690 9 testCaseResults.reserve(tests.getSize());
6691
6692 // Print start message
6693 9 unsigned long totalTestCount = 0l;
6694
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 9 times.
20 foreach const Vector<const model::Function*>* innerVector : tests {
6695 11 totalTestCount += innerVector.getSize();
6696 11 }
6697 9 Vector<llvm::Value> allStartArgs;
6698 9 allStartArgs.pushBack(allStartMsg);
6699 9 allStartArgs.pushBack(this.getInt32(cast<long>(totalTestCount)));
6700 9 allStartArgs.pushBack(this.getInt32(tests.getSize()));
6701 9 this.builder.createCall(printfFct, allStartArgs);
6702
6703 // Generate a call to each test function
6704
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 11 times.
20 foreach const Vector<const model::Function*>* testSuite : tests {
6705 // Print test suite prologue
6706 11 const model::Function* firstTestFunction = testSuite.front();
6707 11 const String& fileName = firstTestFunction.bodyScope.sourceFile.fileName;
6708 11 const llvm::Value fileNameValue = this.createGlobalStringConst(String("fileName"), fileName, firstTestFunction.getDeclCodeLoc());
6709 11 Vector<llvm::Value> fileStartArgs;
6710 11 fileStartArgs.pushBack(fileStartMsg);
6711 11 fileStartArgs.pushBack(this.getInt32(testSuite.getSize()));
6712 11 fileStartArgs.pushBack(fileNameValue);
6713 11 this.builder.createCall(printfFct, fileStartArgs);
6714
6715
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 11 times.
32 foreach const model::Function* testFunction : *testSuite {
6716 21 assert testFunction.isNormalFunction();
6717 21 assert testFunction.paramList.isEmpty();
6718
6719 // Retrieve attribute list for the test function
6720 21 assert testFunction.declNode.isFctOrProcDef();
6721 21 const ASTFctDefNode* fctDefNode = downcast<ASTFctDefNode>(testFunction.declNode);
6722 21 assert fctDefNode.attrs != nil<IVisitable*>;
6723 21 const ASTTopLevelDefAttrNode* topLevelDefAttrs = downcast<ASTTopLevelDefAttrNode>(fctDefNode.attrs);
6724 21 const ASTAttrLstNode* attrs = topLevelDefAttrs.attrLst;
6725 21 const CompileTimeValue testAttrValue = attrs.getAttrValueByName(String(ATTR_TEST));
6726 21 assert testAttrValue.getBoolValue(); // The test attribute must be present
6727 21 bool skipTest = false;
6728
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 6 times.
21 if attrs.hasAttr(String(ATTR_TEST_SKIP)) {
6729 6 const CompileTimeValue testSkipAttrValue = attrs.getAttrValueByName(String(ATTR_TEST_SKIP));
6730 6 skipTest = testSkipAttrValue.getBoolValue();
6731 6 }
6732
6733 // Prepare test name
6734 21 String testName = testFunction.name;
6735
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 2 times.
21 if attrs.hasAttr(String(ATTR_TEST_NAME)) {
6736 2 const CompileTimeValue testNameAttrValue = attrs.getAttrValueByName(String(ATTR_TEST_NAME));
6737 2 const String& testNameAttr = this.resourceManager.compileTimeStringValues.get(testNameAttrValue.getStringValueOffset());
6738 2 testName += " (";
6739 2 testName += testNameAttr;
6740 2 testName += ")";
6741 2 }
6742
6743 // Print test case run message
6744 21 const llvm::Value testNameValue = this.createGlobalStringConst(String("testName"), testName, testFunction.getDeclCodeLoc());
6745 21 Vector<llvm::Value> runArgs;
6746 21 runArgs.pushBack(runMsg);
6747 21 runArgs.pushBack(testNameValue);
6748 21 this.builder.createCall(printfFct, runArgs);
6749
6750
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 19 times.
21 if skipTest {
6751 // Print test case skip message
6752 2 Vector<llvm::Value> skipArgs;
6753 2 skipArgs.pushBack(skippedMsg);
6754 2 skipArgs.pushBack(testNameValue);
6755 2 this.builder.createCall(printfFct, skipArgs);
6756 2 continue;
6757 2 }
6758
6759 // Test function is not defined in the current module -> declare it
6760 19 const String mangledName = testFunction.getMangledName();
6761 19 const llvm::Function existingTestFct = this.module.getFunction(mangledName.getRaw());
6762
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 4 times.
19 if existingTestFct.isNull() {
6763 4 assert testFunction.returnType.is(SuperType::TY_BOOL);
6764 4 assert testFunction.paramList.isEmpty();
6765 4 const Vector<llvm::Type> noParamTypes;
6766 4 const llvm::Type fctType = llvm::getFunctionType(this.builder.getInt1Ty(), noParamTypes, false);
6767 4 this.module.getOrInsertFunction(mangledName.getRaw(), fctType);
6768 4 }
6769
6770 // Call test function
6771 19 const llvm::Function callee = this.module.getFunction(mangledName.getRaw());
6772 19 assert !callee.isNull();
6773 19 const Vector<llvm::Value> noArgs;
6774 19 const llvm::Value testCaseResult = this.builder.createCall(callee, noArgs);
6775 19 testCaseResults.pushBack(testCaseResult);
6776
6777 // Print test case result message
6778 19 const llvm::Value message = this.builder.createSelect(testCaseResult, successMsg, errorMsg);
6779 19 Vector<llvm::Value> resultArgs;
6780 19 resultArgs.pushBack(message);
6781 19 resultArgs.pushBack(testNameValue);
6782 19 this.builder.createCall(printfFct, resultArgs);
6783 21 }
6784
6785 // Print test suite epilogue
6786 11 Vector<llvm::Value> fileEndArgs;
6787 11 fileEndArgs.pushBack(fileEndMsg);
6788 11 fileEndArgs.pushBack(this.getInt32(testSuite.getSize()));
6789 11 fileEndArgs.pushBack(fileNameValue);
6790 11 this.builder.createCall(printfFct, fileEndArgs);
6791 11 }
6792
6793 // Print end message
6794 9 Vector<llvm::Value> allEndArgs;
6795 9 allEndArgs.pushBack(allEndMsg);
6796 9 allEndArgs.pushBack(this.getInt32(cast<long>(totalTestCount)));
6797 9 allEndArgs.pushBack(this.getInt32(tests.getSize()));
6798 9 this.builder.createCall(printfFct, allEndArgs);
6799
6800 // Compute overall result
6801 9 llvm::Value overallResult = this.builder.getTrue();
6802
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 9 times.
28 foreach const llvm::Value& testCaseResult : testCaseResults {
6803 19 overallResult = this.builder.createAnd(overallResult, testCaseResult);
6804 19 }
6805
6806 // Return code must be 0 for success and 1 for failure, so we need to invert the result and zero extend to 32 bit
6807 9 const llvm::Value overallResultNegated = this.builder.createNot(overallResult);
6808 9 const llvm::Value exitCode = this.builder.createZExt(overallResultNegated, this.builder.getInt32Ty());
6809 9 this.builder.createRet(exitCode);
6810
6811 9 this.endImplicitFunction(&testMain, fct, true);
6812 9 }
6813
6814 // ====================================================== Target dependent =======================================================
6815
6816 5 f<String> IRGenerator.getSysCallAsmString(unsigned int numRegs) {
6817 5 const llvm::Triple& targetTriple = this.cliOptions.targetTriple;
6818 5 String asmString;
6819
6820 // For each OS/architecture we have a mapping of operand index -> register.
6821 // Operand $0 is the result, so the inputs start at $1.
6822
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if targetTriple.isOSDarwin() {
6823
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if targetTriple.isX86_64() {
6824 ✗ const string[7] regs = ["%rax", "%rdi", "%rsi", "%rdx", "%r10", "%r8", "%r9"];
6825
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for unsigned int i = 0; i < numRegs; i++ {
6826 ✗ asmString += "movq $" + toString(i + 1) + ", " + regs[i] + "\n";
6827 // macOS x86_64 syscall numbers are offset by 0x2000000 (BSD syscall class). "$$" emits a literal '$'.
6828
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if i == 0 {
6829 ✗ asmString += "addq $$" + toString(33554432) + ", %rax\n";
6830 ✗ }
6831 ✗ }
6832 ✗ asmString += "syscall\n";
6833
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if targetTriple.isAArch64() {
6834 ✗ const string[7] regs = ["x16", "x0", "x1", "x2", "x3", "x4", "x5"];
6835
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for unsigned int i = 0; i < numRegs; i++ {
6836 ✗ asmString += String("mov ") + regs[i] + ", $" + toString(i + 1) + "\n";
6837 ✗ }
6838 ✗ asmString += "svc #0x80\n";
6839 − } else { // LCOV_EXCL_LINE
6840 − panic(Error("Unsupported macOS target for inline assembly")); // LCOV_EXCL_LINE
6841 − } // LCOV_EXCL_LINE
6842
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 } else if targetTriple.isOSLinux() {
6843
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if targetTriple.isX86_64() {
6844 5 const string[7] regs = ["%rax", "%rdi", "%rsi", "%rdx", "%r10", "%r8", "%r9"];
6845
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
27 for unsigned int i = 0; i < numRegs; i++ {
6846 22 asmString += "movq $" + toString(i + 1) + ", " + regs[i] + "\n";
6847 22 }
6848 5 asmString += "syscall\n";
6849
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
5 } else if targetTriple.isX86() {
6850 // The operands are bound to their registers via constraints. The sixth arg belongs into ebp, which may be the frame
6851 // pointer. Therefore, it is passed in eax, the syscall number is passed via memory and ebp is restored afterwards.
6852
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if numRegs == 7 {
6853 ✗ asmString += "pushl $1\npush %ebp\nmov %eax, %ebp\nmov 4(%esp), %eax\nint $$0x80\npop %ebp\nlea 4(%esp), %esp\n";
6854 ✗ } else {
6855 ✗ asmString += "int $$0x80\n";
6856 ✗ }
6857
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if targetTriple.isAArch64() {
6858 ✗ const string[7] regs = ["x8", "x0", "x1", "x2", "x3", "x4", "x5"];
6859
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for unsigned int i = 0; i < numRegs; i++ {
6860 ✗ asmString += String("mov ") + regs[i] + ", $" + toString(i + 1) + "\n";
6861 ✗ }
6862 ✗ asmString += "svc 0\n";
6863 − } else { // LCOV_EXCL_LINE
6864 − panic(Error("Unsupported Linux target for inline assembly")); // LCOV_EXCL_LINE
6865 − } // LCOV_EXCL_LINE
6866 − } else { // LCOV_EXCL_LINE
6867 − panic(Error("Unsupported target for inline assembly")); // LCOV_EXCL_LINE
6868 − } // LCOV_EXCL_LINE
6869
6870 5 return asmString;
6871 }
6872
6873 5 f<String> IRGenerator.getSysCallConstraintString(unsigned int numRegs) {
6874 5 const llvm::Triple& targetTriple = this.cliOptions.targetTriple;
6875
6876 // For each OS/architecture we have a mapping of operand index -> register, plus the register holding the result.
6877 5 Vector<String> regs;
6878 5 String resultReg;
6879
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if targetTriple.isOSDarwin() {
6880
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if targetTriple.isX86_64() {
6881 ✗ const string[7] darwinX86_64Regs = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"];
6882
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for unsigned int i = 0; i < 7; i++ { regs.pushBack(String(darwinX86_64Regs[i])); }
6883 ✗ resultReg = String("rax");
6884
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if targetTriple.isAArch64() {
6885 ✗ const string[7] darwinAArch64Regs = ["x16", "x0", "x1", "x2", "x3", "x4", "x5"];
6886
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for unsigned int i = 0; i < 7; i++ { regs.pushBack(String(darwinAArch64Regs[i])); }
6887 ✗ resultReg = String("x0");
6888 − } else { // LCOV_EXCL_LINE
6889 − panic(Error("Unsupported macOS target for inline assembly")); // LCOV_EXCL_LINE
6890 − } // LCOV_EXCL_LINE
6891
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 } else if targetTriple.isOSLinux() {
6892
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if targetTriple.isX86_64() {
6893 5 const string[7] linuxX86_64Regs = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"];
6894
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 5 times.
40 for unsigned int i = 0; i < 7; i++ { regs.pushBack(String(linuxX86_64Regs[i])); }
6895 5 resultReg = String("rax");
6896
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
5 } else if targetTriple.isX86() {
6897 // i386 has too few registers to pass the operands in arbitrary ones, so each operand gets its fixed register.
6898 // The syscall number is tied to the result in eax. For six args, see getSysCallAsmString.
6899 ✗ const string[5] argRegs = ["ebx", "ecx", "edx", "esi", "edi"];
6900 ✗ String constraints = String("={eax},") + (numRegs == 7 ? "*m" : "0");
6901
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ const unsigned int maxReg = numRegs < 6 ? numRegs : 6;
6902
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for unsigned int i = 1; i < maxReg; i++ {
6903 ✗ constraints += String(",{") + argRegs[i - 1] + "}";
6904 ✗ }
6905
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if numRegs == 7 {
6906 ✗ constraints += ",0";
6907 ✗ }
6908 ✗ constraints += ",~{dirflag},~{fpsr},~{flags}";
6909 ✗ return constraints;
6910
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if targetTriple.isAArch64() {
6911 ✗ const string[7] linuxAArch64Regs = ["x8", "x0", "x1", "x2", "x3", "x4", "x5"];
6912
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for unsigned int i = 0; i < 7; i++ { regs.pushBack(String(linuxAArch64Regs[i])); }
6913 ✗ resultReg = String("x0");
6914 − } else { // LCOV_EXCL_LINE
6915 − panic(Error("Unsupported Linux target for inline assembly")); // LCOV_EXCL_LINE
6916 − } // LCOV_EXCL_LINE
6917 − } else { // LCOV_EXCL_LINE
6918 − panic(Error("Unsupported target for inline assembly")); // LCOV_EXCL_LINE
6919 − } // LCOV_EXCL_LINE
6920
6921 // Generate a comma-separated constraint string: first the result, then the operand constraints,
6922 // then the clobbers of the used registers, then extra clobbers.
6923 // The result is early-clobber, because the asm writes the result register before all inputs are read.
6924 5 String constraints = "=&{" + resultReg + "}";
6925
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
27 for unsigned int i = 0; i < numRegs; i++ {
6926 22 constraints += ",r";
6927 22 }
6928
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
27 for unsigned int i = 0; i < numRegs; i++ {
6929 22 const String& reg = regs.get(i);
6930
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 17 times.
22 if reg != resultReg {
6931 17 constraints += ",~{" + reg + "}";
6932 17 }
6933 22 }
6934 5 constraints += ",~{dirflag},~{fpsr},~{flags}";
6935 10 return constraints;
6936 5 }
6937
6938 // =========================================================== VTables ===========================================================
6939
6940 1554 f<llvm::Value> IRGenerator.generateTypeInfoName(StructBase* spiceStruct) {
6941 // Resolve mangled type info name and mangled global name
6942 1554 const String globalName = mangleTypeInfoName(spiceStruct);
6943
6944 // Generate global string constant
6945 1554 const String mangledName = mangleTypeInfoValue(spiceStruct.name);
6946 1554 llvm::Value global = this.module.createGlobalString(mangledName, globalName.getRaw());
6947
6948 // If the output should be comparable, fix alignment to 4 bytes
6949
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1554 times.
1554 if this.cliOptions.comparableOutput {
6950 1554 global.setAlignment(4);
6951 1554 }
6952
6953 1554 const QualType& entryType = spiceStruct.entry.getQualType();
6954 1554 const bool isPublic = entryType.isPublic();
6955
6956 // Set global attributes
6957 1554 global.setConstant(true);
6958 1554 global.setUnnamedAddr(llvm::LLVMUnnamedAddr::NoUnnamedAddr);
6959 1554 global.setLinkage(this.getVTableLinkageType(isPublic));
6960 // ToDo: Set dso_local as soon as the LLVM C API allows it (not for public symbols of a shared library)
6961 1554 this.attachComdatToSymbol(global, globalName, isPublic);
6962
6963 1554 spiceStruct.vTableData.typeInfoName = global;
6964 3108 return global;
6965 1554 }
6966
6967 1554 #[ignoreUnusedReturnValue]
6968 1554 f<llvm::Value> IRGenerator.generateTypeInfo(StructBase* spiceStruct) {
6969 // Generate type info name
6970 1554 const llvm::Value typeInfoName = this.generateTypeInfoName(spiceStruct);
6971
6972 // Generate LLVM type for type info
6973 1554 const String mangledName = mangleTypeInfo(spiceStruct);
6974 1554 const llvm::Type ptrTy = this.builder.getPtrTy();
6975
6976 1554 QualTypeList interfaceTypes;
6977 1554 const QualType& entryType = spiceStruct.entry.getQualType();
6978
2/2
✓ Branch 0 taken 682 times.
✓ Branch 1 taken 872 times.
1554 if entryType.is(SuperType::TY_STRUCT) {
6979 unsafe {
6980 // StructBase is the first member of Struct, so both share the same address
6981 872 Struct* spiceStructWithInterfaces = cast<Struct*>(spiceStruct);
6982 872 interfaceTypes = spiceStructWithInterfaces.interfaceTypes;
6983 }
6984 872 }
6985
6986 // Build type info LLVM type
6987 1554 Vector<llvm::Type> typeInfoFieldTypes;
6988 1554 typeInfoFieldTypes.pushBack(ptrTy);
6989 1554 typeInfoFieldTypes.pushBack(ptrTy);
6990
2/2
✓ Branch 0 taken 706 times.
✓ Branch 1 taken 1554 times.
2260 for unsigned long i = 0l; i < interfaceTypes.getSize(); i++ {
6991 706 typeInfoFieldTypes.pushBack(ptrTy);
6992 706 }
6993 1554 spiceStruct.vTableData.typeInfoType = llvm::getStructType(*this.context, typeInfoFieldTypes);
6994
6995 // Generate type info values
6996 1554 llvm::Value typeInfoVTable = llvm::getNullValue(ptrTy);
6997
2/2
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 1394 times.
1554 if !this.sourceFile.isRttiRT() {
6998 // Add external global pointer for TypeInfo vtable
6999 1394 assert this.sourceFile.isRuntimeModuleAvailable(RuntimeModule::RTTI_RT);
7000 1394 const String typeInfoMangledName = mangleVTable(String("TypeInfo"));
7001 1394 const llvm::Value typeInfoExtPtr = this.module.getOrInsertGlobal(typeInfoMangledName.getRaw(), ptrTy);
7002 1394 Vector<llvm::Value> indices;
7003 1394 indices.pushBack(this.getInt64(2l));
7004 1394 typeInfoVTable = llvm::getConstInBoundsGEP(ptrTy, typeInfoExtPtr, indices);
7005 1394 }
7006 1554 Vector<llvm::Value> fieldValues;
7007 1554 fieldValues.pushBack(typeInfoVTable);
7008 1554 fieldValues.pushBack(typeInfoName);
7009
2/2
✓ Branch 0 taken 1554 times.
✓ Branch 1 taken 706 times.
2260 foreach const QualType& interfaceType : interfaceTypes {
7010 706 const Interface* spiceInterface = interfaceType.getInterface(nil<ASTNode*>);
7011 706 assert spiceInterface != nil<const Interface*>;
7012 706 const String interfaceMangledName = mangleTypeInfo(&spiceInterface.structBase);
7013 706 const llvm::Value interfaceGlobal = this.module.getOrInsertGlobal(interfaceMangledName.getRaw(), ptrTy);
7014 706 fieldValues.pushBack(interfaceGlobal);
7015 706 }
7016 1554 const llvm::Value typeInfo = llvm::getConstNamedStruct(spiceStruct.vTableData.typeInfoType, fieldValues);
7017
7018 // Generate global variable
7019 1554 this.module.getOrInsertGlobal(mangledName.getRaw(), spiceStruct.vTableData.typeInfoType);
7020 1554 llvm::Value global = this.module.getNamedGlobal(mangledName.getRaw());
7021 1554 global.setInitializer(typeInfo);
7022
7023 1554 const bool isPublic = entryType.isPublic();
7024
7025 // Set global attributes
7026 1554 global.setConstant(true);
7027 1554 global.setUnnamedAddr(llvm::LLVMUnnamedAddr::NoUnnamedAddr);
7028 1554 global.setLinkage(this.getVTableLinkageType(isPublic));
7029 // ToDo: Set dso_local as soon as the LLVM C API allows it (not for public symbols of a shared library)
7030 1554 global.setAlignment(8);
7031 1554 this.attachComdatToSymbol(global, mangledName, isPublic);
7032
7033 1554 spiceStruct.vTableData.typeInfo = global;
7034 3108 return global;
7035 1554 }
7036
7037 1554 #[ignoreUnusedReturnValue]
7038 1554 f<llvm::Value> IRGenerator.generateVTable(StructBase* spiceStruct) {
7039 // Generate type info data structures
7040 1554 this.generateTypeInfo(spiceStruct);
7041
7042 // Generate VTable type
7043 1554 spiceStruct.vTableData.vtableType = this.getVTableType(spiceStruct);
7044
7045 1554 const String mangledName = mangleVTable(spiceStruct);
7046 1554 this.module.getOrInsertGlobal(mangledName.getRaw(), spiceStruct.vTableData.vtableType);
7047 1554 llvm::Value global = this.module.getNamedGlobal(mangledName.getRaw());
7048
7049 1554 const QualType& entryType = spiceStruct.entry.getQualType();
7050 1554 const bool isPublic = entryType.isPublic();
7051
7052 // Set global attributes
7053 1554 global.setConstant(true);
7054 1554 global.setUnnamedAddr(llvm::LLVMUnnamedAddr::GlobalUnnamedAddr);
7055 1554 global.setLinkage(this.getVTableLinkageType(isPublic));
7056 // ToDo: Set dso_local as soon as the LLVM C API allows it (not for public symbols of a shared library)
7057 1554 global.setAlignment(8);
7058 1554 this.attachComdatToSymbol(global, mangledName, isPublic);
7059
7060 1554 spiceStruct.vTableData.vtable = global;
7061 3108 return global;
7062 1554 }
7063
7064 1554 p IRGenerator.generateVTableInitializer(const StructBase* spiceStruct) {
7065 // Retrieve virtual method count
7066 1554 assert spiceStruct.scope != nil<Scope*>;
7067 1554 const Vector<const model::Function*> virtualMethods = spiceStruct.scope.getVirtualMethods();
7068
7069 // Generate VTable type
7070 1554 const llvm::Type ptrTy = this.builder.getPtrTy();
7071 1554 assert !spiceStruct.vTableData.vtableType.isNull();
7072
7073 // Generate VTable values
7074 1554 Vector<llvm::Value> arrayValues;
7075 1554 arrayValues.pushBack(llvm::getNullValue(ptrTy)); // nullptr as safety guard
7076 1554 arrayValues.pushBack(spiceStruct.vTableData.typeInfo); // TypeInfo to identify the type for the VTable
7077
2/2
✓ Branch 0 taken 3519 times.
✓ Branch 1 taken 1554 times.
5073 foreach const model::Function* virtualMethod : virtualMethods {
7078 // Methods, that are inherited from an interface default method, are implemented by the default method
7079
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3511 times.
3519 llvm::Function llvmFunc = virtualMethod.isInheritedDefaultMethod() ? this.getOrDeclareDefaultMethod(virtualMethod.defaultMethod) : this.getLLVMFunction(virtualMethod);
7080 3519 assert spiceStruct.scope.scopeType == ScopeType::INTERFACE || !llvmFunc.isNull();
7081 // A method may return a struct, where the interface method returns an interface, which the struct implements. Its
7082 // return type does not match the one of the interface method, but it is never called through the VTable: the type
7083 // checker rejects calls through an interface, that return an interface by value (INTERFACE_RETURNED_BY_VALUE)
7084
2/2
✓ Branch 0 taken 1764 times.
✓ Branch 1 taken 1755 times.
3519 arrayValues.pushBack(!llvmFunc.isNull() ? llvmFunc.asValue() : llvm::getNullValue(ptrTy));
7085 3519 }
7086
7087 // Generate VTable struct
7088 1554 Vector<llvm::Value> fieldValues;
7089 1554 fieldValues.pushBack(llvm::getConstArray(ptrTy, arrayValues));
7090 1554 const llvm::Value initializer = llvm::getConstNamedStruct(spiceStruct.vTableData.vtableType, fieldValues);
7091
7092 1554 const String mangledName = mangleVTable(spiceStruct);
7093 1554 llvm::Value global = this.module.getNamedGlobal(mangledName.getRaw());
7094 1554 assert !global.isNull();
7095 1554 global.setInitializer(initializer);
7096 1554 }
7097
7098 /**
7099 * Get the LLVM function of a function or procedure, that is not a method, and declare it if required, because it is
7100 * defined later, in another source file or externally
7101 *
7102 * @param fct Function or procedure
7103 * @return LLVM function
7104 */
7105 64 f<llvm::Function> IRGenerator.getOrDeclareFunction(const model::Function* fct) {
7106 64 assert !fct.isMethod();
7107 64 const String mangledName = fct.getMangledName();
7108 64 const llvm::Function existing = this.module.getFunction(mangledName.getRaw());
7109
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 4 times.
64 if !existing.isNull() {
7110 120 return existing;
7111 }
7112
7113 4 Vector<llvm::Type> paramTypes;
7114
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 foreach const QualType& paramType : fct.getParamTypes() {
7115 4 paramTypes.pushBack(paramType.getParamLLVMType(this.sourceFile));
7116 4 }
7117
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
4 const QualType returnType = fct.isProcedure() ? QualType(SuperType::TY_DYN) : fct.returnType;
7118 4 const llvm::Type fctType = this.getFunctionType(returnType, paramTypes, fct.isVararg);
7119 4 this.module.getOrInsertFunction(mangledName.getRaw(), fctType);
7120 4 llvm::Function llvmFct = this.module.getFunction(mangledName.getRaw());
7121 4 const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType);
7122
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if returnABI.isIndirect() {
7123 ✗ this.addSRetParamAttrs(llvmFct, returnABI.memoryType);
7124 ✗ }
7125 8 return llvmFct;
7126 64 }
7127
7128 8 f<llvm::Function> IRGenerator.getOrDeclareDefaultMethod(const model::Function* defaultMethod) {
7129 8 assert defaultMethod.isInterfaceDefaultMethod;
7130 // The default method might be defined in another source file or later in this one -> declare it if required
7131 8 const String mangledName = defaultMethod.getMangledName();
7132 8 const llvm::Function existing = this.module.getFunction(mangledName.getRaw());
7133
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if !existing.isNull() {
7134 16 return existing;
7135 }
7136
7137 ✗ Vector<llvm::Type> paramTypes;
7138 ✗ paramTypes.pushBack(this.builder.getPtrTy()); // This pointer
7139
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ foreach const QualType& paramType : defaultMethod.getParamTypes() {
7140 ✗ paramTypes.pushBack(paramType.getParamLLVMType(this.sourceFile));
7141 ✗ }
7142 ✗ const llvm::Type fctType = this.getFunctionType(defaultMethod.returnType, paramTypes);
7143 ✗ this.module.getOrInsertFunction(mangledName.getRaw(), fctType);
7144 ✗ llvm::Function fct = this.module.getFunction(mangledName.getRaw());
7145 ✗ const ReturnABIInfo returnABI = this.getReturnABIInfo(defaultMethod.returnType);
7146
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if returnABI.isIndirect() {
7147 ✗ this.addSRetParamAttrs(fct, returnABI.memoryType);
7148 ✗ }
7149 ✗ return fct;
7150 8 }
7151
7152 11555 f<bool> isPublicDefaultMethod(const model::Function* function) {
7153
2/2
✓ Branch 0 taken 11545 times.
✓ Branch 1 taken 10 times.
11555 return function.isInterfaceDefaultMethod && function.thisType.isPublic();
7154 }
7155
7156 /**
7157 * Check if the given function is emitted with external linkage and therefore visible to the linker under its mangled name.
7158 * Default methods of public interfaces are public, because structs in other source files can inherit them. Test functions
7159 * are public, if there is a test main, because it calls them
7160 *
7161 * @param function Function or procedure
7162 * @param hasTestMain Whether a test main is generated
7163 * @return Exported or not
7164 */
7165 101554 public f<bool> isExportedFunction(const model::Function* function, bool hasTestMain) {
7166 101554 const QualType& entryType = function.entry.getQualType();
7167
4/4
✓ Branch 0 taken 89999 times.
✓ Branch 1 taken 11555 times.
✓ Branch 2 taken 89999 times.
✓ Branch 3 taken 11555 times.
101554 if entryType.isPublic() || isPublicDefaultMethod(function) {
7168 89999 return true;
7169 }
7170
2/2
✓ Branch 0 taken 8160 times.
✓ Branch 1 taken 3395 times.
11555 if function.declNode.kind != ASTNodeKind::FCT_DEF {
7171 8160 return false;
7172 }
7173 // Test functions are only exported, if the test main calls them
7174
2/2
✓ Branch 0 taken 3331 times.
✓ Branch 1 taken 64 times.
3395 if !hasTestMain {
7175 3331 return false;
7176 }
7177 64 const ASTFctDefNode* fctDefNode = downcast<ASTFctDefNode>(function.declNode);
7178
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 18 times.
64 if fctDefNode.node.attrs == nil<IVisitable*> {
7179 18 return false;
7180 }
7181 46 const ASTTopLevelDefAttrNode* attrs = downcast<ASTTopLevelDefAttrNode>(fctDefNode.node.attrs);
7182 46 const String testAttrName = String(ATTR_TEST);
7183
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if !attrs.attrLst.hasAttr(testAttrName) {
7184 ✗ return false;
7185 }
7186 46 const CompileTimeValue testAttrValue = attrs.attrLst.getAttrValueByName(testAttrName);
7187 92 return testAttrValue.getBoolValue();
7188 46 }
7189
7190 1568 f<llvm::Type> IRGenerator.getVTableType(const StructBase* spiceStruct) {
7191 // Retrieve virtual method count
7192 1568 const Vector<const model::Function*> virtualMethods = spiceStruct.scope.getVirtualMethods();
7193 1568 const unsigned long arrayElementCount = virtualMethods.getSize() + 2l; // +2 for nullptr and TypeInfo
7194
7195 1568 const llvm::Type vtableArrayTy = llvm::getArrayType(this.builder.getPtrTy(), arrayElementCount);
7196 1568 Vector<llvm::Type> vtableFieldTypes;
7197 1568 vtableFieldTypes.pushBack(vtableArrayTy);
7198 3136 return llvm::getStructType(*this.context, vtableFieldTypes, false);
7199 1568 }
7200
7201 14 f<llvm::Value> IRGenerator.getVTableAddressPoint(const StructBase* spiceStruct) {
7202 // Look up the VTable by name in the current module instead of using vTableData.vtable. The latter belongs to the
7203 // module of the source file that defines the struct, so for an imported struct a declaration is inserted instead.
7204 14 const llvm::Type vtableType = this.getVTableType(spiceStruct);
7205 14 const String mangledName = mangleVTable(spiceStruct);
7206 14 const llvm::Value vtable = this.module.getOrInsertGlobal(mangledName.getRaw(), vtableType);
7207
7208 // The address point of the VTable is behind the nullptr and the TypeInfo
7209 14 Vector<llvm::Value> indices;
7210 14 indices.pushBack(this.getInt64(0l));
7211 14 indices.pushBack(this.getInt32(0l));
7212 14 indices.pushBack(this.getInt32(2l));
7213 28 return llvm::getConstInBoundsGEP(vtableType, vtable, indices);
7214 14 }
7215
7216 // ======================================================= Instrumentation =======================================================
7217
7218 44014 p IRGenerator.enableFunctionInstrumentation(llvm::Function function) {
7219
5/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42486 times.
✓ Branch 3 taken 1336 times.
✓ Branch 4 taken 184 times.
✓ Branch 5 taken 4 times.
44014 switch this.cliOptions.instrumentation.sanitizer {
7220 − case Sanitizer::NONE: {} // LCOV_EXCL_LINE
7221 1336 case Sanitizer::ADDRESS: { function.addFnAttr("sanitize_address"); }
7222 184 case Sanitizer::THREAD: { function.addFnAttr("sanitize_thread"); }
7223 4 case Sanitizer::MEMORY: { function.addFnAttr("sanitize_memory"); }
7224 4 case Sanitizer::TYPE: { function.addFnAttr("sanitize_type"); }
7225 − default: { panic(Error("Unhandled sanitizer type")); } // LCOV_EXCL_LINE
7226 }
7227 44014 }
7228