factor/basis/compiler/cfg/alias-analysis/alias-analysis.factor

323 lines
8.5 KiB
Factor
Raw Normal View History

2010-04-21 03:08:52 -04:00
! Copyright (C) 2008, 2010 Slava Pestov.
2008-10-22 19:37:47 -04:00
! See http://factorcode.org/license.txt for BSD license.
USING: kernel math namespaces assocs hashtables sequences arrays
accessors words vectors combinators combinators.short-circuit
sets classes layouts fry locals cpu.architecture
compiler.cfg
compiler.cfg.rpo
compiler.cfg.def-use
compiler.cfg.liveness
compiler.cfg.registers
compiler.cfg.utilities
compiler.cfg.comparisons
compiler.cfg.instructions
compiler.cfg.representations.preferred ;
2010-02-26 16:01:01 -05:00
FROM: namespaces => set ;
2008-10-22 19:37:47 -04:00
IN: compiler.cfg.alias-analysis
! We try to eliminate redundant slot operations using some simple heuristics.
2008-10-22 19:37:47 -04:00
!
! All heap-allocated objects which are loaded from the stack, or
! other object slots are pessimistically assumed to belong to
! the same alias class.
!
! Freshly-allocated objects get their own alias class.
!
! Simple pseudo-C example showing load elimination:
!
! int *x, *y, z: inputs
! int a, b, c, d, e: locals
!
! Before alias analysis:
!
! a = x[2]
! b = x[2]
! c = x[3]
! y[2] = z
! d = x[2]
! e = y[2]
! f = x[3]
!
! After alias analysis:
!
! a = x[2]
! b = a /* ELIMINATED */
! c = x[3]
! y[2] = z
! d = x[2] /* if x=y, d=z, if x!=y, d=b; NOT ELIMINATED */
! e = z /* ELIMINATED */
! f = c /* ELIMINATED */
!
! Simple pseudo-C example showing store elimination:
!
! Before alias analysis:
!
! x[0] = a
! b = x[n]
! x[0] = c
! x[1] = d
! e = x[0]
! x[1] = c
!
! After alias analysis:
!
! x[0] = a /* dead if n = 0, live otherwise; NOT ELIMINATED */
! b = x[n]
! x[0] = c
! /* x[1] = d */ /* ELIMINATED */
! e = c
! x[1] = c
! Local copy propagation
SYMBOL: copies
: resolve ( vreg -- vreg ) copies get ?at drop ;
: record-copy ( ##copy -- )
[ src>> resolve ] [ dst>> ] bi copies get set-at ; inline
2008-10-22 19:37:47 -04:00
! Map vregs -> alias classes
SYMBOL: vregs>acs
ERROR: vreg-ac-not-set vreg ;
2008-10-22 19:37:47 -04:00
: vreg>ac ( vreg -- ac )
#! Only vregs produced by ##allot, ##peek and ##slot can
#! ever be used as valid inputs to ##slot and ##set-slot,
#! so we assert this fact by not giving alias classes to
#! other vregs.
vregs>acs get ?at [ vreg-ac-not-set ] unless ;
2008-10-22 19:37:47 -04:00
! Map alias classes -> sequence of vregs
SYMBOL: acs>vregs
: ac>vregs ( ac -- vregs ) acs>vregs get at ;
: aliases ( vreg -- vregs )
2008-10-22 19:37:47 -04:00
#! All vregs which may contain the same value as vreg.
vreg>ac ac>vregs ;
: each-alias ( vreg quot -- )
[ aliases ] dip each ; inline
: merge-acs ( vreg into -- )
[ vreg>ac ] dip
2dup eq? [ 2drop ] [
[ ac>vregs ] dip
[ vregs>acs get '[ [ _ ] dip _ set-at ] each ]
[ acs>vregs get at push-all ]
2bi
] if ;
2008-10-22 19:37:47 -04:00
! Map vregs -> slot# -> vreg
SYMBOL: live-slots
! Maps vreg -> slot# -> insn# of last store or f
SYMBOL: recent-stores
2008-10-22 19:37:47 -04:00
! A set of insn#s of dead stores
SYMBOL: dead-stores
2008-10-22 19:37:47 -04:00
: dead-store ( insn# -- ) dead-stores get adjoin ;
2008-10-22 19:37:47 -04:00
:: set-ac ( vreg ac -- )
2008-10-22 19:37:47 -04:00
#! Set alias class of newly-seen vreg.
H{ } clone vreg recent-stores get set-at
H{ } clone vreg live-slots get set-at
ac vreg vregs>acs get set-at
vreg ac acs>vregs get push-at ;
2008-10-22 19:37:47 -04:00
: live-slot ( slot#/f vreg -- vreg' )
#! If the slot number is unknown, we never reuse a previous
#! value.
over [ live-slots get at at ] [ 2drop f ] if ;
ERROR: vreg-has-no-slots vreg ;
2008-10-22 19:37:47 -04:00
: load-constant-slot ( value slot# vreg -- )
live-slots get ?at [ vreg-has-no-slots ] unless set-at ;
2008-10-22 19:37:47 -04:00
: load-slot ( value slot#/f vreg -- )
over [ load-constant-slot ] [ 3drop ] if ;
: record-constant-slot ( slot# vreg -- )
#! A load can potentially read every store of this slot#
#! in that alias class.
[ recent-stores get at delete-at ] with each-alias ;
2008-10-22 19:37:47 -04:00
: record-computed-slot ( vreg -- )
#! Computed load is like a load of every slot touched so far
[ recent-stores get at clear-assoc ] each-alias ;
2008-10-22 19:37:47 -04:00
:: remember-slot ( value slot# vreg -- )
slot# [
slot# vreg record-constant-slot
value slot# vreg load-constant-slot
] [ vreg record-computed-slot ] if ;
2008-10-22 19:37:47 -04:00
SYMBOL: ac-counter
: next-ac ( -- n )
ac-counter [ dup 1 + ] change ;
2008-10-22 19:37:47 -04:00
! Alias class for objects which are loaded from the data stack
! or other object slots. We pessimistically assume that they
! can all alias each other.
SYMBOL: heap-ac
: set-heap-ac ( vreg -- ) heap-ac get set-ac ;
: set-new-ac ( vreg -- ) next-ac set-ac ;
: kill-constant-set-slot ( slot# vreg -- )
[ live-slots get at delete-at ] with each-alias ;
:: record-constant-set-slot ( insn# slot# vreg -- )
vreg recent-stores get at :> recent-stores
slot# recent-stores at [ dead-store ] when*
insn# slot# recent-stores set-at ;
2008-10-22 19:37:47 -04:00
: kill-computed-set-slot ( vreg -- )
2008-10-22 19:37:47 -04:00
[ live-slots get at clear-assoc ] each-alias ;
:: remember-set-slot ( insn# slot# vreg -- )
slot# [
insn# slot# vreg record-constant-set-slot
slot# vreg kill-constant-set-slot
] [ vreg kill-computed-set-slot ] if ;
2008-10-22 19:37:47 -04:00
2010-07-02 15:44:12 -04:00
: init-alias-analysis ( -- )
H{ } clone vregs>acs set
H{ } clone acs>vregs set
H{ } clone live-slots set
H{ } clone copies set
H{ } clone recent-stores set
HS{ } clone dead-stores set
0 ac-counter set ;
2008-10-22 19:37:47 -04:00
GENERIC: insn-slot# ( insn -- slot#/f )
GENERIC: insn-object ( insn -- vreg )
2010-04-21 03:08:52 -04:00
M: ##slot insn-slot# drop f ;
2008-10-22 19:37:47 -04:00
M: ##slot-imm insn-slot# slot>> ;
2010-04-21 03:08:52 -04:00
M: ##set-slot insn-slot# drop f ;
2008-10-22 19:37:47 -04:00
M: ##set-slot-imm insn-slot# slot>> ;
M: ##alien-global insn-slot# [ library>> ] [ symbol>> ] bi 2array ;
M: ##vm-field insn-slot# offset>> ;
M: ##set-vm-field insn-slot# offset>> ;
2008-10-22 19:37:47 -04:00
M: ##slot insn-object obj>> resolve ;
M: ##slot-imm insn-object obj>> resolve ;
M: ##set-slot insn-object obj>> resolve ;
M: ##set-slot-imm insn-object obj>> resolve ;
M: ##alien-global insn-object drop \ ##alien-global ;
M: ##vm-field insn-object drop \ ##vm-field ;
M: ##set-vm-field insn-object drop \ ##vm-field ;
2008-10-22 19:37:47 -04:00
GENERIC: analyze-aliases ( insn -- insn' )
M: insn analyze-aliases ;
2008-10-22 19:37:47 -04:00
M: vreg-insn analyze-aliases
! If an instruction defines a value with a non-integer
! representation it means that the value will be boxed
! anywhere its used as a tagged pointer. Boxing allocates
! a new value, except boxing instructions haven't been
! inserted yet.
dup defs-vreg [
over defs-vreg-rep { int-rep tagged-rep } member?
[ set-heap-ac ] [ set-new-ac ] if
] when* ;
M: ##phi analyze-aliases
dup defs-vreg set-heap-ac ;
M: ##allocation analyze-aliases
#! A freshly allocated object is distinct from any other
#! object.
dup dst>> set-new-ac ;
M: ##box-displaced-alien analyze-aliases
2010-05-13 01:46:58 -04:00
[ call-next-method ]
[ base>> heap-ac get merge-acs ] bi ;
M: ##read analyze-aliases
call-next-method
2008-10-22 19:37:47 -04:00
dup [ dst>> ] [ insn-slot# ] [ insn-object ] tri
2dup live-slot dup
[ 2nip <copy> analyze-aliases nip ]
[ drop remember-slot ]
if ;
2008-10-22 19:37:47 -04:00
: idempotent? ( value slot#/f vreg -- ? )
#! Are we storing a value back to the same slot it was read
#! from?
live-slot = ;
M:: ##write analyze-aliases ( insn -- insn )
insn src>> resolve :> src
insn insn-slot# :> slot#
insn insn-object :> vreg
insn insn#>> :> insn#
src slot# vreg idempotent? [ insn# dead-store ] [
src heap-ac get merge-acs
insn insn#>> slot# vreg remember-set-slot
src slot# vreg load-slot
] if
2008-10-22 19:37:47 -04:00
insn ;
M: ##copy analyze-aliases
2008-10-22 19:37:47 -04:00
#! The output vreg gets the same alias class as the input
#! vreg, since they both contain the same value.
dup record-copy ;
: useless-compare? ( insn -- ? )
{
[ cc>> cc= eq? ]
[ [ src1>> ] [ src2>> ] bi [ resolve vreg>ac ] bi@ = not ]
} 1&& ; inline
M: ##compare analyze-aliases
call-next-method
dup useless-compare? [
2010-04-21 03:08:52 -04:00
dst>> f \ ##load-reference new-insn
analyze-aliases
] when ;
: reset-alias-analysis ( -- )
recent-stores get clear-assoc
vregs>acs get clear-assoc
acs>vregs get clear-assoc
live-slots get clear-assoc
copies get clear-assoc
dead-stores get table>> clear-assoc
2008-10-22 19:37:47 -04:00
next-ac heap-ac set
\ ##vm-field set-new-ac
\ ##alien-global set-new-ac ;
2008-10-22 19:37:47 -04:00
2010-07-02 15:44:12 -04:00
M: factor-call-insn analyze-aliases
heap-ac get ac>vregs [
[ live-slots get at clear-assoc ]
[ recent-stores get at clear-assoc ] bi
] each ;
GENERIC: eliminate-dead-stores ( insn -- ? )
M: ##set-slot-imm eliminate-dead-stores
insn#>> dead-stores get in? not ;
M: insn eliminate-dead-stores drop t ;
: alias-analysis-step ( insns -- insns' )
reset-alias-analysis
[ local-live-in [ set-heap-ac ] each ]
[ 0 [ [ insn#<< ] [ drop 1 + ] 2bi ] reduce drop ]
[ [ analyze-aliases ] map! [ eliminate-dead-stores ] filter! ] tri ;
2010-04-30 18:55:20 -04:00
: alias-analysis ( cfg -- cfg )
init-alias-analysis
2010-04-30 18:55:20 -04:00
dup [ alias-analysis-step ] simple-optimization ;