Login
7 branches 0 tags
Ben (X13/Arch) More compact images c5107a2 2 years ago 1170 Commits
nujel / stdlib / compiler / backend_bytecode / disassembler.nuj
;;; Nujel - Copyright (C) 2020-2021 - Benjamin Vincent Schulenburg
;;; This project uses the MIT license, a copy should be included under /LICENSE
;;;
;;; Contains the disassembler, which is probably not that important to most
;;; people, but invaluable when working on compiler internals

(def disassemble (let*
                   (defn disassemble/length (op)
                         "Return the length in bytes of a bytecode operation and all its arguments"
                         (case op
                               ((#x0 #x1 #x3 #xC #xD #x11 #x12 #x13 #x14 #x15 #x16 #x17 #x18 #x19 #x1B #x1C #x1D #x1E #x1F #x20 #x21 #x22 #x23 #x24 #x25 #x26 #x27 #x28 #x29 #x2A #x2B #x2C #x2D #x2F #x30 #x31 #x32 #x33 #x34 #x35 #x36 #x37 #x38) 1)
                               ((#x2 #x4 #x5 #x7 #xE #x1A #x2E) 2)
                               ((#x6 #x8 #x9 #xA #xB #xF #x10 ) 3)
                               (otherwise (exception :unknown-op "This op needs its length specified for disassembly to work" op))))

                   (defn bytecode/nil-catcher (error) (if (= (car error) :type-error) #nil (throw error)))

                   (defn bytecode-arr->val (a i)
                         "Read a bytecode encoded value in A at I and return it"
                         (try bytecode/nil-catcher (bytecode-op->val (ref a i) (ref a (+ 1 i)) (ref a (+ 2 i)))))

                   (defn bytecode-arr->sym (a i)
                         "Read a bytecode encoded symbol in A at I and return it"
                         (try bytecode/nil-catcher (bytecode-op->sym (ref a i) (ref a (+ 1 i)) (ref a (+ 2 i)))))

                   (defn bytecode-op->offset (a b)
                         "Turn two bytecode ops encoding an offset into the integer representation"
                         (def t (bit-or (bit-shift-left a 8) b))
                         (if-not (> t 32768) t
                                 (- (- 65536 t))))

                   (defn bytecode-arr->offset (a i)
                         "Read a bytecode encoded offset in A at I and return it as a signed integer"
                         (bytecode-op->offset (ref a i) (ref a (+ 1 i))))

                   (defn disassemble/maybe-quote (v)
                         "Quotes symbols but just passes through every other value"
                         (cond ((symbol? v) (list 'quote v))
                               ((bytecode-array? v) (disassemble/bytecode-array v))
                               (#t v)))

                   (defn disassemble/op (a i literals)
                         "Disassemble a single bytecode op in A at I and return it as an s-expression, that could be applied to eval"
                         (case (ref a i)
                               (#x0  '($nop))
                               (#x1  '($ret))
                               (#x2  `($push/int/byte ~(ref a (+ i 1))))
                               (#x3  '($add/int))
                               (#x4  `($apply ~(ref a (+ i 1))))
                               (#x5  `($set/val ~(disassemble/maybe-quote (ref literals (ref a (+ i 1))))))
                               (#x6  `($push/val/ext ~(ref literals (bit-or (ref a (+ i 1))
                                                                            (bit-shift-left (ref a (+ i 2))) 8))))
                               (#x7  `($def/val ~(disassemble/maybe-quote (ref literals (ref a (+ i 1))))))
                               (#x8  `($def/val/ext ~(ref literals (bit-or (ref a (+ i 1))
                                                                           (bit-shift-left (ref a (+ i 2)) 8)))))
                               (#x9  `($jmp*  ~(bytecode-arr->offset a (+ i 1))))
                               (#xA  `($jt*   ~(bytecode-arr->offset a (+ i 1))))
                               (#xB  `($jf*   ~(bytecode-arr->offset a (+ i 1))))
                               (#xC  '($dup))
                               (#xD  '($drop))
                               (#xE  `($get/val ~(disassemble/maybe-quote (ref literals (ref a (+ i 1))))))
                               (#xF  `($get/val/ext ~(ref literals (bit-or (ref a (+ i 1))
                                                                           (bit-shift-left (ref a (+ i 2)) 8)))))
                               (#x10 `($set/val/ext ~(ref literals (bit-or (ref a (+ i 1))
                                                                           (bit-shift-left (ref a (+ i 2)) 8)))))
                               (#x11 '($car))
                               (#x12 '($cdr))
                               (#x13 '($closure/push))
                               (#x14 '($cons))
                               (#x15 '($let))
                               (#x16 '($closure/pop))
                               (#x17 '($fn/dynamic))
                               (#x18 '($macro/dynamic))
                               (#x19 `($try      ~(bytecode-arr->offset a (+ i 1))))
                               (#x1A `($push/val ~(disassemble/maybe-quote (ref literals (ref a (+ i 1))))))
                               (#x1B '($push/true))
                               (#x1C '($push/false))
                               (#x1D '($eval))
                               (#x1E '($<))
                               (#x1F '($<=))
                               (#x20 '($=))
                               (#x21 '($>=))
                               (#x22 '($>))
                               (#x23 '($inc/int))
                               (#x24 '($push/nil))
                               (#x25 '($add))
                               (#x26 '($sub))
                               (#x27 '($mul))
                               (#x28 '($div))
                               (#x29 '($rem))
                               (#x2A '($zero?))
                               (#x2B '($ref))
                               (#x2C '($cadr))
                               (#x2D '($mutable-eval))
                               (#x2E `($list ~(ref a (+ i 1))))
                               (#x2F '($throw))
                               (#x30 '($apply/collection))
                               (#x31 '($bit-shift-left))
                               (#x32 '($bit-shift-right))
                               (#x33 '($bit-and))
                               (#x34 '($bit-or))
                               (#x35 '($bit-xor))
                               (#x36 '($bit-not))
                               (#x37 '($set/gen))
                               (#x38 '($not=))
                               (otherwise :unknown-op)))

                   (defn disassemble/array (a i literals)
                         "Disassemble all bytecode operations in the plain array A starting at I, turning it into an assembler S-Expression and return it as a dotted pair, with the car containing the offset and the cdr containing the S-Expression"
                         (def ret #nil)
                         (while (< i (:length a))
                           (cons! (cons i (disassemble/op a i literals)) ret)
                           (set! i (+ i (disassemble/length (ref a i)))))
                         (nreverse ret))

                   (defn disassemble/bytecode-array (code)
                         "Disassemble the bytecode array CODE, turning it into a list of dotted pairs, with the car containing the offset and the cdr containing assembler S-Expressions,"
                         (disassemble/array (:array code) 0 (:literals code)))

                   (defn disassemble/to-string (bc)
                         (disassemble/bytecode-array bc))

                   (defn disassemble (bc)
                         "Disassemble the bytecode array CODE, turning it into a list of dotted pairs, with the car containing the offset and the cdr containing assembler S-Expressions,"
                         :cat :bytecode
                         :internal
                         (case (:type-name bc)
                               ((:lambda :macro) (disassemble/to-string (:code bc)))
                               (:bytecode-array  (disassemble/to-string bc))
                               (otherwise (exception :type-error "Can't disassemble that" bc))))))