Skip to content

Commit 25e2f5a

Browse files
authored
Add inlining for #ifNil:* variants (#65)
2 parents 8370d82 + 73bd707 commit 25e2f5a

File tree

10 files changed

+380
-21
lines changed

10 files changed

+380
-21
lines changed

src/som/compiler/ast/parser.py

+66
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
from som.interpreter.ast.nodes.specialized.literal_if import (
2525
IfInlinedNode,
2626
IfElseInlinedNode,
27+
IfNilInlinedNode,
28+
IfNotNilInlinedNode,
29+
IfNilNotNilInlinedNode,
2730
)
2831
from som.interpreter.ast.nodes.specialized.literal_while import WhileInlinedNode
2932
from som.vm.symbols import symbol_for
@@ -249,6 +252,26 @@ def _try_inlining_if(if_true, receiver, arguments, source, mgenc):
249252
body_expr = method.inline(mgenc)
250253
return IfInlinedNode(receiver, body_expr, if_true, source)
251254

255+
@staticmethod
256+
def _try_inlining_if_nil(receiver, arguments, source, mgenc):
257+
arg = arguments[0]
258+
if not isinstance(arg, BlockNode):
259+
return None
260+
261+
method = arg.get_method()
262+
body_expr = method.inline(mgenc)
263+
return IfNilInlinedNode(receiver, body_expr, source)
264+
265+
@staticmethod
266+
def _try_inlining_if_not_nil(receiver, arguments, source, mgenc):
267+
arg = arguments[0]
268+
if not isinstance(arg, BlockNode):
269+
return None
270+
271+
method = arg.get_method()
272+
body_expr = method.inline(mgenc)
273+
return IfNotNilInlinedNode(receiver, body_expr, source)
274+
252275
@staticmethod
253276
def _try_inlining_if_else(if_true, receiver, arguments, source, mgenc):
254277
arg1 = arguments[0]
@@ -263,6 +286,25 @@ def _try_inlining_if_else(if_true, receiver, arguments, source, mgenc):
263286
false_expr = arg2.get_method().inline(mgenc)
264287
return IfElseInlinedNode(receiver, true_expr, false_expr, if_true, source)
265288

289+
@staticmethod
290+
def _try_inlining_if_nil_not_nil(is_if_nil, receiver, arguments, source, mgenc):
291+
arg1 = arguments[0]
292+
if not isinstance(arg1, BlockNode):
293+
return None
294+
295+
arg2 = arguments[1]
296+
if not isinstance(arg2, BlockNode):
297+
return None
298+
299+
arg1_expr = arg1.get_method().inline(mgenc)
300+
arg2_expr = arg2.get_method().inline(mgenc)
301+
return IfNilNotNilInlinedNode(
302+
receiver,
303+
arg1_expr if is_if_nil else arg2_expr,
304+
arg2_expr if is_if_nil else arg1_expr,
305+
source,
306+
)
307+
266308
@staticmethod
267309
def _try_inlining_while(while_true, receiver, arguments, source, mgenc):
268310
if not isinstance(receiver, BlockNode):
@@ -321,6 +363,18 @@ def _keyword_message(self, mgenc, receiver):
321363
)
322364
if inlined is not None:
323365
return inlined
366+
elif keyword == "ifNil:":
367+
inlined = self._try_inlining_if_nil(
368+
receiver, arguments, source, mgenc
369+
)
370+
if inlined is not None:
371+
return inlined
372+
elif keyword == "ifNotNil:":
373+
inlined = self._try_inlining_if_not_nil(
374+
receiver, arguments, source, mgenc
375+
)
376+
if inlined is not None:
377+
return inlined
324378
elif keyword == "whileTrue:":
325379
inlined = self._try_inlining_while(
326380
True, receiver, arguments, source, mgenc
@@ -358,6 +412,18 @@ def _keyword_message(self, mgenc, receiver):
358412
)
359413
if inlined is not None:
360414
return inlined
415+
elif keyword == "ifNil:ifNotNil:":
416+
inlined = self._try_inlining_if_nil_not_nil(
417+
True, receiver, arguments, source, mgenc
418+
)
419+
if inlined is not None:
420+
return inlined
421+
elif keyword == "ifNotNil:ifNil:":
422+
inlined = self._try_inlining_if_nil_not_nil(
423+
False, receiver, arguments, source, mgenc
424+
)
425+
if inlined is not None:
426+
return inlined
361427

