Skip to content

Commit f26e8cf

Browse files
committed
LLVMCodeBuilder: Implement if statements
1 parent a6de26e commit f26e8cf

File tree

5 files changed

+267
-1
lines changed

5 files changed

+267
-1
lines changed

src/dev/engine/internal/llvmcodebuilder.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
3838
{
3939
size_t functionIndex = 0;
4040
llvm::Function *currentFunc = beginFunction(functionIndex);
41+
std::vector<IfStatement> ifStatements;
4142

4243
// Execute recorded steps
4344
for (const Step &step : m_steps) {
@@ -71,6 +72,68 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
7172
endFunction(currentFunc, functionIndex);
7273
currentFunc = beginFunction(++functionIndex);
7374
break;
75+
76+
case Step::Type::BeginIf: {
77+
IfStatement statement;
78+
statement.beforeIf = m_builder.GetInsertBlock();
79+
statement.body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
80+
81+
// Convert last reg to bool
82+
assert(step.args.size() == 1);
83+
statement.condition = m_builder.CreateCall(resolve_value_toBool(), step.args[0]->value);
84+
85+
// Switch to body branch
86+
m_builder.SetInsertPoint(statement.body);
87+
88+
ifStatements.push_back(statement);
89+
break;
90+
}
91+
92+
case Step::Type::BeginElse: {
93+
assert(!ifStatements.empty());
94+
IfStatement &statement = ifStatements.back();
95+
96+
// Jump to the branch after the if statement
97+
assert(!statement.afterIf);
98+
statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
99+
m_builder.CreateBr(statement.afterIf);
100+
101+
// Create else branch
102+
assert(!statement.elseBranch);
103+
statement.elseBranch = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
104+
105+
// Since there's an else branch, the conditional instruction should jump to it
106+
m_builder.SetInsertPoint(statement.beforeIf);
107+
m_builder.CreateCondBr(statement.condition, statement.body, statement.elseBranch);
108+
109+
// Switch to the else branch
110+
m_builder.SetInsertPoint(statement.elseBranch);
111+
break;
112+
}
113+
114+
case Step::Type::EndIf: {
115+
assert(!ifStatements.empty());
116+
IfStatement &statement = ifStatements.back();
117+
118+
// Jump to the branch after the if statement
119+
if (!statement.afterIf)
120+
statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
121+
122+
m_builder.CreateBr(statement.afterIf);
123+
124+
if (statement.elseBranch) {
125+
} else {
126+
// If there wasn't an 'else' branch, create a conditional instruction which skips the if statement if false
127+
m_builder.SetInsertPoint(statement.beforeIf);
128+
m_builder.CreateCondBr(statement.condition, statement.body, statement.afterIf);
129+
}
130+
131+
// Switch to the branch after the if statement
132+
m_builder.SetInsertPoint(statement.afterIf);
133+
134+
ifStatements.pop_back();
135+
break;
136+
}
74137
}
75138
}
76139

@@ -140,14 +203,21 @@ void LLVMCodeBuilder::addListContents(List *list)
140203

141204
void LLVMCodeBuilder::beginIfStatement()
142205
{
206+
Step step(Step::Type::BeginIf);
207+
assert(!m_tmpRegs.empty());
208+
step.args.push_back(m_tmpRegs.back());
209+
m_tmpRegs.pop_back();
210+
m_steps.push_back(step);
143211
}
144212

145213
void LLVMCodeBuilder::beginElseBranch()
146214
{
215+
m_steps.push_back(Step(Step::Type::BeginElse));
147216
}
148217

149218
void LLVMCodeBuilder::endIf()
150219
{
220+
m_steps.push_back(Step(Step::Type::EndIf));
151221
}
152222

153223
void LLVMCodeBuilder::beginLoop()
@@ -289,3 +359,8 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_special()
289359
{
290360
return resolveFunction("value_assign_special", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt32Ty() }, false));
291361
}
362+
363+
llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toBool()
364+
{
365+
return resolveFunction("value_toBool", llvm::FunctionType::get(m_builder.getInt1Ty(), m_valueDataType->getPointerTo(), false));
366+
}

