zingen/visitor/
control.rs

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
//! 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<()> {
        // TODO: do sth here?
        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 for breaking out
        self.table.label(self.masm.pc(), label);
        self.masm.increment_sp(1)?;

        // JUMPI will check condition and jump if true
        self.masm._jumpi()?;

        // If we don't jump, we continue in the current frame
        // No need for explicit ISZERO since JUMPI handles the condition

        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 => 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()
            }
        }
    }
}