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
//! 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_offset(),
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_offset(),
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_offset(),
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_offset(),
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_offset());
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.offset(self.masm.pc_offset(), 4);
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<()> {
todo!()
}
/// 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)?;
self.table.label(self.masm.pc_offset(), label);
self.masm.asm.increment_sp(1)?;
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.
///
/// TODO: (#28)
///
/// - End of control flow operators.
/// - End of function.
/// - End of program.
pub fn _end(&mut self) -> Result<()> {
if let Ok(frame) = self.control.pop() {
self.handle_frame_popping(frame)
} else if !self.is_main {
tracing::trace!("end of call");
self.handle_call_return()
} else {
tracing::trace!("end of main function");
self.handle_return()
}
}
/// 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(())
}
}