src/dev/engine/internal/llvmcodebuilder.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ class LLVMCodeBuilder : public ICodeBuilder
4949
enum class Type
5050
{
5151
FunctionCall,
52-
Yield
52+
Yield,
53+
BeginIf,
54+
BeginElse,
55+
EndIf
5356
};
5457

5558
Step(Type type) :
@@ -64,6 +67,15 @@ class LLVMCodeBuilder : public ICodeBuilder
6467
size_t functionReturnRegIndex = 0;
6568
};
6669

70+
struct IfStatement
71+
{
72+
llvm::Value *condition = nullptr;
73+
llvm::BasicBlock *beforeIf = nullptr;
74+
llvm::BasicBlock *body = nullptr;
75+
llvm::BasicBlock *elseBranch = nullptr;
76+
llvm::BasicBlock *afterIf = nullptr;
77+
};
78+
6779
void initTypes();
6880
llvm::Function *beginFunction(size_t index);
6981
void endFunction(llvm::Function *func, size_t index);
@@ -76,6 +88,7 @@ class LLVMCodeBuilder : public ICodeBuilder
7688
llvm::FunctionCallee resolve_value_assign_bool();
7789
llvm::FunctionCallee resolve_value_assign_cstring();
7890
llvm::FunctionCallee resolve_value_assign_special();
91+
llvm::FunctionCallee resolve_value_toBool();
7992

8093
std::string m_id;
8194
llvm::LLVMContext m_ctx;

test/dev/llvm/llvmcodebuilder_test.cpp

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
using namespace libscratchcpp;
1010

