summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2021-03-10 07:29:54 -0800
committerKaz Kylheku <kaz@kylheku.com>2021-03-10 07:29:54 -0800
commit9dde7f43b8ba88b95ca31fb92d5c85d0df40732e (patch)
tree6b7f15fb3a9c2193b3c9440478b8caf96d8ea3e1
parentbb392fbb4beee89eaa5914191b3f58262f16d68b (diff)
downloadtxr-9dde7f43b8ba88b95ca31fb92d5c85d0df40732e.tar.gz
txr-9dde7f43b8ba88b95ca31fb92d5c85d0df40732e.tar.bz2
txr-9dde7f43b8ba88b95ca31fb92d5c85d0df40732e.zip
compiler: eliminate dead calls.
The main idea in this patch is to identify calls functions whose values are not used and which have no side effects. For this purpose, we borrow the same set of functions that we use as targets for constant folding: those in the compiler's %const-foldable% hash. A call is dead if it is a gcall or gapply instruction which calls one of these functions, and the destination register is dead. To maximize the opportunities for this elimination, whenever we eliminate such an instruction, we mark the block for re-scanning, and we re-calculate the liveness info for that block and then globally. * share/txr/stdlib/compiler.tl (struct compiler): New slots, datavec and symvec. (compiler get-datavec): Cache the calculated datavec in the new datavec slot. (compiler get-symvec): Cache the calculated symvec in the new symvec slot. (compiler optimize): Pass the symvec to the basic-blocks BOA constructor; it is now needed for identifying functions that are referenced by symvec index number in the gcall and gapply instructions. * share/txr/stdlib/optimize.tl (struct basic-blocks): New symvec slot, added to the the BOA parameter list also. New slot recalc, indicating re-calculation of liveness is needed. (basic-blocks cut-block): Use pushnew to put bl onto the rescan list, to prevent duplicates. Also push the new block onto the rescan list. (basic-blocks local-liveness): This method can now be called again to re-calculate local liveness, so we must reset the live slot to nil. (basic-blocks calc-liveness): Take an optional list of blocks to scan for local liveness, defaulting to all of them. (basic-blocks peephole-block): Factor out some register liveness tests to local functions. Add a pattern targetting gcall and gapply instructions, testing for a dead register from a call to a %const-foldable% function. Use pushnew everywhere to add to the rescan list. Set the recalc flag when the liveness-based reductions are applied. (basic-blocks peephole): If there are blocks to be scanned again, then if the recalc flag is set, recalculate local liveness for all the blocks to be re-scanned and re-do global liveness.
-rw-r--r--share/txr/stdlib/compiler.tl10
-rw-r--r--share/txr/stdlib/optimize.tl162
2 files changed, 98 insertions, 74 deletions
diff --git a/share/txr/stdlib/compiler.tl b/share/txr/stdlib/compiler.tl
index c7c57e3b..890e1a8a 100644
--- a/share/txr/stdlib/compiler.tl
+++ b/share/txr/stdlib/compiler.tl
@@ -183,6 +183,8 @@
(data (hash :eql-based))
(sidx (hash :eql-based))
(stab (hash :eql-based))
+ datavec
+ symvec
lt-frags
last-form
var-spies
@@ -361,10 +363,12 @@
(set [me.sidx atom] sidx))))
(defmeth compiler get-datavec (me)
- (vec-list [mapcar me.data (range* 0 me.dreg-cntr)]))
+ (or me.datavec
+ (set me.datavec (vec-list [mapcar me.data (range* 0 me.dreg-cntr)]))))
(defmeth compiler get-symvec (me)
- (vec-list [mapcar me.stab (range* 0 me.sidx-cntr)]))
+ (or me.symvec
+ (set me.symvec (vec-list [mapcar me.stab (range* 0 me.sidx-cntr)]))))
(defmeth compiler alloc-treg (me)
(cond
@@ -1564,7 +1568,7 @@
(let ((olev *opt-level*))
(if (>= olev 4)
(let* ((lt-dregs (mapcar .oreg me.lt-frags))
- (bb (new (basic-blocks insns lt-dregs))))
+ (bb (new (basic-blocks insns lt-dregs me.(get-symvec)))))
(when (>= olev 4)
bb.(thread-jumps)
bb.(elim-dead-code))
diff --git a/share/txr/stdlib/optimize.tl b/share/txr/stdlib/optimize.tl
index babf7bce..8f166a6f 100644
--- a/share/txr/stdlib/optimize.tl
+++ b/share/txr/stdlib/optimize.tl
@@ -47,14 +47,16 @@
rlinks ,(mapcar .label bl.rlinks)
next ,bl.next) stream)))
- (defstruct (basic-blocks insns lt-dregs) nil
+ (defstruct (basic-blocks insns lt-dregs symvec) nil
insns
lt-dregs
+ symvec
root
(hash (hash))
(li-hash (hash :eq-based))
list
rescan
+ recalc
(:static start (gensym "start-"))
(:static jump-ops '(jmp if ifq ifql close swtch ret abscsr
uwprot catch block jend))
@@ -88,7 +90,8 @@
(set bb.list (append (ldiff bb.list ltail) (list nbl) ltail))
(set bl.insns (ldiff insns at))
(set [bb.hash nlabel] nbl)
- (push bl bb.rescan)
+ (pushnew bl bb.rescan)
+ (pushnew nbl bb.rescan)
nbl))
(:method next-block (bb bl)
@@ -148,6 +151,7 @@
(pushnew bl nxbl.rlinks)))))
(defmeth basic-blocks local-liveness (bb bl)
+ (set bl.live nil)
(labels ((regnum (reg)
(when-match (t @num) reg num))
(regnums (regs)
@@ -233,8 +237,8 @@
(set bl.used li.used
bl.defined li.defined))))
-(defmeth basic-blocks calc-liveness (bb)
- (each ((bl bb.list))
+(defmeth basic-blocks calc-liveness (bb : (blist bb.list))
+ (each ((bl blist))
bb.(local-liveness bl))
(let (changed)
(while* changed
@@ -342,79 +346,95 @@
(t (set [bb.li-hash sub] li) sub))))
(defmeth basic-blocks peephole-block (bb bl code)
- (rewrite-case insns code
- ;; dead t-reg
- (@(require ((mov (t @n) . @nil) . @nil)
- (let ((li [bb.li-hash (car insns)]))
- (and li (not (bit li.used n)))))
- (cdr insns))
- ;; unnecessary copying t-reg
- (@(require ((mov @(as dst (t @n)) @src) . @rest)
- (let ((li [bb.li-hash (car insns)]))
- (and li (bit li.used n) (not (bit bl.live n))))
- (or (neq (car src) 'v)
- (none rest [andf [chain car (op eq 'end)]
- [chain bb.li-hash .used (lop bit n)]]))
- (not (find dst rest : [chain bb.li-hash .def]))
- (not (find src rest : [chain bb.li-hash .def])))
- (labels ((rename (insns n dst src)
- (tree-case insns
- ((fi . re)
- (cons (subst-preserve dst src bb [bb.li-hash fi] fi)
- (rename (cdr insns) n dst src)))
- (else else))))
- (rename (cdr insns) n dst src)))
- ;; wasteful moves
- (((mov @reg0 @nil) (mov @reg0 @nil) . @nil)
- (cdr insns))
- (((mov @reg0 @reg1) (mov reg1 @reg0) . @rest)
- ^(,(car insns) ,*rest))
- ;; frame reduction
- (((@(or frame dframe) @lev @size)
- (@(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))
- (let ((jinsns [bb.hash jlabel].insns))
- (match-case jinsns
- ((@jlabel
- (end (t @reg)) . @jrest)
- (let* ((xbl (if jrest
- bb.(cut-block [bb.hash jlabel] jrest jinsns)
- bb.(next-block [bb.hash jlabel])))
- (ybl bb.(next-block bl))
- (yinsns ybl.insns))
- (cond
- ((and xbl ybl)
- (set ybl.insns ^(,ybl.label ,(car insns) ,*(cdr yinsns)))
- (push ybl bb.rescan)
- ^((if (t ,reg) ,xbl.label)))
- (t insns))))
- (@jelse insns))))
- (@(require ((if @(as reg (d @dn)) @jlabel) . @nil)
- (not (memqual reg bb.lt-dregs)))
- nil)
- (@(require ((ifq @(as reg (d @dn)) (t 0) @jlabel) . @nil)
- (not (memqual reg bb.lt-dregs)))
- ^((jmp ,jlabel)))
- (((jmp @jlabel) . @rest)
- (let ((jinsns (cdr [bb.hash jlabel].insns)))
- (match-case jinsns
- (((jend @nil) . @nil)
- ^(,(car jinsns) ,*rest))
- ((@nil (jend @nil) . @nil)
- ^(,(car jinsns) ,(cadr jinsns) ,*rest))
- (@else insns))))
- (@else insns)))
+ (labels ((dead-treg (insn n)
+ (let ((li [bb.li-hash insn]))
+ (and li (not (bit li.used n)))))
+ (only-locally-used-treg (insn n)
+ (let ((li [bb.li-hash insn]))
+ (and li (bit li.used n) (not (bit bl.live n))))))
+ (rewrite-case insns code
+ ;; dead t-reg
+ (@(require ((mov (t @n) . @nil) . @nil)
+ (dead-treg (car insns) n))
+ (pushnew bl bb.rescan)
+ (set bb.recalc t)
+ (cdr insns))
+ (@(require ((@(or gcall gapply) (t @n) @idx . @nil) . @nil)
+ (dead-treg (car insns) n)
+ [%const-foldable% [bb.symvec idx]])
+ (pushnew bl bb.rescan)
+ (set bb.recalc t)
+ (cdr insns))
+ ;; unnecessary copying t-reg
+ (@(require ((mov @(as dst (t @n)) @src) . @rest)
+ (only-locally-used-treg (car insns) n)
+ (or (neq (car src) 'v)
+ (none rest [andf [chain car (op eq 'end)]
+ [chain bb.li-hash .used (lop bit n)]]))
+ (not (find dst rest : [chain bb.li-hash .def]))
+ (not (find src rest : [chain bb.li-hash .def])))
+ (pushnew bl bb.rescan)
+ (labels ((rename (insns n dst src)
+ (tree-case insns
+ ((fi . re)
+ (cons (subst-preserve dst src bb [bb.li-hash fi] fi)
+ (rename (cdr insns) n dst src)))
+ (else else))))
+ (rename (cdr insns) n dst src)))
+ ;; wasteful moves
+ (((mov @reg0 @nil) (mov @reg0 @nil) . @nil)
+ (cdr insns))
+ (((mov @reg0 @reg1) (mov reg1 @reg0) . @rest)
+ ^(,(car insns) ,*rest))
+ ;; frame reduction
+ (((@(or frame dframe) @lev @size)
+ (@(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))
+ (let ((jinsns [bb.hash jlabel].insns))
+ (match-case jinsns
+ ((@jlabel
+ (end (t @reg)) . @jrest)
+ (let* ((xbl (if jrest
+ bb.(cut-block [bb.hash jlabel] jrest jinsns)
+ bb.(next-block [bb.hash jlabel])))
+ (ybl bb.(next-block bl))
+ (yinsns ybl.insns))
+ (cond
+ ((and xbl ybl)
+ (set ybl.insns ^(,ybl.label ,(car insns) ,*(cdr yinsns)))
+ (pushnew ybl bb.rescan)
+ ^((if (t ,reg) ,xbl.label)))
+ (t insns))))
+ (@jelse insns))))
+ (@(require ((if @(as reg (d @dn)) @jlabel) . @nil)
+ (not (memqual reg bb.lt-dregs)))
+ nil)
+ (@(require ((ifq @(as reg (d @dn)) (t 0) @jlabel) . @nil)
+ (not (memqual reg bb.lt-dregs)))
+ ^((jmp ,jlabel)))
+ (((jmp @jlabel) . @rest)
+ (let ((jinsns (cdr [bb.hash jlabel].insns)))
+ (match-case jinsns
+ (((jend @nil) . @nil)
+ ^(,(car jinsns) ,*rest))
+ ((@nil (jend @nil) . @nil)
+ ^(,(car jinsns) ,(cadr jinsns) ,*rest))
+ (@else insns))))
+ (@else insns))))
(defmeth basic-blocks peephole (bb)
(each ((bl bb.list))
(set bl.insns bb.(peephole-block bl bl.insns)))
(whilet ((rescan bb.rescan))
(set bb.rescan nil)
+ (when bb.recalc
+ bb.(calc-liveness rescan)
+ (set bb.recalc nil))
(each ((bl rescan))
(set bl.insns bb.(peephole-block bl bl.insns)))))