Skip to content

Commit ba0f983

Browse files
committed
[Tolk] Optimization: generate IFJMP inside match arms
Then `match` is the last statement, or is immediately followed by `return`, generate not `IF...ELSE` to Fift output, but `IFJMP...`. Same for `if/else` statement.
1 parent 3f4eca4 commit ba0f983

File tree

2 files changed

+208
-5
lines changed

2 files changed

+208
-5
lines changed

tolk-tester/tests/if-else-tests.tolk

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
global t: tuple;
2+
13
@method_id(101)
24
fun test1(x: int): int {
35
if (x > 200) {
@@ -100,6 +102,86 @@ fun test8(op: int) {
100102
return (withNoElse(op), withElse(op), withMatch(op));
101103
}
102104

105+
fun demo9_1(x: int) {
106+
if (x > 0) { t.push(123); }
107+
else { t.push(456) }
108+
}
109+
110+
fun demo9_2(x: int) {
111+
if (x > 0) { t.push(123); }
112+
}
113+
114+
fun demo9_3(x: int) {
115+
if (x > 0) { t.push(123); }
116+
return;
117+
}
118+
119+
fun demo9_4(x: int) {
120+
if (x != -100) {
121+
if (x > 0) { t.push(123); }
122+
return;
123+
}
124+
}
125+
126+
@method_id(109)
127+
fun test9(x: int) {
128+
t = createEmptyTuple();
129+
demo9_1(x); demo9_2(x); demo9_3(x); demo9_4(x);
130+
return t;
131+
}
132+
133+
fun demo10_1(x: int) {
134+
match(x) {
135+
0 => t.push(123),
136+
1 => t.push(456),
137+
}
138+
}
139+
140+
fun demo10_2(x: int) {
141+
if (x != -100) {
142+
match (x) {
143+
0 => t.push(123),
144+
else => t.push(456)
145+
}
146+
return;
147+
}
148+
t.push(789)
149+
}
150+
151+
@method_id(110)
152+
fun test10(x: int) {
153+
t = createEmptyTuple();
154+
demo10_1(x); demo10_2(x);
155+
return t;
156+
}
157+
158+
fun demo_neg_11_1(mutate x: int) {
159+
match (x) {
160+
-1 => { x = 0; }
161+
else => { x = 1; }
162+
}
163+
}
164+
165+
fun justVoidPush() {
166+
t.push(100);
167+
}
168+
169+
fun demo_neg_11_2(x: int) {
170+
match (x) {
171+
-1 => t.push(123),
172+
else => t.push(456)
173+
}
174+
return justVoidPush();
175+
}
176+
177+
@method_id(111)
178+
fun test11(x: int) {
179+
t = createEmptyTuple();
180+
demo_neg_11_1(mutate x);
181+
demo_neg_11_2(x);
182+
return (x, t);
183+
}
184+
103185
fun main() {
104186

105187
}
@@ -125,6 +207,11 @@ fun main() {
125207
@testcase | 106 | 5 | 5000
126208
@testcase | 108 | 123 | 100 100 100
127209
@testcase | 108 | 345 | 300 300 300
210+
@testcase | 109 | 10 | [ 123 123 123 123 ]
211+
@testcase | 109 | -10 | [ 456 ]
212+
@testcase | 110 | 0 | [ 123 123 ]
213+
@testcase | 110 | 5 | [ 456 ]
214+
@testcase | 111 | 5 | 1 [ 456 100 ]
128215

129216
@fif_codegen
130217
"""
@@ -214,4 +301,88 @@ fun main() {
214301
255 THROW
215302
}>
216303
"""
304+
305+
@fif_codegen
306+
"""
307+
demo9_1 PROC:<{
308+
0 GTINT
309+
IFJMP:<{
310+
t GETGLOB
311+
123 PUSHINT
312+
TPUSH
313+
t SETGLOB
314+
}>
315+
t GETGLOB
316+
456 PUSHINT
317+
TPUSH
318+
t SETGLOB
319+
}>
320+
"""
321+
322+
@fif_codegen
323+
"""
324+
demo9_2 PROC:<{
325+
0 GTINT
326+
IFJMP:<{
327+
t GETGLOB
328+
123 PUSHINT
329+
TPUSH
330+
t SETGLOB
331+
}>
332+
}>
333+
"""
334+
335+
@fif_codegen
336+
"""
337+
demo9_3 PROC:<{
338+
0 GTINT
339+
IFJMP:<{
340+
t GETGLOB
341+
123 PUSHINT
342+
TPUSH
343+
t SETGLOB
344+
}>
345+
}>
346+
"""
347+
348+
@fif_codegen
349+
"""
350+
demo9_4 PROC:<{
351+
DUP
352+
-100 NEQINT
353+
IFJMP:<{
354+
0 GTINT
355+
IFJMP:<{
356+
t GETGLOB
357+
123 PUSHINT
358+
TPUSH
359+
t SETGLOB
360+
}>
361+
}>
362+
DROP
363+
}>
364+
"""
365+
366+
@fif_codegen
367+
"""
368+
demo10_1 PROC:<{
369+
DUP
370+
0 EQINT
371+
IFJMP:<{
372+
DROP
373+
t GETGLOB
374+
123 PUSHINT
375+
TPUSH
376+
t SETGLOB
377+
}>
378+
1 EQINT
379+
IFJMP:<{
380+
t GETGLOB
381+
456 PUSHINT
382+
TPUSH
383+
t SETGLOB
384+
}>
385+
}>
386+
"""
387+
217388
*/

tolk/pipe-ast-to-legacy.cpp

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ std::vector<var_idx_t> pre_compile_expr(AnyExprV v, CodeBlob& code, TypePtr targ
5454
std::vector<var_idx_t> pre_compile_symbol(SrcLocation loc, const Symbol* sym, CodeBlob& code, LValContext* lval_ctx);
5555
void process_any_statement(AnyV v, CodeBlob& code);
5656

57+
static AnyV stmt_before_immediate_return = nullptr;
5758

5859
// The goal of VarsModificationWatcher is to detect such cases: `return (x, x += y, x)`.
5960
// Without any changes, ops will be { _Call $2 = +($0_x, $1_y); _Return $0_x, $2, $0_x } - incorrect
@@ -1236,6 +1237,9 @@ static std::vector<var_idx_t> process_match_expression(V<ast_match_expression> v
12361237
code.push_set_cur(if_op.block0);
12371238
if (v->is_statement()) {
12381239
pre_compile_expr(v_ith_arm->get_body(), code);
1240+
if (v == stmt_before_immediate_return) {
1241+
code.emplace_back(v_ith_arm->loc, Op::_Return);
1242+
}
12391243
} else {
12401244
std::vector<var_idx_t> arm_ir_idx = pre_compile_expr(v_ith_arm->get_body(), code, v->inferred_type);
12411245
code.emplace_back(v->loc, Op::_Let, result_ir_idx, std::move(arm_ir_idx));
@@ -1249,6 +1253,9 @@ static std::vector<var_idx_t> process_match_expression(V<ast_match_expression> v
12491253
auto v_last_arm = v->get_arm(n_arms - 1);
12501254
if (v->is_statement()) {
12511255
pre_compile_expr(v_last_arm->get_body(), code);
1256+
if (v == stmt_before_immediate_return) {
1257+
code.emplace_back(v_last_arm->loc, Op::_Return);
1258+
}
12521259
} else {
12531260
std::vector<var_idx_t> arm_ir_idx = pre_compile_expr(v_last_arm->get_body(), code, v->inferred_type);
12541261
code.emplace_back(v->loc, Op::_Let, result_ir_idx, std::move(arm_ir_idx));
@@ -1743,9 +1750,30 @@ std::vector<var_idx_t> pre_compile_expr(AnyExprV v, CodeBlob& code, TypePtr targ
17431750

17441751

17451752
static void process_block_statement(V<ast_block_statement> v, CodeBlob& code) {
1746-
for (AnyV item : v->get_items()) {
1747-
process_any_statement(item, code);
1753+
if (v->empty()) {
1754+
return;
17481755
}
1756+
1757+
FunctionPtr cur_f = code.fun_ref;
1758+
bool does_f_return_nothing = cur_f->inferred_return_type == TypeDataVoid::create() && !cur_f->does_return_self() && !cur_f->has_mutate_params();
1759+
bool is_toplevel_block = v == cur_f->ast_root->as<ast_function_declaration>()->get_body();
1760+
1761+
// we want to optimize `match` and `if/else`: if it's the last statement, implicitly add "return" to every branch
1762+
// (to generate IFJMP instead of nested IF ELSE);
1763+
// a competent way is to do it at the IR level (building CST, etc.), it's impossible to tweak Ops for now;
1764+
// so, for every `f() { here }` of `... here; return;`, save it into a global, and handle within match/if
1765+
AnyV backup = stmt_before_immediate_return;
1766+
for (int i = 0; i < v->size() - 1; ++i) {
1767+
AnyV stmt = v->get_item(i);
1768+
AnyV next_stmt = v->get_item(i + 1);
1769+
bool next_is_empty_return = next_stmt->kind == ast_return_statement && !next_stmt->as<ast_return_statement>()->has_return_value();
1770+
stmt_before_immediate_return = next_is_empty_return && does_f_return_nothing ? stmt : nullptr;
1771+
process_any_statement(stmt, code);
1772+
}
1773+
AnyV last_stmt = v->get_item(v->size() - 1);
1774+
stmt_before_immediate_return = is_toplevel_block && does_f_return_nothing ? last_stmt : nullptr;
1775+
process_any_statement(last_stmt, code);
1776+
stmt_before_immediate_return = backup;
17491777
}
17501778

17511779
static void process_assert_statement(V<ast_assert_statement> v, CodeBlob& code) {
@@ -1817,9 +1845,15 @@ static void process_if_statement(V<ast_if_statement> v, CodeBlob& code) {
18171845
Op& if_op = code.emplace_back(v->loc, Op::_If, std::move(cond));
18181846
code.push_set_cur(if_op.block0);
18191847
process_any_statement(v->get_if_body(), code);
1848+
if (v == stmt_before_immediate_return) {
1849+
code.emplace_back(v->get_if_body()->loc_end, Op::_Return);
1850+
}
18201851
code.close_pop_cur(v->get_if_body()->loc_end);
18211852
code.push_set_cur(if_op.block1);
18221853
process_any_statement(v->get_else_body(), code);
1854+
if (v == stmt_before_immediate_return) {
1855+
code.emplace_back(v->get_else_body()->loc_end, Op::_Return);
1856+
}
18231857
code.close_pop_cur(v->get_else_body()->loc_end);
18241858
if (v->is_ifnot) {
18251859
std::swap(if_op.block0, if_op.block1);
@@ -1978,9 +2012,7 @@ static void convert_function_body_to_CodeBlob(FunctionPtr fun_ref, FunctionBodyC
19782012
blob->in_var_cnt = blob->var_cnt;
19792013
tolk_assert(blob->var_cnt == total_arg_width);
19802014

1981-
for (AnyV item : v_body->get_items()) {
1982-
process_any_statement(item, *blob);
1983-
}
2015+
process_block_statement(v_body, *blob);
19842016
append_implicit_return_statement(v_body->loc_end, *blob);
19852017

19862018
blob->close_blk(v_body->loc_end);

0 commit comments

Comments
 (0)