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