hexagon/
opcode.rs

1use smallvec::SmallVec;
2use call_stack::Frame;
3use object_pool::ObjectPool;
4use value::Value;
5use errors::ValidateError;
6
7/// Hexagon VM opcodes.
8///
9/// Note that the `Rt` variant is only meant to be used internally
10/// by the optimizer and will not pass code validation at function
11/// creation.
12#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
13pub enum OpCode {
14    Nop,
15    LoadNull,
16    LoadInt(i64),
17    LoadFloat(f64),
18    LoadString(String),
19    LoadBool(bool),
20    LoadThis,
21    Pop,
22    Dup,
23    InitLocal(usize),
24    GetLocal(usize),
25    SetLocal(usize),
26    GetArgument(usize),
27    GetNArguments,
28    GetStatic,
29    SetStatic,
30    GetField,
31    SetField,
32    Call(usize),
33    CallField(usize),
34    Branch(usize),
35    ConditionalBranch(usize, usize),
36    Return,
37    Add,
38    Sub,
39    Mul,
40    Div,
41    Mod,
42    Pow,
43    IntAdd,
44    IntSub,
45    IntMul,
46    IntDiv,
47    IntMod,
48    IntPow,
49    FloatAdd,
50    FloatSub,
51    FloatMul,
52    FloatDiv,
53    FloatPowi,
54    FloatPowf,
55    StringAdd,
56    CastToFloat,
57    CastToInt,
58    CastToBool,
59    CastToString,
60    And,
61    Or,
62    Not,
63    TestLt,
64    TestLe,
65    TestEq,
66    TestNe,
67    TestGe,
68    TestGt,
69    Rotate2,
70    Rotate3,
71    RotateReverse(usize),
72
73    // used for short-circuiting operations
74    // both blocks must pop no value and produce exactly one value
75    Select(SelectType, Vec<OpCode>, Vec<OpCode>),
76
77    #[serde(skip_serializing, skip_deserializing)]
78    Rt(RtOpCode)
79}
80
81#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
82pub enum SelectType {
83    And,
84    Or
85}
86
87#[derive(Clone, Debug, PartialEq)]
88pub enum RtOpCode {
89    LoadObject(usize),
90    BulkLoad(SmallVec<[Value; 4]>),
91    StackMap(StackMapPattern),
92    ConstCall(ValueLocation /* target */, ValueLocation /* this */, usize /* n_args */),
93    ConstGetField(usize /* object id */, Value /* key */)
94}
95
96#[derive(Clone, Debug, PartialEq)]
97pub struct StackMapPattern {
98    pub(crate) map: SmallVec<[ValueLocation; 4]>,
99    pub(crate) end_state: isize
100}
101
102#[derive(Clone, Debug, PartialEq)]
103pub enum ValueLocation {
104    Stack(isize),
105    Local(usize),
106    Argument(usize),
107    ConstInt(i64),
108    ConstFloat(f64),
109    ConstString(String),
110    ConstBool(bool),
111    ConstNull,
112    ConstObject(usize),
113    This
114}
115
116impl StackMapPattern {
117    pub fn to_opcode_sequence(&self) -> Option<Vec<OpCode>> {
118        let mut opcodes: Vec<OpCode> = Vec::new();
119        let n_pushes: isize = self.end_state - self.map.len() as isize;
120        if n_pushes > 0 {
121            return None;
122        } else {
123            for _ in 0..(-n_pushes) {
124                opcodes.push(OpCode::Pop);
125            }
126        }
127
128        for loc in self.map.iter() {
129            if let Some(op) = loc.to_opcode() {
130                opcodes.push(op);
131            } else {
132                return None;
133            }
134        }
135        Some(opcodes)
136    }
137}
138
139impl ValueLocation {
140    pub fn to_opcode(&self) -> Option<OpCode> {
141        match *self {
142            ValueLocation::Stack(_) => None,
143            ValueLocation::Local(id) => Some(OpCode::GetLocal(id)),
144            ValueLocation::Argument(id) => Some(OpCode::GetArgument(id)),
145            ValueLocation::ConstInt(v) => Some(OpCode::LoadInt(v)),
146            ValueLocation::ConstFloat(v) => Some(OpCode::LoadFloat(v)),
147            ValueLocation::ConstString(ref s) => Some(OpCode::LoadString(s.clone())),
148            ValueLocation::ConstBool(v) => Some(OpCode::LoadBool(v)),
149            ValueLocation::ConstNull => Some(OpCode::LoadNull),
150            ValueLocation::ConstObject(id) => Some(OpCode::Rt(RtOpCode::LoadObject(id))),
151            ValueLocation::This => Some(OpCode::LoadThis)
152        }
153    }
154
155    pub fn from_opcode(op: &OpCode) -> Option<ValueLocation> {
156        match *op {
157            OpCode::GetLocal(id) => Some(ValueLocation::Local(id)),
158            OpCode::GetArgument(id) => Some(ValueLocation::Argument(id)),
159            OpCode::LoadInt(v) => Some(ValueLocation::ConstInt(v)),
160            OpCode::LoadFloat(v) => Some(ValueLocation::ConstFloat(v)),
161            OpCode::LoadString(ref v) => Some(ValueLocation::ConstString(v.clone())),
162            OpCode::LoadBool(v) => Some(ValueLocation::ConstBool(v)),
163            OpCode::LoadNull => Some(ValueLocation::ConstNull),
164            OpCode::Rt(RtOpCode::LoadObject(id)) => Some(ValueLocation::ConstObject(id)),
165            OpCode::LoadThis => Some(ValueLocation::This),
166            _ => None
167        }
168    }
169
170    pub fn extract(&self, frame: &Frame, pool: &mut ObjectPool) -> Value {
171        match *self {
172            ValueLocation::Stack(dt) => {
173                let center = frame.exec_stack.len() - 1;
174                frame.exec_stack.get((center as isize + dt) as usize).unwrap()
175            },
176            ValueLocation::Local(id) => frame.get_local(id),
177            ValueLocation::Argument(id) => frame.must_get_argument(id),
178            ValueLocation::ConstString(ref s) => {
179                Value::Object(pool.allocate(Box::new(s.clone())))
180            },
181            ValueLocation::ConstNull => Value::Null,
182            ValueLocation::ConstInt(v) => Value::Int(v),
183            ValueLocation::ConstFloat(v) => Value::Float(v),
184            ValueLocation::ConstBool(v) => Value::Bool(v),
185            ValueLocation::ConstObject(id) => Value::Object(id),
186            ValueLocation::This => frame.get_this()
187        }
188    }
189
190    pub fn to_value(&self) -> Option<Value> {
191        match *self {
192            ValueLocation::ConstNull => Some(Value::Null),
193            ValueLocation::ConstInt(v) => Some(Value::Int(v)),
194            ValueLocation::ConstFloat(v) => Some(Value::Float(v)),
195            ValueLocation::ConstBool(v) => Some(Value::Bool(v)),
196            ValueLocation::ConstObject(v) => Some(Value::Object(v)),
197            _ => None
198        }
199    }
200}
201
202macro_rules! validate_select_opcode_sequence {
203    ($seq:expr) => ({
204        {
205            let mut stack_depth: isize = 0;
206            for op in $seq {
207                op.validate(false)?;
208                let (n_pops, n_pushes) = op.get_stack_depth_change();
209                stack_depth -= n_pops as isize;
210                if stack_depth < 0 {
211                    return Err(ValidateError::new("Stack underflow"));
212                }
213                stack_depth += n_pushes as isize;
214            }
215            if stack_depth != 1 {
216                return Err(ValidateError::new("Expecting exactly one value"));
217            }
218        }
219    })
220}
221
222impl OpCode {
223    pub fn modifies_control_flow(&self) -> bool {
224        match *self {
225            OpCode::Branch(_) | OpCode::ConditionalBranch(_, _) | OpCode::Return => true,
226            _ => false
227        }
228    }
229
230    pub fn validate(&self, allow_modify_control_flow: bool) -> Result<(), ValidateError> {
231        if !allow_modify_control_flow {
232            if self.modifies_control_flow() {
233                return Err(ValidateError::new("Modifying control flow is not allowed here"));
234            }
235        }
236
237        match *self {
238            OpCode::RotateReverse(n) => {
239                if n <= 0 {
240                    return Err(ValidateError::new("RotateReverse only accepts an operand greater than zero"));
241                }
242                Ok(())
243            },
244            OpCode::Select(_, ref left, ref right) => {
245                validate_select_opcode_sequence!(left);
246                validate_select_opcode_sequence!(right);
247                Ok(())
248            },
249            _ => Ok(())
250        }
251    }
252
253    pub fn from_value(v: Value) -> OpCode {
254        use self::OpCode::*;
255
256        match v {
257            Value::Null => LoadNull,
258            Value::Bool(v) => LoadBool(v),
259            Value::Int(v) => LoadInt(v),
260            Value::Float(v) => LoadFloat(v),
261            Value::Object(v) => Rt(RtOpCode::LoadObject(v))
262        }
263    }
264
265    pub fn to_value(&self) -> Option<Value> {
266        use self::OpCode::*;
267
268        match *self {
269            LoadNull => Some(Value::Null),
270            LoadBool(v) => Some(Value::Bool(v)),
271            LoadInt(v) => Some(Value::Int(v)),
272            LoadFloat(v) => Some(Value::Float(v)),
273            Rt(RtOpCode::LoadObject(id)) => Some(Value::Object(id)),
274            _ => None
275        }
276    }
277
278    pub fn get_stack_depth_change(&self) -> (usize, usize) {
279        use self::OpCode::*;
280
281        // (pop, push)
282        match *self {
283            Nop => (0, 0),
284            LoadNull | LoadInt(_) | LoadFloat(_) | LoadString(_) | LoadBool(_) | LoadThis => (0, 1), // pushes the value
285            Pop => (1, 0), // pops the object on the top
286            Dup => (0, 1), // duplicates the object on the top
287            InitLocal(_) => (0, 0),
288            GetLocal(_) => (0, 1), // pushes object
289            SetLocal(_) => (1, 0), // pops object
290            GetArgument(_) => (0, 1), // pushes the argument
291            GetNArguments => (0, 1), // pushes n_arguments
292            GetStatic => (1, 1), // pops name, pushes object
293            SetStatic => (2, 0), // pops name & object
294            GetField => (2, 1), // pops target object & key, pushes object
295            SetField => (3, 0), // pops target object & key & value
296            Branch(_) => (0, 0),
297            ConditionalBranch(_, _) => (1, 0), // pops condition
298            Return => (1, 0), // pops retval,
299            Add | Sub | Mul | Div | Mod | Pow
300                | IntAdd | IntSub | IntMul | IntDiv | IntMod | IntPow
301                | FloatAdd | FloatSub | FloatMul | FloatDiv
302                | FloatPowi | FloatPowf
303                | StringAdd => (2, 1), // pops the two operands, pushes the result
304            CastToFloat | CastToInt | CastToBool | CastToString => (1, 1),
305            Not => (1, 1),
306            And | Or => (2, 1), // pops the two operands, pushes the result
307            TestLt | TestLe | TestEq | TestNe | TestGe | TestGt => (2, 1), // pops the two operands, pushes the result
308            Call(n_args) => (n_args + 2, 1), // pops target & this & arguments, pushes the result
309            CallField(n_args) => (n_args + 3, 1), // pops target & this & field_name & arguments, pushes the result
310            Rotate2 => (2, 2),
311            Rotate3 => (3, 3),
312            RotateReverse(n) => (n, n),
313            Select(_, _, _) => (0, 1), // pushes exactly one value
314            Rt(ref op) => match *op {
315                RtOpCode::LoadObject(_) => (0, 1), // pushes the object at id
316                RtOpCode::BulkLoad(ref values) => (0, values.len()), // pushes all the values
317                RtOpCode::StackMap(ref p) => if p.end_state >= 0 {
318                    (0, p.end_state as usize)
319                } else {
320                    ((-p.end_state) as usize, 0)
321                },
322                RtOpCode::ConstCall(_, _, n_args) => (n_args, 1), // pops arguments, pushes the result
323                RtOpCode::ConstGetField(_, _) => (0, 1) // pushes the object
324            }
325        }
326    }
327}