GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
236 / 256
92.2%
Branches:
8 / 8
100.0%
Functions:
31 / 33
93.9%

irgenerator/std-function-manager.spice
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 // Std imports
4 import "std/data/vector";
5 import "std/bindings/llvm/llvm" as llvm;
6
7 // Own imports
8 import "@/source-file";
9 import "@/irgenerator/abi-info";
10 import "@/irgenerator/ir-generator";
11 import "@/irgenerator/name-mangling";
12 import "@/model/function" as model; // Aliased, because Function clashes with llvm::Function
13 import "@/ast/ast-nodes";
14 import "@/model/generic-type";
15 import "@/symboltablebuilder/qual-type";
16 import "@/symboltablebuilder/super-type";
17 import "@/symboltablebuilder/symbol-table-entry";
18
19 /**
20 * Helper for the IRGenerator to retrieve the declarations of functions from the C standard library, LLVM intrinsics and the
21 * Spice runtime modules
22 */
23 public type StdFunctionManager struct {
24 SourceFile* sourceFile
25 llvm::IRBuilder* builder
26 llvm::Module* module
27 IRGenerator* irGenerator
28 }
29
30 3499 public p StdFunctionManager.ctor(SourceFile* sourceFile, llvm::Module* module, IRGenerator* irGenerator) {
31 3499 this.sourceFile = sourceFile;
32 3499 this.builder = &sourceFile.builder;
33 3499 this.module = module;
34 3499 this.irGenerator = irGenerator;
35 3499 }
36
37 2346 public f<llvm::Function> StdFunctionManager.getPrintfFct() {
38 2346 Vector<llvm::Type> paramTypes;
39 2346 paramTypes.pushBack(this.builder.getPtrTy());
40 2346 llvm::Function printfFct = this.getFunction("printf", this.builder.getInt32Ty(), paramTypes, true);
41 // Set attributes
42 2346 printfFct.setUnnamedAddr(llvm::LLVMUnnamedAddr::LocalUnnamedAddr);
43 2346 printfFct.addFnAttr("nofree");
44 2346 printfFct.addFnAttr("nounwind");
45 2346 printfFct.addParamAttr(0, "captures", 0l); // captures(none)
46 2346 printfFct.addParamAttr(0, "noundef");
47 2346 printfFct.addParamAttr(0, "readonly");
48 2346 printfFct.addRetAttr("noundef");
49 4692 return printfFct;
50 2346 }
51
52 7755 public f<llvm::Function> StdFunctionManager.getFPrintfFct() {
53 7755 Vector<llvm::Type> paramTypes;
54 7755 paramTypes.pushBack(this.builder.getPtrTy());
55 7755 paramTypes.pushBack(this.builder.getPtrTy());
56 7755 llvm::Function fprintfFct = this.getFunction("fprintf", this.builder.getInt32Ty(), paramTypes, true);
57 // Set attributes
58 7755 fprintfFct.setUnnamedAddr(llvm::LLVMUnnamedAddr::LocalUnnamedAddr);
59 7755 fprintfFct.addFnAttr("nofree");
60 7755 fprintfFct.addParamAttr(0, "captures", 0l); // captures(none)
61 7755 fprintfFct.addParamAttr(0, "noundef");
62 7755 fprintfFct.addParamAttr(1, "captures", 0l); // captures(none)
63 7755 fprintfFct.addParamAttr(1, "noundef");
64 7755 fprintfFct.addParamAttr(1, "readonly");
65 7755 fprintfFct.addRetAttr("noundef");
66 15510 return fprintfFct;
67 7755 }
68
69 7755 public f<llvm::Function> StdFunctionManager.getExitFct() {
70 7755 Vector<llvm::Type> paramTypes;
71 7755 paramTypes.pushBack(this.builder.getInt32Ty());
72 7755 llvm::Function exitFct = this.getProcedure("exit", paramTypes);
73 // Set attributes
74 7755 exitFct.addFnAttr("cold");
75 7755 exitFct.addFnAttr("noreturn");
76 7755 exitFct.addFnAttr("nounwind");
77 15510 return exitFct;
78 7755 }
79
80 ✗ public f<llvm::Function> StdFunctionManager.getFreeFct() {
81 ✗ Vector<llvm::Type> paramTypes;
82 ✗ paramTypes.pushBack(this.builder.getPtrTy());
83 ✗ llvm::Function freeFct = this.getProcedure("free", paramTypes);
84 // Set attributes
85 ✗ freeFct.addFnAttr("nounwind");
86 ✗ freeFct.addParamAttr(0, "captures", 0l); // captures(none)
87 ✗ freeFct.addParamAttr(0, "noundef");
88 ✗ freeFct.addParamAttr(0, "readonly");
89 ✗ return freeFct;
90 ✗ }
91
92 68 public f<llvm::Function> StdFunctionManager.getMemcmpFct() {
93 68 Vector<llvm::Type> paramTypes;
94 68 paramTypes.pushBack(this.builder.getPtrTy());
95 68 paramTypes.pushBack(this.builder.getPtrTy());
96 68 paramTypes.pushBack(this.builder.getInt64Ty());
97 68 llvm::Function memcmpFct = this.getFunction("memcmp", this.builder.getInt32Ty(), paramTypes);
98 // Set attributes
99 68 memcmpFct.addFnAttr("nounwind");
100 68 memcmpFct.addParamAttr(0, "captures", 0l); // captures(none)
101 68 memcmpFct.addParamAttr(0, "noundef");
102 68 memcmpFct.addParamAttr(0, "readonly");
103 68 memcmpFct.addParamAttr(1, "captures", 0l); // captures(none)
104 68 memcmpFct.addParamAttr(1, "noundef");
105 68 memcmpFct.addParamAttr(1, "readonly");
106 68 memcmpFct.addParamAttr(2, "noundef");
107 68 memcmpFct.addRetAttr("noundef");
108 136 return memcmpFct;
109 68 }
110
111 2136 public f<llvm::Function> StdFunctionManager.getMemcpyIntrinsic() {
112 2136 Vector<llvm::Type> paramTypes;
113 2136 paramTypes.pushBack(this.builder.getPtrTy());
114 2136 paramTypes.pushBack(this.builder.getPtrTy());
115 2136 paramTypes.pushBack(this.builder.getInt64Ty());
116 2136 paramTypes.pushBack(this.builder.getInt1Ty());
117 2136 llvm::Function memcpyFct = this.getProcedure("llvm.memcpy.p0.p0.i64", paramTypes);
118 // Set attributes
119 2136 memcpyFct.addFnAttr("nocallback");
120 2136 memcpyFct.addFnAttr("nofree");
121 2136 memcpyFct.addFnAttr("nounwind");
122 2136 memcpyFct.addFnAttr("willreturn");
123 4272 return memcpyFct;
124 2136 }
125
126 99 public f<llvm::Function> StdFunctionManager.getStringGetRawLengthStringFct() {
127 99 model::ParamList paramLst;
128 99 paramLst.pushBack(model::Param{QualType(SuperType::TY_STRING), false});
129 99 const String mangledName = mangleStdFunction("getRawLength", QualType(SuperType::TY_LONG), paramLst);
130 99 Vector<llvm::Type> paramTypes;
131 99 paramTypes.pushBack(this.builder.getPtrTy());
132 198 return this.getFunction(mangledName.getRaw(), this.builder.getInt64Ty(), paramTypes);
133 99 }
134
135 529 public f<llvm::Function> StdFunctionManager.getStringIsRawEqualStringStringFct() {
136 529 model::ParamList paramLst;
137 529 paramLst.pushBack(model::Param{QualType(SuperType::TY_STRING), false});
138 529 paramLst.pushBack(model::Param{QualType(SuperType::TY_STRING), false});
139 529 const String mangledName = mangleStdFunction("isRawEqual", QualType(SuperType::TY_BOOL), paramLst);
140 529 Vector<llvm::Type> paramTypes;
141 529 paramTypes.pushBack(this.builder.getPtrTy());
142 529 paramTypes.pushBack(this.builder.getPtrTy());
143 1058 return this.getFunction(mangledName.getRaw(), this.builder.getInt1Ty(), paramTypes);
144 529 }
145
146 244 public f<llvm::Function> StdFunctionManager.getAllocUnsafeLongFct() {
147 244 QualType unsignedLong = QualType(SuperType::TY_LONG);
148 244 unsignedLong.makeUnsigned();
149 244 model::ParamList paramLst;
150 244 paramLst.pushBack(model::Param{unsignedLong, false});
151 244 const QualType bytePtrType = QualType(SuperType::TY_BYTE);
152 244 const String mangledName = mangleStdFunction("sAllocUnsafe", bytePtrType.toPtr(nil<ASTNode*>), paramLst);
153 244 Vector<llvm::Type> paramTypes;
154 244 paramTypes.pushBack(this.builder.getInt64Ty());
155 488 return this.getFunction(mangledName.getRaw(), this.builder.getPtrTy(), paramTypes);
156 244 }
157
158 324 public f<llvm::Function> StdFunctionManager.getDeallocBytePtrRefFct() {
159 324 const QualType byteType = QualType(SuperType::TY_BYTE);
160 324 QualType heapBytePtrType = byteType.toPtr(nil<ASTNode*>);
161 324 heapBytePtrType.makeHeap();
162 324 model::ParamList paramLst;
163 324 paramLst.pushBack(model::Param{heapBytePtrType.toRef(nil<ASTNode*>), false});
164 324 const String mangledName = mangleStdFunction("sDealloc", QualType(SuperType::TY_DYN), paramLst);
165 324 Vector<llvm::Type> paramTypes;
166 324 paramTypes.pushBack(this.builder.getPtrTy());
167 648 return this.getProcedure(mangledName.getRaw(), paramTypes);
168 324 }
169
170 7 public f<llvm::Function> StdFunctionManager.getIterateFct(const model::Function* spiceFunc) {
171 7 const String functionName = mangleFunction(*spiceFunc);
172 7 Vector<llvm::Type> paramTypes;
173 7 paramTypes.pushBack(this.builder.getPtrTy());
174 7 paramTypes.pushBack(this.builder.getInt64Ty());
175 14 return this.getSpiceFunction(functionName.getRaw(), spiceFunc.returnType, paramTypes);
176 7 }
177
178 393 public f<llvm::Function> StdFunctionManager.getIteratorFct(const model::Function* spiceFunc) {
179 393 const String functionName = mangleFunction(*spiceFunc);
180 393 Vector<llvm::Type> paramTypes;
181 393 paramTypes.pushBack(this.builder.getPtrTy());
182 786 return this.getSpiceFunction(functionName.getRaw(), spiceFunc.returnType, paramTypes);
183 393 }
184
185 408 public f<llvm::Function> StdFunctionManager.getIteratorGetFct(const model::Function* spiceFunc) {
186 408 const String functionName = mangleFunction(*spiceFunc);
187 408 Vector<llvm::Type> paramTypes;
188 408 paramTypes.pushBack(this.builder.getPtrTy());
189 816 return this.getFunction(functionName.getRaw(), this.builder.getPtrTy(), paramTypes);
190 408 }
191
192 30 public f<llvm::Function> StdFunctionManager.getIteratorGetIdxFct(const model::Function* spiceFunc) {
193 30 const String functionName = mangleFunction(*spiceFunc);
194 30 Vector<llvm::Type> paramTypes;
195 30 paramTypes.pushBack(this.builder.getPtrTy());
196 60 return this.getSpiceFunction(functionName.getRaw(), spiceFunc.returnType, paramTypes);
197 30 }
198
199 438 public f<llvm::Function> StdFunctionManager.getIteratorIsValidFct(const model::Function* spiceFunc) {
200 438 const String functionName = mangleFunction(*spiceFunc);
201 438 Vector<llvm::Type> paramTypes;
202 438 paramTypes.pushBack(this.builder.getPtrTy());
203 876 return this.getFunction(functionName.getRaw(), this.builder.getInt1Ty(), paramTypes);
204 438 }
205
206 438 public f<llvm::Function> StdFunctionManager.getIteratorNextFct(const model::Function* spiceFunc) {
207 438 const String functionName = mangleFunction(*spiceFunc);
208 438 Vector<llvm::Type> paramTypes;
209 438 paramTypes.pushBack(this.builder.getPtrTy());
210 876 return this.getProcedure(functionName.getRaw(), paramTypes);
211 438 }
212
213 93 public f<llvm::Function> StdFunctionManager.getResultIsErrFct(const model::Function* spiceFunc) {
214 93 const String functionName = mangleFunction(*spiceFunc);
215 93 Vector<llvm::Type> paramTypes;
216 93 paramTypes.pushBack(this.builder.getPtrTy());
217 186 return this.getFunction(functionName.getRaw(), this.builder.getInt1Ty(), paramTypes);
218 93 }
219
220 93 public f<llvm::Function> StdFunctionManager.getResultUnwrapFct(const model::Function* spiceFunc) {
221 93 const String functionName = mangleFunction(*spiceFunc);
222 93 Vector<llvm::Type> paramTypes;
223 93 paramTypes.pushBack(this.builder.getPtrTy());
224 186 return this.getFunction(functionName.getRaw(), this.builder.getPtrTy(), paramTypes);
225 93 }
226
227 93 public f<llvm::Function> StdFunctionManager.getResultGetErrFct(const model::Function* spiceFunc) {
228 93 const String functionName = mangleFunction(*spiceFunc);
229 93 Vector<llvm::Type> paramTypes;
230 93 paramTypes.pushBack(this.builder.getPtrTy());
231 186 return this.getFunction(functionName.getRaw(), this.builder.getPtrTy(), paramTypes);
232 93 }
233
234 93 public f<llvm::Function> StdFunctionManager.getResultErrCtorFct(const model::Function* spiceFunc) {
235 93 const String functionName = mangleFunction(*spiceFunc);
236 93 Vector<llvm::Type> paramTypes;
237 93 paramTypes.pushBack(this.builder.getPtrTy());
238 186 return this.getSpiceFunction(functionName.getRaw(), spiceFunc.returnType, paramTypes);
239 93 }
240
241 5 public f<llvm::Function> StdFunctionManager.getErrTraceResetFct() {
242 5 const String mangledName = mangleStdFunction("sErrTraceReset", QualType(SuperType::TY_DYN), getErrTraceParamList());
243 10 return this.getProcedure(mangledName.getRaw(), this.getErrTraceParamTypes());
244 5 }
245
246 3 public f<llvm::Function> StdFunctionManager.getErrTracePushFct() {
247 3 const String mangledName = mangleStdFunction("sErrTracePush", QualType(SuperType::TY_DYN), getErrTraceParamList());
248 6 return this.getProcedure(mangledName.getRaw(), this.getErrTraceParamTypes());
249 3 }
250
251 2933 public f<llvm::Function> StdFunctionManager.getErrTraceDumpFct() {
252 2933 const model::ParamList paramLst;
253 2933 const String mangledName = mangleStdFunction("sErrTraceDump", QualType(SuperType::TY_DYN), paramLst);
254 2933 const Vector<llvm::Type> paramTypes;
255 5866 return this.getProcedure(mangledName.getRaw(), paramTypes);
256 2933 }
257
258 ✗ public f<llvm::Function> StdFunctionManager.getDumpStacktraceFct() {
259 // Defaults are resolved by the time a function is mangled, so the params are not optional
260 ✗ model::ParamList paramLst;
261 ✗ paramLst.pushBack(model::Param{QualType(SuperType::TY_BOOL), false});
262 ✗ paramLst.pushBack(model::Param{QualType(SuperType::TY_BOOL), false});
263 ✗ const String mangledName = mangleStdFunction("sDumpStacktrace", QualType(SuperType::TY_DYN), paramLst);
264 ✗ Vector<llvm::Type> paramTypes;
265 ✗ paramTypes.pushBack(this.builder.getInt1Ty());
266 ✗ paramTypes.pushBack(this.builder.getInt1Ty());
267 ✗ return this.getProcedure(mangledName.getRaw(), paramTypes);
268 ✗ }
269
270 58 public f<llvm::Function> StdFunctionManager.getAcrtIOFuncFct() {
271 58 Vector<llvm::Type> paramTypes;
272 58 paramTypes.pushBack(this.builder.getInt32Ty());
273 58 llvm::Function stdErrPFct = this.getFunction("__acrt_iob_func", this.builder.getPtrTy(), paramTypes);
274 // ToDo: Set dso_local as soon as the LLVM C API allows it
275 // Set attributes
276 58 stdErrPFct.addFnAttr("nounwind");
277 58 stdErrPFct.addParamAttr(0, "noundef");
278 58 stdErrPFct.addRetAttr("noundef");
279 116 return stdErrPFct;
280 58 }
281
282 25818 f<llvm::Function> StdFunctionManager.getFunction(string funcName, llvm::Type returnType, const Vector<llvm::Type>& args, bool varArg = false) {
283 // Check if function already exists in the current module
284 25818 const llvm::Function opFct = this.module.getFunction(funcName);
285
4/4
✓ Branch 0 taken 18189 times.
✓ Branch 1 taken 5506 times.
✓ Branch 2 taken 1269 times.
✓ Branch 3 taken 854 times.
25818 if !opFct.isNull() {
286 19458 return opFct;
287 }
288
289 // Add function to the current module
290 6360 const llvm::Type opFctTy = llvm::getFunctionType(returnType, args, varArg);
291 6360 return this.module.getOrInsertFunction(funcName, opFctTy);
292 }
293
294 523 f<llvm::Function> StdFunctionManager.getSpiceFunction(string funcName, const QualType& returnType, const Vector<llvm::Type>& args) {
295 // Check if function already exists in the current module
296 523 const llvm::Function existingFct = this.module.getFunction(funcName);
297
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 243 times.
523 if !existingFct.isNull() {
298 280 return existingFct;
299 }
300
301 // Add function with the return value lowered to the calling convention of the target to the current module
302 243 llvm::Function fct = this.module.getOrInsertFunction(funcName, this.irGenerator.getFunctionType(returnType, args));
303 243 const ReturnABIInfo returnABI = this.irGenerator.getReturnABIInfo(returnType);
304
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 228 times.
243 if returnABI.isIndirect() {
305 228 this.irGenerator.addSRetParamAttrs(fct, returnABI.memoryType);
306 228 }
307 243 return fct;
308 }
309
310 13594 f<llvm::Function> StdFunctionManager.getProcedure(string procName, const Vector<llvm::Type>& args) {
311 13594 return this.getFunction(procName, this.builder.getVoidTy(), args, false);
312 }
313
314 8 f<Vector<llvm::Type>> StdFunctionManager.getErrTraceParamTypes() {
315 8 Vector<llvm::Type> paramTypes;
316 8 paramTypes.pushBack(this.builder.getPtrTy());
317 8 paramTypes.pushBack(this.builder.getPtrTy());
318 8 paramTypes.pushBack(this.builder.getInt32Ty());
319 8 paramTypes.pushBack(this.builder.getInt32Ty());
320 8 return paramTypes;
321 }
322
323 /**
324 * Build the parameter list (string, string, unsigned int, unsigned int) of the error trace runtime functions
325 *
326 * @return Parameter list
327 */
328 8 f<model::ParamList> getErrTraceParamList() {
329 8 QualType unsignedInt = QualType(SuperType::TY_INT);
330 8 unsignedInt.makeUnsigned();
331 8 model::ParamList paramLst;
332 8 paramLst.pushBack(model::Param{QualType(SuperType::TY_STRING), false});
333 8 paramLst.pushBack(model::Param{QualType(SuperType::TY_STRING), false});
334 8 paramLst.pushBack(model::Param{unsignedInt, false});
335 8 paramLst.pushBack(model::Param{unsignedInt, false});
336 8 return paramLst;
337 }
338
339 /**
340 * Mangle a runtime function, that is known by its name and signature only
341 *
342 * @param name Function name
343 * @param returnType Return type
344 * @param paramLst Parameter list
345 * @return Mangled name
346 */
347 4137 f<String> mangleStdFunction(string name, const QualType& returnType, const model::ParamList& paramLst) {
348 4137 const Vector<GenericType> noTemplateTypes;
349 4137 const model::Function function = model::Function(String(name), nil<SymbolTableEntry*>, QualType(SuperType::TY_DYN), returnType, paramLst, noTemplateTypes, nil<ASTNode*>);
350 8274 return mangleFunction(function);
351 4137 }
352