1 ! Copyright (C) 2005, 2008 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: alien.c-types arrays cpu.x86.assembler
4 cpu.x86.architecture cpu.x86.intrinsics cpu.x86.allot
5 cpu.architecture kernel kernel.private math namespaces sequences
6 generator.registers generator.fixup generator system layouts
7 alien.compiler combinators command-line
8 compiler compiler.units io vocabs.loader accessors init ;
11 ! We implement the FFI for Linux, OS X and Windows all at once.
12 ! OS X requires that the stack be 16-byte aligned, and we do
13 ! this on all platforms, sacrificing some stack space for
16 M: x86.32 ds-reg ESI ;
17 M: x86.32 rs-reg EDI ;
18 M: x86.32 stack-reg ESP ;
19 M: x86.32 stack-save-reg EDX ;
20 M: x86.32 temp-reg-1 EAX ;
21 M: x86.32 temp-reg-2 ECX ;
23 M: temp-reg v>operand drop EBX ;
25 M: x86.32 %alien-global 0 [] MOV rc-absolute-cell rel-dlsym ;
27 M: x86.32 %alien-invoke (CALL) rel-dlsym ;
29 ! On x86, parameters are never passed in registers.
30 M: int-regs return-reg drop EAX ;
31 M: int-regs param-regs drop { } ;
32 M: int-regs vregs drop { EAX ECX EDX EBP } ;
33 M: int-regs push-return-reg return-reg PUSH ;
34 : load/store-int-return ( n reg-class -- src dst )
35 return-reg stack-reg rot [+] ;
36 M: int-regs load-return-reg load/store-int-return MOV ;
37 M: int-regs store-return-reg load/store-int-return swap MOV ;
39 M: float-regs param-regs drop { } ;
40 M: float-regs vregs drop { XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7 } ;
42 : FSTP ( operand size -- ) 4 = [ FSTPS ] [ FSTPL ] if ;
44 M: float-regs push-return-reg
45 stack-reg swap reg-size [ SUB stack-reg [] ] keep FSTP ;
47 : FLD ( operand size -- ) 4 = [ FLDS ] [ FLDL ] if ;
49 : load/store-float-return ( n reg-class -- op size )
50 [ stack@ ] [ reg-size ] bi* ;
51 M: float-regs load-return-reg load/store-float-return FLD ;
52 M: float-regs store-return-reg load/store-float-return FSTP ;
55 dup 16 align swap - ESP swap SUB ;
58 16 align ESP swap ADD ;
60 : with-aligned-stack ( n quot -- )
61 swap dup align-sub slip align-add ; inline
63 ! On x86, we can always use an address as an operand
65 M: x86.32 address-operand ;
67 M: x86.32 fixnum>slot@ 1 SHR ;
69 M: x86.32 prepare-division CDQ ;
71 M: x86.32 load-indirect
72 0 [] MOV rc-absolute-cell rel-literal ;
74 M: object %load-param-reg 3drop ;
76 M: object %save-param-reg 3drop ;
78 M: x86.32 %prepare-unbox ( -- )
79 #! Move top of data stack to EAX.
83 : (%unbox) ( func -- )
89 ] with-aligned-stack ;
91 M: x86.32 %unbox ( n reg-class func -- )
92 #! The value being unboxed must already be in EAX.
93 #! If n is f, we're unboxing a return value about to be
94 #! returned by the callback. Otherwise, we're unboxing
95 #! a parameter to a C function about to be called.
97 ! Store the return value on the C stack
98 over [ store-return-reg ] [ 2drop ] if ;
100 M: x86.32 %unbox-long-long ( n func -- )
102 ! Store the return value on the C stack
105 cell + stack@ EDX MOV
108 M: x86.32 %unbox-struct-2
109 #! Alien must be in EAX.
112 "alien_offset" f %alien-invoke
117 ] with-aligned-stack ;
119 M: x86.32 %unbox-large-struct ( n size -- )
120 #! Alien must be in EAX.
121 ! Compute destination address
126 ! Push destination address
128 ! Push source address
130 ! Copy the struct to the stack
131 "to_value_struct" f %alien-invoke
132 ] with-aligned-stack ;
134 : box@ ( n reg-class -- stack@ )
135 #! Used for callbacks; we want to box the values given to
136 #! us by the C function caller. Computes stack location of
137 #! nth parameter; note that we must go back one more stack
138 #! frame, since %box sets one up to call the one-arg boxer
139 #! function. The size of this stack frame so far depends on
140 #! the reg-class of the boxer's arg.
141 reg-size neg + stack-frame* + 20 + ;
143 : (%box) ( n reg-class -- )
144 #! If n is f, push the return register onto the stack; we
145 #! are boxing a return value of a C function. If n is an
146 #! integer, push [ESP+n] on the stack; we are boxing a
147 #! parameter being passed to a callback from C.
148 over [ [ box@ ] keep [ load-return-reg ] keep ] [ nip ] if
151 M: x86.32 %box ( n reg-class func -- )
153 >r (%box) r> f %alien-invoke
154 ] with-aligned-stack ;
156 : (%box-long-long) ( n -- )
157 #! If n is f, push the return registers onto the stack; we
158 #! are boxing a return value of a C function. If n is an
159 #! integer, push [ESP+n]:[ESP+n+4] on the stack; we are
160 #! boxing a parameter being passed to a callback from C.
164 EAX swap cell - stack@ MOV
169 M: x86.32 %box-long-long ( n func -- )
171 [ (%box-long-long) ] [ f %alien-invoke ] bi*
172 ] with-aligned-stack ;
174 M: x86.32 %box-large-struct ( n size -- )
175 ! Compute destination address
176 [ swap struct-return@ ] keep
181 ! Push destination address
183 ! Copy the struct from the C stack
184 "box_value_struct" f %alien-invoke
185 ] with-aligned-stack ;
187 M: x86.32 %prepare-box-struct ( size -- )
188 ! Compute target address for value struct return
189 EAX ESP rot f struct-return@ [+] LEA
190 ! Store it as the first parameter
193 M: x86.32 %unbox-struct-1
194 #! Alien must be in EAX.
197 "alien_offset" f %alien-invoke
200 ] with-aligned-stack ;
202 M: x86.32 %box-small-struct ( size -- )
203 #! Box a <= 8-byte struct returned in EAX:DX. OS X only.
208 "box_small_struct" f %alien-invoke
209 ] with-aligned-stack ;
211 M: x86.32 %prepare-alien-indirect ( -- )
212 "unbox_alien" f %alien-invoke
215 M: x86.32 %alien-indirect ( -- )
218 M: x86.32 %alien-callback ( quot -- )
222 "c_to_factor" f %alien-invoke
223 ] with-aligned-stack ;
225 M: x86.32 %callback-value ( ctype -- )
228 ! Save top of data stack
231 ! Restore data/call/retain stacks
232 "unnest_stacks" f %alien-invoke
233 ! Place top of data stack in EAX
240 M: x86.32 %cleanup ( alien-node -- )
241 #! a) If we just called an stdcall function in Windows, it
242 #! cleaned up the stack frame for us. But we don't want that
243 #! so we 'undo' the cleanup since we do that in %epilogue.
244 #! b) If we just called a function returning a struct, we
248 [ dup abi>> "stdcall" = ]
249 [ alien-stack-frame ESP swap SUB ]
251 [ dup return>> large-struct? ]
257 M: x86.32 %unwind ( n -- ) %epilogue-later RET ;
260 cell "longlong" c-type set-c-type-align
261 cell "ulonglong" c-type set-c-type-align
262 4 "double" c-type set-c-type-align
265 : (sse2?) ( -- ? ) "Intrinsic" throw ;
270 { EAX EBX ECX EDX } [ PUSH ] each
275 { EAX EBX ECX EDX } [ POP ] each
277 ] { } define-if-intrinsic
281 : sse2? ( -- ? ) (sse2?) ;
283 "-no-sse2" cli-args member? [
284 "Checking if your CPU supports SSE2..." print flush
285 [ optimized-recompile-hook ] recompile-hook [
286 [ sse2? ] compile-call
290 "cpu.x86.sse2" require
293 "This image was built to use SSE2, which your CPU does not support." print
294 "You will need to bootstrap Factor again." print
298 ] "cpu.x86" add-init-hook