362428
selector = symbol_for(keyword)
363429

src/som/compiler/bc/bytecode_generator.py

+19-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
from som.vm.symbols import sym_nil, sym_true, sym_false
44

55

6+
class JumpCondition(object):
7+
on_nil = 0
8+
on_not_nil = 1
9+
on_true = 2
10+
on_false = 3
11+
12+
613
def emit_inc(mgenc):
714
emit1(mgenc, BC.inc, 0)
815

@@ -186,18 +193,27 @@ def emit_push_constant_index(mgenc, lit_index):
186193
emit2(mgenc, BC.push_constant, lit_index, 1)
187194

188195

189-
def emit_jump_on_bool_with_dummy_offset(mgenc, is_if_true, needs_pop):
196+
def emit_jump_on_with_dummy_offset(mgenc, condition, needs_pop):
190197
# Remember: true and false seem flipped here.
191198
# This is because if the test passes, the block is inlined directly.
192199
# But if the test fails, we need to jump.
193200
# Thus, an `#ifTrue:` needs to generated a jump_on_false.
194201
if needs_pop:
195-
bc = BC.jump_on_false_pop if is_if_true else BC.jump_on_true_pop
196202
stack_effect = -1
197203
else:
198-
bc = BC.jump_on_false_top_nil if is_if_true else BC.jump_on_true_top_nil
199204
stack_effect = 0
200205

206+
if condition == JumpCondition.on_true:
207+
bc = BC.jump_on_true_pop if needs_pop else BC.jump_on_true_top_nil
208+
elif condition == JumpCondition.on_false:
209+
bc = BC.jump_on_false_pop if needs_pop else BC.jump_on_false_top_nil
210+
elif condition == JumpCondition.on_not_nil:
211+
bc = BC.jump_on_not_nil_pop if needs_pop else BC.jump_on_not_nil_top_top
212+
elif condition == JumpCondition.on_nil:
213+
bc = BC.jump_on_nil_pop if needs_pop else BC.jump_on_nil_top_top
214+
else:
215+
raise NotImplementedError("Unknown condition: " + str(condition))
216+
201217
emit1(mgenc, bc, stack_effect)
202218
idx = mgenc.add_bytecode_argument_and_get_index(0)
203219
mgenc.add_bytecode_argument(0)

src/som/compiler/bc/method_generation_context.py

+14-11
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from rlib.debug import make_sure_not_resized
22
from som.compiler.bc.bytecode_generator import (
3-
emit_jump_on_bool_with_dummy_offset,
3+
emit_jump_on_with_dummy_offset,
44
emit_jump_with_dummy_offset,
55
emit_pop,
66
emit_push_constant,
77
emit_jump_backward_with_offset,
88
emit_inc_field_push,
99
emit_return_field,
10+
JumpCondition,
1011
)
1112

1213
from som.compiler.method_generation_context import MethodGenerationContextBase
@@ -583,7 +584,7 @@ def _assemble_field_setter(self, return_candidate):
583584
arg_idx = self._bytecode[-(pop_len + return_len + 2)]
584585
return FieldWrite(self.signature, field_idx, arg_idx)
585586

586-
def inline_if_true_or_if_false(self, parser, is_if_true):
587+
def inline_then_branch(self, parser, condition):
587588
# HACK: We do assume that the receiver on the stack is a boolean,
588589
# HACK: similar to the IfTrueIfFalseNode.
589590
# HACK: We don't support anything but booleans at the moment.
@@ -596,8 +597,8 @@ def inline_if_true_or_if_false(self, parser, is_if_true):
596597

