GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 97.0% 162 / 0 / 167
Functions: 100.0% 10 / 0 / 10
Branches: 53.3% 229 / 0 / 430

src/irgenerator/GenBuiltinFunctions.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include "IRGenerator.h"
4
5 #include <ast/ASTNodes.h>
6 #include <driver/Driver.h>
7 #include <global/GlobalResourceManager.h>
8 #include <global/TypeRegistry.h>
9 #include <typechecker/Builtins.h>
10 #include <typechecker/TypeChecker.h>
11
12 #include <llvm/IR/InlineAsm.h>
13 #include <llvm/IR/Module.h>
14
15 namespace spice::compiler {
16
17 15238 std::any IRGenerator::visitBuiltinCall(const FctCallNode *node) {
18 // If we have a compile time value, but the computation is still there, we can simply use this constant value
19
2/2
✓ Branch 3 → 4 taken 3068 times.
✓ Branch 3 → 10 taken 12170 times.
15238 if (node->hasCompileTimeValue(manIdx)) {
20
2/4
✓ Branch 5 → 6 taken 3068 times.
✗ Branch 5 → 25 not taken.
✓ Branch 6 → 7 taken 3068 times.
✗ Branch 6 → 25 not taken.
3068 llvm::Constant *value = getConst(node->getCompileTimeValue(manIdx), node->getEvaluatedSymbolType(manIdx), node);
21
1/2
✓ Branch 7 → 8 taken 3068 times.
✗ Branch 7 → 26 not taken.
6136 return LLVMExprResult{.constant = value};
22 }
23
24 // If we need to perform runtime actions, call the specified IRGenerator delegate
25
2/4
✓ Branch 11 → 12 taken 12170 times.
✗ Branch 11 → 27 not taken.
✓ Branch 12 → 13 taken 12170 times.
✗ Branch 12 → 14 not taken.
12170 assert(BUILTIN_DISPATCH_MAP.contains(node->fqFunctionName) && "Builtin function not implemented!");
26
1/2
✓ Branch 16 → 17 taken 12170 times.
✗ Branch 16 → 28 not taken.
12170 const BuiltinFunctionDispatch &dispatch = BUILTIN_DISPATCH_MAP.find(node->fqFunctionName)->second;
27
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 12170 times.
12170 assert(dispatch.irGeneratorVisitMethod != nullptr);
28
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 12170 times.
12170 return (this->*dispatch.irGeneratorVisitMethod)(node);
29 }
30
31 2559 std::any IRGenerator::visitBuiltinPrintfCall(const FctCallNode *node) {
32 // Retrieve templated string
33
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2559 times.
2559 assert(node->hasArgs);
34 2559 const ExprNode *firstArg = node->argLst->args.front();
35
5/10
✓ Branch 5 → 6 taken 2559 times.
✗ Branch 5 → 133 not taken.
✓ Branch 6 → 7 taken 2559 times.
✗ Branch 6 → 133 not taken.
✓ Branch 7 → 8 taken 2559 times.
✗ Branch 7 → 11 not taken.
✓ Branch 8 → 9 taken 2559 times.
✗ Branch 8 → 133 not taken.
✓ Branch 9 → 10 taken 2559 times.
✗ Branch 9 → 11 not taken.
2559 assert(firstArg->getEvaluatedSymbolType(manIdx).is(TY_STRING) && firstArg->hasCompileTimeValue(manIdx));
36
1/2
✓ Branch 12 → 13 taken 2559 times.
✗ Branch 12 → 133 not taken.
2559 const size_t stringOffset = firstArg->getCompileTimeValue(manIdx).stringValueOffset;
37
2/4
✓ Branch 13 → 14 taken 2559 times.
✗ Branch 13 → 133 not taken.
✓ Branch 14 → 15 taken 2559 times.
✗ Branch 14 → 133 not taken.
2559 const std::string templatedString = resourceManager.compileTimeStringValues.at(stringOffset);
38
39 // Push the template string as first argument
40 2559 std::vector<llvm::Value *> printfArgs;
41
2/4
✓ Branch 17 → 18 taken 2559 times.
✗ Branch 17 → 93 not taken.
✓ Branch 18 → 19 taken 2559 times.
✗ Branch 18 → 91 not taken.
5118 llvm::Constant *templateString = createGlobalStringConst("printf.str.", templatedString, node->codeLoc);
42
1/2
✓ Branch 21 → 22 taken 2559 times.
✗ Branch 21 → 97 not taken.
2559 printfArgs.push_back(templateString);
43
44 // Collect replacement arguments
45
2/2
✓ Branch 73 → 23 taken 2034 times.
✓ Branch 73 → 74 taken 2559 times.
4593 for (size_t argIdx = 1; argIdx < node->argLst->args.size(); argIdx++) {
46
1/2
✓ Branch 23 → 24 taken 2034 times.
✗ Branch 23 → 124 not taken.
2034 const ExprNode *arg = node->argLst->args.at(argIdx);
47 // Retrieve type of argument
48
1/2
✓ Branch 24 → 25 taken 2034 times.
✗ Branch 24 → 124 not taken.
2034 const QualType argSymbolType = arg->getEvaluatedSymbolType(manIdx);
49
50 // Re-map some values
51 llvm::Value *argVal;
52
2/4
✓ Branch 25 → 26 taken 2034 times.
✗ Branch 25 → 124 not taken.
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 39 taken 2034 times.
2034 if (argSymbolType.isArray()) {
53 // ToDo: Check if GEP can be removed
54 llvm::Value *argValPtr = resolveAddress(arg);
55 llvm::Value *indices[2] = {builder.getInt64(0), builder.getInt32(0)};
56 llvm::Type *argType = argSymbolType.toLLVMType(sourceFile);
57 argVal = insertInBoundsGEP(argType, argValPtr, indices);
58
4/6
✓ Branch 39 → 40 taken 2034 times.
✗ Branch 39 → 106 not taken.
✓ Branch 40 → 41 taken 2034 times.
✗ Branch 40 → 106 not taken.
✓ Branch 41 → 42 taken 176 times.
✓ Branch 41 → 59 taken 1858 times.
2034 } else if (argSymbolType.getBase().isStringObj()) {
59
1/2
✓ Branch 42 → 43 taken 176 times.
✗ Branch 42 → 124 not taken.
176 llvm::Value *argValPtr = resolveAddress(arg);
60
2/4
✓ Branch 43 → 44 taken 176 times.
✗ Branch 43 → 107 not taken.
✓ Branch 44 → 45 taken 176 times.
✗ Branch 44 → 107 not taken.
176 llvm::Type *argBaseType = argSymbolType.getBase().toLLVMType(sourceFile);
61
1/2
✓ Branch 48 → 49 taken 176 times.
✗ Branch 48 → 108 not taken.
176 argValPtr = insertStructGEP(argBaseType, argValPtr, 0);
62
2/4
✓ Branch 54 → 55 taken 176 times.
✗ Branch 54 → 114 not taken.
✓ Branch 55 → 56 taken 176 times.
✗ Branch 55 → 114 not taken.
176 argVal = insertLoad(builder.getPtrTy(), argValPtr);
63 } else {
64
1/2
✓ Branch 59 → 60 taken 1858 times.
✗ Branch 59 → 124 not taken.
1858 argVal = resolveValue(arg);
65 }
66
67 // Extend all integer types lower than 32 bit to 32 bit
68
4/6
✓ Branch 61 → 62 taken 2034 times.
✗ Branch 61 → 121 not taken.
✓ Branch 62 → 63 taken 2034 times.
✗ Branch 62 → 120 not taken.
✓ Branch 63 → 64 taken 360 times.
✓ Branch 63 → 70 taken 1674 times.
2034 if (argSymbolType.removeReferenceWrapper().isOneOf({TY_SHORT, TY_BYTE, TY_CHAR, TY_BOOL}))
69
5/10
✓ Branch 64 → 65 taken 360 times.
✗ Branch 64 → 123 not taken.
✓ Branch 65 → 66 taken 360 times.
✗ Branch 65 → 122 not taken.
✓ Branch 66 → 67 taken 360 times.
✗ Branch 66 → 122 not taken.
✓ Branch 67 → 68 taken 360 times.
✗ Branch 67 → 122 not taken.
✓ Branch 68 → 69 taken 360 times.
✗ Branch 68 → 122 not taken.
360 argVal = builder.CreateIntCast(argVal, builder.getInt32Ty(), argSymbolType.removeReferenceWrapper().isSigned());
70
71
1/2
✓ Branch 70 → 71 taken 2034 times.
✗ Branch 70 → 124 not taken.
2034 printfArgs.push_back(argVal);
72 }
73
74 // Call printf function
75
1/2
✓ Branch 74 → 75 taken 2559 times.
✗ Branch 74 → 129 not taken.
2559 llvm::Function *printfFct = stdFunctionManager.getPrintfFct();
76
4/8
✓ Branch 75 → 76 taken 2559 times.
✗ Branch 75 → 127 not taken.
✓ Branch 77 → 78 taken 2559 times.
✗ Branch 77 → 125 not taken.
✓ Branch 78 → 79 taken 2559 times.
✗ Branch 78 → 125 not taken.
✓ Branch 79 → 80 taken 2559 times.
✗ Branch 79 → 129 not taken.
2559 llvm::CallInst *callInst = builder.CreateCall(printfFct, printfArgs);
77
78 // Add noundef attribute to return value and all arguments
79
1/2
✓ Branch 79 → 80 taken 2559 times.
✗ Branch 79 → 129 not taken.
2559 callInst->addRetAttr(llvm::Attribute::NoUndef);
80
2/2
✓ Branch 84 → 81 taken 4593 times.
✓ Branch 84 → 85 taken 2559 times.
7152 for (size_t i = 0; i < printfArgs.size(); i++)
81
1/2
✓ Branch 81 → 82 taken 4593 times.
✗ Branch 81 → 129 not taken.
4593 callInst->addParamAttr(i, llvm::Attribute::NoUndef);
82
83
1/2
✓ Branch 85 → 86 taken 2559 times.
✗ Branch 85 → 128 not taken.
5118 return LLVMExprResult{.value = callInst};
84 2559 }
85
86 156 std::any IRGenerator::visitBuiltinLenCall(const FctCallNode *node) {
87
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 156 times.
156 assert(node->fqFunctionName == BUILTIN_FCT_NAME_LEN);
88
89 // Check if the length is fixed and known via the symbol type
90 156 const ExprNode *argNode = node->argLst->args.front();
91
1/2
✓ Branch 7 → 8 taken 156 times.
✗ Branch 7 → 28 not taken.
156 QualType symbolType = argNode->getEvaluatedSymbolType(manIdx);
92
1/2
✓ Branch 8 → 9 taken 156 times.
✗ Branch 8 → 22 not taken.
156 symbolType = symbolType.removeReferenceWrapper();
93
2/4
✓ Branch 9 → 10 taken 156 times.
✗ Branch 9 → 28 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 156 times.
156 assert(symbolType.is(TY_STRING));
94
95
1/2
✓ Branch 12 → 13 taken 156 times.
✗ Branch 12 → 28 not taken.
156 llvm::Function *getRawLengthFct = stdFunctionManager.getStringGetRawLengthStringFct();
96
5/10
✓ Branch 13 → 14 taken 156 times.
✗ Branch 13 → 26 not taken.
✓ Branch 14 → 15 taken 156 times.
✗ Branch 14 → 24 not taken.
✓ Branch 16 → 17 taken 156 times.
✗ Branch 16 → 23 not taken.
✓ Branch 17 → 18 taken 156 times.
✗ Branch 17 → 23 not taken.
✓ Branch 18 → 19 taken 156 times.
✗ Branch 18 → 27 not taken.
156 llvm::Value *lengthValue = builder.CreateCall(getRawLengthFct, resolveValue(argNode));
97
1/2
✓ Branch 18 → 19 taken 156 times.
✗ Branch 18 → 27 not taken.
312 return LLVMExprResult{.value = lengthValue};
98 }
99
100 4979 std::any IRGenerator::visitBuiltinPanicCall(const FctCallNode *node) {
101
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 4979 times.
4979 assert(node->fqFunctionName == BUILTIN_FCT_NAME_PANIC);
102
103
1/2
✓ Branch 6 → 7 taken 4979 times.
✗ Branch 6 → 127 not taken.
4979 llvm::PointerType *ptrTy = builder.getPtrTy();
104
105 // Get value for stderr
106
1/2
✓ Branch 7 → 8 taken 4979 times.
✗ Branch 7 → 127 not taken.
4979 llvm::Value *stdErr = getStdErrValue();
107
108 // Create constant for error message
109
1/2
✓ Branch 8 → 9 taken 4979 times.
✗ Branch 8 → 127 not taken.
4979 const std::string codeLoc = node->codeLoc.toPrettyString();
110
5/10
✓ Branch 9 → 10 taken 4979 times.
✗ Branch 9 → 85 not taken.
✓ Branch 10 → 11 taken 4979 times.
✗ Branch 10 → 81 not taken.
✓ Branch 11 → 12 taken 4979 times.
✗ Branch 11 → 79 not taken.
✓ Branch 12 → 13 taken 4979 times.
✗ Branch 12 → 77 not taken.
✓ Branch 13 → 14 taken 4979 times.
✗ Branch 13 → 75 not taken.
4979 const std::string templateStr = "Program panicked at " + codeLoc + ": %s\n" + node->getErrorMessage() + "\n";
111
4/8
✓ Branch 20 → 21 taken 4979 times.
✗ Branch 20 → 92 not taken.
✓ Branch 21 → 22 taken 4979 times.
✗ Branch 21 → 90 not taken.
✓ Branch 22 → 23 taken 4979 times.
✗ Branch 22 → 88 not taken.
✓ Branch 24 → 25 taken 4979 times.
✗ Branch 24 → 87 not taken.
9958 llvm::GlobalVariable *globalString = builder.CreateGlobalString(templateStr, getUnusedGlobalName(ANON_GLOBAL_STRING_NAME));
112
113 // If the output should be comparable, fix alignment to 4 bytes
114
1/2
✓ Branch 28 → 29 taken 4979 times.
✗ Branch 28 → 32 not taken.
4979 if (cliOptions.comparableOutput)
115
2/4
✓ Branch 29 → 30 taken 4979 times.
✗ Branch 29 → 98 not taken.
✓ Branch 30 → 31 taken 4979 times.
✗ Branch 30 → 98 not taken.
4979 globalString->setAlignment(llvm::Align(4));
116
117 // Get actual error message
118
1/2
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 4979 times.
4979 assert(node->hasArgs);
119 4979 const ExprNode *assignExpr = node->argLst->args.front();
120
1/2
✓ Branch 35 → 36 taken 4979 times.
✗ Branch 35 → 123 not taken.
4979 llvm::Value *errorObjPtr = resolveAddress(assignExpr);
121
2/4
✓ Branch 36 → 37 taken 4979 times.
✗ Branch 36 → 123 not taken.
✓ Branch 37 → 38 taken 4979 times.
✗ Branch 37 → 123 not taken.
4979 llvm::Type *errorObjTy = assignExpr->getEvaluatedSymbolType(manIdx).toLLVMType(sourceFile);
122
1/2
✓ Branch 41 → 42 taken 4979 times.
✗ Branch 41 → 99 not taken.
4979 llvm::Value *errorMessagePtr = insertStructGEP(errorObjTy, errorObjPtr, 1);
123
1/2
✓ Branch 47 → 48 taken 4979 times.
✗ Branch 47 → 105 not taken.
4979 llvm::Value *errorMessage = insertLoad(ptrTy, errorMessagePtr);
124
125 // Print the error message to stderr
126
1/2
✓ Branch 50 → 51 taken 4979 times.
✗ Branch 50 → 123 not taken.
4979 llvm::Function *fprintfFct = stdFunctionManager.getFPrintfFct();
127
4/8
✓ Branch 51 → 52 taken 4979 times.
✗ Branch 51 → 114 not taken.
✓ Branch 53 → 54 taken 4979 times.
✗ Branch 53 → 111 not taken.
✓ Branch 54 → 55 taken 4979 times.
✗ Branch 54 → 111 not taken.
✓ Branch 55 → 56 taken 4979 times.
✗ Branch 55 → 117 not taken.
4979 builder.CreateCall(fprintfFct, {stdErr, globalString, errorMessage});
128
129 // Print the recorded error return trace to stderr. A no-op unless error return tracing was enabled somewhere
130 // along the propagation chain that produced this error - see error_trace_rt.spice.
131
5/10
✓ Branch 55 → 56 taken 4979 times.
✗ Branch 55 → 117 not taken.
✓ Branch 57 → 58 taken 4979 times.
✗ Branch 57 → 115 not taken.
✓ Branch 58 → 59 taken 4979 times.
✗ Branch 58 → 115 not taken.
✓ Branch 59 → 60 taken 4979 times.
✗ Branch 59 → 115 not taken.
✓ Branch 60 → 61 taken 4979 times.
✗ Branch 60 → 123 not taken.
4979 builder.CreateCall(stdFunctionManager.getErrTraceDumpFct());
132
133 // Cleanup the scope before calling exit()
134 // Unreachable below counts as terminator
135
2/4
✓ Branch 60 → 61 taken 4979 times.
✗ Branch 60 → 123 not taken.
✓ Branch 61 → 62 taken 4979 times.
✗ Branch 61 → 123 not taken.
4979 terminateBlock(node->getNextOuterStmtLst());
136
137 // Generate call to exit()
138
1/2
✓ Branch 62 → 63 taken 4979 times.
✗ Branch 62 → 123 not taken.
4979 llvm::Function *exitFct = stdFunctionManager.getExitFct();
139
5/10
✓ Branch 63 → 64 taken 4979 times.
✗ Branch 63 → 121 not taken.
✓ Branch 64 → 65 taken 4979 times.
✗ Branch 64 → 119 not taken.
✓ Branch 66 → 67 taken 4979 times.
✗ Branch 66 → 118 not taken.
✓ Branch 67 → 68 taken 4979 times.
✗ Branch 67 → 118 not taken.
✓ Branch 68 → 69 taken 4979 times.
✗ Branch 68 → 123 not taken.
4979 builder.CreateCall(exitFct, builder.getInt32(EXIT_FAILURE));
140 // Create unreachable instruction
141
1/2
✓ Branch 68 → 69 taken 4979 times.
✗ Branch 68 → 123 not taken.
4979 builder.CreateUnreachable();
142
143
1/2
✓ Branch 69 → 70 taken 4979 times.
✗ Branch 69 → 122 not taken.
9958 return nullptr;
144 4979 }
145
146 4 std::any IRGenerator::visitBuiltinSyscallCall(const FctCallNode *node) {
147
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 4 times.
4 assert(node->fqFunctionName == BUILTIN_FCT_NAME_SYSCALL);
148
149 // Determine the required number of operands.
150 // (We assume at least one argument is provided: the syscall number)
151
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 4 times.
4 assert(node->hasArgs);
152 4 const auto requiredRegs = static_cast<uint8_t>(node->argLst->args.size());
153
2/4
✓ Branch 9 → 10 taken 4 times.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 4 times.
✗ Branch 10 → 12 not taken.
4 assert(requiredRegs >= 1 && requiredRegs <= 6);
154
155 // Create the asm and constraint strings based on the required number of registers.
156
1/2
✓ Branch 13 → 14 taken 4 times.
✗ Branch 13 → 85 not taken.
4 const std::string asmString = getSysCallAsmString(requiredRegs);
157
1/2
✓ Branch 14 → 15 taken 4 times.
✗ Branch 14 → 83 not taken.
4 const std::string constraints = getSysCallConstraintString(requiredRegs);
158
159 // Create the LLVM function type for the inline asm with only the needed operands.
160
1/2
✓ Branch 15 → 16 taken 4 times.
✗ Branch 15 → 81 not taken.
4 llvm::Type *int64Ty = builder.getInt64Ty();
161
1/2
✓ Branch 18 → 19 taken 4 times.
✗ Branch 18 → 61 not taken.
8 const std::vector argTypes(requiredRegs, int64Ty);
162
2/4
✓ Branch 21 → 22 taken 4 times.
✗ Branch 21 → 64 not taken.
✓ Branch 22 → 23 taken 4 times.
✗ Branch 22 → 64 not taken.
4 llvm::FunctionType *fctType = llvm::FunctionType::get(builder.getVoidTy(), argTypes, false);
163
1/2
✓ Branch 25 → 26 taken 4 times.
✗ Branch 25 → 65 not taken.
4 llvm::InlineAsm *inlineAsm = llvm::InlineAsm::get(fctType, asmString, constraints, true);
164
165 // Build the argument list (each provided argument is converted to i64).
166 4 std::vector<llvm::Value *> argValues;
167
1/2
✓ Branch 26 → 27 taken 4 times.
✗ Branch 26 → 77 not taken.
4 argValues.reserve(requiredRegs);
168
2/2
✓ Branch 48 → 28 taken 16 times.
✓ Branch 48 → 49 taken 4 times.
20 for (uint8_t i = 0; i < requiredRegs; i++) {
169
1/2
✓ Branch 28 → 29 taken 16 times.
✗ Branch 28 → 77 not taken.
16 const ExprNode *argNode = node->argLst->args.at(i);
170
1/2
✓ Branch 29 → 30 taken 16 times.
✗ Branch 29 → 77 not taken.
16 const QualType &argType = argNode->getEvaluatedSymbolType(manIdx);
171
2/4
✓ Branch 30 → 31 taken 16 times.
✗ Branch 30 → 67 not taken.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 16 times.
16 assert(argType.isOneOf({TY_INT, TY_LONG, TY_SHORT, TY_BOOL, TY_BYTE, TY_PTR, TY_STRING}));
172
3/4
✓ Branch 33 → 34 taken 16 times.
✗ Branch 33 → 68 not taken.
✓ Branch 34 → 35 taken 4 times.
✓ Branch 34 → 41 taken 12 times.
16 if (argType.isOneOf({TY_PTR, TY_STRING}))
173
5/10
✓ Branch 35 → 36 taken 4 times.
✗ Branch 35 → 69 not taken.
✓ Branch 36 → 37 taken 4 times.
✗ Branch 36 → 69 not taken.
✓ Branch 37 → 38 taken 4 times.
✗ Branch 37 → 69 not taken.
✓ Branch 38 → 39 taken 4 times.
✗ Branch 38 → 69 not taken.
✓ Branch 39 → 40 taken 4 times.
✗ Branch 39 → 69 not taken.
4 argValues.push_back(builder.CreatePtrToInt(resolveValue(argNode), builder.getInt64Ty()));
174 else
175
5/10
✓ Branch 41 → 42 taken 12 times.
✗ Branch 41 → 71 not taken.
✓ Branch 42 → 43 taken 12 times.
✗ Branch 42 → 71 not taken.
✓ Branch 43 → 44 taken 12 times.
✗ Branch 43 → 71 not taken.
✓ Branch 44 → 45 taken 12 times.
✗ Branch 44 → 71 not taken.
✓ Branch 45 → 46 taken 12 times.
✗ Branch 45 → 71 not taken.
12 argValues.push_back(builder.CreateZExt(resolveValue(argNode), builder.getInt64Ty()));
176 }
177
178 // Generate the call using only the required number of arguments.
179
4/8
✓ Branch 49 → 50 taken 4 times.
✗ Branch 49 → 75 not taken.
✓ Branch 51 → 52 taken 4 times.
✗ Branch 51 → 73 not taken.
✓ Branch 52 → 53 taken 4 times.
✗ Branch 52 → 73 not taken.
✓ Branch 53 → 54 taken 4 times.
✗ Branch 53 → 76 not taken.
4 llvm::Value *result = builder.CreateCall(inlineAsm, argValues);
180
181
1/2
✓ Branch 53 → 54 taken 4 times.
✗ Branch 53 → 76 not taken.
8 return LLVMExprResult{.value = result};
182 4 }
183
184 483 std::any IRGenerator::visitBuiltinNewCall(const FctCallNode *node) {
185
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 483 times.
483 assert(node->fqFunctionName == BUILTIN_FCT_NAME_NEW);
186
187 483 const FctCallNode::FctCallData &data = node->data.at(manIdx);
188 483 const QualType &templateType = data.templateTypes.front();
189
190 // Allocate sizeof(T) bytes on the heap
191 483 llvm::Type *llvmType = templateType.toLLVMType(sourceFile);
192
2/4
✓ Branch 10 → 11 taken 483 times.
✗ Branch 10 → 51 not taken.
✓ Branch 11 → 12 taken 483 times.
✗ Branch 11 → 51 not taken.
483 const uint64_t typeSize = module->getDataLayout().getTypeAllocSize(llvmType);
193 483 llvm::Function *allocFct = stdFunctionManager.getAllocUnsafeLongFct();
194
6/10
✓ Branch 13 → 14 taken 483 times.
✗ Branch 13 → 55 not taken.
✓ Branch 14 → 15 taken 483 times.
✗ Branch 14 → 53 not taken.
✓ Branch 16 → 17 taken 483 times.
✗ Branch 16 → 52 not taken.
✓ Branch 17 → 18 taken 483 times.
✗ Branch 17 → 52 not taken.
✓ Branch 18 → 19 taken 472 times.
✓ Branch 18 → 40 taken 11 times.
483 llvm::Value *targetPtr = builder.CreateCall(allocFct, {builder.getInt64(typeSize)});
195
196 // Call the constructor if one was resolved during type checking
197
2/2
✓ Branch 18 → 19 taken 472 times.
✓ Branch 18 → 40 taken 11 times.
483 if (const Function *ctor = data.callee) {
198 472 std::vector<llvm::Value *> ctorArgValues;
199
2/2
✓ Branch 19 → 20 taken 444 times.
✓ Branch 19 → 37 taken 28 times.
472 if (node->hasArgs) {
200
1/2
✓ Branch 20 → 21 taken 444 times.
✗ Branch 20 → 60 not taken.
444 const QualTypeList paramTypes = ctor->getParamTypes();
201
2/2
✓ Branch 34 → 22 taken 536 times.
✓ Branch 34 → 35 taken 444 times.
980 for (size_t i = 0; i < node->argLst->args.size(); i++) {
202
1/2
✓ Branch 22 → 23 taken 536 times.
✗ Branch 22 → 58 not taken.
536 const ExprNode *argNode = node->argLst->args.at(i);
203
4/6
✓ Branch 23 → 24 taken 536 times.
✗ Branch 23 → 58 not taken.
✓ Branch 24 → 25 taken 536 times.
✗ Branch 24 → 58 not taken.
✓ Branch 25 → 26 taken 135 times.
✓ Branch 25 → 29 taken 401 times.
536 if (paramTypes.at(i).isRef())
204
2/4
✓ Branch 26 → 27 taken 135 times.
✗ Branch 26 → 56 not taken.
✓ Branch 27 → 28 taken 135 times.
✗ Branch 27 → 56 not taken.
135 ctorArgValues.push_back(resolveAddress(argNode));
205 else
206
2/4
✓ Branch 29 → 30 taken 401 times.
✗ Branch 29 → 57 not taken.
✓ Branch 30 → 31 taken 401 times.
✗ Branch 30 → 57 not taken.
401 ctorArgValues.push_back(resolveValue(argNode));
207 }
208 444 }
209
1/2
✓ Branch 37 → 38 taken 472 times.
✗ Branch 37 → 61 not taken.
472 generateCtorOrDtorCall(targetPtr, ctor, ctorArgValues);
210 472 } else {
211 11 const std::vector<ExprNode *> &args = node->argLst->args;
212
2/2
✓ Branch 40 → 41 taken 6 times.
✓ Branch 40 → 44 taken 5 times.
11 llvm::Value *initVal = node->hasArgs ? resolveValue(args.front()) : getDefaultValueForSymbolType(templateType);
213 11 insertStore(initVal, targetPtr);
214 }
215
216
1/2
✓ Branch 47 → 48 taken 483 times.
✗ Branch 47 → 64 not taken.
966 return LLVMExprResult{.value = targetPtr};
217 }
218
219 1403 std::any IRGenerator::visitBuiltinPlacementNewCall(const FctCallNode *node) {
220
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1403 times.
1403 assert(node->fqFunctionName == BUILTIN_FCT_NAME_PLACEMENT_NEW);
221
222 1403 const FctCallNode::FctCallData &data = node->data.at(manIdx);
223
224 // Resolve the target address from the first argument (the byte* pointer)
225 1403 llvm::Value *targetPtr = resolveValue(node->argLst->args.front());
226
227 // Call the constructor if one was resolved during type checking
228
2/2
✓ Branch 9 → 10 taken 883 times.
✓ Branch 9 → 29 taken 520 times.
1403 if (const Function *ctor = data.callee) {
229 883 std::vector<llvm::Value *> ctorArgValues;
230
1/2
✓ Branch 10 → 11 taken 883 times.
✗ Branch 10 → 46 not taken.
883 const QualTypeList paramTypes = ctor->getParamTypes();
231
2/2
✓ Branch 24 → 12 taken 893 times.
✓ Branch 24 → 25 taken 883 times.
1776 for (size_t i = 1; i < node->argLst->args.size(); i++) {
232
1/2
✓ Branch 12 → 13 taken 893 times.
✗ Branch 12 → 44 not taken.
893 const ExprNode *argNode = node->argLst->args.at(i);
233
4/6
✓ Branch 13 → 14 taken 893 times.
✗ Branch 13 → 44 not taken.
✓ Branch 14 → 15 taken 893 times.
✗ Branch 14 → 44 not taken.
✓ Branch 15 → 16 taken 573 times.
✓ Branch 15 → 19 taken 320 times.
893 if (paramTypes.at(i - 1).isRef())
234
2/4
✓ Branch 16 → 17 taken 573 times.
✗ Branch 16 → 42 not taken.
✓ Branch 17 → 18 taken 573 times.
✗ Branch 17 → 42 not taken.
573 ctorArgValues.push_back(resolveAddress(argNode));
235 else
236
2/4
✓ Branch 19 → 20 taken 320 times.
✗ Branch 19 → 43 not taken.
✓ Branch 20 → 21 taken 320 times.
✗ Branch 20 → 43 not taken.
320 ctorArgValues.push_back(resolveValue(argNode));
237 }
238
1/2
✓ Branch 25 → 26 taken 883 times.
✗ Branch 25 → 44 not taken.
883 generateCtorOrDtorCall(targetPtr, ctor, ctorArgValues);
239 883 } else {
240 520 const QualType &templateType = data.templateTypes.front();
241 520 const std::vector<ExprNode *> &args = node->argLst->args;
242
1/2
✓ Branch 31 → 32 taken 520 times.
✗ Branch 31 → 35 not taken.
520 llvm::Value *initVal = args.size() > 1 ? resolveValue(args.at(1)) : getDefaultValueForSymbolType(templateType);
243 520 insertStore(initVal, targetPtr);
244 }
245
246
1/2
✓ Branch 38 → 39 taken 1403 times.
✗ Branch 38 → 49 not taken.
2806 return LLVMExprResult{.value = targetPtr};
247 }
248
249 1293 std::any IRGenerator::visitBuiltinErrTraceBufferCall(const FctCallNode *node) {
250
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1293 times.
1293 assert(node->fqFunctionName == BUILTIN_FCT_NAME_ERR_TRACE_BUFFER);
251
252 // Get or create the thread-local ErrorTraceBuffer instance for this module. It is only ever referenced from
253 // within error_trace_rt.spice's own wrapper procedures (all compiled into this same module), so internal
254 // linkage is enough - no other module ever needs to see this symbol.
255 1293 constexpr auto TLS_GLOBAL_NAME = "__spice_errTraceBufferTLS";
256
2/4
✓ Branch 6 → 7 taken 1293 times.
✗ Branch 6 → 25 not taken.
✓ Branch 7 → 8 taken 1293 times.
✗ Branch 7 → 25 not taken.
1293 llvm::GlobalVariable *tlsBuffer = module->getNamedGlobal(TLS_GLOBAL_NAME);
257
2/2
✓ Branch 8 → 9 taken 431 times.
✓ Branch 8 → 21 taken 862 times.
1293 if (!tlsBuffer) {
258
3/6
✓ Branch 9 → 10 taken 431 times.
✗ Branch 9 → 26 not taken.
✓ Branch 10 → 11 taken 431 times.
✗ Branch 10 → 26 not taken.
✓ Branch 11 → 12 taken 431 times.
✗ Branch 11 → 26 not taken.
431 llvm::Type *bufferType = node->getEvaluatedSymbolType(manIdx).removeReferenceWrapper().toLLVMType(sourceFile);
259 431 llvm::Constant *zeroInit = llvm::Constant::getNullValue(bufferType);
260 tlsBuffer = new llvm::GlobalVariable(*module, bufferType, /*isConstant=*/false, llvm::GlobalValue::InternalLinkage, zeroInit,
261
3/6
✓ Branch 15 → 16 taken 431 times.
✗ Branch 15 → 27 not taken.
✓ Branch 16 → 17 taken 431 times.
✗ Branch 16 → 27 not taken.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 20 taken 431 times.
431 TLS_GLOBAL_NAME);
262 431 tlsBuffer->setThreadLocal(true);
263 }
264
265
1/2
✓ Branch 21 → 22 taken 1293 times.
✗ Branch 21 → 33 not taken.
2586 return LLVMExprResult{.ptr = tlsBuffer};
266 }
267
268 1293 std::any IRGenerator::visitBuiltinStdErrCall([[maybe_unused]] const FctCallNode *node) {
269
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1293 times.
1293 assert(node->fqFunctionName == BUILTIN_FCT_NAME_STDERR);
270
271
2/4
✓ Branch 6 → 7 taken 1293 times.
✗ Branch 6 → 11 not taken.
✓ Branch 7 → 8 taken 1293 times.
✗ Branch 7 → 11 not taken.
2586 return LLVMExprResult{.value = getStdErrValue()};
272 }
273
274 /**
275 * Retrieves the process's stderr stream (FILE*). Spice has no syntax for declaring an external variable, so this
276 * reaches the real libc symbol directly, the same way on every platform: an ACRT accessor call on Windows, and
277 * the platform-specific extern global everywhere else.
278 */
279 6302 llvm::Value *IRGenerator::getStdErrValue() const {
280 6302 llvm::PointerType *ptrTy = builder.getPtrTy();
281
282
2/2
✓ Branch 4 → 5 taken 26 times.
✓ Branch 4 → 13 taken 6276 times.
6302 if (cliOptions.targetTriple.isOSWindows()) {
283 26 llvm::Function *getAcrtIOFuncFct = stdFunctionManager.getAcrtIOFuncFct();
284
4/8
✓ Branch 6 → 7 taken 26 times.
✗ Branch 6 → 40 not taken.
✓ Branch 7 → 8 taken 26 times.
✗ Branch 7 → 38 not taken.
✓ Branch 9 → 10 taken 26 times.
✗ Branch 9 → 37 not taken.
✓ Branch 10 → 11 taken 26 times.
✗ Branch 10 → 37 not taken.
26 return builder.CreateCall(getAcrtIOFuncFct, {builder.getInt32(/*constant for stderr*/ 2)});
285 }
286
287
2/2
✓ Branch 14 → 15 taken 26 times.
✓ Branch 14 → 16 taken 6250 times.
6276 const char *globalName = cliOptions.targetTriple.isOSDarwin() ? "__stderrp" : "stderr";
288
2/4
✓ Branch 17 → 18 taken 6276 times.
✗ Branch 17 → 41 not taken.
✓ Branch 18 → 19 taken 6276 times.
✗ Branch 18 → 41 not taken.
6276 llvm::GlobalVariable *stdErrPtr = module->getNamedGlobal(globalName);
289
2/2
✓ Branch 19 → 20 taken 2115 times.
✓ Branch 19 → 28 taken 4161 times.
6276 if (!stdErrPtr) {
290
3/6
✓ Branch 20 → 21 taken 2115 times.
✗ Branch 20 → 42 not taken.
✓ Branch 21 → 22 taken 2115 times.
✗ Branch 21 → 42 not taken.
✓ Branch 22 → 23 taken 2115 times.
✗ Branch 22 → 42 not taken.
2115 stdErrPtr = llvm::cast<llvm::GlobalVariable>(module->getOrInsertGlobal(globalName, ptrTy));
291 2115 stdErrPtr->setLinkage(llvm::GlobalVariable::ExternalLinkage);
292 2115 stdErrPtr->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Local);
293
2/4
✓ Branch 25 → 26 taken 2115 times.
✗ Branch 25 → 43 not taken.
✓ Branch 26 → 27 taken 2115 times.
✗ Branch 26 → 43 not taken.
2115 stdErrPtr->setAlignment(llvm::MaybeAlign(8));
294 }
295
1/2
✓ Branch 31 → 32 taken 6276 times.
✗ Branch 31 → 44 not taken.
6276 return insertLoad(ptrTy, stdErrPtr);
296 }
297
298 } // namespace spice::compiler
299