]> gitweb.factorcode.org Git - factor.git/blob - basis/cpu/x86/32/32.factor
Merge branch 'master' into global_optimization
[factor.git] / basis / cpu / x86 / 32 / 32.factor
1 ! Copyright (C) 2005, 2008 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: locals alien.c-types alien.syntax arrays kernel
4 math namespaces sequences system layouts io vocabs.loader
5 accessors init combinators command-line cpu.x86.assembler
6 cpu.x86 cpu.architecture compiler compiler.units
7 compiler.constants compiler.alien compiler.codegen
8 compiler.codegen.fixup compiler.cfg.instructions
9 compiler.cfg.builder compiler.cfg.intrinsics make ;
10 IN: cpu.x86.32
11
12 ! We implement the FFI for Linux, OS X and Windows all at once.
13 ! OS X requires that the stack be 16-byte aligned, and we do
14 ! this on all platforms, sacrificing some stack space for
15 ! code simplicity.
16
17 M: x86.32 machine-registers
18     {
19         { int-regs { EAX ECX EDX EBP EBX } }
20         { double-float-regs { XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7 } }
21     } ;
22
23 M: x86.32 ds-reg ESI ;
24 M: x86.32 rs-reg EDI ;
25 M: x86.32 stack-reg ESP ;
26 M: x86.32 temp-reg-1 ECX ;
27 M: x86.32 temp-reg-2 EDX ;
28
29 M:: x86.32 %dispatch ( src temp -- )
30     ! Load jump table base.
31     src HEX: ffffffff ADD
32     0 rc-absolute-cell rel-here
33     ! Go
34     src HEX: 7f [+] JMP
35     ! Fix up the displacement above
36     cell code-alignment
37     [ 7 + building get dup pop* push ]
38     [ align-code ]
39     bi ;
40
41 ! Registers for fastcall
42 M: x86.32 param-reg-1 EAX ;
43 M: x86.32 param-reg-2 EDX ;
44
45 M: x86.32 pic-tail-reg EBX ;
46
47 M: x86.32 reserved-area-size 0 ;
48
49 M: x86.32 %alien-invoke 0 CALL rc-relative rel-dlsym ;
50
51 M: x86.32 %alien-invoke-tail 0 JMP rc-relative rel-dlsym ;
52
53 M: x86.32 return-struct-in-registers? ( c-type -- ? )
54     c-type
55     [ return-in-registers?>> ]
56     [ heap-size { 1 2 4 8 } member? ] bi
57     os { linux netbsd solaris } member? not
58     and or ;
59
60 : struct-return@ ( n -- operand )
61     [ next-stack@ ] [ stack-frame get params>> stack@ ] if* ;
62
63 ! On x86, parameters are never passed in registers.
64 M: int-regs return-reg drop EAX ;
65 M: int-regs param-regs drop { } ;
66 M: int-regs push-return-reg return-reg PUSH ;
67
68 M: int-regs load-return-reg
69     return-reg swap next-stack@ MOV ;
70
71 M: int-regs store-return-reg
72     [ stack@ ] [ return-reg ] bi* MOV ;
73
74 M: float-regs param-regs drop { } ;
75
76 : FSTP ( operand size -- ) 4 = [ FSTPS ] [ FSTPL ] if ;
77
78 M: float-regs push-return-reg
79     stack-reg swap reg-size
80     [ SUB ] [ [ [] ] dip FSTP ] 2bi ;
81
82 : FLD ( operand size -- ) 4 = [ FLDS ] [ FLDL ] if ;
83
84 M: float-regs load-return-reg
85     [ next-stack@ ] [ reg-size ] bi* FLD ;
86
87 M: float-regs store-return-reg
88     [ stack@ ] [ reg-size ] bi* FSTP ;
89
90 : align-sub ( n -- )
91     [ align-stack ] keep - decr-stack-reg ;
92
93 : align-add ( n -- )
94     align-stack incr-stack-reg ;
95
96 : with-aligned-stack ( n quot -- )
97     [ [ align-sub ] [ call ] bi* ]
98     [ [ align-add ] [ drop ] bi* ] 2bi ; inline
99
100 M: x86.32 %prologue ( n -- )
101     dup PUSH
102     0 PUSH rc-absolute-cell rel-this
103     stack-reg swap 3 cells - SUB ;
104
105 M: object %load-param-reg 3drop ;
106
107 M: object %save-param-reg 3drop ;
108
109 : (%box) ( n reg-class -- )
110     #! If n is f, push the return register onto the stack; we
111     #! are boxing a return value of a C function. If n is an
112     #! integer, push [ESP+n] on the stack; we are boxing a
113     #! parameter being passed to a callback from C.
114     over [ load-return-reg ] [ 2drop ] if ;
115
116 M:: x86.32 %box ( n reg-class func -- )
117     n reg-class (%box)
118     reg-class reg-size [
119         reg-class push-return-reg
120         func f %alien-invoke
121     ] with-aligned-stack ;
122     
123 : (%box-long-long) ( n -- )
124     [
125         EDX over next-stack@ MOV
126         EAX swap cell - next-stack@ MOV 
127     ] when* ;
128
129 M: x86.32 %box-long-long ( n func -- )
130     [ (%box-long-long) ] dip
131     8 [
132         EDX PUSH
133         EAX PUSH
134         f %alien-invoke
135     ] with-aligned-stack ;
136
137 M:: x86.32 %box-large-struct ( n c-type -- )
138     ! Compute destination address
139     ECX n struct-return@ LEA
140     8 [
141         ! Push struct size
142         c-type heap-size PUSH
143         ! Push destination address
144         ECX PUSH
145         ! Copy the struct from the C stack
146         "box_value_struct" f %alien-invoke
147     ] with-aligned-stack ;
148
149 M: x86.32 %prepare-box-struct ( -- )
150     ! Compute target address for value struct return
151     EAX f struct-return@ LEA
152     ! Store it as the first parameter
153     0 stack@ EAX MOV ;
154
155 M: x86.32 %box-small-struct ( c-type -- )
156     #! Box a <= 8-byte struct returned in EAX:EDX. OS X only.
157     12 [
158         heap-size PUSH
159         EDX PUSH
160         EAX PUSH
161         "box_small_struct" f %alien-invoke
162     ] with-aligned-stack ;
163
164 M: x86.32 %prepare-unbox ( -- )
165     #! Move top of data stack to EAX.
166     EAX ESI [] MOV
167     ESI 4 SUB ;
168
169 : (%unbox) ( func -- )
170     4 [
171         ! Push parameter
172         EAX PUSH
173         ! Call the unboxer
174         f %alien-invoke
175     ] with-aligned-stack ;
176
177 M: x86.32 %unbox ( n reg-class func -- )
178     #! The value being unboxed must already be in EAX.
179     #! If n is f, we're unboxing a return value about to be
180     #! returned by the callback. Otherwise, we're unboxing
181     #! a parameter to a C function about to be called.
182     (%unbox)
183     ! Store the return value on the C stack
184     over [ store-return-reg ] [ 2drop ] if ;
185
186 M: x86.32 %unbox-long-long ( n func -- )
187     (%unbox)
188     ! Store the return value on the C stack
189     [
190         dup stack@ EAX MOV
191         cell + stack@ EDX MOV
192     ] when* ;
193
194 : %unbox-struct-1 ( -- )
195     #! Alien must be in EAX.
196     4 [
197         EAX PUSH
198         "alien_offset" f %alien-invoke
199         ! Load first cell
200         EAX EAX [] MOV
201     ] with-aligned-stack ;
202
203 : %unbox-struct-2 ( -- )
204     #! Alien must be in EAX.
205     4 [
206         EAX PUSH
207         "alien_offset" f %alien-invoke
208         ! Load second cell
209         EDX EAX 4 [+] MOV
210         ! Load first cell
211         EAX EAX [] MOV
212     ] with-aligned-stack ;
213
214 M: x86 %unbox-small-struct ( size -- )
215     #! Alien must be in EAX.
216     heap-size cell align cell /i {
217         { 1 [ %unbox-struct-1 ] }
218         { 2 [ %unbox-struct-2 ] }
219     } case ;
220
221 M: x86.32 %unbox-large-struct ( n c-type -- )
222     ! Alien must be in EAX.
223     ! Compute destination address
224     ECX rot stack@ LEA
225     12 [
226         ! Push struct size
227         heap-size PUSH
228         ! Push destination address
229         ECX PUSH
230         ! Push source address
231         EAX PUSH
232         ! Copy the struct to the stack
233         "to_value_struct" f %alien-invoke
234     ] with-aligned-stack ;
235
236 M: x86.32 %prepare-alien-indirect ( -- )
237     "unbox_alien" f %alien-invoke
238     EBP EAX MOV ;
239
240 M: x86.32 %alien-indirect ( -- )
241     EBP CALL ;
242
243 M: x86.32 %alien-callback ( quot -- )
244     4 [
245         EAX swap %load-reference
246         EAX PUSH
247         "c_to_factor" f %alien-invoke
248     ] with-aligned-stack ;
249
250 M: x86.32 %callback-value ( ctype -- )
251     ! Align C stack
252     ESP 12 SUB
253     ! Save top of data stack in non-volatile register
254     %prepare-unbox
255     EAX PUSH
256     ! Restore data/call/retain stacks
257     "unnest_stacks" f %alien-invoke
258     ! Place top of data stack in EAX
259     EAX POP
260     ! Restore C stack
261     ESP 12 ADD
262     ! Unbox EAX
263     unbox-return ;
264
265 M: x86.32 %cleanup ( params -- )
266     #! a) If we just called an stdcall function in Windows, it
267     #! cleaned up the stack frame for us. But we don't want that
268     #! so we 'undo' the cleanup since we do that in %epilogue.
269     #! b) If we just called a function returning a struct, we
270     #! have to fix ESP.
271     {
272         {
273             [ dup abi>> "stdcall" = ]
274             [ drop ESP stack-frame get params>> SUB ]
275         } {
276             [ dup return>> large-struct? ]
277             [ drop EAX PUSH ]
278         }
279         [ drop ]
280     } cond ;
281
282 M: x86.32 %callback-return ( n -- )
283     #! a) If the callback is stdcall, we have to clean up the
284     #! caller's stack frame.
285     #! b) If the callback is returning a large struct, we have
286     #! to fix ESP.
287     {
288         { [ dup abi>> "stdcall" = ] [
289             <alien-stack-frame>
290             [ params>> ] [ return>> ] bi +
291         ] }
292         { [ dup return>> large-struct? ] [ drop 4 ] }
293         [ drop 0 ]
294     } cond RET ;
295
296 M: x86.32 dummy-stack-params? f ;
297
298 M: x86.32 dummy-int-params? f ;
299
300 M: x86.32 dummy-fp-params? f ;
301
302 os windows? [
303     cell "longlong" c-type (>>align)
304     cell "ulonglong" c-type (>>align)
305     4 "double" c-type (>>align)
306 ] unless
307
308 USING: cpu.x86.features cpu.x86.features.private ;
309
310 "-no-sse2" (command-line) member? [
311     [ { check_sse2 } compile ] with-optimizer
312
313     "Checking if your CPU supports SSE2..." print flush
314     sse2? [
315         " - yes" print
316         enable-float-intrinsics
317         [
318             sse2? [
319                 "This image was built to use SSE2, which your CPU does not support." print
320                 "You will need to bootstrap Factor again." print
321                 flush
322                 1 exit
323             ] unless
324         ] "cpu.x86" add-init-hook
325     ] [ " - no" print ] if
326 ] unless