597598
self._remove_last_bytecodes(1) # remove push_block*
598599

599-
jump_offset_idx_to_skip_true_branch = emit_jump_on_bool_with_dummy_offset(
600-
self, is_if_true, False
600+
jump_offset_idx_to_skip_true_branch = emit_jump_on_with_dummy_offset(
601+
self, condition, False
601602
)
602603

603604
# TODO: remove the block from the literal list
@@ -624,7 +625,7 @@ def _has_two_literal_block_arguments(self):
624625
self._last_bytecode_is_one_of(1, PUSH_BLOCK_BYTECODES) != Bytecodes.invalid
625626
)
626627

627-
def inline_if_true_false(self, parser, is_if_true):
628+
def inline_then_else_branches(self, parser, condition):
628629
# HACK: We do assume that the receiver on the stack is a boolean,
629630
# HACK: similar to the IfTrueIfFalseNode.
630631
# HACK: We don't support anything but booleans at the moment.
@@ -642,8 +643,8 @@ def inline_if_true_false(self, parser, is_if_true):
642643
to_be_inlined_2,
643644
) = self._extract_block_methods_and_remove_bytecodes()
644645

645-
jump_offset_idx_to_skip_true_branch = emit_jump_on_bool_with_dummy_offset(
646-
self, is_if_true, True
646+
jump_offset_idx_to_skip_true_branch = emit_jump_on_with_dummy_offset(
647+
self, condition, True
647648
)
648649

649650
self._is_currently_inlining_a_block = True
@@ -698,8 +699,10 @@ def inline_while(self, parser, is_while_true):
698699
self._is_currently_inlining_a_block = True
699700
cond_method.inline(self)
700701

701-
jump_offset_idx_to_skip_loop_body = emit_jump_on_bool_with_dummy_offset(
702-
self, is_while_true, True
702+
jump_offset_idx_to_skip_loop_body = emit_jump_on_with_dummy_offset(
703+
self,
704+
JumpCondition.on_false if is_while_true else JumpCondition.on_true,
705+
True,
703706
)
704707

705708
body_method.inline(self)
@@ -725,8 +728,8 @@ def inline_andor(self, parser, is_or):
725728

726729
self._remove_last_bytecodes(1) # remove push_block*
727730

728-
jump_offset_idx_to_skip_branch = emit_jump_on_bool_with_dummy_offset(
729-
self, not is_or, True
731+
jump_offset_idx_to_skip_branch = emit_jump_on_with_dummy_offset(
732+
self, JumpCondition.on_true if is_or else JumpCondition.on_false, True
730733
)
731734

732735
to_be_inlined = self._literals[block_literal_idx]

src/som/compiler/bc/parser.py

+27-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
emit_push_constant_index,
2020
emit_return_non_local,
2121
)
22-
from som.compiler.bc.method_generation_context import MethodGenerationContext
22+
from som.compiler.bc.method_generation_context import (
23+
MethodGenerationContext,
24+
JumpCondition,
25+
)
2326
from som.compiler.parser import ParserBase
2427
from som.compiler.symbol import Symbol
2528
from som.vm.symbols import (
@@ -269,10 +272,21 @@ def _keyword_message(self, mgenc):
269272

270273
if not is_super_send:
271274
if num_args == 1 and (
272-
(keyword == "ifTrue:" and mgenc.inline_if_true_or_if_false(self, True))
275+
(
276+
keyword == "ifTrue:"
277+
and mgenc.inline_then_branch(self, JumpCondition.on_false)
278+
)
273279
or (
274280
keyword == "ifFalse:"
275-
and mgenc.inline_if_true_or_if_false(self, False)
281+
and mgenc.inline_then_branch(self, JumpCondition.on_true)
282+
)
283+
or (
284+
keyword == "ifNil:"
285+
and mgenc.inline_then_branch(self, JumpCondition.on_not_nil)
286+
)
287+
or (
288+
keyword == "ifNotNil:"
289+
and mgenc.inline_then_branch(self, JumpCondition.on_nil)
276290
)
277291
or (keyword == "whileTrue:" and mgenc.inline_while(self, True))
278292
or (keyword == "whileFalse:" and mgenc.inline_while(self, False))
@@ -284,11 +298,19 @@ def _keyword_message(self, mgenc):
284298
if num_args == 2 and (
285299
(
286300
keyword == "ifTrue:ifFalse:"
287-
and mgenc.inline_if_true_false(self, True)
301+
and mgenc.inline_then_else_branches(self, JumpCondition.on_false)
288302
)
289303
or (
290304
keyword == "ifFalse:ifTrue:"
291-
and mgenc.inline_if_true_false(self, False)
305+
and mgenc.inline_then_else_branches(self, JumpCondition.on_true)
306+
)
307+
or (
308+
keyword == "ifNil:ifNotNil:"
309+
and mgenc.inline_then_else_branches(self, JumpCondition.on_not_nil)
310+
)
311+
or (
312+
keyword == "ifNotNil:ifNil:"
313+
and mgenc.inline_then_else_branches(self, JumpCondition.on_nil)
292314
)
293315
):
294316
return

src/som/interpreter/ast/nodes/specialized/literal_if.py

+62
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,46 @@ def execute(self, frame):
3131
)
3232

3333

34+
class IfNilInlinedNode(ExpressionNode):
35+
_immutable_fields_ = [
36+
"_condition_expr?",
37+
"_body_expr?",
38+
"universe",
39+
]
40+
_child_nodes_ = ["_condition_expr", "_body_expr"]
41+
42+
def __init__(self, condition_expr, body_expr, source_section):
43+
ExpressionNode.__init__(self, source_section)
44+
self._condition_expr = self.adopt_child(condition_expr)
45+
self._body_expr = self.adopt_child(body_expr)
46+
47+
def execute(self, frame):
48+
result = self._condition_expr.execute(frame)
49+
if result is nilObject:
50+
return self._body_expr.execute(frame)
51+
return result
52+
53+
54+
class IfNotNilInlinedNode(ExpressionNode):
55+
_immutable_fields_ = [
56+
"_condition_expr?",
57+
"_body_expr?",
58+
"universe",
59+
]
60+
_child_nodes_ = ["_condition_expr", "_body_expr"]
61+
62+
def __init__(self, condition_expr, body_expr, source_section):
63+
ExpressionNode.__init__(self, source_section)
64+
self._condition_expr = self.adopt_child(condition_expr)
65+
self._body_expr = self.adopt_child(body_expr)
66+
67+
def execute(self, frame):
68+
result = self._condition_expr.execute(frame)
69+
if result is nilObject:
70+
return result
71+
return self._body_expr.execute(frame)
72+
73+
3474
class IfElseInlinedNode(ExpressionNode):
3575
_immutable_fields_ = [
3676
"_condition_expr?",
@@ -61,3 +101,25 @@ def execute(self, frame):
61101
"Would need to generalize, but we haven't implemented that "
62102
+ "for the bytecode interpreter either"
63103
)
104+
105+
106+
class IfNilNotNilInlinedNode(ExpressionNode):
107+
_immutable_fields_ = [
108+
"_condition_expr?",
109+
"_nil_expr?",
110+
"_not_nil_expr?",
111+
"universe",
112+
]
113+
_child_nodes_ = ["_condition_expr", "_nil_expr", "_not_nil_expr"]
114+
115+
def __init__(self, condition_expr, nil_expr, not_nil_expr, source_section):
116+
ExpressionNode.__init__(self, source_section)
117+
self._condition_expr = self.adopt_child(condition_expr)
118+
self._nil_expr = self.adopt_child(nil_expr)
119+
self._not_nil_expr = self.adopt_child(not_nil_expr)
120+
121+
def execute(self, frame):
122+
result = self._condition_expr.execute(frame)
123+
if result is nilObject:
124+
return self._nil_expr.execute(frame)
125+
return self._not_nil_expr.execute(frame)

0 commit comments

Comments
 (0)