]> gitweb.factorcode.org Git - factor.git/blob - basis/cpu/x86/x86.factor
Removing more >r/r> usages
[factor.git] / basis / cpu / x86 / x86.factor
1 ! Copyright (C) 2005, 2008 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors assocs alien alien.c-types arrays strings
4 cpu.x86.assembler cpu.x86.assembler.private cpu.architecture
5 kernel kernel.private math memory namespaces make sequences
6 words system layouts combinators math.order fry locals
7 compiler.constants compiler.cfg.registers
8 compiler.cfg.instructions compiler.codegen
9 compiler.codegen.fixup ;
10 IN: cpu.x86
11
12 M: x86 two-operand? t ;
13
14 HOOK: temp-reg-1 cpu ( -- reg )
15 HOOK: temp-reg-2 cpu ( -- reg )
16
17 HOOK: param-reg-1 cpu ( -- reg )
18 HOOK: param-reg-2 cpu ( -- reg )
19
20 M: x86 %load-immediate MOV ;
21
22 M: x86 %load-indirect swap 0 MOV rc-absolute-cell rel-immediate ;
23
24 HOOK: ds-reg cpu ( -- reg )
25 HOOK: rs-reg cpu ( -- reg )
26
27 : reg-stack ( n reg -- op ) swap cells neg [+] ;
28
29 GENERIC: loc>operand ( loc -- operand )
30
31 M: ds-loc loc>operand n>> ds-reg reg-stack ;
32 M: rs-loc loc>operand n>> rs-reg reg-stack ;
33
34 M: x86 %peek loc>operand MOV ;
35 M: x86 %replace loc>operand swap MOV ;
36 : (%inc) ( n reg -- ) swap cells dup 0 > [ ADD ] [ neg SUB ] if ; inline
37 M: x86 %inc-d ( n -- ) ds-reg (%inc) ;
38 M: x86 %inc-r ( n -- ) rs-reg (%inc) ;
39
40 : align-stack ( n -- n' )
41     os macosx? cpu x86.64? or [ 16 align ] when ;
42
43 HOOK: reserved-area-size cpu ( -- n )
44
45 M: x86 stack-frame-size ( stack-frame -- i )
46     [ spill-counts>> [ swap reg-size * ] { } assoc>map sum ]
47     [ params>> ]
48     [ return>> ]
49     tri + +
50     3 cells +
51     reserved-area-size +
52     align-stack ;
53
54 M: x86 %call ( label -- ) CALL ;
55 M: x86 %jump-label ( label -- ) JMP ;
56 M: x86 %return ( -- ) 0 RET ;
57
58 : code-alignment ( align -- n )
59     [ building get [ integer? ] count dup ] dip align swap - ;
60
61 : align-code ( n -- )
62     0 <repetition> % ;
63
64 M: x86 %dispatch-label ( word -- )
65     0 cell, rc-absolute-cell rel-word ;
66
67 :: (%slot) ( obj slot tag temp -- op )
68     temp slot obj [+] LEA
69     temp tag neg [+] ; inline
70
71 :: (%slot-imm) ( obj slot tag -- op )
72     obj slot cells tag - [+] ; inline
73
74 M: x86 %slot ( dst obj slot tag temp -- ) (%slot) MOV ;
75 M: x86 %slot-imm ( dst obj slot tag -- ) (%slot-imm) MOV ;
76 M: x86 %set-slot ( src obj slot tag temp -- ) (%slot) swap MOV ;
77 M: x86 %set-slot-imm ( src obj slot tag -- ) (%slot-imm) swap MOV ;
78
79 M: x86 %add     [+] LEA ;
80 M: x86 %add-imm [+] LEA ;
81 M: x86 %sub     nip SUB ;
82 M: x86 %sub-imm neg [+] LEA ;
83 M: x86 %mul     nip swap IMUL2 ;
84 M: x86 %mul-imm nip IMUL2 ;
85 M: x86 %and     nip AND ;
86 M: x86 %and-imm nip AND ;
87 M: x86 %or      nip OR ;
88 M: x86 %or-imm  nip OR ;
89 M: x86 %xor     nip XOR ;
90 M: x86 %xor-imm nip XOR ;
91 M: x86 %shl-imm nip SHL ;
92 M: x86 %shr-imm nip SHR ;
93 M: x86 %sar-imm nip SAR ;
94 M: x86 %not     drop NOT ;
95
96 : ?MOV ( dst src -- )
97     2dup = [ 2drop ] [ MOV ] if ; inline
98
99 :: move>args ( src1 src2 -- )
100     {
101         { [ src1 param-reg-2 = ] [ param-reg-1 src2 ?MOV param-reg-1 param-reg-2 XCHG ] }
102         { [ src1 param-reg-1 = ] [ param-reg-2 src2 ?MOV ] }
103         { [ src2 param-reg-1 = ] [ param-reg-2 src1 ?MOV param-reg-1 param-reg-2 XCHG ] }
104         { [ src2 param-reg-2 = ] [ param-reg-1 src1 ?MOV ] }
105         [
106             param-reg-1 src1 MOV
107             param-reg-2 src2 MOV
108         ]
109     } cond ;
110
111 HOOK: %alien-invoke-tail cpu ( func dll -- )
112
113 :: overflow-template ( src1 src2 insn inverse func -- )
114     <label> "no-overflow" set
115     src1 src2 insn call
116     ds-reg [] src1 MOV
117     "no-overflow" get JNO
118     src1 src2 inverse call
119     src1 src2 move>args
120     %prepare-alien-invoke
121     func f %alien-invoke
122     "no-overflow" resolve-label ; inline
123
124 :: overflow-template-tail ( src1 src2 insn inverse func -- )
125     <label> "no-overflow" set
126     src1 src2 insn call
127     "no-overflow" get JNO
128     src1 src2 inverse call
129     src1 src2 move>args
130     %prepare-alien-invoke
131     func f %alien-invoke-tail
132     "no-overflow" resolve-label
133     ds-reg [] src1 MOV
134     0 RET ; inline
135
136 M: x86 %fixnum-add ( src1 src2 -- )
137     [ ADD ] [ SUB ] "overflow_fixnum_add" overflow-template ;
138
139 M: x86 %fixnum-add-tail ( src1 src2 -- )
140     [ ADD ] [ SUB ] "overflow_fixnum_add" overflow-template-tail ;
141
142 M: x86 %fixnum-sub ( src1 src2 -- )
143     [ SUB ] [ ADD ] "overflow_fixnum_subtract" overflow-template ;
144
145 M: x86 %fixnum-sub-tail ( src1 src2 -- )
146     [ SUB ] [ ADD ] "overflow_fixnum_subtract" overflow-template-tail ;
147
148 M:: x86 %fixnum-mul ( src1 src2 temp1 temp2 -- )
149     "no-overflow" define-label
150     temp1 src1 MOV
151     temp1 tag-bits get SAR
152     src2 temp1 IMUL2
153     ds-reg [] temp1 MOV
154     "no-overflow" get JNO
155     src1 src2 move>args
156     param-reg-1 tag-bits get SAR
157     param-reg-2 tag-bits get SAR
158     %prepare-alien-invoke
159     "overflow_fixnum_multiply" f %alien-invoke
160     "no-overflow" resolve-label ;
161
162 M:: x86 %fixnum-mul-tail ( src1 src2 temp1 temp2 -- )
163     "overflow" define-label
164     temp1 src1 MOV
165     temp1 tag-bits get SAR
166     src2 temp1 IMUL2
167     "overflow" get JO
168     ds-reg [] temp1 MOV
169     0 RET
170     "overflow" resolve-label
171     src1 src2 move>args
172     param-reg-1 tag-bits get SAR
173     param-reg-2 tag-bits get SAR
174     %prepare-alien-invoke
175     "overflow_fixnum_multiply" f %alien-invoke-tail ;
176
177 : bignum@ ( reg n -- op )
178     cells bignum tag-number - [+] ; inline
179
180 M:: x86 %integer>bignum ( dst src temp -- )
181     #! on entry, inreg is a signed 32-bit quantity
182     #! exits with tagged ptr to bignum in outreg
183     #! 1 cell header, 1 cell length, 1 cell sign, + digits
184     #! length is the # of digits + sign
185     [
186         "end" define-label
187         ! Load cached zero value
188         dst 0 >bignum %load-indirect
189         src 0 CMP
190         ! Is it zero? Then just go to the end and return this zero
191         "end" get JE
192         ! Allocate a bignum
193         dst 4 cells bignum temp %allot
194         ! Write length
195         dst 1 bignum@ 2 tag-fixnum MOV
196         ! Store value
197         dst 3 bignum@ src MOV
198         ! Compute sign
199         temp src MOV
200         temp cell-bits 1- SAR
201         temp 1 AND
202         ! Store sign
203         dst 2 bignum@ temp MOV
204         ! Make negative value positive
205         temp temp ADD
206         temp NEG
207         temp 1 ADD
208         src temp IMUL2
209         ! Store the bignum
210         dst 3 bignum@ temp MOV
211         "end" resolve-label
212     ] with-scope ;
213
214 M:: x86 %bignum>integer ( dst src temp -- )
215     [
216         "end" define-label
217         ! load length
218         temp src 1 bignum@ MOV
219         ! if the length is 1, its just the sign and nothing else,
220         ! so output 0
221         dst 0 MOV
222         temp 1 tag-fixnum CMP
223         "end" get JE
224         ! load the value
225         dst src 3 bignum@ MOV
226         ! load the sign
227         temp src 2 bignum@ MOV
228         ! convert it into -1 or 1
229         temp temp ADD
230         temp NEG
231         temp 1 ADD
232         ! make dst signed
233         temp dst IMUL2
234         "end" resolve-label
235     ] with-scope ;
236
237 M: x86 %add-float nip ADDSD ;
238 M: x86 %sub-float nip SUBSD ;
239 M: x86 %mul-float nip MULSD ;
240 M: x86 %div-float nip DIVSD ;
241
242 M: x86 %integer>float CVTSI2SD ;
243 M: x86 %float>integer CVTTSD2SI ;
244
245 M: x86 %copy ( dst src -- ) ?MOV ;
246
247 M: x86 %copy-float ( dst src -- )
248     2dup = [ 2drop ] [ MOVSD ] if ;
249
250 M: x86 %unbox-float ( dst src -- )
251     float-offset [+] MOVSD ;
252
253 M:: x86 %unbox-any-c-ptr ( dst src temp -- )
254     [
255         { "is-byte-array" "end" "start" } [ define-label ] each
256         dst 0 MOV
257         temp src MOV
258         ! We come back here with displaced aliens
259         "start" resolve-label
260         ! Is the object f?
261         temp \ f tag-number CMP
262         "end" get JE
263         ! Is the object an alien?
264         temp header-offset [+] alien type-number tag-fixnum CMP
265         "is-byte-array" get JNE
266         ! If so, load the offset and add it to the address
267         dst temp alien-offset [+] ADD
268         ! Now recurse on the underlying alien
269         temp temp underlying-alien-offset [+] MOV
270         "start" get JMP
271         "is-byte-array" resolve-label
272         ! Add byte array address to address being computed
273         dst temp ADD
274         ! Add an offset to start of byte array's data
275         dst byte-array-offset ADD
276         "end" resolve-label
277     ] with-scope ;
278
279 M:: x86 %box-float ( dst src temp -- )
280     dst 16 float temp %allot
281     dst float-offset [+] src MOVSD ;
282
283 : alien@ ( reg n -- op ) cells alien tag-number - [+] ;
284
285 M:: x86 %box-alien ( dst src temp -- )
286     [
287         "end" define-label
288         dst \ f tag-number MOV
289         src 0 CMP
290         "end" get JE
291         dst 4 cells alien temp %allot
292         dst 1 alien@ \ f tag-number MOV
293         dst 2 alien@ \ f tag-number MOV
294         ! Store src in alien-offset slot
295         dst 3 alien@ src MOV
296         "end" resolve-label
297     ] with-scope ;
298
299 : small-reg-4 ( reg -- reg' )
300     H{
301         { EAX EAX }
302         { ECX ECX }
303         { EDX EDX }
304         { EBX EBX }
305         { ESP ESP }
306         { EBP EBP }
307         { ESI ESP }
308         { EDI EDI }
309
310         { RAX EAX }
311         { RCX ECX }
312         { RDX EDX }
313         { RBX EBX }
314         { RSP ESP }
315         { RBP EBP }
316         { RSI ESP }
317         { RDI EDI }
318     } at ; inline
319
320 : small-reg-2 ( reg -- reg' )
321     small-reg-4 H{
322         { EAX AX }
323         { ECX CX }
324         { EDX DX }
325         { EBX BX }
326         { ESP SP }
327         { EBP BP }
328         { ESI SI }
329         { EDI DI }
330     } at ; inline
331
332 : small-reg-1 ( reg -- reg' )
333     small-reg-4 {
334         { EAX AL }
335         { ECX CL }
336         { EDX DL }
337         { EBX BL }
338     } at ; inline
339
340 : small-reg ( reg size -- reg' )
341     {
342         { 1 [ small-reg-1 ] }
343         { 2 [ small-reg-2 ] }
344         { 4 [ small-reg-4 ] }
345     } case ;
346
347 : small-regs ( -- regs ) { EAX ECX EDX EBX } ; inline
348
349 : small-reg-that-isn't ( exclude -- reg' )
350     small-regs swap [ small-reg-4 ] map '[ _ memq? not ] find nip ;
351
352 : with-save/restore ( reg quot -- )
353     [ drop PUSH ] [ call ] [ drop POP ] 2tri ; inline
354
355 :: with-small-register ( dst exclude quot: ( new-dst -- ) -- )
356     #! If the destination register overlaps a small register, we
357     #! call the quot with that. Otherwise, we find a small
358     #! register that is not in exclude, and call quot, saving
359     #! and restoring the small register.
360     dst small-reg-4 small-regs memq? [ dst quot call ] [
361         exclude small-reg-that-isn't
362         [ quot call ] with-save/restore
363     ] if ; inline
364
365 M:: x86 %string-nth ( dst src index temp -- )
366     "end" define-label
367     dst { src index temp } [| new-dst |
368         temp src index [+] LEA
369         new-dst 1 small-reg temp string-offset [+] MOV
370         new-dst new-dst 1 small-reg MOVZX
371         temp src string-aux-offset [+] MOV
372         temp \ f tag-number CMP
373         "end" get JE
374         new-dst temp XCHG
375         new-dst index ADD
376         new-dst index ADD
377         new-dst 2 small-reg new-dst byte-array-offset [+] MOV
378         new-dst new-dst 2 small-reg MOVZX
379         new-dst 8 SHL
380         new-dst temp OR
381         "end" resolve-label
382         dst new-dst ?MOV
383     ] with-small-register ;
384
385 :: %alien-integer-getter ( dst src size quot -- )
386     dst { src } [| new-dst |
387         new-dst dup size small-reg dup src [] MOV
388         quot call
389         dst new-dst ?MOV
390     ] with-small-register ; inline
391
392 : %alien-unsigned-getter ( dst src size -- )
393     [ MOVZX ] %alien-integer-getter ; inline
394
395 M: x86 %alien-unsigned-1 1 %alien-unsigned-getter ;
396 M: x86 %alien-unsigned-2 2 %alien-unsigned-getter ;
397
398 : %alien-signed-getter ( dst src size -- )
399     [ MOVSX ] %alien-integer-getter ; inline
400
401 M: x86 %alien-signed-1 1 %alien-signed-getter ;
402 M: x86 %alien-signed-2 2 %alien-signed-getter ;
403 M: x86 %alien-signed-4 4 %alien-signed-getter ;
404
405 M: x86 %alien-unsigned-4 4 [ 2drop ] %alien-integer-getter ;
406
407 M: x86 %alien-cell [] MOV ;
408 M: x86 %alien-float dupd [] MOVSS dup CVTSS2SD ;
409 M: x86 %alien-double [] MOVSD ;
410
411 :: %alien-integer-setter ( ptr value size -- )
412     value { ptr } [| new-value |
413         new-value value ?MOV
414         ptr [] new-value size small-reg MOV
415     ] with-small-register ; inline
416
417 M: x86 %set-alien-integer-1 1 %alien-integer-setter ;
418 M: x86 %set-alien-integer-2 2 %alien-integer-setter ;
419 M: x86 %set-alien-integer-4 4 %alien-integer-setter ;
420 M: x86 %set-alien-cell [ [] ] dip MOV ;
421 M: x86 %set-alien-float dup dup CVTSD2SS [ [] ] dip MOVSS ;
422 M: x86 %set-alien-double [ [] ] dip MOVSD ;
423
424 : load-zone-ptr ( reg -- )
425     #! Load pointer to start of zone array
426     0 MOV "nursery" f rc-absolute-cell rel-dlsym ;
427
428 : load-allot-ptr ( nursery-ptr allot-ptr -- )
429     [ drop load-zone-ptr ] [ swap cell [+] MOV ] 2bi ;
430
431 : inc-allot-ptr ( nursery-ptr n -- )
432     [ cell [+] ] dip 8 align ADD ;
433
434 : store-header ( temp class -- )
435     [ [] ] [ type-number tag-fixnum ] bi* MOV ;
436
437 : store-tagged ( dst tag -- )
438     tag-number OR ;
439
440 M:: x86 %allot ( dst size class nursery-ptr -- )
441     nursery-ptr dst load-allot-ptr
442     dst class store-header
443     dst class store-tagged
444     nursery-ptr size inc-allot-ptr ;
445
446 HOOK: %alien-global cpu ( symbol dll register -- )
447
448 M:: x86 %write-barrier ( src card# table -- )
449     #! Mark the card pointed to by vreg.
450     ! Mark the card
451     card# src MOV
452     card# card-bits SHR
453     "cards_offset" f table %alien-global
454     table card# [+] card-mark <byte> MOV
455
456     ! Mark the card deck
457     card# deck-bits card-bits - SHR
458     "decks_offset" f table %alien-global
459     table card# [+] card-mark <byte> MOV ;
460
461 M: x86 %gc ( -- )
462     "end" define-label
463     temp-reg-1 load-zone-ptr
464     temp-reg-2 temp-reg-1 cell [+] MOV
465     temp-reg-2 1024 ADD
466     temp-reg-1 temp-reg-1 3 cells [+] MOV
467     temp-reg-2 temp-reg-1 CMP
468     "end" get JLE
469     %prepare-alien-invoke
470     "minor_gc" f %alien-invoke
471     "end" resolve-label ;
472
473 HOOK: stack-reg cpu ( -- reg )
474
475 : decr-stack-reg ( n -- )
476     dup 0 = [ drop ] [ stack-reg swap SUB ] if ;
477
478 : incr-stack-reg ( n -- )
479     dup 0 = [ drop ] [ stack-reg swap ADD ] if ;
480
481 M: x86 %epilogue ( n -- ) cell - incr-stack-reg ;
482
483 :: %boolean ( dst temp word -- )
484     dst \ f tag-number MOV
485     temp 0 MOV \ t rc-absolute-cell rel-immediate
486     dst temp word execute ; inline
487
488 M: x86 %compare ( dst temp cc src1 src2 -- )
489     CMP {
490         { cc< [ \ CMOVL %boolean ] }
491         { cc<= [ \ CMOVLE %boolean ] }
492         { cc> [ \ CMOVG %boolean ] }
493         { cc>= [ \ CMOVGE %boolean ] }
494         { cc= [ \ CMOVE %boolean ] }
495         { cc/= [ \ CMOVNE %boolean ] }
496     } case ;
497
498 M: x86 %compare-imm ( dst temp cc src1 src2 -- )
499     %compare ;
500
501 M: x86 %compare-float ( dst temp cc src1 src2 -- )
502     UCOMISD {
503         { cc< [ \ CMOVB %boolean ] }
504         { cc<= [ \ CMOVBE %boolean ] }
505         { cc> [ \ CMOVA %boolean ] }
506         { cc>= [ \ CMOVAE %boolean ] }
507         { cc= [ \ CMOVE %boolean ] }
508         { cc/= [ \ CMOVNE %boolean ] }
509     } case ;
510
511 M: x86 %compare-branch ( label cc src1 src2 -- )
512     CMP {
513         { cc< [ JL ] }
514         { cc<= [ JLE ] }
515         { cc> [ JG ] }
516         { cc>= [ JGE ] }
517         { cc= [ JE ] }
518         { cc/= [ JNE ] }
519     } case ;
520
521 M: x86 %compare-imm-branch ( label src1 src2 cc -- )
522     %compare-branch ;
523
524 M: x86 %compare-float-branch ( label cc src1 src2 -- )
525     UCOMISD {
526         { cc< [ JB ] }
527         { cc<= [ JBE ] }
528         { cc> [ JA ] }
529         { cc>= [ JAE ] }
530         { cc= [ JE ] }
531         { cc/= [ JNE ] }
532     } case ;
533
534 : stack@ ( n -- op ) stack-reg swap [+] ;
535
536 : param@ ( n -- op ) reserved-area-size + stack@ ;
537
538 : spill-integer-base ( stack-frame -- n )
539     [ params>> ] [ return>> ] bi + reserved-area-size + ;
540
541 : spill-integer@ ( n -- op )
542     cells
543     stack-frame get spill-integer-base
544     + stack@ ;
545
546 : spill-float-base ( stack-frame -- n )
547     [ spill-integer-base ]
548     [ spill-counts>> int-regs swap at int-regs reg-size * ]
549     bi + ;
550
551 : spill-float@ ( n -- op )
552     double-float-regs reg-size *
553     stack-frame get spill-float-base
554     + stack@ ;
555
556 M: x86 %spill-integer ( src n -- ) spill-integer@ swap MOV ;
557 M: x86 %reload-integer ( dst n -- ) spill-integer@ MOV ;
558
559 M: x86 %spill-float ( src n -- ) spill-float@ swap MOVSD ;
560 M: x86 %reload-float ( dst n -- ) spill-float@ MOVSD ;
561
562 M: x86 %loop-entry 16 code-alignment [ NOP ] times ;
563
564 M: int-regs %save-param-reg drop [ param@ ] dip MOV ;
565 M: int-regs %load-param-reg drop swap param@ MOV ;
566
567 GENERIC: MOVSS/D ( dst src reg-class -- )
568
569 M: single-float-regs MOVSS/D drop MOVSS ;
570 M: double-float-regs MOVSS/D drop MOVSD ;
571
572 M: float-regs %save-param-reg [ param@ ] 2dip MOVSS/D ;
573 M: float-regs %load-param-reg [ swap param@ ] dip MOVSS/D ;
574
575 GENERIC: push-return-reg ( reg-class -- )
576 GENERIC: load-return-reg ( n reg-class -- )
577 GENERIC: store-return-reg ( n reg-class -- )
578
579 M: x86 %prepare-alien-invoke
580     #! Save Factor stack pointers in case the C code calls a
581     #! callback which does a GC, which must reliably trace
582     #! all roots.
583     "stack_chain" f temp-reg-1 %alien-global
584     temp-reg-1 [] stack-reg MOV
585     temp-reg-1 [] cell SUB
586     temp-reg-1 2 cells [+] ds-reg MOV
587     temp-reg-1 3 cells [+] rs-reg MOV ;
588
589 M: x86 value-struct? drop t ;
590
591 M: x86 small-enough? ( n -- ? )
592     HEX: -80000000 HEX: 7fffffff between? ;
593
594 : next-stack@ ( n -- operand )
595     #! nth parameter from the next stack frame. Used to box
596     #! input values to callbacks; the callback has its own
597     #! stack frame set up, and we want to read the frame
598     #! set up by the caller.
599     stack-frame get total-size>> + stack@ ;