779 lines
26 KiB
Scheme
779 lines
26 KiB
Scheme
|
; -*- Mode: Scheme; Syntax: Scheme; Package: Scheme; -*-
|
||
|
; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING.
|
||
|
|
||
|
|
||
|
; This is file cprim.scm.
|
||
|
|
||
|
;;;; Compiling primitive procedures and calls to them.
|
||
|
|
||
|
; (primitive-procedure name) => a procedure
|
||
|
|
||
|
(define-compilator 'primitive-procedure syntax-type
|
||
|
(lambda (node level depth cont)
|
||
|
(let ((name (cadr (node-form node))))
|
||
|
(deliver-value (sequentially
|
||
|
(instruction (enum op closure))
|
||
|
(template ((primop-closed (get-primop name)))
|
||
|
(name->symbol (cont-name cont)))
|
||
|
(instruction 0)) ; last byte of closure instruction
|
||
|
cont))))
|
||
|
|
||
|
; --------------------
|
||
|
; Direct primitives.
|
||
|
|
||
|
; The simplest kind of primitive has fixed arity, corresponds to some
|
||
|
; single VM instruction, and takes its arguments in the usual way (all
|
||
|
; on the stack).
|
||
|
|
||
|
(define (direct-compilator type opcode)
|
||
|
(lambda (node level depth cont)
|
||
|
(let ((args (cdr (node-form node))))
|
||
|
(sequentially (if (null? args)
|
||
|
empty-segment
|
||
|
(push-all-but-last args level depth node))
|
||
|
(deliver-value (instruction opcode) cont)))))
|
||
|
|
||
|
(define (direct-closed-compilator opcode)
|
||
|
(lambda ()
|
||
|
(let ((arg-specs (vector-ref opcode-arg-specs opcode)))
|
||
|
(sequentially (if (pair? arg-specs)
|
||
|
(sequentially
|
||
|
(instruction (enum op protocol) (car arg-specs))
|
||
|
(instruction (enum op pop)))
|
||
|
(instruction (enum op protocol) 0))
|
||
|
(instruction opcode)
|
||
|
(instruction (enum op return))))))
|
||
|
|
||
|
(define (nargs->domain nargs)
|
||
|
(do ((nargs nargs (- nargs 1))
|
||
|
(l '() (cons value-type l)))
|
||
|
((= nargs 0) (make-some-values-type l))))
|
||
|
|
||
|
(define (get-primop-type id arg-count)
|
||
|
(or (any (lambda (foo)
|
||
|
(if (if (pair? (car foo))
|
||
|
(memq id (car foo))
|
||
|
(eq? id (car foo)))
|
||
|
(cadr foo)
|
||
|
#f))
|
||
|
primop-types)
|
||
|
(procedure-type (nargs->domain arg-count)
|
||
|
value-type
|
||
|
#t)))
|
||
|
|
||
|
; Types for various primops.
|
||
|
|
||
|
(define primop-types
|
||
|
`((with-continuation
|
||
|
,(proc (escape-type (proc () any-values-type #f))
|
||
|
any-arguments-type))
|
||
|
(eq?
|
||
|
,(proc (value-type value-type) boolean-type))
|
||
|
((number? integer? rational? real? complex? char? eof-object? port?)
|
||
|
,(proc (value-type) boolean-type))
|
||
|
(exact?
|
||
|
,(proc (number-type) boolean-type))
|
||
|
(exact->inexact
|
||
|
,(proc (exact-type) inexact-type))
|
||
|
(inexact->exact
|
||
|
,(proc (inexact-type) exact-type))
|
||
|
((exp log sin cos tan asin acos sqrt)
|
||
|
,(proc (number-type) number-type))
|
||
|
((atan)
|
||
|
,(proc (number-type number-type) number-type))
|
||
|
((floor)
|
||
|
,(proc (real-type) integer-type))
|
||
|
((real-part imag-part angle magnitude)
|
||
|
,(proc (complex-type) real-type))
|
||
|
((numerator denominator)
|
||
|
,(proc (rational-type) integer-type))
|
||
|
((make-polar make-rectangular)
|
||
|
,(proc (real-type real-type) complex-type))
|
||
|
((quotient remainder)
|
||
|
,(proc (integer-type integer-type) integer-type))
|
||
|
((bitwise-not)
|
||
|
,(proc (exact-integer-type) exact-integer-type))
|
||
|
((arithmetic-shift)
|
||
|
,(proc (exact-integer-type exact-integer-type)
|
||
|
exact-integer-type))
|
||
|
((char=? char<?)
|
||
|
,(proc (char-type char-type) boolean-type))
|
||
|
(char->ascii
|
||
|
,(proc (char-type) exact-integer-type))
|
||
|
(ascii->char
|
||
|
,(proc (exact-integer-type) char-type))
|
||
|
(string=?
|
||
|
,(proc (string-type string-type) boolean-type))
|
||
|
(open-channel
|
||
|
;; Can return #f
|
||
|
,(proc (string-type exact-integer-type) value-type))
|
||
|
(cons
|
||
|
,(proc (value-type value-type) pair-type))
|
||
|
(intern
|
||
|
,(proc (string-type) symbol-type))))
|
||
|
|
||
|
; Can't do I/O until the meta-types interface exports input-port-type and
|
||
|
; output-port-type.
|
||
|
|
||
|
; Define all the primitives that correspond to opcodes in the obvious way.
|
||
|
|
||
|
(do ((opcode 0 (+ opcode 1)))
|
||
|
((= opcode op-count))
|
||
|
(let ((arg-specs (vector-ref opcode-arg-specs opcode))
|
||
|
(name (enumerand->name opcode op)))
|
||
|
(cond ((memq name '(call-external-value return-from-interrupt return)))
|
||
|
((null? arg-specs)
|
||
|
(let ((type (proc () value-type)))
|
||
|
(define-compiler-primitive name type
|
||
|
(direct-compilator type opcode)
|
||
|
(direct-closed-compilator opcode))))
|
||
|
((not (number? (car arg-specs))))
|
||
|
((memq name '(+ * - / = < > <= >=
|
||
|
bitwise-ior bitwise-xor bitwise-and
|
||
|
make-string closed-apply)))
|
||
|
(else
|
||
|
(let ((type (get-primop-type name (car arg-specs))))
|
||
|
(define-compiler-primitive name type
|
||
|
(direct-compilator type opcode)
|
||
|
(direct-closed-compilator opcode)))))))
|
||
|
|
||
|
; --------------------
|
||
|
; Simple primitives are executed using a fixed instruction or
|
||
|
; instruction sequence.
|
||
|
|
||
|
(define (define-simple-primitive name type segment)
|
||
|
(let ((winner? (fixed-arity-procedure-type? type)))
|
||
|
(let ((nargs (if winner?
|
||
|
(procedure-type-arity type)
|
||
|
(error "n-ary simple primitive?!" name type))))
|
||
|
(define-compiler-primitive name type
|
||
|
(simple-compilator segment)
|
||
|
(simple-closed-compilator nargs segment)))))
|
||
|
|
||
|
(define (simple-compilator segment)
|
||
|
(lambda (node level depth cont)
|
||
|
(let ((args (cdr (node-form node))))
|
||
|
(sequentially (if (null? args)
|
||
|
empty-segment
|
||
|
(push-all-but-last args level depth node))
|
||
|
(deliver-value segment cont)))))
|
||
|
|
||
|
(define (simple-closed-compilator nargs segment)
|
||
|
(lambda ()
|
||
|
(sequentially (instruction (enum op protocol) nargs)
|
||
|
(instruction (enum op pop))
|
||
|
segment
|
||
|
(instruction (enum op return)))))
|
||
|
|
||
|
(define (symbol-append . syms)
|
||
|
(string->symbol (apply string-append
|
||
|
(map symbol->string syms))))
|
||
|
|
||
|
(define (define-stob-predicate name stob-name)
|
||
|
(define-simple-primitive name
|
||
|
(proc (value-type) boolean-type)
|
||
|
(instruction (enum op stored-object-has-type?)
|
||
|
(name->enumerand stob-name stob))))
|
||
|
|
||
|
(define-stob-predicate 'byte-vector? 'byte-vector)
|
||
|
(define-stob-predicate 'double? 'double)
|
||
|
(define-stob-predicate 'string? 'string)
|
||
|
|
||
|
; Making doubles
|
||
|
|
||
|
(let ((:value (sexp->type ':value #t))
|
||
|
(:double (sexp->type ':double #t)))
|
||
|
(define-simple-primitive 'make-double
|
||
|
(proc () :double)
|
||
|
(sequentially
|
||
|
(instruction-with-literal (enum op literal) 0)
|
||
|
(instruction (enum op push))
|
||
|
(instruction-with-literal (enum op literal) 0)
|
||
|
(instruction (enum op make-stored-object) 2 (enum stob double)))))
|
||
|
|
||
|
; Define primitives for record-like stored objects (e.g. pairs).
|
||
|
|
||
|
(define (define-data-struct-primitives name predicate maker . slots)
|
||
|
(let* ((def-prim (lambda (name type op . stuff)
|
||
|
(define-simple-primitive name type
|
||
|
(apply instruction (cons op stuff)))))
|
||
|
(type-byte (name->enumerand name stob))
|
||
|
(type (sexp->type (symbol-append ': name) #t)))
|
||
|
(define-stob-predicate predicate name)
|
||
|
(if maker
|
||
|
(def-prim maker
|
||
|
(procedure-type (nargs->domain (length slots)) type #t)
|
||
|
(enum op make-stored-object)
|
||
|
(length slots)
|
||
|
type-byte))
|
||
|
(do ((i 0 (+ i 1))
|
||
|
(slots slots (cdr slots)))
|
||
|
((null? slots))
|
||
|
(let ((slot (car slots)))
|
||
|
(if (car slot)
|
||
|
(def-prim (car slot)
|
||
|
(proc (type) value-type)
|
||
|
(enum op stored-object-ref) type-byte i))
|
||
|
(if (not (null? (cdr slot)))
|
||
|
(def-prim (cadr slot)
|
||
|
(proc (type value-type) unspecific-type)
|
||
|
(enum op stored-object-set!) type-byte i))))))
|
||
|
|
||
|
(for-each (lambda (stuff)
|
||
|
(apply define-data-struct-primitives stuff))
|
||
|
stob-data)
|
||
|
|
||
|
; Define primitives for vector-like stored objects.
|
||
|
|
||
|
(define (define-vector-primitives name element-type make length ref set!)
|
||
|
(let* ((type-byte (name->enumerand name stob))
|
||
|
(def-prim (lambda (name type op)
|
||
|
(define-simple-primitive name type
|
||
|
(instruction op type-byte))))
|
||
|
(type (sexp->type (symbol-append ': name) #t)))
|
||
|
(define-stob-predicate (symbol-append name '?) name)
|
||
|
(if (not (eq? name 'vector)) ; 2nd arg to make-vector is optional
|
||
|
(def-prim (symbol-append 'make- name)
|
||
|
(proc (exact-integer-type element-type) type)
|
||
|
make))
|
||
|
(def-prim (symbol-append name '- 'length)
|
||
|
(proc (type) exact-integer-type)
|
||
|
length)
|
||
|
(def-prim (symbol-append name '- 'ref)
|
||
|
(proc (type exact-integer-type) element-type)
|
||
|
ref)
|
||
|
(def-prim (symbol-append name '- 'set!)
|
||
|
(proc (type exact-integer-type element-type) unspecific-type)
|
||
|
set!)))
|
||
|
|
||
|
(for-each (lambda (name)
|
||
|
(define-vector-primitives name value-type
|
||
|
(enum op make-vector-object)
|
||
|
(enum op stored-object-length)
|
||
|
(enum op stored-object-indexed-ref)
|
||
|
(enum op stored-object-indexed-set!)))
|
||
|
'(vector record continuation extended-number template))
|
||
|
|
||
|
; SIGNAL-CONDITION is the same as TRAP.
|
||
|
|
||
|
(define-simple-primitive 'signal-condition (proc (pair-type) unspecific-type)
|
||
|
(instruction (enum op trap)))
|
||
|
|
||
|
|
||
|
; (primitive-catch (lambda (cont) ...))
|
||
|
|
||
|
(define-compiler-primitive 'primitive-catch
|
||
|
(proc ((proc (escape-type) any-values-type #f)) any-values-type)
|
||
|
;; (primitive-catch (lambda (cont) ...))
|
||
|
(lambda (node level depth cont)
|
||
|
(let* ((exp (node-form node))
|
||
|
(args (cdr exp)))
|
||
|
(maybe-push-continuation
|
||
|
(sequentially (instruction (enum op current-cont))
|
||
|
(instruction (enum op push))
|
||
|
;; If lambda exp, should do compile-lambda-code to
|
||
|
;; avoid consing closure...
|
||
|
(compile (car args) level 1 (fall-through-cont node 1))
|
||
|
(instruction (enum op call) 1))
|
||
|
0
|
||
|
cont)))
|
||
|
(lambda ()
|
||
|
(sequentially (instruction (enum op protocol) 1)
|
||
|
(instruction (enum op make-env) ;Seems unavoidable.
|
||
|
(high-byte 1)
|
||
|
(low-byte 1))
|
||
|
(instruction (enum op current-cont))
|
||
|
(instruction (enum op push))
|
||
|
(instruction (enum op local0) 1)
|
||
|
(instruction (enum op call) 1))))
|
||
|
|
||
|
; (call-with-values (lambda () ...producer...)
|
||
|
; (lambda args ...consumer...))
|
||
|
|
||
|
(define-compiler-primitive 'call-with-values
|
||
|
(proc ((proc () any-values-type #f)
|
||
|
any-procedure-type)
|
||
|
any-values-type)
|
||
|
(lambda (node level depth cont)
|
||
|
(let ((args (cdr (node-form node))))
|
||
|
(let ((producer (car args))
|
||
|
(consumer (cadr args)))
|
||
|
(maybe-push-continuation
|
||
|
(sequentially (compile consumer level 0 (fall-through-cont node 2))
|
||
|
(instruction (enum op push))
|
||
|
(maybe-push-continuation ; nothing maybe about it
|
||
|
(compile-call (make-node operator/call `(,producer))
|
||
|
level 0
|
||
|
(return-cont #f))
|
||
|
1
|
||
|
(fall-through-cont #f 0))
|
||
|
(instruction (enum op call-with-values)))
|
||
|
depth
|
||
|
cont))))
|
||
|
(lambda ()
|
||
|
;; producer and consumer on stack
|
||
|
(let ((label (make-label)))
|
||
|
(sequentially (instruction (enum op protocol) 2)
|
||
|
(instruction (enum op make-env)
|
||
|
(high-byte 2)
|
||
|
(low-byte 2))
|
||
|
(instruction (enum op local0) 1) ;consumer
|
||
|
(instruction (enum op push))
|
||
|
(instruction-using-label (enum op make-cont) label 1)
|
||
|
(instruction (enum op local0) 2) ;producer
|
||
|
(instruction (enum op call) 0)
|
||
|
(attach-label label
|
||
|
(instruction (enum op call-with-values)))))))
|
||
|
|
||
|
|
||
|
; --------------------
|
||
|
; Variable-arity primitives
|
||
|
|
||
|
(define (define-n-ary-compiler-primitive name result-type min-nargs
|
||
|
compilator closed)
|
||
|
(define-compiler-primitive name
|
||
|
(if result-type
|
||
|
(procedure-type any-arguments-type result-type #f)
|
||
|
#f)
|
||
|
(if compilator
|
||
|
(n-ary-primitive-compilator name min-nargs compilator)
|
||
|
compile-unknown-call)
|
||
|
closed))
|
||
|
|
||
|
(define (n-ary-primitive-compilator name min-nargs compilator)
|
||
|
(lambda (node level depth cont)
|
||
|
(let ((exp (node-form node)))
|
||
|
(if (>= (length (cdr exp)) min-nargs)
|
||
|
(compilator node level depth cont)
|
||
|
(begin (warn "too few arguments to primitive"
|
||
|
(schemify node))
|
||
|
(compile-unknown-call node level depth cont))))))
|
||
|
|
||
|
|
||
|
; APPLY wants the arguments on the stack, with the final list on top, and the
|
||
|
; procedure in *VAL*.
|
||
|
|
||
|
(define-compiler-primitive 'apply
|
||
|
(proc (any-procedure-type &rest value-type) any-values-type)
|
||
|
(n-ary-primitive-compilator 'apply 2
|
||
|
(lambda (node level depth cont)
|
||
|
(let ((exp (node-form node))) ; (apply proc arg1 arg2 arg3 rest)
|
||
|
(let* ((proc+args+rest (cdr exp))
|
||
|
(rest+args ; (rest arg3 arg2 arg1)
|
||
|
(reverse (cdr proc+args+rest)))
|
||
|
(args+rest+proc ; (arg1 arg2 arg3 rest proc)
|
||
|
(reverse (cons (car proc+args+rest) rest+args)))
|
||
|
(stack-nargs (length (cdr rest+args))))
|
||
|
(maybe-push-continuation
|
||
|
(sequentially (push-all-but-last args+rest+proc level 0 #f)
|
||
|
;; Operand is number of non-final arguments
|
||
|
(instruction (enum op apply)
|
||
|
(high-byte stack-nargs)
|
||
|
(low-byte stack-nargs)))
|
||
|
depth
|
||
|
cont)))))
|
||
|
(lambda ()
|
||
|
(sequentially (instruction (enum op protocol) args+nargs-protocol 2)
|
||
|
(instruction (enum op closed-apply)))))
|
||
|
|
||
|
; (values value1 value2 ...)
|
||
|
|
||
|
(define-n-ary-compiler-primitive 'values #f 0
|
||
|
(lambda (node level depth cont)
|
||
|
(let* ((args (cdr (node-form node)))
|
||
|
(nargs (length args)))
|
||
|
(if (= 1 nargs)
|
||
|
(compile (car args) level depth cont) ;+++
|
||
|
(maybe-push-continuation (sequentially (push-arguments node level 0)
|
||
|
(instruction (enum op values)
|
||
|
(high-byte nargs)
|
||
|
(low-byte nargs)))
|
||
|
depth
|
||
|
cont))))
|
||
|
(lambda ()
|
||
|
(sequentially (instruction (enum op protocol) args+nargs-protocol 0)
|
||
|
(instruction (enum op closed-values)))))
|
||
|
|
||
|
|
||
|
; (error message irritant1 irritant2)
|
||
|
; => (trap (cons 'error (cons message (cons irritant1 (cons irritant2 '())))))
|
||
|
|
||
|
(let ((cons-instruction
|
||
|
(instruction (enum op make-stored-object) 2 (enum stob pair))))
|
||
|
|
||
|
(define-n-ary-compiler-primitive 'error error-type 1
|
||
|
(lambda (node level depth cont)
|
||
|
(let ((exp (node-form node)))
|
||
|
(let ((args (cdr exp)))
|
||
|
(sequentially (instruction-with-literal (enum op literal) 'error)
|
||
|
(instruction (enum op push))
|
||
|
(push-arguments node level (+ depth 1))
|
||
|
(instruction-with-literal (enum op literal) '())
|
||
|
(apply sequentially
|
||
|
(map (lambda (arg) cons-instruction) args))
|
||
|
cons-instruction
|
||
|
(deliver-value (instruction (enum op trap)) cont)))))
|
||
|
(lambda ()
|
||
|
; stack at start is: irritants message
|
||
|
(sequentially (instruction (enum op protocol)
|
||
|
two-byte-nargs+list-protocol
|
||
|
0 ; (high-byte 1)
|
||
|
1) ; (low-byte 1)
|
||
|
(instruction (enum op pop)) ; list into *val*
|
||
|
cons-instruction
|
||
|
(instruction (enum op push))
|
||
|
(instruction-with-literal (enum op literal) 'error)
|
||
|
(instruction (enum op push))
|
||
|
(instruction (enum op stack-ref) 1)
|
||
|
cons-instruction
|
||
|
(instruction (enum op trap))
|
||
|
(instruction (enum op return))))))
|
||
|
|
||
|
|
||
|
; (call-external-value external-routine arg ...)
|
||
|
|
||
|
(define-n-ary-compiler-primitive 'call-external-value value-type 1
|
||
|
#f ;Could be done
|
||
|
(lambda ()
|
||
|
(sequentially (instruction (enum op protocol) args+nargs-protocol 1)
|
||
|
(instruction (enum op call-external-value))
|
||
|
(instruction (enum op return)))))
|
||
|
|
||
|
(let ((n-ary-constructor
|
||
|
(lambda (name type type-byte)
|
||
|
(define-n-ary-compiler-primitive name type 0
|
||
|
(lambda (node level depth cont)
|
||
|
(let ((args (cdr (node-form node))))
|
||
|
(sequentially (if (null? args)
|
||
|
empty-segment
|
||
|
(push-all-but-last args level depth node))
|
||
|
(deliver-value
|
||
|
(instruction (enum op make-stored-object)
|
||
|
(length args)
|
||
|
type-byte)
|
||
|
cont))))
|
||
|
(lambda ()
|
||
|
(sequentially
|
||
|
(instruction (enum op protocol) args+nargs-protocol 0)
|
||
|
(instruction (enum op closed-make-stored-object) type-byte)
|
||
|
(instruction (enum op return))))))))
|
||
|
(n-ary-constructor 'vector vector-type (enum stob vector))
|
||
|
(n-ary-constructor 'record #f (enum stob record)))
|
||
|
|
||
|
; READ-CHAR, PEEK-CHAR and WRITE-CHAR
|
||
|
|
||
|
(let ((define-char-io
|
||
|
(lambda (id opcode type)
|
||
|
(define-compiler-primitive id
|
||
|
type
|
||
|
(lambda (node level depth cont)
|
||
|
(if (node-ref node 'type-error)
|
||
|
(compile-unknown-call node level depth cont)
|
||
|
(let ((args (cdr (node-form node))))
|
||
|
(if (null? args)
|
||
|
(deliver-value (instruction opcode 1) cont)
|
||
|
(sequentially
|
||
|
(push-all-but-last args level depth node)
|
||
|
(deliver-value (instruction opcode 0) cont))))))
|
||
|
(lambda ()
|
||
|
(make-dispatch-protocol
|
||
|
; Zero arguments
|
||
|
(sequentially
|
||
|
(instruction opcode 1)
|
||
|
(instruction (enum op return)))
|
||
|
; One argument
|
||
|
(sequentially
|
||
|
(instruction (enum op pop))
|
||
|
(instruction opcode 0)
|
||
|
(instruction (enum op return)))
|
||
|
empty-segment
|
||
|
empty-segment))))))
|
||
|
(define-char-io 'read-char
|
||
|
(enum op read-char)
|
||
|
(proc (&opt value-type) value-type))
|
||
|
(define-char-io 'peek-char
|
||
|
(enum op peek-char)
|
||
|
(proc (&opt value-type) value-type)))
|
||
|
|
||
|
(let ((define-char-io
|
||
|
(lambda (id opcode type)
|
||
|
(define-compiler-primitive id
|
||
|
type
|
||
|
(lambda (node level depth cont)
|
||
|
(if (node-ref node 'type-error)
|
||
|
(compile-unknown-call node level depth cont)
|
||
|
(let ((args (cdr (node-form node))))
|
||
|
(sequentially
|
||
|
(push-all-but-last args level depth node)
|
||
|
(if (null? (cdr args))
|
||
|
(deliver-value (instruction opcode 1) cont)
|
||
|
(sequentially
|
||
|
(deliver-value (instruction opcode 0) cont)))))))
|
||
|
(lambda ()
|
||
|
(make-dispatch-protocol
|
||
|
empty-segment
|
||
|
; One argument
|
||
|
(sequentially
|
||
|
(instruction (enum op pop))
|
||
|
(instruction opcode 1)
|
||
|
(instruction (enum op return)))
|
||
|
; Two arguments
|
||
|
(sequentially
|
||
|
(instruction (enum op pop))
|
||
|
(instruction opcode 0)
|
||
|
(instruction (enum op return)))
|
||
|
empty-segment))))))
|
||
|
(define-char-io 'write-char
|
||
|
(enum op write-char)
|
||
|
(proc (char-type &opt value-type) unspecific-type)))
|
||
|
|
||
|
; Timings in 0.47 to figure out how to handle the optional ports.
|
||
|
;
|
||
|
; reading 10**6 characters (no buffer underflow)
|
||
|
; empty loop time: 3.44 seconds
|
||
|
; read-char time: 3.68 seconds ; special primitive, exceptions
|
||
|
; xread-char time: 9.04 seconds ; special primitive, no exceptions
|
||
|
; xxread-char time: 14.05 seconds ; no special primitive
|
||
|
; Currently, looping through a 10**6 character file takes 1.51 seconds or
|
||
|
; 2.50 seconds if you count the number of characters.
|
||
|
|
||
|
;----------------
|
||
|
; Variable-arity arithmetic primitives.
|
||
|
|
||
|
; +, *, bitwise-... take any number of arguments.
|
||
|
|
||
|
(let ((define+*
|
||
|
(lambda (id opcode identity type)
|
||
|
(define-compiler-primitive id
|
||
|
(proc (&rest type) type)
|
||
|
(lambda (node level depth cont)
|
||
|
(if (node-ref node 'type-error)
|
||
|
(compile-unknown-call node level depth cont)
|
||
|
(let ((args (cdr (node-form node))))
|
||
|
(cond ((null? args)
|
||
|
(deliver-value
|
||
|
(instruction-with-literal (enum op literal)
|
||
|
identity)
|
||
|
cont))
|
||
|
((null? (cdr args))
|
||
|
(call-on-arg-and-id opcode identity (car args)
|
||
|
node level depth cont))
|
||
|
(else
|
||
|
(call-on-args opcode args node level depth cont))))))
|
||
|
(lambda ()
|
||
|
(make-dispatch-protocol
|
||
|
; No arguments
|
||
|
(sequentially
|
||
|
(instruction-with-literal (enum op literal) identity)
|
||
|
(instruction (enum op return)))
|
||
|
; One argument
|
||
|
(sequentially
|
||
|
(instruction-with-literal (enum op literal) identity)
|
||
|
(instruction opcode)
|
||
|
(instruction (enum op return)))
|
||
|
; Two arguments
|
||
|
(sequentially
|
||
|
(instruction (enum op pop))
|
||
|
(instruction opcode)
|
||
|
(instruction (enum op return)))
|
||
|
; More than two arguments
|
||
|
(sequentially
|
||
|
(instruction (enum op pop)) ; pop off nargs
|
||
|
(instruction (enum op binary-reduce1))
|
||
|
(instruction opcode)
|
||
|
(instruction (enum op binary-reduce2))
|
||
|
(instruction (enum op return)))))))))
|
||
|
(define+* '+ (enum op +) 0 number-type)
|
||
|
(define+* '* (enum op *) 1 number-type)
|
||
|
(define+* 'bitwise-ior (enum op bitwise-ior) 0 exact-integer-type)
|
||
|
(define+* 'bitwise-xor (enum op bitwise-xor) 0 exact-integer-type)
|
||
|
(define+* 'bitwise-and (enum op bitwise-and) -1 exact-integer-type))
|
||
|
|
||
|
; = and < and so forth take two or more arguments.
|
||
|
|
||
|
(let ((define=<
|
||
|
(lambda (id opcode)
|
||
|
(define-compiler-primitive id
|
||
|
(proc (real-type real-type &rest real-type) boolean-type)
|
||
|
(lambda (node level depth cont)
|
||
|
(if (node-ref node 'type-error)
|
||
|
(compile-unknown-call node level depth cont)
|
||
|
(let ((args (cdr (node-form node))))
|
||
|
(if (= (length args) 2)
|
||
|
(call-on-args opcode args node level depth cont)
|
||
|
(compile-unknown-call node level depth cont)))))
|
||
|
(lambda ()
|
||
|
(make-dispatch-protocol
|
||
|
empty-segment
|
||
|
empty-segment
|
||
|
; Two arguments
|
||
|
(sequentially
|
||
|
(instruction (enum op pop)) ; get first argument
|
||
|
(instruction opcode)
|
||
|
(instruction (enum op return)))
|
||
|
; More than two arguments
|
||
|
(sequentially
|
||
|
(instruction (enum op pop))
|
||
|
(instruction (enum op binary-reduce1))
|
||
|
(instruction opcode)
|
||
|
(instruction (enum op binary-comparison-reduce2))
|
||
|
(instruction (enum op return)))))))))
|
||
|
(define=< '= (enum op =))
|
||
|
(define=< '< (enum op <))
|
||
|
(define=< '> (enum op >))
|
||
|
(define=< '<= (enum op <=))
|
||
|
(define=< '>= (enum op >=)))
|
||
|
|
||
|
; Returns code to apply OPCODE to IDENTITY and ARGUMENT.
|
||
|
|
||
|
(define (call-on-arg-and-id opcode identity argument node level depth cont)
|
||
|
(sequentially (instruction-with-literal (enum op literal) identity)
|
||
|
(instruction (enum op push))
|
||
|
(compile argument level (+ depth 1) (fall-through-cont node 1))
|
||
|
(deliver-value (instruction opcode) cont)))
|
||
|
|
||
|
; Returns code to redue ARGS using OPCODE.
|
||
|
|
||
|
(define (call-on-args opcode args node level depth cont)
|
||
|
(let ((do-arg (lambda (arg index)
|
||
|
(compile arg
|
||
|
level
|
||
|
(if (= index 1) depth (+ depth 1))
|
||
|
(fall-through-cont node index)))))
|
||
|
(let loop ((args (cdr args)) (i 2) (code (do-arg (car args) 1)))
|
||
|
(if (null? args)
|
||
|
(deliver-value code cont)
|
||
|
(loop (cdr args)
|
||
|
(+ i 1)
|
||
|
(sequentially code
|
||
|
(instruction (enum op push))
|
||
|
(do-arg (car args) i)
|
||
|
(instruction opcode)))))))
|
||
|
|
||
|
(define op/unspecific (get-operator 'unspecific))
|
||
|
(define op/literal (get-operator 'literal))
|
||
|
|
||
|
; -, and / take one or two arguments.
|
||
|
|
||
|
(let ((define-one-or-two
|
||
|
(lambda (id opcode default-arg)
|
||
|
(define-compiler-primitive id
|
||
|
(proc (number-type &opt number-type) number-type)
|
||
|
(lambda (node level depth cont)
|
||
|
(if (node-ref node 'type-error)
|
||
|
(compile-unknown-call node level depth cont)
|
||
|
(let* ((args (cdr (node-form node)))
|
||
|
(args (if (null? (cdr args))
|
||
|
(list (make-node op/literal default-arg)
|
||
|
(car args))
|
||
|
args)))
|
||
|
(sequentially
|
||
|
(push-all-but-last args level depth node)
|
||
|
(deliver-value (instruction opcode) cont)))))
|
||
|
(lambda ()
|
||
|
(make-dispatch-protocol
|
||
|
empty-segment
|
||
|
; One argument
|
||
|
(sequentially
|
||
|
(instruction-with-literal (enum op literal) default-arg)
|
||
|
(instruction (enum op push))
|
||
|
(instruction (enum op stack-ref) 1)
|
||
|
(instruction opcode)
|
||
|
(instruction (enum op return)))
|
||
|
; Two arguments
|
||
|
(sequentially
|
||
|
(instruction (enum op pop))
|
||
|
(instruction opcode)
|
||
|
(instruction (enum op return)))
|
||
|
empty-segment))))))
|
||
|
(define-one-or-two '- (enum op -) 0)
|
||
|
(define-one-or-two '/ (enum op /) 1))
|
||
|
|
||
|
; make-vector and make-string take one or two arguments.
|
||
|
|
||
|
(let ((define-one-or-two
|
||
|
(lambda (id op-segment default-arg default-arg-segment type)
|
||
|
(define-compiler-primitive id
|
||
|
type
|
||
|
(lambda (node level depth cont)
|
||
|
(if (node-ref node 'type-error)
|
||
|
(compile-unknown-call node level depth cont)
|
||
|
(let* ((args (cdr (node-form node)))
|
||
|
(args (if (null? (cdr args))
|
||
|
(list (car args) default-arg)
|
||
|
args)))
|
||
|
(sequentially
|
||
|
(push-all-but-last args level depth node)
|
||
|
(deliver-value op-segment cont)))))
|
||
|
(lambda ()
|
||
|
(make-dispatch-protocol
|
||
|
empty-segment
|
||
|
; One argument
|
||
|
(sequentially
|
||
|
default-arg-segment
|
||
|
op-segment
|
||
|
(instruction (enum op return)))
|
||
|
; Two arguments
|
||
|
(sequentially
|
||
|
(instruction (enum op pop))
|
||
|
op-segment
|
||
|
(instruction (enum op return)))
|
||
|
empty-segment))))))
|
||
|
(define-one-or-two 'make-vector
|
||
|
(instruction (enum op make-vector-object) (enum stob vector))
|
||
|
(make-node op/unspecific '(unspecific))
|
||
|
(instruction (enum op unspecific))
|
||
|
(proc (number-type &opt value-type) vector-type))
|
||
|
(define-one-or-two 'make-string
|
||
|
(instruction (enum op make-string))
|
||
|
(make-node op/literal #\?)
|
||
|
(instruction-with-literal (enum op literal) #\?)
|
||
|
(proc (number-type &opt char-type) string-type)))
|
||
|
|
||
|
; --------------------
|
||
|
; Utilities
|
||
|
|
||
|
(define (push-all-but-last args level depth source-info)
|
||
|
(let recur ((args args) (depth depth) (i 1))
|
||
|
(let ((first-code
|
||
|
(compile (car args) level depth (fall-through-cont source-info i))))
|
||
|
(if (null? (cdr args))
|
||
|
first-code
|
||
|
(sequentially first-code
|
||
|
(instruction (enum op push))
|
||
|
(recur (cdr args) (+ depth 1) (+ i 1)))))))
|
||
|
|
||
|
; Building primitives that use the computed-goto provided by the
|
||
|
; protocol dispatcher.
|
||
|
|
||
|
(define dispatch-protocol-size
|
||
|
(segment-size (instruction (enum op protocol) nary-dispatch-protocol
|
||
|
0 0 0 0)))
|
||
|
|
||
|
(define (make-dispatch-protocol zero-args one-arg two-args three-plus-args)
|
||
|
(sequentially
|
||
|
(instruction (enum op protocol) nary-dispatch-protocol
|
||
|
(if (= 0 (segment-size zero-args))
|
||
|
0
|
||
|
dispatch-protocol-size)
|
||
|
(if (= 0 (segment-size one-arg))
|
||
|
0
|
||
|
(+ dispatch-protocol-size
|
||
|
(segment-size zero-args)))
|
||
|
(if (= 0 (segment-size two-args))
|
||
|
0
|
||
|
(+ dispatch-protocol-size
|
||
|
(segment-size zero-args)
|
||
|
(segment-size one-arg)))
|
||
|
(if (= 0 (segment-size three-plus-args))
|
||
|
0
|
||
|
(+ dispatch-protocol-size
|
||
|
(segment-size zero-args)
|
||
|
(segment-size one-arg)
|
||
|
(segment-size two-args))))
|
||
|
zero-args
|
||
|
one-arg
|
||
|
two-args
|
||
|
three-plus-args))
|
||
|
|