! Copyright (C) 2006, 2007 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. IN: alien USING: arrays generator generic hashtables kernel kernel-internals math namespaces sequences words inference ; ! Common protocol for alien-invoke/alien-callback/alien-indirect GENERIC: alien-node-parameters ( node -- seq ) GENERIC: alien-node-return ( node -- ctype ) GENERIC: alien-node-abi ( node -- str ) : large-struct? ( ctype -- ? ) dup c-struct? [ heap-size struct-small-enough? not ] [ drop f ] if ; : alien-node-parameters* ( node -- seq ) dup alien-node-parameters swap alien-node-return large-struct? [ "void*" add* ] when ; : alien-node-return* ( node -- ctype ) alien-node-return dup large-struct? [ drop "void" ] when ; : parameter-align ( n type -- n delta ) over >r dup c-type-stack-align? [ c-type-align ] [ drop cell ] if 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 ( node -- n ) alien-node-parameters* parameter-sizes drop ; : alien-invoke-frame ( node -- n ) #! One cell is temporary storage, temp@ dup alien-node-return return-size swap alien-stack-frame + cell + ; : 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 : reg-class-full? ( class -- ? ) dup class get swap param-regs length >= ; : spill-param ( reg-class -- n reg-class ) reg-size stack-params dup get -rot +@ T{ stack-params } ; : fastcall-param ( reg-class -- n reg-class ) [ dup class get swap inc-reg-class ] keep ; : alloc-parameter ( parameter -- reg reg-class ) c-type c-type-reg-class dup reg-class-full? [ spill-param ] [ fastcall-param ] if [ param-reg ] keep ; : (flatten-int-type) ( size -- ) cell /i "void*" % ; : flatten-int-type ( n type -- n ) [ parameter-align (flatten-int-type) ] keep stack-size cell align dup (flatten-int-type) + ; : flatten-value-type ( n type -- n ) dup c-type c-type-reg-class T{ int-regs } = [ flatten-int-type ] [ , ] if ; : flatten-value-types ( params -- params ) #! Convert value type structs to consecutive void*s. [ 0 [ 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 ( -- ) 0 { int-regs float-regs stack-params } [ set ] each-with ; : 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). swap alien-node-parameters* flatten-value-types [ pick >r alloc-parameter r> execute ] each-parameter drop ; inline : if-void ( type true false -- ) pick "void" = [ drop nip call ] [ nip call ] if ; inline : alien-invoke-stack ( node extra -- ) over alien-node-parameters length + over consume-values dup alien-node-return "void" = 0 1 ? swap produce-values ; : (make-prep-quot) ( parameters -- ) dup empty? [ drop ] [ unclip c-type c-type-prep % \ >r , (make-prep-quot) \ r> , ] if ; : make-prep-quot ( node -- quot ) alien-node-parameters [ (make-prep-quot) ] [ ] make ; : unbox-parameters ( offset node -- ) alien-node-parameters [ %prepare-unbox >r over + r> unbox-parameter ] reverse-each-parameter drop ; : %before-alien #! Save Factor stack pointers in case the C code calls a #! callback which does a GC, which must reliably trace #! all roots. "save_stacks" f %alien-invoke ; : 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. alien-node-return dup large-struct? [ heap-size %prepare-box-struct cell ] [ drop 0 ] if ; : objects>registers ( node -- ) #! 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 %before-alien \ %load-param-reg move-parameters ] with-param-regs ; : box-return* ( node -- ) alien-node-return [ ] [ box-return ] if-void ;