1 # Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
2 # Copyright (C) 2013 University of Szeged. All rights reserved.
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions
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.
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.
34 when "ARMv7_TRADITIONAL", "ARM"
37 raise "bad value for $activeBackend: #{$activeBackend}"
41 def isARMv7Traditional
43 when "ARMv7_TRADITIONAL"
48 raise "bad value for $activeBackend: #{$activeBackend}"
54 doubleOperand
= armOperand
55 raise "Bogus register name #{doubleOperand}" unless doubleOperand
=~
/^d/
56 "s" +
($~
.post_match
.to_i
* 2).to_s
66 ARM_EXTRA_GPRS
= [SpecialRegister
.new("r6"), SpecialRegister
.new("r10"), SpecialRegister
.new("r12")]
67 ARM_EXTRA_FPRS
= [SpecialRegister
.new("d7")]
68 ARM_SCRATCH_FPR
= SpecialRegister
.new("d6")
70 def armMoveImmediate(value
, register
)
71 # Currently we only handle the simple cases, and fall back to mov/movt for the complex ones.
73 $asm.puts
"mov #{register.armOperand}, (#{value})"
74 elsif value
>= 0 && value
< 256
75 $asm.puts
"mov #{register.armOperand}, \##{value}"
76 elsif (~value
) >= 0 && (~value
) < 256
77 $asm.puts
"mvn #{register.armOperand}, \##{~value}"
78 elsif isARMv7
or isARMv7Traditional
79 $asm.puts
"movw #{register.armOperand}, \##{value & 0xffff}"
80 if (value
& 0xffff0000) !
= 0
81 $asm.puts
"movt #{register.armOperand}, \##{(value >> 16) & 0xffff}"
84 $asm.puts
"ldr #{register.armOperand}, =#{value}"
106 isARMv7
? "r7" : "r11"
114 raise "Bad register #{name} for ARM at #{codeOriginString}"
135 raise "Bad register #{name} for ARM at #{codeOriginString}"
142 raise "Invalid immediate #{value} at #{codeOriginString}" if value
< 0 or value
> 255
149 raise "Bad offset at #{codeOriginString}" if offset
.value
< -0xff or offset
.value
> 0xfff
150 "[#{base.armOperand}, \##{offset.value}]"
156 raise "Bad offset at #{codeOriginString}" if offset
.value !
= 0
157 "[#{base.armOperand}, #{index.armOperand}, lsl \##{scaleShift}]"
161 class AbsoluteAddress
163 raise "Unconverted absolute address at #{codeOriginString}"
172 def armEmitLea(destination
)
173 if destination
== base
174 $asm.puts
"adds #{destination.armOperand}, \##{offset.value}"
176 $asm.puts
"adds #{destination.armOperand}, #{base.armOperand}, \##{offset.value}"
182 def armEmitLea(destination
)
183 raise "Malformed BaseIndex, offset should be zero at #{codeOriginString}" unless offset
.value
== 0
184 $asm.puts
"add #{destination.armOperand}, #{base.armOperand}, #{index.armOperand}, lsl \##{scaleShift}"
188 # FIXME: we could support AbsoluteAddress for lea, but we don't.
191 # Actual lowering code follows.
195 def getModifiedListARM
196 raise unless $activeBackend == "ARM"
197 getModifiedListARMCommon
200 def getModifiedListARMv7
201 raise unless $activeBackend == "ARMv7"
202 getModifiedListARMCommon
205 def getModifiedListARMv7_TRADITIONAL
206 raise unless $activeBackend == "ARMv7_TRADITIONAL"
207 getModifiedListARMCommon
210 def getModifiedListARMCommon
212 result
= riscLowerSimpleBranchOps(result
)
213 result
= riscLowerHardBranchOps(result
)
214 result
= riscLowerShiftOps(result
)
215 result
= riscLowerMalformedAddresses(result
) {
217 if address
.is_a
? BaseIndex
218 address
.offset
.value
== 0
219 elsif address
.is_a
? Address
220 (-0xff..0xfff
).include? address
.offset
.value
225 result
= riscLowerMalformedAddressesDouble(result
)
226 result
= riscLowerMisplacedImmediates(result
, ["storeb", "storei", "storep", "storeq"])
227 result
= riscLowerMalformedImmediates(result
, 0..0xff
)
228 result
= riscLowerMisplacedAddresses(result
)
229 result
= riscLowerRegisterReuse(result
)
230 result
= assignRegistersToTemporaries(result
, :gpr, ARM_EXTRA_GPRS
)
231 result
= assignRegistersToTemporaries(result
, :fpr, ARM_EXTRA_FPRS
)
236 def armOperands(operands
)
237 operands
.map
{|v
| v
.armOperand
}.join(", ")
240 def armFlippedOperands(operands
)
241 armOperands([operands
[-1]] + operands
[0..-2])
244 def emitArmCompact(opcode2
, opcode3
, operands
)
245 if operands
.size
== 3
246 $asm.puts
"#{opcode3} #{armFlippedOperands(operands)}"
248 raise unless operands
.size
== 2
249 raise unless operands
[1].register
?
250 if operands
[0].immediate
?
251 $asm.puts
"#{opcode3} #{operands[1].armOperand}, #{operands[1].armOperand}, #{operands[0].armOperand}"
253 $asm.puts
"#{opcode2} #{armFlippedOperands(operands)}"
258 def emitArm(opcode
, operands
)
259 if operands
.size
== 3
260 $asm.puts
"#{opcode} #{armFlippedOperands(operands)}"
262 raise unless operands
.size
== 2
263 $asm.puts
"#{opcode} #{operands[1].armOperand}, #{operands[1].armOperand}, #{operands[0].armOperand}"
267 def emitArmDoubleBranch(branchOpcode
, operands
)
268 $asm.puts
"vcmpe.f64 #{armOperands(operands[0..1])}"
269 $asm.puts
"vmrs apsr_nzcv, fpscr"
270 $asm.puts
"#{branchOpcode} #{operands[2].asmLabel}"
273 def emitArmTest(operands
)
277 mask
= Immediate
.new(codeOrigin
, -1)
281 raise "Expected 2 or 3 operands but got #{operands.size} at #{codeOriginString}"
284 if mask
.immediate
? and mask
.value
== -1
285 $asm.puts
"tst #{value.armOperand}, #{value.armOperand}"
287 $asm.puts
"tst #{value.armOperand}, #{mask.armOperand}"
291 def emitArmCompare(operands
, code
)
292 $asm.puts
"movs #{operands[2].armOperand}, \#0"
293 $asm.puts
"cmp #{operands[0].armOperand}, #{operands[1].armOperand}"
294 $asm.puts
"it #{code}"
295 $asm.puts
"mov#{code} #{operands[2].armOperand}, \#1"
298 def emitArmTestSet(operands
, code
)
299 $asm.puts
"movs #{operands[-1].armOperand}, \#0"
300 emitArmTest(operands
)
301 $asm.puts
"it #{code}"
302 $asm.puts
"mov#{code} #{operands[-1].armOperand}, \#1"
307 raise unless $activeBackend == "ARM"
312 raise unless $activeBackend == "ARMv7"
316 def lowerARMv7_TRADITIONAL
317 raise unless $activeBackend == "ARMv7_TRADITIONAL"
322 $asm.codeOrigin codeOriginString
if $enableCodeOriginComments
323 $asm.annotation annotation
if $enableInstrAnnotations
326 when "addi", "addp", "addis", "addps"
327 if opcode
== "addis" or opcode
== "addps"
332 if operands
.size
== 3 and operands
[0].immediate
?
333 raise unless operands
[1].register
?
334 raise unless operands
[2].register
?
335 if operands
[0].value
== 0 and suffix
.empty
?
336 unless operands
[1] == operands
[2]
337 $asm.puts
"mov #{operands[2].armOperand}, #{operands[1].armOperand}"
340 $asm.puts
"adds #{operands[2].armOperand}, #{operands[1].armOperand}, #{operands[0].armOperand}"
342 elsif operands
.size
== 3 and operands
[0].register
?
343 raise unless operands
[1].register
?
344 raise unless operands
[2].register
?
345 $asm.puts
"adds #{armFlippedOperands(operands)}"
347 if operands
[0].immediate
?
348 unless Immediate
.new(nil, 0) == operands
[0]
349 $asm.puts
"adds #{armFlippedOperands(operands)}"
352 $asm.puts
"add#{suffix} #{armFlippedOperands(operands)}"
356 emitArmCompact("ands", "and", operands
)
358 emitArmCompact("orrs", "orr", operands
)
360 emitArmCompact("orrs", "orrs", operands
)
362 emitArmCompact("eors", "eor", operands
)
363 when "lshifti", "lshiftp"
364 emitArmCompact("lsls", "lsls", operands
)
365 when "rshifti", "rshiftp"
366 emitArmCompact("asrs", "asrs", operands
)
367 when "urshifti", "urshiftp"
368 emitArmCompact("lsrs", "lsrs", operands
)
370 emitArm("mul", operands
)
371 when "subi", "subp", "subis"
372 emitArmCompact("subs", "subs", operands
)
374 $asm.puts
"rsbs #{operands[0].armOperand}, #{operands[0].armOperand}, \#0"
376 $asm.puts
"mvns #{operands[0].armOperand}, #{operands[0].armOperand}"
377 when "loadi", "loadis", "loadp"
378 $asm.puts
"ldr #{armFlippedOperands(operands)}"
379 when "storei", "storep"
380 $asm.puts
"str #{armOperands(operands)}"
382 $asm.puts
"ldrb #{armFlippedOperands(operands)}"
384 $asm.puts
"ldrsb.w #{armFlippedOperands(operands)}"
386 $asm.puts
"strb #{armOperands(operands)}"
388 $asm.puts
"ldrh #{armFlippedOperands(operands)}"
390 $asm.puts
"ldrsh.w #{armFlippedOperands(operands)}"
392 $asm.puts
"strh #{armOperands(operands)}"
394 $asm.puts
"vldr.64 #{armFlippedOperands(operands)}"
396 $asm.puts
"vstr.64 #{armOperands(operands)}"
398 emitArm("vadd.f64", operands
)
400 emitArm("vdiv.f64", operands
)
402 emitArm("vsub.f64", operands
)
404 emitArm("vmul.f64", operands
)
406 $asm.puts
"vsqrt.f64 #{armFlippedOperands(operands)}"
408 $asm.puts
"vmov #{operands[1].armSingle}, #{operands[0].armOperand}"
409 $asm.puts
"vcvt.f64.s32 #{operands[1].armOperand}, #{operands[1].armSingle}"
411 emitArmDoubleBranch("beq", operands
)
413 $asm.puts
"vcmpe.f64 #{armOperands(operands[0..1])}"
414 $asm.puts
"vmrs apsr_nzcv, fpscr"
415 isUnordered
= LocalLabel
.unique("bdneq")
416 $asm.puts
"bvs #{LocalLabelReference.new(codeOrigin, isUnordered).asmLabel}"
417 $asm.puts
"bne #{operands[2].asmLabel}"
418 isUnordered
.lower("ARM")
420 emitArmDoubleBranch("bgt", operands
)
422 emitArmDoubleBranch("bge", operands
)
424 emitArmDoubleBranch("bmi", operands
)
426 emitArmDoubleBranch("bls", operands
)
428 $asm.puts
"vcmpe.f64 #{armOperands(operands[0..1])}"
429 $asm.puts
"vmrs apsr_nzcv, fpscr"
430 $asm.puts
"bvs #{operands[2].asmLabel}"
431 $asm.puts
"beq #{operands[2].asmLabel}"
433 emitArmDoubleBranch("bne", operands
)
435 emitArmDoubleBranch("bhi", operands
)
437 emitArmDoubleBranch("bpl", operands
)
439 emitArmDoubleBranch("blt", operands
)
441 emitArmDoubleBranch("ble", operands
)
443 # FIXME: may be a good idea to just get rid of this instruction, since the interpreter
444 # currently does not use it.
445 raise "ARM does not support this opcode yet, #{codeOrigin}"
447 $asm.puts
"vcvt.s32.f64 #{ARM_SCRATCH_FPR.armSingle}, #{operands[0].armOperand}"
448 $asm.puts
"vmov #{operands[1].armOperand}, #{ARM_SCRATCH_FPR.armSingle}"
450 $asm.puts
"vcvt.s32.f64 #{ARM_SCRATCH_FPR.armSingle}, #{operands[0].armOperand}"
451 $asm.puts
"vmov #{operands[1].armOperand}, #{ARM_SCRATCH_FPR.armSingle}"
452 $asm.puts
"vcvt.f64.s32 #{ARM_SCRATCH_FPR.armOperand}, #{ARM_SCRATCH_FPR.armSingle}"
453 emitArmDoubleBranch("bne", [ARM_SCRATCH_FPR
, operands
[0], operands
[2]])
454 $asm.puts
"tst #{operands[1].armOperand}, #{operands[1].armOperand}"
455 $asm.puts
"beq #{operands[2].asmLabel}"
457 # FIXME: either support this or remove it.
458 raise "ARM does not support this opcode yet, #{codeOrigin}"
462 $asm.puts
"pop { #{op.armOperand} }"
467 $asm.puts
"push { #{op.armOperand} }"
469 when "popCalleeSaves"
471 $asm.puts
"pop {r4-r6, r8-r11}"
473 $asm.puts
"pop {r4-r10}"
475 when "pushCalleeSaves"
477 $asm.puts
"push {r4-r6, r8-r11}"
479 $asm.puts
"push {r4-r10}"
482 if operands
[0].immediate
?
483 armMoveImmediate(operands
[0].value
, operands
[1])
485 $asm.puts
"mov #{armFlippedOperands(operands)}"
488 $asm.puts
"movw #{operands[1].armOperand}, \#:lower16:#{operands[0].value}"
489 $asm.puts
"movt #{operands[1].armOperand}, \#:upper16:#{operands[0].value}"
492 when "bieq", "bpeq", "bbeq"
493 if Immediate
.new(nil, 0) == operands
[0]
494 $asm.puts
"tst #{operands[1].armOperand}, #{operands[1].armOperand}"
495 elsif Immediate
.new(nil, 0) == operands
[1]
496 $asm.puts
"tst #{operands[0].armOperand}, #{operands[0].armOperand}"
498 $asm.puts
"cmp #{armOperands(operands[0..1])}"
500 $asm.puts
"beq #{operands[2].asmLabel}"
501 when "bineq", "bpneq", "bbneq"
502 if Immediate
.new(nil, 0) == operands
[0]
503 $asm.puts
"tst #{operands[1].armOperand}, #{operands[1].armOperand}"
504 elsif Immediate
.new(nil, 0) == operands
[1]
505 $asm.puts
"tst #{operands[0].armOperand}, #{operands[0].armOperand}"
507 $asm.puts
"cmp #{armOperands(operands[0..1])}"
509 $asm.puts
"bne #{operands[2].asmLabel}"
510 when "bia", "bpa", "bba"
511 $asm.puts
"cmp #{armOperands(operands[0..1])}"
512 $asm.puts
"bhi #{operands[2].asmLabel}"
513 when "biaeq", "bpaeq", "bbaeq"
514 $asm.puts
"cmp #{armOperands(operands[0..1])}"
515 $asm.puts
"bhs #{operands[2].asmLabel}"
516 when "bib", "bpb", "bbb"
517 $asm.puts
"cmp #{armOperands(operands[0..1])}"
518 $asm.puts
"blo #{operands[2].asmLabel}"
519 when "bibeq", "bpbeq", "bbbeq"
520 $asm.puts
"cmp #{armOperands(operands[0..1])}"
521 $asm.puts
"bls #{operands[2].asmLabel}"
522 when "bigt", "bpgt", "bbgt"
523 $asm.puts
"cmp #{armOperands(operands[0..1])}"
524 $asm.puts
"bgt #{operands[2].asmLabel}"
525 when "bigteq", "bpgteq", "bbgteq"
526 $asm.puts
"cmp #{armOperands(operands[0..1])}"
527 $asm.puts
"bge #{operands[2].asmLabel}"
528 when "bilt", "bplt", "bblt"
529 $asm.puts
"cmp #{armOperands(operands[0..1])}"
530 $asm.puts
"blt #{operands[2].asmLabel}"
531 when "bilteq", "bplteq", "bblteq"
532 $asm.puts
"cmp #{armOperands(operands[0..1])}"
533 $asm.puts
"ble #{operands[2].asmLabel}"
534 when "btiz", "btpz", "btbz"
535 emitArmTest(operands
)
536 $asm.puts
"beq #{operands[-1].asmLabel}"
537 when "btinz", "btpnz", "btbnz"
538 emitArmTest(operands
)
539 $asm.puts
"bne #{operands[-1].asmLabel}"
540 when "btis", "btps", "btbs"
541 emitArmTest(operands
)
542 $asm.puts
"bmi #{operands[-1].asmLabel}"
544 if operands
[0].label
?
545 $asm.puts
"b #{operands[0].asmLabel}"
547 $asm.puts
"mov pc, #{operands[0].armOperand}"
549 if not isARMv7
and not isARMv7Traditional
553 if operands
[0].label
?
554 $asm.puts
"blx #{operands[0].asmLabel}"
556 $asm.puts
"blx #{operands[0].armOperand}"
562 when "cieq", "cpeq", "cbeq"
563 emitArmCompare(operands
, "eq")
564 when "cineq", "cpneq", "cbneq"
565 emitArmCompare(operands
, "ne")
566 when "cia", "cpa", "cba"
567 emitArmCompare(operands
, "hi")
568 when "ciaeq", "cpaeq", "cbaeq"
569 emitArmCompare(operands
, "hs")
570 when "cib", "cpb", "cbb"
571 emitArmCompare(operands
, "lo")
572 when "cibeq", "cpbeq", "cbbeq"
573 emitArmCompare(operands
, "ls")
574 when "cigt", "cpgt", "cbgt"
575 emitArmCompare(operands
, "gt")
576 when "cigteq", "cpgteq", "cbgteq"
577 emitArmCompare(operands
, "ge")
578 when "cilt", "cplt", "cblt"
579 emitArmCompare(operands
, "lt")
580 when "cilteq", "cplteq", "cblteq"
581 emitArmCompare(operands
, "le")
582 when "tis", "tbs", "tps"
583 emitArmTestSet(operands
, "mi")
584 when "tiz", "tbz", "tpz"
585 emitArmTestSet(operands
, "eq")
586 when "tinz", "tbnz", "tpnz"
587 emitArmTestSet(operands
, "ne")
589 $asm.puts
"ldr #{operands[1].armOperand}, [sp, \##{operands[0].value * 4}]"
591 $asm.puts
"str #{operands[1].armOperand}, [sp, \##{operands[0].value * 4}]"
593 $asm.puts
"vmov #{operands[2].armOperand}, #{operands[0].armOperand}, #{operands[1].armOperand}"
595 $asm.puts
"vmov #{operands[1].armOperand}, #{operands[2].armOperand}, #{operands[0].armOperand}"
597 $asm.puts
"bvs #{operands[0].asmLabel}"
599 $asm.puts
"bmi #{operands[0].asmLabel}"
601 $asm.puts
"beq #{operands[0].asmLabel}"
603 $asm.puts
"bne #{operands[0].asmLabel}"
605 operands
[0].armEmitLea(operands
[1])
607 raise "Wrong number of arguments to smull in #{self.inspect} at #{codeOriginString}" unless operands
.length
== 4
608 $asm.puts
"smull #{operands[2].armOperand}, #{operands[3].armOperand}, #{operands[0].armOperand}, #{operands[1].armOperand}"
612 $asm.puts
"bic #{operands[2].armOperand}, #{operands[0].armOperand}, #{operands[1].armOperand}"