1 ! Copyright (C) 2008 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors kernel arrays sequences math math.order
4 math.partial-dispatch generic generic.standard generic.math
5 classes.algebra classes.union sets quotations assocs combinators
6 words namespaces continuations classes fry
9 compiler.tree.recursive
10 compiler.tree.combinators
11 compiler.tree.normalization
12 compiler.tree.propagation.info
13 compiler.tree.propagation.nodes ;
14 IN: compiler.tree.propagation.inlining
16 ! We count nodes up-front; if there are relatively few nodes,
17 ! we are more eager to inline
20 : count-nodes ( nodes -- )
21 0 swap [ drop 1+ ] each-node node-count set ;
23 ! We try not to inline the same word too many times, to avoid
24 ! combinatorial explosion
25 SYMBOL: inlining-count
28 GENERIC: splicing-nodes ( #call word/quot/f -- nodes )
30 M: word splicing-nodes
31 [ [ in-d>> ] [ out-d>> ] bi ] dip #call 1array ;
33 M: callable splicing-nodes
34 build-sub-tree analyze-recursive normalize ;
36 : propagate-body ( #call -- )
39 ! Dispatch elimination
40 : eliminate-dispatch ( #call class/f word/quot/f -- ? )
43 over method>> over = [ drop ] [
45 [ >>method ] [ >>body ] bi*
48 ] [ 2drop f >>method f >>body f >>class drop f ] if ;
50 : inlining-standard-method ( #call word -- class/f method/f )
51 dup "methods" word-prop assoc-empty? [ 2drop f f ] [
52 [ in-d>> <reversed> ] [ [ dispatch# ] keep ] bi*
53 [ swap nth value-info class>> dup ] dip
57 : inline-standard-method ( #call word -- ? )
58 dupd inlining-standard-method eliminate-dispatch ;
60 : normalize-math-class ( class -- class' )
68 } [ class<= ] with find nip ;
70 : inlining-math-method ( #call word -- class/f quot/f )
72 first2 [ value-info class>> normalize-math-class ] bi@
74 [ math-method* ] [ 3drop f ] if
77 : inline-math-method ( #call word -- ? )
78 dupd inlining-math-method eliminate-dispatch ;
80 : inlining-math-partial ( #call word -- class/f quot/f )
81 [ "derived-from" word-prop first inlining-math-method ]
82 [ nip 1quotation ] 2bi
83 [ = not ] [ drop ] 2bi and ;
85 : inline-math-partial ( #call word -- ? )
86 dupd inlining-math-partial eliminate-dispatch ;
88 ! Method body inlining
89 SYMBOL: recursive-calls
92 : word-flat-length ( word -- n )
95 { [ dup { dip 2dip 3dip } memq? ] [ drop 1 ] }
97 { [ dup inline? not ] [ drop 1 ] }
98 ! recursive and inline
99 { [ dup recursive-calls get key? ] [ drop 10 ] }
101 [ [ recursive-calls get conjoin ] [ def>> (flat-length) ] bi ]
104 : (flat-length) ( seq -- n )
107 { [ dup quotation? ] [ (flat-length) 2 + ] }
108 { [ dup array? ] [ (flat-length) ] }
109 { [ dup word? ] [ word-flat-length ] }
114 : flat-length ( word -- n )
115 H{ } clone recursive-calls [
116 [ recursive-calls get conjoin ]
117 [ def>> (flat-length) 5 /i ]
121 : classes-known? ( #call -- ? )
124 [ class-types length 1 = ]
129 : node-count-bias ( -- n )
130 45 node-count get [-] 8 /i ;
132 : body-length-bias ( word -- n )
133 [ flat-length ] [ inlining-count get at 0 or ] bi
134 over 2 <= [ drop ] [ 2/ 1+ * ] if 24 swap [-] 4 /i ;
136 : inlining-rank ( #call word -- n )
137 [ classes-known? 2 0 ? ]
141 [ "default" word-prop -4 0 ? ]
142 [ "specializer" word-prop 1 0 ? ]
143 [ method-body? 1 0 ? ]
146 loop-nesting get 0 or 2 *
149 : should-inline? ( #call word -- ? )
150 dup "inline" word-prop [ 2drop t ] [ inlining-rank 5 >= ] if ;
154 : remember-inlining ( word -- )
155 [ inlining-count get inc-at ]
156 [ history [ swap suffix ] change ]
159 : inline-word-def ( #call word quot -- ? )
160 over history get memq? [ 3drop f ] [
162 swap remember-inlining
163 dupd splicing-nodes >>body
169 : inline-word ( #call word -- ? )
170 dup def>> inline-word-def ;
172 : inline-method-body ( #call word -- ? )
173 2dup should-inline? [ inline-word ] [ 2drop f ] if ;
175 : always-inline-word? ( word -- ? )
176 { curry compose } memq? ;
178 : custom-inlining? ( word -- ? )
179 "custom-inlining" word-prop ;
181 : inline-custom ( #call word -- ? )
182 [ dup 1array ] [ "custom-inlining" word-prop ] bi* with-datastack
183 first object swap eliminate-dispatch ;
185 : inline-instance-check ( #call word -- ? )
186 over in-d>> second value-info literal>> dup class?
187 [ "predicate" word-prop '[ drop @ ] inline-word-def ] [ 3drop f ] if ;
189 : (do-inlining) ( #call word -- ? )
190 #! If the generic was defined in an outer compilation unit,
191 #! then it doesn't have a definition yet; the definition
192 #! is built at the end of the compilation unit. We do not
193 #! attempt inlining at this stage since the stack discipline
194 #! is not finalized yet, so dispatch# might return an out
195 #! of bounds value. This case comes up if a parsing word
196 #! calls the compiler at parse time (doing so is
197 #! discouraged, but it should still work.)
199 { [ dup deferred? ] [ 2drop f ] }
200 { [ dup \ instance? eq? ] [ inline-instance-check ] }
201 { [ dup always-inline-word? ] [ inline-word ] }
202 { [ dup standard-generic? ] [ inline-standard-method ] }
203 { [ dup math-generic? ] [ inline-math-method ] }
204 { [ dup method-body? ] [ inline-method-body ] }
208 : do-inlining ( #call word -- ? )
209 #! Note the logic here: if there's a custom inlining hook,
210 #! it is permitted to return f, which means that we try the
211 #! normal inlining heuristic.
212 dup custom-inlining? [ 2dup inline-custom ] [ f ] if
213 [ 2drop t ] [ (do-inlining) ] if ;