summaryrefslogtreecommitdiffstats
path: root/stdlib/optimize.tl
diff options
context:
space:
mode:
Diffstat (limited to 'stdlib/optimize.tl')
-rw-r--r--stdlib/optimize.tl854
1 files changed, 854 insertions, 0 deletions
diff --git a/stdlib/optimize.tl b/stdlib/optimize.tl
new file mode 100644
index 00000000..52e5504c
--- /dev/null
+++ b/stdlib/optimize.tl
@@ -0,0 +1,854 @@
+;; Copyright 2021-2024
+;; 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.
+
+(load-for
+ (usr:var %const-foldable% "constfun")
+ (usr:macro when-opt "comp-opts"))
+
+(compile-only
+ (defstruct live-info nil
+ (defined 0)
+ (used 0)
+ def0 def1)
+
+ (defstruct basic-block (live-info)
+ (live 0)
+ label
+ next
+ prev
+ links
+ rlinks
+ insns
+ closer
+ nojoin
+
+ (:method print (bl stream pretty-p)
+ (ignore pretty-p)
+ (put-string "#S" stream)
+ (print ^(basic-block live ,bl.live
+ label ,bl.label
+ insns ,bl.insns
+ links ,(mapcar .label bl.links)
+ rlinks ,(mapcar .label bl.rlinks)
+ next ,bl.next.?label) stream)))
+
+ (defstruct (basic-blocks compiler insns lt-dregs symvec) nil
+ compiler
+ insns
+ lt-dregs
+ symvec
+ (hash (hash))
+ (li-hash (hash :eq-based))
+ list
+ closures
+ (cl-hash (hash))
+ rescan
+ recalc
+ reelim
+ tryjoin
+ warned-insns
+ (:static start (gensym "start-"))
+ (:static jump-ops '(jmp if ifq ifql close swtch ret abscsr
+ uwprot catch block jend xend))
+
+ (:postinit (bb)
+ (let* ((insns (early-peephole (dedup-labels (cons bb.start bb.insns))))
+ (parts (partition-if (lambda (prev next)
+ (or (symbolp next)
+ (and (consp prev)
+ (memq (car prev) bb.jump-ops))))
+ insns))
+ (lparts (mapcar [iff [chain car symbolp]
+ use
+ (op cons (gensym))]
+ parts)))
+ (set bb.list (mapcar (do new basic-block
+ insns @1 label (car @1))
+ lparts))
+ (mapdo (do set [bb.hash @1.label] @1) bb.list))
+ bb.(link-graph t))
+
+ (:method num-blocks (bb)
+ (len bb.list))
+
+ (:method get-insns (bb)
+ [mappend .insns bb.list])
+
+ (:method cut-block (bb bl at insns)
+ (let* ((nlabel (gensym "nl"))
+ (ltail (cdr (memq bl bb.list)))
+ (nbl (new basic-block
+ label nlabel
+ insns (cons nlabel at))))
+ (set bb.list (append (ldiff bb.list ltail) (list nbl) ltail))
+ (set bl.insns (ldiff insns at))
+ (set [bb.hash nlabel] nbl)
+ (pushnew bl bb.rescan)
+ (pushnew nbl bb.rescan)
+ nbl))
+
+ (:method join-block (bb bl nxbl)
+ (when (eql (car nxbl.insns) nxbl.label)
+ (pop nxbl.insns))
+ (set bl.insns (append bl.insns nxbl.insns)
+ nxbl.insns nil
+ bl.next nxbl.next
+ bl.links nxbl.links
+ bl.nojoin nxbl.nojoin
+ bb.list (remq nxbl bb.list))
+ (if nxbl.next
+ (set nxbl.next.prev bl.prev))
+ (del [bb.hash nxbl.label])
+ (each ((nx bl.links))
+ (upd nx.rlinks (remq nxbl))
+ (pushnew bl nx.rlinks)))))
+
+(defmacro rewrite-case (sym list . cases)
+ ^(rewrite (lambda (,sym)
+ (match-case ,sym
+ ,*cases))
+ ,list))
+
+(defmeth basic-blocks link-graph (bb : first-time)
+ (unless first-time
+ (each ((bl bb.list))
+ (set bl.links nil
+ bl.next nil
+ bl.prev nil
+ bl.rlinks nil)))
+ (each* ((bl bb.list)
+ (nxbl (append (cdr bl) '(nil))))
+ (let* ((code bl.insns)
+ (tail (last code))
+ (linsn (car tail))
+ (link-next t))
+ (match-case linsn
+ ((jmp @jlabel)
+ (set bl.links (list [bb.hash jlabel])
+ link-next nil))
+ ((if @nil @jlabel)
+ (set bl.links (list [bb.hash jlabel])))
+ ((@(or ifq ifql) @nil @nil @jlabel)
+ (set bl.links (list [bb.hash jlabel])))
+ ((close @nil @nil @nil @jlabel . @nil)
+ (set bl.links (list [bb.hash jlabel])
+ bl.next nxbl
+ link-next nil)
+ (if nxbl
+ (set nxbl.prev bl)))
+ ((swtch @nil . @jlabels)
+ (set bl.links [mapcar bb.hash (uniq jlabels)]
+ link-next nil))
+ ((catch @nil @nil @nil @nil @hlabel)
+ (set bl.links (list [bb.hash hlabel])))
+ ((block @nil @nil @slabel)
+ (set bl.links (list [bb.hash slabel])))
+ ((uwprot @clabel)
+ (set bl.links (list [bb.hash clabel])))
+ ((@(or abscsr ret jend) . @nil)
+ (set link-next nil))
+ ((xend . @nil)
+ (set bl.nojoin t)))
+ (when (and nxbl link-next)
+ (set bl.next nxbl)
+ (if nxbl
+ (set nxbl.prev bl))
+ (pushnew nxbl bl.links))
+ (each ((nx bl.links))
+ (pushnew bl nx.rlinks)))))
+
+(defmeth basic-blocks local-liveness (bb bl)
+ (set bl.live 0)
+ (labels ((regnum (reg)
+ (when-match (t @num) reg num))
+ (regnums (regs)
+ (mappend (do when-match
+ (t @num) @1 (list num)) regs))
+ (defs (li insn def0 : def1)
+ (set [bb.li-hash insn] li)
+ (let* ((dn0 (regnum def0))
+ (dn1 (regnum def1))
+ (dmask0 (if dn0 (mask dn0) 0))
+ (dmask1 (if dn1 (mask dn1) 0))
+ (dmask (logior dmask0 dmask1)))
+ (cond
+ ((nzerop dmask)
+ (set li.def0 def0)
+ (set li.def1 def1)
+ (new live-info
+ used (logand li.used (lognot dmask))
+ defined dmask))
+ (t (prog1
+ (copy li)
+ (set li.def0 def0)
+ (set li.def1 def1))))))
+ (refs (li insn . refs)
+ (set [bb.li-hash insn] li)
+ (let* ((rn (regnums refs))
+ (rmask (mask . rn)))
+ (new live-info
+ used (logior li.used rmask))))
+ (def-ref (li insn def . refs)
+ (set li.def0 def
+ [bb.li-hash insn] li)
+ (let* ((rn (regnums refs))
+ (dn (regnum def))
+ (dmask (if dn (mask dn)))
+ (rmask (mask . rn)))
+ (cond
+ (dn (new live-info
+ used (logior (logand li.used (lognot dmask)) rmask)
+ defined dmask))
+ (t (new live-info
+ used (logior li.used rmask))))))
+ (liveness (insns)
+ (if (null insns)
+ (new live-info used 0)
+ (let* ((li (liveness (cdr insns)))
+ (insn (car insns)))
+ (match-case insn
+ ((@(or end jend xend prof) @reg)
+ (refs li insn reg))
+ ((@(or apply call) @def . @refs)
+ (def-ref li insn def . refs))
+ ((@(or gapply gcall) @def @nil . @refs)
+ (def-ref li insn def . refs))
+ ((mov @def @ref)
+ (def-ref li insn def ref))
+ ((if @reg . @nil)
+ (refs li insn reg))
+ ((@(or ifq ifql) @reg @creg . @nil)
+ (refs li insn reg creg))
+ ((swtch @reg . @nil)
+ (refs li insn reg))
+ ((block @reg @nreg . @nil)
+ (refs li insn reg nreg))
+ ((@(or ret abscsr) @nreg @reg)
+ (refs li insn reg nreg))
+ ((catch @esreg @eareg @syreg @descreg . @nil)
+ (let ((ili (defs li insn esreg eareg)))
+ (refs ili insn syreg descreg)))
+ ((handle @funreg @syreg)
+ (refs li insn funreg syreg))
+ ((@(or getv getvb getfb getl1b getlx getf) @def . @nil)
+ (defs li insn def))
+ ((@(or setv setl1 setlx bindv) @reg . @nil)
+ (refs li insn reg))
+ ((close @reg . @nil)
+ (defs li insn reg))
+ ((@(or jmp frame dframe uwprot) . @nil)
+ (set [bb.li-hash insn] li)
+ (copy li))
+ ((@nil . @nil)
+ (error `unhandled/mishandled @insn instruction`))
+ (@else
+ (set [bb.li-hash else] li)
+ (copy li)))))))
+ (let ((li (liveness bl.insns)))
+ (set bl.used li.used
+ bl.defined li.defined))))
+
+(defmeth basic-blocks calc-liveness (bb : (blist bb.list))
+ (each ((bl blist))
+ bb.(local-liveness bl))
+ (let (changed)
+ (while* changed
+ (let ((visited (hash :eq-based)))
+ (labels ((upd-used (bl insns live)
+ (tree-case insns
+ ((fi . re)
+ (let* ((live (upd-used bl re live))
+ (lif [bb.li-hash fi]))
+ (clear-mask live lif.defined)
+ (set-mask lif.used live)
+ live))
+ (t live)))
+ (visit (bl)
+ (unless [visited bl]
+ (set [visited bl] t)
+ (when bl.next
+ (visit bl.next))
+ (let ((used 0)
+ (old-live (or bl.live 0)))
+ (each ((nx bl.links))
+ (visit nx)
+ (set-mask used nx.used))
+ (when (neql (set bl.live (logior used old-live))
+ old-live)
+ (let ((live-in (logand (upd-used bl bl.insns bl.live)
+ (lognot bl.defined))))
+ (set-mask bl.used live-in))
+ (set changed t))))))
+ (set changed nil)
+ (visit (car bb.list)))))))
+
+(defmeth basic-blocks thread-jumps-block (bb code)
+ (let* ((tail (last code))
+ (oinsn (car tail))
+ (insn oinsn)
+ (ninsn oinsn))
+ (while* (nequal ninsn insn)
+ (set insn ninsn
+ ninsn (match-case insn
+ (@(require (if @(as reg (d @nil)) @nil)
+ (not (memqual reg bb.lt-dregs)))
+ nil)
+ ((if (t 0) @jlabel)
+ ^(jmp ,jlabel))
+ ((jmp @jlabel)
+ (let ((jinsns [bb.hash jlabel].insns))
+ (match-case jinsns
+ ((@jlabel
+ (jmp @(and @jjlabel @(not @jlabel))) . @nil)
+ ^(jmp ,jjlabel))
+ (@nil insn))))
+ ((if @reg @jlabel)
+ (let* ((jbl [bb.hash jlabel]))
+ (match-case jbl.insns
+ ((@jlabel
+ (if @reg
+ @(and @jjlabel @(not @jlabel))) . @nil)
+ ^(if ,reg ,jjlabel))
+ ((@jlabel
+ (jmp @(and @jjlabel @(not @jlabel))) . @nil)
+ ^(if ,reg ,jjlabel))
+ ((@nil
+ (ifq @reg (t 0) @nil) . @nil)
+ (let ((xbl jbl.next))
+ (if xbl
+ ^(if ,reg ,xbl.label)
+ insn)))
+ (@nil insn))))
+ ((ifq @reg @creg @jlabel)
+ (let ((jbl [bb.hash jlabel]))
+ (match-case jbl.insns
+ ((@jlabel
+ (ifq @reg @creg
+ @(and @jjlabel @(not @jlabel))) . @nil)
+ ^(ifq ,reg ,creg ,jjlabel))
+ ((@(require @jlabel (equal creg '(t 0)))
+ (if @reg @(not @jlabel)) . @nil)
+ (let ((xbl jbl.next))
+ (if xbl
+ ^(ifq ,reg ,creg ,xbl.label)
+ insn)))
+ ((@nil
+ (jmp @(and @jjlabel @(not @jlabel))) . @nil)
+ ^(ifq ,reg ,creg ,jjlabel))
+ (@nil insn))))
+ ((close @reg @frsize @ntregs @jlabel . @cargs)
+ (let ((jbl [bb.hash jlabel]))
+ (match-case jbl.insns
+ ((@jlabel
+ (jmp @(and @jjlabel @(not @jlabel))) . @nil)
+ ^(close ,reg ,frsize ,ntregs ,jjlabel ,*cargs))
+ (@nil insn))))
+ (@else else))))
+ (cond
+ ((null ninsn) (ldiff code tail))
+ ((nequal ninsn oinsn) (append (ldiff code tail) (list ninsn)))
+ (t code))))
+
+(defun subst-preserve (x y bb li insn)
+ (let ((sub (subst x y insn)))
+ (cond
+ ((equal sub insn) insn)
+ (t (set [bb.li-hash sub] li) sub))))
+
+(defun careful-subst-preserve (x y bb li insn)
+ (let ((sub (match-case insn
+ ((@(or apply call) @def . @refs)
+ ^(,(car insn) ,def ,*(subst x y refs)))
+ ((@(or gapply gcall) @def @fn . @refs)
+ ^(,(car insn) ,def ,fn ,*(subst x y refs)))
+ ((mov @def @x)
+ ^(mov ,def ,y))
+ ((catch @esreg @eareg . @refs)
+ ^(catch ,esreg ,eareg ,*(subst x y refs)))
+ (@else else))))
+ (cond
+ ((equal sub insn) insn)
+ (t (set [bb.li-hash sub] li) sub))))
+
+(defmeth basic-blocks rename (bb insns dst src)
+ (build
+ (let ((vreg (eq (car src) 'v)))
+ (whilet ((insn (pop insns)))
+ (let ((end (if-match (end . @nil) insn t))
+ (close (if-match (close . @nil) insn t))
+ (li [bb.li-hash insn]))
+ (cond
+ (close (add insn))
+ ((and vreg end)
+ (add (subst-preserve dst src bb li insn))
+ (pend insns)
+ (set insns nil))
+ ((or (mequal dst li.def0 li.def1)
+ (mequal src li.def0 li.def1))
+ (add (careful-subst-preserve dst src bb li insn))
+ (pend insns)
+ (set insns nil))
+ (t (add (subst-preserve dst src bb li insn)))))))))
+
+(defmeth basic-blocks peephole-block (bb bl)
+ (let ((code bb.(do-peephole-block bl bl.insns)))
+ (set bl.insns code)))
+
+(defmeth basic-blocks do-peephole-block (bb bl code)
+ (labels ((dead-treg (insn n)
+ (let ((li [bb.li-hash insn]))
+ (and li (not (bit li.used n))))))
+ (rewrite-case insns code
+ ;; dead t-reg
+ (@(require ((@(or mov getlx getv getf getfb) (t @n) . @nil) . @nil)
+ (dead-treg (car insns) n))
+ (pushnew bl bb.rescan)
+ (set bb.recalc t)
+ (cdr insns))
+ (@(require ((close (t @n) @nil @nil @jlabel . @nil) . @nil)
+ (dead-treg (car insns) n))
+ (pushnew bl bb.rescan)
+ (set bb.recalc t
+ bb.reelim t)
+ ^((jmp ,jlabel)))
+ (@(require ((@(or gcall gapply) (t @n) @idx . @nil) . @nil)
+ (dead-treg (car insns) n)
+ [%effect-free% [bb.symvec idx]])
+ (pushnew bl bb.rescan)
+ (set bb.recalc t)
+ (cdr insns))
+ ;; wasteful moves
+ (((mov @reg0 @nil) (mov @reg0 @nil) . @nil)
+ (cdr insns))
+ (((mov @reg0 @reg1) (mov @reg1 @reg0) . @rest)
+ (pushnew bl bb.rescan)
+ (set bb.recalc t)
+ ^(,(car insns) ,*rest))
+ ;; frame reduction
+ (((@(or frame dframe) @lev @nil)
+ (@(or call gcall mov)
+ . @(require @(coll (v @vlev @nil))
+ (none vlev (op eql (ppred lev)))))
+ . @rest)
+ ^(,(cadr insns) ,(car insns) ,*rest))
+ (((@(or frame dframe) . @nil)
+ (if (t @reg) @jlabel) . @nil)
+ (let* ((jbl [bb.hash jlabel])
+ (jinsns jbl.insns))
+ (match-case jinsns
+ ((@nil
+ (end (t @reg)) . @jrest)
+ (let* ((ybl bl.next)
+ (xbl (if ybl
+ (if jrest
+ bb.(cut-block jbl jrest jinsns)
+ jbl.next)))
+ (yinsns ybl.insns))
+ (cond
+ (xbl
+ (set ybl.insns ^(,ybl.label ,(car insns) ,*(cdr yinsns)))
+ (pushnew ybl bb.rescan)
+ (set bb.recalc t)
+ (set bb.links (list ybl xbl))
+ ^((if (t ,reg) ,xbl.label)))
+ (t insns))))
+ (@nil insns))))
+ (@(require ((if @(as reg (d @nil)) @nil) . @nil)
+ (not (memqual reg bb.lt-dregs)))
+ (pushnew bl bb.tryjoin)
+ (pushnew bl bb.rescan)
+ (pushnew bl.next bb.rescan)
+ (set bb.recalc t)
+ nil)
+ (@(require @(or ((@(or ifq ifql) @(as reg (d @nil)) (t 0) @jlabel) . @nil)
+ ((@(or ifq ifql) (t 0) @(as reg (d @nil)) @jlabel) . @nil))
+ (not (memqual reg bb.lt-dregs)))
+ (pushnew bl.next bb.rescan)
+ (if bl.next
+ (set bl.prev nil))
+ (set bb.recalc t
+ bl.next nil
+ bl.links (list [bb.hash jlabel]))
+ ^((jmp ,jlabel)))
+ (@(require ((@(or ifq ifql) @(as reg0 (d @n0)) @(as reg1 (d @n1)) @label)
+ . @nil)
+ (neql n0 n1)
+ (not (and (memqual reg0 bb.lt-dregs)
+ (memqual reg1 bb.lt-dregs))))
+ ^((jmp ,label)))
+ (((@(or ifq ifql) @reg @reg . @nil) . @nil)
+ (rest insns))
+ ;; wasteful move of previously tested value
+ (@(require ((ifq (t @reg) (d @n) @nil) . @nil)
+ (let* ((nxbl bl.next)
+ (nxinsns nxbl.insns))
+ (if (null (cdr nxbl.rlinks))
+ (if-match (@label (mov (t @reg) (d @n)) . @rest) nxinsns
+ (set nxbl.insns ^(,label ,*rest)
+ bb.recalc t)))))
+ insns)
+ (((jmp @jlabel) . @nil)
+ (let* ((jinsns (cdr [bb.hash jlabel].insns))
+ (oinsns (match-case jinsns
+ (((jend @nil) . @nil)
+ ^(,(car jinsns)))
+ ((@nil (jend @nil) . @nil)
+ ^(,(car jinsns) ,(cadr jinsns)))
+ (@nil insns))))
+ (when (neq insns oinsns)
+ (pushnew bl bb.rescan)
+ (if bl.next
+ (set bl.prev nil))
+ (set bb.recalc t
+ bl.next nil
+ bl.links nil))
+ oinsns))
+ ;; unnecessary copying t-reg
+ (@(require ((mov @(as dst (t @nil)) @src) . @rest)
+ (nequal dst src))
+ (let ((ren bb.(rename rest dst src)))
+ (cond
+ ((nequal rest ren)
+ (pushnew bl bb.rescan)
+ (set bb.recalc t)
+ (cons (car insns) ren))
+ (t insns))))
+ ;; constant folding
+ (@(require ((@(as op @(or gapply gcall)) @tgt @idx
+ . @(all @(or (d @dn)
+ @(with (t 0) dn nil))))
+ . @(with @rest
+ val nil))
+ [%const-foldable% [bb.symvec idx]]
+ [none dn (lop member bb.lt-dregs : cadr)]
+ (let ((err '#:err))
+ (set val (let* ((insn (car insns))
+ (co bb.compiler)
+ (dvec co.(get-datavec))
+ (fun [bb.symvec idx])
+ (args (mapcar [iffi true dvec] dn))
+ (val (usr:catch
+ (if (eq op 'gcall)
+ (apply fun args)
+ (apply fun (append [args 0..-1]
+ [args -1])))
+ (error (#:x) err))))
+ (when-opt usr:constant-throws
+ (when (and (eq val err)
+ (not (member insn bb.warned-insns)))
+ (diag co.top-form
+ "function ~s with arguments ~s throws"
+ fun args)
+ (push insn bb.warned-insns)))
+ val))
+ (neq val err)))
+ (let* ((dreg bb.compiler.(get-dreg val)))
+ ^((mov ,tgt ,dreg) ,*rest)))
+ ;; apply to gapply
+ (@(require @(with ((getf @(as treg (t @tn)) @idx) . @rest)
+ @(scan @(or @(with @(as apl ((apply @dest @treg . @args)
+ . @arest))
+ li [bb.li-hash (car apl)])
+ @(require (@insn . @nil)
+ (find treg insn))))
+ rest)
+ apl
+ (or (equal dest treg)
+ (not (bit li.used tn))))
+ (set bb.recalc t)
+ (let* ((gapl ^(gapply ,dest ,idx ,*args)))
+ (set [bb.li-hash gapl] li)
+ ^(,*(ldiff rest apl) ,gapl ,*arest)))
+ (@nil insns))))
+
+(defmeth basic-blocks peephole (bb)
+ (each ((bl bb.list))
+ bb.(peephole-block bl))
+ (whilet ((rescan (zap bb.rescan)))
+ (whilet ((bl (pop bb.tryjoin)))
+ (let ((nxbl bl.next))
+ (unless (or bl.nojoin (cdr nxbl.rlinks))
+ bb.(join-block bl nxbl)
+ (set bb.recalc t)
+ (when (memq nxbl bb.tryjoin)
+ (upd bb.tryjoin (remq nxbl))
+ (push bl bb.tryjoin))
+ (upd bb.rescan (remq nxbl)))))
+ (when (zap bb.recalc)
+ bb.(calc-liveness rescan))
+ (each ((bl rescan))
+ bb.(peephole-block bl)))
+ (when bb.reelim
+ bb.(elim-dead-code)))
+
+(defmeth basic-blocks thread-jumps (bb)
+ (each ((bl bb.list))
+ (set bl.insns bb.(thread-jumps-block bl.insns))))
+
+(defmeth basic-blocks join-blocks (bb)
+ (labels ((joinbl (list)
+ (tree-case list
+ ((bl nxbl . rest)
+ (cond
+ ((and (eq bl.next nxbl)
+ (eq (car bl.links) nxbl)
+ (null bl.nojoin)
+ (null (cdr bl.links))
+ (null (cdr nxbl.rlinks)))
+ bb.(join-block bl nxbl)
+ (joinbl (cons bl rest)))
+ (t (cons bl (joinbl (cdr list))))))
+ (else else))))
+ (set bb.list (joinbl bb.list))))
+
+(defmeth basic-blocks elim-dead-code (bb)
+ bb.(link-graph)
+ (let* ((visited (hash :eq-based)))
+ (labels ((visit (bl)
+ (when (test-set [visited bl])
+ (when bl.next
+ (visit bl.next))
+ [mapcar visit bl.links])))
+ (for ((bl (car bb.list))) (bl) ((set bl bl.next))
+ (visit bl)))
+ (upd bb.list (keep-if visited))
+ (let (flg)
+ (each ((bl bb.list)
+ (nx (cdr bb.list)))
+ (when bl.(check-bypass-empty nx)
+ (set flg t)
+ (del [visited bl])))
+ (if flg
+ (upd bb.list (keep-if visited))))
+ (while
+ (let (rep)
+ (each ((bl bb.list)
+ (nx (cdr bb.list)))
+ bl.(elim-next-jump nx)
+ (when bl.(check-bypass-empty nx)
+ (set rep t)
+ (del [visited bl])))
+ (if rep
+ (upd bb.list (keep-if visited))))))
+ bb.(join-blocks)
+ bb.(link-graph))
+
+(defmeth basic-blocks merge-jump-thunks (bb)
+ (let* ((candidates (mappend [andf [chain .links len (op eql 1)]
+ [chain .insns len (lop < 4)]
+ [chain .insns last car
+ [iff consp
+ [chain car (op eq 'jmp)]]]
+ list]
+ bb.list))
+ (hash (group-by [chain .insns cdr] candidates)))
+ (dohash (insns bls hash)
+ (let ((link (car (car bls).links)))
+ (each ((bb bb.list))
+ (if (and (not (member bb bls))
+ (null (cdr bb.links))
+ (eq bb.next link)
+ (starts-with (cdr bb.insns) insns)
+ (eql (len bb.insns) (len insns)))
+ (push bb bls))))
+ (when (cdr bls)
+ (whenlet ((keep (or (keep-if (op some @1.rlinks (op eq @@1) .next) bls)
+ (list (car bls))))
+ (leader (car keep)))
+ (whenlet ((dupes (diff bls keep)))
+ (each ((bl dupes))
+ (each ((pbl bl.rlinks))
+ (let* ((code pbl.insns)
+ (tail (last code))
+ (lins (car tail))
+ (sins (subst bl.label leader.label lins)))
+ (set pbl.insns (append (ldiff code tail) (list sins))))))
+ (set bb.list (remove-if (lop memq dupes) bb.list))))))))
+
+(defmeth basic-blocks late-peephole (bb code)
+ (rewrite-case insns code
+ (((if @reg @lab1)
+ @lab2
+ (jmp @lab3)
+ @lab1
+ . @rest)
+ (let* ((bl [bb.hash lab2]))
+ (if (cdr bl.rlinks)
+ insns
+ ^((ifq ,reg (t 0) ,lab3)
+ ,lab1
+ ,*rest))))
+ (((mov (t @tn) (d @nil))
+ (jmp @lab3)
+ @nil
+ (mov (t @tn) (t 0))
+ @(symbolp @lab3)
+ (ifq (t @tn) (t 0) @lab4)
+ . @nil)
+ ^(,(car insns)
+ (jmp ,lab4)
+ ,*(cddr insns)))
+ ((@(symbolp @lab1)
+ (mov (t @tn) (t 0))
+ @lab2
+ (ifq (t @tn) (t 0) @lab4)
+ @(symbolp)
+ (gcall (t @tn) . @nil)
+ . @nil)
+ ^(,lab2
+ (ifq (t ,tn) (t 0) ,lab4)
+ ,lab1
+ ,*(cddddr insns)))
+ (((mov (t @tx) (t @ty))
+ (if (t @ty) @lab2)
+ @(symbolp @lab1)
+ (gcall (t @tx) . @args)
+ @(symbolp @lab2)
+ (jend (t @tx))
+ . @rest)
+ ^((if (t ,ty) ,lab2)
+ ,lab1
+ (gcall (t ,ty) ,*args)
+ ,lab2
+ (jend (t ,ty))
+ ,*rest))
+ (@else else)))
+
+(defmeth basic-blocks identify-closures (bb)
+ (zap bb.closures)
+ (each ((bl bb.list))
+ (when-match @(end ((close . @nil))) bl.insns
+ (let ((nx bl.next))
+ (set nx.closer bl)
+ (push nx bb.closures))))
+ (upd bb.closures nreverse)
+ (let ((visited (hash :eq-based)))
+ (labels ((visit (bl clhead)
+ (when (test-set [visited bl])
+ (push bl [bb.cl-hash clhead])
+ [mapcar (lop visit clhead) bl.links])))
+ (each ((cb bb.closures))
+ (visit cb cb))))
+ [hash-update bb.cl-hash nreverse])
+
+(defmeth basic-block fill-treg-compacting-map (bl map)
+ (labels ((add-treg (reg)
+ (unless [map reg]
+ (if-match (t @nil) reg
+ (set [map reg] ^(t ,(len map))))))
+ (add-tregs (args)
+ [mapcar add-treg args]))
+ (iflet ((cl bl.closer))
+ (let ((cloinsn (car (last cl.insns))))
+ (add-tregs (cddr cloinsn))))
+ (each ((insn bl.insns))
+ (match-case insn
+ ((close @reg . @nil)
+ (add-treg reg))
+ ((@nil . @args)
+ (add-tregs args))))))
+
+(defmeth basic-block apply-treg-compacting-map (bl map)
+ (labels ((fix (arg) [map arg arg])
+ (fix-tregs (args) [mapcar fix args]))
+ (iflet ((cl bl.closer))
+ (match ((close @reg @frsize @nil . @rest)) (last cl.insns)
+ (set (last cl.insns)
+ ^((close ,reg ,frsize ,(len map) ,*(fix-tregs rest))))))
+ (set bl.insns (collect-each ((insn bl.insns))
+ (match-case insn
+ ((close @reg . @rest)
+ ^(close ,(fix reg) ,*rest))
+ ((@op . @args)
+ ^(,op ,*(fix-tregs args)))
+ (@else else))))))
+
+(defmeth basic-block check-bypass-empty (bl nx)
+ (unless (cdr bl.insns)
+ (upd nx.rlinks (remq bl))
+ (whenlet ((pb bl.prev))
+ (set pb.next nx)
+ (set nx.prev pb))
+ (each ((pb bl.rlinks))
+ (upd pb.links (subst bl nx))
+ (upd pb.insns (mapcar [iffi consp (op subst bl.label nx.label)]))
+ (push pb nx.rlinks))
+ bl))
+
+(defmeth basic-block elim-next-jump (bl nx)
+ (let* ((tail (last bl.insns))
+ (linsn (car tail)))
+ (match-case linsn
+ (@(or (jmp @jlabel)
+ (if @nil @jlabel)
+ (@(or ifq ifql) @nil @nil @jlabel))
+ (when (eql nx.label jlabel)
+ (set bl.insns (butlast bl.insns)))))))
+
+(defmeth basic-blocks compact-tregs (bb)
+ bb.(identify-closures)
+ (each ((bl bb.closures))
+ (let ((clist [bb.cl-hash bl]))
+ (let ((map (hash-from-pairs '(((t 0) (t 0)) ((t 1) (t 1))))))
+ (each ((cl clist))
+ cl.(fill-treg-compacting-map map))
+ (each ((cl clist))
+ cl.(apply-treg-compacting-map map))))))
+
+(defun rewrite (fun list)
+ (build
+ (while* list
+ (let ((nlist [fun list]))
+ (if (eq list nlist)
+ (if list (add (pop list)))
+ (set list nlist))))))
+
+(defun dedup-labels (insns)
+ (rewrite-case tail insns
+ ((@(symbolp @label0) @(symbolp @label1) . @rest)
+ (set insns (mapcar [iffi listp (op subst label1 label0)]
+ (remq label1 insns)))
+ (cons label0 rest))
+ (@nil tail))
+ insns)
+
+(defun early-peephole (code)
+ (rewrite-case insns code
+ (((mov (t @t1) (d @d1))
+ (jmp @lab2)
+ @(symbolp @lab1)
+ (mov (t @t1) (t 0))
+ @lab2
+ (ifq (t @t1) (t 0) @lab3)
+ . @rest)
+ ^((mov (t ,t1) (d ,d1))
+ (jmp ,lab3)
+ ,lab1
+ (mov (t ,t1) (t 0))
+ ,lab2
+ ,*rest))
+ (@else else)))