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