1 ! Copyright (C) 2020 Doug Coleman.
2 ! Copyright (C) 2023 Giftpflanze.
3 ! See https://factorcode.org/license.txt for BSD license.
4 USING: bootstrap.image.private compiler.codegen.relocation
5 compiler.constants compiler.units cpu.arm.64.assembler
6 generic.single.private kernel kernel.private layouts
7 locals.backend math math.private namespaces slots.private
8 strings.private threads.private vocabs ;
9 IN: bootstrap.assembler.arm
15 ! X0-X17 volatile scratch registers
16 ! X0-X8 parameter registers
18 ! X16-X17 intra-procedure-call registers
19 ! X18-X29 non-volatile scratch registers
20 ! X18 platform register (TEB pointer under Windows)
21 ! X29/FP frame pointer
22 ! X30/LR non-volatile link register
24 : words ( n -- n ) 4 * ; inline
25 : stack-frame-size ( -- n ) 8 bootstrap-cells ; inline
27 : return-reg ( -- reg ) X0 ; inline
28 : arg1 ( -- reg ) X0 ; inline
29 : arg2 ( -- reg ) X1 ; inline
30 : arg3 ( -- reg ) X2 ; inline
31 : arg4 ( -- reg ) X3 ; inline
33 : temp0 ( -- reg ) X9 ; inline
34 : temp1 ( -- reg ) X10 ; inline
35 : temp2 ( -- reg ) X11 ; inline
36 : temp3 ( -- reg ) X12 ; inline
37 : pic-tail-reg ( -- reg ) X12 ; inline
39 : stack-reg ( -- reg ) SP ; inline
40 : link-reg ( -- reg ) X30 ; inline ! LR
41 : stack-frame-reg ( -- reg ) X29 ; inline ! FP
42 : vm-reg ( -- reg ) X28 ; inline
43 : ds-reg ( -- reg ) X27 ; inline
44 : rs-reg ( -- reg ) X26 ; inline
45 : ctx-reg ( -- reg ) X25 ; inline
47 : push-link-reg ( -- ) -16 stack-reg link-reg STRpre ;
48 : pop-link-reg ( -- ) 16 stack-reg link-reg LDRpost ;
50 : load0 ( -- ) 0 ds-reg temp0 LDRuoff ;
51 : load1 ( -- ) -8 ds-reg temp1 LDUR ;
52 : load2 ( -- ) -16 ds-reg temp2 LDUR ;
53 : load1/0 ( -- ) -8 ds-reg temp0 temp1 LDPsoff ;
54 : load2/1 ( -- ) -16 ds-reg temp1 temp2 LDPsoff ;
55 : load2/1* ( -- ) -8 ds-reg temp1 temp2 LDPsoff ;
56 : load3/2 ( -- ) -24 ds-reg temp2 temp3 LDPsoff ;
57 : load-arg1/2 ( -- ) -8 ds-reg arg2 arg1 LDPsoff ;
59 : ndrop ( n -- ) bootstrap-cells ds-reg dup SUBi ;
61 : pop0 ( -- ) -8 ds-reg temp0 LDRpost ;
62 : popr ( -- ) -8 rs-reg temp0 LDRpost ;
63 : pop-arg1 ( -- ) -8 ds-reg arg1 LDRpost ;
64 : pop-arg2 ( -- ) -8 ds-reg arg2 LDRpost ;
66 : push0 ( -- ) 8 ds-reg temp0 STRpre ;
67 : push1 ( -- ) 8 ds-reg temp1 STRpre ;
68 : push2 ( -- ) 8 ds-reg temp2 STRpre ;
69 : push3 ( -- ) 8 ds-reg temp3 STRpre ;
70 : pushr ( -- ) 8 rs-reg temp0 STRpre ;
71 : push-arg2 ( -- ) 8 ds-reg arg2 STRpre ;
73 : push-down0 ( n -- ) neg bootstrap-cells ds-reg temp0 STRpre ;
74 : push-down-arg3 ( -- ) -8 ds-reg arg3 STRpre ;
76 : store0 ( -- ) 0 ds-reg temp0 STRuoff ;
77 : store1 ( -- ) 0 ds-reg temp1 STRuoff ;
78 : store0/1 ( -- ) -8 ds-reg temp1 temp0 STPsoff ;
79 : store0/2 ( -- ) -8 ds-reg temp2 temp0 STPsoff ;
80 : store1/0 ( -- ) -8 ds-reg temp0 temp1 STPsoff ;
81 : store1/2 ( -- ) -16 ds-reg temp2 temp1 STPsoff ;
83 :: tag ( reg -- ) tag-bits get reg reg LSLi ;
84 :: untag ( reg -- ) tag-bits get reg reg ASRi ;
85 : tagged>offset0 ( -- ) 1 temp0 temp0 ASRi ;
87 : >r ( -- ) pop0 pushr ;
88 : r> ( -- ) popr push0 ;
90 : absolute-jump ( -- word class )
93 NOP NOP f rc-absolute-cell ; inline
95 : absolute-call ( -- word class )
101 NOP NOP f rc-absolute-cell ; inline
104 ! ! pic-tail-reg 5 [RIP+] LEA
105 ! why do we store the address after JMP in EBX, where is it
108 ! ! 0 JMP f rc-relative rel-word-pic-tail
109 ! 0 Br f rc-relative-arm64-branch rel-word-pic-tail
110 absolute-jump rel-word-pic-tail
111 ] JIT-WORD-JUMP jit-define
114 ! ! 0 CALL f rc-relative rel-word-pic
116 ! 0 BL f rc-relative-arm64-branch rel-word-pic
118 absolute-call rel-word-pic
119 ] JIT-WORD-CALL jit-define
121 : jit-call ( name -- )
122 ! RAX 0 MOV f rc-absolute-cell rel-dlsym
124 absolute-call rel-dlsym ;
126 :: jit-call-1arg ( arg1s name -- )
132 :: jit-call-2arg ( arg1s arg2s name -- )
140 : jit-load-vm ( -- ) ;
142 : jit-load-context ( -- )
143 ! ctx-reg vm-reg vm-context-offset [+] MOV ;
144 vm-context-offset vm-reg ctx-reg LDRuoff ;
146 : jit-save-context ( -- )
148 ! The reason for -8 I think is because we are anticipating a CALL
149 ! instruction. After the call instruction, the contexts frame_top
150 ! will point to the origin jump address.
152 ! ctx-reg context-callstack-top-offset [+] R11 MOV
153 stack-reg temp0 MOVsp
155 context-callstack-top-offset ctx-reg temp0 STRuoff
156 ! ctx-reg context-datastack-offset [+] ds-reg MOV
157 ! ctx-reg context-retainstack-offset [+] rs-reg MOV ;
158 context-datastack-offset ctx-reg ds-reg STRuoff
159 context-retainstack-offset ctx-reg rs-reg STRuoff ;
161 ! ctx-reg must already have been loaded
162 : jit-restore-context ( -- )
163 ! ds-reg ctx-reg context-datastack-offset [+] MOV
164 ! rs-reg ctx-reg context-retainstack-offset [+] MOV ;
165 context-datastack-offset ctx-reg ds-reg LDRuoff
166 context-retainstack-offset ctx-reg rs-reg LDRuoff ;
169 ! ! ctx-reg is preserved across the call because it is non-volatile
172 ! ! call the primitive
174 ! RAX 0 MOV f f rc-absolute-cell rel-dlsym
179 ] JIT-PRIMITIVE jit-define
181 : jit-jump-quot ( -- )
182 ! arg1 quot-entry-point-offset [+] JMP ;
183 quot-entry-point-offset arg1 temp0 LDUR
186 : jit-call-quot ( -- )
187 ! arg1 quot-entry-point-offset [+] CALL ;
189 quot-entry-point-offset arg1 temp0 LDUR
194 ! temp2 0 MOV f rc-absolute-cell rel-literal
199 NOP NOP f rc-absolute-cell rel-literal
200 ] PIC-CHECK-TUPLE jit-define
202 ! Inline cache miss entry points
203 : jit-load-return-address ( -- )
204 ! RBX RSP stack-frame-size bootstrap-cell - [+] MOV ;
205 stack-frame-size bootstrap-cell - stack-reg arg1 LDRuoff ;
207 ! These are always in tail position with an existing stack
208 ! frame, and the stack. The frame setup takes this into account.
209 : jit-inline-cache-miss ( -- )
214 ! RAX 0 MOV rc-absolute-cell rel-inline-cache-miss
216 absolute-call nip rel-inline-cache-miss
218 jit-restore-context ;
220 [ jit-load-return-address jit-inline-cache-miss ] [
228 ] \ inline-cache-miss define-combinator-primitive
230 [ jit-inline-cache-miss ] [
238 ] \ inline-cache-miss-tail define-combinator-primitive
241 : jit-switch-context ( reg -- )
242 ! ! Push a bogus return address so the GC can track this frame back
247 ! ! Make the new context the current one
249 ! vm-reg vm-context-offset [+] ctx-reg MOV
251 vm-context-offset vm-reg ctx-reg STRuoff
253 ! ! Load new stack pointer
254 ! RSP ctx-reg context-callstack-top-offset [+] MOV
255 context-callstack-top-offset ctx-reg temp0 LDRuoff
256 temp0 stack-reg MOVsp
258 ! ! Load new ds, rs registers
261 ctx-reg jit-update-tib ;
263 : jit-pop-context-and-param ( -- )
265 ! arg1 arg1 alien-offset [+] MOV
266 ! arg2 ds-reg -8 [+] MOV
269 alien-offset arg1 arg1 ADDi
273 : jit-push-param ( -- )
275 ! ds-reg [] arg2 MOV ;
278 : jit-set-context ( -- )
279 jit-pop-context-and-param
281 arg1 jit-switch-context
283 16 stack-reg stack-reg ADDi
286 : jit-pop-quot-and-param ( -- )
288 ! arg2 ds-reg -8 [+] MOV
292 : jit-start-context ( -- )
293 ! Create the new context in return-reg. Have to save context
294 ! twice, first before calling new_context() which may GC,
295 ! and again after popping the two parameters from the stack.
297 vm-reg "new_context" jit-call-1arg
299 jit-pop-quot-and-param
301 return-reg jit-switch-context
305 : jit-delete-current-context ( -- )
306 vm-reg "delete_context" jit-call-1arg ;
308 ! Resets the active context and instead the passed in quotation
309 ! becomes the new code that it executes.
310 : jit-start-context-and-delete ( -- )
311 ! Updates the context to match the values in the data and retain
312 ! stack registers. reset_context can GC.
315 ! Resets the context. The top two ds items are preserved.
316 vm-reg "reset_context" jit-call-1arg
318 ! Switches to the same context I think.
319 ctx-reg jit-switch-context
321 ! Pops the quotation from the stack and puts it in arg1.
326 ! Jump to quotation arg1
330 ! 0 [RIP+] EAX MOV rc-relative rel-safepoint
334 NOP NOP rc-absolute-cell rel-safepoint
335 ] JIT-SAFEPOINT jit-define
337 ! C to Factor entry point
340 ! ! Optimizing compiler's side of callback accesses
341 ! ! arguments that are on the stack via the frame pointer.
342 ! ! On x86-32 fastcall, and x86-64, some arguments are passed
343 ! ! in registers, and so the only registers that are safe for
344 ! ! use here are frame-reg, nv-reg and vm-reg.
346 ! frame-reg stack-reg MOV
348 ! ! Save all non-volatile registers
349 ! nv-regs [ PUSH ] each
350 -16 SP X19 X18 STPpre
351 -16 SP X21 X20 STPpre
352 -16 SP X23 X22 STPpre
353 -16 SP X25 X24 STPpre
354 -16 SP X27 X26 STPpre
355 -16 SP X29 X28 STPpre
357 stack-reg stack-frame-reg MOVsp
361 ! ! Load VM into vm-reg
362 ! vm-reg 0 MOV 0 rc-absolute-cell rel-vm
365 NOP NOP 0 rc-absolute-cell rel-vm
368 ! nv-reg vm-reg vm-context-offset [+] MOV
370 vm-context-offset vm-reg ctx-reg LDRuoff
373 ! ! Switch over to the spare context
374 ! nv-reg vm-reg vm-spare-context-offset [+] MOV
375 ! vm-reg vm-context-offset [+] nv-reg MOV
376 vm-spare-context-offset vm-reg ctx-reg LDRuoff
377 vm-context-offset vm-reg ctx-reg STRuoff
379 ! ! Save C callstack pointer
380 ! nv-reg context-callstack-save-offset [+] stack-reg MOV
382 stack-reg temp0 MOVsp
383 context-callstack-save-offset ctx-reg temp0 STRuoff
384 ! stack-reg X24 MOVsp
387 ! ! Load Factor stack pointers
388 ! stack-reg nv-reg context-callstack-bottom-offset [+] MOV
389 context-callstack-bottom-offset ctx-reg temp0 LDRuoff
390 temp0 stack-reg MOVsp
392 ctx-reg jit-update-tib
395 ! rs-reg nv-reg context-retainstack-offset [+] MOV
396 ! ds-reg nv-reg context-datastack-offset [+] MOV
397 context-retainstack-offset ctx-reg rs-reg LDRuoff
398 context-datastack-offset ctx-reg ds-reg LDRuoff
400 ! ! Call into Factor code
401 ! link-reg 0 MOV f rc-absolute-cell rel-word
406 NOP NOP f rc-absolute-cell rel-word
408 ! ! Load C callstack pointer
409 ! nv-reg vm-reg vm-context-offset [+] MOV
410 ! stack-reg nv-reg context-callstack-save-offset [+] MOV
411 vm-context-offset vm-reg ctx-reg LDRuoff
413 context-callstack-save-offset ctx-reg temp0 LDRuoff
414 temp0 stack-reg MOVsp
415 ! X24 stack-reg MOVsp
420 ! vm-reg vm-context-offset [+] nv-reg MOV
422 vm-context-offset vm-reg ctx-reg STRuoff
426 ! ! Restore non-volatile registers
427 ! nv-regs <reversed> [ POP ] each
430 16 SP X29 X28 LDPpost
431 16 SP X27 X26 LDPpost
432 16 SP X25 X24 LDPpost
433 16 SP X23 X22 LDPpost
434 16 SP X21 X20 LDPpost
435 16 SP X19 X18 LDPpost
437 ! ! Callbacks which return structs, or use stdcall/fastcall/thiscall,
438 ! ! need a parameter here.
440 ! ! See the comment for M\ x86.32 stack-cleanup in cpu.x86.32
441 ! 0xffff RET f rc-absolute-2 rel-untagged
443 2 temp0 temp0 LDRHuoff
444 temp0 stack-reg stack-reg ADDr
446 NOP f rc-absolute-2 rel-untagged
447 ] CALLBACK-STUB jit-define
451 ! temp0 0 MOV f rc-absolute-cell rel-literal
454 NOP NOP f rc-absolute-cell rel-literal
455 ! ! increment datastack pointer
456 ! ds-reg bootstrap-cell ADD
457 ! ! store literal on datastack
458 ! ds-reg [] temp0 MOV
460 ] JIT-PUSH-LITERAL jit-define
462 ! The *-signal-handler subprimitives are special-cased in vm/quotations.cpp
463 ! not to trigger generation of a stack frame, so they can
464 ! perform their own prolog/epilog preserving registers.
466 ! It is important that the total is 192/64 and that it matches the
467 ! constants in vm/cpu-x86.*.hpp
468 : jit-signal-handler-prolog ( -- )
469 ! ! Return address already on stack -> 8/4 bytes.
471 ! ! Push all registers. 15 regs/120 bytes on 64bit, 7 regs/28 bytes
472 ! ! on 32bit -> 128/32 bytes.
473 ! signal-handler-save-regs [ PUSH ] each
475 ! ! Push flags -> 136/36 bytes
482 -16 SP X11 X10 STPpre
483 -16 SP X13 X12 STPpre
484 -16 SP X15 X14 STPpre
485 -16 SP X17 X16 STPpre
486 -16 SP X19 X18 STPpre
487 -16 SP X21 X20 STPpre
488 -16 SP X23 X22 STPpre
489 -16 SP X25 X24 STPpre
490 -16 SP X27 X26 STPpre
491 -16 SP X29 X28 STPpre
495 ! ! Register parameter area 32 bytes, unused on platforms other than
496 ! ! windows 64 bit, but including it doesn't hurt. Plus
497 ! ! alignment. LEA used so we don't dirty flags -> 192/64 bytes.
498 ! stack-reg stack-reg 7 bootstrap-cells neg [+] LEA
499 4 bootstrap-cells stack-reg stack-reg SUBi
503 : jit-signal-handler-epilog ( -- )
504 ! stack-reg stack-reg 7 bootstrap-cells [+] LEA
506 ! signal-handler-save-regs reverse [ POP ] each ;
509 16 SP X29 X28 LDPpost
510 16 SP X27 X26 LDPpost
511 16 SP X25 X24 LDPpost
512 16 SP X23 X22 LDPpost
513 16 SP X21 X20 LDPpost
514 16 SP X19 X18 LDPpost
515 16 SP X17 X16 LDPpost
516 16 SP X15 X14 LDPpost
517 16 SP X13 X12 LDPpost
518 16 SP X11 X10 LDPpost
523 16 SP X1 X0 LDPpost ;
527 ! temp0 ds-reg [] MOV
529 ! ds-reg bootstrap-cell SUB
531 ! ! compare boolean with f
532 ! temp0 \ f type-number CMP
533 \ f type-number temp0 CMPi
534 ! ! jump to true branch if not equal
535 ! ! 0 JNE f rc-relative rel-word
536 ! 0 NE B.cond f rc-relative-arm64-bcond rel-word
538 absolute-jump rel-word
539 ! ! jump to false branch if equal
540 ! ! 0 JMP f rc-relative rel-word
541 ! 0 Br f rc-relative-arm64-branch rel-word
542 absolute-jump rel-word
547 ! ! 0 CALL f rc-relative rel-word
549 ! 0 Br f rc-relative-arm64-branch rel-word
551 absolute-call rel-word
557 ! ! 0 CALL f rc-relative rel-word
559 ! 0 Br f rc-relative-arm64-branch rel-word
561 absolute-call rel-word
563 ] JIT-2DIP jit-define
567 ! ! 0 CALL f rc-relative rel-word
569 ! 0 Br f rc-relative-arm64-branch rel-word
571 absolute-call rel-word
573 ] JIT-3DIP jit-define
577 ! temp0 ds-reg [] MOV
579 ! ds-reg bootstrap-cell SUB
582 ! temp0 word-entry-point-offset [+] CALL
587 ! temp0 word-entry-point-offset [+] JMP
589 ] \ (execute) define-combinator-primitive
592 ! temp0 ds-reg [] MOV
593 ! ds-reg bootstrap-cell SUB
595 ! temp0 word-entry-point-offset [+] JMP
596 word-entry-point-offset temp0 temp0 ADDi
598 ] JIT-EXECUTE jit-define
600 ! https://elixir.bootlin.com/linux/latest/source/arch/arm64/kernel/stacktrace.c#L22
602 ! ! make room for LR plus magic number of callback, 16byte align
603 ! x64 ! stack-reg stack-frame-size bootstrap-cell - SUB
604 stack-frame-size stack-reg stack-reg SUBi
606 ] JIT-PROLOG jit-define
609 ! x64 ! stack-reg stack-frame-size bootstrap-cell - ADD
611 stack-frame-size stack-reg stack-reg ADDi
612 ] JIT-EPILOG jit-define
614 [ f RET ] JIT-RETURN jit-define
616 ! ! ! Polymorphic inline caches
618 ! The PIC stubs are not permitted to touch pic-tail-reg.
620 ! Load a value from a stack position
622 ! temp1 ds-reg 0x7f [+] MOV f rc-absolute-1 rel-untagged
624 3 temp2 temp2 LDRBuoff
625 temp2 ds-reg temp1 LDRr
627 NOP f rc-absolute-1 rel-untagged
628 ] PIC-LOAD jit-define
631 ! temp1/32 tag-mask get AND
632 tag-mask get temp1 temp1 ANDi
638 ! temp1/32 tag-mask get AND
639 tag-mask get temp1 temp1 ANDi
640 ! temp1/32 tuple type-number CMP
641 tuple type-number temp1 CMPi
643 ! [ temp1 temp0 tuple-class-offset [+] MOV ]
645 tuple-class-offset temp0 temp1 LDUR
647 ] PIC-TUPLE jit-define
650 ! temp1/32 0x7f CMP f rc-absolute-1 rel-untagged
652 3 temp2 temp2 LDRBuoff
655 NOP f rc-absolute-1 rel-untagged
656 ] PIC-CHECK-TAG jit-define
659 ! ! 0 JE f rc-relative rel-word
660 ! 0 EQ B.cond f rc-relative-arm64-bcond rel-word
662 absolute-jump rel-word
665 ! ! ! Megamorphic caches
671 ! temp1/32 tag-mask get AND
672 tag-mask get temp1 temp1 ANDi
673 ! temp1/32 tag-bits get SHL
675 ! temp1/32 tuple type-number tag-fixnum CMP
676 tuple type-number tag-fixnum temp1 CMPi
678 ! [ temp1 temp0 tuple-class-offset [+] MOV ]
680 tuple-class-offset temp0 temp1 LDUR
683 ! temp0 0 MOV f rc-absolute-cell rel-literal
686 NOP NOP f rc-absolute-cell rel-literal
687 ! ! key = hashcode(class)
690 ! ! key &= cache.length - 1
691 ! temp2 mega-cache-size get 1 - bootstrap-cell * AND
692 mega-cache-size get 1 - bootstrap-cells temp2 temp2 ANDi
693 ! ! cache += array-start-offset
694 ! temp0 array-start-offset ADD
695 array-start-offset temp0 temp0 ADDi
698 temp2 temp0 temp0 ADDr
699 ! ! if(get(cache) == class)
701 0 temp0 temp2 LDRuoff
705 ! ! megamorphic_cache_hits++
706 ! temp1 0 MOV rc-absolute-cell rel-megamorphic-cache-hits
709 NOP NOP rc-absolute-cell rel-megamorphic-cache-hits
713 ! ! goto get(cache + bootstrap-cell)
714 ! temp0 temp0 bootstrap-cell [+] MOV
715 bootstrap-cell temp0 temp0 LDRuoff
716 ! temp0 word-entry-point-offset [+] JMP
717 word-entry-point-offset temp0 temp0 ADDi
719 ! ! fall-through on miss
721 ] MEGA-LOOKUP jit-define
724 : jit-compare ( cond -- )
726 ! temp3 0 MOV t rc-absolute-cell rel-literal
729 NOP NOP t rc-absolute-cell rel-literal
731 ! temp1 \ f type-number MOV
732 \ f type-number temp2 MOVwi
734 ! temp0 ds-reg [] MOV
735 ! ! adjust stack pointer
736 ! ds-reg bootstrap-cell SUB
738 ! ! compare with second value
739 ! ds-reg [] temp0 CMP
742 ! [ temp1 temp3 ] dip execute( dst src -- )
743 [ temp2 temp3 temp0 ] dip CSEL
745 ! ds-reg [] temp1 MOV
750 ! Overflowing fixnum arithmetic
751 : jit-overflow ( insn func -- )
755 ! arg2 ds-reg 8 [+] MOV
758 ! [ [ arg3 arg2 ] dip call ] dip
759 [ [ arg2 arg1 arg3 ] dip call ] dip
767 ] jit-conditional ; inline
769 : jit-math ( insn -- )
770 ! ! load second input
771 ! temp0 ds-reg [] MOV
773 ! ds-reg bootstrap-cell SUB
776 ! [ ds-reg [] temp0 ] dip execute( dst src -- )
777 [ temp0 temp1 temp0 ] dip execute( arg2 arg1 dst -- )
780 : jit-fixnum-/mod ( -- )
781 ! ! load second parameter
782 ! temp1 ds-reg [] MOV
783 ! ! load first parameter
784 ! div-arg ds-reg bootstrap-cell neg [+] MOV
787 temp0 temp1 temp2 SDIV
788 temp1 temp0 temp2 temp0 MSUB ;
790 ! # All arm.64 subprimitives
793 { (set-context) [ jit-set-context ] }
794 { (set-context-and-delete) [
795 jit-delete-current-context
798 { (start-context) [ jit-start-context ] }
799 { (start-context-and-delete) [ jit-start-context-and-delete ] }
804 vm-reg "begin_callback" jit-call-1arg
808 vm-reg "end_callback" jit-call-1arg
810 { unwind-native-frames [
811 ! ! unwind-native-frames is marked as "special" in
812 ! ! vm/quotations.cpp so it does not have a standard prolog
813 ! ! Unwind stack frames
816 ! ! Load VM pointer into vm-reg, since we're entering from
818 ! vm-reg 0 MOV 0 rc-absolute-cell rel-vm
821 NOP NOP 0 rc-absolute-cell rel-vm
822 ! ! Load ds and rs registers
825 ! ! Clear the fault flag
826 ! vm-reg vm-fault-flag-offset [+] 0 MOV
827 vm-fault-flag-offset vm-reg XZR STRuoff
833 { fixnum+ [ [ ADDr ] "overflow_fixnum_add" jit-overflow ] }
834 { fixnum- [ [ SUBr ] "overflow_fixnum_subtract" jit-overflow ] }
839 ! RBX ds-reg 8 [+] MOV
841 ! RBX tag-bits get SAR
845 ! RAX * RBX = RDX:RAX
846 temp1 temp0 temp0 MUL
853 ! arg1 tag-bits get SAR
859 "overflow_fixnum_multiply" jit-call
875 ! RSP [] arg1 16-bit-version-of MOV
881 ! ! Load callstack object
883 ! ds-reg bootstrap-cell SUB
885 ! ! Get ctx->callstack_bottom
887 ! arg1 ctx-reg context-callstack-bottom-offset [+] MOV
888 context-callstack-bottom-offset ctx-reg arg1 LDRuoff
889 ! ! Get top of callstack object -- 'src' for memcpy
890 ! arg2 arg4 callstack-top-offset [+] LEA
891 callstack-top-offset temp0 arg2 ADDi
892 ! ! Get callstack length, in bytes --- 'len' for memcpy
893 ! arg3 arg4 callstack-length-offset [+] MOV
894 2 temp0 temp0 SUBi ! callstack-length-offset
896 ! arg3 tag-bits get SHR
897 tag-bits get arg3 arg3 LSRi
898 ! ! Compute new stack pointer -- 'dst' for memcpy
901 ! ! Install new stack pointer
904 ! ! Call memcpy; arguments are now in the correct registers
905 ! ! Create register shadow area for Win64
907 32 stack-reg stack-reg SUBi
908 "factor_memcpy" jit-call
909 ! ! Tear down register shadow area
911 32 stack-reg stack-reg ADDi
912 ! ! Return with new callstack
920 { fixnum+fast [ \ ADDr jit-math ] }
922 ! ### Bit manipulation
923 { fixnum-bitand [ \ ANDr jit-math ] }
930 ! ds-reg [] tag-mask get XOR
931 tag-mask get temp0 temp0 EORi
934 { fixnum-bitor [ \ ORRr jit-math ] }
935 { fixnum-bitxor [ \ EORr jit-math ] }
936 { fixnum-shift-fast [
938 ! shift-arg ds-reg [] MOV
939 ! ! adjust stack pointer
940 ! ds-reg bootstrap-cell SUB
942 ! temp3 ds-reg [] MOV
944 ! ! untag shift count
945 ! shift-arg tag-bits get SAR
950 ! ! compute positive shift value in temp2
952 temp0 temp1 temp1 LSLr
953 ! ! compute negative shift value in temp3
957 temp0 temp2 temp2 ASRr
958 ! temp3 tag-mask get bitnot AND
959 tag-mask get bitnot temp2 temp2 ANDi
960 ! ! if shift count was negative, move temp3 to temp2
963 temp2 temp1 temp0 PL CSEL
965 ! ds-reg [] temp2 MOV
971 ! temp0 ds-reg [] MOV
972 ! ds-reg bootstrap-cell SUB
975 temp1 temp0 temp0 ORRr
976 ! temp0 tag-mask get TEST
977 tag-mask get temp0 TSTi
978 ! temp0 \ f type-number MOV
979 \ f type-number temp0 MOVwi
980 ! temp1 1 tag-fixnum MOV
981 1 tag-fixnum temp1 MOVwi
983 temp0 temp1 temp0 EQ CSEL
984 ! ds-reg [] temp0 MOV
987 { eq? [ EQ jit-compare ] }
988 { fixnum> [ GT jit-compare ] }
989 { fixnum>= [ GE jit-compare ] }
990 { fixnum< [ LT jit-compare ] }
991 { fixnum<= [ LE jit-compare ] }
996 ! ! adjust stack pointer
997 ! ds-reg bootstrap-cell SUB
999 ! ds-reg [] mod-arg MOV
1004 ! ! adjust stack pointer
1005 ! ds-reg bootstrap-cell SUB
1007 ! div-arg tag-bits get SHL
1008 tag-bits get temp2 temp0 LSLi
1010 ! ds-reg [] div-arg MOV
1016 ! div-arg tag-bits get SHL
1019 ! ds-reg [] mod-arg MOV
1020 ! ds-reg bootstrap-cell neg [+] div-arg MOV
1026 ! ! load second input
1027 ! temp0 ds-reg [] MOV
1029 ! ds-reg bootstrap-cell SUB
1030 ! ! load first input
1031 ! temp1 ds-reg [] MOV
1033 ! ! untag second input
1034 ! temp0 tag-bits get SAR
1038 temp1 temp0 temp0 MUL
1040 ! ds-reg [] temp0 MOV
1045 { fixnum-fast [ \ SUBr jit-math ] }
1049 ! ! load local count
1050 ! temp0 ds-reg [] MOV
1051 ! ! adjust stack pointer
1052 ! ds-reg bootstrap-cell SUB
1054 ! ! turn local number into offset
1056 ! ! decrement retain stack pointer
1058 temp0 rs-reg rs-reg SUBr
1061 ! ! load local number
1062 ! temp0 ds-reg [] MOV
1064 ! ! turn local number into offset
1066 ! ! load local value
1067 ! temp0 rs-reg temp0 [+] MOV
1068 temp0 rs-reg temp0 LDRr
1070 ! ds-reg [] temp0 MOV
1073 { load-local [ >r ] }
1077 ! ! load slot number
1078 ! temp0 ds-reg [] MOV
1079 ! ! adjust stack pointer
1080 ! ds-reg bootstrap-cell SUB
1082 ! temp1 ds-reg [] MOV
1084 ! ! turn slot number into offset
1087 ! temp1 tag-bits get SHR
1088 ! temp1 tag-bits get SHL
1089 tag-mask get bitnot temp1 temp1 ANDi
1091 ! temp0 temp1 temp0 [+] MOV
1092 temp1 temp0 temp0 LDRr
1094 ! ds-reg [] temp0 MOV
1098 ! ! load string index from stack
1099 ! temp0 ds-reg bootstrap-cell neg [+] MOV
1100 ! temp0 tag-bits get SHR
1101 ! ! load string from stack
1102 ! temp1 ds-reg [] MOV
1105 ! temp0 8-bit-version-of temp0 temp1 string-offset [++] MOV
1106 ! temp0 temp0 8-bit-version-of MOVZX
1107 ! temp0 tag-bits get SHL
1108 temp1 temp0 temp0 LDRBr
1110 ! ! store character to stack
1111 ! ds-reg bootstrap-cell SUB
1112 ! ds-reg [] temp0 MOV
1117 ! temp0 ds-reg [] MOV
1120 ! temp0/32 tag-mask get AND
1121 tag-mask get temp0 temp0 ANDi
1123 ! temp0/32 tag-bits get SHL
1126 ! ds-reg [] temp0 MOV
1133 { drop [ 1 ndrop ] }
1134 { 2drop [ 2 ndrop ] }
1135 { 3drop [ 3 ndrop ] }
1136 { 4drop [ 4 ndrop ] }
1139 { dup [ load0 push0 ] }
1140 { 2dup [ load1/0 push1 push0 ] }
1141 { 3dup [ load2 load1/0 push2 push1 push0 ] }
1142 { 4dup [ load3/2 load1/0 push3 push2 push1 push0 ] }
1143 { dupd [ load1/0 store1 push0 ] }
1145 ! ! ### Misc shufflers
1146 { over [ load1 push1 ] }
1147 { pick [ load2 push2 ] }
1150 { nip [ load0 1 push-down0 ] }
1151 { 2nip [ load0 2 push-down0 ] }
1154 { -rot [ pop0 load2/1* store0/2 push1 ] }
1155 { rot [ pop0 load2/1* store1/0 push2 ] }
1156 { swap [ load1/0 store0/1 ] }
1157 { swapd [ load2/1 store1/2 ] }
1159 ! ## Signal handling
1160 { leaf-signal-handler [
1161 jit-signal-handler-prolog
1163 ! temp0 vm-reg vm-signal-handler-addr-offset [+] MOV
1165 vm-signal-handler-addr-offset vm-reg temp0 LDRuoff
1167 jit-signal-handler-epilog
1168 ! Pop the fake leaf frame along with our return address
1169 ! leaf-stack-frame-size bootstrap-cell - RET
1170 leaf-stack-frame-size bootstrap-cell - SP SP ADDi
1174 jit-signal-handler-prolog
1176 ! temp0 vm-reg vm-signal-handler-addr-offset [+] MOV
1178 vm-signal-handler-addr-offset vm-reg temp0 LDRuoff
1180 jit-signal-handler-epilog
1184 } define-sub-primitives
1186 [ "bootstrap.arm.64" forget-vocab ] with-compilation-unit