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