1 ! Copyright (C) 2020 Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: bootstrap.image.private compiler.codegen.relocation
4 compiler.constants compiler.units cpu.arm.assembler
5 cpu.arm.assembler.opcodes generic.single.private
6 kernel kernel.private layouts locals.backend
7 math math.private memory namespaces sequences slots.private
8 strings.private threads.private vocabs ;
9 IN: bootstrap.assembler.arm
16 ! https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=vs-2019
18 ! x0 Volatile Parameter/scratch register 1, result register
19 ! x1-x7 Volatile Parameter/scratch register 2-8
20 ! x8-x15 Volatile Scratch registers
21 ! x16-x17 Volatile Intra-procedure-call scratch registers
22 ! x18 Non-volatile Platform register: in kernel mode, points to KPCR for the current processor;
23 ! in user mode, points to TEB
24 ! x19-x28 Non-volatile Scratch registers
25 ! x29/fp Non-volatile Frame pointer
26 ! x30/lr Non-volatile Link registers
28 ! varargs https://developer.arm.com/documentation/ihi0055/d/?lang=en
29 : stack-frame-size ( -- n ) 8 bootstrap-cells ;
30 : volatile-regs ( -- seq ) { X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 } ;
31 ! windows arm - X18 is non-volatile https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-160
32 : nv-regs ( -- seq ) { X18 X19 X20 X21 X22 X23 X24 X25 X26 X27 X28 X29 X30 } ;
34 ! callee-save = non-volatile aka call-preserved
36 ! x30 is the link register (used to return from subroutines)
37 ! x29 is the frame register
38 ! x19 to x29 are callee-saved
39 ! x18 is the 'platform register', used for some operating-system-specific special purpose,
40 ! or an additional caller-saved register
41 ! x16 and x17 are the Intra-Procedure-call scratch register
42 ! x9 to x15: used to hold local variables (caller saved)
43 ! x8: used to hold indirect return value address
44 ! x0 to x7: used to hold argument values passed to a subroutine, and also hold
45 ! results returned from a subroutine
48 ! https://en.wikichip.org/wiki/arm/aarch64
49 ! Generally, X0 through X18 (volatile, can corrupt) while X19-X29 must be preserved (non-volatile)
50 ! Volatile registers' content may change over a subroutine call
51 ! non-volatile register is a type of register with contents that must be preserved over subroutine calls
52 ! Register Role Requirement
53 ! X0 - X7 Parameter/result registers Can Corrupt (volatile)
54 ! X8 Indirect result location register (volatile)
55 ! X9 - X15 Temporary registers (volatile)
56 ! X16 - X17 Intra-procedure call temporary (volatile)
57 ! x16 - syscall reg with SVC instructioin
58 ! X18 Platform register, otherwise temporary, DONT USE (volatile)
60 ! X19 - X29 Callee-saved register Must preserve (non-volatile)
61 ! X29 - frame pointer register, must always be valid
62 ! X30 Link Register LR Can Corrupt
63 ! X31 Stack Pointer SP
64 ! 16-byte stack alignment
66 ! stack walking - {fp, lr} pairs if compiled with frame pointers enabled
68 : arg1 ( -- reg ) X0 ;
69 : arg2 ( -- reg ) X1 ;
70 : arg3 ( -- reg ) X2 ;
71 : arg4 ( -- reg ) X3 ;
74 ! windows arm64: 16 bytes https://devblogs.microsoft.com/oldnewthing/20190111-00/?p=100685
75 ! windows arm32: 8 bytes
77 ! Apple arm64: 128 bytes https://developer.apple.com/documentation/xcode/writing_arm64_code_for_apple_platforms?language=objc
78 : red-zone-size ( -- n ) 16 ; ! 16 bytes on windows, or 128 bytes on linux? or 0?
80 ! no red zone on x86/x64 windows
83 ! https://github.com/MicrosoftDocs/cpp-docs/blob/master/docs/build/arm64-windows-abi-conventions.md
85 : shift-arg ( -- reg ) X1 ;
86 : div-arg ( -- reg ) X0 ;
87 : mod-arg ( -- reg ) X2 ;
89 ! caller-saved registers X9-X15
90 ! callee-saved registers X19-X29
91 : temp0 ( -- reg ) X9 ;
92 : temp1 ( -- reg ) X10 ;
93 : temp2 ( -- reg ) X11 ;
94 : temp3 ( -- reg ) X12 ;
97 ! : pic-tail-reg ( -- reg ) RBX ;
98 : return-reg ( -- reg ) X0 ;
99 : stack-reg ( -- reg ) SP ;
100 ! https://developer.arm.com/documentation/dui0801/a/Overview-of-AArch64-state/Link-registers
101 : link-reg ( -- reg ) X30 ; ! LR
102 : stack-frame-reg ( -- reg ) X29 ; ! FP
103 : vm-reg ( -- reg ) X28 ;
104 : ds-reg ( -- reg ) X27 ;
105 : rs-reg ( -- reg ) X26 ;
106 ! : ctx-reg ( -- reg ) R12 ;
107 ! : fixnum>slot@ ( -- ) temp0 1 SAR ;
108 ! : rex-length ( -- n ) 1 ;
110 ! rc-absolute-cell is just CONSTANT: 0
111 : jit-call ( name -- )
113 f rc-absolute-cell rel-dlsym
115 ! RAX 0 MOV f rc-absolute-cell rel-dlsym
118 :: jit-call-1arg ( arg1s name -- )
124 :: jit-call-2arg ( arg1s arg2s name -- )
133 ! pic-tail-reg 5 [RIP+] LEA
134 ! 0 JMP f rc-relative rel-word-pic-tail
135 ] JIT-WORD-JUMP jit-define
138 ! no-op on x86-64. in factor contexts vm-reg always contains the
142 : jit-load-context ( -- ) ;
143 ! ctx-reg vm-reg vm-context-offset [+] MOV ;
145 : jit-save-context ( -- ) ;
147 ! The reason for -8 I think is because we are anticipating a CALL
148 ! instruction. After the call instruction, the contexts frame_top
149 ! will point to the origin jump address.
151 ! ctx-reg context-callstack-top-offset [+] R11 MOV
152 ! ctx-reg context-datastack-offset [+] ds-reg MOV
153 ! ctx-reg context-retainstack-offset [+] rs-reg MOV ;
155 ! ctx-reg must already have been loaded
156 : jit-restore-context ( -- ) ;
157 ! ds-reg ctx-reg context-datastack-offset [+] MOV
158 ! rs-reg ctx-reg context-retainstack-offset [+] MOV ;
163 ! ! ctx-reg is preserved across the call because it is non-volatile
166 ! ! call the primitive
168 ! RAX 0 MOV f f rc-absolute-cell rel-dlsym
170 ! jit-restore-context
171 ] JIT-PRIMITIVE jit-define
174 : jit-jump-quot ( -- )
175 quot-entry-point-offset arg1 ADR
177 ! arg1 quot-entry-point-offset [+] JMP ;
179 : jit-call-quot ( -- )
180 quot-entry-point-offset arg1 ADR
182 ! arg1 quot-entry-point-offset [+] CALL ;
184 : signal-handler-save-regs ( -- regs ) { } ;
185 ! { RAX RCX RDX RBX RBP RSI RDI R8 R9 R10 R11 R12 R13 R14 R15 } ;
189 ! temp2 0 MOV f rc-absolute-cell rel-literal
191 ] PIC-CHECK-TUPLE jit-define
196 1 bootstrap-cells rs-reg rs-reg ADDi64
197 -1 bootstrap-cells ds-reg rs-reg LDR-post ;
200 1 bootstrap-cells ds-reg ds-reg ADDi64
201 -1 bootstrap-cells rs-reg ds-reg LDR-post ;
204 1 bootstrap-cells rs-reg rs-reg ADDi64
205 -1 bootstrap-cells ds-reg rs-reg LDR-post
206 1 bootstrap-cells rs-reg rs-reg ADDi64
207 -1 bootstrap-cells ds-reg rs-reg LDR-post ;
210 1 bootstrap-cells ds-reg ds-reg ADDi64
211 -1 bootstrap-cells rs-reg ds-reg LDR-post
212 1 bootstrap-cells ds-reg ds-reg ADDi64
213 -1 bootstrap-cells rs-reg ds-reg LDR-post ;
216 1 bootstrap-cells rs-reg rs-reg ADDi64
217 -1 bootstrap-cells ds-reg rs-reg LDR-post
218 1 bootstrap-cells rs-reg rs-reg ADDi64
219 -1 bootstrap-cells ds-reg rs-reg LDR-post
220 1 bootstrap-cells rs-reg rs-reg ADDi64
221 -1 bootstrap-cells ds-reg rs-reg LDR-post ;
224 1 bootstrap-cells ds-reg ds-reg ADDi64
225 -1 bootstrap-cells rs-reg ds-reg LDR-post
226 1 bootstrap-cells ds-reg ds-reg ADDi64
227 -1 bootstrap-cells rs-reg ds-reg LDR-post
228 1 bootstrap-cells ds-reg ds-reg ADDi64
229 -1 bootstrap-cells rs-reg ds-reg LDR-post ;
232 : jit-switch-context ( reg -- ) drop ;
233 ! ! Push a bogus return address so the GC can track this frame back
237 ! ! Make the new context the current one
239 ! vm-reg vm-context-offset [+] ctx-reg MOV
241 ! ! Load new stack pointer
242 ! RSP ctx-reg context-callstack-top-offset [+] MOV
244 ! ! Load new ds, rs registers
245 ! jit-restore-context
247 ! ctx-reg jit-update-tib ;
249 : jit-pop-context-and-param ( -- ) ;
251 ! arg1 arg1 alien-offset [+] MOV
252 ! arg2 ds-reg -8 [+] MOV
255 : jit-push-param ( -- ) ;
257 ! ds-reg [] arg2 MOV ;
259 : jit-set-context ( -- ) ;
260 ! jit-pop-context-and-param
262 ! arg1 jit-switch-context
266 : jit-pop-quot-and-param ( -- ) ;
268 ! arg2 ds-reg -8 [+] MOV
271 : jit-start-context ( -- ) ;
272 ! Create the new context in return-reg. Have to save context
273 ! twice, first before calling new_context() which may GC,
274 ! and again after popping the two parameters from the stack.
276 ! vm-reg "new_context" jit-call-1arg
278 ! jit-pop-quot-and-param
280 ! return-reg jit-switch-context
284 : jit-delete-current-context ( -- ) ;
285 ! vm-reg "delete_context" jit-call-1arg ;
289 ! 0 CALL f rc-relative rel-word
297 ! 0 [RIP+] EAX MOV rc-relative rel-safepoint
298 ] JIT-SAFEPOINT jit-define
300 ! # All arm.64 subprimitives
303 ! Set up the datastack and retainstack registers
304 ! and jump into the quotation
319 ! Rn Rd MOVr64 ! comment
321 vm-reg "begin_callback" jit-call-1arg
323 return-reg arg1 MOVr64 ! arg1 is return
326 vm-reg "end_callback" jit-call-1arg
328 } define-sub-primitives
333 ! { (set-context) [ jit-set-context ] }
334 ! { (set-context-and-delete) [
335 ! jit-delete-current-context
338 ! { (start-context) [ jit-start-context ] }
339 ! { (start-context-and-delete) [ jit-start-context-and-delete ] }
345 ! ! vm-reg "begin_callback" jit-call-1arg
347 ! ! ! call the quotation
348 ! ! arg1 return-reg MOV
351 ! ! vm-reg "end_callback" jit-call-1arg
370 ! ! vm-reg "begin_callback" jit-call-1arg
372 ! ! return-reg arg1 MOVr64 ! arg1 is return
375 ! ! vm-reg "end_callback" jit-call-1arg
380 ! { unwind-native-frames [ ] }
383 ! { fixnum+ [ [ ADD ] "overflow_fixnum_add" jit-overflow ] }
384 ! { fixnum- [ [ SUB ] "overflow_fixnum_subtract" jit-overflow ] }
389 ! RBX ds-reg 8 [+] MOV
390 ! RBX tag-bits get SAR
397 ! arg1 tag-bits get SAR
400 ! "overflow_fixnum_multiply" jit-call
415 ! RSP [] arg1 16-bit-version-of MOV
420 ! ! Load callstack object
422 ! ds-reg bootstrap-cell SUB
423 ! ! Get ctx->callstack_bottom
425 ! arg1 ctx-reg context-callstack-bottom-offset [+] MOV
426 ! ! Get top of callstack object -- 'src' for memcpy
427 ! arg2 arg4 callstack-top-offset [+] LEA
428 ! ! Get callstack length, in bytes --- 'len' for memcpy
429 ! arg3 arg4 callstack-length-offset [+] MOV
430 ! arg3 tag-bits get SHR
431 ! ! Compute new stack pointer -- 'dst' for memcpy
433 ! ! Install new stack pointer
435 ! ! Call memcpy; arguments are now in the correct registers
436 ! ! Create register shadow area for Win64
438 ! "factor_memcpy" jit-call
439 ! ! Tear down register shadow area
441 ! ! Return with new callstack
444 ! } define-sub-primitives
448 ! C to Factor entry point
452 ! ! Optimizing compiler's side of callback accesses
453 ! ! arguments that are on the stack via the frame pointer.
454 ! ! On x86-32 fastcall, and x86-64, some arguments are passed
455 ! ! in registers, and so the only registers that are safe for
456 ! ! use here are frame-reg, nv-reg and vm-reg.
458 ! frame-reg stack-reg MOV
460 ! ! Save all non-volatile registers
461 ! nv-regs [ PUSH ] each
465 ! ! Load VM into vm-reg
466 ! vm-reg 0 MOV 0 rc-absolute-cell rel-vm
469 ! nv-reg vm-reg vm-context-offset [+] MOV
472 ! ! Switch over to the spare context
473 ! nv-reg vm-reg vm-spare-context-offset [+] MOV
474 ! vm-reg vm-context-offset [+] nv-reg MOV
476 ! ! Save C callstack pointer
477 ! nv-reg context-callstack-save-offset [+] stack-reg MOV
479 ! ! Load Factor stack pointers
480 ! stack-reg nv-reg context-callstack-bottom-offset [+] MOV
481 ! nv-reg jit-update-tib
484 ! rs-reg nv-reg context-retainstack-offset [+] MOV
485 ! ds-reg nv-reg context-datastack-offset [+] MOV
487 ! ! Call into Factor code
488 ! link-reg 0 MOV f rc-absolute-cell rel-word
491 ! ! Load VM into vm-reg; only needed on x86-32, but doesn't
493 ! vm-reg 0 MOV 0 rc-absolute-cell rel-vm
495 ! ! Load C callstack pointer
496 ! nv-reg vm-reg vm-context-offset [+] MOV
497 ! stack-reg nv-reg context-callstack-save-offset [+] MOV
501 ! vm-reg vm-context-offset [+] nv-reg MOV
503 ! ! Restore non-volatile registers
506 ! nv-regs <reversed> [ POP ] each
510 ! ! Callbacks which return structs, or use stdcall/fastcall/thiscall,
511 ! ! need a parameter here.
513 ! ! See the comment for M\ x86.32 stack-cleanup in cpu.x86.32
514 ! 0xffff RET f rc-absolute-2 rel-untagged
515 ] CALLBACK-STUB jit-define
519 ! temp0 0 MOV f rc-absolute-cell rel-literal
520 ! ! increment datastack pointer
521 ! ds-reg bootstrap-cell ADD
522 ! ! store literal on datastack
523 ! ds-reg [] temp0 MOV
524 ] JIT-PUSH-LITERAL jit-define
527 ! 0 CALL f rc-relative rel-word-pic
528 ] JIT-WORD-CALL jit-define
530 ! The *-signal-handler subprimitives are special-cased in vm/quotations.cpp
531 ! not to trigger generation of a stack frame, so they can
532 ! peform their own prolog/epilog preserving registers.
534 ! It is important that the total is 192/64 and that it matches the
535 ! constants in vm/cpu-x86.*.hpp
536 : jit-signal-handler-prolog ( -- ) ;
537 ! ! Return address already on stack -> 8/4 bytes.
539 ! ! Push all registers. 15 regs/120 bytes on 64bit, 7 regs/28 bytes
540 ! ! on 32bit -> 128/32 bytes.
541 ! signal-handler-save-regs [ PUSH ] each
543 ! ! Push flags -> 136/36 bytes
546 ! ! Register parameter area 32 bytes, unused on platforms other than
547 ! ! windows 64 bit, but including it doesn't hurt. Plus
548 ! ! alignment. LEA used so we don't dirty flags -> 192/64 bytes.
549 ! stack-reg stack-reg 7 bootstrap-cells neg [+] LEA
553 : jit-signal-handler-epilog ( -- ) ;
554 ! stack-reg stack-reg 7 bootstrap-cells [+] LEA
556 ! signal-handler-save-regs reverse [ POP ] each ;
560 ! temp0 ds-reg [] MOV
562 ! ds-reg bootstrap-cell SUB
563 ! ! compare boolean with f
564 ! temp0 \ f type-number CMP
565 ! ! jump to true branch if not equal
566 ! 0 JNE f rc-relative rel-word
567 ! ! jump to false branch if equal
568 ! 0 JMP f rc-relative rel-word
574 ! 0 CALL f rc-relative rel-word
580 ! 0 CALL f rc-relative rel-word
582 ] JIT-2DIP jit-define
586 ! 0 CALL f rc-relative rel-word
588 ] JIT-3DIP jit-define
592 ! temp0 ds-reg [] MOV
594 ! ds-reg bootstrap-cell SUB
596 ! [ temp0 word-entry-point-offset [+] CALL ]
597 ! [ temp0 word-entry-point-offset [+] JMP ]
598 ! \ (execute) define-combinator-primitive
601 ! temp0 ds-reg [] MOV
602 ! ds-reg bootstrap-cell SUB
603 ! temp0 word-entry-point-offset [+] JMP
604 ] JIT-EXECUTE jit-define
607 ! https://elixir.bootlin.com/linux/latest/source/arch/arm64/kernel/stacktrace.c#L22
609 ! x64 ! stack-reg stack-frame-size bootstrap-cell - SUB
612 ! : link-reg ( -- reg ) X30 ; ! LR
613 ! : stack-frame-reg ( -- reg ) X29 ; ! FP
615 ! ! make room for LR plus magic number of callback, 16byte align
616 stack-frame-size bootstrap-cell 2 * + stack-reg stack-reg SUBi64
617 ! link-reg X29 stack-reg STP
618 -16 SP link-reg X29 STP-pre
619 ] JIT-PROLOG jit-define
622 ! x64 ! stack-reg stack-frame-size bootstrap-cell - ADD
623 -16 SP link-reg X29 LDP-pre
624 stack-frame-size bootstrap-cell 2 * + stack-reg stack-reg ADDi64
625 ] JIT-EPILOG jit-define
629 ] JIT-RETURN jit-define
631 ! ! ! Polymorphic inline caches
633 ! The PIC stubs are not permitted to touch pic-tail-reg.
635 ! Load a value from a stack position
637 ! temp1 ds-reg 0x7f [+] MOV f rc-absolute-1 rel-untagged
638 ] PIC-LOAD jit-define
641 ! temp1/32 tag-mask get AND
646 ! temp1/32 tag-mask get AND
647 ! temp1/32 tuple type-number CMP
649 ! [ temp1 temp0 tuple-class-offset [+] MOV ]
651 ] PIC-TUPLE jit-define
654 ! temp1/32 0x7f CMP f rc-absolute-1 rel-untagged
655 ] PIC-CHECK-TAG jit-define
658 ! 0 JE f rc-relative rel-word
661 ! ! ! Megamorphic caches
666 ! temp1/32 tag-mask get AND
667 ! temp1/32 tag-bits get SHL
668 ! temp1/32 tuple type-number tag-fixnum CMP
670 ! [ temp1 temp0 tuple-class-offset [+] MOV ]
673 ! temp0 0 MOV f rc-absolute-cell rel-literal
674 ! ! key = hashcode(class)
676 ! bootstrap-cell 4 = [ temp2 1 SHR ] when
677 ! ! key &= cache.length - 1
678 ! temp2 mega-cache-size get 1 - bootstrap-cell * AND
679 ! ! cache += array-start-offset
680 ! temp0 array-start-offset ADD
683 ! ! if(get(cache) == class)
687 ! ! megamorphic_cache_hits++
688 ! temp1 0 MOV rc-absolute-cell rel-megamorphic-cache-hits
690 ! ! goto get(cache + bootstrap-cell)
691 ! temp0 temp0 bootstrap-cell [+] MOV
692 ! temp0 word-entry-point-offset [+] JMP
693 ! ! fall-through on miss
695 ] MEGA-LOOKUP jit-define
698 : jit-compare ( insn -- ) drop ;
700 ! temp3 0 MOV t rc-absolute-cell rel-literal
702 ! temp1 \ f type-number MOV
704 ! temp0 ds-reg [] MOV
705 ! ! adjust stack pointer
706 ! ds-reg bootstrap-cell SUB
707 ! ! compare with second value
708 ! ds-reg [] temp0 CMP
710 ! [ temp1 temp3 ] dip execute( dst src -- )
712 ! ds-reg [] temp1 MOV ;
715 : jit-math ( insn -- ) drop ;
716 ! ! load second input
717 ! temp0 ds-reg [] MOV
719 ! ds-reg bootstrap-cell SUB
721 ! [ ds-reg [] temp0 ] dip execute( dst src -- ) ;
723 : jit-fixnum-/mod ( -- ) ;
724 ! ! load second parameter
725 ! temp1 ds-reg [] MOV
726 ! ! load first parameter
727 ! div-arg ds-reg bootstrap-cell neg [+] MOV
729 ! mod-arg div-arg MOV
731 ! mod-arg bootstrap-cell-bits 1 - SAR
735 ! # Rest of arm64 subprimitives
740 ! { fixnum+fast [ \ ADD jit-math ] }
743 ! { fixnum-bitand [ \ AND jit-math ] }
748 ! ds-reg [] tag-mask get XOR
750 ! { fixnum-bitor [ \ OR jit-math ] }
751 ! { fixnum-bitxor [ \ XOR jit-math ] }
752 ! { fixnum-shift-fast [
754 ! shift-arg ds-reg [] MOV
755 ! ! untag shift count
756 ! shift-arg tag-bits get SAR
757 ! ! adjust stack pointer
758 ! ds-reg bootstrap-cell SUB
760 ! temp3 ds-reg [] MOV
763 ! ! compute positive shift value in temp2
766 ! ! compute negative shift value in temp3
768 ! temp3 tag-mask get bitnot AND
770 ! ! if shift count was negative, move temp0 to temp2
773 ! ds-reg [] temp2 MOV
778 ! temp0 ds-reg [] MOV
779 ! ds-reg bootstrap-cell SUB
781 ! temp0 tag-mask get TEST
782 ! temp0 \ f type-number MOV
783 ! temp1 1 tag-fixnum MOV
785 ! ds-reg [] temp0 MOV
787 ! { eq? [ \ CMOVE jit-compare ] }
788 ! { fixnum> [ \ CMOVG jit-compare ] }
789 ! { fixnum>= [ \ CMOVGE jit-compare ] }
790 ! { fixnum< [ \ CMOVL jit-compare ] }
791 ! { fixnum<= [ \ CMOVLE jit-compare ] }
796 ! ! adjust stack pointer
797 ! ds-reg bootstrap-cell SUB
799 ! ds-reg [] mod-arg MOV
803 ! ! adjust stack pointer
804 ! ds-reg bootstrap-cell SUB
806 ! div-arg tag-bits get SHL
808 ! ds-reg [] div-arg MOV
810 ! { fixnum/mod-fast [
813 ! div-arg tag-bits get SHL
815 ! ds-reg [] mod-arg MOV
816 ! ds-reg bootstrap-cell neg [+] div-arg MOV
821 ! ! load second input
822 ! temp0 ds-reg [] MOV
824 ! ds-reg bootstrap-cell SUB
826 ! temp1 ds-reg [] MOV
827 ! ! untag second input
828 ! temp0 tag-bits get SAR
832 ! ds-reg [] temp0 MOV
836 ! { fixnum-fast [ \ SUB jit-math ] }
841 ! temp0 ds-reg [] MOV
842 ! ! adjust stack pointer
843 ! ds-reg bootstrap-cell SUB
844 ! ! turn local number into offset
846 ! ! decrement retain stack pointer
850 ! ! load local number
851 ! temp0 ds-reg [] MOV
852 ! ! turn local number into offset
855 ! temp0 rs-reg temp0 [+] MOV
857 ! ds-reg [] temp0 MOV
859 ! { load-local [ jit->r ] }
864 ! temp0 ds-reg [] MOV
865 ! ! adjust stack pointer
866 ! ds-reg bootstrap-cell SUB
868 ! temp1 ds-reg [] MOV
869 ! ! turn slot number into offset
872 ! temp1 tag-bits get SHR
873 ! temp1 tag-bits get SHL
875 ! temp0 temp1 temp0 [+] MOV
877 ! ds-reg [] temp0 MOV
879 ! { string-nth-fast [
880 ! ! load string index from stack
881 ! temp0 ds-reg bootstrap-cell neg [+] MOV
882 ! temp0 tag-bits get SHR
883 ! ! load string from stack
884 ! temp1 ds-reg [] MOV
886 ! temp0 8-bit-version-of temp0 temp1 string-offset [++] MOV
887 ! temp0 temp0 8-bit-version-of MOVZX
888 ! temp0 tag-bits get SHL
889 ! ! store character to stack
890 ! ds-reg bootstrap-cell SUB
891 ! ds-reg [] temp0 MOV
895 ! temp0 ds-reg [] MOV
897 ! temp0/32 tag-mask get AND
899 ! temp0/32 tag-bits get SHL
901 ! ds-reg [] temp0 MOV
907 ! { drop [ ds-reg bootstrap-cell SUB ] }
908 ! { 2drop [ ds-reg 2 bootstrap-cells SUB ] }
909 ! { 3drop [ ds-reg 3 bootstrap-cells SUB ] }
910 ! { 4drop [ ds-reg 4 bootstrap-cells SUB ] }
914 ! temp0 ds-reg [] MOV
915 ! ds-reg bootstrap-cell ADD
916 ! ds-reg [] temp0 MOV
919 ! temp0 ds-reg [] MOV
920 ! temp1 ds-reg bootstrap-cell neg [+] MOV
921 ! ds-reg 2 bootstrap-cells ADD
922 ! ds-reg [] temp0 MOV
923 ! ds-reg bootstrap-cell neg [+] temp1 MOV
926 ! temp0 ds-reg [] MOV
927 ! temp1 ds-reg -1 bootstrap-cells [+] MOV
928 ! temp3 ds-reg -2 bootstrap-cells [+] MOV
929 ! ds-reg 3 bootstrap-cells ADD
930 ! ds-reg [] temp0 MOV
931 ! ds-reg -1 bootstrap-cells [+] temp1 MOV
932 ! ds-reg -2 bootstrap-cells [+] temp3 MOV
935 ! temp0 ds-reg [] MOV
936 ! temp1 ds-reg -1 bootstrap-cells [+] MOV
937 ! temp2 ds-reg -2 bootstrap-cells [+] MOV
938 ! temp3 ds-reg -3 bootstrap-cells [+] MOV
939 ! ds-reg 4 bootstrap-cells ADD
940 ! ds-reg [] temp0 MOV
941 ! ds-reg -1 bootstrap-cells [+] temp1 MOV
942 ! ds-reg -2 bootstrap-cells [+] temp2 MOV
943 ! ds-reg -3 bootstrap-cells [+] temp3 MOV
946 ! temp0 ds-reg [] MOV
947 ! temp1 ds-reg -1 bootstrap-cells [+] MOV
948 ! ds-reg [] temp1 MOV
949 ! ds-reg bootstrap-cell ADD
950 ! ds-reg [] temp0 MOV
953 ! ! ### Misc shufflers
955 ! temp0 ds-reg -1 bootstrap-cells [+] MOV
956 ! ds-reg bootstrap-cell ADD
957 ! ds-reg [] temp0 MOV
960 ! temp0 ds-reg -2 bootstrap-cells [+] MOV
961 ! ds-reg bootstrap-cell ADD
962 ! ds-reg [] temp0 MOV
967 ! temp0 ds-reg [] MOV
968 ! ds-reg bootstrap-cell SUB
969 ! ds-reg [] temp0 MOV
972 ! temp0 ds-reg [] MOV
973 ! ds-reg 2 bootstrap-cells SUB
974 ! ds-reg [] temp0 MOV
979 ! temp0 ds-reg [] MOV
980 ! temp1 ds-reg -1 bootstrap-cells [+] MOV
981 ! temp3 ds-reg -2 bootstrap-cells [+] MOV
982 ! ds-reg -2 bootstrap-cells [+] temp0 MOV
983 ! ds-reg -1 bootstrap-cells [+] temp3 MOV
984 ! ds-reg [] temp1 MOV
987 ! temp0 ds-reg [] MOV
988 ! temp1 ds-reg -1 bootstrap-cells [+] MOV
989 ! temp3 ds-reg -2 bootstrap-cells [+] MOV
990 ! ds-reg -2 bootstrap-cells [+] temp1 MOV
991 ! ds-reg -1 bootstrap-cells [+] temp0 MOV
992 ! ds-reg [] temp3 MOV
995 ! temp0 ds-reg [] MOV
996 ! temp1 ds-reg bootstrap-cell neg [+] MOV
997 ! ds-reg bootstrap-cell neg [+] temp0 MOV
998 ! ds-reg [] temp1 MOV
1001 ! temp0 ds-reg -1 bootstrap-cells [+] MOV
1002 ! temp1 ds-reg -2 bootstrap-cells [+] MOV
1003 ! ds-reg -2 bootstrap-cells [+] temp0 MOV
1004 ! ds-reg -1 bootstrap-cells [+] temp1 MOV
1007 ! ! ## Signal handling
1008 ! { leaf-signal-handler [
1009 ! jit-signal-handler-prolog
1011 ! temp0 vm-reg vm-signal-handler-addr-offset [+] MOV
1013 ! jit-signal-handler-epilog
1014 ! ! Pop the fake leaf frame along with our return address
1015 ! leaf-stack-frame-size bootstrap-cell - RET
1017 ! { signal-handler [
1018 ! jit-signal-handler-prolog
1020 ! temp0 vm-reg vm-signal-handler-addr-offset [+] MOV
1022 ! jit-signal-handler-epilog
1025 } define-sub-primitives
1027 [ "bootstrap.arm.64" forget-vocab ] with-compilation-unit