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