Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2021-2024 ChilliBits. All rights reserved. | ||
2 | |||
3 | #include "IRGenerator.h" | ||
4 | |||
5 | #include <ast/ASTNodes.h> | ||
6 | #include <llvm/IR/InlineAsm.h> | ||
7 | |||
8 | #include <llvm/IR/Module.h> | ||
9 | |||
10 | namespace spice::compiler { | ||
11 | |||
12 | 1093 | std::any IRGenerator::visitBuiltinCall(const BuiltinCallNode *node) { | |
13 |
2/2✓ Branch 0 taken 579 times.
✓ Branch 1 taken 514 times.
|
1093 | if (node->printfCall) |
14 | 579 | return visit(node->printfCall); | |
15 |
2/2✓ Branch 0 taken 119 times.
✓ Branch 1 taken 395 times.
|
514 | if (node->sizeofCall) |
16 | 119 | return visit(node->sizeofCall); | |
17 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 384 times.
|
395 | if (node->alignofCall) |
18 | 11 | return visit(node->alignofCall); | |
19 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 349 times.
|
384 | if (node->lenCall) |
20 | 35 | return visit(node->lenCall); | |
21 |
2/2✓ Branch 0 taken 348 times.
✓ Branch 1 taken 1 times.
|
349 | if (node->panicCall) |
22 | 348 | return visit(node->panicCall); | |
23 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (node->sysCall) |
24 | 1 | return visit(node->sysCall); | |
25 | ✗ | assert_fail("Unknown builtin call"); | |
26 | return nullptr; | ||
27 | } | ||
28 | |||
29 | 579 | std::any IRGenerator::visitPrintfCall(const PrintfCallNode *node) { | |
30 | // Retrieve printf function | ||
31 |
1/2✓ Branch 1 taken 579 times.
✗ Branch 2 not taken.
|
579 | llvm::Function *printfFct = stdFunctionManager.getPrintfFct(); |
32 | |||
33 | // Push the template string as first argument | ||
34 | 579 | std::vector<llvm::Value *> printfArgs; | |
35 |
2/4✓ Branch 1 taken 579 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 579 times.
✗ Branch 5 not taken.
|
1158 | llvm::Constant *templateString = createGlobalStringConst("printf.str.", node->templatedString, node->codeLoc); |
36 |
1/2✓ Branch 1 taken 579 times.
✗ Branch 2 not taken.
|
579 | printfArgs.push_back(templateString); |
37 | |||
38 | // Collect replacement arguments | ||
39 |
2/2✓ Branch 5 taken 489 times.
✓ Branch 6 taken 579 times.
|
1068 | for (const AssignExprNode *arg : node->args) { |
40 | // Retrieve type of argument | ||
41 |
1/2✓ Branch 1 taken 489 times.
✗ Branch 2 not taken.
|
489 | const QualType argSymbolType = arg->getEvaluatedSymbolType(manIdx); |
42 | |||
43 | // Re-map some values | ||
44 | llvm::Value *argVal; | ||
45 |
2/4✓ Branch 1 taken 489 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 489 times.
|
489 | if (argSymbolType.isArray()) { |
46 | ✗ | llvm::Value *argValPtr = resolveAddress(arg); | |
47 | ✗ | llvm::Value *indices[2] = {builder.getInt64(0), builder.getInt32(0)}; | |
48 | ✗ | llvm::Type *argType = argSymbolType.toLLVMType(sourceFile); | |
49 | ✗ | argVal = insertInBoundsGEP(argType, argValPtr, indices); | |
50 |
4/6✓ Branch 1 taken 489 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 489 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 27 times.
✓ Branch 7 taken 462 times.
|
489 | } else if (argSymbolType.getBase().isStringObj()) { |
51 |
1/2✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
|
27 | llvm::Value *argValPtr = resolveAddress(arg); |
52 |
2/4✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
|
27 | llvm::Type *argBaseType = argSymbolType.getBase().toLLVMType(sourceFile); |
53 |
2/4✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
|
54 | argValPtr = insertStructGEP(argBaseType, argValPtr, 0); |
54 |
3/6✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 27 times.
✗ Branch 8 not taken.
|
54 | argVal = insertLoad(builder.getPtrTy(), argValPtr); |
55 | } else { | ||
56 |
1/2✓ Branch 1 taken 462 times.
✗ Branch 2 not taken.
|
462 | argVal = resolveValue(arg); |
57 | } | ||
58 | |||
59 | // Extend all integer types lower than 32 bit to 32 bit | ||
60 |
4/6✓ Branch 1 taken 489 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 489 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 77 times.
✓ Branch 7 taken 412 times.
|
489 | if (argSymbolType.removeReferenceWrapper().isOneOf({TY_SHORT, TY_BYTE, TY_CHAR, TY_BOOL})) |
61 |
5/10✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 77 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 77 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 77 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 77 times.
✗ Branch 14 not taken.
|
77 | argVal = builder.CreateIntCast(argVal, builder.getInt32Ty(), argSymbolType.removeReferenceWrapper().isSigned()); |
62 | |||
63 |
1/2✓ Branch 1 taken 489 times.
✗ Branch 2 not taken.
|
489 | printfArgs.push_back(argVal); |
64 | } | ||
65 | |||
66 | // Call printf function | ||
67 |
3/6✓ Branch 1 taken 579 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 579 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 579 times.
✗ Branch 9 not taken.
|
579 | llvm::CallInst *returnValue = builder.CreateCall(printfFct, printfArgs); |
68 | |||
69 | // Add noundef attribute to template string | ||
70 |
1/2✓ Branch 1 taken 579 times.
✗ Branch 2 not taken.
|
579 | returnValue->addParamAttr(0, llvm::Attribute::NoUndef); |
71 | |||
72 |
1/2✓ Branch 1 taken 579 times.
✗ Branch 2 not taken.
|
1158 | return LLVMExprResult{.value = returnValue}; |
73 | 579 | } | |
74 | |||
75 | 119 | std::any IRGenerator::visitSizeofCall(const SizeofCallNode *node) { | |
76 | llvm::Type *type; | ||
77 |
2/2✓ Branch 0 taken 102 times.
✓ Branch 1 taken 17 times.
|
119 | if (node->isType) { // Size of type |
78 |
2/4✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 102 times.
✗ Branch 5 not taken.
|
102 | type = any_cast<llvm::Type *>(visit(node->dataType)); |
79 | } else { // Size of value | ||
80 |
2/4✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
|
17 | type = node->assignExpr->getEvaluatedSymbolType(manIdx).toLLVMType(sourceFile); |
81 | } | ||
82 | // Calculate size at compile-time | ||
83 |
1/2✓ Branch 2 taken 119 times.
✗ Branch 3 not taken.
|
119 | const llvm::TypeSize sizeInBits = module->getDataLayout().getTypeSizeInBits(type); |
84 | |||
85 | // Return size value | ||
86 |
2/4✓ Branch 1 taken 119 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 119 times.
✗ Branch 5 not taken.
|
119 | llvm::Value *sizeValue = builder.getInt64(sizeInBits); |
87 |
1/2✓ Branch 1 taken 119 times.
✗ Branch 2 not taken.
|
238 | return LLVMExprResult{.value = sizeValue}; |
88 | } | ||
89 | |||
90 | 11 | std::any IRGenerator::visitAlignofCall(const AlignofCallNode *node) { | |
91 | llvm::Type *type; | ||
92 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
|
11 | if (node->isType) { // Align of type |
93 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | type = any_cast<llvm::Type *>(visit(node->dataType)); |
94 | } else { // Align of value | ||
95 |
2/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
|
10 | type = node->assignExpr->getEvaluatedSymbolType(manIdx).toLLVMType(sourceFile); |
96 | } | ||
97 | // Calculate size at compile-time | ||
98 |
1/2✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | const llvm::Align align = module->getDataLayout().getABITypeAlign(type); |
99 | |||
100 | // Return align value | ||
101 |
1/2✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | llvm::Value *sizeValue = builder.getInt64(align.value()); |
102 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
22 | return LLVMExprResult{.value = sizeValue}; |
103 | } | ||
104 | |||
105 | 35 | std::any IRGenerator::visitLenCall(const LenCallNode *node) { | |
106 | // Check if the length is fixed and known via the symbol type | ||
107 |
1/2✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
|
35 | QualType symbolType = node->assignExpr->getEvaluatedSymbolType(manIdx); |
108 |
1/2✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
|
35 | symbolType = symbolType.removeReferenceWrapper(); |
109 | |||
110 | llvm::Value *lengthValue; | ||
111 |
3/4✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
✓ Branch 4 taken 12 times.
|
35 | if (symbolType.is(TY_STRING)) { |
112 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
23 | llvm::Function *getRawLengthFct = stdFunctionManager.getStringGetRawLengthStringFct(); |
113 |
4/8✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 23 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 23 times.
✗ Branch 12 not taken.
|
23 | lengthValue = builder.CreateCall(getRawLengthFct, resolveValue(node->assignExpr)); |
114 | } else { | ||
115 |
4/8✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
|
12 | assert(symbolType.isArray() && symbolType.getArraySize() != ARRAY_SIZE_UNKNOWN); |
116 | // Return length value | ||
117 |
2/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
|
12 | lengthValue = builder.getInt64(symbolType.getArraySize()); |
118 | } | ||
119 |
1/2✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
|
70 | return LLVMExprResult{.value = lengthValue}; |
120 | } | ||
121 | |||
122 | 348 | std::any IRGenerator::visitPanicCall(const PanicCallNode *node) { | |
123 | // Create constant for error message | ||
124 |
1/2✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
|
348 | const std::string codeLoc = node->codeLoc.toPrettyString(); |
125 |
5/10✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 348 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 348 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 348 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 348 times.
✗ Branch 14 not taken.
|
348 | const std::string errorMsg = "Program panicked at " + codeLoc + ":\n" + node->getErrorMessage() + "\n"; |
126 |
4/8✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 348 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 348 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 348 times.
✗ Branch 12 not taken.
|
696 | llvm::Constant *globalString = builder.CreateGlobalStringPtr(errorMsg, getUnusedGlobalName(ANON_GLOBAL_STRING_NAME)); |
127 | // Print the error message | ||
128 |
1/2✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
|
348 | llvm::Function *printfFct = stdFunctionManager.getPrintfFct(); |
129 |
3/6✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 348 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 348 times.
✗ Branch 9 not taken.
|
348 | builder.CreateCall(printfFct, globalString); |
130 | // Generate call to exit() | ||
131 |
1/2✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
|
348 | llvm::Function *exitFct = stdFunctionManager.getExitFct(); |
132 |
4/8✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 348 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 348 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 348 times.
✗ Branch 12 not taken.
|
348 | builder.CreateCall(exitFct, builder.getInt32(EXIT_FAILURE)); |
133 | // Create unreachable instruction | ||
134 |
1/2✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
|
348 | builder.CreateUnreachable(); |
135 | // Unreachable counts as terminator | ||
136 |
2/4✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 348 times.
✗ Branch 5 not taken.
|
348 | terminateBlock(node->getNextOuterStmtLst()); |
137 | |||
138 |
1/2✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
|
696 | return nullptr; |
139 | 348 | } | |
140 | |||
141 | 1 | std::any IRGenerator::visitSysCall(const SysCallNode *node) { | |
142 | // Create assembly string | ||
143 | static constexpr uint8_t NUM_REGS = 7; | ||
144 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | const char *asmString = getSysCallAsmString(); |
145 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | const char *constraints = getSysCallConstraintString(); |
146 | |||
147 | // Create inline assembly | ||
148 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | llvm::Type *int64Ty = builder.getInt64Ty(); |
149 | 1 | llvm::Type *argTypes[NUM_REGS] = {int64Ty, int64Ty, int64Ty, int64Ty, int64Ty, int64Ty, int64Ty}; | |
150 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | llvm::FunctionType *fctType = llvm::FunctionType::get(builder.getVoidTy(), argTypes, false); |
151 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
1 | llvm::InlineAsm *inlineAsm = llvm::InlineAsm::get(fctType, asmString, constraints, true); |
152 | |||
153 | // Fill arguments array (first argument is syscall number) | ||
154 | llvm::Value *argValues[NUM_REGS]; | ||
155 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
|
8 | for (unsigned short i = 0; i < NUM_REGS; i++) { |
156 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 3 times.
|
7 | if (i < node->args.size()) { |
157 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | const AssignExprNode *argNode = node->args.at(i); |
158 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | const QualType &argType = argNode->getEvaluatedSymbolType(manIdx); |
159 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
|
4 | assert(argType.isOneOf({TY_INT, TY_LONG, TY_SHORT, TY_BOOL, TY_BYTE, TY_PTR, TY_STRING})); |
160 |
3/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 3 times.
|
4 | if (argType.isOneOf({TY_PTR, TY_STRING})) |
161 |
4/8✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
|
1 | argValues[i] = builder.CreatePtrToInt(resolveValue(argNode), builder.getInt64Ty()); |
162 | else | ||
163 |
4/8✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
|
3 | argValues[i] = builder.CreateZExt(resolveValue(argNode), builder.getInt64Ty()); |
164 | } else { | ||
165 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | argValues[i] = builder.getInt64(0); |
166 | } | ||
167 | } | ||
168 | |||
169 | // Generate call | ||
170 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
|
1 | llvm::Value *result = builder.CreateCall(inlineAsm, argValues); |
171 | |||
172 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | return LLVMExprResult{.value = result}; |
173 | } | ||
174 | |||
175 | } // namespace spice::compiler | ||
176 |