summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisplib.c23
-rw-r--r--share/txr/stdlib/compiler.tl402
2 files changed, 425 insertions, 0 deletions
diff --git a/lisplib.c b/lisplib.c
index a8e68c4a..b4d20e59 100644
--- a/lisplib.c
+++ b/lisplib.c
@@ -644,6 +644,28 @@ static val asm_set_entries(val dlt, val fun)
return nil;
}
+static val compiler_instantiate(val set_fun)
+{
+ funcall1(set_fun, nil);
+ load(format(nil, lit("~acompiler.tl"), stdlib_path, nao));
+ return nil;
+}
+
+static val compiler_set_entries(val dlt, val fun)
+{
+ val sys_name[] = {
+ lit("compiler"),
+ nil
+ };
+ val name[] = {
+ lit("compile-toplevel"),
+ nil
+ };
+
+ set_dlt_entries_sys(dlt, sys_name, fun);
+ set_dlt_entries(dlt, name, fun);
+ return nil;
+}
static val op_set_entries(val dlt, val fun)
{
@@ -705,6 +727,7 @@ void lisplib_init(void)
dlt_register(dl_table, doloop_instantiate, doloop_set_entries);
dlt_register(dl_table, stream_wrap_instantiate, stream_wrap_set_entries);
dlt_register(dl_table, asm_instantiate, asm_set_entries);
+ dlt_register(dl_table, compiler_instantiate, compiler_set_entries);
if (!opt_compat || opt_compat >= 185)
dlt_register(dl_table, op_instantiate, op_set_entries);
diff --git a/share/txr/stdlib/compiler.tl b/share/txr/stdlib/compiler.tl
new file mode 100644
index 00000000..684224a0
--- /dev/null
+++ b/share/txr/stdlib/compiler.tl
@@ -0,0 +1,402 @@
+;; Copyright 2018
+;; Kaz Kylheku <kaz@kylheku.com>
+;; Vancouver, Canada
+;; All rights reserved.
+;;
+;; Redistribution and use in source and binary forms, with or without
+;; modification, are permitted provided that the following conditions are met:
+;;
+;; 1. Redistributions of source code must retain the above copyright notice, this
+;; list of conditions and the following disclaimer.
+;;
+;; 2. Redistributions in binary form must reproduce the above copyright notice,
+;; this list of conditions and the following disclaimer in the documentation
+;; and/or other materials provided with the distribution.
+;;
+;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+;; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :sys)
+
+(defstruct (frag oreg code : fvars ffuns) nil
+ oreg
+ code
+ fvars
+ ffuns)
+
+(defstruct binding nil
+ sym
+ loc
+ sys:env)
+
+(defstruct sys:env nil
+ vb
+ fb
+ up
+ co
+ lev
+ (v-cntr 0)
+
+ (:postinit (me)
+ (unless me.lev
+ (set me.lev (if me.up (succ me.up.lev) 1)))
+ (unless (or me.co (null me.up))
+ (set me.co me.up.co)
+ me.co.(new-env me)))
+
+ (:method lookup-var (me sym)
+ (condlet
+ (((cell (assoc sym me.vb))) (cdr cell))
+ (((up me.up)) up.(lookup-var sym))
+ (t nil)))
+
+ (:method lookup-fun (me sym)
+ (condlet
+ (((cell (assoc me.fb sym))) (cdr cell))
+ (((up me.up)) up.(lookup-fun sym))
+ (t nil)))
+
+ (:method extend-var (me sym)
+ (let* ((loc ^(v ,(ppred me.lev) ,(pinc me.v-cntr)))
+ (bn (new binding sym sym loc loc env me)))
+ (set me.vb (acons-new sym bn me.vb))))
+
+ (:method extend-fun (me sym)
+ (let* ((loc ^(v ,me.lev ,(pinc me.v-cntr)))
+ (bn (new binding sym sym loc loc env me)))
+ (set me.fb (acons-new sym bn me.fb)))))
+
+(defstruct compiler nil
+ (dreg-cntr 0)
+ (nlev 2)
+ (nreg 1)
+ (tregs (mapcar (op list t) (range 1 255)))
+ (dreg (hash :eql-based))
+ (data (hash :eql-based))
+ last-form)
+
+(defmeth compiler get-dreg (me atom)
+ (iflet ((dreg [me.dreg atom]))
+ dreg
+ (let* ((dreg ^(d ,(pinc me.dreg-cntr))))
+ (set [me.data (cadr dreg)] atom)
+ (set [me.dreg atom] dreg))))
+
+(defmeth compiler get-datavec (me)
+ (vec-list [mapcar me.data (range* 0 me.dreg-cntr)]))
+
+(defmeth compiler alloc-treg (me)
+ (let ((treg (pop me.tregs)))
+ (unless treg
+ (compile-error me.last-form "code too complex: out of registers"))
+ (set me.nreg (max me.nreg (succ (cadr treg))))
+ treg))
+
+(defmeth compiler free-treg (me treg)
+ (when (and (eq t (car treg)) (neq 0 (cadr treg)))
+ (push treg me.tregs)))
+
+(defmeth compiler free-tregs (me tregs)
+ (mapdo (meth me free-treg) tregs))
+
+(defmeth compiler new-env (me env)
+ (when (>= env.lev me.nlev)
+ (set me.nlev (succ env.lev))))
+
+(defmeth compiler compile (me env form)
+ (set me.last-form form)
+ (cond
+ ((null form) (new (frag '(t 0) nil)))
+ ((and (symbolp form)
+ (not (bindable form))) me.(comp-atom form))
+ ((symbolp form) me.(comp-var env form))
+ ((atom form) me.(comp-atom form))
+ ((consp form)
+ (let ((sym (car form)))
+ (cond
+ ((special-operator-p sym)
+ (caseq sym
+ (quote me.(comp-atom (cadr form)))
+ (sys:setq me.(comp-setq env (cdr form)))
+ (block me.(comp-block env (cdr form)))
+ ((let let*) me.(comp-let env sym (cdr form)))
+ (lambda me.(comp-lambda env (cdr form)))
+ (sys:for-op me.(comp-for env (cdr form)))
+ (progn me.(comp-progn env (cadr form)))
+ (prog1 me.(comp-prog1 env form))
+ (sys:dvbind me.(compile env (caddr form)))
+ (sys:with-dyn-rebinds me.(comp-progn env (cddr form)))
+ ((macrolet symacrolet macro-time)
+ (compile-error form "unexpanded ~s encountered" sym))
+ ((sys:var sys:expr)
+ (compile-error form "meta with no meaning: ~s " form))
+ ((usr:qquote usr:unquote usr:splice
+ sys:qquote sys:unquote sys:splice)
+ (compile-error form "unexpanded quasiquote encountered"))
+ (t
+ (compile-error form "special op ~s not handled yet" sym))))
+ ((bindable sym) me.(comp-call env sym (cdr form)))
+ (t (compile-error form "invalid compound form")))))))
+
+(defmeth compiler comp-atom (me form)
+ (cond
+ ((or (and (integerp form)
+ (< (width form) 32))
+ (chrp form))
+ (let ((oreg me.(alloc-treg)))
+ (new (frag oreg ^((movi ,oreg ,form))))))
+ (t (let ((dreg me.(get-dreg form)))
+ (new (frag dreg nil))))))
+
+(defmeth compiler comp-var (me env sym)
+ (iflet ((vbin env.(lookup-var sym)))
+ (new (frag vbin.loc nil (list sym)))
+ (let ((oreg me.(alloc-treg))
+ (dreg me.(get-dreg sym)))
+ (new (frag oreg ^((getv ,oreg ,dreg)) (list sym))))))
+
+(defmeth compiler comp-setq (me env args)
+ (tree-bind (sym value) args
+ (let* ((bind env.(lookup-var sym))
+ (vloc (if bind
+ bind.loc
+ me.(get-dreg sym)))
+ (vfrag me.(compile env value)))
+ me.(free-treg vfrag.oreg)
+ (new (frag vloc
+ ^(,*vfrag.code
+ ,*(if bind
+ ^((mov ,vloc ,vfrag.oreg))
+ ^((setv ,vloc ,vfrag.oreg))))
+ (uni (list sym) vfrag.fvars)
+ vfrag.ffuns)))))
+
+(defmeth compiler comp-block (me env args)
+ (tree-bind (name . body) args
+ (let* ((dreg me.(get-dreg name))
+ (bfrag me.(comp-progn env body))
+ (lskip (gensym "l"))
+ (oreg (if (equal bfrag.oreg '(t 0))
+ me.(alloc-treg)
+ bfrag.oreg)))
+ me.(free-treg bfrag.oreg)
+ (new (frag oreg
+ ^((block ,oreg ,dreg ,lskip)
+ ,*bfrag.code
+ (end ,bfrag.oreg)
+ ,lskip)
+ bfrag.fvars
+ bfrag.ffuns)))))
+
+(defmeth compiler comp-let (me env sym args)
+ (tree-bind (raw-vis . body) args
+ (let* ((vis (mapcar [iffi atom list] raw-vis))
+ (specials [keep-if special-var-p vis car])
+ (lexsyms [remove-if special-var-p [mapcar car vis]])
+ (specials-occur [find-if special-var-p vis car])
+ (frsize (len lexsyms))
+ (seq (eq sym 'let*))
+ (nenv (new env up env co me))
+ (fenv (if seq nenv env)))
+ (unless seq
+ (each ((lsym lexsyms))
+ nenv.(extend-var lsym)))
+ (let* (ffuns fvars
+ (code (build
+ (add ^(,(if specials-occur 'dframe 'frame)
+ ,nenv.lev ,frsize))
+ (each ((vi vis))
+ (tree-bind (sym : form) vi
+ (cond
+ ((special-var-p sym)
+ (let ((frag me.(compile fenv form))
+ (dreg me.(get-dreg sym)))
+ (pend frag.code)
+ (add ^(bindv ,frag.oreg ,dreg))
+ me.(free-treg frag.oreg)
+ (set ffuns (uni ffuns frag.ffuns)
+ fvars (uni fvars frag.fvars))))
+ (form
+ (let ((frag me.(compile fenv form))
+ (bind (progn
+ (if seq nenv.(extend-var sym))
+ nenv.(lookup-var sym))))
+ (pend frag.code)
+ (add ^(mov ,bind.loc ,frag.oreg))
+ me.(free-treg frag.oreg)
+ (set ffuns (uni ffuns frag.ffuns)
+ fvars (uni fvars frag.fvars)))))))))
+ (bfrag me.(comp-progn nenv body)))
+ (new (frag bfrag.oreg
+ (append code bfrag.code ^((end ,bfrag.oreg)))
+ (uni (diff bfrag.fvars lexsyms) fvars)
+ (uni ffuns bfrag.ffuns)))))))
+
+(defmeth compiler comp-lambda (me env args)
+ (tree-bind (pars . body) args
+ (let* ((rest-par (nthlast 0 pars))
+ (fixed-pars (ldiff pars rest-par))
+ (need-frame (or fixed-pars rest-par))
+ lexsyms specials)
+ (tree-bind (: req-pars raw-opt-pars) (split* fixed-pars
+ (op where (op eq :)))
+ (let* ((opt-pars (mapcar [iffi atom list] raw-opt-pars))
+ (nenv (if need-frame (new env up env co me) env))
+ (nreq (len req-pars))
+ (nfixed (+ nreq (len opt-pars)))
+ (frsize (+ nfixed (if rest-par 1 0))))
+ (flet ((spec-sub (sym)
+ (cond
+ ((special-var-p sym)
+ (let ((sub (gensym)))
+ (push (cons sym sub) specials)
+ nenv.(extend-var sub)
+ sub))
+ (t
+ (push sym lexsyms)
+ nenv.(extend-var sym)
+ sym))))
+ (set req-pars (collect-each ((rp req-pars))
+ (spec-sub rp)))
+ (set opt-pars (collect-each ((op opt-pars))
+ (tree-bind (var-sym : init-form have-sym) op
+ (list (spec-sub var-sym)
+ init-form
+ (if have-sym (spec-sub have-sym))))))
+ (when rest-par
+ (set rest-par (spec-sub rest-par)))
+ (upd specials nreverse)
+ (let* ((col-reg (if opt-pars me.(get-dreg :)))
+ (tee-reg (if opt-pars me.(get-dreg t)))
+ (ifrags (collect-each ((op opt-pars))
+ (let* ((init-form (cadr op))
+ (init-frag me.(compile env init-form)))
+ me.(free-treg init-frag.oreg)
+ init-frag)))
+ (opt-code (append-each ((op opt-pars)
+ (ifrg ifrags))
+ (tree-bind (var-sym : init-form have-sym) op
+ (let ((var-bind nenv.(lookup-var var-sym))
+ (have-bind nenv.(lookup-var have-sym))
+ (lskip (gensym "l")))
+ ^(,*(if have-sym
+ ^((mov ,have-bind.loc ,tee-reg)))
+ (ifq ,var-bind.loc ,col-reg ,lskip)
+ ,*(if have-sym
+ ^((mov ,have-bind.loc nil)))
+ ,*ifrg.code
+ (mov ,var-bind.loc ,ifrg.oreg)
+ ,lskip)))))
+ (benv (if specials (new env up nenv co me) nenv))
+ (bfrag me.(comp-progn benv body))
+ (oreg me.(alloc-treg))
+ (lskip (gensym "l-")))
+ me.(free-treg bfrag.oreg)
+ (new (frag oreg
+ ^((close ,oreg ,frsize ,lskip ,nfixed ,nreq
+ ,(if rest-par t nil)
+ ,*(collect-each ((rp req-pars))
+ nenv.(lookup-var rp).loc)
+ ,*(collect-each ((op opt-pars))
+ nenv.(lookup-var (car op)).loc)
+ ,*(if rest-par
+ (list nenv.(lookup-var rest-par).loc)))
+ ,*opt-code
+ ,*(if specials
+ ^((dframe ,benv.lev 0)))
+ ,*(if specials
+ (collect-each ((vs specials))
+ (tree-bind (special . gensym) vs
+ (let ((sub-bind nenv.(lookup-var gensym))
+ (dreg me.(get-dreg special)))
+ ^(bindv ,sub-bind.loc ,dreg)))))
+ ,*bfrag.code
+ (end ,bfrag.oreg)
+ ,lskip)
+ (uni [reduce-left uni ifrags nil .fvars]
+ (diff bfrag.fvars lexsyms))
+ (uni [reduce-left uni ifrags nil .ffuns]
+ bfrag.ffuns))))))))))
+
+(defmeth compiler comp-progn (me env args)
+ (let* ((oreg me.(alloc-treg))
+ ffuns fvars
+ (code (build
+ (each ((form args))
+ me.(free-treg oreg)
+ (let ((frag me.(compile env form)))
+ (set oreg frag.oreg)
+ (set fvars (uni fvars frag.fvars))
+ (set ffuns (uni ffuns frag.ffuns))
+ (pend frag.code))))))
+ (new (frag oreg code fvars ffuns))))
+
+(defmeth compiler comp-prog1 (me env form)
+ (tree-case form
+ ((prog1 fi . re) (let ((fi-frag me.(compile env fi))
+ (re-frag me.(comp-progn env re)))
+ me.(free-treg re-frag.oreg)
+ (new (frag fi-frag.oreg
+ (append fi-frag.code re-frag.code)
+ (uni fi-frag.fvars re-frag.fvars)
+ (uni fi-frag.ffuns re-frag.ffuns)))))
+ ((prog1 fi) me.(compile env fi))
+ ((prog1) me.(compile env nil))))
+
+(defmeth compiler comp-call (me env sym args)
+ (let ((oreg me.(alloc-treg))
+ (dreg me.(get-dreg sym))
+ (afrags (mapcar (meth me compile env) args)))
+ (let ((aregs (mapcar .oreg afrags)))
+ me.(free-tregs aregs)
+ (new (frag oreg
+ ^(,*(mappend .code afrags) (call ,oreg ,dreg ,*aregs))
+ [reduce-left uni afrags nil .fvars]
+ [reduce-left uni afrags nil .ffuns])))))
+
+(defmeth compiler comp-for (me env args)
+ (tree-bind (inits (: test . rets) incs . body) args
+ (let* ((ifrag me.(comp-progn env inits))
+ (tfrag (progn
+ me.(free-treg ifrag.oreg)
+ me.(compile env test)))
+ (rfrag me.(comp-progn env rets))
+ (nfrag me.(comp-progn env incs))
+ (bfrag (progn
+ me.(free-treg nfrag.oreg)
+ me.(comp-progn env body)))
+ (lback (gensym "l"))
+ (lskip (gensym "l"))
+ (frags (list ifrag tfrag rfrag nfrag bfrag))
+ (infin (equal tfrag.oreg '(t 0)))
+ (oreg (if rets rfrag.oreg '(t 0))))
+ me.(free-tregs (list bfrag.oreg tfrag.oreg rfrag.oreg))
+ (new (frag oreg
+ ^(,*ifrag.code
+ ,lback
+ ,*tfrag.code
+ ,*(if test
+ ^((if ,tfrag.oreg ,lskip)))
+ ,*bfrag.code
+ ,*nfrag.code
+ (jmp ,lback)
+ ,*(if test
+ ^(,lskip))
+ ,*rfrag.code)
+ [reduce-left uni frags nil .fvars]
+ [reduce-left uni frags nil .ffuns])))))
+
+(defun usr:compile-toplevel (exp)
+ (let ((co (new compiler))
+ (as (new assembler)))
+ (let ((frag co.(compile (new env) (expand exp))))
+ as.(asm ^(,*frag.code (end ,frag.oreg)))
+ (vm-make-desc co.nlev co.nreg as.buf co.(get-datavec)))))