1# Copyright (C) 2011, 2012 Apple Inc. All rights reserved. 2# Copyright (C) 2013 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 25require "config" 26require "ast" 27require "opt" 28require "risc" 29 30def isARMv7 31 case $activeBackend 32 when "ARMv7" 33 true 34 when "ARMv7_TRADITIONAL", "ARM" 35 false 36 else 37 raise "bad value for $activeBackend: #{$activeBackend}" 38 end 39end 40 41def isARMv7Traditional 42 case $activeBackend 43 when "ARMv7_TRADITIONAL" 44 true 45 when "ARMv7", "ARM" 46 false 47 else 48 raise "bad value for $activeBackend: #{$activeBackend}" 49 end 50end 51 52class Node 53 def armSingle 54 doubleOperand = armOperand 55 raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^d/ 56 "s" + ($~.post_match.to_i * 2).to_s 57 end 58end 59 60class SpecialRegister 61 def armOperand 62 @name 63 end 64end 65 66ARM_EXTRA_GPRS = [SpecialRegister.new("r9"), SpecialRegister.new("r8"), SpecialRegister.new("r3")] 67ARM_EXTRA_FPRS = [SpecialRegister.new("d7")] 68ARM_SCRATCH_FPR = SpecialRegister.new("d6") 69 70def 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}" 80 end 81 else 82 $asm.puts "ldr #{register.armOperand}, =#{value}" 83 end 84end 85 86class RegisterID 87 def armOperand 88 case name 89 when "t0", "a0", "r0" 90 "r0" 91 when "t1", "a1", "r1" 92 "r1" 93 when "t2", "a2" 94 "r2" 95 when "a3" 96 "r3" 97 when "t3" 98 "r4" 99 when "t4" 100 "r10" 101 when "cfr" 102 "r5" 103 when "lr" 104 "lr" 105 when "sp" 106 "sp" 107 else 108 raise "Bad register #{name} for ARM at #{codeOriginString}" 109 end 110 end 111end 112 113class FPRegisterID 114 def armOperand 115 case name 116 when "ft0", "fr" 117 "d0" 118 when "ft1" 119 "d1" 120 when "ft2" 121 "d2" 122 when "ft3" 123 "d3" 124 when "ft4" 125 "d4" 126 when "ft5" 127 "d5" 128 else 129 raise "Bad register #{name} for ARM at #{codeOriginString}" 130 end 131 end 132end 133 134class Immediate 135 def armOperand 136 raise "Invalid immediate #{value} at #{codeOriginString}" if value < 0 or value > 255 137 "\##{value}" 138 end 139end 140 141class Address 142 def armOperand 143 raise "Bad offset at #{codeOriginString}" if offset.value < -0xff or offset.value > 0xfff 144 "[#{base.armOperand}, \##{offset.value}]" 145 end 146end 147 148class BaseIndex 149 def armOperand 150 raise "Bad offset at #{codeOriginString}" if offset.value != 0 151 "[#{base.armOperand}, #{index.armOperand}, lsl \##{scaleShift}]" 152 end 153end 154 155class AbsoluteAddress 156 def armOperand 157 raise "Unconverted absolute address at #{codeOriginString}" 158 end 159end 160 161# 162# Lea support. 163# 164 165class Address 166 def armEmitLea(destination) 167 if destination == base 168 $asm.puts "adds #{destination.armOperand}, \##{offset.value}" 169 else 170 $asm.puts "adds #{destination.armOperand}, #{base.armOperand}, \##{offset.value}" 171 end 172 end 173end 174 175class BaseIndex 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}" 179 end 180end 181 182# FIXME: we could support AbsoluteAddress for lea, but we don't. 183 184# 185# Actual lowering code follows. 186# 187 188class Sequence 189 def getModifiedListARM 190 raise unless $activeBackend == "ARM" 191 getModifiedListARMCommon 192 end 193 194 def getModifiedListARMv7 195 raise unless $activeBackend == "ARMv7" 196 getModifiedListARMCommon 197 end 198 199 def getModifiedListARMv7_TRADITIONAL 200 raise unless $activeBackend == "ARMv7_TRADITIONAL" 201 getModifiedListARMCommon 202 end 203 204 def getModifiedListARMCommon 205 result = @list 206 result = riscLowerSimpleBranchOps(result) 207 result = riscLowerHardBranchOps(result) 208 result = riscLowerShiftOps(result) 209 result = riscLowerMalformedAddresses(result) { 210 | node, address | 211 if address.is_a? BaseIndex 212 address.offset.value == 0 213 elsif address.is_a? Address 214 (-0xff..0xfff).include? address.offset.value 215 else 216 false 217 end 218 } 219 result = riscLowerMalformedAddressesDouble(result) 220 result = riscLowerMisplacedImmediates(result, ["storeb", "storei", "storep"]) 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) 226 return result 227 end 228end 229 230def armOperands(operands) 231 operands.map{|v| v.armOperand}.join(", ") 232end 233 234def armFlippedOperands(operands) 235 armOperands([operands[-1]] + operands[0..-2]) 236end 237 238def emitArmCompact(opcode2, opcode3, operands) 239 if operands.size == 3 240 $asm.puts "#{opcode3} #{armFlippedOperands(operands)}" 241 else 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}" 246 else 247 $asm.puts "#{opcode2} #{armFlippedOperands(operands)}" 248 end 249 end 250end 251 252def emitArm(opcode, operands) 253 if operands.size == 3 254 $asm.puts "#{opcode} #{armFlippedOperands(operands)}" 255 else 256 raise unless operands.size == 2 257 $asm.puts "#{opcode} #{operands[1].armOperand}, #{operands[1].armOperand}, #{operands[0].armOperand}" 258 end 259end 260 261def 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}" 265end 266 267def emitArmTest(operands) 268 value = operands[0] 269 case operands.size 270 when 2 271 mask = Immediate.new(codeOrigin, -1) 272 when 3 273 mask = operands[1] 274 else 275 raise "Expected 2 or 3 operands but got #{operands.size} at #{codeOriginString}" 276 end 277 278 if mask.immediate? and mask.value == -1 279 $asm.puts "tst #{value.armOperand}, #{value.armOperand}" 280 else 281 $asm.puts "tst #{value.armOperand}, #{mask.armOperand}" 282 end 283end 284 285def 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" 290end 291 292def 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" 297end 298 299class Instruction 300 def lowerARM 301 raise unless $activeBackend == "ARM" 302 lowerARMCommon 303 end 304 305 def lowerARMv7 306 raise unless $activeBackend == "ARMv7" 307 lowerARMCommon 308 end 309 310 def lowerARMv7_TRADITIONAL 311 raise unless $activeBackend == "ARMv7_TRADITIONAL" 312 lowerARMCommon 313 end 314 315 def lowerARMCommon 316 $asm.codeOrigin codeOriginString if $enableCodeOriginComments 317 $asm.annotation annotation if $enableInstrAnnotations 318 319 case opcode 320 when "addi", "addp", "addis", "addps" 321 if opcode == "addis" or opcode == "addps" 322 suffix = "s" 323 else 324 suffix = "" 325 end 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}" 332 end 333 else 334 $asm.puts "adds #{operands[2].armOperand}, #{operands[1].armOperand}, #{operands[0].armOperand}" 335 end 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)}" 340 else 341 if operands[0].immediate? 342 unless Immediate.new(nil, 0) == operands[0] 343 $asm.puts "adds #{armFlippedOperands(operands)}" 344 end 345 else 346 $asm.puts "add#{suffix} #{armFlippedOperands(operands)}" 347 end 348 end 349 when "andi", "andp" 350 emitArmCompact("ands", "and", operands) 351 when "ori", "orp" 352 emitArmCompact("orrs", "orr", operands) 353 when "oris" 354 emitArmCompact("orrs", "orrs", operands) 355 when "xori", "xorp" 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) 363 when "muli", "mulp" 364 emitArm("mul", operands) 365 when "subi", "subp", "subis" 366 emitArmCompact("subs", "subs", operands) 367 when "negi", "negp" 368 $asm.puts "rsbs #{operands[0].armOperand}, #{operands[0].armOperand}, \#0" 369 when "noti" 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)}" 375 when "loadb" 376 $asm.puts "ldrb #{armFlippedOperands(operands)}" 377 when "loadbs" 378 $asm.puts "ldrsb.w #{armFlippedOperands(operands)}" 379 when "storeb" 380 $asm.puts "strb #{armOperands(operands)}" 381 when "loadh" 382 $asm.puts "ldrh #{armFlippedOperands(operands)}" 383 when "loadhs" 384 $asm.puts "ldrsh.w #{armFlippedOperands(operands)}" 385 when "storeh" 386 $asm.puts "strh #{armOperands(operands)}" 387 when "loadd" 388 $asm.puts "vldr.64 #{armFlippedOperands(operands)}" 389 when "stored" 390 $asm.puts "vstr.64 #{armOperands(operands)}" 391 when "addd" 392 emitArm("vadd.f64", operands) 393 when "divd" 394 emitArm("vdiv.f64", operands) 395 when "subd" 396 emitArm("vsub.f64", operands) 397 when "muld" 398 emitArm("vmul.f64", operands) 399 when "sqrtd" 400 $asm.puts "vsqrt.f64 #{armFlippedOperands(operands)}" 401 when "ci2d" 402 $asm.puts "vmov #{operands[1].armSingle}, #{operands[0].armOperand}" 403 $asm.puts "vcvt.f64.s32 #{operands[1].armOperand}, #{operands[1].armSingle}" 404 when "bdeq" 405 emitArmDoubleBranch("beq", operands) 406 when "bdneq" 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") 413 when "bdgt" 414 emitArmDoubleBranch("bgt", operands) 415 when "bdgteq" 416 emitArmDoubleBranch("bge", operands) 417 when "bdlt" 418 emitArmDoubleBranch("bmi", operands) 419 when "bdlteq" 420 emitArmDoubleBranch("bls", operands) 421 when "bdequn" 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}" 426 when "bdnequn" 427 emitArmDoubleBranch("bne", operands) 428 when "bdgtun" 429 emitArmDoubleBranch("bhi", operands) 430 when "bdgtequn" 431 emitArmDoubleBranch("bpl", operands) 432 when "bdltun" 433 emitArmDoubleBranch("blt", operands) 434 when "bdltequn" 435 emitArmDoubleBranch("ble", operands) 436 when "btd2i" 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}" 440 when "td2i" 441 $asm.puts "vcvt.s32.f64 #{ARM_SCRATCH_FPR.armSingle}, #{operands[0].armOperand}" 442 $asm.puts "vmov #{operands[1].armOperand}, #{ARM_SCRATCH_FPR.armSingle}" 443 when "bcd2i" 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}" 450 when "movdz" 451 # FIXME: either support this or remove it. 452 raise "ARM does not support this opcode yet, #{codeOrigin}" 453 when "pop" 454 $asm.puts "pop #{operands[0].armOperand}" 455 when "push" 456 $asm.puts "push #{operands[0].armOperand}" 457 when "move" 458 if operands[0].immediate? 459 armMoveImmediate(operands[0].value, operands[1]) 460 else 461 $asm.puts "mov #{armFlippedOperands(operands)}" 462 end 463 when "nop" 464 $asm.puts "nop" 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}" 470 else 471 $asm.puts "cmp #{armOperands(operands[0..1])}" 472 end 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}" 479 else 480 $asm.puts "cmp #{armOperands(operands[0..1])}" 481 end 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}" 516 when "jmp" 517 if operands[0].label? 518 $asm.puts "b #{operands[0].asmLabel}" 519 else 520 $asm.puts "mov pc, #{operands[0].armOperand}" 521 end 522 if not isARMv7 and not isARMv7Traditional 523 $asm.puts ".ltorg" 524 end 525 when "call" 526 if operands[0].label? 527 $asm.puts "blx #{operands[0].asmLabel}" 528 else 529 $asm.puts "blx #{operands[0].armOperand}" 530 end 531 when "break" 532 $asm.puts "bkpt #0" 533 when "ret" 534 $asm.puts "bx lr" 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") 561 when "peek" 562 $asm.puts "ldr #{operands[1].armOperand}, [sp, \##{operands[0].value * 4}]" 563 when "poke" 564 $asm.puts "str #{operands[1].armOperand}, [sp, \##{operands[0].value * 4}]" 565 when "fii2d" 566 $asm.puts "vmov #{operands[2].armOperand}, #{operands[0].armOperand}, #{operands[1].armOperand}" 567 when "fd2ii" 568 $asm.puts "vmov #{operands[1].armOperand}, #{operands[2].armOperand}, #{operands[0].armOperand}" 569 when "bo" 570 $asm.puts "bvs #{operands[0].asmLabel}" 571 when "bs" 572 $asm.puts "bmi #{operands[0].asmLabel}" 573 when "bz" 574 $asm.puts "beq #{operands[0].asmLabel}" 575 when "bnz" 576 $asm.puts "bne #{operands[0].asmLabel}" 577 when "leai", "leap" 578 operands[0].armEmitLea(operands[1]) 579 when "smulli" 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}" 582 else 583 lowerDefault 584 end 585 end 586end 587 588