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 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
|
#| assembler.jl -- higher-level assembler
$Id$
Copyright (C) 2000 John Harper <john@dcs.warwick.ac.uk>
This file is part of librep.
librep is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
librep is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with librep; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA
|#
;; The plan is to use in the compiler at some point to remove the ugly
;; lap code representation, instead compile to assembly language, then
;; assemble that.. (with the peephole pass in between)
(define-structure rep.vm.assembler
(export assemble)
(open rep
rep.vm.bytecodes
rep.data.tables
rep.data.records)
(define-record-type :label
(make-label name)
labelp
(name label-name label-name-set)
(address label-address label-address-set)
(forwards label-forwards label-forwards-set))
;; Syntax of INSNS is a list of `(INSN [ARG])' or `LABEL'. One pseudo
;; insn: `(push-label LABEL)'
;; Example:
;; ((push 0)
;; foo
;; (push 1)
;; (add)
;; (jmp foo))
;; Returns (BYTE-CODE-VECTOR . CONSTANT-VECTOR)
(define (assemble insns #!optional start)
(let ((code '())
(pc (or start 0))
(labels (make-table symbol-hash eq))
(constants '())
(next-const-id 0))
(define (get-label name)
(or (table-ref labels name)
(let ((l (make-label name)))
(table-set labels name l)
l)))
(define (get-const-id value)
(or (cdr (assoc value constants))
(prog1 next-const-id
(setq constants (cons (cons value next-const-id) constants))
(setq next-const-id (1+ next-const-id)))))
(define (emit-byte-at byte addr)
(setq code (cons (cons byte addr) code)))
(define (emit-byte byte)
(emit-byte-at byte pc)
(setq pc (1+ pc)))
(define (emit-address-at addr pc)
(emit-byte-at (ash addr -8) pc)
(emit-byte-at (logand addr 255) (1+ pc)))
(define (emit-address addr)
(emit-address-at addr pc)
(setq pc (+ pc 2)))
(define (emit-label-addr label)
(if (label-address label)
(emit-address (label-address label))
(label-forwards-set label (cons pc (label-forwards label)))
(setq pc (+ pc 2))))
(define (emit-insn insn #!optional arg)
(let ((op (bytecode-ref insn)))
(if (>= op (bytecode last-with-args))
(progn
;; ``normal'' one-byte insn encoding
(emit-byte op)
(when arg
(cond ((memq op byte-two-byte-insns)
(if (< arg 256)
(emit-byte arg)
(error
"Argument overflow in two-byte insn: %s" insn)))
((memq op byte-three-byte-insns)
(if (< arg 65536)
(progn
(emit-byte (ash arg -8))
(emit-byte (logand arg 255)))
(error
"Argument overflow in three-byte insn: %s" insn)))
(t (error "Spurious argument to insn: %s" insn)))))
;; insn with embedded argument
(cond ((<= arg byte-max-1-byte-arg)
(emit-byte (+ op arg)))
((<= arg byte-max-2-byte-arg)
(emit-byte (+ op 6))
(emit-byte arg))
((<= arg byte-max-3-byte-arg)
(emit-byte (+ op 7))
(emit-byte (ash arg -8))
(emit-byte (logand arg 255)))
(t (error "Argument overflow in insn: %s" insn))))))
(define (emit-jmp insn dest)
(emit-byte (bytecode-ref insn))
(emit-label-addr (get-label dest)))
(define (emit-push arg)
(cond ((and (fixnump arg) (<= arg 65535) (>= arg -65535))
(cond ((zerop arg)
(emit-insn 'pushi-0))
((= arg 1)
(emit-insn 'pushi-1))
((= arg 2)
(emit-insn 'pushi-2))
((= arg -1)
(emit-insn 'pushi-minus-1))
((= arg -2)
(emit-insn 'pushi-minus-2))
((and (<= arg 127) (>= arg -128))
(emit-insn 'pushi (logand arg 255)))
((and (< arg 0) (>= arg -65535))
(emit-insn 'pushi-pair-neg (- arg)))
(t (emit-insn 'pushi-pair-pos arg))))
((eq arg '()) (emit-insn 'nil))
((eq arg 't) (emit-insn 't))
(t (emit-insn 'push (get-const-id arg)))))
(define (emit-push-label arg)
;; push address of label
(emit-byte (bytecode pushi-pair-pos))
(emit-label-addr (get-label arg)))
(define (emit-label name)
(let ((label (get-label name)))
(and (label-address label)
(error "Multiply-defined label: %s, %s" name insns))
(label-address-set label pc)
;; backpatch forward references
(do ((refs (label-forwards label) (cdr refs)))
((null refs) (label-forwards-set label '()))
(emit-byte-at (ash pc -8) (car refs))
(emit-byte-at (logand pc 255) (1+ (car refs))))))
(let loop ((rest insns))
(when rest
(let ((insn (car rest)))
(cond ((symbolp insn) (emit-label insn))
((eq (car insn) 'push) (emit-push (cadr insn)))
((eq (car insn) 'push-label) (emit-push-label (cadr insn)))
((memq (car insn) '(refg setg))
;; instruction with constant
(emit-insn (car insn) (get-const-id (cadr insn))))
((memq (car insn) byte-jmp-insns)
(emit-jmp (car insn) (cadr insn)))
(t (apply emit-insn insn)))
(loop (cdr rest)))))
(let ((byte-vec (make-string pc))
(const-vec (make-vector next-const-id)))
(do ((rest code (cdr rest)))
((null rest))
(aset byte-vec (cdar rest) (caar rest)))
(do ((rest constants (cdr rest)))
((null rest))
(aset const-vec (cdar rest) (caar rest)))
(cons byte-vec const-vec)))))
|