X-Git-Url: http://git.hcoop.net/bpt/guile.git/blobdiff_plain/dece041203724bcf4bf74dbec459f5dbae4aa7ed..2bd0f970a4009ed4ae03632ec018f04cf2810181:/module/system/vm/assembler.scm diff --git a/module/system/vm/assembler.scm b/module/system/vm/assembler.scm index 68c86ae9e..bb4ddf742 100644 --- a/module/system/vm/assembler.scm +++ b/module/system/vm/assembler.scm @@ -1,6 +1,6 @@ ;;; Guile bytecode assembler -;;; Copyright (C) 2001, 2009, 2010, 2012, 2013, 2014 Free Software Foundation, Inc. +;;; Copyright (C) 2001, 2009, 2010, 2012, 2013, 2014, 2015 Free Software Foundation, Inc. ;;; ;;; This library is free software; you can redistribute it and/or ;;; modify it under the terms of the GNU Lesser General Public @@ -57,6 +57,124 @@ #:use-module (srfi srfi-9) #:use-module (srfi srfi-11) #:export (make-assembler + + emit-call + emit-call-label + emit-tail-call + emit-tail-call-label + (emit-receive* . emit-receive) + emit-receive-values + emit-return + emit-return-values + emit-call/cc + emit-abort + (emit-builtin-ref* . emit-builtin-ref) + emit-br-if-nargs-ne + emit-br-if-nargs-lt + emit-br-if-nargs-gt + emit-assert-nargs-ee + emit-assert-nargs-ge + emit-assert-nargs-le + emit-alloc-frame + emit-reset-frame + emit-assert-nargs-ee/locals + emit-br-if-npos-gt + emit-bind-kwargs + emit-bind-rest + emit-br + emit-br-if-true + emit-br-if-null + emit-br-if-nil + emit-br-if-pair + emit-br-if-struct + emit-br-if-char + emit-br-if-tc7 + (emit-br-if-eq* . emit-br-if-eq) + (emit-br-if-eqv* . emit-br-if-eqv) + (emit-br-if-equal* . emit-br-if-equal) + (emit-br-if-=* . emit-br-if-=) + (emit-br-if-<* . emit-br-if-<) + (emit-br-if-<=* . emit-br-if-<=) + (emit-br-if-logtest* . emit-br-if-logtest) + (emit-mov* . emit-mov) + (emit-box* . emit-box) + (emit-box-ref* . emit-box-ref) + (emit-box-set!* . emit-box-set!) + emit-make-closure + (emit-free-ref* . emit-free-ref) + (emit-free-set!* . emit-free-set!) + emit-current-module + emit-resolve + (emit-define!* . emit-define!) + emit-toplevel-box + emit-module-box + emit-prompt + (emit-wind* . emit-wind) + emit-unwind + (emit-push-fluid* . emit-push-fluid) + emit-pop-fluid + (emit-fluid-ref* . emit-fluid-ref) + (emit-fluid-set* . emit-fluid-set) + (emit-string-length* . emit-string-length) + (emit-string-ref* . emit-string-ref) + (emit-string->number* . emit-string->number) + (emit-string->symbol* . emit-string->symbol) + (emit-symbol->keyword* . emit-symbol->keyword) + (emit-cons* . emit-cons) + (emit-car* . emit-car) + (emit-cdr* . emit-cdr) + (emit-set-car!* . emit-set-car!) + (emit-set-cdr!* . emit-set-cdr!) + (emit-add* . emit-add) + (emit-add1* . emit-add1) + (emit-sub* . emit-sub) + (emit-sub1* . emit-sub1) + (emit-mul* . emit-mul) + (emit-div* . emit-div) + (emit-quo* . emit-quo) + (emit-rem* . emit-rem) + (emit-mod* . emit-mod) + (emit-ash* . emit-ash) + (emit-logand* . emit-logand) + (emit-logior* . emit-logior) + (emit-logxor* . emit-logxor) + (emit-make-vector* . emit-make-vector) + (emit-make-vector/immediate* . emit-make-vector/immediate) + (emit-vector-length* . emit-vector-length) + (emit-vector-ref* . emit-vector-ref) + (emit-vector-ref/immediate* . emit-vector-ref/immediate) + (emit-vector-set!* . emit-vector-set!) + (emit-vector-set!/immediate* . emit-vector-set!/immediate) + (emit-struct-vtable* . emit-struct-vtable) + (emit-allocate-struct/immediate* . emit-allocate-struct/immediate) + (emit-struct-ref/immediate* . emit-struct-ref/immediate) + (emit-struct-set!/immediate* . emit-struct-set!/immediate) + (emit-allocate-struct* . emit-allocate-struct) + (emit-struct-ref* . emit-struct-ref) + (emit-struct-set!* . emit-struct-set!) + (emit-class-of* . emit-class-of) + (emit-make-array* . emit-make-array) + (emit-bv-u8-ref* . emit-bv-u8-ref) + (emit-bv-s8-ref* . emit-bv-s8-ref) + (emit-bv-u16-ref* . emit-bv-u16-ref) + (emit-bv-s16-ref* . emit-bv-s16-ref) + (emit-bv-u32-ref* . emit-bv-u32-ref) + (emit-bv-s32-ref* . emit-bv-s32-ref) + (emit-bv-u64-ref* . emit-bv-u64-ref) + (emit-bv-s64-ref* . emit-bv-s64-ref) + (emit-bv-f32-ref* . emit-bv-f32-ref) + (emit-bv-f64-ref* . emit-bv-f64-ref) + (emit-bv-u8-set!* . emit-bv-u8-set!) + (emit-bv-s8-set!* . emit-bv-s8-set!) + (emit-bv-u16-set!* . emit-bv-u16-set!) + (emit-bv-s16-set!* . emit-bv-s16-set!) + (emit-bv-u32-set!* . emit-bv-u32-set!) + (emit-bv-s32-set!* . emit-bv-s32-set!) + (emit-bv-u64-set!* . emit-bv-u64-set!) + (emit-bv-s64-set!* . emit-bv-s64-set!) + (emit-bv-f32-set!* . emit-bv-f32-set!) + (emit-bv-f64-set!* . emit-bv-f64-set!) + emit-text link-assembly)) @@ -65,14 +183,16 @@ ;; Like define-inlinable, but only for first-order uses of the defined ;; routine. Should residualize less code. -(define-syntax define-inline - (lambda (x) - (syntax-case x () - ((_ (name arg ...) body ...) - (with-syntax (((temp ...) (generate-temporaries #'(arg ...)))) - #`(define-syntax-rule (name temp ...) - (let ((arg temp) ...) - body ...))))))) +(eval-when (expand) + (define-syntax define-inline + (lambda (x) + (syntax-case x () + ((_ (name arg ...) body ...) + (with-syntax (((temp ...) (generate-temporaries #'(arg ...)))) + #`(eval-when (expand) + (define-syntax-rule (name temp ...) + (let ((arg temp) ...) + body ...))))))))) ;;; Bytecode consists of 32-bit units, often subdivided in some way. ;;; These helpers create one 32-bit unit from multiple components. @@ -123,24 +243,25 @@ (error "out of range" z)) (logior x (ash y 8) (ash z 16) (ash w 24))) -(define-syntax pack-flags - (syntax-rules () - ;; Add clauses as needed. - ((pack-flags f1 f2) (logior (if f1 (ash 1 0) 0) - (if f2 (ash 2 0) 0))))) +(eval-when (expand) + (define-syntax pack-flags + (syntax-rules () + ;; Add clauses as needed. + ((pack-flags f1 f2) (logior (if f1 (ash 1 0) 0) + (if f2 (ash 2 0) 0)))))) ;;; Helpers to read and write 32-bit units in a buffer. -(define-syntax-rule (u32-ref buf n) +(define-inline (u32-ref buf n) (bytevector-u32-native-ref buf (* n 4))) -(define-syntax-rule (u32-set! buf n val) +(define-inline (u32-set! buf n val) (bytevector-u32-native-set! buf (* n 4) val)) -(define-syntax-rule (s32-ref buf n) +(define-inline (s32-ref buf n) (bytevector-s32-native-ref buf (* n 4))) -(define-syntax-rule (s32-set! buf n val) +(define-inline (s32-set! buf n val) (bytevector-s32-native-set! buf (* n 4) val)) @@ -149,10 +270,11 @@ ;;; A entry collects metadata for one procedure. Procedures are ;;; written as contiguous ranges of bytecode. ;;; -(define-syntax-rule (assert-match arg pattern kind) - (let ((x arg)) - (unless (match x (pattern #t) (_ #f)) - (error (string-append "expected " kind) x)))) +(eval-when (expand) + (define-syntax-rule (assert-match arg pattern kind) + (let ((x arg)) + (unless (match x (pattern #t) (_ #f)) + (error (string-append "expected " kind) x))))) (define-record-type (%make-meta label properties low-pc high-pc arities) @@ -185,7 +307,8 @@ (high-pc arity-high-pc set-arity-high-pc!) (definitions arity-definitions set-arity-definitions!)) -(define-syntax *block-size* (identifier-syntax 32)) +(eval-when (expand) + (define-syntax *block-size* (identifier-syntax 32))) ;;; An assembler collects all of the words emitted during assembly, and ;;; also maintains ancillary information such as the constant table, a @@ -202,7 +325,8 @@ constants inits shstrtab next-section-number meta sources - dead-slot-maps) + dead-slot-maps + to-file?) asm? ;; We write bytecode into what is logically a growable vector, @@ -285,13 +409,15 @@ ;; DEAD-SLOT-MAP is a bitfield of slots that are dead at call sites, ;; as an integer. ;; - (dead-slot-maps asm-dead-slot-maps set-asm-dead-slot-maps!)) + (dead-slot-maps asm-dead-slot-maps set-asm-dead-slot-maps!) + (to-file? asm-to-file?)) (define-inline (fresh-block) (make-u32vector *block-size*)) (define* (make-assembler #:key (word-size (target-word-size)) - (endianness (target-endianness))) + (endianness (target-endianness)) + (to-file? #t)) "Create an assembler for a given target @var{word-size} and @var{endianness}, falling back to appropriate values for the configured target." @@ -300,7 +426,7 @@ target." word-size endianness vlist-null '() (make-string-table) 1 - '() '() '())) + '() '() '() to-file?)) (define (intern-section-name! asm string) "Add a string to the section name table (shstrtab)." @@ -365,136 +491,294 @@ later by the linker." ;;; opcode in `(instruction-list)'. ;;; -(eval-when (expand compile load eval) +(eval-when (expand) (define (id-append ctx a b) - (datum->syntax ctx (symbol-append (syntax->datum a) (syntax->datum b))))) - -(define-syntax assembler - (lambda (x) - (define-syntax op-case - (lambda (x) - (syntax-case x () - ((_ asm name ((type arg ...) code ...) clause ...) - #`(if (eq? name 'type) - (with-syntax (((arg ...) (generate-temporaries #'(arg ...)))) - #'((arg ...) - code ...)) - (op-case asm name clause ...))) - ((_ asm name) - #'(error "unmatched name" name))))) - - (define (pack-first-word asm opcode type) - (with-syntax ((opcode opcode)) + (datum->syntax ctx (symbol-append (syntax->datum a) (syntax->datum b)))) + + (define-syntax assembler + (lambda (x) + (define-syntax op-case + (lambda (x) + (syntax-case x () + ((_ asm name ((type arg ...) code ...) clause ...) + #`(if (eq? name 'type) + (with-syntax (((arg ...) (generate-temporaries #'(arg ...)))) + #'((arg ...) + code ...)) + (op-case asm name clause ...))) + ((_ asm name) + #'(error "unmatched name" name))))) + + (define (pack-first-word asm opcode type) + (with-syntax ((opcode opcode)) + (op-case + asm type + ((U8_X24) + (emit asm opcode)) + ((U8_U24 arg) + (emit asm (pack-u8-u24 opcode arg))) + ((U8_L24 label) + (record-label-reference asm label) + (emit asm opcode)) + ((U8_U8_I16 a imm) + (emit asm (pack-u8-u8-u16 opcode a (object-address imm)))) + ((U8_U12_U12 a b) + (emit asm (pack-u8-u12-u12 opcode a b))) + ((U8_U8_U8_U8 a b c) + (emit asm (pack-u8-u8-u8-u8 opcode a b c)))))) + + (define (pack-tail-word asm type) (op-case asm type - ((U8_X24) - (emit asm opcode)) - ((U8_U24 arg) - (emit asm (pack-u8-u24 opcode arg))) - ((U8_L24 label) + ((U8_U24 a b) + (emit asm (pack-u8-u24 a b))) + ((U8_L24 a label) (record-label-reference asm label) - (emit asm opcode)) - ((U8_U8_I16 a imm) - (emit asm (pack-u8-u8-u16 opcode a (object-address imm)))) - ((U8_U12_U12 a b) - (emit asm (pack-u8-u12-u12 opcode a b))) - ((U8_U8_U8_U8 a b c) - (emit asm (pack-u8-u8-u8-u8 opcode a b c)))))) - - (define (pack-tail-word asm type) - (op-case - asm type - ((U8_U24 a b) - (emit asm (pack-u8-u24 a b))) - ((U8_L24 a label) - (record-label-reference asm label) - (emit asm a)) - ((U32 a) - (emit asm a)) - ((I32 imm) - (let ((val (object-address imm))) - (unless (zero? (ash val -32)) - (error "FIXME: enable truncation of negative fixnums when cross-compiling")) - (emit asm val))) - ((A32 imm) - (unless (= (asm-word-size asm) 8) - (error "make-long-immediate unavailable for this target")) - (emit asm (ash (object-address imm) -32)) - (emit asm (logand (object-address imm) (1- (ash 1 32))))) - ((B32)) - ((N32 label) - (record-far-label-reference asm label) - (emit asm 0)) - ((S32 label) - (record-far-label-reference asm label) - (emit asm 0)) - ((L32 label) - (record-far-label-reference asm label) - (emit asm 0)) - ((LO32 label offset) - (record-far-label-reference asm label - (* offset (/ (asm-word-size asm) 4))) - (emit asm 0)) - ((X8_U24 a) - (emit asm (pack-u8-u24 0 a))) - ((X8_L24 label) - (record-label-reference asm label) - (emit asm 0)) - ((B1_X7_L24 a label) - (record-label-reference asm label) - (emit asm (pack-u1-u7-u24 (if a 1 0) 0 0))) - ((B1_U7_L24 a b label) - (record-label-reference asm label) - (emit asm (pack-u1-u7-u24 (if a 1 0) b 0))) - ((B1_X31 a) - (emit asm (pack-u1-u7-u24 (if a 1 0) 0 0))) - ((B1_X7_U24 a b) - (emit asm (pack-u1-u7-u24 (if a 1 0) 0 b))))) - - (syntax-case x () - ((_ name opcode word0 word* ...) - (with-syntax ((((formal0 ...) - code0 ...) - (pack-first-word #'asm - (syntax->datum #'opcode) - (syntax->datum #'word0))) - ((((formal* ...) - code* ...) ...) - (map (lambda (word) (pack-tail-word #'asm word)) - (syntax->datum #'(word* ...))))) - #'(lambda (asm formal0 ... formal* ... ...) - (unless (asm? asm) (error "not an asm")) - code0 ... - code* ... ... - (reset-asm-start! asm))))))) + (emit asm a)) + ((U32 a) + (emit asm a)) + ((I32 imm) + (let ((val (object-address imm))) + (unless (zero? (ash val -32)) + (error "FIXME: enable truncation of negative fixnums when cross-compiling")) + (emit asm val))) + ((A32 imm) + (unless (= (asm-word-size asm) 8) + (error "make-long-immediate unavailable for this target")) + (emit asm (ash (object-address imm) -32)) + (emit asm (logand (object-address imm) (1- (ash 1 32))))) + ((B32)) + ((N32 label) + (record-far-label-reference asm label) + (emit asm 0)) + ((S32 label) + (record-far-label-reference asm label) + (emit asm 0)) + ((L32 label) + (record-far-label-reference asm label) + (emit asm 0)) + ((LO32 label offset) + (record-far-label-reference asm label + (* offset (/ (asm-word-size asm) 4))) + (emit asm 0)) + ((X8_U24 a) + (emit asm (pack-u8-u24 0 a))) + ((X8_L24 label) + (record-label-reference asm label) + (emit asm 0)) + ((B1_X7_L24 a label) + (record-label-reference asm label) + (emit asm (pack-u1-u7-u24 (if a 1 0) 0 0))) + ((B1_U7_L24 a b label) + (record-label-reference asm label) + (emit asm (pack-u1-u7-u24 (if a 1 0) b 0))) + ((B1_X31 a) + (emit asm (pack-u1-u7-u24 (if a 1 0) 0 0))) + ((B1_X7_U24 a b) + (emit asm (pack-u1-u7-u24 (if a 1 0) 0 b))))) + + (syntax-case x () + ((_ name opcode word0 word* ...) + (with-syntax ((((formal0 ...) + code0 ...) + (pack-first-word #'asm + (syntax->datum #'opcode) + (syntax->datum #'word0))) + ((((formal* ...) + code* ...) ...) + (map (lambda (word) (pack-tail-word #'asm word)) + (syntax->datum #'(word* ...))))) + #'(lambda (asm formal0 ... formal* ... ...) + (unless (asm? asm) (error "not an asm")) + code0 ... + code* ... ... + (reset-asm-start! asm)))))))) (define assemblers (make-hash-table)) -(define-syntax define-assembler - (lambda (x) - (syntax-case x () - ((_ name opcode kind arg ...) - (with-syntax ((emit (id-append #'name #'emit- #'name))) - #'(begin - (define emit +(eval-when (expand) + (define-syntax define-assembler + (lambda (x) + (syntax-case x () + ((_ name opcode kind arg ...) + (with-syntax ((emit (id-append #'name #'emit- #'name))) + #'(define emit (let ((emit (assembler name opcode arg ...))) (hashq-set! assemblers 'name emit) - emit)) - (export emit))))))) - -(define-syntax visit-opcodes - (lambda (x) - (syntax-case x () - ((visit-opcodes macro arg ...) - (with-syntax (((inst ...) - (map (lambda (x) (datum->syntax #'macro x)) - (instruction-list)))) - #'(begin - (macro arg ... . inst) - ...)))))) + emit))))))) + + (define-syntax visit-opcodes + (lambda (x) + (syntax-case x () + ((visit-opcodes macro arg ...) + (with-syntax (((inst ...) + (map (lambda (x) (datum->syntax #'macro x)) + (instruction-list)))) + #'(begin + (macro arg ... . inst) + ...))))))) (visit-opcodes define-assembler) +(eval-when (expand) + + ;; Some operands are encoded using a restricted subset of the full + ;; 24-bit local address space, in order to make the bytecode more + ;; dense in the usual case that there are few live locals. Here we + ;; define wrapper emitters that shuffle out-of-range operands into and + ;; out of the reserved range of locals [233,255]. This range is + ;; sufficient because these restricted operands are only present in + ;; the first word of an instruction. Since 8 bits is the smallest + ;; slot-addressing operand size, that means we can fit 3 operands in + ;; the 24 bits of payload of the first word (the lower 8 bits being + ;; taken by the opcode). + ;; + ;; The result are wrapper emitters with the same arity, + ;; e.g. emit-cons* that wraps emit-cons. We expose these wrappers as + ;; the public interface for emitting `cons' instructions. That way we + ;; solve the problem fully and in just one place. The only manual + ;; care that need be taken is in the exports list at the top of the + ;; file -- to be sure that we export the wrapper and not the wrapped + ;; emitter. + + (define (shuffling-assembler name kind word0 word*) + (define (analyze-first-word) + (define-syntax op-case + (syntax-rules () + ((_ type ((%type %kind arg ...) values) clause ...) + (if (and (eq? type '%type) (eq? kind '%kind)) + (with-syntax (((arg ...) (generate-temporaries #'(arg ...)))) + #'((arg ...) values)) + (op-case type clause ...))) + ((_ type) + #f))) + (op-case + word0 + ((U8_U8_I16 ! a imm) + (values (if (< a (ash 1 8)) a (begin (emit-mov* asm 253 a) 253)) + imm)) + ((U8_U8_I16 <- a imm) + (values (if (< a (ash 1 8)) a 253) + imm)) + ((U8_U12_U12 ! a b) + (values (if (< a (ash 1 12)) a (begin (emit-mov* asm 253 a) 253)) + (if (< b (ash 1 12)) b (begin (emit-mov* asm 254 b) 254)))) + ((U8_U12_U12 <- a b) + (values (if (< a (ash 1 12)) a 253) + (if (< b (ash 1 12)) b (begin (emit-mov* asm 254 b) 254)))) + ((U8_U8_U8_U8 ! a b c) + (values (if (< a (ash 1 8)) a (begin (emit-mov* asm 253 a) 253)) + (if (< b (ash 1 8)) b (begin (emit-mov* asm 254 b) 254)) + (if (< c (ash 1 8)) c (begin (emit-mov* asm 255 c) 255)))) + ((U8_U8_U8_U8 <- a b c) + (values (if (< a (ash 1 8)) a 253) + (if (< b (ash 1 8)) b (begin (emit-mov* asm 254 b) 254)) + (if (< c (ash 1 8)) c (begin (emit-mov* asm 255 c) 255)))))) + + (define (tail-formals type) + (define-syntax op-case + (syntax-rules () + ((op-case type (%type arg ...) clause ...) + (if (eq? type '%type) + (generate-temporaries #'(arg ...)) + (op-case type clause ...))) + ((op-case type) + (error "unmatched type" type)))) + (op-case type + (U8_U24 a b) + (U8_L24 a label) + (U32 a) + (I32 imm) + (A32 imm) + (B32) + (N32 label) + (S32 label) + (L32 label) + (LO32 label offset) + (X8_U24 a) + (X8_L24 label) + (B1_X7_L24 a label) + (B1_U7_L24 a b label) + (B1_X31 a) + (B1_X7_U24 a b))) + + (define (shuffle-up dst) + (define-syntax op-case + (syntax-rules () + ((_ type ((%type ...) exp) clause ...) + (if (memq type '(%type ...)) + #'exp + (op-case type clause ...))) + ((_ type) + (error "unexpected type" type)))) + (with-syntax ((dst dst)) + (op-case + word0 + ((U8_U8_I16 U8_U8_U8_U8) + (unless (< dst (ash 1 8)) + (emit-mov* asm dst 253))) + ((U8_U12_U12) + (unless (< dst (ash 1 12)) + (emit-mov* asm dst 253)))))) + + (and=> + (analyze-first-word) + (lambda (formals+shuffle) + (with-syntax ((emit-name (id-append name #'emit- name)) + (((formal0 ...) shuffle) formals+shuffle) + (((formal* ...) ...) (map tail-formals word*))) + (with-syntax (((shuffle-up-dst ...) + (if (eq? kind '<-) + (syntax-case #'(formal0 ...) () + ((dst . _) + (list (shuffle-up #'dst)))) + '()))) + #'(lambda (asm formal0 ... formal* ... ...) + (call-with-values (lambda () shuffle) + (lambda (formal0 ...) + (emit-name asm formal0 ... formal* ... ...))) + shuffle-up-dst ...)))))) + + (define-syntax define-shuffling-assembler + (lambda (stx) + (syntax-case stx () + ((_ #:except (except ...) name opcode kind word0 word* ...) + (cond + ((or-map (lambda (op) (eq? (syntax->datum #'name) op)) + (map syntax->datum #'(except ...))) + #'(begin)) + ((shuffling-assembler #'name (syntax->datum #'kind) + (syntax->datum #'word0) + (map syntax->datum #'(word* ...))) + => (lambda (proc) + (with-syntax ((emit (id-append #'name + (id-append #'name #'emit- #'name) + #'*)) + (proc proc)) + #'(define emit + (let ((emit proc)) + (hashq-set! assemblers 'name emit) + emit))))) + (else #'(begin)))))))) + +(visit-opcodes define-shuffling-assembler #:except (receive mov)) + +;; Mov and receive are two special cases that can work without wrappers. +;; Indeed it is important that they do so. + +(define (emit-mov* asm dst src) + (if (and (< dst (ash 1 12)) (< src (ash 1 12))) + (emit-mov asm dst src) + (emit-long-mov asm dst src))) + +(define (emit-receive* asm dst proc nlocals) + (if (and (< dst (ash 1 12)) (< proc (ash 1 12))) + (emit-receive asm dst proc nlocals) + (begin + (emit-receive-values asm proc #t 1) + (emit-mov* asm dst (1+ proc)) + (emit-reset-frame asm nlocals)))) + (define (emit-text asm instructions) "Assemble @var{instructions} using the assembler @var{asm}. @var{instructions} is a sequence of instructions, expressed as a list of @@ -592,7 +876,7 @@ table, its existing label is used directly." ((static-procedure? obj) `((static-patch! ,label 1 ,(static-procedure-code obj)))) ((cache-cell? obj) '()) - ((symbol? obj) + ((and (symbol? obj) (symbol-interned? obj)) `((make-non-immediate 1 ,(recur (symbol->string obj))) (string->symbol 1 1) (static-set! 1 ,label 0))) @@ -622,14 +906,19 @@ table, its existing label is used directly." ,(recur (make-uniform-vector-backing-store (uniform-array->bytevector obj) width)))))) + ((array? obj) + `((static-patch! ,label 1 ,(recur (shared-array-root obj))))) (else - (error "don't know how to intern" obj)))) + (if (asm-to-file? asm) + (error "don't know how to intern" obj) + `((make-short-immediate 1 ,(vlist-length (asm-constants asm))) + (vector-ref 1 0 1) + (static-set! 1 ,label 0)))))) (cond ((immediate? obj) #f) ((vhash-assoc obj (asm-constants asm)) => cdr) (else - ;; Note that calling intern may mutate asm-constants and - ;; asm-constant-inits. + ;; Note that calling intern may mutate asm-constants and asm-inits. (let* ((label (gensym "constant")) (inits (intern obj label))) (set-asm-constants! asm (vhash-cons obj label (asm-constants asm))) @@ -662,17 +951,18 @@ returned instead." ;;; some higher-level operations. ;;; -(define-syntax define-macro-assembler - (lambda (x) - (syntax-case x () - ((_ (name arg ...) body body* ...) - (with-syntax ((emit (id-append #'name #'emit- #'name))) - #'(begin - (define emit - (let ((emit (lambda (arg ...) body body* ...))) - (hashq-set! assemblers 'name emit) - emit)) - (export emit))))))) +(eval-when (expand) + (define-syntax define-macro-assembler + (lambda (x) + (syntax-case x () + ((_ (name arg ...) body body* ...) + (with-syntax ((emit (id-append #'name #'emit- #'name))) + #'(begin + (define emit + (let ((emit (lambda (arg ...) body body* ...))) + (hashq-set! assemblers 'name emit) + emit)) + (export emit)))))))) (define-macro-assembler (load-constant asm dst obj) (cond @@ -719,6 +1009,7 @@ returned instead." ;(define-tc7-macro-assembler br-if-fluid 37) ;(define-tc7-macro-assembler br-if-dynamic-state 45) ;(define-tc7-macro-assembler br-if-frame 47) +(define-tc7-macro-assembler br-if-keyword 53) ;(define-tc7-macro-assembler br-if-vm 55) ;(define-tc7-macro-assembler br-if-vm-cont 71) ;(define-tc7-macro-assembler br-if-rtl-program 69) @@ -779,6 +1070,19 @@ returned instead." (set-arity-definitions! arity (reverse (arity-definitions arity))) (set-arity-high-pc! arity (asm-start asm)))) +;; As noted above, we reserve locals 253 through 255 for shuffling large +;; operands. However the calling convention has all arguments passed in +;; a contiguous block. This helper, called after the clause has been +;; chosen and the keyword/optional/rest arguments have been processed, +;; shuffles up arguments from slot 253 and higher into their final +;; allocations. +;; +(define (shuffle-up-args asm nargs) + (when (> nargs 253) + (let ((slot (1- nargs))) + (emit-mov asm (+ slot 3) slot) + (shuffle-up-args asm (1- nargs))))) + (define-macro-assembler (standard-prelude asm nreq nlocals alternate) (cond (alternate @@ -788,7 +1092,8 @@ returned instead." (emit-assert-nargs-ee/locals asm nreq (- nlocals nreq))) (else (emit-assert-nargs-ee asm nreq) - (emit-alloc-frame asm nlocals)))) + (emit-alloc-frame asm nlocals))) + (shuffle-up-args asm nreq)) (define-macro-assembler (opt-prelude asm nreq nopt rest? nlocals alternate) (if alternate @@ -801,7 +1106,8 @@ returned instead." (emit-br-if-nargs-gt asm (+ nreq nopt) alternate)) (else (emit-assert-nargs-le asm (+ nreq nopt)))) - (emit-alloc-frame asm nlocals)) + (emit-alloc-frame asm nlocals) + (shuffle-up-args asm (+ nreq nopt (if rest? 1 0)))) (define-macro-assembler (kw-prelude asm nreq nopt rest? kw-indices allow-other-keys? nlocals alternate) @@ -822,7 +1128,8 @@ returned instead." (+ nreq nopt) ntotal (intern-constant asm kw-indices)) - (emit-alloc-frame asm nlocals))) + (emit-alloc-frame asm nlocals) + (shuffle-up-args asm ntotal))) (define-macro-assembler (label asm sym) (hashq-set! (asm-labels asm) sym (asm-start asm))) @@ -908,7 +1215,10 @@ a procedure to do that and return its label. Otherwise return (let ((label (gensym "init-constants"))) (emit-text asm `((begin-program ,label ()) - (assert-nargs-ee/locals 1 1) + ,@(if (asm-to-file? asm) + '((assert-nargs-ee/locals 1 1)) + '((assert-nargs-ee/locals 2 0) + (mov 0 1))) ,@(reverse inits) (load-constant 1 ,*unspecified*) (return 1) @@ -935,6 +1245,7 @@ should be .data or .rodata), and return the resulting linker object. (define tc7-program 69) (define tc7-bytevector 77) (define tc7-bitvector 95) + (define tc7-array 93) (let ((word-size (asm-word-size asm)) (endianness (asm-endianness asm))) @@ -959,6 +1270,8 @@ should be .data or .rodata), and return the resulting linker object. (* 4 word-size)) ((uniform-vector-backing-store? x) (bytevector-length (uniform-vector-backing-store-bytes x))) + ((array? x) + (* word-size (+ 3 (* 3 (array-rank x))))) (else word-size))) @@ -1015,7 +1328,7 @@ should be .data or .rodata), and return the resulting linker object. (write-immediate asm buf pos #f)) ((string? obj) - (let ((tag (logior tc7-ro-string (ash (string-length obj) 8)))) + (let ((tag (logior tc7-ro-string (ash (string-length obj) 8)))) ; FIXME: unused? (case word-size ((4) (bytevector-u32-set! buf pos tc7-ro-string endianness) @@ -1047,7 +1360,7 @@ should be .data or .rodata), and return the resulting linker object. (write-constant-reference buf pos elt) (lp (1+ i))))))) - ((symbol? obj) + ((and (symbol? obj) (symbol-interned? obj)) (write-immediate asm buf pos #f)) ((keyword? obj) @@ -1090,8 +1403,31 @@ should be .data or .rodata), and return the resulting linker object. ;; Need to swap units of element-size bytes (error "FIXME: Implement byte order swap")))) + ((array? obj) + (let-values + ;; array tag + rank + contp flag: see libguile/arrays.h . + (((tag) (logior tc7-array (ash (array-rank obj) 17) (ash 1 16))) + ((bv-set! bvs-set!) + (case word-size + ((4) (values bytevector-u32-set! bytevector-s32-set!)) + ((8) (values bytevector-u64-set! bytevector-s64-set!)) + (else (error "bad word size"))))) + (bv-set! buf pos tag endianness) + (write-immediate asm buf (+ pos word-size) #f) ; root vector (fixed later) + (bv-set! buf (+ pos (* word-size 2)) 0 endianness) ; base + (let lp ((pos (+ pos (* word-size 3))) + (bounds (array-shape obj)) + (incs (shared-array-increments obj))) + (when (pair? bounds) + (bvs-set! buf pos (first (first bounds)) endianness) + (bvs-set! buf (+ pos word-size) (second (first bounds)) endianness) + (bvs-set! buf (+ pos (* word-size 2)) (first incs) endianness) + (lp (+ pos (* 3 word-size)) (cdr bounds) (cdr incs)))))) + (else - (error "unrecognized object" obj)))) + (if (asm-to-file? asm) + (error "unrecognized object" obj) + (write-constant-reference buf pos obj))))) (cond ((vlist-null? data) #f) @@ -1293,7 +1629,7 @@ needed." ;; FIXME: Define these somewhere central, shared with C. (define *bytecode-major-version* #x0202) -(define *bytecode-minor-version* 5) +(define *bytecode-minor-version* 6) (define (link-dynamic-section asm text rw rw-init frame-maps) "Link the dynamic section for an ELF image with bytecode @var{text}, @@ -1473,9 +1809,9 @@ procedure with label @var{rw-init}. @var{rw-init} may be false. If (define (port-position port) (seek port 0 SEEK_CUR)) -(define-syntax-rule (pack-arity-flags has-rest? allow-other-keys? - has-keyword-args? is-case-lambda? - is-in-case-lambda?) +(define-inline (pack-arity-flags has-rest? allow-other-keys? + has-keyword-args? is-case-lambda? + is-in-case-lambda?) (logior (if has-rest? (ash 1 0) 0) (if allow-other-keys? (ash 1 1) 0) (if has-keyword-args? (ash 1 2) 0) @@ -1820,11 +2156,13 @@ procedure with label @var{rw-init}. @var{rw-init} may be false. If (lp sources ;; Guile line and column numbers are 0-indexed, but ;; they are 1-indexed for DWARF. - (cons (list pc - (if file (intern-file file) 0) - (if line (1+ line)) - (if col (1+ col))) - out)))) + (if (and line col) + (cons (list pc + (if (string? file) (intern-file file) 0) + (1+ line) + (1+ col)) + out) + out)))) (() ;; Compilation unit header for .debug_line. We write in ;; DWARF 2 format because more tools understand it than DWARF @@ -2086,4 +2424,7 @@ procedure with label @var{rw-init}. @var{rw-init} may be false. If The result is a bytevector, by default linked so that read-only and writable data are on separate pages. Pass @code{#:page-aligned? #f} to disable this behavior." - (link-elf (link-objects asm) #:page-aligned? page-aligned?)) + (define (asm-constant-vector asm) + (list->vector (reverse (map car (vlist->list (asm-constants asm)))))) + (let ((bv (link-elf (link-objects asm) #:page-aligned? page-aligned?))) + (cons bv (if (asm-to-file? asm) #f (asm-constant-vector asm)))))