Checking in new codegen
parent
63a1e604ae
commit
83aa1ccb68
|
@ -0,0 +1,46 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: accessors kernel namespaces make math sequences layouts
|
||||
alien.c-types alien.structs compiler.backend ;
|
||||
IN: compiler.alien
|
||||
|
||||
! Common utilities
|
||||
|
||||
: large-struct? ( ctype -- ? )
|
||||
dup c-struct? [
|
||||
heap-size struct-small-enough? not
|
||||
] [ drop f ] if ;
|
||||
|
||||
: alien-parameters ( params -- seq )
|
||||
dup parameters>>
|
||||
swap return>> large-struct? [ "void*" prefix ] when ;
|
||||
|
||||
: alien-return ( params -- ctype )
|
||||
return>> dup large-struct? [ drop "void" ] when ;
|
||||
|
||||
: c-type-stack-align ( type -- align )
|
||||
dup c-type-stack-align? [ c-type-align ] [ drop cell ] if ;
|
||||
|
||||
: parameter-align ( n type -- n delta )
|
||||
over >r c-type-stack-align align dup r> - ;
|
||||
|
||||
: parameter-sizes ( types -- total offsets )
|
||||
#! Compute stack frame locations.
|
||||
[
|
||||
0 [
|
||||
[ parameter-align drop dup , ] keep stack-size +
|
||||
] reduce cell align
|
||||
] { } make ;
|
||||
|
||||
: return-size ( ctype -- n )
|
||||
#! Amount of space we reserve for a return value.
|
||||
dup large-struct? [ heap-size ] [ drop 0 ] if ;
|
||||
|
||||
: alien-stack-frame ( params -- n )
|
||||
alien-parameters parameter-sizes drop ;
|
||||
|
||||
: alien-invoke-frame ( params -- n )
|
||||
#! One cell is temporary storage, temp@
|
||||
dup return>> return-size
|
||||
swap alien-stack-frame +
|
||||
cell + ;
|
|
@ -0,0 +1,281 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
IN: compiler.backend.alien
|
||||
|
||||
! #alien-invoke
|
||||
: set-stack-frame ( n -- )
|
||||
dup [ frame-required ] when* \ stack-frame set ;
|
||||
|
||||
: with-stack-frame ( n quot -- )
|
||||
swap set-stack-frame
|
||||
call
|
||||
f set-stack-frame ; inline
|
||||
|
||||
GENERIC: reg-size ( register-class -- n )
|
||||
|
||||
M: int-regs reg-size drop cell ;
|
||||
|
||||
M: single-float-regs reg-size drop 4 ;
|
||||
|
||||
M: double-float-regs reg-size drop 8 ;
|
||||
|
||||
GENERIC: reg-class-variable ( register-class -- symbol )
|
||||
|
||||
M: reg-class reg-class-variable ;
|
||||
|
||||
M: float-regs reg-class-variable drop float-regs ;
|
||||
|
||||
GENERIC: inc-reg-class ( register-class -- )
|
||||
|
||||
M: reg-class inc-reg-class
|
||||
dup reg-class-variable inc
|
||||
fp-shadows-int? [ reg-size stack-params +@ ] [ drop ] if ;
|
||||
|
||||
M: float-regs inc-reg-class
|
||||
dup call-next-method
|
||||
fp-shadows-int? [ reg-size cell /i int-regs +@ ] [ drop ] if ;
|
||||
|
||||
GENERIC: reg-class-full? ( class -- ? )
|
||||
|
||||
M: stack-params reg-class-full? drop t ;
|
||||
|
||||
M: object reg-class-full?
|
||||
[ reg-class-variable get ] [ param-regs length ] bi >= ;
|
||||
|
||||
: spill-param ( reg-class -- n reg-class )
|
||||
stack-params get
|
||||
>r reg-size stack-params +@ r>
|
||||
stack-params ;
|
||||
|
||||
: fastcall-param ( reg-class -- n reg-class )
|
||||
[ reg-class-variable get ] [ inc-reg-class ] [ ] tri ;
|
||||
|
||||
: alloc-parameter ( parameter -- reg reg-class )
|
||||
c-type-reg-class dup reg-class-full?
|
||||
[ spill-param ] [ fastcall-param ] if
|
||||
[ param-reg ] keep ;
|
||||
|
||||
: (flatten-int-type) ( size -- )
|
||||
cell /i "void*" c-type <repetition> % ;
|
||||
|
||||
GENERIC: flatten-value-type ( type -- )
|
||||
|
||||
M: object flatten-value-type , ;
|
||||
|
||||
M: struct-type flatten-value-type ( type -- )
|
||||
stack-size cell align (flatten-int-type) ;
|
||||
|
||||
M: long-long-type flatten-value-type ( type -- )
|
||||
stack-size cell align (flatten-int-type) ;
|
||||
|
||||
: flatten-value-types ( params -- params )
|
||||
#! Convert value type structs to consecutive void*s.
|
||||
[
|
||||
0 [
|
||||
c-type
|
||||
[ parameter-align (flatten-int-type) ] keep
|
||||
[ stack-size cell align + ] keep
|
||||
flatten-value-type
|
||||
] reduce drop
|
||||
] { } make ;
|
||||
|
||||
: each-parameter ( parameters quot -- )
|
||||
>r [ parameter-sizes nip ] keep r> 2each ; inline
|
||||
|
||||
: reverse-each-parameter ( parameters quot -- )
|
||||
>r [ parameter-sizes nip ] keep r> 2reverse-each ; inline
|
||||
|
||||
: reset-freg-counts ( -- )
|
||||
{ int-regs float-regs stack-params } [ 0 swap set ] each ;
|
||||
|
||||
: with-param-regs ( quot -- )
|
||||
#! In quot you can call alloc-parameter
|
||||
[ reset-freg-counts call ] with-scope ; inline
|
||||
|
||||
: move-parameters ( node word -- )
|
||||
#! Moves values from C stack to registers (if word is
|
||||
#! %load-param-reg) and registers to C stack (if word is
|
||||
#! %save-param-reg).
|
||||
>r
|
||||
alien-parameters
|
||||
flatten-value-types
|
||||
r> [ >r alloc-parameter r> execute ] curry each-parameter ;
|
||||
inline
|
||||
|
||||
: unbox-parameters ( offset node -- )
|
||||
parameters>> [
|
||||
%prepare-unbox >r over + r> unbox-parameter
|
||||
] reverse-each-parameter drop ;
|
||||
|
||||
: prepare-box-struct ( node -- offset )
|
||||
#! Return offset on C stack where to store unboxed
|
||||
#! parameters. If the C function is returning a structure,
|
||||
#! the first parameter is an implicit target area pointer,
|
||||
#! so we need to use a different offset.
|
||||
return>> dup large-struct?
|
||||
[ heap-size %prepare-box-struct cell ] [ drop 0 ] if ;
|
||||
|
||||
: objects>registers ( params -- )
|
||||
#! Generate code for unboxing a list of C types, then
|
||||
#! generate code for moving these parameters to register on
|
||||
#! architectures where parameters are passed in registers.
|
||||
[
|
||||
[ prepare-box-struct ] keep
|
||||
[ unbox-parameters ] keep
|
||||
\ %load-param-reg move-parameters
|
||||
] with-param-regs ;
|
||||
|
||||
: box-return* ( node -- )
|
||||
return>> [ ] [ box-return ] if-void ;
|
||||
|
||||
TUPLE: no-such-library name ;
|
||||
|
||||
M: no-such-library summary
|
||||
drop "Library not found" ;
|
||||
|
||||
M: no-such-library compiler-error-type
|
||||
drop +linkage+ ;
|
||||
|
||||
: no-such-library ( name -- )
|
||||
\ no-such-library boa
|
||||
compiling-word get compiler-error ;
|
||||
|
||||
TUPLE: no-such-symbol name ;
|
||||
|
||||
M: no-such-symbol summary
|
||||
drop "Symbol not found" ;
|
||||
|
||||
M: no-such-symbol compiler-error-type
|
||||
drop +linkage+ ;
|
||||
|
||||
: no-such-symbol ( name -- )
|
||||
\ no-such-symbol boa
|
||||
compiling-word get compiler-error ;
|
||||
|
||||
: check-dlsym ( symbols dll -- )
|
||||
dup dll-valid? [
|
||||
dupd [ dlsym ] curry contains?
|
||||
[ drop ] [ no-such-symbol ] if
|
||||
] [
|
||||
dll-path no-such-library drop
|
||||
] if ;
|
||||
|
||||
: stdcall-mangle ( symbol node -- symbol )
|
||||
"@"
|
||||
swap parameters>> parameter-sizes drop
|
||||
number>string 3append ;
|
||||
|
||||
: alien-invoke-dlsym ( params -- symbols dll )
|
||||
dup function>> dup pick stdcall-mangle 2array
|
||||
swap library>> library dup [ dll>> ] when
|
||||
2dup check-dlsym ;
|
||||
|
||||
M: #alien-invoke generate-node
|
||||
params>>
|
||||
dup alien-invoke-frame [
|
||||
end-basic-block
|
||||
%prepare-alien-invoke
|
||||
dup objects>registers
|
||||
%prepare-var-args
|
||||
dup alien-invoke-dlsym %alien-invoke
|
||||
dup %cleanup
|
||||
box-return*
|
||||
iterate-next
|
||||
] with-stack-frame ;
|
||||
|
||||
! #alien-indirect
|
||||
M: #alien-indirect generate-node
|
||||
params>>
|
||||
dup alien-invoke-frame [
|
||||
! Flush registers
|
||||
end-basic-block
|
||||
! Save registers for GC
|
||||
%prepare-alien-invoke
|
||||
! Save alien at top of stack to temporary storage
|
||||
%prepare-alien-indirect
|
||||
dup objects>registers
|
||||
%prepare-var-args
|
||||
! Call alien in temporary storage
|
||||
%alien-indirect
|
||||
dup %cleanup
|
||||
box-return*
|
||||
iterate-next
|
||||
] with-stack-frame ;
|
||||
|
||||
! #alien-callback
|
||||
: box-parameters ( params -- )
|
||||
alien-parameters [ box-parameter ] each-parameter ;
|
||||
|
||||
: registers>objects ( node -- )
|
||||
[
|
||||
dup \ %save-param-reg move-parameters
|
||||
"nest_stacks" f %alien-invoke
|
||||
box-parameters
|
||||
] with-param-regs ;
|
||||
|
||||
TUPLE: callback-context ;
|
||||
|
||||
: current-callback 2 getenv ;
|
||||
|
||||
: wait-to-return ( token -- )
|
||||
dup current-callback eq? [
|
||||
drop
|
||||
] [
|
||||
yield wait-to-return
|
||||
] if ;
|
||||
|
||||
: do-callback ( quot token -- )
|
||||
init-catchstack
|
||||
dup 2 setenv
|
||||
slip
|
||||
wait-to-return ; inline
|
||||
|
||||
: callback-return-quot ( ctype -- quot )
|
||||
return>> {
|
||||
{ [ dup "void" = ] [ drop [ ] ] }
|
||||
{ [ dup large-struct? ] [ heap-size [ memcpy ] curry ] }
|
||||
[ c-type c-type-unboxer-quot ]
|
||||
} cond ;
|
||||
|
||||
: callback-prep-quot ( params -- quot )
|
||||
parameters>> [ c-type c-type-boxer-quot ] map spread>quot ;
|
||||
|
||||
: wrap-callback-quot ( params -- quot )
|
||||
[
|
||||
[ callback-prep-quot ]
|
||||
[ quot>> ]
|
||||
[ callback-return-quot ] tri 3append ,
|
||||
[ callback-context new do-callback ] %
|
||||
] [ ] make ;
|
||||
|
||||
: %unnest-stacks ( -- ) "unnest_stacks" f %alien-invoke ;
|
||||
|
||||
: callback-unwind ( params -- n )
|
||||
{
|
||||
{ [ dup abi>> "stdcall" = ] [ alien-stack-frame ] }
|
||||
{ [ dup return>> large-struct? ] [ drop 4 ] }
|
||||
[ drop 0 ]
|
||||
} cond ;
|
||||
|
||||
: %callback-return ( params -- )
|
||||
#! All the extra book-keeping for %unwind is only for x86.
|
||||
#! On other platforms its an alias for %return.
|
||||
dup alien-return
|
||||
[ %unnest-stacks ] [ %callback-value ] if-void
|
||||
callback-unwind %unwind ;
|
||||
|
||||
: generate-callback ( params -- )
|
||||
dup xt>> dup [
|
||||
init-templates
|
||||
%prologue
|
||||
dup alien-stack-frame [
|
||||
[ registers>objects ]
|
||||
[ wrap-callback-quot %alien-callback ]
|
||||
[ %callback-return ]
|
||||
tri
|
||||
] with-stack-frame
|
||||
] with-cfg-builder ;
|
||||
|
||||
M: #alien-callback generate-node
|
||||
end-basic-block
|
||||
params>> generate-callback iterate-next ;
|
|
@ -0,0 +1,10 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: system ;
|
||||
IN: compiler.backend
|
||||
|
||||
! Is this structure small enough to be returned in registers?
|
||||
HOOK: struct-small-enough? cpu ( size -- ? )
|
||||
|
||||
! Mapping from register class to machine registers
|
||||
HOOK: machine-registers cpu ( -- assoc )
|
|
@ -0,0 +1,10 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: system cpu.x86.assembler compiler.registers compiler.backend ;
|
||||
IN: compiler.backend.x86.32
|
||||
|
||||
M: x86.32 machine-registers
|
||||
{
|
||||
{ int-regs { EAX ECX EDX EBP EBX } }
|
||||
{ float-regs { XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7 } }
|
||||
} ;
|
|
@ -0,0 +1,4 @@
|
|||
IN: compiler.cfg.builder.tests
|
||||
USING: compiler.cfg.builder tools.test ;
|
||||
|
||||
\ build-cfg must-infer
|
|
@ -0,0 +1,256 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: arrays kernel assocs sequences sequences.lib fry accessors
|
||||
namespaces math combinators math.order
|
||||
compiler.tree
|
||||
compiler.tree.combinators
|
||||
compiler.tree.propagation.info
|
||||
compiler.cfg
|
||||
compiler.vops
|
||||
compiler.vops.builder ;
|
||||
IN: compiler.cfg.builder
|
||||
|
||||
! Convert tree SSA IR to CFG SSA IR.
|
||||
|
||||
! We construct the graph and set successors first, then we
|
||||
! set predecessors in a separate pass. This simplifies the
|
||||
! logic.
|
||||
|
||||
SYMBOL: procedures
|
||||
|
||||
SYMBOL: loop-nesting
|
||||
|
||||
SYMBOL: values>vregs
|
||||
|
||||
GENERIC: convert ( node -- )
|
||||
|
||||
M: #introduce convert drop ;
|
||||
|
||||
: init-builder ( -- )
|
||||
H{ } clone values>vregs set ;
|
||||
|
||||
: end-basic-block ( -- )
|
||||
basic-block get [ %b emit ] when ;
|
||||
|
||||
: set-basic-block ( basic-block -- )
|
||||
[ basic-block set ] [ instructions>> building set ] bi ;
|
||||
|
||||
: begin-basic-block ( -- )
|
||||
<basic-block> basic-block get
|
||||
[
|
||||
end-basic-block
|
||||
dupd successors>> push
|
||||
] when*
|
||||
set-basic-block ;
|
||||
|
||||
: convert-nodes ( node -- )
|
||||
[ convert ] each ;
|
||||
|
||||
: (build-cfg) ( node word -- )
|
||||
init-builder
|
||||
begin-basic-block
|
||||
basic-block get swap procedures get set-at
|
||||
convert-nodes ;
|
||||
|
||||
: build-cfg ( node word -- procedures )
|
||||
H{ } clone [
|
||||
procedures [ (build-cfg) ] with-variable
|
||||
] keep ;
|
||||
|
||||
: value>vreg ( value -- vreg )
|
||||
values>vregs get at ;
|
||||
|
||||
: output-vreg ( value vreg -- )
|
||||
swap values>vregs get set-at ;
|
||||
|
||||
: produce-vreg ( value -- vreg )
|
||||
next-vreg [ output-vreg ] keep ;
|
||||
|
||||
: (load-inputs) ( seq stack -- )
|
||||
over empty? [ 2drop ] [
|
||||
[ <reversed> ] dip
|
||||
[ '[ produce-vreg _ , %peek emit ] each-index ]
|
||||
[ [ length neg ] dip %height emit ]
|
||||
2bi
|
||||
] if ;
|
||||
|
||||
: load-in-d ( node -- ) in-d>> %data (load-inputs) ;
|
||||
|
||||
: load-in-r ( node -- ) in-r>> %retain (load-inputs) ;
|
||||
|
||||
: (store-outputs) ( seq stack -- )
|
||||
over empty? [ 2drop ] [
|
||||
[ <reversed> ] dip
|
||||
[ [ length ] dip %height emit ]
|
||||
[ '[ value>vreg _ , %replace emit ] each-index ]
|
||||
2bi
|
||||
] if ;
|
||||
|
||||
: store-out-d ( node -- ) out-d>> %data (store-outputs) ;
|
||||
|
||||
: store-out-r ( node -- ) out-r>> %retain (store-outputs) ;
|
||||
|
||||
: (emit-call) ( word -- )
|
||||
begin-basic-block %call emit begin-basic-block ;
|
||||
|
||||
: intrinsic-inputs ( node -- )
|
||||
[ load-in-d ]
|
||||
[ in-d>> { #1 #2 #3 #4 } [ [ value>vreg ] dip set ] 2each ]
|
||||
bi ;
|
||||
|
||||
: intrinsic-outputs ( node -- )
|
||||
[ out-d>> { ^1 ^2 ^3 ^4 } [ get output-vreg ] 2each ]
|
||||
[ store-out-d ]
|
||||
bi ;
|
||||
|
||||
: intrinsic ( node quot -- )
|
||||
[
|
||||
init-intrinsic
|
||||
|
||||
[ intrinsic-inputs ]
|
||||
swap
|
||||
[ intrinsic-outputs ]
|
||||
tri
|
||||
] with-scope ; inline
|
||||
|
||||
USING: kernel.private math.private slots.private ;
|
||||
|
||||
: maybe-emit-fixnum-shift-fast ( node -- node )
|
||||
dup dup in-d>> second node-value-info literal>> dup fixnum? [
|
||||
'[ , emit-fixnum-shift-fast ] intrinsic
|
||||
] [
|
||||
drop dup word>> (emit-call)
|
||||
] if ;
|
||||
|
||||
: emit-call ( node -- )
|
||||
dup word>> {
|
||||
{ \ tag [ [ emit-tag ] intrinsic ] }
|
||||
|
||||
{ \ slot [ [ dup emit-slot ] intrinsic ] }
|
||||
{ \ set-slot [ [ dup emit-set-slot ] intrinsic ] }
|
||||
|
||||
{ \ fixnum-bitnot [ [ emit-fixnum-bitnot ] intrinsic ] }
|
||||
{ \ fixnum+fast [ [ emit-fixnum+fast ] intrinsic ] }
|
||||
{ \ fixnum-fast [ [ emit-fixnum-fast ] intrinsic ] }
|
||||
{ \ fixnum-bitand [ [ emit-fixnum-bitand ] intrinsic ] }
|
||||
{ \ fixnum-bitor [ [ emit-fixnum-bitor ] intrinsic ] }
|
||||
{ \ fixnum-bitxor [ [ emit-fixnum-bitxor ] intrinsic ] }
|
||||
{ \ fixnum*fast [ [ emit-fixnum*fast ] intrinsic ] }
|
||||
{ \ fixnum<= [ [ emit-fixnum<= ] intrinsic ] }
|
||||
{ \ fixnum>= [ [ emit-fixnum>= ] intrinsic ] }
|
||||
{ \ fixnum< [ [ emit-fixnum< ] intrinsic ] }
|
||||
{ \ fixnum> [ [ emit-fixnum> ] intrinsic ] }
|
||||
{ \ eq? [ [ emit-eq? ] intrinsic ] }
|
||||
|
||||
{ \ fixnum-shift-fast [ maybe-emit-fixnum-shift-fast ] }
|
||||
|
||||
{ \ float+ [ [ emit-float+ ] intrinsic ] }
|
||||
{ \ float- [ [ emit-float- ] intrinsic ] }
|
||||
{ \ float* [ [ emit-float* ] intrinsic ] }
|
||||
{ \ float/f [ [ emit-float/f ] intrinsic ] }
|
||||
{ \ float<= [ [ emit-float<= ] intrinsic ] }
|
||||
{ \ float>= [ [ emit-float>= ] intrinsic ] }
|
||||
{ \ float< [ [ emit-float< ] intrinsic ] }
|
||||
{ \ float> [ [ emit-float> ] intrinsic ] }
|
||||
{ \ float? [ [ emit-float= ] intrinsic ] }
|
||||
|
||||
! { \ (tuple) [ dup first-input '[ , emit-(tuple) ] intrinsic ] }
|
||||
! { \ (array) [ dup first-input '[ , emit-(array) ] intrinsic ] }
|
||||
! { \ (byte-array) [ dup first-input '[ , emit-(byte-array) ] intrinsic ] }
|
||||
|
||||
[ (emit-call) ]
|
||||
} case drop ;
|
||||
|
||||
M: #call convert emit-call ;
|
||||
|
||||
: emit-call-loop ( #recursive -- )
|
||||
dup label>> loop-nesting get at basic-block get successors>> push
|
||||
end-basic-block
|
||||
basic-block off
|
||||
drop ;
|
||||
|
||||
: emit-call-recursive ( #recursive -- )
|
||||
label>> id>> (emit-call) ;
|
||||
|
||||
M: #call-recursive convert
|
||||
dup label>> loop?>>
|
||||
[ emit-call-loop ] [ emit-call-recursive ] if ;
|
||||
|
||||
M: #push convert
|
||||
[
|
||||
[ out-d>> first produce-vreg ]
|
||||
[ node-output-infos first literal>> ]
|
||||
bi emit-literal
|
||||
]
|
||||
[ store-out-d ] bi ;
|
||||
|
||||
M: #shuffle convert [ load-in-d ] [ store-out-d ] bi ;
|
||||
|
||||
M: #>r convert [ load-in-d ] [ store-out-r ] bi ;
|
||||
|
||||
M: #r> convert [ load-in-r ] [ store-out-d ] bi ;
|
||||
|
||||
M: #terminate convert drop ;
|
||||
|
||||
: integer-conditional ( in1 in2 cc -- )
|
||||
[ [ next-vreg dup ] 2dip %icmp emit ] dip %bi emit ; inline
|
||||
|
||||
: float-conditional ( in1 in2 branch -- )
|
||||
[ next-vreg [ %fcmp emit ] keep ] dip emit ; inline
|
||||
|
||||
: emit-if ( #if -- )
|
||||
in-d>> first value>vreg
|
||||
next-vreg dup f emit-literal
|
||||
cc/= integer-conditional ;
|
||||
|
||||
: convert-nested ( node -- last-bb )
|
||||
[
|
||||
<basic-block>
|
||||
[ set-basic-block ] keep
|
||||
[ convert-nodes end-basic-block ] dip
|
||||
basic-block get
|
||||
] with-scope
|
||||
[ basic-block get successors>> push ] dip ;
|
||||
|
||||
: convert-if-children ( #if -- )
|
||||
children>> [ convert-nested ] map sift
|
||||
<basic-block>
|
||||
[ '[ , _ successors>> push ] each ]
|
||||
[ set-basic-block ]
|
||||
bi ;
|
||||
|
||||
M: #if convert
|
||||
[ load-in-d ] [ emit-if ] [ convert-if-children ] tri ;
|
||||
|
||||
M: #dispatch convert
|
||||
"Unimplemented" throw ;
|
||||
|
||||
M: #phi convert drop ;
|
||||
|
||||
M: #declare convert drop ;
|
||||
|
||||
M: #return convert drop %return emit ;
|
||||
|
||||
: convert-recursive ( #recursive -- )
|
||||
[ [ label>> id>> ] [ child>> ] bi (build-cfg) ]
|
||||
[ (emit-call) ]
|
||||
bi ;
|
||||
|
||||
: begin-loop ( #recursive -- )
|
||||
label>> basic-block get 2array loop-nesting get push ;
|
||||
|
||||
: end-loop ( -- )
|
||||
loop-nesting get pop* ;
|
||||
|
||||
: convert-loop ( #recursive -- )
|
||||
begin-basic-block
|
||||
[ begin-loop ]
|
||||
[ child>> convert-nodes ]
|
||||
[ drop end-loop ]
|
||||
tri ;
|
||||
|
||||
M: #recursive convert
|
||||
dup label>> loop?>>
|
||||
[ convert-loop ] [ convert-recursive ] if ;
|
||||
|
||||
M: #copy convert drop ;
|
|
@ -0,0 +1,47 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: kernel accessors namespaces assocs sequences sets fry ;
|
||||
IN: compiler.cfg
|
||||
|
||||
! The id is a globally unique id used for fast hashcode* and
|
||||
! equal? on basic blocks. The number is assigned by
|
||||
! linearization.
|
||||
TUPLE: basic-block < identity-tuple
|
||||
id
|
||||
number
|
||||
instructions
|
||||
successors
|
||||
predecessors
|
||||
stack-frame ;
|
||||
|
||||
SYMBOL: next-block-id
|
||||
|
||||
: <basic-block> ( -- basic-block )
|
||||
basic-block new
|
||||
next-block-id counter >>id
|
||||
V{ } clone >>instructions
|
||||
V{ } clone >>successors
|
||||
V{ } clone >>predecessors ;
|
||||
|
||||
M: basic-block hashcode* id>> nip ;
|
||||
|
||||
! Utilities
|
||||
SYMBOL: visited-blocks
|
||||
|
||||
: visit-block ( basic-block quot -- )
|
||||
over visited-blocks get 2dup key?
|
||||
[ 2drop 2drop ] [ conjoin call ] if ; inline
|
||||
|
||||
: (each-block) ( basic-block quot -- )
|
||||
'[
|
||||
,
|
||||
[ call ]
|
||||
[ [ successors>> ] dip '[ , (each-block) ] each ]
|
||||
2bi
|
||||
] visit-block ; inline
|
||||
|
||||
: each-block ( basic-block quot -- )
|
||||
H{ } clone visited-blocks [ (each-block) ] with-variable ; inline
|
||||
|
||||
: copy-at ( from to assoc -- )
|
||||
3dup nip at* [ -rot set-at drop ] [ 2drop 2drop ] if ; inline
|
|
@ -0,0 +1 @@
|
|||
Slava Pestov
|
|
@ -1,4 +1,45 @@
|
|||
IN: compiler.cfg.builder.tests
|
||||
USING: compiler.cfg.builder tools.test ;
|
||||
USING: compiler.cfg.builder tools.test kernel sequences
|
||||
math.private compiler.tree.builder compiler.tree.optimizer
|
||||
words sequences.private fry prettyprint alien ;
|
||||
|
||||
\ build-cfg must-infer
|
||||
! Just ensure that various CFGs build correctly.
|
||||
: test-cfg ( quot -- result )
|
||||
build-tree optimize-tree gensym gensym build-cfg ;
|
||||
|
||||
{
|
||||
[ ]
|
||||
[ dup ]
|
||||
[ swap ]
|
||||
[ >r r> ]
|
||||
[ fixnum+ ]
|
||||
[ fixnum< ]
|
||||
[ [ 1 ] [ 2 ] if ]
|
||||
[ fixnum< [ 1 ] [ 2 ] if ]
|
||||
[ float+ [ 2.0 float* ] [ 3.0 float* ] bi float/f ]
|
||||
[ { [ 1 ] [ 2 ] [ 3 ] } dispatch ]
|
||||
[ [ t ] loop ]
|
||||
[ [ dup ] loop ]
|
||||
[ [ 2 ] [ 3 throw ] if 4 ]
|
||||
[ "int" f "malloc" { "int" } alien-invoke ]
|
||||
[ "int" { "int" } "cdecl" alien-indirect ]
|
||||
[ "int" { "int" } "cdecl" [ ] alien-callback ]
|
||||
} [
|
||||
'[ _ test-cfg drop ] [ ] swap unit-test
|
||||
] each
|
||||
|
||||
: test-word-cfg ( word -- result )
|
||||
[ build-tree-from-word nip optimize-tree ] keep dup
|
||||
build-cfg ;
|
||||
|
||||
: test-1 ( -- ) test-1 ;
|
||||
: test-2 ( -- ) 3 . test-2 ;
|
||||
: test-3 ( a -- b ) dup [ test-3 ] when ;
|
||||
|
||||
{
|
||||
test-1
|
||||
test-2
|
||||
test-3
|
||||
} [
|
||||
'[ _ test-word-cfg drop ] [ ] swap unit-test
|
||||
] each
|
||||
|
|
|
@ -1,256 +1,295 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! Copyright (C) 2004, 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: arrays kernel assocs sequences sequences.lib fry accessors
|
||||
namespaces math combinators math.order
|
||||
USING: accessors arrays assocs combinators hashtables kernel
|
||||
math fry namespaces make sequences words stack-checker.inlining
|
||||
compiler.tree
|
||||
compiler.tree.builder
|
||||
compiler.tree.combinators
|
||||
compiler.tree.propagation.info
|
||||
compiler.cfg
|
||||
compiler.vops
|
||||
compiler.vops.builder ;
|
||||
compiler.cfg.stacks
|
||||
compiler.cfg.templates
|
||||
compiler.cfg.iterator
|
||||
compiler.alien
|
||||
compiler.instructions
|
||||
compiler.registers ;
|
||||
IN: compiler.cfg.builder
|
||||
|
||||
! Convert tree SSA IR to CFG SSA IR.
|
||||
|
||||
! We construct the graph and set successors first, then we
|
||||
! set predecessors in a separate pass. This simplifies the
|
||||
! logic.
|
||||
|
||||
SYMBOL: procedures
|
||||
|
||||
SYMBOL: loop-nesting
|
||||
|
||||
SYMBOL: values>vregs
|
||||
|
||||
GENERIC: convert ( node -- )
|
||||
|
||||
M: #introduce convert drop ;
|
||||
|
||||
: init-builder ( -- )
|
||||
H{ } clone values>vregs set ;
|
||||
|
||||
: end-basic-block ( -- )
|
||||
basic-block get [ %b emit ] when ;
|
||||
! Convert tree SSA IR to CFG (not quite SSA yet) IR.
|
||||
|
||||
: set-basic-block ( basic-block -- )
|
||||
[ basic-block set ] [ instructions>> building set ] bi ;
|
||||
|
||||
: begin-basic-block ( -- )
|
||||
<basic-block> basic-block get
|
||||
[
|
||||
end-basic-block
|
||||
<basic-block> basic-block get [
|
||||
dupd successors>> push
|
||||
] when*
|
||||
set-basic-block ;
|
||||
|
||||
: convert-nodes ( node -- )
|
||||
[ convert ] each ;
|
||||
: end-basic-block ( -- )
|
||||
building off
|
||||
basic-block off ;
|
||||
|
||||
: (build-cfg) ( node word -- )
|
||||
init-builder
|
||||
USE: qualified
|
||||
FROM: compiler.generator.registers => +input+ ;
|
||||
FROM: compiler.generator.registers => +output+ ;
|
||||
FROM: compiler.generator.registers => +scratch+ ;
|
||||
FROM: compiler.generator.registers => +clobber+ ;
|
||||
|
||||
SYMBOL: procedures
|
||||
|
||||
SYMBOL: current-word
|
||||
|
||||
SYMBOL: current-label
|
||||
|
||||
SYMBOL: loops
|
||||
|
||||
! Basic block after prologue, makes recursion faster
|
||||
SYMBOL: current-label-start
|
||||
|
||||
: add-procedure ( -- )
|
||||
basic-block get current-word get current-label get
|
||||
<procedure> procedures get push ;
|
||||
|
||||
: begin-procedure ( word label -- )
|
||||
end-basic-block
|
||||
begin-basic-block
|
||||
basic-block get swap procedures get set-at
|
||||
convert-nodes ;
|
||||
H{ } clone loops set
|
||||
current-label set
|
||||
current-word set
|
||||
add-procedure ;
|
||||
|
||||
: build-cfg ( node word -- procedures )
|
||||
H{ } clone [
|
||||
procedures [ (build-cfg) ] with-variable
|
||||
: with-cfg-builder ( nodes word label quot -- )
|
||||
'[ begin-procedure @ ] with-scope ; inline
|
||||
|
||||
GENERIC: emit-node ( node -- next )
|
||||
|
||||
: check-basic-block ( node -- node' )
|
||||
basic-block get [ drop f ] unless ; inline
|
||||
|
||||
: emit-nodes ( nodes -- )
|
||||
[ current-node emit-node check-basic-block ] iterate-nodes
|
||||
finalize-phantoms ;
|
||||
|
||||
: remember-loop ( label -- )
|
||||
basic-block get swap loops get set-at ;
|
||||
|
||||
: begin-word ( -- )
|
||||
#! We store the basic block after the prologue as a loop
|
||||
#! labelled by the current word, so that self-recursive
|
||||
#! calls can skip an epilogue/prologue.
|
||||
init-phantoms
|
||||
%prologue
|
||||
%branch
|
||||
begin-basic-block
|
||||
current-label get remember-loop ;
|
||||
|
||||
: (build-cfg) ( nodes word label -- )
|
||||
[
|
||||
begin-word
|
||||
[ emit-nodes ] with-node-iterator
|
||||
] with-cfg-builder ;
|
||||
|
||||
: build-cfg ( nodes word label -- procedures )
|
||||
V{ } clone [
|
||||
procedures [
|
||||
(build-cfg)
|
||||
] with-variable
|
||||
] keep ;
|
||||
|
||||
: value>vreg ( value -- vreg )
|
||||
values>vregs get at ;
|
||||
: if-intrinsics ( #call -- quot )
|
||||
word>> "if-intrinsics" word-prop ;
|
||||
|
||||
: output-vreg ( value vreg -- )
|
||||
swap values>vregs get set-at ;
|
||||
: local-recursive-call ( basic-block -- )
|
||||
%branch
|
||||
basic-block get successors>> push
|
||||
end-basic-block ;
|
||||
|
||||
: produce-vreg ( value -- vreg )
|
||||
next-vreg [ output-vreg ] keep ;
|
||||
: emit-call ( word -- next )
|
||||
finalize-phantoms
|
||||
{
|
||||
{ [ tail-call? not ] [ 0 %frame-required %call iterate-next ] }
|
||||
{ [ dup loops get key? ] [ loops get at local-recursive-call f ] }
|
||||
[ %epilogue %jump f ]
|
||||
} cond ;
|
||||
|
||||
: (load-inputs) ( seq stack -- )
|
||||
over empty? [ 2drop ] [
|
||||
[ <reversed> ] dip
|
||||
[ '[ produce-vreg _ , %peek emit ] each-index ]
|
||||
[ [ length neg ] dip %height emit ]
|
||||
2bi
|
||||
] if ;
|
||||
! #recursive
|
||||
: compile-recursive ( node -- next )
|
||||
[ label>> id>> emit-call ]
|
||||
[ [ child>> ] [ label>> word>> ] [ label>> id>> ] tri (build-cfg) ] bi ;
|
||||
|
||||
: load-in-d ( node -- ) in-d>> %data (load-inputs) ;
|
||||
|
||||
: load-in-r ( node -- ) in-r>> %retain (load-inputs) ;
|
||||
|
||||
: (store-outputs) ( seq stack -- )
|
||||
over empty? [ 2drop ] [
|
||||
[ <reversed> ] dip
|
||||
[ [ length ] dip %height emit ]
|
||||
[ '[ value>vreg _ , %replace emit ] each-index ]
|
||||
2bi
|
||||
] if ;
|
||||
|
||||
: store-out-d ( node -- ) out-d>> %data (store-outputs) ;
|
||||
|
||||
: store-out-r ( node -- ) out-r>> %retain (store-outputs) ;
|
||||
|
||||
: (emit-call) ( word -- )
|
||||
begin-basic-block %call emit begin-basic-block ;
|
||||
|
||||
: intrinsic-inputs ( node -- )
|
||||
[ load-in-d ]
|
||||
[ in-d>> { #1 #2 #3 #4 } [ [ value>vreg ] dip set ] 2each ]
|
||||
bi ;
|
||||
|
||||
: intrinsic-outputs ( node -- )
|
||||
[ out-d>> { ^1 ^2 ^3 ^4 } [ get output-vreg ] 2each ]
|
||||
[ store-out-d ]
|
||||
bi ;
|
||||
|
||||
: intrinsic ( node quot -- )
|
||||
[
|
||||
init-intrinsic
|
||||
|
||||
[ intrinsic-inputs ]
|
||||
swap
|
||||
[ intrinsic-outputs ]
|
||||
tri
|
||||
] with-scope ; inline
|
||||
|
||||
USING: kernel.private math.private slots.private ;
|
||||
|
||||
: maybe-emit-fixnum-shift-fast ( node -- node )
|
||||
dup dup in-d>> second node-value-info literal>> dup fixnum? [
|
||||
'[ , emit-fixnum-shift-fast ] intrinsic
|
||||
] [
|
||||
drop dup word>> (emit-call)
|
||||
] if ;
|
||||
|
||||
: emit-call ( node -- )
|
||||
dup word>> {
|
||||
{ \ tag [ [ emit-tag ] intrinsic ] }
|
||||
|
||||
{ \ slot [ [ dup emit-slot ] intrinsic ] }
|
||||
{ \ set-slot [ [ dup emit-set-slot ] intrinsic ] }
|
||||
|
||||
{ \ fixnum-bitnot [ [ emit-fixnum-bitnot ] intrinsic ] }
|
||||
{ \ fixnum+fast [ [ emit-fixnum+fast ] intrinsic ] }
|
||||
{ \ fixnum-fast [ [ emit-fixnum-fast ] intrinsic ] }
|
||||
{ \ fixnum-bitand [ [ emit-fixnum-bitand ] intrinsic ] }
|
||||
{ \ fixnum-bitor [ [ emit-fixnum-bitor ] intrinsic ] }
|
||||
{ \ fixnum-bitxor [ [ emit-fixnum-bitxor ] intrinsic ] }
|
||||
{ \ fixnum*fast [ [ emit-fixnum*fast ] intrinsic ] }
|
||||
{ \ fixnum<= [ [ emit-fixnum<= ] intrinsic ] }
|
||||
{ \ fixnum>= [ [ emit-fixnum>= ] intrinsic ] }
|
||||
{ \ fixnum< [ [ emit-fixnum< ] intrinsic ] }
|
||||
{ \ fixnum> [ [ emit-fixnum> ] intrinsic ] }
|
||||
{ \ eq? [ [ emit-eq? ] intrinsic ] }
|
||||
|
||||
{ \ fixnum-shift-fast [ maybe-emit-fixnum-shift-fast ] }
|
||||
|
||||
{ \ float+ [ [ emit-float+ ] intrinsic ] }
|
||||
{ \ float- [ [ emit-float- ] intrinsic ] }
|
||||
{ \ float* [ [ emit-float* ] intrinsic ] }
|
||||
{ \ float/f [ [ emit-float/f ] intrinsic ] }
|
||||
{ \ float<= [ [ emit-float<= ] intrinsic ] }
|
||||
{ \ float>= [ [ emit-float>= ] intrinsic ] }
|
||||
{ \ float< [ [ emit-float< ] intrinsic ] }
|
||||
{ \ float> [ [ emit-float> ] intrinsic ] }
|
||||
{ \ float? [ [ emit-float= ] intrinsic ] }
|
||||
|
||||
! { \ (tuple) [ dup first-input '[ , emit-(tuple) ] intrinsic ] }
|
||||
! { \ (array) [ dup first-input '[ , emit-(array) ] intrinsic ] }
|
||||
! { \ (byte-array) [ dup first-input '[ , emit-(byte-array) ] intrinsic ] }
|
||||
|
||||
[ (emit-call) ]
|
||||
} case drop ;
|
||||
|
||||
M: #call convert emit-call ;
|
||||
|
||||
: emit-call-loop ( #recursive -- )
|
||||
dup label>> loop-nesting get at basic-block get successors>> push
|
||||
end-basic-block
|
||||
basic-block off
|
||||
drop ;
|
||||
|
||||
: emit-call-recursive ( #recursive -- )
|
||||
label>> id>> (emit-call) ;
|
||||
|
||||
M: #call-recursive convert
|
||||
dup label>> loop?>>
|
||||
[ emit-call-loop ] [ emit-call-recursive ] if ;
|
||||
|
||||
M: #push convert
|
||||
[
|
||||
[ out-d>> first produce-vreg ]
|
||||
[ node-output-infos first literal>> ]
|
||||
bi emit-literal
|
||||
]
|
||||
[ store-out-d ] bi ;
|
||||
|
||||
M: #shuffle convert [ load-in-d ] [ store-out-d ] bi ;
|
||||
|
||||
M: #>r convert [ load-in-d ] [ store-out-r ] bi ;
|
||||
|
||||
M: #r> convert [ load-in-r ] [ store-out-d ] bi ;
|
||||
|
||||
M: #terminate convert drop ;
|
||||
|
||||
: integer-conditional ( in1 in2 cc -- )
|
||||
[ [ next-vreg dup ] 2dip %icmp emit ] dip %bi emit ; inline
|
||||
|
||||
: float-conditional ( in1 in2 branch -- )
|
||||
[ next-vreg [ %fcmp emit ] keep ] dip emit ; inline
|
||||
|
||||
: emit-if ( #if -- )
|
||||
in-d>> first value>vreg
|
||||
next-vreg dup f emit-literal
|
||||
cc/= integer-conditional ;
|
||||
|
||||
: convert-nested ( node -- last-bb )
|
||||
[
|
||||
<basic-block>
|
||||
[ set-basic-block ] keep
|
||||
[ convert-nodes end-basic-block ] dip
|
||||
basic-block get
|
||||
] with-scope
|
||||
[ basic-block get successors>> push ] dip ;
|
||||
|
||||
: convert-if-children ( #if -- )
|
||||
children>> [ convert-nested ] map sift
|
||||
<basic-block>
|
||||
[ '[ , _ successors>> push ] each ]
|
||||
[ set-basic-block ]
|
||||
bi ;
|
||||
|
||||
M: #if convert
|
||||
[ load-in-d ] [ emit-if ] [ convert-if-children ] tri ;
|
||||
|
||||
M: #dispatch convert
|
||||
"Unimplemented" throw ;
|
||||
|
||||
M: #phi convert drop ;
|
||||
|
||||
M: #declare convert drop ;
|
||||
|
||||
M: #return convert drop %return emit ;
|
||||
|
||||
: convert-recursive ( #recursive -- )
|
||||
[ [ label>> id>> ] [ child>> ] bi (build-cfg) ]
|
||||
[ (emit-call) ]
|
||||
bi ;
|
||||
|
||||
: begin-loop ( #recursive -- )
|
||||
label>> basic-block get 2array loop-nesting get push ;
|
||||
|
||||
: end-loop ( -- )
|
||||
loop-nesting get pop* ;
|
||||
|
||||
: convert-loop ( #recursive -- )
|
||||
: compile-loop ( node -- next )
|
||||
finalize-phantoms
|
||||
begin-basic-block
|
||||
[ begin-loop ]
|
||||
[ child>> convert-nodes ]
|
||||
[ drop end-loop ]
|
||||
tri ;
|
||||
[ label>> id>> remember-loop ] [ child>> emit-nodes ] bi
|
||||
iterate-next ;
|
||||
|
||||
M: #recursive convert
|
||||
dup label>> loop?>>
|
||||
[ convert-loop ] [ convert-recursive ] if ;
|
||||
M: #recursive emit-node
|
||||
dup label>> loop?>> [ compile-loop ] [ compile-recursive ] if ;
|
||||
|
||||
M: #copy convert drop ;
|
||||
! #if
|
||||
: emit-branch ( nodes -- final-bb )
|
||||
[
|
||||
begin-basic-block copy-phantoms
|
||||
emit-nodes
|
||||
basic-block get dup [ %branch ] when
|
||||
] with-scope ;
|
||||
|
||||
: emit-if ( node -- next )
|
||||
children>> [ emit-branch ] map
|
||||
end-basic-block
|
||||
begin-basic-block
|
||||
basic-block get '[ [ _ swap successors>> push ] when* ] each
|
||||
init-phantoms
|
||||
iterate-next ;
|
||||
|
||||
M: #if emit-node
|
||||
{ { f "flag" } } lazy-load first %branch-t
|
||||
emit-if ;
|
||||
|
||||
! #dispatch
|
||||
: dispatch-branch ( nodes word -- label )
|
||||
gensym [
|
||||
[
|
||||
copy-phantoms
|
||||
%prologue
|
||||
[ emit-nodes ] with-node-iterator
|
||||
%epilogue
|
||||
%return
|
||||
] with-cfg-builder
|
||||
] keep ;
|
||||
|
||||
: dispatch-branches ( node -- )
|
||||
children>> [
|
||||
current-word get dispatch-branch
|
||||
%dispatch-label
|
||||
] each ;
|
||||
|
||||
: emit-dispatch ( node -- )
|
||||
%dispatch dispatch-branches init-phantoms ;
|
||||
|
||||
M: #dispatch emit-node
|
||||
#! The order here is important, dispatch-branches must
|
||||
#! run after %dispatch, so that each branch gets the
|
||||
#! correct register state
|
||||
tail-call? [
|
||||
emit-dispatch iterate-next
|
||||
] [
|
||||
current-word get gensym [
|
||||
[
|
||||
begin-word
|
||||
emit-dispatch
|
||||
] with-cfg-builder
|
||||
] keep emit-call
|
||||
] if ;
|
||||
|
||||
! #call
|
||||
: define-intrinsics ( word intrinsics -- )
|
||||
"intrinsics" set-word-prop ;
|
||||
|
||||
: define-intrinsic ( word quot assoc -- )
|
||||
2array 1array define-intrinsics ;
|
||||
|
||||
: define-if-intrinsics ( word intrinsics -- )
|
||||
[ +input+ associate ] assoc-map
|
||||
"if-intrinsics" set-word-prop ;
|
||||
|
||||
: define-if-intrinsic ( word quot inputs -- )
|
||||
2array 1array define-if-intrinsics ;
|
||||
|
||||
: find-intrinsic ( #call -- pair/f )
|
||||
word>> "intrinsics" word-prop find-template ;
|
||||
|
||||
: find-boolean-intrinsic ( #call -- pair/f )
|
||||
word>> "if-intrinsics" word-prop find-template ;
|
||||
|
||||
: find-if-intrinsic ( #call -- pair/f )
|
||||
node@ {
|
||||
{ [ dup length 2 < ] [ 2drop f ] }
|
||||
{ [ dup second #if? ] [ drop find-boolean-intrinsic ] }
|
||||
[ 2drop f ]
|
||||
} cond ;
|
||||
|
||||
: do-if-intrinsic ( pair -- next )
|
||||
[ %if-intrinsic ] apply-template skip-next emit-if ;
|
||||
|
||||
: do-boolean-intrinsic ( pair -- next )
|
||||
[
|
||||
f alloc-vreg [ %boolean-intrinsic ] keep phantom-push
|
||||
] apply-template iterate-next ;
|
||||
|
||||
: do-intrinsic ( pair -- next )
|
||||
[ %intrinsic ] apply-template iterate-next ;
|
||||
|
||||
: setup-operand-classes ( #call -- )
|
||||
node-input-infos [ class>> ] map set-operand-classes ;
|
||||
|
||||
M: #call emit-node
|
||||
dup setup-operand-classes
|
||||
dup find-if-intrinsic [ do-if-intrinsic ] [
|
||||
dup find-boolean-intrinsic [ do-boolean-intrinsic ] [
|
||||
dup find-intrinsic [ do-intrinsic ] [
|
||||
word>> emit-call
|
||||
] ?if
|
||||
] ?if
|
||||
] ?if ;
|
||||
|
||||
! #call-recursive
|
||||
M: #call-recursive emit-node label>> id>> emit-call ;
|
||||
|
||||
! #push
|
||||
M: #push emit-node
|
||||
literal>> <constant> phantom-push iterate-next ;
|
||||
|
||||
! #shuffle
|
||||
M: #shuffle emit-node
|
||||
shuffle-effect phantom-shuffle iterate-next ;
|
||||
|
||||
M: #>r emit-node
|
||||
[ in-d>> length ] [ out-r>> empty? ] bi
|
||||
[ phantom-drop ] [ phantom->r ] if
|
||||
iterate-next ;
|
||||
|
||||
M: #r> emit-node
|
||||
[ in-r>> length ] [ out-d>> empty? ] bi
|
||||
[ phantom-rdrop ] [ phantom-r> ] if
|
||||
iterate-next ;
|
||||
|
||||
! #return
|
||||
M: #return emit-node
|
||||
drop finalize-phantoms %epilogue %return f ;
|
||||
|
||||
M: #return-recursive emit-node
|
||||
finalize-phantoms
|
||||
label>> id>> loops get key?
|
||||
[ %epilogue %return ] unless f ;
|
||||
|
||||
! #terminate
|
||||
M: #terminate emit-node drop end-basic-block f ;
|
||||
|
||||
! FFI
|
||||
M: #alien-invoke emit-node
|
||||
params>>
|
||||
[ alien-invoke-frame %frame-required ]
|
||||
[ %alien-invoke iterate-next ]
|
||||
bi ;
|
||||
|
||||
M: #alien-indirect emit-node
|
||||
params>>
|
||||
[ alien-invoke-frame %frame-required ]
|
||||
[ %alien-indirect iterate-next ]
|
||||
bi ;
|
||||
|
||||
M: #alien-callback emit-node
|
||||
params>> dup xt>> dup
|
||||
[ init-phantoms %alien-callback ] with-cfg-builder
|
||||
iterate-next ;
|
||||
|
||||
! No-op nodes
|
||||
M: #introduce emit-node drop iterate-next ;
|
||||
|
||||
M: #copy emit-node drop iterate-next ;
|
||||
|
||||
M: #enter-recursive emit-node drop iterate-next ;
|
||||
|
||||
M: #phi emit-node drop iterate-next ;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Final stage of compilation generates machine code from dataflow IR
|
|
@ -0,0 +1 @@
|
|||
compiler
|
|
@ -3,16 +3,19 @@
|
|||
USING: kernel accessors namespaces assocs sequences sets fry ;
|
||||
IN: compiler.cfg
|
||||
|
||||
! The id is a globally unique id used for fast hashcode* and
|
||||
! equal? on basic blocks. The number is assigned by
|
||||
! linearization.
|
||||
TUPLE: procedure entry word label ;
|
||||
|
||||
C: <procedure> procedure
|
||||
|
||||
! - "id" is a globally unique id used for hashcode*.
|
||||
! - "number" is assigned by linearization.
|
||||
TUPLE: basic-block < identity-tuple
|
||||
id
|
||||
number
|
||||
label
|
||||
instructions
|
||||
successors
|
||||
predecessors
|
||||
stack-frame ;
|
||||
predecessors ;
|
||||
|
||||
SYMBOL: next-block-id
|
||||
|
||||
|
@ -34,14 +37,11 @@ SYMBOL: visited-blocks
|
|||
|
||||
: (each-block) ( basic-block quot -- )
|
||||
'[
|
||||
,
|
||||
_
|
||||
[ call ]
|
||||
[ [ successors>> ] dip '[ , (each-block) ] each ]
|
||||
[ [ successors>> ] dip '[ _ (each-block) ] each ]
|
||||
2bi
|
||||
] visit-block ; inline
|
||||
|
||||
: each-block ( basic-block quot -- )
|
||||
H{ } clone visited-blocks [ (each-block) ] with-variable ; inline
|
||||
|
||||
: copy-at ( from to assoc -- )
|
||||
3dup nip at* [ -rot set-at drop ] [ 2drop 2drop ] if ; inline
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: namespaces sequences kernel compiler.tree ;
|
||||
IN: compiler.cfg.iterator
|
||||
|
||||
SYMBOL: node-stack
|
||||
|
||||
: >node ( cursor -- ) node-stack get push ;
|
||||
: node> ( -- cursor ) node-stack get pop ;
|
||||
: node@ ( -- cursor ) node-stack get peek ;
|
||||
: current-node ( -- node ) node@ first ;
|
||||
: iterate-next ( -- cursor ) node@ rest-slice ;
|
||||
: skip-next ( -- next ) node> rest-slice [ first ] [ >node ] bi ;
|
||||
|
||||
: iterate-nodes ( cursor quot: ( -- ) -- )
|
||||
over empty? [
|
||||
2drop
|
||||
] [
|
||||
[ swap >node call node> drop ] keep iterate-nodes
|
||||
] if ; inline recursive
|
||||
|
||||
: with-node-iterator ( quot -- )
|
||||
>r V{ } clone node-stack r> with-variable ; inline
|
||||
|
||||
DEFER: (tail-call?)
|
||||
|
||||
: tail-phi? ( cursor -- ? )
|
||||
[ first #phi? ] [ rest-slice (tail-call?) ] bi and ;
|
||||
|
||||
: (tail-call?) ( cursor -- ? )
|
||||
[ t ] [
|
||||
[
|
||||
first
|
||||
[ #return? ]
|
||||
[ #return-recursive? ]
|
||||
[ #terminate? ] tri or or
|
||||
] [ tail-phi? ] bi or
|
||||
] if-empty ;
|
||||
|
||||
: tail-call? ( -- ? )
|
||||
node-stack get [
|
||||
rest-slice
|
||||
[ t ] [
|
||||
[ (tail-call?) ]
|
||||
[ first #terminate? not ]
|
||||
bi and
|
||||
] if-empty
|
||||
] all? ;
|
|
@ -0,0 +1 @@
|
|||
Slava Pestov
|
|
@ -0,0 +1,389 @@
|
|||
! Copyright (C) 2006, 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: arrays assocs classes classes.private classes.algebra
|
||||
combinators hashtables kernel layouts math fry namespaces
|
||||
quotations sequences system vectors words effects alien
|
||||
byte-arrays accessors sets math.order compiler.instructions
|
||||
compiler.registers ;
|
||||
IN: compiler.cfg.stacks
|
||||
|
||||
! Converting stack operations into register operations, while
|
||||
! doing a bit of optimization along the way.
|
||||
|
||||
USE: qualified
|
||||
FROM: compiler.generator.registers => +input+ ;
|
||||
FROM: compiler.generator.registers => +output+ ;
|
||||
FROM: compiler.generator.registers => +scratch+ ;
|
||||
FROM: compiler.generator.registers => +clobber+ ;
|
||||
SYMBOL: known-tag
|
||||
|
||||
! Value protocol
|
||||
GENERIC: set-operand-class ( class obj -- )
|
||||
GENERIC: operand-class* ( operand -- class )
|
||||
GENERIC: move-spec ( obj -- spec )
|
||||
GENERIC: live-loc? ( actual current -- ? )
|
||||
GENERIC# (lazy-load) 1 ( value spec -- value )
|
||||
GENERIC# (eager-load) 1 ( value spec -- value )
|
||||
GENERIC: lazy-store ( dst src -- )
|
||||
GENERIC: minimal-ds-loc* ( min obj -- min )
|
||||
|
||||
! This will be a multimethod soon
|
||||
DEFER: %move
|
||||
|
||||
PRIVATE>
|
||||
|
||||
: operand-class ( operand -- class )
|
||||
operand-class* object or ;
|
||||
|
||||
! Default implementation
|
||||
M: value set-operand-class 2drop ;
|
||||
M: value operand-class* drop f ;
|
||||
M: value live-loc? 2drop f ;
|
||||
M: value minimal-ds-loc* drop ;
|
||||
M: value lazy-store 2drop ;
|
||||
|
||||
M: vreg move-spec reg-class>> move-spec ;
|
||||
|
||||
M: int-regs move-spec drop f ;
|
||||
M: int-regs operand-class* drop object ;
|
||||
|
||||
M: float-regs move-spec drop float ;
|
||||
M: float-regs operand-class* drop float ;
|
||||
|
||||
M: ds-loc minimal-ds-loc* n>> min ;
|
||||
M: ds-loc live-loc?
|
||||
over ds-loc? [ [ n>> ] bi@ = not ] [ 2drop t ] if ;
|
||||
|
||||
M: rs-loc live-loc?
|
||||
over rs-loc? [ [ n>> ] bi@ = not ] [ 2drop t ] if ;
|
||||
|
||||
M: loc operand-class* class>> ;
|
||||
M: loc set-operand-class (>>class) ;
|
||||
M: loc move-spec drop loc ;
|
||||
|
||||
M: f move-spec drop loc ;
|
||||
M: f operand-class* ;
|
||||
|
||||
M: cached set-operand-class vreg>> set-operand-class ;
|
||||
M: cached operand-class* vreg>> operand-class* ;
|
||||
M: cached move-spec drop cached ;
|
||||
M: cached live-loc? loc>> live-loc? ;
|
||||
M: cached (lazy-load) >r vreg>> r> (lazy-load) ;
|
||||
M: cached (eager-load) >r vreg>> r> (eager-load) ;
|
||||
M: cached lazy-store
|
||||
2dup loc>> live-loc?
|
||||
[ "live-locs" get at %move ] [ 2drop ] if ;
|
||||
M: cached minimal-ds-loc* loc>> minimal-ds-loc* ;
|
||||
|
||||
M: tagged set-operand-class (>>class) ;
|
||||
M: tagged operand-class* class>> ;
|
||||
M: tagged move-spec drop f ;
|
||||
|
||||
M: unboxed-alien operand-class* drop simple-alien ;
|
||||
M: unboxed-alien move-spec class ;
|
||||
|
||||
M: unboxed-byte-array operand-class* drop c-ptr ;
|
||||
M: unboxed-byte-array move-spec class ;
|
||||
|
||||
M: unboxed-f operand-class* drop \ f ;
|
||||
M: unboxed-f move-spec class ;
|
||||
|
||||
M: unboxed-c-ptr operand-class* drop c-ptr ;
|
||||
M: unboxed-c-ptr move-spec class ;
|
||||
|
||||
M: constant operand-class* value>> class ;
|
||||
M: constant move-spec class ;
|
||||
|
||||
! Moving values between locations and registers
|
||||
: %move-bug ( -- * ) "Bug in generator.registers" throw ;
|
||||
|
||||
: %unbox-c-ptr ( dst src -- )
|
||||
dup operand-class {
|
||||
{ [ dup \ f class<= ] [ drop %unbox-f ] }
|
||||
{ [ dup simple-alien class<= ] [ drop %unbox-alien ] }
|
||||
{ [ dup byte-array class<= ] [ drop %unbox-byte-array ] }
|
||||
[ drop %unbox-any-c-ptr ]
|
||||
} cond ; inline
|
||||
|
||||
: %move-via-temp ( dst src -- )
|
||||
#! For many transfers, such as loc to unboxed-alien, we
|
||||
#! don't have an intrinsic, so we transfer the source to
|
||||
#! temp then temp to the destination.
|
||||
int-regs next-vreg [ over %move operand-class ] keep
|
||||
tagged new
|
||||
swap >>vreg
|
||||
swap >>class
|
||||
%move ;
|
||||
|
||||
: %move ( dst src -- )
|
||||
2dup [ move-spec ] bi@ 2array {
|
||||
{ { f f } [ %copy ] }
|
||||
{ { unboxed-alien unboxed-alien } [ %copy ] }
|
||||
{ { unboxed-byte-array unboxed-byte-array } [ %copy ] }
|
||||
{ { unboxed-f unboxed-f } [ %copy ] }
|
||||
{ { unboxed-c-ptr unboxed-c-ptr } [ %copy ] }
|
||||
{ { float float } [ %copy-float ] }
|
||||
|
||||
{ { f unboxed-c-ptr } [ %move-bug ] }
|
||||
{ { f unboxed-byte-array } [ %move-bug ] }
|
||||
|
||||
{ { f constant } [ value>> swap %load-literal ] }
|
||||
|
||||
{ { f float } [ %box-float ] }
|
||||
{ { f unboxed-alien } [ %box-alien ] }
|
||||
{ { f loc } [ %peek ] }
|
||||
|
||||
{ { float f } [ %unbox-float ] }
|
||||
{ { unboxed-alien f } [ %unbox-alien ] }
|
||||
{ { unboxed-byte-array f } [ %unbox-byte-array ] }
|
||||
{ { unboxed-f f } [ %unbox-f ] }
|
||||
{ { unboxed-c-ptr f } [ %unbox-c-ptr ] }
|
||||
{ { loc f } [ swap %replace ] }
|
||||
|
||||
[ drop %move-via-temp ]
|
||||
} case ;
|
||||
|
||||
! A compile-time stack
|
||||
TUPLE: phantom-stack height stack ;
|
||||
|
||||
M: phantom-stack clone
|
||||
call-next-method [ clone ] change-stack ;
|
||||
|
||||
GENERIC: finalize-height ( stack -- )
|
||||
|
||||
: new-phantom-stack ( class -- stack )
|
||||
>r 0 V{ } clone r> boa ; inline
|
||||
|
||||
: (loc) ( m stack -- n )
|
||||
#! Utility for methods on <loc>
|
||||
height>> - ;
|
||||
|
||||
: (finalize-height) ( stack word -- )
|
||||
#! We consolidate multiple stack height changes until the
|
||||
#! last moment, and we emit the final height changing
|
||||
#! instruction here.
|
||||
'[ dup zero? [ drop ] [ _ execute ] if 0 ] change-height drop ; inline
|
||||
|
||||
GENERIC: <loc> ( n stack -- loc )
|
||||
|
||||
TUPLE: phantom-datastack < phantom-stack ;
|
||||
|
||||
: <phantom-datastack> ( -- stack )
|
||||
phantom-datastack new-phantom-stack ;
|
||||
|
||||
M: phantom-datastack <loc> (loc) <ds-loc> ;
|
||||
|
||||
M: phantom-datastack finalize-height
|
||||
\ %inc-d (finalize-height) ;
|
||||
|
||||
TUPLE: phantom-retainstack < phantom-stack ;
|
||||
|
||||
: <phantom-retainstack> ( -- stack )
|
||||
phantom-retainstack new-phantom-stack ;
|
||||
|
||||
M: phantom-retainstack <loc> (loc) <rs-loc> ;
|
||||
|
||||
M: phantom-retainstack finalize-height
|
||||
\ %inc-r (finalize-height) ;
|
||||
|
||||
: phantom-locs ( n phantom -- locs )
|
||||
#! A sequence of n ds-locs or rs-locs indexing the stack.
|
||||
>r <reversed> r> '[ _ <loc> ] map ;
|
||||
|
||||
: phantom-locs* ( phantom -- locs )
|
||||
[ stack>> length ] keep phantom-locs ;
|
||||
|
||||
: phantoms ( -- phantom phantom )
|
||||
phantom-datastack get phantom-retainstack get ;
|
||||
|
||||
: (each-loc) ( phantom quot -- )
|
||||
>r [ phantom-locs* ] [ stack>> ] bi r> 2each ; inline
|
||||
|
||||
: each-loc ( quot -- )
|
||||
phantoms 2array swap '[ _ (each-loc) ] each ; inline
|
||||
|
||||
: adjust-phantom ( n phantom -- )
|
||||
swap '[ _ + ] change-height drop ;
|
||||
|
||||
: cut-phantom ( n phantom -- seq )
|
||||
swap '[ _ cut* swap ] change-stack drop ;
|
||||
|
||||
: phantom-append ( seq stack -- )
|
||||
over length over adjust-phantom stack>> push-all ;
|
||||
|
||||
: add-locs ( n phantom -- )
|
||||
2dup stack>> length <= [
|
||||
2drop
|
||||
] [
|
||||
[ phantom-locs ] keep
|
||||
[ stack>> length head-slice* ] keep
|
||||
[ append >vector ] change-stack drop
|
||||
] if ;
|
||||
|
||||
: phantom-input ( n phantom -- seq )
|
||||
2dup add-locs
|
||||
2dup cut-phantom
|
||||
>r >r neg r> adjust-phantom r> ;
|
||||
|
||||
: each-phantom ( quot -- ) phantoms rot bi@ ; inline
|
||||
|
||||
: finalize-heights ( -- ) [ finalize-height ] each-phantom ;
|
||||
|
||||
: (live-locs) ( phantom -- seq )
|
||||
#! Discard locs which haven't moved
|
||||
[ phantom-locs* ] [ stack>> ] bi zip
|
||||
[ live-loc? ] assoc-filter
|
||||
values ;
|
||||
|
||||
: live-locs ( -- seq )
|
||||
[ (live-locs) ] each-phantom append prune ;
|
||||
|
||||
! Operands holding pointers to freshly-allocated objects which
|
||||
! are guaranteed to be in the nursery
|
||||
SYMBOL: fresh-objects
|
||||
|
||||
: reg-spec>class ( spec -- class )
|
||||
float eq? double-float-regs int-regs ? ;
|
||||
|
||||
: alloc-vreg ( spec -- reg )
|
||||
[ reg-spec>class next-vreg ] keep {
|
||||
{ f [ <tagged> ] }
|
||||
{ unboxed-alien [ <unboxed-alien> ] }
|
||||
{ unboxed-byte-array [ <unboxed-byte-array> ] }
|
||||
{ unboxed-f [ <unboxed-f> ] }
|
||||
{ unboxed-c-ptr [ <unboxed-c-ptr> ] }
|
||||
[ drop ]
|
||||
} case ;
|
||||
|
||||
: compatible? ( value spec -- ? )
|
||||
>r move-spec r> {
|
||||
{ [ 2dup = ] [ t ] }
|
||||
{ [ dup unboxed-c-ptr eq? ] [
|
||||
over { unboxed-byte-array unboxed-alien } member?
|
||||
] }
|
||||
[ f ]
|
||||
} cond 2nip ;
|
||||
|
||||
: alloc-vreg-for ( value spec -- vreg )
|
||||
alloc-vreg swap operand-class
|
||||
over tagged? [ >>class ] [ drop ] if ;
|
||||
|
||||
M: value (lazy-load)
|
||||
{
|
||||
{ [ dup quotation? ] [ drop ] }
|
||||
{ [ 2dup compatible? ] [ drop ] }
|
||||
[ (eager-load) ]
|
||||
} cond ;
|
||||
|
||||
M: value (eager-load) ( value spec -- vreg )
|
||||
[ alloc-vreg-for ] [ drop ] 2bi
|
||||
[ %move ] [ drop ] 2bi ;
|
||||
|
||||
M: loc lazy-store
|
||||
2dup live-loc? [ "live-locs" get at %move ] [ 2drop ] if ;
|
||||
|
||||
: finalize-locs ( -- )
|
||||
#! Perform any deferred stack shuffling.
|
||||
live-locs [ dup f (lazy-load) ] H{ } map>assoc
|
||||
dup assoc-empty? [ drop ] [
|
||||
"live-locs" set [ lazy-store ] each-loc
|
||||
] if ;
|
||||
|
||||
: finalize-vregs ( -- )
|
||||
#! Store any vregs to their final stack locations.
|
||||
[
|
||||
dup loc? over cached? or [ 2drop ] [ %move ] if
|
||||
] each-loc ;
|
||||
|
||||
: reset-phantom ( phantom -- )
|
||||
#! Kill register assignments but preserve constants and
|
||||
#! class information.
|
||||
dup phantom-locs*
|
||||
over stack>> [
|
||||
dup constant? [ nip ] [
|
||||
operand-class over set-operand-class
|
||||
] if
|
||||
] 2map
|
||||
over stack>> delete-all
|
||||
swap stack>> push-all ;
|
||||
|
||||
: reset-phantoms ( -- )
|
||||
[ reset-phantom ] each-phantom ;
|
||||
|
||||
: finalize-contents ( -- )
|
||||
finalize-locs finalize-vregs reset-phantoms ;
|
||||
|
||||
! Loading stacks to vregs
|
||||
: vreg-substitution ( value vreg -- pair )
|
||||
dupd <cached> 2array ;
|
||||
|
||||
: substitute-vreg? ( old new -- ? )
|
||||
#! We don't substitute locs for float or alien vregs,
|
||||
#! since in those cases the boxing overhead might kill us.
|
||||
vreg>> tagged? >r loc? r> and ;
|
||||
|
||||
: substitute-vregs ( values vregs -- )
|
||||
[ vreg-substitution ] 2map
|
||||
[ substitute-vreg? ] assoc-filter >hashtable
|
||||
'[ stack>> _ substitute-here ] each-phantom ;
|
||||
|
||||
: clear-phantoms ( -- )
|
||||
[ stack>> delete-all ] each-phantom ;
|
||||
|
||||
: set-operand-classes ( classes -- )
|
||||
phantom-datastack get
|
||||
over length over add-locs
|
||||
stack>> [ set-operand-class ] 2reverse-each ;
|
||||
|
||||
: finalize-phantoms ( -- )
|
||||
#! Commit all deferred stacking shuffling, and ensure the
|
||||
#! in-memory data and retain stacks are up to date with
|
||||
#! respect to the compiler's current picture.
|
||||
finalize-contents
|
||||
clear-phantoms
|
||||
finalize-heights
|
||||
fresh-objects get [ empty? [ %gc ] unless ] [ delete-all ] bi ;
|
||||
|
||||
: fresh-object ( obj -- ) fresh-objects get push ;
|
||||
|
||||
: fresh-object? ( obj -- ? ) fresh-objects get memq? ;
|
||||
|
||||
: init-phantoms ( -- )
|
||||
V{ } clone fresh-objects set
|
||||
<phantom-datastack> phantom-datastack set
|
||||
<phantom-retainstack> phantom-retainstack set ;
|
||||
|
||||
: copy-phantoms ( -- )
|
||||
fresh-objects [ clone ] change
|
||||
phantom-datastack [ clone ] change
|
||||
phantom-retainstack [ clone ] change ;
|
||||
|
||||
: operand-tag ( operand -- tag/f )
|
||||
operand-class dup [ class-tag ] when ;
|
||||
|
||||
UNION: immediate fixnum POSTPONE: f ;
|
||||
|
||||
: operand-immediate? ( operand -- ? )
|
||||
operand-class immediate class<= ;
|
||||
|
||||
: phantom-push ( obj -- )
|
||||
1 phantom-datastack get adjust-phantom
|
||||
phantom-datastack get stack>> push ;
|
||||
|
||||
: phantom-shuffle ( shuffle -- )
|
||||
[ in>> length phantom-datastack get phantom-input ] keep
|
||||
shuffle phantom-datastack get phantom-append ;
|
||||
|
||||
: phantom->r ( n -- )
|
||||
phantom-datastack get phantom-input
|
||||
phantom-retainstack get phantom-append ;
|
||||
|
||||
: phantom-r> ( n -- )
|
||||
phantom-retainstack get phantom-input
|
||||
phantom-datastack get phantom-append ;
|
||||
|
||||
: phantom-drop ( n -- )
|
||||
phantom-datastack get phantom-input drop ;
|
||||
|
||||
: phantom-rdrop ( n -- )
|
||||
phantom-retainstack get phantom-input drop ;
|
|
@ -0,0 +1,103 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: assocs accessors sequences kernel fry namespaces
|
||||
quotations combinators classes.algebra compiler.instructions
|
||||
compiler.registers compiler.cfg.stacks ;
|
||||
IN: compiler.cfg.templates
|
||||
|
||||
USE: qualified
|
||||
FROM: compiler.generator.registers => +input+ ;
|
||||
FROM: compiler.generator.registers => +output+ ;
|
||||
FROM: compiler.generator.registers => +scratch+ ;
|
||||
FROM: compiler.generator.registers => +clobber+ ;
|
||||
|
||||
: template-input +input+ swap at ; inline
|
||||
: template-output +output+ swap at ; inline
|
||||
: template-scratch +scratch+ swap at ; inline
|
||||
: template-clobber +clobber+ swap at ; inline
|
||||
|
||||
: phantom&spec ( phantom specs -- phantom' specs' )
|
||||
>r stack>> r>
|
||||
[ length f pad-left ] keep
|
||||
[ <reversed> ] bi@ ; inline
|
||||
|
||||
: phantom&spec-agree? ( phantom spec quot -- ? )
|
||||
>r phantom&spec r> 2all? ; inline
|
||||
|
||||
: live-vregs ( -- seq )
|
||||
[ stack>> [ >vreg ] map sift ] each-phantom append ;
|
||||
|
||||
: clobbered ( template -- seq )
|
||||
[ template-output ] [ template-clobber ] bi append ;
|
||||
|
||||
: clobbered? ( value name -- ? )
|
||||
\ clobbered get member? [
|
||||
>vreg \ live-vregs get member?
|
||||
] [ drop f ] if ;
|
||||
|
||||
: lazy-load ( specs -- seq )
|
||||
[ length phantom-datastack get phantom-input ] keep
|
||||
[ drop ] [
|
||||
[
|
||||
2dup second clobbered?
|
||||
[ first (eager-load) ] [ first (lazy-load) ] if
|
||||
] 2map
|
||||
] 2bi
|
||||
[ substitute-vregs ] keep ;
|
||||
|
||||
: load-inputs ( template -- assoc )
|
||||
[
|
||||
live-vregs \ live-vregs set
|
||||
dup clobbered \ clobbered set
|
||||
template-input [ values ] [ lazy-load ] bi zip
|
||||
] with-scope ;
|
||||
|
||||
: alloc-scratch ( template -- assoc )
|
||||
template-scratch [ swap alloc-vreg ] assoc-map ;
|
||||
|
||||
: do-template-inputs ( template -- inputs )
|
||||
#! Load input values into registers and allocates scratch
|
||||
#! registers.
|
||||
[ load-inputs ] [ alloc-scratch ] bi assoc-union ;
|
||||
|
||||
: do-template-outputs ( template inputs -- )
|
||||
[ template-output ] dip '[ _ at ] map
|
||||
phantom-datastack get phantom-append ;
|
||||
|
||||
: apply-template ( pair quot -- vregs )
|
||||
[
|
||||
first2 dup do-template-inputs
|
||||
[ do-template-outputs ] keep
|
||||
] dip call ; inline
|
||||
|
||||
: value-matches? ( value spec -- ? )
|
||||
#! If the spec is a quotation and the value is a literal
|
||||
#! fixnum, see if the quotation yields true when applied
|
||||
#! to the fixnum. Otherwise, the values don't match. If the
|
||||
#! spec is not a quotation, its a reg-class, in which case
|
||||
#! the value is always good.
|
||||
dup quotation? [
|
||||
over constant?
|
||||
[ >r value>> r> 2drop f ] [ 2drop f ] if
|
||||
] [
|
||||
2drop t
|
||||
] if ;
|
||||
|
||||
: class-matches? ( actual expected -- ? )
|
||||
{
|
||||
{ f [ drop t ] }
|
||||
{ known-tag [ dup [ class-tag >boolean ] when ] }
|
||||
[ class<= ]
|
||||
} case ;
|
||||
|
||||
: spec-matches? ( value spec -- ? )
|
||||
2dup first value-matches?
|
||||
>r >r operand-class 2 r> ?nth class-matches? r> and ;
|
||||
|
||||
: template-matches? ( template -- ? )
|
||||
template-input phantom-datastack get swap
|
||||
[ spec-matches? ] phantom&spec-agree? ;
|
||||
|
||||
: find-template ( templates -- pair/f )
|
||||
#! Pair has shape { quot assoc }
|
||||
[ second template-matches? ] find nip ;
|
|
@ -0,0 +1 @@
|
|||
Slava Pestov
|
|
@ -0,0 +1,154 @@
|
|||
! Copyright (C) 2007, 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: arrays byte-arrays generic assocs hashtables io.binary
|
||||
kernel kernel.private math namespaces make sequences words
|
||||
quotations strings alien.accessors alien.strings layouts system
|
||||
combinators math.bitwise words.private cpu.architecture
|
||||
math.order accessors growable ;
|
||||
IN: compiler.cfg.fixup
|
||||
|
||||
: no-stack-frame -1 ; inline
|
||||
|
||||
TUPLE: frame-required n ;
|
||||
|
||||
: frame-required ( n -- ) \ frame-required boa , ;
|
||||
|
||||
: stack-frame-size ( code -- n )
|
||||
no-stack-frame [
|
||||
dup frame-required? [ n>> max ] [ drop ] if
|
||||
] reduce ;
|
||||
|
||||
GENERIC: fixup* ( frame-size obj -- frame-size )
|
||||
|
||||
: code-format 22 getenv ;
|
||||
|
||||
: compiled-offset ( -- n ) building get length code-format * ;
|
||||
|
||||
TUPLE: label offset ;
|
||||
|
||||
: <label> ( -- label ) label new ;
|
||||
|
||||
M: label fixup*
|
||||
compiled-offset >>offset drop ;
|
||||
|
||||
: define-label ( name -- ) <label> swap set ;
|
||||
|
||||
: resolve-label ( label/name -- ) dup label? [ get ] unless , ;
|
||||
|
||||
: if-stack-frame ( frame-size quot -- )
|
||||
swap dup no-stack-frame =
|
||||
[ 2drop ] [ stack-frame swap call ] if ; inline
|
||||
|
||||
M: word fixup*
|
||||
{
|
||||
{ \ %prologue-later [ dup [ %prologue ] if-stack-frame ] }
|
||||
{ \ %epilogue-later [ dup [ %epilogue ] if-stack-frame ] }
|
||||
} case ;
|
||||
|
||||
SYMBOL: relocation-table
|
||||
SYMBOL: label-table
|
||||
|
||||
! Relocation classes
|
||||
: rc-absolute-cell 0 ;
|
||||
: rc-absolute 1 ;
|
||||
: rc-relative 2 ;
|
||||
: rc-absolute-ppc-2/2 3 ;
|
||||
: rc-relative-ppc-2 4 ;
|
||||
: rc-relative-ppc-3 5 ;
|
||||
: rc-relative-arm-3 6 ;
|
||||
: rc-indirect-arm 7 ;
|
||||
: rc-indirect-arm-pc 8 ;
|
||||
|
||||
: rc-absolute? ( n -- ? )
|
||||
dup rc-absolute-cell =
|
||||
over rc-absolute =
|
||||
rot rc-absolute-ppc-2/2 = or or ;
|
||||
|
||||
! Relocation types
|
||||
: rt-primitive 0 ;
|
||||
: rt-dlsym 1 ;
|
||||
: rt-literal 2 ;
|
||||
: rt-dispatch 3 ;
|
||||
: rt-xt 4 ;
|
||||
: rt-here 5 ;
|
||||
: rt-label 6 ;
|
||||
: rt-immediate 7 ;
|
||||
|
||||
TUPLE: label-fixup label class ;
|
||||
|
||||
: label-fixup ( label class -- ) \ label-fixup boa , ;
|
||||
|
||||
M: label-fixup fixup*
|
||||
dup class>> rc-absolute?
|
||||
[ "Absolute labels not supported" throw ] when
|
||||
dup label>> swap class>> compiled-offset 4 - rot
|
||||
3array label-table get push ;
|
||||
|
||||
TUPLE: rel-fixup arg class type ;
|
||||
|
||||
: rel-fixup ( arg class type -- ) \ rel-fixup boa , ;
|
||||
|
||||
: push-4 ( value vector -- )
|
||||
[ length ] [ B{ 0 0 0 0 } swap push-all ] [ underlying>> ] tri
|
||||
swap set-alien-unsigned-4 ;
|
||||
|
||||
M: rel-fixup fixup*
|
||||
[ [ arg>> ] [ class>> ] [ type>> ] tri { 0 8 16 } bitfield ]
|
||||
[ class>> rc-absolute-cell = cell 4 ? compiled-offset swap - ] bi
|
||||
[ relocation-table get push-4 ] bi@ ;
|
||||
|
||||
M: frame-required fixup* drop ;
|
||||
|
||||
M: integer fixup* , ;
|
||||
|
||||
: adjoin* ( obj table -- n )
|
||||
2dup swap [ eq? ] curry find drop
|
||||
[ 2nip ] [ dup length >r push r> ] if* ;
|
||||
|
||||
SYMBOL: literal-table
|
||||
|
||||
: add-literal ( obj -- n ) literal-table get adjoin* ;
|
||||
|
||||
: add-dlsym-literals ( symbol dll -- )
|
||||
>r string>symbol r> 2array literal-table get push-all ;
|
||||
|
||||
: rel-dlsym ( name dll class -- )
|
||||
>r literal-table get length >r
|
||||
add-dlsym-literals
|
||||
r> r> rt-dlsym rel-fixup ;
|
||||
|
||||
: rel-word ( word class -- )
|
||||
>r add-literal r> rt-xt rel-fixup ;
|
||||
|
||||
: rel-primitive ( word class -- )
|
||||
>r def>> first r> rt-primitive rel-fixup ;
|
||||
|
||||
: rel-literal ( literal class -- )
|
||||
>r add-literal r> rt-literal rel-fixup ;
|
||||
|
||||
: rel-this ( class -- )
|
||||
0 swap rt-label rel-fixup ;
|
||||
|
||||
: rel-here ( class -- )
|
||||
0 swap rt-here rel-fixup ;
|
||||
|
||||
: init-fixup ( -- )
|
||||
BV{ } clone relocation-table set
|
||||
V{ } clone label-table set ;
|
||||
|
||||
: resolve-labels ( labels -- labels' )
|
||||
[
|
||||
first3 offset>>
|
||||
[ "Unresolved label" throw ] unless*
|
||||
3array
|
||||
] map concat ;
|
||||
|
||||
: fixup ( code -- literals relocation labels code )
|
||||
[
|
||||
init-fixup
|
||||
dup stack-frame-size swap [ fixup* ] each drop
|
||||
|
||||
literal-table get >array
|
||||
relocation-table get >byte-array
|
||||
label-table get resolve-labels
|
||||
] { } make ;
|
|
@ -0,0 +1 @@
|
|||
Support for generation of relocatable code
|
|
@ -0,0 +1,72 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: assocs accessors arrays kernel sequences
|
||||
compiler.instructions.syntax ;
|
||||
IN: compiler.instructions
|
||||
|
||||
! Virtual CPU instructions, used by CFG and machine IRs
|
||||
|
||||
INSN: %cond-branch vreg ;
|
||||
INSN: %unary dst src ;
|
||||
|
||||
! Stack operations
|
||||
INSN: %peek vreg loc ;
|
||||
INSN: %replace vreg loc ;
|
||||
INSN: %inc-d n ;
|
||||
INSN: %inc-r n ;
|
||||
INSN: %load-literal obj vreg ;
|
||||
|
||||
! Calling convention
|
||||
INSN: %prologue ;
|
||||
INSN: %epilogue ;
|
||||
INSN: %frame-required n ;
|
||||
INSN: %return ;
|
||||
|
||||
! Subroutine calls
|
||||
INSN: %call word ;
|
||||
INSN: %jump word ;
|
||||
INSN: %intrinsic quot vregs ;
|
||||
|
||||
! Jump tables
|
||||
INSN: %dispatch-label label ;
|
||||
INSN: %dispatch ;
|
||||
|
||||
! Unconditional branch to successor (CFG only)
|
||||
INSN: %branch ;
|
||||
|
||||
! Conditional branches (CFG only)
|
||||
INSN: %branch-f < %cond-branch ;
|
||||
INSN: %branch-t < %cond-branch ;
|
||||
INSN: %if-intrinsic quot vregs ;
|
||||
INSN: %boolean-intrinsic quot vregs out ;
|
||||
|
||||
! Boxing and unboxing
|
||||
INSN: %copy < %unary ;
|
||||
INSN: %copy-float < %unary ;
|
||||
INSN: %unbox-float < %unary ;
|
||||
INSN: %unbox-f < %unary ;
|
||||
INSN: %unbox-alien < %unary ;
|
||||
INSN: %unbox-byte-array < %unary ;
|
||||
INSN: %unbox-any-c-ptr < %unary ;
|
||||
INSN: %box-float < %unary ;
|
||||
INSN: %box-alien < %unary ;
|
||||
|
||||
INSN: %gc ;
|
||||
|
||||
! FFI
|
||||
INSN: %alien-invoke params ;
|
||||
INSN: %alien-indirect params ;
|
||||
INSN: %alien-callback params ;
|
||||
|
||||
GENERIC: uses-vregs ( insn -- seq )
|
||||
|
||||
M: insn uses-vregs drop f ;
|
||||
M: %peek uses-vregs vreg>> 1array ;
|
||||
M: %replace uses-vregs vreg>> 1array ;
|
||||
M: %load-literal uses-vregs vreg>> 1array ;
|
||||
M: %cond-branch uses-vregs vreg>> 1array ;
|
||||
M: %unary uses-vregs [ dst>> ] [ src>> ] bi 2array ;
|
||||
M: %intrinsic uses-vregs vregs>> values ;
|
||||
M: %if-intrinsic uses-vregs vregs>> values ;
|
||||
M: %boolean-intrinsic uses-vregs
|
||||
[ vregs>> values ] [ out>> ] bi suffix ;
|
|
@ -0,0 +1,15 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: classes.tuple classes.tuple.parser kernel words
|
||||
make parser ;
|
||||
IN: compiler.instructions.syntax
|
||||
|
||||
TUPLE: insn ;
|
||||
|
||||
: INSN:
|
||||
parse-tuple-definition
|
||||
[ dup tuple eq? [ drop insn ] when ] dip
|
||||
[ define-tuple-class ]
|
||||
[ 2drop save-location ]
|
||||
[ 2drop dup [ boa , ] curry define-inline ]
|
||||
3tri ; parsing
|
|
@ -0,0 +1,50 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: kernel math accessors sequences namespaces
|
||||
compiler.cfg compiler.vops compiler.lvops ;
|
||||
IN: compiler.machine.builder
|
||||
|
||||
SYMBOL: block-counter
|
||||
|
||||
: number-basic-block ( basic-block -- )
|
||||
#! Make this fancy later.
|
||||
dup number>> [ drop ] [
|
||||
block-counter [ dup 1+ ] change >>number
|
||||
[ , ] [
|
||||
successors>> <reversed>
|
||||
[ number-basic-block ] each
|
||||
] bi
|
||||
] if ;
|
||||
|
||||
: flatten-basic-blocks ( procedure -- blocks )
|
||||
[
|
||||
0 block-counter
|
||||
[ number-basic-block ]
|
||||
with-variable
|
||||
] { } make ;
|
||||
|
||||
GENERIC: linearize-instruction ( basic-block insn -- )
|
||||
|
||||
M: object linearize-instruction
|
||||
, drop ;
|
||||
|
||||
M: %b linearize-instruction
|
||||
drop successors>> first number>> _b emit ;
|
||||
|
||||
: conditional-branch ( basic-block insn class -- )
|
||||
[ successors>> ] 2dip
|
||||
[ [ first number>> ] [ [ in>> ] [ code>> ] bi ] [ ] tri* emit ]
|
||||
[ 2drop second number>> _b emit ]
|
||||
3bi ; inline
|
||||
|
||||
M: %bi linearize-instruction _bi conditional-branch ;
|
||||
M: %bf linearize-instruction _bf conditional-branch ;
|
||||
|
||||
: build-mr ( procedure -- insns )
|
||||
[
|
||||
flatten-basic-blocks [
|
||||
[ number>> _label emit ]
|
||||
[ dup instructions>> [ linearize-instruction ] with each ]
|
||||
bi
|
||||
] each
|
||||
] { } make ;
|
|
@ -1,14 +1,17 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: kernel math accessors sequences namespaces
|
||||
compiler.cfg compiler.vops compiler.lvops ;
|
||||
USING: kernel math accessors sequences namespaces make
|
||||
compiler.cfg compiler.instructions compiler.machine ;
|
||||
IN: compiler.machine.builder
|
||||
|
||||
! Convert CFG IR to machine IR.
|
||||
|
||||
SYMBOL: block-counter
|
||||
|
||||
: number-basic-block ( basic-block -- )
|
||||
#! Make this fancy later.
|
||||
dup number>> [ drop ] [
|
||||
<label> >>label
|
||||
block-counter [ dup 1+ ] change >>number
|
||||
[ , ] [
|
||||
successors>> <reversed>
|
||||
|
@ -23,28 +26,47 @@ SYMBOL: block-counter
|
|||
with-variable
|
||||
] { } make ;
|
||||
|
||||
GENERIC: linearize-instruction ( basic-block insn -- )
|
||||
GENERIC: linearize* ( basic-block insn -- )
|
||||
|
||||
M: object linearize-instruction
|
||||
, drop ;
|
||||
M: object linearize* , drop ;
|
||||
|
||||
M: %b linearize-instruction
|
||||
drop successors>> first number>> _b emit ;
|
||||
M: %branch linearize*
|
||||
drop successors>> first label>> _branch ;
|
||||
|
||||
: conditional-branch ( basic-block insn class -- )
|
||||
[ successors>> ] 2dip
|
||||
[ [ first number>> ] [ [ in>> ] [ code>> ] bi ] [ ] tri* emit ]
|
||||
[ 2drop second number>> _b emit ]
|
||||
3bi ; inline
|
||||
: conditional ( basic-block -- label1 label2 )
|
||||
successors>> first2 [ label>> ] bi@ swap ; inline
|
||||
|
||||
M: %bi linearize-instruction _bi conditional-branch ;
|
||||
M: %bf linearize-instruction _bf conditional-branch ;
|
||||
: boolean-conditional ( basic-block insn -- label1 vreg label2 )
|
||||
[ conditional ] [ vreg>> ] bi* swap ; inline
|
||||
|
||||
: build-mr ( procedure -- insns )
|
||||
M: %branch-f linearize*
|
||||
boolean-conditional _branch-f _branch ;
|
||||
|
||||
M: %branch-t linearize*
|
||||
boolean-conditional _branch-t _branch ;
|
||||
|
||||
M: %if-intrinsic linearize*
|
||||
[ conditional ] [ [ quot>> ] [ vregs>> ] bi ] bi*
|
||||
_if-intrinsic _branch ;
|
||||
|
||||
M: %boolean-intrinsic linearize*
|
||||
[
|
||||
flatten-basic-blocks [
|
||||
[ number>> _label emit ]
|
||||
[ dup instructions>> [ linearize-instruction ] with each ]
|
||||
"false" define-label
|
||||
"end" define-label
|
||||
"false" get over [ quot>> ] [ vregs>> ] bi _if-intrinsic
|
||||
t over out>> %load-literal
|
||||
"end" get _branch
|
||||
"false" resolve-label
|
||||
f over out>> %load-literal
|
||||
"end" resolve-label
|
||||
] with-scope
|
||||
2drop ;
|
||||
|
||||
: build-machine ( procedure -- insns )
|
||||
[
|
||||
entry>> flatten-basic-blocks [
|
||||
[ label>> _label ]
|
||||
[ dup instructions>> [ linearize* ] with each ]
|
||||
bi
|
||||
] each
|
||||
] { } make ;
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: namespaces sequences math math.order kernel assocs
|
||||
accessors vectors fry
|
||||
compiler.machine.linear-scan.live-intervals
|
||||
compiler.backend ;
|
||||
IN: compiler.machine.linear-scan.allocation
|
||||
|
||||
! Mapping from vregs to machine registers
|
||||
SYMBOL: register-allocation
|
||||
|
||||
! Mapping from vregs to spill locations
|
||||
SYMBOL: spill-locations
|
||||
|
||||
! Vector of active live intervals, in order of increasing end point
|
||||
SYMBOL: active-intervals
|
||||
|
||||
: add-active ( live-interval -- )
|
||||
active-intervals get push ;
|
||||
|
||||
: delete-active ( live-interval -- )
|
||||
active-intervals get delete ;
|
||||
|
||||
! Mapping from register classes to sequences of machine registers
|
||||
SYMBOL: free-registers
|
||||
|
||||
! Counter of spill locations
|
||||
SYMBOL: spill-counter
|
||||
|
||||
: next-spill-location ( -- n )
|
||||
spill-counter [ dup 1+ ] change ;
|
||||
|
||||
: assign-spill ( live-interval -- )
|
||||
next-spill-location swap vreg>> spill-locations get set-at ;
|
||||
|
||||
: free-registers-for ( vreg -- seq )
|
||||
reg-class>> free-registers get at ;
|
||||
|
||||
: free-register ( vreg -- )
|
||||
#! Free machine register currently assigned to vreg.
|
||||
[ register-allocation get at ] [ free-registers-for ] bi push ;
|
||||
|
||||
: expire-old-intervals ( live-interval -- )
|
||||
active-intervals get
|
||||
swap '[ end>> _ start>> < ] partition
|
||||
active-intervals set
|
||||
[ vreg>> free-register ] each ;
|
||||
|
||||
: interval-to-spill ( -- live-interval )
|
||||
#! We spill the interval with the longest remaining range.
|
||||
active-intervals get unclip-slice [
|
||||
[ [ end>> ] bi@ > ] most
|
||||
] reduce ;
|
||||
|
||||
: reuse-register ( live-interval to-spill -- )
|
||||
vreg>> swap vreg>>
|
||||
register-allocation get
|
||||
tuck [ at ] [ set-at ] 2bi* ;
|
||||
|
||||
: spill-at-interval ( live-interval -- )
|
||||
interval-to-spill
|
||||
2dup [ end>> ] bi@ > [
|
||||
[ reuse-register ]
|
||||
[ nip assign-spill ]
|
||||
[ [ add-active ] [ delete-active ] bi* ]
|
||||
2tri
|
||||
] [ drop assign-spill ] if ;
|
||||
|
||||
: init-allocator ( -- )
|
||||
H{ } clone register-allocation set
|
||||
H{ } clone spill-locations set
|
||||
V{ } clone active-intervals set
|
||||
machine-registers [ >vector ] assoc-map free-registers set
|
||||
0 spill-counter set ;
|
||||
|
||||
: assign-register ( live-interval register -- )
|
||||
swap vreg>> register-allocation get set-at ;
|
||||
|
||||
: allocate-register ( live-interval -- )
|
||||
dup vreg>> free-registers-for [
|
||||
spill-at-interval
|
||||
] [
|
||||
[ pop assign-register ]
|
||||
[ drop add-active ]
|
||||
2bi
|
||||
] if-empty ;
|
||||
|
||||
: allocate-registers ( live-intervals -- )
|
||||
init-allocator
|
||||
[ [ expire-old-intervals ] [ allocate-register ] bi ] each ;
|
|
@ -0,0 +1,12 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
IN: compiler.machine.linear-scan
|
||||
|
||||
! See http://www.cs.ucla.edu/~palsberg/course/cs132/linearscan.pdf
|
||||
|
||||
! ! ! Step 1: compute live intervals
|
||||
|
||||
|
||||
! ! ! Step 2: allocate registers
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: namespaces kernel assocs accessors sequences math
|
||||
math.order sorting compiler.instructions compiler.registers ;
|
||||
IN: compiler.machine.linear-scan.live-intervals
|
||||
|
||||
TUPLE: live-interval < identity-tuple vreg start end ;
|
||||
|
||||
M: live-interval hashcode* nip [ start>> ] [ end>> 1000 * ] bi + ;
|
||||
|
||||
! Mapping from vreg to live-interval
|
||||
SYMBOL: live-intervals
|
||||
|
||||
: update-live-interval ( n vreg -- )
|
||||
>vreg
|
||||
live-intervals get
|
||||
[ over f live-interval boa ] cache
|
||||
(>>end) ;
|
||||
|
||||
: compute-live-intervals* ( n insn -- )
|
||||
uses-vregs [ update-live-interval ] with each ;
|
||||
|
||||
: sort-live-intervals ( assoc -- seq' )
|
||||
#! Sort by increasing start location.
|
||||
values [ [ start>> ] compare ] sort ;
|
||||
|
||||
: compute-live-intervals ( instructions -- live-intervals )
|
||||
H{ } clone [
|
||||
live-intervals [
|
||||
[ swap compute-live-intervals* ] each-index
|
||||
] with-variable
|
||||
] keep sort-live-intervals ;
|
|
@ -0,0 +1,27 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: assocs accessors arrays namespaces kernel math
|
||||
sequences compiler.instructions compiler.instructions.syntax ;
|
||||
IN: compiler.machine
|
||||
|
||||
! Machine representation. Flat list of instructions, all
|
||||
! registers allocated, with labels and jumps.
|
||||
|
||||
INSN: _prologue n ;
|
||||
INSN: _epilogue n ;
|
||||
|
||||
INSN: _label label ;
|
||||
|
||||
: <label> ( -- label ) \ <label> counter ;
|
||||
: define-label ( name -- ) <label> swap set ;
|
||||
: resolve-label ( label/name -- ) dup integer? [ get ] unless _label ;
|
||||
|
||||
TUPLE: _cond-branch vreg label ;
|
||||
|
||||
INSN: _branch label ;
|
||||
INSN: _branch-f < _cond-branch ;
|
||||
INSN: _branch-t < _cond-branch ;
|
||||
INSN: _if-intrinsic label quot vregs ;
|
||||
|
||||
M: _cond-branch uses-vregs vreg>> 1array ;
|
||||
M: _if-intrinsic uses-vregs vregs>> values ;
|
|
@ -0,0 +1,4 @@
|
|||
IN: compiler.machine.optimizer.tests
|
||||
USING: compiler.machine.optimizer tools.test ;
|
||||
|
||||
\ optimize-machine must-infer
|
|
@ -0,0 +1,39 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: kernel accessors math namespaces make sequences
|
||||
sequences.next
|
||||
compiler.instructions
|
||||
compiler.instructions.syntax
|
||||
compiler.machine ;
|
||||
IN: compiler.machine.optimizer
|
||||
|
||||
: frame-required ( insns -- n/f )
|
||||
[ %frame-required? ] filter
|
||||
[ f ] [ [ n>> ] map supremum ] if-empty ;
|
||||
|
||||
GENERIC: optimize* ( next insn -- )
|
||||
|
||||
: useless-branch? ( next insn -- ? )
|
||||
over _label? [ [ label>> ] bi@ = ] [ 2drop f ] if ;
|
||||
|
||||
M: _branch optimize*
|
||||
#! Remove unconditional branches to labels immediately
|
||||
#! following.
|
||||
tuck useless-branch? [ drop ] [ , ] if ;
|
||||
|
||||
M: %prologue optimize*
|
||||
2drop \ frame-required get [ _prologue ] when* ;
|
||||
|
||||
M: %epilogue optimize*
|
||||
2drop \ frame-required get [ _epilogue ] when* ;
|
||||
|
||||
M: %frame-required optimize* 2drop ;
|
||||
|
||||
M: insn optimize* nip , ;
|
||||
|
||||
: optimize-machine ( insns -- insns )
|
||||
[
|
||||
[ frame-required \ frame-required set ]
|
||||
[ [ optimize* ] each-next ]
|
||||
bi
|
||||
] { } make ;
|
|
@ -0,0 +1,90 @@
|
|||
! Copyright (C) 2008 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: accessors namespaces math kernel ;
|
||||
IN: compiler.registers
|
||||
|
||||
! Virtual CPU registers, used by CFG and machine IRs
|
||||
|
||||
MIXIN: value
|
||||
|
||||
GENERIC: >vreg ( obj -- vreg )
|
||||
|
||||
M: value >vreg drop f ;
|
||||
|
||||
! Register classes
|
||||
SINGLETON: int-regs
|
||||
SINGLETON: single-float-regs
|
||||
SINGLETON: double-float-regs
|
||||
UNION: float-regs single-float-regs double-float-regs ;
|
||||
UNION: reg-class int-regs float-regs ;
|
||||
|
||||
! Virtual registers
|
||||
TUPLE: vreg reg-class n ;
|
||||
SYMBOL: vreg-counter
|
||||
: next-vreg ( reg-class -- vreg ) \ vreg-counter counter vreg boa ;
|
||||
|
||||
M: vreg >vreg ;
|
||||
|
||||
INSTANCE: vreg value
|
||||
|
||||
! Stack locations
|
||||
TUPLE: loc n class ;
|
||||
|
||||
! A data stack location.
|
||||
TUPLE: ds-loc < loc ;
|
||||
: <ds-loc> ( n -- loc ) f ds-loc boa ;
|
||||
|
||||
TUPLE: rs-loc < loc ;
|
||||
: <rs-loc> ( n -- loc ) f rs-loc boa ;
|
||||
|
||||
INSTANCE: loc value
|
||||
|
||||
! A stack location which has been loaded into a register. To
|
||||
! read the location, we just read the register, but when time
|
||||
! comes to save it back to the stack, we know the register just
|
||||
! contains a stack value so we don't have to redundantly write
|
||||
! it back.
|
||||
TUPLE: cached loc vreg ;
|
||||
C: <cached> cached
|
||||
|
||||
M: cached >vreg vreg>> >vreg ;
|
||||
|
||||
INSTANCE: cached value
|
||||
|
||||
! A tagged pointer
|
||||
TUPLE: tagged vreg class ;
|
||||
: <tagged> ( vreg -- tagged ) f tagged boa ;
|
||||
|
||||
M: tagged >vreg vreg>> ;
|
||||
|
||||
INSTANCE: tagged value
|
||||
|
||||
! Unboxed value
|
||||
TUPLE: unboxed vreg ;
|
||||
C: <unboxed> unboxed
|
||||
|
||||
M: unboxed >vreg vreg>> ;
|
||||
|
||||
INSTANCE: unboxed value
|
||||
|
||||
! Unboxed alien pointer
|
||||
TUPLE: unboxed-alien < unboxed ;
|
||||
C: <unboxed-alien> unboxed-alien
|
||||
|
||||
! Untagged byte array pointer
|
||||
TUPLE: unboxed-byte-array < unboxed ;
|
||||
C: <unboxed-byte-array> unboxed-byte-array
|
||||
|
||||
! A register set to f
|
||||
TUPLE: unboxed-f < unboxed ;
|
||||
C: <unboxed-f> unboxed-f
|
||||
|
||||
! An alien, byte array or f
|
||||
TUPLE: unboxed-c-ptr < unboxed ;
|
||||
C: <unboxed-c-ptr> unboxed-c-ptr
|
||||
|
||||
! A constant value
|
||||
TUPLE: constant value ;
|
||||
C: <constant> constant
|
||||
|
||||
INSTANCE: constant value
|
Loading…
Reference in New Issue