zingen/visitor/control.rs
1//! Control flow visitors
2
3use crate::{
4 control::{ControlStackFrame, ControlStackFrameType},
5 Function, Result,
6};
7use wasmparser::{BlockType, BrTable};
8
9impl Function {
10 /// The beginning of an if construct with an implicit block.
11 pub fn _if(&mut self, blockty: BlockType) -> Result<()> {
12 // Emit iszero to check the condition.
13 self.masm._iszero()?;
14
15 // push an `If` frame to the control stack
16 let frame = ControlStackFrame::new(
17 ControlStackFrameType::If(false),
18 self.masm.pc(),
19 self.masm.sp(),
20 blockty,
21 );
22 self.control.push(frame);
23
24 // mock the stack output of the counter
25 //
26 // the program counter operators should be patched afterwards.
27 self.masm.asm.increment_sp(1)?;
28 self.masm._jumpi()?;
29
30 Ok(())
31 }
32
33 /// The begeinning of a block construct. A sequence of
34 /// instructions with a label at the end.
35 pub fn _block(&mut self, blockty: BlockType) -> Result<()> {
36 let frame = ControlStackFrame::new(
37 ControlStackFrameType::Block,
38 self.masm.pc(),
39 self.masm.sp(),
40 blockty,
41 );
42 self.masm._jumpdest()?;
43 self.control.push(frame);
44
45 Ok(())
46 }
47
48 /// A block with a label which may be used to
49 /// form loops.
50 pub fn _loop(&mut self, blockty: BlockType) -> Result<()> {
51 let frame = ControlStackFrame::new(
52 ControlStackFrameType::Loop,
53 self.masm.pc(),
54 self.masm.sp(),
55 blockty,
56 );
57
58 self.masm._jumpdest()?;
59 self.control.push(frame);
60
61 Ok(())
62 }
63
64 /// Marks an else block of an if.
65 pub fn _else(&mut self) -> Result<()> {
66 let last_frame = self.control.mark_else()?;
67
68 // push an `Else` frame to the control stack.
69 let frame = ControlStackFrame::new(
70 ControlStackFrameType::Else,
71 self.masm.pc(),
72 self.masm.sp(),
73 last_frame.result(),
74 );
75 self.control.push(frame);
76 self.masm.asm.increment_sp(1)?;
77 self.masm._jump()?;
78
79 // mark else as the jump destination of the if block.
80 self.table
81 .label(last_frame.original_pc_offset, self.masm.pc());
82 self.masm._jumpdest()?;
83
84 Ok(())
85 }
86
87 /// The select instruction selects one of its first two operands based
88 /// on whether its third oprand is zero or not.
89 ///
90 /// STACK: [cond, val2, val1] -> \[val1\] if cond is non-zero, \[val2\] otherwise.
91 pub fn _select(&mut self) -> Result<()> {
92 tracing::trace!("select");
93 self.masm._iszero()?;
94 self.masm.increment_sp(1)?;
95 self.table.label(self.masm.pc(), self.masm.pc() + 2);
96 self.masm._jumpi()?;
97 self.masm._drop()?;
98 self.masm._jumpdest()?;
99
100 Ok(())
101 }
102
103 /// Branch to a given label in an enclosing construct.
104 ///
105 /// Performs an unconditional branch.
106 pub fn _br(&mut self, depth: u32) -> Result<()> {
107 // Get the target label
108 let label = self.control.label_from_depth(depth)?;
109
110 // Check if this is a branch that would exit the function
111 let _is_exit_branch = self.control.is_exit_branch(depth);
112
113 // Mark affected frames as having potential early returns
114 self.control.mark_frames_with_early_return(depth);
115
116 // Set up jump target in the jump table
117 self.table.label(self.masm.pc(), label);
118
119 // Emit unconditional jump instruction
120 self.masm._jump()?;
121
122 Ok(())
123 }
124
125 /// Performs a conditional branch if i32 is non-zero.
126 ///
127 /// Conditional branch to a given label in an enclosing construct.
128 pub fn _br_if(&mut self, depth: u32) -> Result<()> {
129 let label = self.control.label_from_depth(depth)?;
130
131 // Register the jump target in the jump table
132 self.table.label(self.masm.pc(), label);
133
134 // for a conditional branch, we need to:
135 //
136 // increment the stack pointer (for JUMPI's arguments) and
137 self.masm.increment_sp(1)?;
138
139 // emit the conditional jump instruction
140 self.masm._jumpi()?;
141
142 Ok(())
143 }
144
145 /// A jump table which jumps to a label in an enclosing construct.
146 ///
147 /// Performs an indirect branch through an operand indexing into the
148 /// label vector that is an immediate to the instruction, or to the
149 /// default target if the operand is out of bounds.
150 pub fn _br_table(&mut self, _table: BrTable<'_>) -> Result<()> {
151 todo!()
152 }
153
154 /// Handle the end of instructions for different situations.
155 ///
156 /// - End of control flow operators.
157 /// - End of function.
158 /// - End of program.
159 pub fn _end(&mut self) -> Result<()> {
160 if let Ok(frame) = self.control.pop() {
161 return self.handle_frame_popping(frame);
162 }
163
164 let results = self.ty.results();
165 if self.is_main || self.abi.is_some() {
166 tracing::trace!("end of main function");
167 self.masm.main_return(results)
168 } else {
169 tracing::trace!("end of call");
170 self.masm.call_return(results)
171 }
172 }
173
174 /// Mark as invalid for now.
175 ///
176 /// TODO: recheck this implementation, if it is okay,
177 /// provide more docs.
178 pub fn _unreachable(&mut self) -> Result<()> {
179 self.masm._invalid()?;
180 Ok(())
181 }
182
183 /// Perform nothing in EVM bytecode.
184 pub fn _nop(&mut self) -> Result<()> {
185 Ok(())
186 }
187
188 /// Handle the popping of a frame.
189 ///
190 /// TODO: validate stack IO for all frames (#59)
191 pub(crate) fn handle_frame_popping(&mut self, frame: ControlStackFrame) -> Result<()> {
192 match frame.ty {
193 ControlStackFrameType::If(true) => Ok(()),
194 ControlStackFrameType::Block => {
195 // For blocks that might have early returns, ensure proper jump destination
196 if frame.might_return_early {
197 // Make sure the jump table has this position as a target for the block
198 self.table.label(frame.original_pc_offset, self.masm.pc());
199 }
200 self.masm._jumpdest()
201 }
202 ControlStackFrameType::Loop => Ok(()),
203 _ => {
204 self.table.label(frame.original_pc_offset, self.masm.pc());
205
206 // TODO: Check the stack output and make decisions
207 // how to handle the results.
208
209 // Emit JUMPDEST at the end of the control flow.
210 self.masm._jumpdest()
211 }
212 }
213 }
214}