]> gitweb.factorcode.org Git - factor.git/blob - basis/bootstrap/assembler/arm.64.factor
arm: 128 byte red zone
[factor.git] / basis / bootstrap / assembler / arm.64.factor
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
10
11 8 \ cell set
12
13 big-endian off
14
15 ! Stack frame
16 ! https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=vs-2019
17
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
27
28 : stack-frame-size ( -- n ) 4 bootstrap-cells ;
29 : volatile-regs ( -- seq ) { X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 } ;
30 : nv-regs ( -- seq ) { X18 X19 X20 X21 X22 X23 X24 X25 X26 X27 X28 X29 X30 } ;
31
32 ! callee-save = non-volatile aka call-preserved
33
34 ! x30 is the link register (used to return from subroutines)
35 ! x29 is the frame register
36 ! x19 to x29 are callee-saved
37 ! x18 is the 'platform register', used for some operating-system-specific special purpose,
38 !   or an additional caller-saved register
39 ! x16 and x17 are the Intra-Procedure-call scratch register
40 ! x9 to x15: used to hold local variables (caller saved)
41 ! x8: used to hold indirect return value address
42 ! x0 to x7: used to hold argument values passed to a subroutine, and also hold
43 !   results returned from a subroutine
44
45
46 ! https://en.wikichip.org/wiki/arm/aarch64
47 ! Generally, X0 through X18 can corrupt while X19-X29 must be preserved
48 ! Register   Role    Requirement
49 ! X0 -  X7   Parameter/result registers   Can Corrupt
50 ! X8         Indirect result location register
51 ! X9 -  X15  Temporary registers
52 ! X16 - X17  Intra-procedure call temporary
53 ! X18        Platform register, otherwise temporary, DONT USE
54
55 ! X19 - X29    Callee-saved register    Must preserve
56 ! X29 - frame pointer register, must always be valid
57 ! X30    Link Register    Can Corrupt
58 ! 16-byte stack alignment
59
60 : arg1 ( -- reg ) X0 ;
61 : arg2 ( -- reg ) X1 ;
62 : arg3 ( -- reg ) X2 ;
63 : arg4 ( -- reg ) X3 ;
64 : red-zone-size ( -- n ) 128 ;
65
66 : shift-arg ( -- reg ) X1 ;
67 : div-arg ( -- reg ) X0 ;
68 : mod-arg ( -- reg ) X2 ;
69
70 ! caller-saved registers X9-X15
71 ! callee-saved registers X19-X29
72 : temp0 ( -- reg ) X9 ;
73 : temp1 ( -- reg ) X10 ;
74 : temp2 ( -- reg ) X11 ;
75 : temp3 ( -- reg ) X12 ;
76
77
78
79 ! : pic-tail-reg ( -- reg ) RBX ;
80 : return-reg ( -- reg ) X0 ;
81 ! : nv-reg ( -- reg ) RBX ;
82 ! : stack-reg ( -- reg ) RSP ;
83 ! : frame-reg ( -- reg ) RBP ;
84 ! : link-reg ( -- reg ) R11 ;
85 ! : ctx-reg ( -- reg ) R12 ;
86 : vm-reg ( -- reg ) X7 ;
87 : ds-reg ( -- reg ) X5 ;
88 : rs-reg ( -- reg ) X6 ;
89 ! : fixnum>slot@ ( -- ) temp0 1 SAR ;
90 ! : rex-length ( -- n ) 1 ;
91
92 ! rc-absolute-cell is just CONSTANT: 0
93 : jit-call ( name -- )
94     0 X0 MOVwi64
95     f rc-absolute-cell rel-dlsym
96     X0 BR ;
97     ! RAX 0 MOV f rc-absolute-cell rel-dlsym
98     ! RAX CALL ;
99
100 :: jit-call-1arg ( arg1s name -- )
101     arg1s arg1 MOVr64
102     name jit-call ;
103     ! arg1 arg1s MOVr64
104     ! name jit-call ;
105
106 :: jit-call-2arg ( arg1s arg2s name -- )
107     arg1s arg1 MOVr64
108     arg2s arg2 MOVr64
109     name jit-call ;
110     ! arg1 arg1s MOV
111     ! arg2 arg2s MOV
112     ! name jit-call ;
113
114 [
115     ! pic-tail-reg 5 [RIP+] LEA
116     ! 0 JMP f rc-relative rel-word-pic-tail
117 ] JIT-WORD-JUMP jit-define
118
119 : jit-load-vm ( -- )
120     ! no-op on x86-64. in factor contexts vm-reg always contains the
121     ! vm pointer.
122     ;
123
124 : jit-load-context ( -- ) ;
125     ! ctx-reg vm-reg vm-context-offset [+] MOV ;
126
127 : jit-save-context ( -- ) ;
128     ! jit-load-context
129     ! The reason for -8 I think is because we are anticipating a CALL
130     ! instruction. After the call instruction, the contexts frame_top
131     ! will point to the origin jump address.
132     ! R11 RSP -8 [+] LEA
133     ! ctx-reg context-callstack-top-offset [+] R11 MOV
134     ! ctx-reg context-datastack-offset [+] ds-reg MOV
135     ! ctx-reg context-retainstack-offset [+] rs-reg MOV ;
136
137 ! ctx-reg must already have been loaded
138 : jit-restore-context ( -- ) ;
139     ! ds-reg ctx-reg context-datastack-offset [+] MOV
140     ! rs-reg ctx-reg context-retainstack-offset [+] MOV ;
141
142
143 [
144     ! ! ctx-reg is preserved across the call because it is non-volatile
145     ! ! in the C ABI
146     ! jit-save-context
147     ! ! call the primitive
148     ! arg1 vm-reg MOV
149     ! RAX 0 MOV f f rc-absolute-cell rel-dlsym
150     ! RAX CALL
151     ! jit-restore-context
152 ] JIT-PRIMITIVE jit-define
153
154
155 : jit-jump-quot ( -- ) ;
156     ! arg1 quot-entry-point-offset [+] JMP ;
157
158 : jit-call-quot ( -- )
159     quot-entry-point-offset arg1 ADR
160     arg1 BR ;
161
162     ! arg1 quot-entry-point-offset [+] CALL ;
163
164 : signal-handler-save-regs ( -- regs ) { } ;
165     ! { RAX RCX RDX RBX RBP RSI RDI R8 R9 R10 R11 R12 R13 R14 R15 } ;
166
167
168 [
169     ! temp2 0 MOV f rc-absolute-cell rel-literal
170     ! temp1 temp2 CMP
171 ] PIC-CHECK-TUPLE jit-define
172
173
174
175 : jit->r ( -- )
176     1 bootstrap-cells rs-reg rs-reg ADDi64
177     -1 bootstrap-cells ds-reg rs-reg LDR-post ;
178
179 : jit-r> ( -- )
180     1 bootstrap-cells ds-reg ds-reg ADDi64
181     -1 bootstrap-cells rs-reg ds-reg LDR-post ;
182
183 : jit-2>r ( -- )
184     1 bootstrap-cells rs-reg rs-reg ADDi64
185     -1 bootstrap-cells ds-reg rs-reg LDR-post
186     1 bootstrap-cells rs-reg rs-reg ADDi64
187     -1 bootstrap-cells ds-reg rs-reg LDR-post ;
188
189 : jit-2r> ( -- )
190     1 bootstrap-cells ds-reg ds-reg ADDi64
191     -1 bootstrap-cells rs-reg ds-reg LDR-post
192     1 bootstrap-cells ds-reg ds-reg ADDi64
193     -1 bootstrap-cells rs-reg ds-reg LDR-post ;
194
195 : jit-3>r ( -- )
196     1 bootstrap-cells rs-reg rs-reg ADDi64
197     -1 bootstrap-cells ds-reg rs-reg LDR-post
198     1 bootstrap-cells rs-reg rs-reg ADDi64
199     -1 bootstrap-cells ds-reg rs-reg LDR-post
200     1 bootstrap-cells rs-reg rs-reg ADDi64
201     -1 bootstrap-cells ds-reg rs-reg LDR-post ;
202
203 : jit-3r> ( -- )
204     1 bootstrap-cells ds-reg ds-reg ADDi64
205     -1 bootstrap-cells rs-reg ds-reg LDR-post
206     1 bootstrap-cells ds-reg ds-reg ADDi64
207     -1 bootstrap-cells rs-reg ds-reg LDR-post
208     1 bootstrap-cells ds-reg ds-reg ADDi64
209     -1 bootstrap-cells rs-reg ds-reg LDR-post ;
210
211 ! Contexts
212 : jit-switch-context ( reg -- ) drop ;
213     ! ! Push a bogus return address so the GC can track this frame back
214     ! ! to the owner
215     ! 0 CALL
216
217     ! ! Make the new context the current one
218     ! ctx-reg swap MOV
219     ! vm-reg vm-context-offset [+] ctx-reg MOV
220
221     ! ! Load new stack pointer
222     ! RSP ctx-reg context-callstack-top-offset [+] MOV
223
224     ! ! Load new ds, rs registers
225     ! jit-restore-context
226
227     ! ctx-reg jit-update-tib ;
228
229 : jit-pop-context-and-param ( -- ) ;
230     ! arg1 ds-reg [] MOV
231     ! arg1 arg1 alien-offset [+] MOV
232     ! arg2 ds-reg -8 [+] MOV
233     ! ds-reg 16 SUB ;
234
235 : jit-push-param ( -- ) ;
236     ! ds-reg 8 ADD
237     ! ds-reg [] arg2 MOV ;
238
239 : jit-set-context ( -- ) ;
240     ! jit-pop-context-and-param
241     ! jit-save-context
242     ! arg1 jit-switch-context
243     ! RSP 8 ADD
244     ! jit-push-param ;
245
246 : jit-pop-quot-and-param ( -- ) ;
247     ! arg1 ds-reg [] MOV
248     ! arg2 ds-reg -8 [+] MOV
249     ! ds-reg 16 SUB ;
250
251 : jit-start-context ( -- ) ;
252     ! Create the new context in return-reg. Have to save context
253     ! twice, first before calling new_context() which may GC,
254     ! and again after popping the two parameters from the stack.
255     ! jit-save-context
256     ! vm-reg "new_context" jit-call-1arg
257
258     ! jit-pop-quot-and-param
259     ! jit-save-context
260     ! return-reg jit-switch-context
261     ! jit-push-param
262     ! jit-jump-quot ;
263
264 : jit-delete-current-context ( -- ) ;
265     ! vm-reg "delete_context" jit-call-1arg ;
266
267 [
268     ! jit->r
269     ! 0 CALL f rc-relative rel-word
270     ! jit-r>
271 ] JIT-DIP jit-define
272
273
274
275
276 [
277     ! 0 [RIP+] EAX MOV rc-relative rel-safepoint
278 ] JIT-SAFEPOINT jit-define
279
280 ! # All arm.64 subprimitives
281
282 {
283     ! ## Contexts
284     ! { (set-context) [ jit-set-context ] }
285     ! { (set-context-and-delete) [
286     !     jit-delete-current-context
287     !     jit-set-context
288     ! ] }
289     ! { (start-context) [ jit-start-context ] }
290     ! { (start-context-and-delete) [ jit-start-context-and-delete ] }
291
292     ! ## Entry points
293     { c-to-factor [
294         ! dst src MOV
295         ! arg2 arg1 MOV
296         ! vm-reg "begin_callback" jit-call-1arg
297
298         ! ! call the quotation
299         ! arg1 return-reg MOV
300         ! jit-call-quot
301
302         ! vm-reg "end_callback" jit-call-1arg
303
304         [
305
306             ! write()
307             ! 68 X8 MOVwi64
308             ! X2 MOVwi64
309             ! 0 SVC
310
311             ! exit(42)
312             9999 BRK
313             42 X0 MOVwi64
314             93 X8 MOVwi64
315             0 SVC
316
317             
318
319             ! Rn Rd MOVr64
320             ! arg1 arg2 MOVr64
321             ! vm-reg "begin_callback" jit-call-1arg
322
323             ! return-reg arg1 MOVr64 ! arg1 is return
324             ! jit-call-quot
325
326             ! vm-reg "end_callback" jit-call-1arg
327
328         ] assemble-arm %
329
330     ] }
331     ! { unwind-native-frames [ ] }
332
333     ! ## Math
334     ! { fixnum+ [ [ ADD ] "overflow_fixnum_add" jit-overflow ] }
335     ! { fixnum- [ [ SUB ] "overflow_fixnum_subtract" jit-overflow ] }
336     ! { fixnum* [
337     !     ds-reg 8 SUB
338     !     jit-save-context
339     !     RCX ds-reg [] MOV
340     !     RBX ds-reg 8 [+] MOV
341     !     RBX tag-bits get SAR
342     !     RAX RCX MOV
343     !     RBX IMUL
344     !     ds-reg [] RAX MOV
345     !     [ JNO ]
346     !     [
347     !         arg1 RCX MOV
348     !         arg1 tag-bits get SAR
349     !         arg2 RBX MOV
350     !         arg3 vm-reg MOV
351     !         "overflow_fixnum_multiply" jit-call
352     !     ]
353     !     jit-conditional
354     ! ] }
355
356     ! ## Misc
357     ! { fpu-state [
358     !     RSP 2 SUB
359     !     RSP [] FNSTCW
360     !     FNINIT
361     !     AX RSP [] MOV
362     !     RSP 2 ADD
363     ! ] }
364     ! { set-fpu-state [
365     !     RSP 2 SUB
366     !     RSP [] arg1 16-bit-version-of MOV
367     !     RSP [] FLDCW
368     !     RSP 2 ADD
369     ! ] }
370     ! { set-callstack [
371     !     ! Load callstack object
372     !     arg4 ds-reg [] MOV
373     !     ds-reg bootstrap-cell SUB
374     !     ! Get ctx->callstack_bottom
375     !     jit-load-context
376     !     arg1 ctx-reg context-callstack-bottom-offset [+] MOV
377     !     ! Get top of callstack object -- 'src' for memcpy
378     !     arg2 arg4 callstack-top-offset [+] LEA
379     !     ! Get callstack length, in bytes --- 'len' for memcpy
380     !     arg3 arg4 callstack-length-offset [+] MOV
381     !     arg3 tag-bits get SHR
382     !     ! Compute new stack pointer -- 'dst' for memcpy
383     !     arg1 arg3 SUB
384     !     ! Install new stack pointer
385     !     RSP arg1 MOV
386     !     ! Call memcpy; arguments are now in the correct registers
387     !     ! Create register shadow area for Win64
388     !     RSP 32 SUB
389     !     "factor_memcpy" jit-call
390     !     ! Tear down register shadow area
391     !     RSP 32 ADD
392     !     ! Return with new callstack
393     !     0 RET
394     ! ] }
395 } define-sub-primitives
396
397
398
399 ! C to Factor entry point
400 [
401     ! ! Optimizing compiler's side of callback accesses
402     ! ! arguments that are on the stack via the frame pointer.
403     ! ! On x86-32 fastcall, and x86-64, some arguments are passed
404     ! ! in registers, and so the only registers that are safe for
405     ! ! use here are frame-reg, nv-reg and vm-reg.
406     ! frame-reg PUSH
407     ! frame-reg stack-reg MOV
408
409     ! ! Save all non-volatile registers
410     ! nv-regs [ PUSH ] each
411
412     ! jit-save-tib
413
414     ! ! Load VM into vm-reg
415     ! vm-reg 0 MOV 0 rc-absolute-cell rel-vm
416
417     ! ! Save old context
418     ! nv-reg vm-reg vm-context-offset [+] MOV
419     ! nv-reg PUSH
420
421     ! ! Switch over to the spare context
422     ! nv-reg vm-reg vm-spare-context-offset [+] MOV
423     ! vm-reg vm-context-offset [+] nv-reg MOV
424
425     ! ! Save C callstack pointer
426     ! nv-reg context-callstack-save-offset [+] stack-reg MOV
427
428     ! ! Load Factor stack pointers
429     ! stack-reg nv-reg context-callstack-bottom-offset [+] MOV
430     ! nv-reg jit-update-tib
431     ! jit-install-seh
432
433     ! rs-reg nv-reg context-retainstack-offset [+] MOV
434     ! ds-reg nv-reg context-datastack-offset [+] MOV
435
436     ! ! Call into Factor code
437     ! link-reg 0 MOV f rc-absolute-cell rel-word
438     ! link-reg CALL
439
440     ! ! Load VM into vm-reg; only needed on x86-32, but doesn't
441     ! ! hurt on x86-64
442     ! vm-reg 0 MOV 0 rc-absolute-cell rel-vm
443
444     ! ! Load C callstack pointer
445     ! nv-reg vm-reg vm-context-offset [+] MOV
446     ! stack-reg nv-reg context-callstack-save-offset [+] MOV
447
448     ! ! Load old context
449     ! nv-reg POP
450     ! vm-reg vm-context-offset [+] nv-reg MOV
451
452     ! ! Restore non-volatile registers
453     ! jit-restore-tib
454
455     ! nv-regs <reversed> [ POP ] each
456
457     ! frame-reg POP
458
459     ! ! Callbacks which return structs, or use stdcall/fastcall/thiscall,
460     ! ! need a parameter here.
461
462     ! ! See the comment for M\ x86.32 stack-cleanup in cpu.x86.32
463     ! 0xffff RET f rc-absolute-2 rel-untagged
464 ] CALLBACK-STUB jit-define
465
466 [
467     ! ! load literal
468     ! temp0 0 MOV f rc-absolute-cell rel-literal
469     ! ! increment datastack pointer
470     ! ds-reg bootstrap-cell ADD
471     ! ! store literal on datastack
472     ! ds-reg [] temp0 MOV
473 ] JIT-PUSH-LITERAL jit-define
474
475 [
476     ! 0 CALL f rc-relative rel-word-pic
477 ] JIT-WORD-CALL jit-define
478
479 ! The *-signal-handler subprimitives are special-cased in vm/quotations.cpp
480 ! not to trigger generation of a stack frame, so they can
481 ! peform their own prolog/epilog preserving registers.
482 !
483 ! It is important that the total is 192/64 and that it matches the
484 ! constants in vm/cpu-x86.*.hpp
485 : jit-signal-handler-prolog ( -- ) ;
486     ! ! Return address already on stack -> 8/4 bytes.
487
488     ! ! Push all registers. 15 regs/120 bytes on 64bit, 7 regs/28 bytes
489     ! ! on 32bit -> 128/32 bytes.
490     ! signal-handler-save-regs [ PUSH ] each
491
492     ! ! Push flags -> 136/36 bytes
493     ! PUSHF
494
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
500     ! jit-load-vm ;
501
502 : jit-signal-handler-epilog ( -- ) ;
503     ! stack-reg stack-reg 7 bootstrap-cells [+] LEA
504     ! POPF
505     ! signal-handler-save-regs reverse [ POP ] each ;
506
507 [
508     ! ! load boolean
509     ! temp0 ds-reg [] MOV
510     ! ! pop boolean
511     ! ds-reg bootstrap-cell SUB
512     ! ! compare boolean with f
513     ! temp0 \ f type-number CMP
514     ! ! jump to true branch if not equal
515     ! 0 JNE f rc-relative rel-word
516     ! ! jump to false branch if equal
517     ! 0 JMP f rc-relative rel-word
518 ] JIT-IF jit-define
519
520
521 [
522     ! jit->r
523     ! 0 CALL f rc-relative rel-word
524     ! jit-r>
525 ] JIT-DIP jit-define
526
527 [
528     ! jit-2>r
529     ! 0 CALL f rc-relative rel-word
530     ! jit-2r>
531 ] JIT-2DIP jit-define
532
533 [
534     ! jit-3>r
535     ! 0 CALL f rc-relative rel-word
536     ! jit-3r>
537 ] JIT-3DIP jit-define
538
539 ! [
540 !     ! load from stack
541 !     temp0 ds-reg [] MOV
542 !     ! pop stack
543 !     ds-reg bootstrap-cell SUB
544 ! ]
545 ! [ temp0 word-entry-point-offset [+] CALL ]
546 ! [ temp0 word-entry-point-offset [+] JMP ]
547 ! \ (execute) define-combinator-primitive
548
549 [
550     ! temp0 ds-reg [] MOV
551     ! ds-reg bootstrap-cell SUB
552     ! temp0 word-entry-point-offset [+] JMP
553 ] JIT-EXECUTE jit-define
554
555 [
556     ! stack-reg stack-frame-size bootstrap-cell - SUB
557 ] JIT-PROLOG jit-define
558
559 [
560     ! stack-reg stack-frame-size bootstrap-cell - ADD
561 ] JIT-EPILOG jit-define
562
563 [
564     ! 0 RET
565 ] JIT-RETURN jit-define
566
567 ! ! ! Polymorphic inline caches
568
569 ! The PIC stubs are not permitted to touch pic-tail-reg.
570
571 ! Load a value from a stack position
572 [
573     ! temp1 ds-reg 0x7f [+] MOV f rc-absolute-1 rel-untagged
574 ] PIC-LOAD jit-define
575
576 [
577     ! temp1/32 tag-mask get AND
578 ] PIC-TAG jit-define
579
580 [
581     ! temp0 temp1 MOV
582     ! temp1/32 tag-mask get AND
583     ! temp1/32 tuple type-number CMP
584     ! [ JNE ]
585     ! [ temp1 temp0 tuple-class-offset [+] MOV ]
586     ! jit-conditional
587 ] PIC-TUPLE jit-define
588
589 [
590     ! temp1/32 0x7f CMP f rc-absolute-1 rel-untagged
591 ] PIC-CHECK-TAG jit-define
592
593 [
594     ! 0 JE f rc-relative rel-word
595 ] PIC-HIT jit-define
596
597 ! ! ! Megamorphic caches
598
599 [
600     ! ! class = ...
601     ! temp0 temp1 MOV
602     ! temp1/32 tag-mask get AND
603     ! temp1/32 tag-bits get SHL
604     ! temp1/32 tuple type-number tag-fixnum CMP
605     ! [ JNE ]
606     ! [ temp1 temp0 tuple-class-offset [+] MOV ]
607     ! jit-conditional
608     ! ! cache = ...
609     ! temp0 0 MOV f rc-absolute-cell rel-literal
610     ! ! key = hashcode(class)
611     ! temp2 temp1 MOV
612     ! bootstrap-cell 4 = [ temp2 1 SHR ] when
613     ! ! key &= cache.length - 1
614     ! temp2 mega-cache-size get 1 - bootstrap-cell * AND
615     ! ! cache += array-start-offset
616     ! temp0 array-start-offset ADD
617     ! ! cache += key
618     ! temp0 temp2 ADD
619     ! ! if(get(cache) == class)
620     ! temp0 [] temp1 CMP
621     ! [ JNE ]
622     ! [
623     !     ! megamorphic_cache_hits++
624     !     temp1 0 MOV rc-absolute-cell rel-megamorphic-cache-hits
625     !     temp1 [] 1 ADD
626     !     ! goto get(cache + bootstrap-cell)
627     !     temp0 temp0 bootstrap-cell [+] MOV
628     !     temp0 word-entry-point-offset [+] JMP
629     !     ! fall-through on miss
630     ! ] jit-conditional
631 ] MEGA-LOOKUP jit-define
632
633 ! Comparisons
634 : jit-compare ( insn -- ) drop ;
635     ! ! load t
636     ! temp3 0 MOV t rc-absolute-cell rel-literal
637     ! ! load f
638     ! temp1 \ f type-number MOV
639     ! ! load first value
640     ! temp0 ds-reg [] MOV
641     ! ! adjust stack pointer
642     ! ds-reg bootstrap-cell SUB
643     ! ! compare with second value
644     ! ds-reg [] temp0 CMP
645     ! ! move t if true
646     ! [ temp1 temp3 ] dip execute( dst src -- )
647     ! ! store
648     ! ds-reg [] temp1 MOV ;
649
650 ! Math
651 : jit-math ( insn -- ) drop ;
652     ! ! load second input
653     ! temp0 ds-reg [] MOV
654     ! ! pop stack
655     ! ds-reg bootstrap-cell SUB
656     ! ! compute result
657     ! [ ds-reg [] temp0 ] dip execute( dst src -- ) ;
658
659 : jit-fixnum-/mod ( -- ) ;
660     ! ! load second parameter
661     ! temp1 ds-reg [] MOV
662     ! ! load first parameter
663     ! div-arg ds-reg bootstrap-cell neg [+] MOV
664     ! ! make a copy
665     ! mod-arg div-arg MOV
666     ! ! sign-extend
667     ! mod-arg bootstrap-cell-bits 1 - SAR
668     ! ! divide
669     ! temp1 IDIV ;
670
671 ! # Rest of arm64 subprimitives
672 {
673     ! ! ## Fixnums
674
675     ! ! ### Add
676     ! { fixnum+fast [ \ ADD jit-math ] }
677
678     ! ! ### Bit stuff
679     ! { fixnum-bitand [ \ AND jit-math ] }
680     ! { fixnum-bitnot [
681     !     ! complement
682     !     ds-reg [] NOT
683     !     ! clear tag bits
684     !     ds-reg [] tag-mask get XOR
685     ! ] }
686     ! { fixnum-bitor [ \ OR jit-math ] }
687     ! { fixnum-bitxor [ \ XOR jit-math ] }
688     ! { fixnum-shift-fast [
689     !     ! load shift count
690     !     shift-arg ds-reg [] MOV
691     !     ! untag shift count
692     !     shift-arg tag-bits get SAR
693     !     ! adjust stack pointer
694     !     ds-reg bootstrap-cell SUB
695     !     ! load value
696     !     temp3 ds-reg [] MOV
697     !     ! make a copy
698     !     temp2 temp3 MOV
699     !     ! compute positive shift value in temp2
700     !     temp2 CL SHL
701     !     shift-arg NEG
702     !     ! compute negative shift value in temp3
703     !     temp3 CL SAR
704     !     temp3 tag-mask get bitnot AND
705     !     shift-arg 0 CMP
706     !     ! if shift count was negative, move temp0 to temp2
707     !     temp2 temp3 CMOVGE
708     !     ! push to stack
709     !     ds-reg [] temp2 MOV
710     ! ] }
711
712     ! ! ### Comparisons
713     ! { both-fixnums? [
714     !     temp0 ds-reg [] MOV
715     !     ds-reg bootstrap-cell SUB
716     !     temp0 ds-reg [] OR
717     !     temp0 tag-mask get TEST
718     !     temp0 \ f type-number MOV
719     !     temp1 1 tag-fixnum MOV
720     !     temp0 temp1 CMOVE
721     !     ds-reg [] temp0 MOV
722     ! ] }
723     ! { eq? [ \ CMOVE jit-compare ] }
724     ! { fixnum> [ \ CMOVG jit-compare ] }
725     ! { fixnum>= [ \ CMOVGE jit-compare ] }
726     ! { fixnum< [ \ CMOVL jit-compare ] }
727     ! { fixnum<= [ \ CMOVLE jit-compare ] }
728
729     ! ! ### Div/mod
730     ! { fixnum-mod [
731     !     jit-fixnum-/mod
732     !     ! adjust stack pointer
733     !     ds-reg bootstrap-cell SUB
734     !     ! push to stack
735     !     ds-reg [] mod-arg MOV
736     ! ] }
737     ! { fixnum/i-fast [
738     !     jit-fixnum-/mod
739     !     ! adjust stack pointer
740     !     ds-reg bootstrap-cell SUB
741     !     ! tag it
742     !     div-arg tag-bits get SHL
743     !     ! push to stack
744     !     ds-reg [] div-arg MOV
745     ! ] }
746     ! { fixnum/mod-fast [
747     !     jit-fixnum-/mod
748     !     ! tag it
749     !     div-arg tag-bits get SHL
750     !     ! push to stack
751     !     ds-reg [] mod-arg MOV
752     !     ds-reg bootstrap-cell neg [+] div-arg MOV
753     ! ] }
754
755     ! ! ### Mul
756     ! { fixnum*fast [
757     !     ! load second input
758     !     temp0 ds-reg [] MOV
759     !     ! pop stack
760     !     ds-reg bootstrap-cell SUB
761     !     ! load first input
762     !     temp1 ds-reg [] MOV
763     !     ! untag second input
764     !     temp0 tag-bits get SAR
765     !     ! multiply
766     !     temp0 temp1 IMUL2
767     !     ! push result
768     !     ds-reg [] temp0 MOV
769     ! ] }
770
771     ! ! ### Sub
772     ! { fixnum-fast [ \ SUB jit-math ] }
773
774     ! ! ## Locals
775     ! { drop-locals [
776     !     ! load local count
777     !     temp0 ds-reg [] MOV
778     !     ! adjust stack pointer
779     !     ds-reg bootstrap-cell SUB
780     !     ! turn local number into offset
781     !     fixnum>slot@
782     !     ! decrement retain stack pointer
783     !     rs-reg temp0 SUB
784     ! ] }
785     ! { get-local [
786     !     ! load local number
787     !     temp0 ds-reg [] MOV
788     !     ! turn local number into offset
789     !     fixnum>slot@
790     !     ! load local value
791     !     temp0 rs-reg temp0 [+] MOV
792     !     ! push to stack
793     !     ds-reg [] temp0 MOV
794     ! ] }
795     ! { load-local [ jit->r ] }
796
797     ! ! ## Objects
798     ! { slot [
799     !     ! load slot number
800     !     temp0 ds-reg [] MOV
801     !     ! adjust stack pointer
802     !     ds-reg bootstrap-cell SUB
803     !     ! load object
804     !     temp1 ds-reg [] MOV
805     !     ! turn slot number into offset
806     !     fixnum>slot@
807     !     ! mask off tag
808     !     temp1 tag-bits get SHR
809     !     temp1 tag-bits get SHL
810     !     ! load slot value
811     !     temp0 temp1 temp0 [+] MOV
812     !     ! push to stack
813     !     ds-reg [] temp0 MOV
814     ! ] }
815     ! { string-nth-fast [
816     !     ! load string index from stack
817     !     temp0 ds-reg bootstrap-cell neg [+] MOV
818     !     temp0 tag-bits get SHR
819     !     ! load string from stack
820     !     temp1 ds-reg [] MOV
821     !     ! load character
822     !     temp0 8-bit-version-of temp0 temp1 string-offset [++] MOV
823     !     temp0 temp0 8-bit-version-of MOVZX
824     !     temp0 tag-bits get SHL
825     !     ! store character to stack
826     !     ds-reg bootstrap-cell SUB
827     !     ds-reg [] temp0 MOV
828     ! ] }
829     ! { tag [
830     !     ! load from stack
831     !     temp0 ds-reg [] MOV
832     !     ! compute tag
833     !     temp0/32 tag-mask get AND
834     !     ! tag the tag
835     !     temp0/32 tag-bits get SHL
836     !     ! push to stack
837     !     ds-reg [] temp0 MOV
838     ! ] }
839
840     ! ! ## Shufflers
841
842     ! ! ### Drops
843     ! { drop [ ds-reg bootstrap-cell SUB ] }
844     ! { 2drop [ ds-reg 2 bootstrap-cells SUB ] }
845     ! { 3drop [ ds-reg 3 bootstrap-cells SUB ] }
846     ! { 4drop [ ds-reg 4 bootstrap-cells SUB ] }
847
848     ! ! ### Dups
849     ! { dup [
850     !     temp0 ds-reg [] MOV
851     !     ds-reg bootstrap-cell ADD
852     !     ds-reg [] temp0 MOV
853     ! ] }
854     ! { 2dup [
855     !     temp0 ds-reg [] MOV
856     !     temp1 ds-reg bootstrap-cell neg [+] MOV
857     !     ds-reg 2 bootstrap-cells ADD
858     !     ds-reg [] temp0 MOV
859     !     ds-reg bootstrap-cell neg [+] temp1 MOV
860     ! ] }
861     ! { 3dup [
862     !     temp0 ds-reg [] MOV
863     !     temp1 ds-reg -1 bootstrap-cells [+] MOV
864     !     temp3 ds-reg -2 bootstrap-cells [+] MOV
865     !     ds-reg 3 bootstrap-cells ADD
866     !     ds-reg [] temp0 MOV
867     !     ds-reg -1 bootstrap-cells [+] temp1 MOV
868     !     ds-reg -2 bootstrap-cells [+] temp3 MOV
869     ! ] }
870     ! { 4dup [
871     !     temp0 ds-reg [] MOV
872     !     temp1 ds-reg -1 bootstrap-cells [+] MOV
873     !     temp2 ds-reg -2 bootstrap-cells [+] MOV
874     !     temp3 ds-reg -3 bootstrap-cells [+] MOV
875     !     ds-reg 4 bootstrap-cells ADD
876     !     ds-reg [] temp0 MOV
877     !     ds-reg -1 bootstrap-cells [+] temp1 MOV
878     !     ds-reg -2 bootstrap-cells [+] temp2 MOV
879     !     ds-reg -3 bootstrap-cells [+] temp3 MOV
880     ! ] }
881     ! { dupd [
882     !     temp0 ds-reg [] MOV
883     !     temp1 ds-reg -1 bootstrap-cells [+] MOV
884     !     ds-reg [] temp1 MOV
885     !     ds-reg bootstrap-cell ADD
886     !     ds-reg [] temp0 MOV
887     ! ] }
888
889     ! ! ### Misc shufflers
890     ! { over [
891     !     temp0 ds-reg -1 bootstrap-cells [+] MOV
892     !     ds-reg bootstrap-cell ADD
893     !     ds-reg [] temp0 MOV
894     ! ] }
895     ! { pick [
896     !     temp0 ds-reg -2 bootstrap-cells [+] MOV
897     !     ds-reg bootstrap-cell ADD
898     !     ds-reg [] temp0 MOV
899     ! ] }
900
901     ! ! ### Nips
902     ! { nip [
903     !     temp0 ds-reg [] MOV
904     !     ds-reg bootstrap-cell SUB
905     !     ds-reg [] temp0 MOV
906     ! ] }
907     ! { 2nip [
908     !     temp0 ds-reg [] MOV
909     !     ds-reg 2 bootstrap-cells SUB
910     !     ds-reg [] temp0 MOV
911     ! ] }
912
913     ! ! ### Swaps
914     ! { -rot [
915     !     temp0 ds-reg [] MOV
916     !     temp1 ds-reg -1 bootstrap-cells [+] MOV
917     !     temp3 ds-reg -2 bootstrap-cells [+] MOV
918     !     ds-reg -2 bootstrap-cells [+] temp0 MOV
919     !     ds-reg -1 bootstrap-cells [+] temp3 MOV
920     !     ds-reg [] temp1 MOV
921     ! ] }
922     ! { rot [
923     !     temp0 ds-reg [] MOV
924     !     temp1 ds-reg -1 bootstrap-cells [+] MOV
925     !     temp3 ds-reg -2 bootstrap-cells [+] MOV
926     !     ds-reg -2 bootstrap-cells [+] temp1 MOV
927     !     ds-reg -1 bootstrap-cells [+] temp0 MOV
928     !     ds-reg [] temp3 MOV
929     ! ] }
930     ! { swap [
931     !     temp0 ds-reg [] MOV
932     !     temp1 ds-reg bootstrap-cell neg [+] MOV
933     !     ds-reg bootstrap-cell neg [+] temp0 MOV
934     !     ds-reg [] temp1 MOV
935     ! ] }
936     ! { swapd [
937     !     temp0 ds-reg -1 bootstrap-cells [+] MOV
938     !     temp1 ds-reg -2 bootstrap-cells [+] MOV
939     !     ds-reg -2 bootstrap-cells [+] temp0 MOV
940     !     ds-reg -1 bootstrap-cells [+] temp1 MOV
941     ! ] }
942
943     ! ! ## Signal handling
944     ! { leaf-signal-handler [
945     !     jit-signal-handler-prolog
946     !     jit-save-context
947     !     temp0 vm-reg vm-signal-handler-addr-offset [+] MOV
948     !     temp0 CALL
949     !     jit-signal-handler-epilog
950     !     ! Pop the fake leaf frame along with our return address
951     !     leaf-stack-frame-size bootstrap-cell - RET
952     ! ] }
953     ! { signal-handler [
954     !     jit-signal-handler-prolog
955     !     jit-save-context
956     !     temp0 vm-reg vm-signal-handler-addr-offset [+] MOV
957     !     temp0 CALL
958     !     jit-signal-handler-epilog
959     !     0 RET
960     ! ] }
961 } define-sub-primitives
962
963 [ "bootstrap.arm.64" forget-vocab ] with-compilation-unit