[ [ make-local ] map ] H{ } make-assoc
(parse-lambda) <lambda> ?rewrite-closures first ;
-: method-effect ( quadruple -- effect )
- [ third ] [ second void? { } { "x" } ? ] bi <effect> ;
-
-: check-method ( quadruple -- )
- [ fourth infer ] [ method-effect ] bi
- 2dup effect<= [ 2drop ] [ effect-error ] if ;
-
SYNTAX: METHOD:
scan-c-type
parse-selector
parse-method-body [ swap ] 2dip 4array
- dup check-method
suffix! ;
M: ##binary-float-function compute-stack-frame* drop vm-frame-required ;
M: ##call compute-stack-frame* drop frame-required ;
-M: ##alien-callback compute-stack-frame* drop frame-required ;
M: ##spill compute-stack-frame* drop frame-required ;
M: ##reload compute-stack-frame* drop frame-required ;
: needs-frame-pointer ( -- )
cfg get t >>frame-pointer? drop ;
+: emit-callback-body ( nodes -- )
+ [ last #return? t assert= ] [ but-last emit-nodes ] bi ;
+
M: #alien-callback emit-node
- params>> dup xt>> dup
+ dup params>> xt>> dup
[
needs-frame-pointer
begin-word
{
- [ callee-parameters ##callback-inputs ]
- [ box-parameters ]
- [
- [
- make-kill-block
- quot>> ##alien-callback
- ] emit-trivial-block
- ]
- [ callee-return ##callback-outputs ]
- [ callback-stack-cleanup ]
+ [ params>> callee-parameters ##callback-inputs ]
+ [ params>> box-parameters ]
+ [ child>> emit-callback-body ]
+ [ params>> callee-return ##callback-outputs ]
+ [ params>> callback-stack-cleanup ]
} cleave
end-word
M: ##write-barrier-imm live-insn? src>> live-vreg? ;
+: filter-alien-outputs ( triples -- triples' )
+ [ first live-vreg? ] filter ;
+
+M: alien-call-insn live-insn?
+ [ filter-alien-outputs ] change-reg-outputs
+ drop t ;
+
+M: ##callback-inputs live-insn?
+ [ filter-alien-outputs ] change-reg-outputs
+ [ filter-alien-outputs ] change-stack-outputs
+ drop t ;
+
M: flushable-insn live-insn? defs-vregs [ live-vreg? ] any? ;
M: insn live-insn? drop t ;
VREG-INSN: ##callback-inputs
literal: reg-outputs stack-outputs ;
-INSN: ##alien-callback
-literal: quot ;
-
VREG-INSN: ##callback-outputs
literal: reg-inputs ;
CODEGEN: ##alien-indirect %alien-indirect
CODEGEN: ##alien-assembly %alien-assembly
CODEGEN: ##callback-inputs %callback-inputs
-CODEGEN: ##alien-callback %alien-callback
CODEGEN: ##callback-outputs %callback-outputs
FUNCTION: int ffi_test_1 ;
[ 3 ] [ ffi_test_1 ] unit-test
+[ ] [ \ ffi_test_1 def>> [ drop ] append compile-call ] unit-test
+
FUNCTION: int ffi_test_2 int x int y ;
[ 5 ] [ 2 3 ffi_test_2 ] unit-test
[ "hi" 3 ffi_test_2 ] must-fail
: check-no-compile ( word -- )
dup "no-compile" word-prop [ do-not-compile ] [ drop ] if ;
-: check-effect ( word effect -- )
- swap required-stack-effect 2dup effect<=
- [ 2drop ] [ effect-error ] if ;
-
: inline-recursive? ( word -- ? )
[ "inline" word-prop ] [ "recursive" word-prop ] bi and ;
M: word (build-tree)
[ check-no-compile ]
[ word-body infer-quot-here ]
- [ current-effect check-effect ] tri ;
+ [ required-stack-effect check-effect ] tri ;
: build-tree-with ( in-stack word/quot -- nodes )
[
M: #alien-node check-stack-flow* [ check-in-d ] [ check-out-d ] bi ;
-M: #alien-callback check-stack-flow* drop ;
+M: #alien-callback check-stack-flow* child>> check-stack-flow ;
M: #declare check-stack-flow* drop ;
14 ndrop
] cleaned-up-tree nodes>quot
] unit-test
+
+USING: alien alien.c-types ;
+
+[ t ] [
+ [ int { } cdecl [ 2 2 + ] alien-callback ]
+ { + } inlined?
+] unit-test
[ cleanup ] change-child
dup label>> calls>> empty? [ flatten-recursive ] when ;
+M: #alien-callback cleanup*
+ [ cleanup ] change-child ;
+
M: node cleanup* ;
-! Copyright (C) 2008 Slava Pestov.
+! Copyright (C) 2008, 2010 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
-USING: assocs fry kernel accessors sequences compiler.utilities
-arrays stack-checker.inlining namespaces compiler.tree
-math.order ;
+USING: assocs combinators combinators.short-circuit fry kernel
+locals accessors sequences compiler.utilities arrays
+stack-checker.inlining namespaces compiler.tree math.order ;
IN: compiler.tree.combinators
-: each-node ( ... nodes quot: ( ... node -- ... ) -- ... )
- dup dup '[
- _ [
- dup #branch? [
- children>> [ _ each-node ] each
- ] [
- dup #recursive? [
- child>> _ each-node
- ] [ drop ] if
- ] if
+:: each-node ( ... nodes quot: ( ... node -- ... ) -- ... )
+ nodes [
+ quot
+ [
+ {
+ { [ dup #branch? ] [ children>> [ quot each-node ] each ] }
+ { [ dup #recursive? ] [ child>> quot each-node ] }
+ { [ dup #alien-callback? ] [ child>> quot each-node ] }
+ [ drop ]
+ } cond
] bi
] each ; inline recursive
-: map-nodes ( ... nodes quot: ( ... node -- ... node' ) -- ... nodes )
- dup dup '[
- @
- dup #branch? [
- [ [ _ map-nodes ] map ] change-children
- ] [
- dup #recursive? [
- [ _ map-nodes ] change-child
- ] when
- ] if
+:: map-nodes ( ... nodes quot: ( ... node -- ... node' ) -- ... nodes )
+ nodes [
+ quot call
+ {
+ { [ dup #branch? ] [ [ [ quot map-nodes ] map ] change-children ] }
+ { [ dup #recursive? ] [ [ quot map-nodes ] change-child ] }
+ { [ dup #alien-callback? ] [ [ quot map-nodes ] change-child ] }
+ [ ]
+ } cond
] map-flat ; inline recursive
-: contains-node? ( ... nodes quot: ( ... node -- ... ? ) -- ... ? )
- dup dup '[
- _ keep swap [ drop t ] [
- dup #branch? [
- children>> [ _ contains-node? ] any?
- ] [
- dup #recursive? [
- child>> _ contains-node?
- ] [ drop f ] if
- ] if
- ] if
+:: contains-node? ( ... nodes quot: ( ... node -- ... ? ) -- ... ? )
+ nodes [
+ {
+ quot
+ [
+ {
+ { [ dup #branch? ] [ children>> [ quot contains-node? ] any? ] }
+ { [ dup #recursive? ] [ child>> quot contains-node? ] }
+ { [ dup #alien-callback? ] [ child>> quot contains-node? ] }
+ [ drop f ]
+ } cond
+ ]
+ } 1||
] any? ; inline recursive
: select-children ( seq flags -- seq' )
M: #alien-node remove-dead-code*
maybe-drop-dead-outputs ;
+
+M: #alien-callback remove-dead-code*
+ [ (remove-dead-code) ] change-child ;
sequences sequences.private quotations generic macros arrays
prettyprint prettyprint.backend prettyprint.custom
prettyprint.sections math words combinators
-combinators.short-circuit io sorting hints
+combinators.short-circuit io sorting hints sets
compiler.tree
compiler.tree.recursive
compiler.tree.normalization
compiler.tree.dead-code
compiler.tree.modular-arithmetic ;
FROM: fry => _ ;
+FROM: namespaces => set ;
RENAME: _ match => __
IN: compiler.tree.debugger
M: #alien-assembly node>quot params>> , \ #alien-assembly , ;
-M: #alien-callback node>quot params>> , \ #alien-callback , ;
+M: #alien-callback node>quot
+ [ params>> , ] [ child>> nodes>quot , ] bi \ #alien-callback , ;
M: node node>quot drop ;
] with-scope ;
: inlined? ( quot seq/word -- ? )
- [ cleaned-up-tree ] dip
- dup word? [ 1array ] when
- '[ dup #call? [ word>> _ member? ] [ drop f ] if ]
- contains-node? not ;
+ dup word? [ 1array ] when swap
+ [ cleaned-up-tree [ dup #call? [ word>> , ] [ drop ] if ] each-node ] V{ } make
+ intersect empty? ;
: each-with-next ( ... seq quot: ( ... elt -- ... ) -- ... )
dupd '[ 1 + _ ?nth next-node set @ ] each-index ; inline
-: (escape-analysis) ( node -- )
+: (escape-analysis) ( nodes -- )
[
[ node-defs-values introduce-values ]
[ escape-analysis* ]
[ out-d>> unknown-allocations ]
bi ;
-M: #alien-callback escape-analysis* drop ;
+M: #alien-callback escape-analysis*
+ child>> (escape-analysis) ;
M: node normalize* ;
: normalize ( nodes -- nodes' )
- dup count-introductions make-values
- H{ } clone rename-map set
- [ (normalize) ] [ nip ] 2bi
- [ #introduce prefix ] unless-empty
- rename-node-values ;
+ [
+ dup count-introductions make-values
+ H{ } clone rename-map set
+ [ (normalize) ] [ nip ] 2bi
+ [ #introduce prefix ] unless-empty
+ rename-node-values
+ ] with-scope ;
+
+M: #alien-callback normalize*
+ [ normalize ] change-child ;
-! Copyright (C) 2004, 2008 Slava Pestov.
+! Copyright (C) 2004, 2010 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: sequences accessors kernel assocs
compiler.tree
GENERIC: propagate-around ( node -- )
-: (propagate) ( node -- )
+: (propagate) ( nodes -- )
[ [ compute-copy-equiv ] [ propagate-around ] bi ] each ;
: extract-value-info ( values -- assoc )
M: #alien-node propagate-before propagate-alien-invoke ;
+M: #alien-callback propagate-around child>> (propagate) ;
+
M: #return annotate-node dup in-d>> (annotate-node) ;
-USING: tools.test kernel combinators.short-circuit math sequences accessors
+USING: tools.test kernel combinators.short-circuit math sequences accessors make
compiler.tree
compiler.tree.builder
compiler.tree.combinators
[ { f f f t } ] [ t { f f t f } (tail-calls) ] unit-test
: label-is-loop? ( nodes word -- ? )
- [
- {
- [ drop #recursive? ]
- [ drop label>> loop?>> ]
- [ swap label>> word>> eq? ]
- } 2&&
- ] curry contains-node? ;
+ swap [
+ [
+ dup {
+ [ #recursive? ]
+ [ label>> loop?>> ]
+ } 1&& [ label>> word>> , ] [ drop ] if
+ ] each-node
+ ] V{ } make member? ;
: label-is-not-loop? ( nodes word -- ? )
- [
- {
- [ drop #recursive? ]
- [ drop label>> loop?>> not ]
- [ swap label>> word>> eq? ]
- } 2&&
- ] curry contains-node? ;
+ swap [
+ [
+ dup {
+ [ #recursive? ]
+ [ label>> loop?>> not ]
+ } 1&& [ label>> word>> , ] [ drop ] if
+ ] each-node
+ ] V{ } make member? ;
: loop-test-1 ( a -- )
dup [ 1 + loop-test-1 ] [ drop ] if ; inline recursive
M: #branch node-call-graph
children>> [ (build-call-graph) ] with each ;
+M: #alien-callback node-call-graph
+ child>> (build-call-graph) ;
+
M: node node-call-graph 2drop ;
SYMBOLS: not-loops recursive-nesting ;
: #alien-assembly ( params -- node )
\ #alien-assembly new-alien-node ;
-TUPLE: #alien-callback < node params ;
+TUPLE: #alien-callback < node params child ;
-: #alien-callback ( params -- node )
+: #alien-callback ( params child -- node )
\ #alien-callback new
+ swap >>child
swap >>params ;
: node, ( node -- ) stack-visitor get push ;
compiler.tree.tuple-unboxing compiler.tree.checker
compiler.tree.def-use kernel accessors sequences math
math.private sorting math.order binary-search sequences.private
-slots.private ;
+slots.private alien alien.c-types ;
IN: compiler.tree.tuple-unboxing.tests
: test-unboxing ( quot -- )
[ 1 cons boa over [ "A" throw ] when car>> ]
[ [ <=> ] sort ]
[ [ <=> ] with search ]
+ [ cons boa car>> void { } cdecl [ ] alien-callback ]
} [ [ ] swap [ test-unboxing ] curry unit-test ] each
! A more complicated example
HOOK: %callback-inputs cpu ( reg-outputs stack-outputs -- )
-HOOK: %alien-callback cpu ( quot -- )
-
HOOK: %callback-outputs cpu ( reg-inputs -- )
HOOK: stack-cleanup cpu ( stack-size return abi -- n )
4 stack@ 0 MOV
"begin_callback" f f %c-invoke ;
-M: x86.32 %alien-callback ( quot -- )
- [ EAX ] dip %load-reference
- EAX quot-entry-point-offset [+] CALL ;
-
M: x86.32 %end-callback ( -- )
0 save-vm-ptr
"end_callback" f f %c-invoke ;
param-reg-1 0 MOV
"begin_callback" f f %c-invoke ;
-M: x86.64 %alien-callback ( quot -- )
- [ param-reg-0 ] dip %load-reference
- param-reg-0 quot-entry-point-offset [+] CALL ;
-
M: x86.64 %end-callback ( -- )
param-reg-0 %mov-vm-ptr
"end_callback" f f %c-invoke ;
namespaces init sets words assocs alien.libraries alien
alien.private alien.c-types fry quotations strings
stack-checker.backend stack-checker.errors stack-checker.visitor
-stack-checker.dependencies compiler.utilities ;
+stack-checker.dependencies stack-checker.state
+compiler.utilities effects ;
IN: stack-checker.alien
TUPLE: alien-node-params
TUPLE: alien-assembly-params < alien-node-params { quot callable } ;
-TUPLE: alien-callback-params < alien-node-params { quot callable } xt ;
+TUPLE: alien-callback-params < alien-node-params xt ;
: param-prep-quot ( params -- quot )
parameters>> [ c-type c-type-unboxer-quot ] map spread>quot ;
callbacks get [ dup "stack-cleanup" word-prop <callback> ] cache ;
: callback-bottom ( params -- )
+ "( callback )" <uninterned-word> >>xt
xt>> '[ _ callback-xt ] infer-quot-here ;
: callback-return-quot ( ctype -- quot )
: 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
- yield-hook get
- '[ _ _ do-callback ]
- >quotation ;
+GENERIC: wrap-callback-quot ( params quot -- quot' )
+
+M: callable wrap-callback-quot
+ swap [ callback-prep-quot ] [ callback-return-quot ] bi surround
+ yield-hook get
+ '[ _ _ do-callback ]
+ >quotation ;
+
+: callback-effect ( params -- effect )
+ [ parameters>> length "x" <array> ] [ return>> void? { } { "x" } ? ] bi
+ <effect> ;
+
+: infer-callback-quot ( params quot -- child )
+ [
+ init-inference
+ nest-visitor
+ infer-quot-here
+ end-infer
+ callback-effect check-effect
+ stack-visitor get
+ ] with-scope ;
: infer-alien-callback ( -- )
- alien-callback-params new
- pop-quot
- pop-abi
- pop-params
- pop-return
- "( callback )" <uninterned-word> >>xt
- dup wrap-callback-quot >>quot
- dup callback-bottom
+ pop-literal nip [
+ alien-callback-params new
+ pop-abi
+ pop-params
+ pop-return
+ dup callback-bottom
+ dup
+ dup
+ ] dip wrap-callback-quot infer-callback-quot
#alien-callback, ;
! M\ declared-effect infer-call* didn't properly unify branches
{ 1 0 } [ [ 1 [ drop ] [ drop ] if ] each ] must-infer-as
+! Make sure alien-callback effects are checked properly
+USING: alien.c-types alien ;
+
+[ void { } cdecl [ ] alien-callback ] must-infer
+
+[ [ void { } cdecl [ f [ drop ] unless ] alien-callback ] infer ] [ unbalanced-branches-error? ] must-fail-with
+
+[ [ void { } cdecl [ drop ] alien-callback ] infer ] [ effect-error? ] must-fail-with
+
+[ [ int { } cdecl [ ] alien-callback ] infer ] [ effect-error? ] must-fail-with
+
+[ int { } cdecl [ 5 ] alien-callback ] must-infer
+
+[ int { int } cdecl [ ] alien-callback ] must-infer
+
+[ int { int } cdecl [ 1 + ] alien-callback ] must-infer
+
+[ void { int } cdecl [ . ] alien-callback ] must-infer
+
+: recursive-callback-1 ( -- x )
+ void { } cdecl [ recursive-callback-1 drop ] alien-callback ;
+
+\ recursive-callback-1 def>> must-infer
+
+: recursive-callback-2 ( -- x )
+ void { } cdecl [ recursive-callback-2 drop ] alien-callback ; inline recursive
+
+[ recursive-callback-2 ] must-infer
meta-d length "x" <array>
terminated? get <terminated-effect> ;
+: check-effect ( required-effect -- )
+ [ current-effect ] dip 2dup effect<= [ 2drop ] [ effect-error ] if ;
+
: init-inference ( -- )
terminated? off
V{ } clone \ meta-d set
M: f #alien-invoke, drop ;
M: f #alien-indirect, drop ;
M: f #alien-assembly, drop ;
-M: f #alien-callback, drop ;
+M: f #alien-callback, 2drop ;
HOOK: #alien-invoke, stack-visitor ( params -- )
HOOK: #alien-indirect, stack-visitor ( params -- )
HOOK: #alien-assembly, stack-visitor ( params -- )
-HOOK: #alien-callback, stack-visitor ( params -- )
+HOOK: #alien-callback, stack-visitor ( params child -- )
! returning from it, to avoid a bad interaction between threads
! and callbacks. See basis/compiler/tests/alien.factor for a
! test case.
-: wait-to-return ( yield-quot callback-id -- )
+: wait-to-return ( yield-quot: ( -- ) callback-id -- )
dup current-callback eq?
- [ 2drop ] [ over call( -- ) wait-to-return ] if ;
+ [ 2drop ] [ over call wait-to-return ] if ; inline recursive
! Used by compiler.codegen to wrap callback bodies
-: do-callback ( callback-quot yield-quot -- )
+: do-callback ( callback-quot yield-quot: ( -- ) -- )
init-namespaces
init-catchstack
current-callback