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("r9"), SpecialRegister
.new("r8"), SpecialRegister
.new("r3")]
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.
72 if value
>= 0 && value
< 256
73 $asm.puts
"mov #{register.armOperand}, \##{value}"
74 elsif (~value
) >= 0 && (~value
) < 256
75 $asm.puts
"mvn #{register.armOperand}, \##{~value}"
76 elsif isARMv7
or isARMv7Traditional
77 $asm.puts
"movw #{register.armOperand}, \##{value & 0xffff}"
78 if (value
& 0xffff0000) !
= 0
79 $asm.puts
"movt #{register.armOperand}, \##{(value >> 16) & 0xffff}"
82 $asm.puts
"ldr #{register.armOperand}, =#{value}"
108 raise "Bad register #{name} for ARM at #{codeOriginString}"
129 raise "Bad register #{name} for ARM at #{codeOriginString}"
136 raise "Invalid immediate #{value} at #{codeOriginString}" if value
< 0 or value
> 255
143 raise "Bad offset at #{codeOriginString}" if offset
.value
< -0xff or offset
.value
> 0xfff
144 "[#{base.armOperand}, \##{offset.value}]"
150 raise "Bad offset at #{codeOriginString}" if offset
.value !
= 0
151 "[#{base.armOperand}, #{index.armOperand}, lsl \##{scaleShift}]"
155 class AbsoluteAddress
157 raise "Unconverted absolute address at #{codeOriginString}"
166 def armEmitLea(destination
)
167 if destination
== base
168 $asm.puts
"adds #{destination.armOperand}, \##{offset.value}"
170 $asm.puts
"adds #{destination.armOperand}, #{base.armOperand}, \##{offset.value}"
176 def armEmitLea(destination
)
177 raise "Malformed BaseIndex, offset should be zero at #{codeOriginString}" unless offset
.value
== 0
178 $asm.puts
"add #{destination.armOperand}, #{base.armOperand}, #{index.armOperand}, lsl \##{scaleShift}"
182 # FIXME: we could support AbsoluteAddress for lea, but we don't.
185 # Actual lowering code follows.
189 def getModifiedListARM
190 raise unless $activeBackend == "ARM"
191 getModifiedListARMCommon
194 def getModifiedListARMv7
195 raise unless $activeBackend == "ARMv7"
196 getModifiedListARMCommon
199 def getModifiedListARMv7_TRADITIONAL
200 raise unless $activeBackend == "ARMv7_TRADITIONAL"
201 getModifiedListARMCommon
204 def getModifiedListARMCommon
206 result
= riscLowerSimpleBranchOps(result
)
207 result
= riscLowerHardBranchOps(result
)
208 result
= riscLowerShiftOps(result
)
209 result
= riscLowerMalformedAddresses(result
) {
211 if address
.is_a
? BaseIndex
212 address
.offset
.value
== 0
213 elsif address
.is_a
? Address
214 (-0xff..0xfff
).include? address
.offset
.value
219 result
= riscLowerMalformedAddressesDouble(result
)
220 result
= riscLowerMisplacedImmediates(result
, ["storeb", "storei", "storep", "storeq"])
221 result
= riscLowerMalformedImmediates(result
, 0..0xff
)
222 result
= riscLowerMisplacedAddresses(result
)
223 result
= riscLowerRegisterReuse(result
)
224 result
= assignRegistersToTemporaries(result
, :gpr, ARM_EXTRA_GPRS
)
225 result
= assignRegistersToTemporaries(result
, :fpr, ARM_EXTRA_FPRS
)
230 def armOperands(operands
)
231 operands
.map
{|v
| v
.armOperand
}.join(", ")
234 def armFlippedOperands(operands
)
235 armOperands([operands
[-1]] + operands
[0..-2])
238 def emitArmCompact(opcode2
, opcode3
, operands
)
239 if operands
.size
== 3
240 $asm.puts
"#{opcode3} #{armFlippedOperands(operands)}"
242 raise unless operands
.size
== 2
243 raise unless operands
[1].register
?
244 if operands
[0].immediate
?
245 $asm.puts
"#{opcode3} #{operands[1].armOperand}, #{operands[1].armOperand}, #{operands[0].armOperand}"
247 $asm.puts
"#{opcode2} #{armFlippedOperands(operands)}"
252 def emitArm(opcode
, operands
)
253 if operands
.size
== 3
254 $asm.puts
"#{opcode} #{armFlippedOperands(operands)}"
256 raise unless operands
.size
== 2
257 $asm.puts
"#{opcode} #{operands[1].armOperand}, #{operands[1].armOperand}, #{operands[0].armOperand}"
261 def emitArmDoubleBranch(branchOpcode
, operands
)
262 $asm.puts
"vcmpe.f64 #{armOperands(operands[0..1])}"
263 $asm.puts
"vmrs apsr_nzcv, fpscr"
264 $asm.puts
"#{branchOpcode} #{operands[2].asmLabel}"
267 def emitArmTest(operands
)
271 mask
= Immediate
.new(codeOrigin
, -1)
275 raise "Expected 2 or 3 operands but got #{operands.size} at #{codeOriginString}"
278 if mask
.immediate
? and mask
.value
== -1
279 $asm.puts
"tst #{value.armOperand}, #{value.armOperand}"
281 $asm.puts
"tst #{value.armOperand}, #{mask.armOperand}"
285 def emitArmCompare(operands
, code
)
286 $asm.puts
"movs #{operands[2].armOperand}, \#0"
287 $asm.puts
"cmp #{operands[0].armOperand}, #{operands[1].armOperand}"
288 $asm.puts
"it #{code}"
289 $asm.puts
"mov#{code} #{operands[2].armOperand}, \#1"
292 def emitArmTestSet(operands
, code
)
293 $asm.puts
"movs #{operands[-1].armOperand}, \#0"
294 emitArmTest(operands
)
295 $asm.puts
"it #{code}"
296 $asm.puts
"mov#{code} #{operands[-1].armOperand}, \#1"
301 raise unless $activeBackend == "ARM"
306 raise unless $activeBackend == "ARMv7"
310 def lowerARMv7_TRADITIONAL
311 raise unless $activeBackend == "ARMv7_TRADITIONAL"
316 $asm.codeOrigin codeOriginString
if $enableCodeOriginComments
317 $asm.annotation annotation
if $enableInstrAnnotations
320 when "addi", "addp", "addis", "addps"
321 if opcode
== "addis" or opcode
== "addps"
326 if operands
.size
== 3 and operands
[0].immediate
?
327 raise unless operands
[1].register
?
328 raise unless operands
[2].register
?
329 if operands
[0].value
== 0 and suffix
.empty
?
330 unless operands
[1] == operands
[2]
331 $asm.puts
"mov #{operands[2].armOperand}, #{operands[1].armOperand}"
334 $asm.puts
"adds #{operands[2].armOperand}, #{operands[1].armOperand}, #{operands[0].armOperand}"
336 elsif operands
.size
== 3 and operands
[0].immediate
?
337 raise unless operands
[1].register
?
338 raise unless operands
[2].register
?
339 $asm.puts
"adds #{armFlippedOperands(operands)}"
341 if operands
[0].immediate
?
342 unless Immediate
.new(nil, 0) == operands
[0]
343 $asm.puts
"adds #{armFlippedOperands(operands)}"
346 $asm.puts
"add#{suffix} #{armFlippedOperands(operands)}"
350 emitArmCompact("ands", "and", operands
)
352 emitArmCompact("orrs", "orr", operands
)
354 emitArmCompact("orrs", "orrs", operands
)
356 emitArmCompact("eors", "eor", operands
)
357 when "lshifti", "lshiftp"
358 emitArmCompact("lsls", "lsls", operands
)
359 when "rshifti", "rshiftp"
360 emitArmCompact("asrs", "asrs", operands
)
361 when "urshifti", "urshiftp"
362 emitArmCompact("lsrs", "lsrs", operands
)
364 emitArm("mul", operands
)
365 when "subi", "subp", "subis"
366 emitArmCompact("subs", "subs", operands
)
368 $asm.puts
"rsbs #{operands[0].armOperand}, #{operands[0].armOperand}, \#0"
370 $asm.puts
"mvns #{operands[0].armOperand}, #{operands[0].armOperand}"
371 when "loadi", "loadis", "loadp"
372 $asm.puts
"ldr #{armFlippedOperands(operands)}"
373 when "storei", "storep"
374 $asm.puts
"str #{armOperands(operands)}"
376 $asm.puts
"ldrb #{armFlippedOperands(operands)}"
378 $asm.puts
"ldrsb.w #{armFlippedOperands(operands)}"
380 $asm.puts
"strb #{armOperands(operands)}"
382 $asm.puts
"ldrh #{armFlippedOperands(operands)}"
384 $asm.puts
"ldrsh.w #{armFlippedOperands(operands)}"
386 $asm.puts
"strh #{armOperands(operands)}"
388 $asm.puts
"vldr.64 #{armFlippedOperands(operands)}"
390 $asm.puts
"vstr.64 #{armOperands(operands)}"
392 emitArm("vadd.f64", operands
)
394 emitArm("vdiv.f64", operands
)
396 emitArm("vsub.f64", operands
)
398 emitArm("vmul.f64", operands
)
400 $asm.puts
"vsqrt.f64 #{armFlippedOperands(operands)}"
402 $asm.puts
"vmov #{operands[1].armSingle}, #{operands[0].armOperand}"
403 $asm.puts
"vcvt.f64.s32 #{operands[1].armOperand}, #{operands[1].armSingle}"
405 emitArmDoubleBranch("beq", operands
)
407 $asm.puts
"vcmpe.f64 #{armOperands(operands[0..1])}"
408 $asm.puts
"vmrs apsr_nzcv, fpscr"
409 isUnordered
= LocalLabel
.unique("bdneq")
410 $asm.puts
"bvs #{LocalLabelReference.new(codeOrigin, isUnordered).asmLabel}"
411 $asm.puts
"bne #{operands[2].asmLabel}"
412 isUnordered
.lower("ARM")
414 emitArmDoubleBranch("bgt", operands
)
416 emitArmDoubleBranch("bge", operands
)
418 emitArmDoubleBranch("bmi", operands
)
420 emitArmDoubleBranch("bls", operands
)
422 $asm.puts
"vcmpe.f64 #{armOperands(operands[0..1])}"
423 $asm.puts
"vmrs apsr_nzcv, fpscr"
424 $asm.puts
"bvs #{operands[2].asmLabel}"
425 $asm.puts
"beq #{operands[2].asmLabel}"
427 emitArmDoubleBranch("bne", operands
)
429 emitArmDoubleBranch("bhi", operands
)
431 emitArmDoubleBranch("bpl", operands
)
433 emitArmDoubleBranch("blt", operands
)
435 emitArmDoubleBranch("ble", operands
)
437 # FIXME: may be a good idea to just get rid of this instruction, since the interpreter
438 # currently does not use it.
439 raise "ARM does not support this opcode yet, #{codeOrigin}"
441 $asm.puts
"vcvt.s32.f64 #{ARM_SCRATCH_FPR.armSingle}, #{operands[0].armOperand}"
442 $asm.puts
"vmov #{operands[1].armOperand}, #{ARM_SCRATCH_FPR.armSingle}"
444 $asm.puts
"vcvt.s32.f64 #{ARM_SCRATCH_FPR.armSingle}, #{operands[0].armOperand}"
445 $asm.puts
"vmov #{operands[1].armOperand}, #{ARM_SCRATCH_FPR.armSingle}"
446 $asm.puts
"vcvt.f64.s32 #{ARM_SCRATCH_FPR.armOperand}, #{ARM_SCRATCH_FPR.armSingle}"
447 emitArmDoubleBranch("bne", [ARM_SCRATCH_FPR
, operands
[0], operands
[2]])
448 $asm.puts
"tst #{operands[1].armOperand}, #{operands[1].armOperand}"
449 $asm.puts
"beq #{operands[2].asmLabel}"
451 # FIXME: either support this or remove it.
452 raise "ARM does not support this opcode yet, #{codeOrigin}"
454 $asm.puts
"pop #{operands[0].armOperand}"
456 $asm.puts
"push #{operands[0].armOperand}"
458 if operands
[0].immediate
?
459 armMoveImmediate(operands
[0].value
, operands
[1])
461 $asm.puts
"mov #{armFlippedOperands(operands)}"
465 when "bieq", "bpeq", "bbeq"
466 if Immediate
.new(nil, 0) == operands
[0]
467 $asm.puts
"tst #{operands[1].armOperand}, #{operands[1].armOperand}"
468 elsif Immediate
.new(nil, 0) == operands
[1]
469 $asm.puts
"tst #{operands[0].armOperand}, #{operands[0].armOperand}"
471 $asm.puts
"cmp #{armOperands(operands[0..1])}"
473 $asm.puts
"beq #{operands[2].asmLabel}"
474 when "bineq", "bpneq", "bbneq"
475 if Immediate
.new(nil, 0) == operands
[0]
476 $asm.puts
"tst #{operands[1].armOperand}, #{operands[1].armOperand}"
477 elsif Immediate
.new(nil, 0) == operands
[1]
478 $asm.puts
"tst #{operands[0].armOperand}, #{operands[0].armOperand}"
480 $asm.puts
"cmp #{armOperands(operands[0..1])}"
482 $asm.puts
"bne #{operands[2].asmLabel}"
483 when "bia", "bpa", "bba"
484 $asm.puts
"cmp #{armOperands(operands[0..1])}"
485 $asm.puts
"bhi #{operands[2].asmLabel}"
486 when "biaeq", "bpaeq", "bbaeq"
487 $asm.puts
"cmp #{armOperands(operands[0..1])}"
488 $asm.puts
"bhs #{operands[2].asmLabel}"
489 when "bib", "bpb", "bbb"
490 $asm.puts
"cmp #{armOperands(operands[0..1])}"
491 $asm.puts
"blo #{operands[2].asmLabel}"
492 when "bibeq", "bpbeq", "bbbeq"
493 $asm.puts
"cmp #{armOperands(operands[0..1])}"
494 $asm.puts
"bls #{operands[2].asmLabel}"
495 when "bigt", "bpgt", "bbgt"
496 $asm.puts
"cmp #{armOperands(operands[0..1])}"
497 $asm.puts
"bgt #{operands[2].asmLabel}"
498 when "bigteq", "bpgteq", "bbgteq"
499 $asm.puts
"cmp #{armOperands(operands[0..1])}"
500 $asm.puts
"bge #{operands[2].asmLabel}"
501 when "bilt", "bplt", "bblt"
502 $asm.puts
"cmp #{armOperands(operands[0..1])}"
503 $asm.puts
"blt #{operands[2].asmLabel}"
504 when "bilteq", "bplteq", "bblteq"
505 $asm.puts
"cmp #{armOperands(operands[0..1])}"
506 $asm.puts
"ble #{operands[2].asmLabel}"
507 when "btiz", "btpz", "btbz"
508 emitArmTest(operands
)
509 $asm.puts
"beq #{operands[-1].asmLabel}"
510 when "btinz", "btpnz", "btbnz"
511 emitArmTest(operands
)
512 $asm.puts
"bne #{operands[-1].asmLabel}"
513 when "btis", "btps", "btbs"
514 emitArmTest(operands
)
515 $asm.puts
"bmi #{operands[-1].asmLabel}"
517 if operands
[0].label
?
518 $asm.puts
"b #{operands[0].asmLabel}"
520 $asm.puts
"mov pc, #{operands[0].armOperand}"
522 if not isARMv7
and not isARMv7Traditional
526 if operands
[0].label
?
527 $asm.puts
"blx #{operands[0].asmLabel}"
529 $asm.puts
"blx #{operands[0].armOperand}"
535 when "cieq", "cpeq", "cbeq"
536 emitArmCompare(operands
, "eq")
537 when "cineq", "cpneq", "cbneq"
538 emitArmCompare(operands
, "ne")
539 when "cia", "cpa", "cba"
540 emitArmCompare(operands
, "hi")
541 when "ciaeq", "cpaeq", "cbaeq"
542 emitArmCompare(operands
, "hs")
543 when "cib", "cpb", "cbb"
544 emitArmCompare(operands
, "lo")
545 when "cibeq", "cpbeq", "cbbeq"
546 emitArmCompare(operands
, "ls")
547 when "cigt", "cpgt", "cbgt"
548 emitArmCompare(operands
, "gt")
549 when "cigteq", "cpgteq", "cbgteq"
550 emitArmCompare(operands
, "ge")
551 when "cilt", "cplt", "cblt"
552 emitArmCompare(operands
, "lt")
553 when "cilteq", "cplteq", "cblteq"
554 emitArmCompare(operands
, "le")
555 when "tis", "tbs", "tps"
556 emitArmTestSet(operands
, "mi")
557 when "tiz", "tbz", "tpz"
558 emitArmTestSet(operands
, "eq")
559 when "tinz", "tbnz", "tpnz"
560 emitArmTestSet(operands
, "ne")
562 $asm.puts
"ldr #{operands[1].armOperand}, [sp, \##{operands[0].value * 4}]"
564 $asm.puts
"str #{operands[1].armOperand}, [sp, \##{operands[0].value * 4}]"
566 $asm.puts
"vmov #{operands[2].armOperand}, #{operands[0].armOperand}, #{operands[1].armOperand}"
568 $asm.puts
"vmov #{operands[1].armOperand}, #{operands[2].armOperand}, #{operands[0].armOperand}"
570 $asm.puts
"bvs #{operands[0].asmLabel}"
572 $asm.puts
"bmi #{operands[0].asmLabel}"
574 $asm.puts
"beq #{operands[0].asmLabel}"
576 $asm.puts
"bne #{operands[0].asmLabel}"
578 operands
[0].armEmitLea(operands
[1])
580 raise "Wrong number of arguments to smull in #{self.inspect} at #{codeOriginString}" unless operands
.length
== 4
581 $asm.puts
"smull #{operands[2].armOperand}, #{operands[3].armOperand}, #{operands[0].armOperand}, #{operands[1].armOperand}"