1 ! Copyright (C) 2009 Slava Pestov
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: kernel fry accessors sequences assocs sets namespaces
4 arrays combinators combinators.short-circuit math make locals
5 deques dlists layouts byte-arrays cpu.architecture
11 compiler.cfg.registers
12 compiler.cfg.instructions
14 compiler.cfg.utilities
15 compiler.cfg.loop-detection
16 compiler.cfg.renaming.functor
17 compiler.cfg.representations.preferred ;
18 FROM: namespaces => set ;
19 IN: compiler.cfg.representations
21 ! Virtual register representation selection.
23 ERROR: bad-conversion dst src dst-rep src-rep ;
25 GENERIC: emit-box ( dst src rep -- )
26 GENERIC: emit-unbox ( dst src rep -- )
28 M:: float-rep emit-box ( dst src rep -- )
29 double-rep next-vreg-rep :> temp
30 temp src ##single>double-float
31 dst temp double-rep emit-box ;
33 M:: float-rep emit-unbox ( dst src rep -- )
34 double-rep next-vreg-rep :> temp
35 temp src double-rep emit-unbox
36 dst temp ##double>single-float ;
38 M: double-rep emit-box
40 [ drop 16 float int-rep next-vreg-rep ##allot ]
41 [ float-offset swap ##set-alien-double ]
44 M: double-rep emit-unbox
45 drop float-offset ##alien-double ;
47 M:: vector-rep emit-box ( dst src rep -- )
48 int-rep next-vreg-rep :> temp
49 dst 16 2 cells + byte-array int-rep next-vreg-rep ##allot
50 temp 16 tag-fixnum ##load-immediate
51 temp dst 1 byte-array type-number ##set-slot-imm
52 dst byte-array-offset src rep ##set-alien-vector ;
54 M: vector-rep emit-unbox
55 [ byte-array-offset ] dip ##alien-vector ;
57 M:: scalar-rep emit-box ( dst src rep -- )
58 int-rep next-vreg-rep :> temp
59 temp src rep ##scalar>integer
60 dst temp tag-bits get ##shl-imm ;
62 M:: scalar-rep emit-unbox ( dst src rep -- )
63 int-rep next-vreg-rep :> temp
64 temp src tag-bits get ##sar-imm
65 dst temp rep ##integer>scalar ;
67 : emit-conversion ( dst src dst-rep src-rep -- )
69 { [ 2dup eq? ] [ drop ##copy ] }
70 { [ dup int-rep eq? ] [ drop emit-unbox ] }
71 { [ over int-rep eq? ] [ nip emit-box ] }
74 { { double-rep float-rep } [ 2drop ##single>double-float ] }
75 { { float-rep double-rep } [ 2drop ##double>single-float ] }
76 ! Punning SIMD vector types? Naughty naughty! But
77 ! it is allowed... otherwise bail out.
79 drop 2dup [ reg-class-of ] bi@ eq?
80 [ drop ##copy ] [ bad-conversion ] if
88 ! For every vreg, compute possible representations.
91 : possible ( vreg -- reps ) possibilities get at ;
93 : compute-possibilities ( cfg -- )
94 H{ } clone [ '[ swap _ conjoin-at ] with-vreg-reps ] keep
95 [ keys ] assoc-map possibilities set ;
97 ! Compute vregs which must remain tagged for their lifetime.
100 :: (compute-always-boxed) ( vreg rep assoc -- )
102 int-rep vreg assoc set-at
105 : compute-always-boxed ( cfg -- assoc )
109 dup [ ##load-reference? ] [ ##load-constant? ] bi or
110 [ drop ] [ [ _ (compute-always-boxed) ] each-def-rep ] if
115 ! For every vreg, compute the cost of keeping it in every possible
118 ! Cost map maps vreg to representation to cost.
122 possibilities get [ [ 0 ] H{ } map>assoc ] assoc-map costs set ;
124 : increase-cost ( rep vreg -- )
125 ! Increase cost of keeping vreg in rep, making a choice of rep less
127 [ basic-block get loop-nesting-at ] 2dip costs get at at+ ;
129 : maybe-increase-cost ( possible vreg preferred -- )
130 pick eq? [ 2drop ] [ increase-cost ] if ;
132 : representation-cost ( vreg preferred -- )
133 ! 'preferred' is a representation that the instruction can accept with no cost.
134 ! So, for each representation that's not preferred, increase the cost of keeping
135 ! the vreg in that representation.
137 [ '[ _ _ maybe-increase-cost ] ]
140 : compute-costs ( cfg -- costs )
141 init-costs [ representation-cost ] with-vreg-reps costs get ;
143 ! For every vreg, compute preferred representation, that minimizes costs.
144 : minimize-costs ( costs -- representations )
145 [ >alist alist-min first ] assoc-map ;
147 : compute-representations ( cfg -- )
148 [ compute-costs minimize-costs ]
149 [ compute-always-boxed ]
151 representations set ;
153 ! Insert conversions. This introduces new temporaries, so we need
154 ! to rename opearands too.
156 ! Mapping from vreg,rep pairs to vregs
159 :: emit-def-conversion ( dst preferred required -- new-dst' )
160 ! If an instruction defines a register with representation 'required',
161 ! but the register has preferred representation 'preferred', then
162 ! we rename the instruction's definition to a new register, which
163 ! becomes the input of a conversion instruction.
164 dst required next-vreg-rep [ preferred required emit-conversion ] keep ;
166 :: emit-use-conversion ( src preferred required -- new-src' )
167 ! If an instruction uses a register with representation 'required',
168 ! but the register has preferred representation 'preferred', then
169 ! we rename the instruction's input to a new register, which
170 ! becomes the output of a conversion instruction.
171 preferred required eq? [ src ] [
172 src required alternatives get [
173 required next-vreg-rep :> new-src
174 [ new-src ] 2dip preferred emit-conversion
179 SYMBOLS: renaming-set needs-renaming? ;
181 : init-renaming-set ( -- )
183 V{ } clone renaming-set set ;
185 : no-renaming ( vreg -- )
186 dup 2array renaming-set get push ;
188 : record-renaming ( from to -- )
189 2array renaming-set get push needs-renaming? on ;
191 :: (compute-renaming-set) ( ..a vreg required quot: ( ..a vreg preferred required -- ..b ) -- ..b )
192 vreg rep-of :> preferred
193 preferred required eq?
195 [ vreg vreg preferred required quot call record-renaming ] if ; inline
197 : compute-renaming-set ( insn -- )
198 ! temp vregs don't need conversions since they're always in their
199 ! preferred representation
201 [ [ [ emit-use-conversion ] (compute-renaming-set) ] each-use-rep ]
203 [ [ [ emit-def-conversion ] (compute-renaming-set) ] each-def-rep ]
206 : converted-value ( vreg -- vreg' )
207 renaming-set get pop first2 [ assert= ] dip ;
209 RENAMING: convert [ converted-value ] [ converted-value ] [ ]
211 : perform-renaming ( insn -- )
212 needs-renaming? get [
213 renaming-set get reverse! drop
214 [ convert-insn-uses ] [ convert-insn-defs ] bi
215 renaming-set get length 0 assert=
218 GENERIC: conversions-for-insn ( insn -- )
222 ! compiler.cfg.cssa inserts conversions which convert phi inputs into
223 ! the representation of the output. However, we still have to do some
224 ! processing here, because if the only node that uses the output of
225 ! the phi instruction is another phi instruction then this phi node's
226 ! output won't have a representation assigned.
227 M: ##phi conversions-for-insn
228 [ , ] [ [ inputs>> values ] [ dst>> ] bi phi-mappings get set-at ] bi ;
230 ! When a literal zeroes/ones vector is unboxed, we replace the ##load-reference
231 ! with a ##zero-vector or ##fill-vector instruction since this is more efficient.
232 : convert-to-zero-vector? ( insn -- ? )
234 [ dst>> rep-of vector-rep? ]
235 [ obj>> B{ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 } = ]
237 : convert-to-fill-vector? ( insn -- ? )
239 [ dst>> rep-of vector-rep? ]
240 [ obj>> B{ 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 } = ]
243 : (convert-to-zero/fill-vector) ( insn -- dst rep )
244 dst>> dup rep-of ; inline
246 : conversions-for-load-insn ( insn -- ?insn )
249 [ dup convert-to-zero-vector? ]
250 [ (convert-to-zero/fill-vector) ##zero-vector f ]
253 [ dup convert-to-fill-vector? ]
254 [ (convert-to-zero/fill-vector) ##fill-vector f ]
259 M: ##load-reference conversions-for-insn
260 conversions-for-load-insn [ call-next-method ] when* ;
262 M: ##load-constant conversions-for-insn
263 conversions-for-load-insn [ call-next-method ] when* ;
265 M: vreg-insn conversions-for-insn
266 [ compute-renaming-set ] [ perform-renaming ] bi ;
268 M: insn conversions-for-insn , ;
270 : conversions-for-block ( bb -- )
271 dup kill-block? [ drop ] [
274 H{ } clone alternatives set
275 [ conversions-for-insn ] each
277 ] change-instructions drop
280 ! If the output of a phi instruction is only used as the input to another
281 ! phi instruction, then we want to use the same representation for both
285 : add-to-work-list ( vregs -- )
286 work-list get push-all-front ;
288 : rep-assigned ( vregs -- vregs' )
289 representations get '[ _ key? ] filter ;
291 : rep-not-assigned ( vregs -- vregs' )
292 representations get '[ _ key? not ] filter ;
294 : add-ready-phis ( -- )
295 phi-mappings get keys rep-assigned add-to-work-list ;
297 : process-phi-mapping ( dst -- )
298 ! If dst = phi(src1,src2,...) and dst's representation has been
299 ! determined, assign that representation to each one of src1,...
300 ! that does not have a representation yet, and process those, too.
301 dup phi-mappings get at* [
302 [ rep-of ] [ rep-not-assigned ] bi*
303 [ [ set-rep-of ] with each ] [ add-to-work-list ] bi
306 : remaining-phi-mappings ( -- )
307 phi-mappings get keys rep-not-assigned
308 [ [ int-rep ] dip set-rep-of ] each ;
310 : process-phi-mappings ( -- )
311 <hashed-dlist> work-list set
313 work-list get [ process-phi-mapping ] slurp-deque
314 remaining-phi-mappings ;
316 : insert-conversions ( cfg -- )
317 H{ } clone phi-mappings set
318 [ conversions-for-block ] each-basic-block
319 process-phi-mappings ;
323 : select-representations ( cfg -- cfg' )
327 [ compute-possibilities ]
328 [ compute-representations ]
329 [ insert-conversions ]
332 representations get cfg get (>>reps) ;