Add write barriers to the LLInt
[WebKit-https.git] / Source / JavaScriptCore / offlineasm / arm64.rb
1 # Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 # 1. Redistributions of source code must retain the above copyright
7 #    notice, this list of conditions and the following disclaimer.
8 # 2. Redistributions in binary form must reproduce the above copyright
9 #    notice, this list of conditions and the following disclaimer in the
10 #    documentation and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
13 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
14 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
16 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
22 # THE POSSIBILITY OF SUCH DAMAGE.
23
24 require "ast"
25 require "opt"
26 require "risc"
27
28 # Naming conventions:
29 #
30 # x<number>  => GPR. This is both the generic name of the register, and the name used
31 #               to indicate that the register is used in 64-bit mode.
32 # w<number>  => GPR in 32-bit mode. This is the low 32-bits of the GPR. If it is
33 #               mutated then the high 32-bit part of the register is zero filled.
34 # q<number>  => FPR. This is the generic name of the register.
35 # d<number>  => FPR used as an IEEE 64-bit binary floating point number (i.e. double).
36 #
37 # GPR conventions, to match the baseline JIT:
38 #
39 #  x0  => return value, cached result, first argument, t0, a0, r0
40 #  x1  => t1, a1, r1
41 #  x2  => t2, a2
42 #  x3  => a3
43 #  x9  => (nonArgGPR1 in baseline)
44 # x10  => t4 (unused in baseline)
45 # x11  => t5 (unused in baseline)
46 # x12  => t6 (unused in baseline)
47 # x13  => scratch (unused in baseline)
48 # x16  => scratch
49 # x17  => scratch
50 # x23  => t3
51 # x27  => csr1 (tagTypeNumber)
52 # x28  => csr2 (tagMask)
53 # x29  => cfr
54 #  sp  => sp
55 #  lr  => lr
56 #
57 # FPR conentions, to match the baseline JIT:
58 #
59 #  q0  => ft0
60 #  q1  => ft1
61 #  q2  => ft2
62 #  q3  => ft3
63 #  q4  => ft4 (unused in baseline)
64 #  q5  => ft5 (unused in baseline)
65 # q31  => scratch
66
67 def arm64GPRName(name, kind)
68     raise "bad GPR name #{name}" unless name =~ /^x/
69     number = name[1..-1]
70     case kind
71     when :int
72         "w" + number
73     when :ptr
74         "x" + number
75     else
76         raise "Wrong kind: #{kind}"
77     end
78 end
79
80 def arm64FPRName(name, kind)
81     raise "bad FPR kind #{kind}" unless kind == :double
82     raise "bad FPR name #{name}" unless name =~ /^q/
83     "d" + name[1..-1]
84 end
85
86 class SpecialRegister
87     def arm64Operand(kind)
88         case @name
89         when /^x/
90             arm64GPRName(@name, kind)
91         when /^q/
92             arm64FPRName(@name, kind)
93         else
94             raise "Bad name: #{@name}"
95         end
96     end
97 end
98
99 ARM64_EXTRA_GPRS = [SpecialRegister.new("x16"), SpecialRegister.new("x17"), SpecialRegister.new("x13")]
100 ARM64_EXTRA_FPRS = [SpecialRegister.new("q31")]
101
102 class RegisterID
103     def arm64Operand(kind)
104         case @name
105         when 't0', 'a0', 'r0'
106             arm64GPRName('x0', kind)
107         when 't1', 'a1', 'r1'
108             arm64GPRName('x1', kind)
109         when 't2', 'a2'
110             arm64GPRName('x2', kind)
111         when 'a3'
112             arm64GPRName('x3', kind)
113         when 't3'
114             arm64GPRName('x23', kind)
115         when 't4'
116             arm64GPRName('x10', kind)
117         when 't5'
118             arm64GPRName('x11', kind)
119         when 't6'
120             arm64GPRName('x12', kind)
121         when 'cfr'
122             arm64GPRName('x29', kind)
123         when 'csr1'
124             arm64GPRName('x27', kind)
125         when 'csr2'
126             arm64GPRName('x28', kind)
127         when 'sp'
128             'sp'
129         when 'lr'
130             'lr'
131         else
132             raise "Bad register name #{@name} at #{codeOriginString}"
133         end
134     end
135 end
136
137 class FPRegisterID
138     def arm64Operand(kind)
139         case @name
140         when 'ft0'
141             arm64FPRName('q0', kind)
142         when 'ft1'
143             arm64FPRName('q1', kind)
144         when 'ft2'
145             arm64FPRName('q2', kind)
146         when 'ft3'
147             arm64FPRName('q3', kind)
148         when 'ft4'
149             arm64FPRName('q4', kind)
150         when 'ft5'
151             arm64FPRName('q5', kind)
152         else "Bad register name #{@name} at #{codeOriginString}"
153         end
154     end
155 end
156
157 class Immediate
158     def arm64Operand(kind)
159         raise "Invalid immediate #{value} at #{codeOriginString}" if value < 0 or value > 4095
160         "\##{value}"
161     end
162 end
163
164 class Address
165     def arm64Operand(kind)
166         raise "Invalid offset #{offset.value} at #{codeOriginString}" if offset.value < -255 or offset.value > 4095
167         "[#{base.arm64Operand(:ptr)}, \##{offset.value}]"
168     end
169     
170     def arm64EmitLea(destination, kind)
171         $asm.puts "add #{destination.arm64Operand(kind)}, #{base.arm64Operand(kind)}, \##{offset.value}"
172     end
173 end
174
175 class BaseIndex
176     def arm64Operand(kind)
177         raise "Invalid offset #{offset.value} at #{codeOriginString}" if offset.value != 0
178         "[#{base.arm64Operand(:ptr)}, #{index.arm64Operand(:ptr)}, lsl \##{scaleShift}]"
179     end
180
181     def arm64EmitLea(destination, kind)
182         $asm.puts "add #{destination.arm64Operand(kind)}, #{base.arm64Operand(kind)}, #{index.arm64Operand(kind)}, lsl \##{scaleShift}"
183     end
184 end
185
186 class AbsoluteAddress
187     def arm64Operand(kind)
188         raise "Unconverted absolute address #{address.value} at #{codeOriginString}"
189     end
190 end
191
192 # FIXME: We could support AbsoluteAddress for lea, but we don't.
193
194 #
195 # Actual lowering code follows.
196 #
197
198 class Sequence
199     def getModifiedListARM64
200         result = @list
201         result = riscLowerNot(result)
202         result = riscLowerSimpleBranchOps(result)
203         result = riscLowerHardBranchOps64(result)
204         result = riscLowerShiftOps(result)
205         result = riscLowerMalformedAddresses(result) {
206             | node, address |
207             case node.opcode
208             when "loadb", "loadbs", "storeb", /^bb/, /^btb/, /^cb/, /^tb/
209                 size = 1
210             when "loadh", "loadhs"
211                 size = 2
212             when "loadi", "loadis", "storei", "addi", "andi", "lshifti", "muli", "negi",
213                 "noti", "ori", "rshifti", "urshifti", "subi", "xori", /^bi/, /^bti/,
214                 /^ci/, /^ti/, "addis", "subis", "mulis", "smulli", "leai"
215                 size = 4
216             when "loadp", "storep", "loadq", "storeq", "loadd", "stored", "lshiftp", "lshiftq", "negp", "negq", "rshiftp", "rshiftq",
217                 "urshiftp", "urshiftq", "addp", "addq", "mulp", "mulq", "andp", "andq", "orp", "orq", "subp", "subq", "xorp", "xorq", "addd",
218                 "divd", "subd", "muld", "sqrtd", /^bp/, /^bq/, /^btp/, /^btq/, /^cp/, /^cq/, /^tp/, /^tq/, /^bd/,
219                 "jmp", "call", "leap", "leaq"
220                 size = 8
221             else
222                 raise "Bad instruction #{node.opcode} for heap access at #{node.codeOriginString}"
223             end
224             
225             if address.is_a? BaseIndex
226                 address.offset.value == 0 and
227                     (node.opcode =~ /^lea/ or address.scale == 1 or address.scale == size)
228             elsif address.is_a? Address
229                 (-255..4095).include? address.offset.value
230             else
231                 false
232             end
233         }
234         result = riscLowerMisplacedImmediates(result, ["storeb", "storei", "storep", "storeq"])
235         result = riscLowerMalformedImmediates(result, 0..4095)
236         result = riscLowerMisplacedAddresses(result)
237         result = riscLowerMalformedAddresses(result) {
238             | node, address |
239             case node.opcode
240             when /^load/
241                 true
242             when /^store/
243                 not (address.is_a? Address and address.offset.value < 0)
244             when /^lea/
245                 true
246             else
247                 raise "Bad instruction #{node.opcode} for heap access at #{node.codeOriginString}"
248             end
249         }
250         result = riscLowerTest(result)
251         result = assignRegistersToTemporaries(result, :gpr, ARM64_EXTRA_GPRS)
252         result = assignRegistersToTemporaries(result, :fpr, ARM64_EXTRA_FPRS)
253         return result
254     end
255 end
256
257 def arm64Operands(operands, kinds)
258     if kinds.is_a? Array
259         raise "Mismatched operand lists: #{operands.inspect} and #{kinds.inspect}" if operands.size != kinds.size
260     else
261         kinds = operands.map{ kinds }
262     end
263     (0...operands.size).map {
264         | index |
265         operands[index].arm64Operand(kinds[index])
266     }.join(', ')
267 end
268
269 def arm64FlippedOperands(operands, kinds)
270     if kinds.is_a? Array
271         kinds = [kinds[-1]] + kinds[0..-2]
272     end
273     arm64Operands([operands[-1]] + operands[0..-2], kinds)
274 end
275
276 # TAC = three address code.
277 def arm64TACOperands(operands, kind)
278     if operands.size == 3
279         return arm64FlippedOperands(operands, kind)
280     end
281     
282     raise unless operands.size == 2
283     
284     return operands[1].arm64Operand(kind) + ", " + arm64FlippedOperands(operands, kind)
285 end
286
287 def emitARM64Add(opcode, operands, kind)
288     if operands.size == 3
289         raise unless operands[1].register?
290         raise unless operands[2].register?
291         
292         if operands[0].immediate?
293             if operands[0].value == 0 and flag !~ /s$/
294                 unless operands[1] == operands[2]
295                     $asm.puts "mov #{arm64FlippedOperands(operands[1..2], kind)}"
296                 end
297             else
298                 $asm.puts "#{opcode} #{arm64Operands(operands.reverse, kind)}"
299             end
300             return
301         end
302         
303         raise unless operands[0].register?
304         $asm.puts "#{opcode} #{arm64FlippedOperands(operands, kind)}"
305         return
306     end
307     
308     raise unless operands.size == 2
309     
310     if operands[0].immediate? and operands[0].value == 0 and opcode !~ /s$/
311         return
312     end
313     
314     $asm.puts "#{opcode} #{arm64TACOperands(operands, kind)}"
315 end
316
317 def emitARM64Unflipped(opcode, operands, kind)
318     $asm.puts "#{opcode} #{arm64Operands(operands, kind)}"
319 end
320
321 def emitARM64TAC(opcode, operands, kind)
322     $asm.puts "#{opcode} #{arm64TACOperands(operands, kind)}"
323 end
324
325 def emitARM64(opcode, operands, kind)
326     $asm.puts "#{opcode} #{arm64FlippedOperands(operands, kind)}"
327 end
328
329 def emitARM64Access(opcode, opcodeNegativeOffset, register, memory, kind)
330     if memory.is_a? Address and memory.offset.value < 0
331         $asm.puts "#{opcodeNegativeOffset} #{register.arm64Operand(kind)}, #{memory.arm64Operand(kind)}"
332         return
333     end
334     
335     $asm.puts "#{opcode} #{register.arm64Operand(kind)}, #{memory.arm64Operand(kind)}"
336 end
337
338 def emitARM64Shift(opcodeRegs, opcodeImmediate, operands, kind)
339     if operands.size == 3 and operands[1].immediate?
340         magicNumbers = yield operands[1].value
341         $asm.puts "#{opcodeImmediate} #{operands[2].arm64Operand(kind)}, #{operands[0].arm64Operand(kind)}, \##{magicNumbers[0]}, \##{magicNumbers[1]}"
342         return
343     end
344     
345     if operands.size == 2 and operands[0].immediate?
346         magicNumbers = yield operands[0].value
347         $asm.puts "#{opcodeImmediate} #{operands[1].arm64Operand(kind)}, #{operands[1].arm64Operand(kind)}, \##{magicNumbers[0]}, \##{magicNumbers[1]}"
348         return
349     end
350     
351     emitARM64TAC(opcodeRegs, operands, kind)
352 end
353
354 def emitARM64Branch(opcode, operands, kind, branchOpcode)
355     emitARM64Unflipped(opcode, operands[0..-2], kind)
356     $asm.puts "#{branchOpcode} #{operands[-1].asmLabel}"
357 end
358
359 def emitARM64Compare(operands, kind, compareCode)
360     emitARM64Unflipped("subs #{arm64GPRName('xzr', kind)}, ", operands[0..-2], kind)
361     $asm.puts "csinc #{operands[-1].arm64Operand(:int)}, wzr, wzr, #{compareCode}"
362 end
363
364 def emitARM64MoveImmediate(value, target)
365     first = true
366     isNegative = value < 0
367     [48, 32, 16, 0].each {
368         | shift |
369         currentValue = (value >> shift) & 0xffff
370         next if currentValue == (isNegative ? 0xffff : 0) and shift != 0
371         if first
372             if isNegative
373                 $asm.puts "movn #{target.arm64Operand(:ptr)}, \##{(~currentValue) & 0xffff}, lsl \##{shift}"
374             else
375                 $asm.puts "movz #{target.arm64Operand(:ptr)}, \##{currentValue}, lsl \##{shift}"
376             end
377             first = false
378         else
379             $asm.puts "movk #{target.arm64Operand(:ptr)}, \##{currentValue}, lsl \##{shift}"
380         end
381     }
382 end
383
384 class Instruction
385     def lowerARM64
386         $asm.comment codeOriginString
387         $asm.annotation annotation if $enableInstrAnnotations
388
389         case opcode
390         when 'addi'
391             emitARM64Add("add", operands, :int)
392         when 'addis'
393             emitARM64Add("adds", operands, :int)
394         when 'addp'
395             emitARM64Add("add", operands, :ptr)
396         when 'addps'
397             emitARM64Add("adds", operands, :ptr)
398         when 'addq'
399             emitARM64Add("add", operands, :ptr)
400         when "andi"
401             emitARM64TAC("and", operands, :int)
402         when "andp"
403             emitARM64TAC("and", operands, :ptr)
404         when "andq"
405             emitARM64TAC("and", operands, :ptr)
406         when "ori"
407             emitARM64TAC("orr", operands, :int)
408         when "orp"
409             emitARM64TAC("orr", operands, :ptr)
410         when "orq"
411             emitARM64TAC("orr", operands, :ptr)
412         when "xori"
413             emitARM64TAC("eor", operands, :int)
414         when "xorp"
415             emitARM64TAC("eor", operands, :ptr)
416         when "xorq"
417             emitARM64TAC("eor", operands, :ptr)
418         when "lshifti"
419             emitARM64Shift("lslv", "ubfm", operands, :int) {
420                 | value |
421                 [32 - value, 31 - value]
422             }
423         when "lshiftp"
424             emitARM64Shift("lslv", "ubfm", operands, :ptr) {
425                 | value |
426                 [64 - value, 63 - value]
427             }
428         when "lshiftq"
429             emitARM64Shift("lslv", "ubfm", operands, :ptr) {
430                 | value |
431                 [64 - value, 63 - value]
432             }
433         when "rshifti"
434             emitARM64Shift("asrv", "sbfm", operands, :int) {
435                 | value |
436                 [value, 31]
437             }
438         when "rshiftp"
439             emitARM64Shift("asrv", "sbfm", operands, :ptr) {
440                 | value |
441                 [value, 63]
442             }
443         when "rshiftq"
444             emitARM64Shift("asrv", "sbfm", operands, :ptr) {
445                 | value |
446                 [value, 63]
447             }
448         when "urshifti"
449             emitARM64Shift("lsrv", "ubfm", operands, :int) {
450                 | value |
451                 [value, 31]
452             }
453         when "urshiftp"
454             emitARM64Shift("lsrv", "ubfm", operands, :ptr) {
455                 | value |
456                 [value, 63]
457             }
458         when "urshiftq"
459             emitARM64Shift("lsrv", "ubfm", operands, :ptr) {
460                 | value |
461                 [value, 63]
462             }
463         when "muli"
464             $asm.puts "madd #{arm64TACOperands(operands, :int)}, wzr"
465         when "mulp"
466             $asm.puts "madd #{arm64TACOperands(operands, :ptr)}, xzr"
467         when "mulq"
468             $asm.puts "madd #{arm64TACOperands(operands, :ptr)}, xzr"
469         when "subi"
470             emitARM64TAC("sub", operands, :int)
471         when "subp"
472             emitARM64TAC("sub", operands, :ptr)
473         when "subq"
474             emitARM64TAC("sub", operands, :ptr)
475         when "subis"
476             emitARM64TAC("subs", operands, :int)
477         when "negi"
478             $asm.puts "sub #{operands[0].arm64Operand(:int)}, wzr, #{operands[0].arm64Operand(:int)}"
479         when "negp"
480             $asm.puts "sub #{operands[0].arm64Operand(:ptr)}, xzr, #{operands[0].arm64Operand(:ptr)}"
481         when "negq"
482             $asm.puts "sub #{operands[0].arm64Operand(:ptr)}, xzr, #{operands[0].arm64Operand(:ptr)}"
483         when "loadi"
484             emitARM64Access("ldr", "ldur", operands[1], operands[0], :int)
485         when "loadis"
486             emitARM64Access("ldrsw", "ldursw", operands[1], operands[0], :ptr)
487         when "loadp"
488             emitARM64Access("ldr", "ldur", operands[1], operands[0], :ptr)
489         when "loadq"
490             emitARM64Access("ldr", "ldur", operands[1], operands[0], :ptr)
491         when "storei"
492             emitARM64Unflipped("str", operands, :int)
493         when "storep"
494             emitARM64Unflipped("str", operands, :ptr)
495         when "storeq"
496             emitARM64Unflipped("str", operands, :ptr)
497         when "loadb"
498             emitARM64Access("ldrb", "ldurb", operands[1], operands[0], :int)
499         when "loadbs"
500             emitARM64Access("ldrsb", "ldursb", operands[1], operands[0], :int)
501         when "storeb"
502             emitARM64Unflipped("strb", operands, :int)
503         when "loadh"
504             emitARM64Access("ldrh", "ldurh", operands[1], operands[0], :int)
505         when "loadhs"
506             emitARM64Access("ldrsh", "ldursh", operands[1], operands[0], :int)
507         when "storeh"
508             emitARM64Unflipped("strh", operands, :int)
509         when "loadd"
510             emitARM64Access("ldr", "ldur", operands[1], operands[0], :double)
511         when "stored"
512             emitARM64Unflipped("str", operands, :double)
513         when "addd"
514             emitARM64TAC("fadd", operands, :double)
515         when "divd"
516             emitARM64TAC("fdiv", operands, :double)
517         when "subd"
518             emitARM64TAC("fsub", operands, :double)
519         when "muld"
520             emitARM64TAC("fmul", operands, :double)
521         when "sqrtd"
522             emitARM64("fsqrt", operands, :double)
523         when "ci2d"
524             emitARM64("scvtf", operands, [:int, :double])
525         when "bdeq"
526             emitARM64Branch("fcmp", operands, :double, "b.eq")
527         when "bdneq"
528             emitARM64Unflipped("fcmp", operands[0..1], :double)
529             isUnordered = LocalLabel.unique("bdneq")
530             $asm.puts "b.vs #{LocalLabelReference.new(codeOrigin, isUnordered).asmLabel}"
531             $asm.puts "b.ne #{operands[2].asmLabel}"
532             isUnordered.lower("ARM64")
533         when "bdgt"
534             emitARM64Branch("fcmp", operands, :double, "b.gt")
535         when "bdgteq"
536             emitARM64Branch("fcmp", operands, :double, "b.ge")
537         when "bdlt"
538             emitARM64Branch("fcmp", operands, :double, "b.mi")
539         when "bdlteq"
540             emitARM64Branch("fcmp", operands, :double, "b.ls")
541         when "bdequn"
542             emitARM64Unflipped("fcmp", operands[0..1], :double)
543             $asm.puts "b.vs #{operands[2].asmLabel}"
544             $asm.puts "b.eq #{operands[2].asmLabel}"
545         when "bdnequn"
546             emitARM64Branch("fcmp", operands, :double, "b.ne")
547         when "bdgtun"
548             emitARM64Branch("fcmp", operands, :double, "b.hi")
549         when "bdgtequn"
550             emitARM64Branch("fcmp", operands, :double, "b.pl")
551         when "bdltun"
552             emitARM64Branch("fcmp", operands, :double, "b.lt")
553         when "bdltequn"
554             emitARM64Branch("fcmp", operands, :double, "b.le")
555         when "btd2i"
556             # FIXME: May be a good idea to just get rid of this instruction, since the interpreter
557             # currently does not use it.
558             raise "ARM64 does not support this opcode yet, #{codeOriginString}"
559         when "td2i"
560             emitARM64("fcvtzs", operands, [:double, :int])
561         when "bcd2i"
562             # FIXME: Remove this instruction, or use it and implement it. Currently it's not
563             # used.
564             raise "ARM64 does not support this opcode yet, #{codeOriginString}"
565         when "movdz"
566             # FIXME: Remove it or support it.
567             raise "ARM64 does not support this opcode yet, #{codeOriginString}"
568         when "pop"
569             operands.each_slice(2) {
570                 | ops |
571                 # Note that the operands are in the reverse order of the case for push.
572                 # This is due to the fact that order matters for pushing and popping, and 
573                 # on platforms that only push/pop one slot at a time they pop their 
574                 # arguments in the reverse order that they were pushed. In order to remain 
575                 # compatible with those platforms we assume here that that's what has been done.
576
577                 # So for example, if we did push(A, B, C, D), we would then pop(D, C, B, A).
578                 # But since the ordering of arguments doesn't change on arm64 between the stp and ldp 
579                 # instructions we need to flip flop the argument positions that were passed to us.
580                 $asm.puts "ldp #{ops[1].arm64Operand(:ptr)}, #{ops[0].arm64Operand(:ptr)}, [sp], #16"
581             }
582         when "push"
583             operands.each_slice(2) {
584                 | ops |
585                 $asm.puts "stp #{ops[0].arm64Operand(:ptr)}, #{ops[1].arm64Operand(:ptr)}, [sp, #-16]!"
586             }
587         when "popLRAndFP"
588             $asm.puts "ldp fp, lr, [sp], #16"
589         when "pushLRAndFP"
590             $asm.puts "stp fp, lr, [sp, #-16]!"
591         when "popCalleeSaves"
592             $asm.puts "ldp x28, x27, [sp], #16"
593             $asm.puts "ldp x26, x25, [sp], #16"
594             $asm.puts "ldp x24, x23, [sp], #16"
595             $asm.puts "ldp x22, x21, [sp], #16"
596             $asm.puts "ldp x20, x19, [sp], #16"
597         when "pushCalleeSaves"
598             $asm.puts "stp x20, x19, [sp, #-16]!"
599             $asm.puts "stp x22, x21, [sp, #-16]!"
600             $asm.puts "stp x24, x23, [sp, #-16]!"
601             $asm.puts "stp x26, x25, [sp, #-16]!"
602             $asm.puts "stp x28, x27, [sp, #-16]!"
603         when "move"
604             if operands[0].immediate?
605                 emitARM64MoveImmediate(operands[0].value, operands[1])
606             else
607                 emitARM64("mov", operands, :ptr)
608             end
609         when "sxi2p"
610             emitARM64("sxtw", operands, :ptr)
611         when "sxi2q"
612             emitARM64("sxtw", operands, :ptr)
613         when "zxi2p"
614             emitARM64("uxtw", operands, :ptr)
615         when "zxi2q"
616             emitARM64("uxtw", operands, :ptr)
617         when "nop"
618             $asm.puts "nop"
619         when "bieq", "bbeq"
620             if operands[0].immediate? and operands[0].value == 0
621                 $asm.puts "cbz #{operands[1].arm64Operand(:int)}, #{operands[2].asmLabel}"
622             elsif operands[1].immediate? and operands[1].value == 0
623                 $asm.puts "cbz #{operands[0].arm64Operand(:int)}, #{operands[2].asmLabel}"
624             else
625                 emitARM64Branch("subs wzr, ", operands, :int, "b.eq")
626             end
627         when "bpeq"
628             if operands[0].immediate? and operands[0].value == 0
629                 $asm.puts "cbz #{operands[1].arm64Operand(:ptr)}, #{operands[2].asmLabel}"
630             elsif operands[1].immediate? and operands[1].value == 0
631                 $asm.puts "cbz #{operands[0].arm64Operand(:ptr)}, #{operands[2].asmLabel}"
632             else
633                 emitARM64Branch("subs xzr, ", operands, :ptr, "b.eq")
634             end
635         when "bqeq"
636             if operands[0].immediate? and operands[0].value == 0
637                 $asm.puts "cbz #{operands[1].arm64Operand(:ptr)}, #{operands[2].asmLabel}"
638             elsif operands[1].immediate? and operands[1].value == 0
639                 $asm.puts "cbz #{operands[0].arm64Operand(:ptr)}, #{operands[2].asmLabel}"
640             else
641                 emitARM64Branch("subs xzr, ", operands, :ptr, "b.eq")
642             end
643         when "bineq", "bbneq"
644             if operands[0].immediate? and operands[0].value == 0
645                 $asm.puts "cbnz #{operands[1].arm64Operand(:int)}, #{operands[2].asmLabel}"
646             elsif operands[1].immediate? and operands[1].value == 0
647                 $asm.puts "cbnz #{operands[0].arm64Operand(:int)}, #{operands[2].asmLabel}"
648             else
649                 emitARM64Branch("subs wzr, ", operands, :int, "b.ne")
650             end
651         when "bpneq"
652             if operands[0].immediate? and operands[0].value == 0
653                 $asm.puts "cbnz #{operands[1].arm64Operand(:ptr)}, #{operands[2].asmLabel}"
654             elsif operands[1].immediate? and operands[1].value == 0
655                 $asm.puts "cbnz #{operands[0].arm64Operand(:ptr)}, #{operands[2].asmLabel}"
656             else
657                 emitARM64Branch("subs xzr, ", operands, :ptr, "b.ne")
658             end
659         when "bqneq"
660             if operands[0].immediate? and operands[0].value == 0
661                 $asm.puts "cbnz #{operands[1].arm64Operand(:ptr)}, #{operands[2].asmLabel}"
662             elsif operands[1].immediate? and operands[1].value == 0
663                 $asm.puts "cbnz #{operands[0].arm64Operand(:ptr)}, #{operands[2].asmLabel}"
664             else
665                 emitARM64Branch("subs xzr, ", operands, :ptr, "b.ne")
666             end
667         when "bia", "bba"
668             emitARM64Branch("subs wzr, ", operands, :int, "b.hi")
669         when "bpa"
670             emitARM64Branch("subs xzr, ", operands, :ptr, "b.hi")
671         when "bqa"
672             emitARM64Branch("subs xzr, ", operands, :ptr, "b.hi")
673         when "biaeq", "bbaeq"
674             emitARM64Branch("subs wzr, ", operands, :int, "b.hs")
675         when "bpaeq"
676             emitARM64Branch("subs xzr, ", operands, :ptr, "b.hs")
677         when "bqaeq"
678             emitARM64Branch("subs xzr, ", operands, :ptr, "b.hs")
679         when "bib", "bbb"
680             emitARM64Branch("subs wzr, ", operands, :int, "b.lo")
681         when "bpb"
682             emitARM64Branch("subs xzr, ", operands, :ptr, "b.lo")
683         when "bqb"
684             emitARM64Branch("subs xzr, ", operands, :ptr, "b.lo")
685         when "bibeq", "bbbeq"
686             emitARM64Branch("subs wzr, ", operands, :int, "b.ls")
687         when "bpbeq"
688             emitARM64Branch("subs xzr, ", operands, :ptr, "b.ls")
689         when "bqbeq"
690             emitARM64Branch("subs xzr, ", operands, :ptr, "b.ls")
691         when "bigt", "bbgt"
692             emitARM64Branch("subs wzr, ", operands, :int, "b.gt")
693         when "bpgt"
694             emitARM64Branch("subs xzr, ", operands, :ptr, "b.gt")
695         when "bqgt"
696             emitARM64Branch("subs xzr, ", operands, :ptr, "b.gt")
697         when "bigteq", "bbgteq"
698             emitARM64Branch("subs wzr, ", operands, :int, "b.ge")
699         when "bpgteq"
700             emitARM64Branch("subs xzr, ", operands, :ptr, "b.ge")
701         when "bqgteq"
702             emitARM64Branch("subs xzr, ", operands, :ptr, "b.ge")
703         when "bilt", "bblt"
704             emitARM64Branch("subs wzr, ", operands, :int, "b.lt")
705         when "bplt"
706             emitARM64Branch("subs xzr, ", operands, :ptr, "b.lt")
707         when "bqlt"
708             emitARM64Branch("subs xzr, ", operands, :ptr, "b.lt")
709         when "bilteq", "bblteq"
710             emitARM64Branch("subs wzr, ", operands, :int, "b.le")
711         when "bplteq"
712             emitARM64Branch("subs xzr, ", operands, :ptr, "b.le")
713         when "bqlteq"
714             emitARM64Branch("subs xzr, ", operands, :ptr, "b.le")
715         when "jmp"
716             if operands[0].label?
717                 $asm.puts "b #{operands[0].asmLabel}"
718             else
719                 emitARM64Unflipped("br", operands, :ptr)
720             end
721         when "call"
722             if operands[0].label?
723                 $asm.puts "bl #{operands[0].asmLabel}"
724             else
725                 emitARM64Unflipped("blr", operands, :ptr)
726             end
727         when "break"
728             $asm.puts "brk \#0"
729         when "ret"
730             $asm.puts "ret"
731         when "cieq", "cbeq"
732             emitARM64Compare(operands, :int, "ne")
733         when "cpeq"
734             emitARM64Compare(operands, :ptr, "ne")
735         when "cqeq"
736             emitARM64Compare(operands, :ptr, "ne")
737         when "cineq", "cbneq"
738             emitARM64Compare(operands, :int, "eq")
739         when "cpneq"
740             emitARM64Compare(operands, :ptr, "eq")
741         when "cqneq"
742             emitARM64Compare(operands, :ptr, "eq")
743         when "cia", "cba"
744             emitARM64Compare(operands, :int, "ls")
745         when "cpa"
746             emitARM64Compare(operands, :ptr, "ls")
747         when "cqa"
748             emitARM64Compare(operands, :ptr, "ls")
749         when "ciaeq", "cbaeq"
750             emitARM64Compare(operands, :int, "lo")
751         when "cpaeq"
752             emitARM64Compare(operands, :ptr, "lo")
753         when "cqaeq"
754             emitARM64Compare(operands, :ptr, "lo")
755         when "cib", "cbb"
756             emitARM64Compare(operands, :int, "hs")
757         when "cpb"
758             emitARM64Compare(operands, :ptr, "hs")
759         when "cqb"
760             emitARM64Compare(operands, :ptr, "hs")
761         when "cibeq", "cbbeq"
762             emitARM64Compare(operands, :int, "hi")
763         when "cpbeq"
764             emitARM64Compare(operands, :ptr, "hi")
765         when "cqbeq"
766             emitARM64Compare(operands, :ptr, "hi")
767         when "cilt", "cblt"
768             emitARM64Compare(operands, :int, "ge")
769         when "cplt"
770             emitARM64Compare(operands, :ptr, "ge")
771         when "cqlt"
772             emitARM64Compare(operands, :ptr, "ge")
773         when "cilteq", "cblteq"
774             emitARM64Compare(operands, :int, "gt")
775         when "cplteq"
776             emitARM64Compare(operands, :ptr, "gt")
777         when "cqlteq"
778             emitARM64Compare(operands, :ptr, "gt")
779         when "cigt", "cbgt"
780             emitARM64Compare(operands, :int, "le")
781         when "cpgt"
782             emitARM64Compare(operands, :ptr, "le")
783         when "cqgt"
784             emitARM64Compare(operands, :ptr, "le")
785         when "cigteq", "cbgteq"
786             emitARM64Compare(operands, :int, "lt")
787         when "cpgteq"
788             emitARM64Compare(operands, :ptr, "lt")
789         when "cqgteq"
790             emitARM64Compare(operands, :ptr, "lt")
791         when "peek"
792             $asm.puts "ldr #{operands[1].arm64Operand(:ptr)}, [sp, \##{operands[0].value * 8}]"
793         when "poke"
794             $asm.puts "str #{operands[1].arm64Operand(:ptr)}, [sp, \##{operands[0].value * 8}]"
795         when "fp2d"
796             emitARM64("fmov", operands, [:ptr, :double])
797         when "fq2d"
798             emitARM64("fmov", operands, [:ptr, :double])
799         when "fd2p"
800             emitARM64("fmov", operands, [:double, :ptr])
801         when "fd2q"
802             emitARM64("fmov", operands, [:double, :ptr])
803         when "bo"
804             $asm.puts "b.vs #{operands[0].asmLabel}"
805         when "bs"
806             $asm.puts "b.mi #{operands[0].asmLabel}"
807         when "bz"
808             $asm.puts "b.eq #{operands[0].asmLabel}"
809         when "bnz"
810             $asm.puts "b.ne #{operands[0].asmLabel}"
811         when "leai"
812             operands[0].arm64EmitLea(operands[1], :int)
813         when "leap"
814             operands[0].arm64EmitLea(operands[1], :ptr)
815         when "leaq"
816             operands[0].arm64EmitLea(operands[1], :ptr)
817         when "smulli"
818             $asm.puts "smaddl #{operands[2].arm64Operand(:ptr)}, #{operands[0].arm64Operand(:int)}, #{operands[1].arm64Operand(:int)}, xzr"
819         when "memfence"
820             $asm.puts "dmb sy"
821         else
822             lowerDefault
823         end
824     end
825 end
826