Skip to content

Commit 6414135

Browse files
feat[venom]: make revert a bb terminator (#4529)
this commit makes `revert` a basic block terminator. previously `revert` was left as a non-terminating instruction since there could be the possibility of reordering. however, since it terminates, it is cleaner to disallow any control flow after the `revert` instruction. it also fixes a minor bug discovered in `ir_node_to_venom`, where the translator was appending instructions after a `revert` instruction (it would have been appending them to a dead block in any case).
1 parent 94cf162 commit 6414135

File tree

7 files changed

+9
-61
lines changed

7 files changed

+9
-61
lines changed

tests/functional/venom/parser/test_parsing.py

-4
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ def test_multi_bb_single_fn():
5555
has_callvalue_bb = IRBasicBlock(IRLabel("has_callvalue"), start_fn)
5656
start_fn.append_basic_block(has_callvalue_bb)
5757
has_callvalue_bb.append_instruction("revert", IRLiteral(0), IRLiteral(0))
58-
has_callvalue_bb.append_instruction("stop")
5958

6059
assert_ctx_eq(parsed_ctx, expected_ctx)
6160

@@ -152,7 +151,6 @@ def test_multi_function():
152151

153152
check_fn.append_basic_block(value_bb := IRBasicBlock(IRLabel("has_value"), check_fn))
154153
value_bb.append_instruction("revert", IRLiteral(0), IRLiteral(0))
155-
value_bb.append_instruction("stop")
156154

157155
assert_ctx_eq(parsed_ctx, expected_ctx)
158156

@@ -215,7 +213,6 @@ def test_multi_function_and_data():
215213

216214
check_fn.append_basic_block(value_bb := IRBasicBlock(IRLabel("has_value"), check_fn))
217215
value_bb.append_instruction("revert", IRLiteral(0), IRLiteral(0))
218-
value_bb.append_instruction("stop")
219216

220217
expected_ctx.data_segment = [
221218
DataSection(
@@ -313,7 +310,6 @@ def test_phis():
313310
%50 = 0
314311
%51 = 0
315312
revert %51, %50
316-
stop
317313
; (__main_entry)
318314
} ; close function __main_entry
319315
"""

vyper/venom/basicblock.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
from vyper.utils import OrderedSet
99

1010
# instructions which can terminate a basic block
11-
BB_TERMINATORS = frozenset(["jmp", "djmp", "jnz", "ret", "return", "stop", "exit", "sink"])
11+
BB_TERMINATORS = frozenset(
12+
["jmp", "djmp", "jnz", "ret", "return", "revert", "stop", "exit", "sink"]
13+
)
1214

1315
VOLATILE_INSTRUCTIONS = frozenset(
1416
[
@@ -602,11 +604,7 @@ def remove_instructions_after(self, instruction: IRInstruction) -> None:
602604

603605
def ensure_well_formed(self):
604606
for inst in self.instructions:
605-
assert inst.parent == self # sanity
606-
if inst.opcode == "revert":
607-
self.remove_instructions_after(inst)
608-
self.append_instruction("stop") # TODO: make revert a bb terminator?
609-
break
607+
assert inst.parent == self # sanity check
610608

611609
def key(inst):
612610
if inst.opcode in ("phi", "param"):

vyper/venom/context.py

-8
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,6 @@ def get_next_variable(self) -> IRVariable:
8888
def get_last_variable(self) -> str:
8989
return f"%{self.last_variable}"
9090

91-
def chain_basic_blocks(self) -> None:
92-
"""
93-
Chain basic blocks together. This is necessary for the IR to be valid, and is done after
94-
the IR is generated.
95-
"""
96-
for fn in self.functions.values():
97-
fn.chain_basic_blocks()
98-
9991
def append_data_section(self, name: IRLabel) -> None:
10092
self.data_segment.append(DataSection(name))
10193

vyper/venom/function.py

-22
Original file line numberDiff line numberDiff line change
@@ -209,28 +209,6 @@ def ast_source(self) -> Optional[IRnode]:
209209
def error_msg(self) -> Optional[str]:
210210
return self._error_msg_stack[-1] if len(self._error_msg_stack) > 0 else None
211211

212-
def chain_basic_blocks(self) -> None:
213-
"""
214-
Chain basic blocks together. If a basic block is not terminated, jump to the next one.
215-
Otherwise, append a stop instruction. This is necessary for the IR to be valid, and is
216-
done after the IR is generated.
217-
"""
218-
bbs = list(self.get_basic_blocks())
219-
for i, bb in enumerate(bbs):
220-
if bb.is_terminated:
221-
continue
222-
223-
if i < len(bbs) - 1:
224-
# TODO: revisit this. When contructor calls internal functions
225-
# they are linked to the last ctor block. Should separate them
226-
# before this so we don't have to handle this here
227-
if bbs[i + 1].label.value.startswith("internal"):
228-
bb.append_instruction("stop")
229-
else:
230-
bb.append_instruction("jmp", bbs[i + 1].label)
231-
else:
232-
bb.append_instruction("stop")
233-
234212
def copy(self):
235213
new = IRFunction(self.name)
236214
for bb in self.get_basic_blocks():

vyper/venom/ir_node_to_venom.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,6 @@ def ir_node_to_venom(ir: IRnode) -> IRContext:
125125

126126
_convert_ir_bb(fn, ir, {})
127127

128-
ctx.chain_basic_blocks()
129-
130128
for fn in ctx.functions.values():
131129
for bb in fn.get_basic_blocks():
132130
bb.ensure_well_formed()
@@ -418,14 +416,15 @@ def _convert_ir_bb(fn, ir, symbols):
418416
code = ir.args[2]
419417
_convert_ir_bb(fn, code, symbols)
420418
elif ir.value == "exit_to":
421-
args = _convert_ir_bb_list(fn, ir.args[1:], symbols)
422-
var_list = args
423-
# TODO: only append return args if the function is external
424-
_append_return_args(fn, *var_list)
425419
bb = fn.get_basic_block()
426420
if bb.is_terminated:
427421
bb = IRBasicBlock(ctx.get_next_label("exit_to"), fn)
428422
fn.append_basic_block(bb)
423+
424+
args = _convert_ir_bb_list(fn, ir.args[1:], symbols)
425+
var_list = args
426+
# TODO: only append return args if the function is external
427+
_append_return_args(fn, *var_list)
429428
bb = fn.get_basic_block()
430429

431430
label = IRLabel(ir.args[0].value)

vyper/venom/parser.py

-11
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,6 @@ def _set_last_label(ctx: IRContext):
8888
ctx.last_label = max(int(label_head), ctx.last_label)
8989

9090

91-
def _ensure_terminated(bb):
92-
# Since "revert" is not considered terminal explicitly check for it
93-
# to ensure basic blocks are terminating
94-
if not bb.is_terminated:
95-
if any(inst.opcode == "revert" for inst in bb.instructions):
96-
bb.append_instruction("stop")
97-
# note: check_venom_ctx will raise error later if still not terminated.
98-
99-
10091
def _unescape(s: str):
10192
"""
10293
Unescape the escaped string. This is the inverse of `IRLabel.__repr__()`.
@@ -136,8 +127,6 @@ def start(self, children) -> IRContext:
136127
assert isinstance(instruction, IRInstruction) # help mypy
137128
bb.insert_instruction(instruction)
138129

139-
_ensure_terminated(bb)
140-
141130
_set_last_var(fn)
142131
_set_last_label(ctx)
143132

vyper/venom/passes/function_inliner.py

-4
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,6 @@ def _inline_call_site(self, func: IRFunction, call_site: IRInstruction) -> None:
143143
elif inst.opcode == "ret":
144144
inst.opcode = "jmp"
145145
inst.operands = [call_site_return.label]
146-
elif inst.opcode == "revert":
147-
bb.remove_instructions_after(inst)
148-
bb.append_instruction("stop")
149-
break
150146

151147
for inst in bb.instructions:
152148
if not inst.annotation:

0 commit comments

Comments
 (0)