go_vm/
instruction.rs

1// Copyright 2022 The Goscript Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5#![allow(non_camel_case_types)]
6
7#[cfg(feature = "serde_borsh")]
8use borsh::{
9    maybestd::io::Result as BorshResult, maybestd::io::Write as BorshWrite, BorshDeserialize,
10    BorshSerialize,
11};
12use std::fmt;
13use std::fmt::Debug;
14
15pub type OpIndex = i32;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
18#[repr(u8)]
19pub enum Opcode {
20    VOID,
21
22    DUPLICATE,
23    LOAD_SLICE,
24    STORE_SLICE,
25    LOAD_ARRAY,
26    STORE_ARRAY,
27    LOAD_MAP,
28    STORE_MAP,
29    LOAD_STRUCT,
30    STORE_STRUCT,
31    LOAD_EMBEDDED,
32    STORE_EMBEDDED,
33    LOAD_PKG,
34    STORE_PKG,
35    LOAD_POINTER,
36    STORE_POINTER,
37    LOAD_UP_VALUE,
38    STORE_UP_VALUE,
39
40    // arithmetic, logical, ref, arrow
41    ADD,            // +
42    SUB,            // -
43    MUL,            // *
44    QUO,            // /
45    REM,            // %
46    AND,            // &
47    OR,             // |
48    XOR,            // ^
49    AND_NOT,        // $^
50    SHL,            // <<
51    SHR,            // >>
52    ADD_ASSIGN,     // +
53    SUB_ASSIGN,     // -
54    MUL_ASSIGN,     // *
55    QUO_ASSIGN,     // /
56    REM_ASSIGN,     // %
57    AND_ASSIGN,     // &
58    OR_ASSIGN,      // |
59    XOR_ASSIGN,     // ^
60    AND_NOT_ASSIGN, // $^
61    SHL_ASSIGN,     // <<
62    SHR_ASSIGN,     // >>
63    INC,            //++
64    DEC,            //--
65    UNARY_SUB,      // -
66    UNARY_XOR,      // ^
67    NOT,            // !
68    EQL,            // ==
69    NEQ,            // !=
70    LSS,            // <
71    GTR,            // >
72    LEQ,            // <=
73    GEQ,            // >=
74    REF,            // &
75    REF_UPVALUE,
76    REF_SLICE_MEMBER,
77    REF_STRUCT_FIELD,
78    REF_EMBEDDED,
79    REF_PKG_MEMBER,
80    SEND, // <-
81    RECV, // <-
82
83    // call
84    PACK_VARIADIC,
85    CALL,
86    RETURN,
87
88    // jump
89    JUMP,
90    JUMP_IF,
91    JUMP_IF_NOT,
92    SWITCH,
93    SELECT,
94    RANGE_INIT,
95    RANGE,
96
97    // misc
98    LOAD_INIT_FUNC,
99    BIND_METHOD,
100    BIND_I_METHOD,
101    CAST,
102    TYPE_ASSERT,
103    TYPE,
104
105    // built-in functinalities
106    IMPORT,  // imports a package
107    SLICE,   // for slice expressions
108    CLOSURE, // for creating a closure with function literal
109    LITERAL, // for composite literal
110    NEW,     // for built-in function new
111    MAKE,    // for built-in function make
112    COMPLEX, // for built-in function complex
113    REAL,    // for built-in function real
114    IMAG,    // for built-in function imag
115    LEN,     // for built-in function len
116    CAP,     // for built-in function cap
117    APPEND,  // for built-in function append
118    COPY,    // for built-in function copy
119    DELETE,  // for built-in function delete
120    CLOSE,   // for built-in function close
121    PANIC,   // for built-in function panic
122    RECOVER, // for built-in function recover
123    ASSERT,  // for built-in function assert
124    FFI,     // for FFI
125}
126
127impl fmt::Display for Opcode {
128    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129        fmt::Debug::fmt(&self, f)
130    }
131}
132
133#[cfg(feature = "serde_borsh")]
134impl BorshSerialize for Opcode {
135    #[inline]
136    fn serialize<W: BorshWrite>(&self, writer: &mut W) -> BorshResult<()> {
137        let val: u8 = unsafe { std::mem::transmute(*self) };
138        val.serialize(writer)
139    }
140}
141
142#[cfg(feature = "serde_borsh")]
143impl BorshDeserialize for Opcode {
144    #[inline]
145    fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> BorshResult<Self> {
146        let val = u8::deserialize_reader(reader)?;
147        Ok(unsafe { std::mem::transmute(val) })
148    }
149}
150
151#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
152#[repr(u8)]
153pub enum ValueType {
154    Void,
155    Bool,
156    Int,
157    Int8,
158    Int16,
159    Int32,
160    Int64,
161    Uint,
162    UintPtr,
163    Uint8,
164    Uint16,
165    Uint32,
166    Uint64,
167    Float32,
168    Float64,
169    Complex64,
170    Function,
171    Package, //COPYABLE_END
172    Metadata,
173    Complex128,
174    String,
175    Array,
176    Struct,
177    Pointer, // COMPARABLE_END
178    UnsafePtr,
179    Closure,
180    Slice,
181    Map,
182    Interface,
183    Channel,
184
185    FlagA, //not a type, works as a flag in instructions
186    FlagB,
187    FlagC,
188    FlagD,
189    FlagE,
190}
191
192impl ValueType {
193    #[inline]
194    pub fn copyable(&self) -> bool {
195        self <= &Self::Package
196    }
197
198    #[inline]
199    pub fn comparable(&self) -> bool {
200        self <= &Self::Pointer
201    }
202
203    #[inline]
204    pub fn nilable(&self) -> bool {
205        self >= &Self::Pointer && self <= &Self::Channel
206    }
207}
208
209#[cfg(feature = "serde_borsh")]
210impl BorshSerialize for ValueType {
211    #[inline]
212    fn serialize<W: BorshWrite>(&self, writer: &mut W) -> BorshResult<()> {
213        let val: u8 = unsafe { std::mem::transmute(*self) };
214        val.serialize(writer)
215    }
216}
217
218#[cfg(feature = "serde_borsh")]
219impl BorshDeserialize for ValueType {
220    #[inline]
221    fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> BorshResult<Self> {
222        let val = u8::deserialize_reader(reader)?;
223        Ok(unsafe { std::mem::transmute(val) })
224    }
225}
226
227impl fmt::Display for ValueType {
228    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
229        fmt::Debug::fmt(&self, f)
230    }
231}
232
233#[derive(Clone, Copy)]
234pub struct Instruction {
235    pub op0: Opcode,
236    pub op1: Opcode,
237    pub t0: ValueType,
238    pub t1: ValueType,
239    pub d: OpIndex,
240    pub s0: OpIndex,
241    pub s1: OpIndex,
242}
243
244impl Instruction {
245    pub fn op1_as_t(&self) -> ValueType {
246        unsafe { std::mem::transmute(self.op1) }
247    }
248
249    // Get the max register index 'instructions' write to
250    pub fn max_write_index(instructions: &[Instruction]) -> OpIndex {
251        let mut i = 0;
252        let mut result = 0;
253        loop {
254            let cur = &instructions[i];
255            let index = match cur.op0 {
256                Opcode::VOID => 0,
257                Opcode::DUPLICATE => cur.d,
258                Opcode::LOAD_SLICE => cur.d,
259                Opcode::STORE_SLICE => 0,
260                Opcode::LOAD_ARRAY => cur.d,
261                Opcode::STORE_ARRAY => 0,
262                Opcode::LOAD_MAP => {
263                    i += 1;
264                    match cur.t1 {
265                        ValueType::FlagB => instructions[i].d,
266                        _ => cur.d,
267                    }
268                }
269                Opcode::STORE_MAP => {
270                    i += 1;
271                    0
272                }
273                Opcode::LOAD_STRUCT => cur.d,
274                Opcode::STORE_STRUCT => 0,
275                Opcode::LOAD_EMBEDDED => cur.d,
276                Opcode::STORE_EMBEDDED => 0,
277                Opcode::LOAD_PKG => cur.d,
278                Opcode::STORE_PKG => 0,
279                Opcode::LOAD_POINTER => cur.d,
280                Opcode::STORE_POINTER => 0,
281                Opcode::LOAD_UP_VALUE => cur.d,
282                Opcode::STORE_UP_VALUE => 0,
283                Opcode::ADD => cur.d,
284                Opcode::SUB => cur.d,
285                Opcode::MUL => cur.d,
286                Opcode::QUO => cur.d,
287                Opcode::REM => cur.d,
288                Opcode::AND => cur.d,
289                Opcode::OR => cur.d,
290                Opcode::XOR => cur.d,
291                Opcode::AND_NOT => cur.d,
292                Opcode::SHL => cur.d,
293                Opcode::SHR => cur.d,
294                Opcode::ADD_ASSIGN => 0,
295                Opcode::SUB_ASSIGN => 0,
296                Opcode::MUL_ASSIGN => 0,
297                Opcode::QUO_ASSIGN => 0,
298                Opcode::REM_ASSIGN => 0,
299                Opcode::AND_ASSIGN => 0,
300                Opcode::OR_ASSIGN => 0,
301                Opcode::XOR_ASSIGN => 0,
302                Opcode::AND_NOT_ASSIGN => 0,
303                Opcode::SHL_ASSIGN => 0,
304                Opcode::SHR_ASSIGN => 0,
305                Opcode::INC => 0,
306                Opcode::DEC => 0,
307                Opcode::UNARY_SUB => cur.d,
308                Opcode::UNARY_XOR => cur.d,
309                Opcode::NOT => cur.d,
310                Opcode::EQL => cur.d,
311                Opcode::NEQ => cur.d,
312                Opcode::LSS => cur.d,
313                Opcode::GTR => cur.d,
314                Opcode::LEQ => cur.d,
315                Opcode::GEQ => cur.d,
316                Opcode::REF => cur.d,
317                Opcode::REF_UPVALUE => cur.d,
318                Opcode::REF_SLICE_MEMBER => cur.d,
319                Opcode::REF_STRUCT_FIELD => cur.d,
320                Opcode::REF_EMBEDDED => cur.d,
321                Opcode::REF_PKG_MEMBER => cur.d,
322                Opcode::SEND => 0,
323                Opcode::RECV => match cur.t1 {
324                    ValueType::FlagB => std::cmp::max(cur.d, cur.s1),
325                    _ => cur.d,
326                },
327                Opcode::PACK_VARIADIC => cur.d,
328                Opcode::CALL => 0,
329                Opcode::RETURN => 0,
330                Opcode::JUMP => 0,
331                Opcode::JUMP_IF => 0,
332                Opcode::JUMP_IF_NOT => 0,
333                Opcode::SWITCH => 0,
334                Opcode::SELECT => {
335                    let begin = i + 1;
336                    i += cur.s0 as usize;
337                    instructions[begin..i].iter().fold(0, |acc, x| {
338                        let val = match x.t0 {
339                            ValueType::FlagC => x.s1,
340                            ValueType::FlagD => x.s1 + 1,
341                            _ => 0,
342                        };
343                        std::cmp::max(acc, val)
344                    })
345                }
346                Opcode::RANGE_INIT => 0,
347                Opcode::RANGE => 0,
348                Opcode::LOAD_INIT_FUNC => {
349                    i += 2;
350                    std::cmp::max(cur.d, cur.s1)
351                }
352                Opcode::BIND_METHOD => cur.d,
353                Opcode::BIND_I_METHOD => cur.d,
354                Opcode::CAST => cur.d,
355                Opcode::TYPE_ASSERT => match cur.t1 {
356                    ValueType::FlagB => {
357                        i += 1;
358                        instructions[i].d
359                    }
360                    _ => cur.d,
361                },
362                Opcode::TYPE => match cur.t0 {
363                    ValueType::FlagA => std::cmp::max(cur.d, cur.s1),
364                    _ => cur.d,
365                },
366                Opcode::IMPORT => 0,
367                Opcode::SLICE => {
368                    i += 1;
369                    cur.d
370                }
371                Opcode::CLOSURE => cur.d,
372                Opcode::LITERAL => {
373                    i += 1 + cur.s1 as usize;
374                    cur.d
375                }
376                Opcode::NEW => cur.d,
377                Opcode::MAKE => {
378                    if cur.t0 == ValueType::FlagC {
379                        i += 1;
380                    }
381                    cur.d
382                }
383                Opcode::COMPLEX => cur.d,
384                Opcode::REAL => cur.d,
385                Opcode::IMAG => cur.d,
386                Opcode::LEN => cur.d,
387                Opcode::CAP => cur.d,
388                Opcode::APPEND => cur.d,
389                Opcode::COPY => cur.d,
390                Opcode::DELETE => 0,
391                Opcode::CLOSE => 0,
392                Opcode::PANIC => 0,
393                Opcode::RECOVER => cur.d,
394                Opcode::ASSERT => 0,
395                Opcode::FFI => cur.d,
396            };
397            result = std::cmp::max(result, index);
398            i += 1;
399            if i >= instructions.len() {
400                break;
401            }
402        }
403        result
404    }
405}
406
407#[cfg(feature = "serde_borsh")]
408impl BorshSerialize for Instruction {
409    fn serialize<W: BorshWrite>(&self, writer: &mut W) -> BorshResult<()> {
410        self.op0.serialize(writer)?;
411        self.op1.serialize(writer)?;
412        self.t0.serialize(writer)?;
413        self.t1.serialize(writer)?;
414        self.d.serialize(writer)?;
415        self.s0.serialize(writer)?;
416        self.s1.serialize(writer)
417    }
418}
419
420#[cfg(feature = "serde_borsh")]
421impl BorshDeserialize for Instruction {
422    fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> BorshResult<Self> {
423        // Optimization: Get all data at once
424        const BYTE_COUNT: usize = 4;
425        const OP_INDEX_SIZE: usize = core::mem::size_of::<OpIndex>();
426        let data = <[u8; BYTE_COUNT + OP_INDEX_SIZE * 3]>::deserialize_reader(reader)?;
427        let op0 = unsafe { std::mem::transmute(data[0]) };
428        let op1 = unsafe { std::mem::transmute(data[1]) };
429        let t0 = unsafe { std::mem::transmute(data[2]) };
430        let t1 = unsafe { std::mem::transmute(data[3]) };
431        let mut begin = BYTE_COUNT;
432        let d = OpIndex::from_le_bytes(data[begin..begin + OP_INDEX_SIZE].try_into().unwrap());
433        begin += OP_INDEX_SIZE;
434        let s0 = OpIndex::from_le_bytes(data[begin..begin + OP_INDEX_SIZE].try_into().unwrap());
435        begin += OP_INDEX_SIZE;
436        let s1 = OpIndex::from_le_bytes(data[begin..begin + OP_INDEX_SIZE].try_into().unwrap());
437        Ok(Instruction {
438            op0,
439            op1,
440            t0,
441            t1,
442            d,
443            s0,
444            s1,
445        })
446    }
447}
448
449impl std::fmt::Debug for Instruction {
450    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
451        let ops = if self.op1 == Opcode::VOID {
452            self.op0.to_string()
453        } else {
454            format!("{}.{}", self.op0, self.op1)
455        };
456        write!(f, "{: <16}|", ops)?;
457        fmt_index(self.d, f)?;
458        f.write_str("\t|")?;
459        fmt_index(self.s0, f)?;
460        f.write_str("\t|")?;
461        fmt_index(self.s1, f)?;
462        f.write_str("\t|")?;
463        fmt_type(self.t0, f)?;
464        f.write_str("\t|")?;
465        fmt_type(self.t1, f)?;
466        Ok(())
467    }
468}
469
470fn fmt_type(t: ValueType, f: &mut std::fmt::Formatter) -> std::fmt::Result {
471    if t == ValueType::Void {
472        f.write_str("...")
473    } else {
474        t.fmt(f)
475    }
476}
477
478fn fmt_index(index: OpIndex, f: &mut std::fmt::Formatter) -> std::fmt::Result {
479    if index == OpIndex::MAX {
480        f.write_str("...")
481    } else {
482        index.fmt(f)
483    }
484}
485
486#[cfg(test)]
487mod test {
488    use super::*;
489
490    #[test]
491    fn test_inst_size() {
492        println!("size {} \n", std::mem::size_of::<Instruction>());
493    }
494}