123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416 |
- ;;; Lightweight compiler directly from Tree-IL to bytecode
- ;; Copyright (C) 2020-2021,2023 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 License as published by
- ;;; the Free Software Foundation; either version 3 of the License, or (at
- ;;; your option) any later version.
- ;;;
- ;;; This library 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 Lesser
- ;;; General Public License for more details.
- ;;;
- ;;; You should have received a copy of the GNU Lesser General Public License
- ;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
- ;;; Commentary:
- ;;;
- ;;; This pass converts Tree-IL directly to bytecode. Whereas first
- ;;; compiling to CPS will yield better-quality bytecode if the optimizer
- ;;; is on, this approach is much faster and less memory-hungry. It's
- ;;; useful if it's more important to reduce time spent in the compiler
- ;;; than to have a fast program.
- ;;;
- ;;; Code:
- (define-module (language tree-il compile-bytecode)
- #:use-module (ice-9 match)
- #:use-module (language bytecode)
- #:use-module (language tree-il)
- #:use-module ((srfi srfi-1) #:select (filter-map
- fold
- lset-adjoin lset-union lset-difference))
- #:use-module (srfi srfi-9)
- #:use-module (system base types internal)
- #:use-module (system vm assembler)
- #:export (compile-bytecode))
- (define (u6? x) (and (exact-integer? x) (<= 0 x #x3f)))
- (define (u8? x) (and (exact-integer? x) (<= 0 x #xff)))
- (define (u12? x) (and (exact-integer? x) (<= 0 x #xfff)))
- (define (emit-box asm dst src)
- (cond
- ((= src dst)
- (emit-mov asm 1 src)
- (emit-box asm dst 1))
- (else
- (let ((tmp 0))
- (emit-allocate-words/immediate asm dst 2)
- (emit-load-u64 asm tmp %tc7-variable)
- (emit-word-set!/immediate asm dst 0 tmp)
- (emit-word-set!/immediate asm dst 1 src)))))
- (define (emit-box-set! asm loc val)
- (emit-scm-set!/immediate asm loc 1 val))
- (define (emit-box-ref asm dst loc)
- (emit-scm-ref/immediate asm dst loc 1))
- (define (emit-cons asm dst car cdr)
- (cond
- ((= car dst)
- (emit-mov asm 1 car)
- (emit-cons asm dst 1 (if (= cdr dst) 1 cdr)))
- ((= cdr dst)
- (emit-mov asm 1 cdr)
- (emit-cons asm dst car 1))
- (else
- (emit-allocate-words/immediate asm dst 2)
- (emit-scm-set!/immediate asm dst 0 car)
- (emit-scm-set!/immediate asm dst 1 cdr))))
- (define (emit-cached-module-box asm dst mod name public? bound? tmp)
- (define key (cons mod name))
- (define cached (gensym "cached"))
- (emit-cache-ref asm dst key)
- (emit-heap-object? asm dst)
- (emit-je asm cached)
- (cond
- (bound?
- (let ((name (symbol->string name)))
- (if public?
- (emit-lookup-bound-public asm dst mod name)
- (emit-lookup-bound-private asm dst mod name))))
- (else
- (emit-load-constant asm dst mod)
- (emit-resolve-module asm dst dst public?)
- (emit-load-constant asm tmp name)
- (emit-lookup asm dst dst tmp)))
- (emit-cache-set! asm key dst)
- (emit-label asm cached))
- (define (emit-cached-toplevel-box asm dst scope name bound? tmp)
- (define key (cons scope name))
- (define cached (gensym "cached"))
- (emit-cache-ref asm dst key)
- (emit-heap-object? asm dst)
- (emit-je asm cached)
- (emit-cache-ref asm dst scope)
- (emit-load-constant asm tmp name)
- (if bound?
- (emit-lookup-bound asm dst dst tmp)
- (emit-lookup asm dst dst tmp))
- (emit-cache-set! asm key dst)
- (emit-label asm cached))
- (define (emit-toplevel-box asm dst name bound? tmp)
- (emit-current-module asm dst)
- (emit-load-constant asm tmp name)
- (if bound?
- (emit-lookup-bound asm dst dst tmp)
- (emit-lookup asm dst dst tmp)))
- (define closure-header-words 2)
- (define (emit-allocate-closure asm dst nfree label tmp)
- (let ((nwords (+ nfree closure-header-words)))
- (cond
- ((u12? nwords)
- (emit-allocate-words/immediate asm dst nwords))
- (else
- (emit-load-u64 asm tmp nwords)
- (emit-allocate-words asm dst tmp)))
- (emit-load-u64 asm tmp (+ %tc7-program (ash nfree 16)))
- (emit-word-set!/immediate asm dst 0 tmp)
- (emit-load-label asm tmp label)
- (emit-word-set!/immediate asm dst 1 tmp)))
- (define (emit-maybe-allocate-closure asm dst nfree label tmp)
- (if (zero? nfree)
- (emit-load-static-procedure asm dst label)
- (emit-allocate-closure asm dst nfree label tmp)))
- (define (emit-load-free-variable asm dst src idx tmp)
- (let ((idx (+ idx closure-header-words)))
- (cond
- ((u8? idx)
- (emit-scm-ref/immediate asm dst src idx))
- (else
- (emit-load-u64 asm tmp idx)
- (emit-scm-ref asm dst src tmp)))))
- (define (emit-init-free-variable asm closure idx val tmp)
- (let ((idx (+ idx closure-header-words)))
- (cond
- ((u8? idx)
- (emit-scm-set!/immediate asm closure idx val))
- (else
- (emit-load-u64 asm tmp idx)
- (emit-scm-set! asm closure tmp val)))))
- (define vector-header-words 1)
- (define (emit-allocate-vector asm dst len tmp)
- (let ((nwords (+ len vector-header-words)))
- (cond
- ((u12? nwords)
- (emit-allocate-words/immediate asm dst nwords))
- (else
- (emit-load-u64 asm tmp nwords)
- (emit-allocate-words asm dst tmp)))
- (emit-load-u64 asm tmp (+ %tc7-vector (ash len 8)))
- (emit-word-set!/immediate asm dst 0 tmp)))
- (define (emit-vector-init! asm v idx val tmp)
- (let ((idx (+ idx vector-header-words)))
- (cond
- ((u8? idx)
- (emit-scm-set!/immediate asm v idx val))
- (else
- (emit-load-u64 asm tmp idx)
- (emit-scm-set! asm v tmp val)))))
- (define struct-header-words 1)
- (define (emit-struct-init! asm s idx val tmp)
- (let ((idx (+ idx struct-header-words)))
- (cond
- ((u8? idx)
- (emit-scm-set!/immediate asm s idx val))
- (else
- (emit-load-u64 asm tmp idx)
- (emit-scm-set! asm s tmp val)))))
- (define-syntax-rule (define-record-type/keywords rtd
- make-rtd pred (field getter init) ...)
- (begin
- (define-record-type rtd (%make-rtd field ...) pred (field getter) ...)
- (define* (make-rtd #:key (field init) ...)
- (%make-rtd field ...))))
- (define-record-type/keywords <primitive>
- make-primitive
- primitive?
- (name primitive-name (error "name required"))
- (nargs primitive-nargs (error "nargs required"))
- (has-result? primitive-has-result? #f)
- (predicate? primitive-predicate? #f)
- (emit primitive-emitter (error "emitter required"))
- (immediate-in-range? primitive-immediate-in-range-predicate #f)
- (emit/immediate primitive-emitter/immediate #f))
- (define *primitives* (make-hash-table))
- (define (lookup-primitive name) (hashq-ref *primitives* name))
- (define-syntax-rule (define-primitive primitive kw ...)
- (hashq-set! *primitives* 'primitive
- (make-primitive #:name 'primitive kw ...)))
- (define-syntax-rule (define-primitives (primitive kw ...) ...)
- (begin (define-primitive primitive kw ...) ...))
- (define-primitives
- (+ #:nargs 2 #:has-result? #t #:emit emit-add
- #:immediate-in-range? u8?
- #:emit/immediate emit-add/immediate)
- (- #:nargs 2 #:has-result? #t #:emit emit-sub
- #:immediate-in-range? u8?
- #:emit/immediate emit-sub/immediate)
- (* #:nargs 2 #:has-result? #t #:emit emit-mul)
- (/ #:nargs 2 #:has-result? #t #:emit emit-div)
- (quotient #:nargs 2 #:has-result? #t #:emit emit-quo)
- (remainder #:nargs 2 #:has-result? #t #:emit emit-rem)
- (modulo #:nargs 2 #:has-result? #t #:emit emit-mod)
- (exact->inexact #:nargs 1 #:has-result? #t #:emit emit-inexact)
- (sqrt #:nargs 1 #:has-result? #t #:emit emit-sqrt)
- (abs #:nargs 1 #:has-result? #t #:emit emit-abs)
- (floor #:nargs 1 #:has-result? #t #:emit emit-floor)
- (ceiling #:nargs 1 #:has-result? #t #:emit emit-ceiling)
- (sin #:nargs 1 #:has-result? #t #:emit emit-sin)
- (cos #:nargs 1 #:has-result? #t #:emit emit-cos)
- (tan #:nargs 1 #:has-result? #t #:emit emit-tan)
- (asin #:nargs 1 #:has-result? #t #:emit emit-asin)
- (acos #:nargs 1 #:has-result? #t #:emit emit-acos)
- (atan #:nargs 1 #:has-result? #t #:emit emit-atan)
- (atan2 #:nargs 2 #:has-result? #t #:emit emit-atan2)
- (logand #:nargs 2 #:has-result? #t #:emit emit-logand)
- (logior #:nargs 2 #:has-result? #t #:emit emit-logior)
- (logxor #:nargs 2 #:has-result? #t #:emit emit-logxor)
- (logsub #:nargs 2 #:has-result? #t #:emit emit-logsub)
- (lsh #:nargs 2 #:has-result? #t #:emit emit-lsh
- #:immediate-in-range? u6?
- #:emit/immediate emit-lsh/immediate)
- (rsh #:nargs 2 #:has-result? #t #:emit emit-rsh
- #:immediate-in-range? u6?
- #:emit/immediate emit-rsh/immediate)
- (throw #:nargs 2 #:emit emit-throw)
- (throw/value #:nargs 2 #:emit #f
- #:immediate-in-range? (lambda (_) #t)
- #:emit/immediate emit-throw/value)
- (throw/value+data #:nargs 2 #:emit #f
- #:immediate-in-range? (lambda (_) #t)
- #:emit/immediate emit-throw/value+data)
- (current-thread #:nargs 2 #:has-result? #t #:emit emit-current-thread)
- (current-module #:nargs 0 #:has-result? #t #:emit emit-current-module)
- (module-ensure-local-variable! #:nargs 2 #:has-result? #t #:emit emit-define!)
- (builtin-ref #:nargs 1 #:has-result? #t #:emit #f
- #:immediate-in-range? (lambda (_) #t)
- #:emit/immediate emit-builtin-ref)
- (wind #:nargs 2 #:emit emit-wind)
- (unwind #:nargs 0 #:emit emit-unwind)
- (push-dynamic-state #:nargs 1 #:emit emit-push-dynamic-state)
- (pop-dynamic-state #:nargs 0 #:emit emit-pop-dynamic-state)
- (push-fluid #:nargs 2 #:emit emit-push-fluid)
- (pop-fluid #:nargs 0 #:emit emit-pop-fluid)
- (pop-fluid-state #:nargs 0 #:emit emit-pop-dynamic-state)
- (fluid-ref #:nargs 1 #:has-result? #t #:emit emit-fluid-ref)
- (fluid-set! #:nargs 2 #:emit emit-fluid-set!)
- (string->number #:nargs 1 #:has-result? #t #:emit emit-string->number)
- (string->symbol #:nargs 1 #:has-result? #t #:emit emit-string->symbol)
- (symbol->keyword #:nargs 1 #:has-result? #t #:emit emit-symbol->keyword)
- (symbol->string #:nargs 1 #:has-result? #t #:emit emit-symbol->string)
- (class-of #:nargs 1 #:has-result? #t #:emit emit-class-of)
- (cons #:nargs 2 #:has-result? #t #:emit emit-cons)
- (car #:nargs 1 #:has-result? #t #:emit emit-$car)
- (cdr #:nargs 1 #:has-result? #t #:emit emit-$cdr)
- (set-car! #:nargs 2 #:emit emit-$set-car!)
- (set-cdr! #:nargs 2 #:emit emit-$set-cdr!)
-
- (box #:nargs 1 #:has-result? #t #:emit emit-box)
- (variable-ref #:nargs 1 #:has-result? #t #:emit emit-$variable-ref)
- (variable-set! #:nargs 2 #:emit emit-$variable-set!)
- (%variable-ref #:nargs 1 #:has-result? #t #:emit emit-$variable-ref)
- (%variable-set! #:nargs 2 #:emit emit-box-set!)
- (vector-length #:nargs 1 #:has-result? #t #:emit emit-$vector-length)
- (vector-ref #:nargs 2 #:has-result? #t #:emit emit-$vector-ref
- #:immediate-in-range? u8?
- #:emit/immediate emit-$vector-ref/immediate)
- (vector-set! #:nargs 3 #:emit emit-$vector-set!
- #:immediate-in-range? u8?
- #:emit/immediate emit-$vector-set!/immediate)
-
- (struct-vtable #:nargs 1 #:has-result? #t #:emit emit-$struct-vtable)
- (struct-ref #:nargs 2 #:has-result? #t #:emit emit-$struct-ref
- #:immediate-in-range? u8?
- #:emit/immediate emit-$struct-ref/immediate)
- (struct-set! #:nargs 3 #:emit emit-$struct-set!
- #:immediate-in-range? u8?
- #:emit/immediate emit-$struct-set!/immediate)
- (eq? #:nargs 2 #:predicate? #t #:emit (lambda (asm a b kf)
- (emit-eq? asm a b)
- (emit-jne asm kf))
- #:immediate-in-range? (lambda (x)
- (and=>
- (scm->immediate-bits x)
- (lambda (bits)
- (truncate-bits bits 16 #t))))
- #:emit/immediate (lambda (asm a b kf)
- (emit-eq-immediate? asm a b)
- (emit-jne asm kf)))
- (< #:nargs 2 #:predicate? #t #:emit (lambda (asm a b kf)
- (emit-<? asm a b)
- (emit-jnl asm kf)))
- (<= #:nargs 2 #:predicate? #t #:emit (lambda (asm a b kf)
- (emit-<? asm b a)
- (emit-jnge asm kf)))
- (= #:nargs 2 #:predicate? #t #:emit (lambda (asm a b kf)
- (emit-=? asm a b)
- (emit-jne asm kf))))
- (define (variadic-constructor? name)
- (memq name '(vector list make-struct/simple)))
- (define-syntax predicate-emitter
- (lambda (stx)
- (define (id-prepend pre id)
- (datum->syntax id (symbol-append pre (syntax->datum id))))
- (syntax-case stx ()
- ((_ pred)
- #`(lambda (asm a kf)
- (#,(id-prepend 'emit- #'pred) asm a)
- (emit-jne asm kf))))))
- (define-syntax define-immediate-type-predicate
- (syntax-rules ()
- ((_ name #f mask tag) #f)
- ((_ name pred mask tag)
- (define-primitive pred #:nargs 1 #:predicate? #t
- #:emit (predicate-emitter pred)))))
- (define-syntax-rule (define-heap-type-predicate name pred mask tag)
- (define-primitive pred #:nargs 1 #:predicate? #t
- #:emit (lambda (asm a kf)
- (emit-heap-object? asm a)
- (emit-jne asm kf)
- ((predicate-emitter pred) asm a kf))))
- (visit-immediate-tags define-immediate-type-predicate)
- (visit-heap-tags define-heap-type-predicate)
- (define (primitive-module name)
- (case name
- ((bytevector?
- bytevector-length
- bytevector-u8-ref bytevector-u8-set!
- bytevector-s8-ref bytevector-s8-set!
- bytevector-u16-ref bytevector-u16-set!
- bytevector-u16-native-ref bytevector-u16-native-set!
- bytevector-s16-ref bytevector-s16-set!
- bytevector-s16-native-ref bytevector-s16-native-set!
- bytevector-u32-ref bytevector-u32-set!
- bytevector-u32-native-ref bytevector-u32-native-set!
- bytevector-s32-ref bytevector-s32-set!
- bytevector-s32-native-ref bytevector-s32-native-set!
- bytevector-u64-ref bytevector-u64-set!
- bytevector-u64-native-ref bytevector-u64-native-set!
- bytevector-s64-ref bytevector-s64-set!
- bytevector-s64-native-ref bytevector-s64-native-set!
- bytevector-ieee-single-ref bytevector-ieee-single-set!
- bytevector-ieee-single-native-ref bytevector-ieee-single-native-set!
- bytevector-ieee-double-ref bytevector-ieee-double-set!
- bytevector-ieee-double-native-ref bytevector-ieee-double-native-set!)
- '(rnrs bytevectors))
- ((atomic-box?
- make-atomic-box atomic-box-ref atomic-box-set!
- atomic-box-swap! atomic-box-compare-and-swap!)
- '(ice-9 atomic))
- ((current-thread) '(ice-9 threads))
- ((class-of) '(oop goops))
- ((u8vector-ref
- u8vector-set! s8vector-ref s8vector-set!
- u16vector-ref u16vector-set! s16vector-ref s16vector-set!
- u32vector-ref u32vector-set! s32vector-ref s32vector-set!
- u64vector-ref u64vector-set! s64vector-ref s64vector-set!
- f32vector-ref f32vector-set! f64vector-ref f64vector-set!)
- '(srfi srfi-4))
- (else '(guile))))
- (define (canonicalize exp)
- (define (reify-primref src name)
- ;; some are builtin-ref
- (cond
- ((builtin-name->index name)
- => (lambda (idx)
- (make-primcall src 'builtin-ref (list (make-const #f idx)))))
- (else
- (make-module-ref src (primitive-module name) name #t))))
- (define (reify-primcall src name args)
- (make-call src (reify-primref src name) args))
- (define (reify-branch src name args)
- (make-conditional src
- (make-primcall src name args)
- (make-const src #t)
- (make-const src #f)))
- (define (finish-conditional exp)
- (define (true? x) (match x (($ <const> _ val) val) (_ #f)))
- (define (false? x) (match x (($ <const> _ val) (not val)) (_ #f)))
- (define (predicate? name)
- (and=> (lookup-primitive name) primitive-predicate?))
- (match exp
- (($ <conditional> src ($ <conditional> _ test (? true?) (? false?))
- consequent alternate)
- (finish-conditional (make-conditional src test consequent alternate)))
- (($ <conditional> src ($ <conditional> _ test (? false?) (? true?))
- consequent alternate)
- (finish-conditional (make-conditional src test alternate consequent)))
- (($ <conditional> src ($ <primcall> _ (? predicate?)))
- exp)
- (($ <conditional> src test consequent alternate)
- (make-conditional src (make-primcall src 'false? (list test))
- alternate consequent))))
- (post-order
- (lambda (exp)
- (match exp
- ;; Turn <void> into *unspecified*.
- (($ <void> src) (make-const src *unspecified*))
- ;; Ensure the test of a conditional is a branching primcall.
- (($ <conditional>) (finish-conditional exp))
- ;; Reify primitives.
- (($ <primitive-ref> src name) (reify-primref src name))
- ;; Invert >= and >.
- (($ <primcall> src '>= (a b)) (reify-branch src '<= (list b a)))
- (($ <primcall> src '> (a b)) (reify-branch src '< (list b a)))
- ;; For eq? on constants, make the second arg the constant.
- (($ <primcall> src 'eq? ((and a ($ <const>))
- (and b (not ($ <const>)))))
- (reify-branch src 'eq? (list b a)))
- ;; Simplify "not".
- (($ <primcall> src 'not (x))
- (finish-conditional
- (make-conditional src x (make-const src #f) (make-const src #t))))
- ;; Special cases for variadic list, vector, make-struct/simple.
- (($ <primcall> src (? variadic-constructor?)) exp)
- ;; struct-set! needs to return its value.
- (($ <primcall> src 'struct-set! (x idx v))
- (with-lexicals src (v)
- (make-seq src
- (make-primcall src 'struct-set! (list x idx v))
- v)))
- ;; Transform "ash" to lsh / rsh.
- (($ <primcall> src 'ash (x ($ <const> src* (? exact-integer? y))))
- (if (negative? y)
- (make-primcall src 'rsh (list x (make-const src* (- y))))
- (make-primcall src 'lsh (list x (make-const src* y)))))
- ;; (throw key subr msg (list x) (list x))
- (($ <primcall> src 'throw
- (($ <const> _ key) ($ <const> _ subr) ($ <const> _ msg)
- ($ <primcall> _ 'list (x))
- ($ <primcall> _ 'list (x))))
- (make-primcall src 'throw/value+data
- (list x (make-const #f `#(,key ,subr ,msg)))))
- ;; (throw key subr msg (list x) #f)
- (($ <primcall> src 'throw
- (($ <const> _ key) ($ <const> _ subr) ($ <const> _ msg)
- ($ <primcall> _ 'list (x))
- ($ <const> _ #f)))
- (make-primcall src 'throw/value
- (list x (make-const #f `#(,key ,subr ,msg)))))
- ;; (throw key arg ...)
- (($ <primcall> src 'throw (key . args))
- (make-primcall src 'throw
- (list key (make-primcall #f 'list args))))
- ;; Now that we handled special cases, ensure remaining primcalls
- ;; are understood by the code generator, and if not, reify them
- ;; as calls.
- (($ <primcall> src name args)
- (or (and=> (lookup-primitive name)
- (lambda (prim)
- (and (= (primitive-nargs prim) (length args))
- (if (primitive-predicate? prim)
- (reify-branch src name args)
- exp))))
- (reify-primcall src name args)))
- ;; Add a clause to clauseless lambdas.
- (($ <lambda> src meta #f)
- (make-lambda src meta
- (make-lambda-case
- src '() #f #f #f '() '()
- (make-primcall
- src 'throw
- (list (make-const src 'wrong-number-of-args)
- (make-const src #f)
- (make-const src "Wrong number of arguments")
- (make-const src '())
- (make-const src #f)))
- #f)))
- ;; Turn <abort> into abort-to-prompt.
- (($ <abort> src tag args ($ <const> _ ()))
- (reify-primcall src 'abort-to-prompt (cons tag args)))
- (($ <abort> src tag args tail)
- (reify-primcall src 'apply
- (cons* (reify-primref src 'abort-to-prompt)
- tag
- (append args (list tail)))))
- ;; Change non-escape-only prompt bodies from being thunks to
- ;; expressions. (Escape-only prompt bodies are already
- ;; expressions.)
- (($ <prompt> src #f tag body handler)
- (make-prompt src #f tag (make-call src body '()) handler))
- (_ exp)))
- exp))
- (define-record-type <closure>
- (make-closure label code module-scope free-vars)
- closure?
- (label closure-label)
- (code closure-code)
- (module-scope closure-module-scope)
- (free-vars closure-free-vars))
- ;; Identify closures and assigned variables within X.
- (define (split-closures exp)
- (define closures '())
- (define assigned (make-hash-table))
- ;; Guile's current semantics are that a toplevel lambda captures a
- ;; reference on the current module, and that all contained lambdas use
- ;; that module to resolve toplevel variables. The `module-scope'
- ;; parameter of `visit-closure' tracks whether or not we are in a
- ;; toplevel lambda. Visiting a top-level lambda allocates a new
- ;; module-scope by incrementing this counter. Visiting a nested
- ;; lambda re-uses the same module-scope. The code generator will
- ;; associate these ID's with the module that was current at the point
- ;; the top-level lambda is created.
- (define scope-counter 0)
- ;; Compute free variables in X, adding entries to `free-vars' as
- ;; lambdas are seen, and adding set! vars to `assigned'.
- (define (visit-closure exp module-scope)
- (define (visit exp)
- (define (adjoin sym f) (lset-adjoin eq? f sym))
- (define (union f1 f2) (lset-union eq? f1 f2))
- (define (union3 f1 f2 f3) (union f1 (union f2 f3)))
- (define (difference f1 f2) (lset-difference eq? f1 f2))
- (define (visit* xs) (fold (lambda (x free) (union (visit x) free))
- '() xs))
- (match exp
- (($ <lexical-ref> src name sym)
- (list sym))
- ((or ($ <const>) ($ <module-ref>) ($ <toplevel-ref>))
- '())
- (($ <lambda> src meta body)
- (let* ((module-scope (or module-scope
- (let ((scope scope-counter))
- (set! scope-counter (1+ scope-counter))
- scope)))
- (free (visit-closure body module-scope))
- (label (gensym "closure")))
- (set! closures
- (cons (make-closure label exp module-scope free)
- closures))
- free))
- (($ <lambda-case> src req opt rest kw inits gensyms body alternate)
- (union (difference (union (visit* inits) (visit body))
- gensyms)
- (if alternate
- (visit alternate)
- '())))
- (($ <module-set> src mod name public? exp)
- (visit exp))
- (($ <toplevel-set> src mod name exp)
- (visit exp))
- (($ <toplevel-define> src modname name exp)
- (visit exp))
- (($ <call> src proc args)
- (union (visit proc) (visit* args)))
- (($ <primcall> src name args)
- (visit* args))
- (($ <prompt> src escape-only? tag body
- ($ <lambda> hsrc hmeta hclause))
- (union3 (visit tag) (visit body) (visit hclause)))
- (($ <conditional> src test consequent alternate)
- (union3 (visit test) (visit consequent) (visit alternate)))
- (($ <lexical-set> src name gensym exp)
- (hashq-set! assigned gensym #t)
- (adjoin gensym (visit exp)))
- (($ <seq> src head tail)
- (union (visit head) (visit tail)))
- (($ <let> src names syms vals body)
- (union (visit* vals)
- (difference (visit body) syms)))
- (($ <fix> src names gensyms funs body)
- (difference (union (visit* funs) (visit body))
- gensyms))
- (($ <let-values> src exp body)
- (union (visit exp) (visit body)))))
- (visit exp))
- (match (visit-closure exp #f)
- (()
- (let ()
- (define x-thunk
- (let ((src (tree-il-srcv exp)))
- (make-lambda src '()
- (make-lambda-case src '() #f #f #f '() '() exp #f))))
- (values (cons (make-closure 'init x-thunk #f '())
- (reverse closures))
- assigned)))
- (vars
- (error "unexpected free vars" vars))))
- (define call-frame-size 3)
- (define (compute-frame-size clause)
- "Compute a conservative count of how many stack slots will be needed
- in the frame with for the lambda-case clause @var{clause}."
- (define (visit* xs)
- (fold (lambda (x size) (max (visit x) size)) 0 xs))
- (define (visit-args xs)
- (let lp ((i 0) (xs xs))
- (match xs
- (() i)
- ((x . xs)
- (max (+ i (visit x))
- (lp (+ i 1) xs))))))
- ;; Computing a value may require temporaries. For example, for
- ;; module-ref, we may need a temporary for the module and a temporary
- ;; for the symbol. Instead of trying to be extraordinarily precise
- ;; about temporary usage in all the different cases, let's just
- ;; reserve 3 temporaries.
- (define temporary-count 3)
- (define (visit exp)
- (match exp
- ((or ($ <const>) ($ <lexical-ref>) ($ <module-ref>) ($ <toplevel-ref>)
- ($ <lambda>))
- 1)
- (($ <module-set> src mod name public? exp)
- (+ 1 (visit exp)))
- (($ <toplevel-set> src mod name exp)
- (+ 1 (visit exp)))
- (($ <toplevel-define> src modname name exp)
- (+ 1 (visit exp)))
- (($ <call> src proc args)
- (+ call-frame-size (visit-args (cons proc args))))
- (($ <primcall> src name args)
- (visit-args args))
- (($ <prompt> src escape-only? tag body
- ($ <lambda> hsrc hmeta
- ($ <lambda-case> _ hreq #f hrest #f () hsyms hbody #f)))
- (max (visit tag)
- (visit body)
- (+ (length hsyms) (visit hbody))))
- (($ <conditional> src test consequent alternate)
- (max (visit test) (visit consequent) (visit alternate)))
- (($ <lexical-set> src name gensym exp)
- (+ 1 (visit exp)))
- (($ <seq> src head tail)
- (max (visit head) (visit tail)))
- (($ <let> src names syms vals body)
- (max (visit-args vals)
- (+ (length vals) (visit body))))
- (($ <fix> src names gensyms funs body)
- (+ (length funs) (visit body)))
- (($ <let-values> src exp
- ($ <lambda-case> lsrc req #f rest #f () syms body #f))
- (max (visit exp)
- (+ (length syms) (visit body))))))
- (match clause
- (($ <lambda-case> src req opt rest kw inits syms body alt)
- (+ 1 ; One slot for the closure.
- (length syms) ; One slot for each arg.
- (max (visit* inits) ; Prologue.
- (visit body)) ; Body.
- temporary-count)))) ; Temporaries.
- (define (sanitize-meta meta)
- (match meta
- (() '())
- (((k . v) . meta)
- (let ((meta (sanitize-meta meta)))
- (case k
- ((maybe-unused) meta)
- (else (acons k v meta)))))))
- (define (compile-closure asm closure assigned? lookup-closure)
- (define-record-type <env>
- (make-env prev name id idx closure? boxed? next-local)
- env?
- ;; Outer <env>, or #f.
- (prev env-prev)
- ;; Pretty name of the binding, or #f.
- (name env-name)
- ;; For a lexical (local or closure), its sym. For temporaries, #f.
- (id env-id)
- ;; For temporary or local, index from SP at which this value can be
- ;; loaded. Otherwise index from closure.
- (idx env-idx)
- ;; True for closure vars, false otherwise.
- (closure? env-closure?)
- ;; True for boxed vars, false otherwise. Only lexicals can be boxed.
- (boxed? env-boxed?)
- ;; If another local is pushed on inside this lexical environment,
- ;; where it should be written. Usually the same as (1- idx) except
- ;; in the case of lexical aliases. Invariant: no binding in the
- ;; <env> chain has an idx of next-local or lower. For closure
- ;; bindings, #f.
- (next-local env-next-local))
- (define (lookup-lexical sym env)
- (match env
- (($ <env> prev _ id)
- (if (eq? id sym)
- env
- (lookup-lexical sym prev)))
- (_ (error "sym not found!" sym))))
- (define (compile-body clause module-scope free-vars frame-size)
- (define (push-free-var sym idx env)
- (make-env env sym sym idx #t (assigned? sym) (env-next-local env)))
- (define (push-local name sym env)
- (let ((idx (env-next-local env)))
- (emit-definition asm name (- frame-size idx 1) 'scm)
- (make-env env name sym idx #f (assigned? sym) (1- idx))))
- (define (push-closure env)
- (push-local 'closure #f env))
- (define (push-local-alias name sym idx env)
- (make-env env name sym idx #f #f (env-next-local env)))
- (define (push-temp env)
- (let ((idx (env-next-local env)))
- (make-env env #f #f idx #f #f (1- idx))))
- (define (push-frame env)
- (let lp ((i 0) (env env))
- (if (< i call-frame-size)
- (lp (1+ i) (push-temp env))
- env)))
- (define (create-initial-env names syms free-syms)
- (define (push-free-vars env)
- (let lp ((idx 0) (free free-syms) (env env))
- (match free
- (() env)
- ((sym . free)
- (lp (1+ idx) free
- (push-free-var sym idx env))))))
- (define frame-base
- (make-env #f 'frame-base #f #f #f #f (- frame-size 1)))
- (fold push-local (push-closure (push-free-vars frame-base)) names syms))
- (define (stack-height-under-local idx)
- (- frame-size idx 1))
- (define (stack-height env)
- (stack-height-under-local (env-next-local env)))
- (define (maybe-cache-module! scope tmp)
- (unless module-scope
- (emit-current-module asm 0)
- (emit-cache-set! asm scope 0)))
-
- (define (maybe-emit-source source)
- (when source (emit-source asm source)))
- (define (init-free-vars dst free-vars env tmp0 tmp1)
- (let lp ((free-idx 0) (free-vars free-vars))
- (unless (null? free-vars)
- (let* ((loc (lookup-lexical (car free-vars) env))
- (idx (env-idx loc)))
- (cond
- ((env-closure? loc)
- (emit-load-free-variable asm tmp0 (1- frame-size) idx tmp1)
- (emit-init-free-variable asm dst free-idx tmp0 tmp1))
- (else
- (emit-init-free-variable asm dst free-idx idx tmp0))))
- (lp (1+ free-idx) (cdr free-vars)))))
-
- ;; Visit let-values or prompt handler.
- (define (visit-values-handler src req rest syms body env ctx)
- (define (push-bindings names syms env)
- (fold (lambda (name sym env)
- (let ((env (push-local name sym env)))
- (when (env-boxed? env)
- (emit-box asm (env-idx env) (env-idx env)))
- env))
- env names syms))
- (let ((proc-slot (stack-height env))
- (nreq (length req)))
- (maybe-emit-source src)
- (unless (and rest (zero? nreq))
- (emit-receive-values asm proc-slot (->bool rest) nreq))
- (when rest
- (emit-bind-rest asm (+ proc-slot nreq)))
- (emit-reset-frame asm frame-size)
- (let ((names (append req (if rest (list rest) '()))))
- (for-context body (push-bindings names syms env) ctx))))
- (define (visit-prompt exp env ctx)
- (match exp
- (($ <prompt> src escape-only? tag body
- ($ <lambda> hsrc hmeta
- ($ <lambda-case> _ hreq #f hrest #f () hsyms hbody #f)))
- (maybe-emit-source src)
- (let ((tag (env-idx (for-value tag env)))
- (proc-slot (stack-height env))
- (khandler (gensym "handler"))
- (done (gensym "done")))
- (emit-prompt asm tag escape-only? proc-slot khandler)
- (match ctx
- ('tail
- ;; Would be nice if we could invoke the body in true tail
- ;; context, but that's not how it currently is.
- (for-values-at body env 0)
- (emit-unwind asm)
- (emit-handle-interrupts asm)
- (emit-return-values asm))
- (_
- (for-context body env ctx)
- (emit-unwind asm)
- (emit-j asm done)))
- (emit-label asm khandler)
- (visit-values-handler hsrc hreq hrest hsyms hbody env ctx)
- (emit-label asm done)))))
- (define (visit-conditional exp env ctx)
- (match exp
- (($ <conditional> src ($ <primcall> tsrc name args)
- consequent alternate)
- (maybe-emit-source tsrc)
- (let ((prim (lookup-primitive name))
- (kf (gensym "false"))
- (kdone (gensym "done")))
- (define (emit/immediate? val)
- (and=> (primitive-immediate-in-range-predicate prim)
- (lambda (pred) (pred val))))
- (match args
- ((a ($ <const> _ (? emit/immediate? b)))
- (let ((emit (primitive-emitter/immediate prim)))
- (match (for-args (list a) env)
- ((a)
- (maybe-emit-source src)
- (emit asm a b kf)))))
- (_
- (let ((emit (primitive-emitter prim))
- (args (for-args args env)))
- (maybe-emit-source src)
- (match args
- ((a) (emit asm a kf))
- ((a b) (emit asm a b kf))))))
- (for-context consequent env ctx)
- (unless (eq? ctx 'tail)
- (emit-j asm kdone))
- (emit-label asm kf)
- (for-context alternate env ctx)
- (emit-label asm kdone)))))
- (define (visit-seq exp env ctx)
- (match exp
- (($ <seq> src head tail)
- (maybe-emit-source src)
- (for-effect head env)
- (for-context tail env ctx))))
- (define (visit-let exp env ctx)
- (define (push-bindings names syms vals env)
- (fold (lambda (name sym val env)
- (for-push val env)
- (let ((env (push-local name sym env)))
- (when (env-boxed? env)
- (emit-box asm (env-idx env) (env-idx env)))
- env))
- env names syms vals))
- (match exp
- (($ <let> src names syms vals body)
- (maybe-emit-source src)
- (for-context body (push-bindings names syms vals env) ctx))))
- (define (visit-fix exp env ctx)
- (define (push-bindings names syms vals env)
- (let* ((closures (map lookup-closure vals))
- (env (fold
- (lambda (name sym closure env)
- (let ((env (push-local name sym env)))
- (match closure
- (($ <closure> label code scope free-vars)
- ;; FIXME: Allocate one scope per fix.
- (maybe-cache-module! scope 0)
- (emit-maybe-allocate-closure
- asm (env-idx env) (length free-vars) label 0)
- env))))
- env names syms closures)))
- (for-each
- (lambda (sym closure)
- (let ((idx (env-idx (lookup-lexical sym env))))
- (match closure
- (($ <closure> label code scope free-vars)
- (init-free-vars idx free-vars env 0 1)))))
- syms closures)
- env))
- (match exp
- (($ <fix> src names syms vals body)
- (maybe-emit-source src)
- (for-context body (push-bindings names syms vals env) ctx))))
- (define (visit-let-values exp env ctx)
- (match exp
- (($ <let-values> src exp
- ($ <lambda-case> lsrc req #f rest #f () syms body #f))
- (maybe-emit-source src)
- (for-values exp env)
- (visit-values-handler lsrc req rest syms body env ctx))))
- (define (for-context exp env ctx)
- (match ctx
- ('effect (for-effect exp env))
- ('value (for-value exp env))
- ('tail (for-tail exp env))
- (('value-at . dst) (for-value-at exp env dst))
- (('values-at . height) (for-values-at exp env height))))
- (define (for-args exps env)
- (match exps
- (() '())
- ((exp . exps)
- (let ((env (for-value exp env)))
- (cons (env-idx env) (for-args exps env))))))
- (define (for-effect exp env)
- (match exp
- ((or ($ <lexical-ref>) ($ <const>) ($ <lambda>))
- ;; Nothing to do.
- (values))
- ((or ($ <module-ref>) ($ <toplevel-ref>)
- ($ <primcall> _ (? variadic-constructor?)))
- ;; Cause side effects but ignore value.
- (for-value exp env))
- (($ <lexical-set> src name sym exp)
- (let ((env (for-value exp env)))
- (maybe-emit-source src)
- (match (lookup-lexical sym env)
- (($ <env> _ _ _ idx #t #t) ;; Boxed closure.
- (emit-load-free-variable asm 0 (1- frame-size) idx 0)
- (emit-box-set! asm 0 (env-idx env)))
- (($ <env> _ _ _ idx #f #t) ;; Boxed local.
- (emit-box-set! asm idx (env-idx env))))))
- (($ <module-set> src mod name public? exp)
- (let ((env (for-value exp env)))
- (maybe-emit-source src)
- (emit-cached-module-box asm 0 mod name public? #f 1)
- (emit-box-set! asm 0 (env-idx env))))
- (($ <toplevel-set> src mod name exp)
- (let ((env (for-value exp env)))
- (maybe-emit-source src)
- (if module-scope
- (emit-cached-toplevel-box asm 0 module-scope name #f 1)
- (emit-toplevel-box asm 0 name #f 1))
- (emit-box-set! asm 0 (env-idx env))))
- (($ <toplevel-define> src mod name exp)
- (let ((env (for-value exp env)))
- (maybe-emit-source src)
- (emit-current-module asm 0)
- (emit-load-constant asm 1 name)
- (emit-define! asm 0 0 1)
- (emit-box-set! asm 0 (env-idx env))))
- (($ <call> src proc args)
- (let ((proc-slot (let ((env (push-frame env)))
- (fold for-push (for-push proc env) args)
- (stack-height env))))
- (maybe-emit-source src)
- (emit-handle-interrupts asm)
- (emit-call asm proc-slot (1+ (length args)))
- (emit-reset-frame asm frame-size)))
- (($ <primcall> src name args)
- (let ((prim (lookup-primitive name)))
- (define (emit/immediate? val)
- (and=> (primitive-immediate-in-range-predicate prim)
- (lambda (pred) (pred val))))
- (cond
- ((primitive-has-result? prim)
- (for-value exp env))
- (else
- (match args
- ((a ($ <const> _ (? emit/immediate? b)))
- (let ((emit (primitive-emitter/immediate prim)))
- (match (for-args (list a) env)
- ((a)
- (maybe-emit-source src)
- (emit asm a b)))))
- ((a ($ <const> _ (? emit/immediate? b)) c)
- (let ((emit (primitive-emitter/immediate prim)))
- (match (for-args (list a c) env)
- ((a c)
- (maybe-emit-source src)
- (emit asm a b c)))))
- (_
- (let ((emit (primitive-emitter prim))
- (args (for-args args env)))
- (maybe-emit-source src)
- (apply emit asm args))))))))
- (($ <prompt>) (visit-prompt exp env 'effect))
- (($ <conditional>) (visit-conditional exp env 'effect))
- (($ <seq>) (visit-seq exp env 'effect))
- (($ <let>) (visit-let exp env 'effect))
- (($ <fix>) (visit-fix exp env 'effect))
- (($ <let-values>) (visit-let-values exp env 'effect))))
- (define (for-value-at exp env dst)
- ;; The baseline compiler follows a stack discipline: compiling
- ;; temporaries pushes entries on an abstract compile-time stack
- ;; (the "env"), which are then popped as they are used. Generally
- ;; speaking the "env" is compiled as stack slots: compiling an
- ;; operand pushes on an "env" entry, which increments the current
- ;; stack height, allocating a new slot that is in use by no live
- ;; value. However since we're targetting a register VM though,
- ;; there are some important optimizations we should make.
- ;;
- ;; 1. In the case of (lambda (x) (+ x x)), we don't want to cause
- ;; the references to "x" to allocate new stack slots. We want
- ;; to emit:
- ;;
- ;; (add 0 0 0)
- ;; (return-values)
- ;;
- ;; and not:
- ;;
- ;; (mov 1 0)
- ;; (mov 2 0)
- ;; (add 0 1 2)
- ;; (return-values)
- ;;
- ;; (These examples use FP-relative indexes.)
- ;;
- ;; This optimization is handled by for-value, which can push
- ;; on a special "env" that aliases a lexical binding.
- ;;
- ;; 2. Again for (lambda (x) (+ x x)), we want to write the result
- ;; directly to its destination, which may alias an operand.
- ;; So we want to avoid this:
- ;;
- ;; (add 1 0 0)
- ;; (mov 0 1)
- ;; (return-values)
- ;;
- ;; That optimization is implemented by for-value-at and
- ;; for-values-at. It works as long as long as the destination
- ;; is clobbered only after operands are used, so each part of
- ;; this function has to be careful not to do some kind of
- ;; multi-part computation that first clobbers "dst" and then
- ;; reads the operands.
- (match exp
- (($ <lexical-ref> src name sym)
- (maybe-emit-source src)
- (match (lookup-lexical sym env)
- (($ <env> _ _ _ idx #t #t)
- (emit-load-free-variable asm dst (1- frame-size) idx 0)
- (emit-box-ref asm dst dst))
- (($ <env> _ _ _ idx #t #f)
- (emit-load-free-variable asm dst (1- frame-size) idx 0))
- (($ <env> _ _ _ idx #f #t)
- (emit-box-ref asm dst idx))
- (($ <env> _ _ _ idx #f #f)
- (emit-mov asm dst idx))))
- (($ <const> src val)
- (maybe-emit-source src)
- (emit-load-constant asm dst val))
- (($ <module-ref> src mod name public?)
- (maybe-emit-source src)
- (emit-cached-module-box asm 0 mod name public? #t 1)
- (emit-box-ref asm dst 0))
- (($ <toplevel-ref> src mod name)
- (maybe-emit-source src)
- (if module-scope
- (emit-cached-toplevel-box asm 0 module-scope name #t 1)
- (emit-toplevel-box asm 0 name #t 1))
- (emit-box-ref asm dst 0))
- (($ <lambda> src)
- (maybe-emit-source src)
- (match (lookup-closure exp)
- (($ <closure> label code scope free-vars)
- (maybe-cache-module! scope 0)
- (match (length free-vars)
- (0
- (emit-load-static-procedure asm dst label))
- (nfree
- ;; Stage closure in 0 to avoid stompling captured free
- ;; vars.
- (emit-allocate-closure asm 0 nfree label 1)
- (init-free-vars 0 free-vars env 1 2)
- (emit-mov asm dst 0))))))
- ((or ($ <lexical-set>)
- ($ <module-set>)
- ($ <toplevel-set>)
- ($ <toplevel-define>))
- (for-effect exp env)
- (emit-load-constant asm dst *unspecified*))
- (($ <call> src proc args)
- (let ((proc-slot (let ((env (push-frame env)))
- (fold for-push (for-push proc env) args)
- (stack-height env))))
- (maybe-emit-source src)
- (emit-handle-interrupts asm)
- (emit-call asm proc-slot (1+ (length args)))
- (emit-receive asm (stack-height-under-local dst) proc-slot
- frame-size)))
- (($ <primcall> src (? variadic-constructor? name) args)
- ;; Stage result in 0 to avoid stompling args.
- (let ((args (for-args args env)))
- (maybe-emit-source src)
- (match name
- ('list
- (emit-load-constant asm 0 '())
- (for-each (lambda (arg)
- (emit-cons asm 0 arg 0))
- (reverse args)))
- ('vector
- (let ((len (length args)))
- (emit-allocate-vector asm 0 len 1)
- (let lp ((i 0) (args args))
- (when (< i len)
- (emit-vector-init! asm 0 i (car args) 1)
- (lp (1+ i) (cdr args))))))
- ('make-struct/simple
- (match args
- ((vtable . args)
- (emit-load-constant asm 0 (length args))
- (emit-$allocate-struct asm 0 vtable 0)
- (let lp ((i 0) (args args))
- (match args
- (() #t)
- ((arg . args)
- (emit-struct-init! asm 0 i arg 1)
- (lp (1+ i) args))))))))
- (emit-mov asm dst 0)))
- (($ <primcall> src name args)
- (let ((prim (lookup-primitive name)))
- (define (emit/immediate? val)
- (and=> (primitive-immediate-in-range-predicate prim)
- (lambda (pred) (pred val))))
- (cond
- ((not (primitive-has-result? prim))
- (for-effect exp env)
- (emit-load-constant asm dst *unspecified*))
- (else
- (match args
- ((($ <const> _ (? emit/immediate? a)))
- (let* ((emit (primitive-emitter/immediate prim)))
- (maybe-emit-source src)
- (emit asm dst a)))
- ((a ($ <const> _ (? emit/immediate? b)))
- (let* ((emit (primitive-emitter/immediate prim))
- (a (for-value a env)))
- (maybe-emit-source src)
- (emit asm dst (env-idx a) b)))
- (_
- (let ((emit (primitive-emitter prim))
- (args (for-args args env)))
- (maybe-emit-source src)
- (apply emit asm dst args))))))))
- (($ <prompt>) (visit-prompt exp env `(value-at . ,dst)))
- (($ <conditional>) (visit-conditional exp env `(value-at . ,dst)))
- (($ <seq>) (visit-seq exp env `(value-at . ,dst)))
- (($ <let>) (visit-let exp env `(value-at . ,dst)))
- (($ <fix>) (visit-fix exp env `(value-at . ,dst)))
- (($ <let-values>) (visit-let-values exp env `(value-at . ,dst)))))
- (define (for-value exp env)
- (match (and (lexical-ref? exp)
- (lookup-lexical (lexical-ref-gensym exp) env))
- (($ <env> _ name sym idx #f #f)
- (push-local-alias name sym idx env))
- (_
- (for-push exp env))))
- (define (for-push exp env)
- (for-value-at exp env (env-next-local env))
- (push-temp env))
- (define (for-init sym init env)
- (match (lookup-lexical sym env)
- (($ <env> prev name sym idx #f boxed? next-local)
- (when init
- (let ((done (gensym "post-init")))
- (emit-undefined? asm idx)
- (emit-jne asm done)
- (for-value-at init env idx)
- (emit-label asm done)))
- (when boxed?
- (emit-box asm idx idx)))))
- (define (for-values-at exp env height)
- (match exp
- ((or ($ <const>)
- ($ <lexical-ref>)
- ($ <lexical-set>)
- ($ <toplevel-ref>)
- ($ <toplevel-set>)
- ($ <toplevel-define>)
- ($ <module-ref>)
- ($ <module-set>)
- ($ <lambda>)
- ($ <primcall>))
- (for-value-at exp env (- frame-size height 1))
- (emit-reset-frame asm (1+ height)))
- (($ <call> src proc args)
- (let* ((env (push-frame env))
- (from (stack-height env)))
- (fold for-push (for-push proc env) args)
- (maybe-emit-source src)
- (emit-handle-interrupts asm)
- (emit-call asm from (1+ (length args)))
- (unless (= from height)
- (emit-shuffle-down asm from height))))
- (($ <prompt>) (visit-prompt exp env `(values-at . ,height)))
- (($ <conditional>) (visit-conditional exp env `(values-at . ,height)))
- (($ <seq>) (visit-seq exp env `(values-at . ,height)))
- (($ <let>) (visit-let exp env `(values-at . ,height)))
- (($ <fix>) (visit-fix exp env `(values-at . ,height)))
- (($ <let-values>) (visit-let-values exp env `(values-at . ,height)))))
- (define (for-values exp env)
- (for-values-at exp env (stack-height env)))
- (define (for-tail exp env)
- (match exp
- ((or ($ <const>)
- ($ <lexical-ref>)
- ($ <lexical-set>)
- ($ <toplevel-ref>)
- ($ <toplevel-set>)
- ($ <toplevel-define>)
- ($ <module-ref>)
- ($ <module-set>)
- ($ <lambda>)
- ($ <primcall>))
- (for-values-at exp env 0)
- (emit-handle-interrupts asm)
- (emit-return-values asm))
- (($ <call> src proc args)
- (let* ((base (stack-height env))
- (env (fold for-push (for-push proc env) args)))
- (maybe-emit-source src)
- (let lp ((i (length args)) (env env))
- (when (<= 0 i)
- (lp (1- i) (env-prev env))
- (emit-mov asm (+ (env-idx env) base) (env-idx env))))
- (emit-reset-frame asm (+ 1 (length args)))
- (emit-handle-interrupts asm)
- (emit-tail-call asm)))
- (($ <prompt>) (visit-prompt exp env 'tail))
- (($ <conditional>) (visit-conditional exp env 'tail))
- (($ <seq>) (visit-seq exp env 'tail))
- (($ <let>) (visit-let exp env 'tail))
- (($ <fix>) (visit-fix exp env 'tail))
- (($ <let-values>) (visit-let-values exp env 'tail))))
- (match clause
- (($ <lambda-case> src req opt rest kw inits syms body alt)
- (let ((names (append req
- (or opt '())
- (if rest (list rest) '())
- (match kw
- ((aok? (key name sym) ...) name)
- (#f '()))))
- (inits (append (make-list (length req) #f)
- (list-head inits (if opt (length opt) 0))
- (if rest '(#f) '())
- (list-tail inits (if opt (length opt) 0)))))
- (unless (= (length names) (length syms) (length inits))
- (error "unexpected args" names syms inits))
- (maybe-emit-source src)
- (let ((env (create-initial-env names syms free-vars)))
- (for-each (lambda (sym init) (for-init sym init env)) syms inits)
- (for-tail body env))))))
- (define (emit-clause label clause module-scope free)
- (let ((frame-size (compute-frame-size clause)))
- (match clause
- (($ <lambda-case> src req opt rest kw inits syms body alt)
- (let ((alt-label (and alt (gensym "clause"))))
- (call-with-values
- (lambda ()
- (match kw
- (#f (values #f '()))
- ((aok? . kw)
- (values aok?
- (map (match-lambda
- ((key name sym)
- (cons key (1+ (list-index syms sym)))))
- kw)))))
- (lambda (allow-other-keys? kw-indices)
- (when label (emit-label asm label))
- (let ((has-closure? #t) (opt (or opt '())))
- (emit-begin-kw-arity asm has-closure? req opt rest kw-indices
- allow-other-keys? frame-size alt-label))
- (compile-body clause module-scope free frame-size)
- (emit-end-arity asm)
- (when alt
- (emit-clause alt-label alt module-scope free)))))))))
- (match closure
- (($ <closure> label ($ <lambda> src meta body) module-scope free)
- (when src (emit-source asm src))
- (emit-begin-program asm label (sanitize-meta meta))
- (emit-clause #f body module-scope free)
- (emit-end-program asm))))
- (define (kw-arg-ref args kw default)
- (match (memq kw args)
- ((_ val . _) val)
- (_ default)))
- (define (compile-bytecode exp env opts)
- (let* ((exp (canonicalize exp))
- (asm (make-assembler)))
- (call-with-values (lambda () (split-closures exp))
- (lambda (closures assigned)
- (let ((by-code (make-hash-table)))
- (for-each (lambda (closure)
- (hashq-set! by-code (closure-code closure) closure))
- closures)
- (define (assigned? sym)
- (hashq-ref assigned sym))
- (define (lookup-closure x)
- (or (hashq-ref by-code x) (error "missing <closure>" x)))
- (for-each (lambda (closure)
- (compile-closure asm closure assigned? lookup-closure))
- closures))))
- (values (link-assembly asm #:page-aligned? (kw-arg-ref opts #:to-file? #f))
- env
- env)))
|