11+
using ::testing::Return;
12+
1113
class LLVMCodeBuilderTest : public testing::Test
1214
{
1315
public:
@@ -103,3 +105,172 @@ TEST_F(LLVMCodeBuilderTest, Yield)
103105
ASSERT_EQ(testing::internal::GetCapturedStdout(), expected2);
104106
ASSERT_TRUE(code->isFinished(ctx.get()));
105107
}
108+
109+
TEST_F(LLVMCodeBuilderTest, IfStatement)
110+
{
111+
// Without else branch (const condition)
112+
m_builder->addConstValue("true");
113+
m_builder->beginIfStatement();
114+
m_builder->addFunctionCall("test_function_no_args", 0, false);
115+
m_builder->endIf();
116+
117+
m_builder->addConstValue("false");
118+
m_builder->beginIfStatement();
119+
m_builder->addFunctionCall("test_function_no_args", 0, false);
120+
m_builder->endIf();
121+
122+
// Without else branch (condition returned by function)
123+
m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
124+
m_builder->addConstValue("no_args_output");
125+
m_builder->addFunctionCall("test_equals", 2, true);
126+
m_builder->beginIfStatement();
127+
m_builder->addConstValue(0);
128+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
129+
m_builder->endIf();
130+
131+
m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
132+
m_builder->addConstValue("");
133+
m_builder->addFunctionCall("test_equals", 2, true);
134+
m_builder->beginIfStatement();
135+
m_builder->addConstValue(1);
136+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
137+
m_builder->endIf();
138+
139+
// With else branch (const condition)
140+
m_builder->addConstValue("true");
141+
m_builder->beginIfStatement();
142+
m_builder->addConstValue(2);
143+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
144+
m_builder->beginElseBranch();
145+
m_builder->addConstValue(3);
146+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
147+
m_builder->endIf();
148+
149+
m_builder->addConstValue("false");
150+
m_builder->beginIfStatement();
151+
m_builder->addConstValue(4);
152+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
153+
m_builder->beginElseBranch();
154+
m_builder->addConstValue(5);
155+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
156+
m_builder->endIf();
157+
158+
// With else branch (condition returned by function)
159+
m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
160+
m_builder->addConstValue("no_args_output");
161+
m_builder->addFunctionCall("test_equals", 2, true);
162+
m_builder->beginIfStatement();
163+
m_builder->addConstValue(6);
164+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
165+
m_builder->beginElseBranch();
166+
m_builder->addConstValue(7);
167+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
168+
m_builder->endIf();
169+
170+
m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
171+
m_builder->addConstValue("");
172+
m_builder->addFunctionCall("test_equals", 2, true);
173+
m_builder->beginIfStatement();
174+
m_builder->addConstValue(8);
175+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
176+
m_builder->beginElseBranch();
177+
m_builder->addConstValue(9);
178+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
179+
m_builder->endIf();
180+
181+
// Nested 1
182+
m_builder->addConstValue(true);
183+
m_builder->beginIfStatement();
184+
{
185+
m_builder->addConstValue(false);
186+
m_builder->beginIfStatement();
187+
{
188+
m_builder->addConstValue(0);
189+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
190+
}
191+
m_builder->beginElseBranch();
192+
{
193+
m_builder->addConstValue(1);
194+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
195+
196+
m_builder->addConstValue(false);
197+
m_builder->beginIfStatement();
198+
m_builder->beginElseBranch();
199+
{
200+
m_builder->addConstValue(2);
201+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
202+
}
203+
m_builder->endIf();
204+
}
205+
m_builder->endIf();
206+
}
207+
m_builder->beginElseBranch();
208+
{
209+
m_builder->addConstValue(true);
210+
m_builder->beginIfStatement();
211+
{
212+
m_builder->addConstValue(3);
213+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
214+
}
215+
m_builder->beginElseBranch();
216+
{
217+
m_builder->addConstValue(4);
218+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
219+
}
220+
m_builder->endIf();
221+
}
222+
m_builder->endIf();
223+
224+
// Nested 2
225+
m_builder->addConstValue(false);
226+
m_builder->beginIfStatement();
227+
{
228+
m_builder->addConstValue(false);
229+
m_builder->beginIfStatement();
230+
{
231+
m_builder->addConstValue(5);
232+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
233+
}
234+
m_builder->beginElseBranch();
235+
{
236+
m_builder->addConstValue(6);
237+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
238+
}
239+
m_builder->endIf();
240+
}
241+
m_builder->beginElseBranch();
242+
{
243+
m_builder->addConstValue(true);
244+
m_builder->beginIfStatement();
245+
{
246+
m_builder->addConstValue(7);
247+
m_builder->addFunctionCall("test_function_1_arg", 1, false);
248+
}
249+
m_builder->beginElseBranch();
250+
m_builder->endIf();
251+
}
252+
m_builder->endIf();
253+
254+
auto code = m_builder->finalize();
255+
auto ctx = code->createExecutionContext(&m_target);
256+
257+
static const std::string expected =
258+
"no_args\n"
259+
"no_args_ret\n"
260+
"1_arg 0\n"
261+
"no_args_ret\n"
262+
"1_arg 2\n"
263+
"1_arg 5\n"
264+
"no_args_ret\n"
265+
"1_arg 6\n"
266+
"no_args_ret\n"
267+
"1_arg 9\n"
268+
"1_arg 1\n"
269+
"1_arg 2\n"
270+
"1_arg 7\n";
271+
272+
EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false));
273+
testing::internal::CaptureStdout();
274+
code->run(ctx.get());
275+
ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
276+
}

test/dev/llvm/testfunctions.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,9 @@ extern "C"
7373
std::cout << "3_args " << s1 << " " << s2 << " " << s3 << std::endl;
7474
value_assign_cstring(ret, "3_args_output");
7575
}
76+
77+
void test_equals(Target *target, ValueData *ret, ValueData *a, ValueData *b)
78+
{
79+
value_assign_bool(ret, value_equals(a, b));
80+
}
7681
}

test/dev/llvm/testfunctions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ extern "C"
1818
void test_function_1_arg_ret(Target *target, ValueData *ret, const ValueData *arg1);
1919
void test_function_3_args(Target *target, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3);
2020
void test_function_3_args_ret(Target *target, ValueData *ret, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3);
21+
22+
void test_equals(Target *target, ValueData *ret, ValueData *a, ValueData *b);
2123
}
2224

2325
} // namespace libscratchcpp

0 commit comments

Comments
 (0)