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