Skip to content

Commit f362d40

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

File tree

5 files changed

+248
-1
lines changed

5 files changed

+248
-1
lines changed

src/dev/engine/internal/llvmcodebuilder.cpp

Lines changed: 65 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<std::pair<std::pair<llvm::BasicBlock *, llvm::BasicBlock *>, bool>> ifStatements; // else branch, branch after if statement, has else branch?
4142

4243
// Execute recorded steps
4344
for (const Step &step : m_steps) {
@@ -71,6 +72,58 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
7172
endFunction(currentFunc, functionIndex);
7273
currentFunc = beginFunction(++functionIndex);
7374
break;
75+
76+
case Step::Type::BeginIf: {
77+
// Create branches
78+
llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
79+
llvm::BasicBlock *elseBody = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
80+
llvm::BasicBlock *afterIf = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
81+
82+
// Convert last reg to bool
83+
assert(step.args.size() == 1);
84+
llvm::Value *ret = m_builder.CreateCall(resolve_value_toBool(), step.args[0]->value);
85+
86+
// Add conditional break and switch to body branch
87+
m_builder.CreateCondBr(ret, body, elseBody);
88+
m_builder.SetInsertPoint(body);
89+
90+
ifStatements.push_back({ { elseBody, afterIf }, false });
91+
break;
92+
}
93+
94+
case Step::Type::BeginElse: {
95+
// Jump to the branch after the if statement
96+
assert(!ifStatements.empty());
97+
auto &info = ifStatements.back();
98+
m_builder.CreateBr(info.first.second);
99+
100+
// There is an else branch
101+
info.second = true;
102+
103+
// Switch to the else branch
104+
m_builder.SetInsertPoint(info.first.first);
105+
break;
106+
}
107+
108+
case Step::Type::EndIf: {
109+
// Jump to the branch after the if statement
110+
assert(!ifStatements.empty());
111+
const auto &info = ifStatements.back();
112+
llvm::BasicBlock *afterIf = info.first.second;
113+
m_builder.CreateBr(afterIf);
114+
115+
// If there wasn't an 'else' branch, add a break to the empty branch
116+
if (!info.second) {
117+
m_builder.SetInsertPoint(info.first.first);
118+
m_builder.CreateBr(afterIf);
119+
}
120+
121+
// Switch to the branch after the if statement
122+
m_builder.SetInsertPoint(afterIf);
123+
124+
ifStatements.pop_back();
125+
break;
126+
}
74127
}
75128
}
76129

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

141194
void LLVMCodeBuilder::beginIfStatement()
142195
{
196+
Step step(Step::Type::BeginIf);
197+
assert(!m_tmpRegs.empty());
198+
step.args.push_back(m_tmpRegs.back());
199+
m_tmpRegs.pop_back();
200+
m_steps.push_back(step);
143201
}
144202

145203
void LLVMCodeBuilder::beginElseBranch()
146204
{
205+
m_steps.push_back(Step(Step::Type::BeginElse));
147206
}
148207

149208
void LLVMCodeBuilder::endIf()
150209
{
210+
m_steps.push_back(Step(Step::Type::EndIf));
151211
}
152212

153213
void LLVMCodeBuilder::beginLoop()
@@ -289,3 +349,8 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_special()
289349
{
290350
return resolveFunction("value_assign_special", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt32Ty() }, false));
291351
}
352+
353+
llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toBool()
354+
{
355+
return resolveFunction("value_toBool", llvm::FunctionType::get(m_builder.getInt1Ty(), m_valueDataType->getPointerTo(), false));
356+
}

src/dev/engine/internal/llvmcodebuilder.h

Lines changed: 5 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) :
@@ -76,6 +79,7 @@ class LLVMCodeBuilder : public ICodeBuilder
7679
llvm::FunctionCallee resolve_value_assign_bool();
7780
llvm::FunctionCallee resolve_value_assign_cstring();
7881
llvm::FunctionCallee resolve_value_assign_special();
82+
llvm::FunctionCallee resolve_value_toBool();
7983

8084
std::string m_id;
8185
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)