blob: 07543574ef3c386ea684a721ff7f09cb47ddf038 [file] [log] [blame]
# Copyright (C) 2011 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
require "config"
require "ast"
require "opt"
class Node
def armV7Single
doubleOperand = armV7Operand
raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^d/
"s" + ($~.post_match.to_i * 2).to_s
end
end
class SpecialRegister < NoChildren
def armV7Operand
@name
end
end
ARMv7_EXTRA_GPRS = [SpecialRegister.new("r9"), SpecialRegister.new("r8"), SpecialRegister.new("r3")]
ARMv7_EXTRA_FPRS = [SpecialRegister.new("d7")]
ARMv7_SCRATCH_FPR = SpecialRegister.new("d8")
def armV7MoveImmediate(value, register)
# Currently we only handle the simple cases, and fall back to mov/movt for the complex ones.
if value >= 0 && value < 256
$asm.puts "movw #{register.armV7Operand}, \##{value}"
elsif (~value) >= 0 && (~value) < 256
$asm.puts "mvn #{register.armV7Operand}, \##{~value}"
else
$asm.puts "movw #{register.armV7Operand}, \##{value & 0xffff}"
if (value & 0xffff0000) != 0
$asm.puts "movt #{register.armV7Operand}, \##{(value >> 16) & 0xffff}"
end
end
end
class RegisterID
def armV7Operand
case name
when "t0", "a0", "r0"
"r0"
when "t1", "a1", "r1"
"r1"
when "t2", "a2"
"r2"
when "a3"
"r3"
when "t3"
"r4"
when "t4"
"r10"
when "cfr"
"r5"
when "lr"
"lr"
when "sp"
"sp"
else
raise "Bad register #{name} for ARMv7 at #{codeOriginString}"
end
end
end
class FPRegisterID
def armV7Operand
case name
when "ft0", "fr"
"d0"
when "ft1"
"d1"
when "ft2"
"d2"
when "ft3"
"d3"
when "ft4"
"d4"
when "ft5"
"d5"
else
raise "Bad register #{name} for ARMv7 at #{codeOriginString}"
end
end
end
class Immediate
def armV7Operand
raise "Invalid immediate #{value} at #{codeOriginString}" if value < 0 or value > 255
"\##{value}"
end
end
class Address
def armV7Operand
raise "Bad offset at #{codeOriginString}" if offset.value < -0xff or offset.value > 0xfff
"[#{base.armV7Operand}, \##{offset.value}]"
end
end
class BaseIndex
def armV7Operand
raise "Bad offset at #{codeOriginString}" if offset.value != 0
"[#{base.armV7Operand}, #{index.armV7Operand}, lsl \##{scaleShift}]"
end
end
class AbsoluteAddress
def armV7Operand
raise "Unconverted absolute address at #{codeOriginString}"
end
end
#
# Lowering of branch ops. For example:
#
# baddiz foo, bar, baz
#
# will become:
#
# addi foo, bar
# bz baz
#
def armV7LowerBranchOps(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
annotation = node.annotation
case node.opcode
when /^b(addi|subi|ori|addp)/
op = $1
branch = "b" + $~.post_match
case op
when "addi", "addp"
op = "addis"
when "subi"
op = "subis"
when "ori"
op = "oris"
end
newList << Instruction.new(node.codeOrigin, op, node.operands[0..-2], annotation)
newList << Instruction.new(node.codeOrigin, branch, [node.operands[-1]])
when "bmulio"
tmp1 = Tmp.new(node.codeOrigin, :gpr)
tmp2 = Tmp.new(node.codeOrigin, :gpr)
newList << Instruction.new(node.codeOrigin, "smulli", [node.operands[0], node.operands[1], node.operands[1], tmp1], annotation)
newList << Instruction.new(node.codeOrigin, "rshifti", [node.operands[-2], Immediate.new(node.codeOrigin, 31), tmp2])
newList << Instruction.new(node.codeOrigin, "bineq", [tmp1, tmp2, node.operands[-1]])
when /^bmuli/
condition = $~.post_match
newList << Instruction.new(node.codeOrigin, "muli", node.operands[0..-2], annotation)
newList << Instruction.new(node.codeOrigin, "bti" + condition, [node.operands[-2], node.operands[-1]])
else
newList << node
end
else
newList << node
end
}
newList
end
#
# Lowering of shift ops. For example:
#
# lshifti foo, bar
#
# will become:
#
# andi foo, 31, tmp
# lshifti tmp, bar
#
def armV7SanitizeShift(operand, list)
return operand if operand.immediate?
tmp = Tmp.new(operand.codeOrigin, :gpr)
list << Instruction.new(operand.codeOrigin, "andi", [operand, Immediate.new(operand.codeOrigin, 31), tmp])
tmp
end
def armV7LowerShiftOps(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
case node.opcode
when "lshifti", "rshifti", "urshifti", "lshiftp", "rshiftp", "urshiftp"
if node.operands.size == 2
newList << Instruction.new(node.codeOrigin, node.opcode, [armV7SanitizeShift(node.operands[0], newList), node.operands[1]], node.annotation)
else
newList << Instruction.new(node.codeOrigin, node.opcode, [node.operands[0], armV7SanitizeShift(node.operands[1], newList), node.operands[2]], node.annotation)
raise "Wrong number of operands for shift at #{node.codeOriginString}" unless node.operands.size == 3
end
else
newList << node
end
else
newList << node
end
}
newList
end
#
# Lowering of malformed addresses. For example:
#
# loadp 10000[foo], bar
#
# will become:
#
# move 10000, tmp
# addp foo, tmp
# loadp 0[tmp], bar
#
class Node
def armV7LowerMalformedAddressesRecurse(list)
mapChildren {
| node |
node.armV7LowerMalformedAddressesRecurse(list)
}
end
end
class Address
def armV7LowerMalformedAddressesRecurse(list)
if offset.value < -0xff or offset.value > 0xfff
tmp = Tmp.new(codeOrigin, :gpr)
list << Instruction.new(codeOrigin, "move", [offset, tmp])
list << Instruction.new(codeOrigin, "addp", [base, tmp])
Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
else
self
end
end
end
class BaseIndex
def armV7LowerMalformedAddressesRecurse(list)
if offset.value != 0
tmp = Tmp.new(codeOrigin, :gpr)
list << Instruction.new(codeOrigin, "move", [offset, tmp])
list << Instruction.new(codeOrigin, "addp", [base, tmp])
BaseIndex.new(codeOrigin, tmp, index, scale, Immediate.new(codeOrigin, 0))
else
self
end
end
end
class AbsoluteAddress
def armV7LowerMalformedAddressesRecurse(list)
tmp = Tmp.new(codeOrigin, :gpr)
list << Instruction.new(codeOrigin, "move", [address, tmp])
Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
end
end
def armV7LowerMalformedAddresses(list)
newList = []
list.each {
| node |
newList << node.armV7LowerMalformedAddressesRecurse(newList)
}
newList
end
#
# Lowering of malformed addresses in double loads and stores. For example:
#
# loadd [foo, bar, 8], baz
#
# becomes:
#
# leap [foo, bar, 8], tmp
# loadd [tmp], baz
#
class Node
def armV7DoubleAddress(list)
self
end
end
class BaseIndex
def armV7DoubleAddress(list)
tmp = Tmp.new(codeOrigin, :gpr)
list << Instruction.new(codeOrigin, "leap", [self, tmp])
Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
end
end
def armV7LowerMalformedAddressesDouble(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
case node.opcode
when "loadd"
newList << Instruction.new(node.codeOrigin, "loadd", [node.operands[0].armV7DoubleAddress(newList), node.operands[1]], node.annotation)
when "stored"
newList << Instruction.new(node.codeOrigin, "stored", [node.operands[0], node.operands[1].armV7DoubleAddress(newList)], node.annotation)
else
newList << node
end
else
newList << node
end
}
newList
end
#
# Lowering of misplaced immediates. For example:
#
# storei 0, [foo]
#
# will become:
#
# move 0, tmp
# storei tmp, [foo]
#
def armV7LowerMisplacedImmediates(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
case node.opcode
when "storeb", "storei", "storep"
operands = node.operands
newOperands = []
operands.each {
| operand |
if operand.is_a? Immediate
tmp = Tmp.new(operand.codeOrigin, :gpr)
newList << Instruction.new(operand.codeOrigin, "move", [operand, tmp])
newOperands << tmp
else
newOperands << operand
end
}
newList << Instruction.new(node.codeOrigin, node.opcode, newOperands, node.annotation)
else
newList << node
end
else
newList << node
end
}
newList
end
#
# Lowering of malformed immediates except when used in a "move" instruction.
# For example:
#
# addp 642641, foo
#
# will become:
#
# move 642641, tmp
# addp tmp, foo
#
class Node
def armV7LowerMalformedImmediatesRecurse(list)
mapChildren {
| node |
node.armV7LowerMalformedImmediatesRecurse(list)
}
end
end
class Address
def armV7LowerMalformedImmediatesRecurse(list)
self
end
end
class BaseIndex
def armV7LowerMalformedImmediatesRecurse(list)
self
end
end
class AbsoluteAddress
def armV7LowerMalformedImmediatesRecurse(list)
self
end
end
class Immediate
def armV7LowerMalformedImmediatesRecurse(list)
if value < 0 or value > 255
tmp = Tmp.new(codeOrigin, :gpr)
list << Instruction.new(codeOrigin, "move", [self, tmp])
tmp
else
self
end
end
end
def armV7LowerMalformedImmediates(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
annotation = node.annotation
case node.opcode
when "move"
newList << node
when "addi", "addp", "addis", "subi", "subp", "subis"
if node.operands[0].is_a? Immediate and
node.operands[0].value < 0 and
node.operands[0].value >= 255 and
node.operands.size == 2
if node.opcode =~ /add/
newOpcode = "sub" + node.opcode[-1..-1]
else
newOpcode = "add" + node.opcode[-1..-1]
end
newList << Instruction.new(node.codeOrigin, newOpcode,
[Immediate.new(-node.operands[0].value)] + node.operands[1..-1],
annotation)
else
newList << node.armV7LowerMalformedImmediatesRecurse(newList)
end
when "muli", "mulp"
if node.operands[0].is_a? Immediate
tmp = Tmp.new(codeOrigin, :gpr)
newList << Instruction.new(node.codeOrigin, "move", [node.operands[0], tmp], annotation)
newList << Instruction.new(node.codeOrigin, "muli", [tmp] + node.operands[1..-1])
else
newList << node.armV7LowerMalformedImmediatesRecurse(newList)
end
else
newList << node.armV7LowerMalformedImmediatesRecurse(newList)
end
else
newList << node
end
}
newList
end
#
# Lowering of misplaced addresses. For example:
#
# addi foo, [bar]
#
# will become:
#
# loadi [bar], tmp
# addi foo, tmp
# storei tmp, [bar]
#
# Another example:
#
# addi [foo], bar
#
# will become:
#
# loadi [foo], tmp
# addi tmp, bar
#
def armV7AsRegister(preList, postList, operand, suffix, needStore)
return operand unless operand.address?
tmp = Tmp.new(operand.codeOrigin, if suffix == "d" then :fpr else :gpr end)
preList << Instruction.new(operand.codeOrigin, "load" + suffix, [operand, tmp])
if needStore
postList << Instruction.new(operand.codeOrigin, "store" + suffix, [tmp, operand])
end
tmp
end
def armV7AsRegisters(preList, postList, operands, suffix)
newOperands = []
operands.each_with_index {
| operand, index |
newOperands << armV7AsRegister(preList, postList, operand, suffix, index == operands.size - 1)
}
newOperands
end
def armV7LowerMisplacedAddresses(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
postInstructions = []
annotation = node.annotation
case node.opcode
when "addi", "addp", "addis", "andi", "andp", "lshifti", "lshiftp", "muli", "mulp", "negi",
"negp", "noti", "ori", "oris", "orp", "rshifti", "urshifti", "rshiftp", "urshiftp", "subi",
"subp", "subis", "xori", "xorp", /^bi/, /^bp/, /^bti/, /^btp/, /^ci/, /^cp/, /^ti/
newList << Instruction.new(node.codeOrigin,
node.opcode,
armV7AsRegisters(newList, postInstructions, node.operands, "i"),
annotation)
when "bbeq", "bbneq", "bba", "bbaeq", "bbb", "bbbeq", "btbz", "btbnz", "tbz", "tbnz",
"cbeq", "cbneq", "cba", "cbaeq", "cbb", "cbbeq"
newList << Instruction.new(node.codeOrigin,
node.opcode,
armV7AsRegisters(newList, postInstructions, node.operands, "b"),
annotation)
when "bbgt", "bbgteq", "bblt", "bblteq", "btbs", "tbs", "cbgt", "cbgteq", "cblt", "cblteq"
newList << Instruction.new(node.codeOrigin,
node.opcode,
armV7AsRegisters(newList, postInstructions, node.operands, "bs"),
annotation)
when "addd", "divd", "subd", "muld", "sqrtd", /^bd/
newList << Instruction.new(node.codeOrigin,
node.opcode,
armV7AsRegisters(newList, postInstructions, node.operands, "d"),
annotation)
when "jmp", "call"
newList << Instruction.new(node.codeOrigin,
node.opcode,
[armV7AsRegister(newList, postInstructions, node.operands[0], "p", false)],
annotation)
else
newList << node
end
newList += postInstructions
else
newList << node
end
}
newList
end
#
# Lowering of register reuse in compare instructions. For example:
#
# cieq t0, t1, t0
#
# will become:
#
# mov tmp, t0
# cieq tmp, t1, t0
#
def armV7LowerRegisterReuse(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
annotation = node.annotation
case node.opcode
when "cieq", "cineq", "cia", "ciaeq", "cib", "cibeq", "cigt", "cigteq", "cilt", "cilteq",
"cpeq", "cpneq", "cpa", "cpaeq", "cpb", "cpbeq", "cpgt", "cpgteq", "cplt", "cplteq",
"tis", "tiz", "tinz", "tbs", "tbz", "tbnz", "tps", "tpz", "tpnz", "cbeq", "cbneq",
"cba", "cbaeq", "cbb", "cbbeq", "cbgt", "cbgteq", "cblt", "cblteq"
if node.operands.size == 2
if node.operands[0] == node.operands[1]
tmp = Tmp.new(node.codeOrigin, :gpr)
newList << Instruction.new(node.codeOrigin, "move", [node.operands[0], tmp], annotation)
newList << Instruction.new(node.codeOrigin, node.opcode, [tmp, node.operands[1]])
else
newList << node
end
else
raise "Wrong number of arguments at #{node.codeOriginString}" unless node.operands.size == 3
if node.operands[0] == node.operands[2]
tmp = Tmp.new(node.codeOrigin, :gpr)
newList << Instruction.new(node.codeOrigin, "move", [node.operands[0], tmp], annotation)
newList << Instruction.new(node.codeOrigin, node.opcode, [tmp, node.operands[1], node.operands[2]])
elsif node.operands[1] == node.operands[2]
tmp = Tmp.new(node.codeOrigin, :gpr)
newList << Instruction.new(node.codeOrigin, "move", [node.operands[1], tmp], annotation)
newList << Instruction.new(node.codeOrigin, node.opcode, [node.operands[0], tmp, node.operands[2]])
else
newList << node
end
end
else
newList << node
end
else
newList << node
end
}
newList
end
#
# Lea support.
#
class Address
def armV7EmitLea(destination)
if destination == base
$asm.puts "adds #{destination.armV7Operand}, \##{offset.value}"
else
$asm.puts "adds #{destination.armV7Operand}, #{base.armV7Operand}, \##{offset.value}"
end
end
end
class BaseIndex
def armV7EmitLea(destination)
raise "Malformed BaseIndex, offset should be zero at #{codeOriginString}" unless offset.value == 0
$asm.puts "add.w #{destination.armV7Operand}, #{base.armV7Operand}, #{index.armV7Operand}, lsl \##{scaleShift}"
end
end
# FIXME: we could support AbsoluteAddress for lea, but we don't.
#
# Actual lowering code follows.
#
class Sequence
def getModifiedListARMv7
myList = @list
# Verify that we will only see instructions and labels.
myList.each {
| node |
unless node.is_a? Instruction or
node.is_a? Label or
node.is_a? LocalLabel or
node.is_a? Skip
raise "Unexpected #{node.inspect} at #{node.codeOrigin}"
end
}
myList = armV7LowerBranchOps(myList)
myList = armV7LowerShiftOps(myList)
myList = armV7LowerMalformedAddresses(myList)
myList = armV7LowerMalformedAddressesDouble(myList)
myList = armV7LowerMisplacedImmediates(myList)
myList = armV7LowerMalformedImmediates(myList)
myList = armV7LowerMisplacedAddresses(myList)
myList = armV7LowerRegisterReuse(myList)
myList = assignRegistersToTemporaries(myList, :gpr, ARMv7_EXTRA_GPRS)
myList = assignRegistersToTemporaries(myList, :fpr, ARMv7_EXTRA_FPRS)
return myList
end
end
def armV7Operands(operands)
operands.map{|v| v.armV7Operand}.join(", ")
end
def armV7FlippedOperands(operands)
armV7Operands([operands[-1]] + operands[0..-2])
end
def emitArmV7Compact(opcode2, opcode3, operands)
if operands.size == 3
$asm.puts "#{opcode3} #{armV7FlippedOperands(operands)}"
else
raise unless operands.size == 2
raise unless operands[1].register?
if operands[0].is_a? Immediate
$asm.puts "#{opcode3} #{operands[1].armV7Operand}, #{operands[1].armV7Operand}, #{operands[0].armV7Operand}"
else
$asm.puts "#{opcode2} #{armV7FlippedOperands(operands)}"
end
end
end
def emitArmV7(opcode, operands)
if operands.size == 3
$asm.puts "#{opcode} #{armV7FlippedOperands(operands)}"
else
raise unless operands.size == 2
$asm.puts "#{opcode} #{operands[1].armV7Operand}, #{operands[1].armV7Operand}, #{operands[0].armV7Operand}"
end
end
def emitArmV7DoubleBranch(branchOpcode, operands)
$asm.puts "vcmpe.f64 #{armV7Operands(operands[0..1])}"
$asm.puts "vmrs apsr_nzcv, fpscr"
$asm.puts "#{branchOpcode} #{operands[2].asmLabel}"
end
def emitArmV7Test(operands)
value = operands[0]
case operands.size
when 2
mask = Immediate.new(codeOrigin, -1)
when 3
mask = operands[1]
else
raise "Expected 2 or 3 operands but got #{operands.size} at #{codeOriginString}"
end
if mask.is_a? Immediate and mask.value == -1
$asm.puts "tst #{value.armV7Operand}, #{value.armV7Operand}"
elsif mask.is_a? Immediate
$asm.puts "tst.w #{value.armV7Operand}, #{mask.armV7Operand}"
else
$asm.puts "tst #{value.armV7Operand}, #{mask.armV7Operand}"
end
end
def emitArmV7Compare(operands, code)
$asm.puts "movs #{operands[2].armV7Operand}, \#0"
$asm.puts "cmp #{operands[0].armV7Operand}, #{operands[1].armV7Operand}"
$asm.puts "it #{code}"
$asm.puts "mov#{code} #{operands[2].armV7Operand}, \#1"
end
def emitArmV7TestSet(operands, code)
$asm.puts "movs #{operands[-1].armV7Operand}, \#0"
emitArmV7Test(operands)
$asm.puts "it #{code}"
$asm.puts "mov#{code} #{operands[-1].armV7Operand}, \#1"
end
class Instruction
def lowerARMv7
$asm.codeOrigin codeOriginString if $enableCodeOriginComments
$asm.annotation annotation if $enableInstrAnnotations
case opcode
when "addi", "addp", "addis"
if opcode == "addis"
suffix = "s"
else
suffix = ""
end
if operands.size == 3 and operands[0].is_a? Immediate
raise unless operands[1].is_a? RegisterID
raise unless operands[2].is_a? RegisterID
if operands[0].value == 0 and suffix.empty?
unless operands[1] == operands[2]
$asm.puts "mov #{operands[2].armV7Operand}, #{operands[1].armV7Operand}"
end
else
$asm.puts "adds #{operands[2].armV7Operand}, #{operands[1].armV7Operand}, #{operands[0].armV7Operand}"
end
elsif operands.size == 3 and operands[0].is_a? RegisterID
raise unless operands[1].is_a? RegisterID
raise unless operands[2].is_a? RegisterID
$asm.puts "adds #{armV7FlippedOperands(operands)}"
else
if operands[0].is_a? Immediate
unless Immediate.new(nil, 0) == operands[0]
$asm.puts "adds #{armV7FlippedOperands(operands)}"
end
else
$asm.puts "add#{suffix} #{armV7FlippedOperands(operands)}"
end
end
when "andi", "andp"
emitArmV7Compact("ands", "and", operands)
when "ori", "orp"
emitArmV7Compact("orrs", "orr", operands)
when "oris"
emitArmV7Compact("orrs", "orrs", operands)
when "xori", "xorp"
emitArmV7Compact("eors", "eor", operands)
when "lshifti", "lshiftp"
emitArmV7Compact("lsls", "lsls", operands)
when "rshifti", "rshiftp"
emitArmV7Compact("asrs", "asrs", operands)
when "urshifti", "urshiftp"
emitArmV7Compact("lsrs", "lsrs", operands)
when "muli", "mulp"
emitArmV7("mul", operands)
when "subi", "subp", "subis"
emitArmV7Compact("subs", "subs", operands)
when "negi", "negp"
$asm.puts "rsbs #{operands[0].armV7Operand}, #{operands[0].armV7Operand}, \#0"
when "noti"
$asm.puts "mvns #{operands[0].armV7Operand}, #{operands[0].armV7Operand}"
when "loadi", "loadis", "loadp"
$asm.puts "ldr #{armV7FlippedOperands(operands)}"
when "storei", "storep"
$asm.puts "str #{armV7Operands(operands)}"
when "loadb"
$asm.puts "ldrb #{armV7FlippedOperands(operands)}"
when "loadbs"
$asm.puts "ldrsb.w #{armV7FlippedOperands(operands)}"
when "storeb"
$asm.puts "strb #{armV7Operands(operands)}"
when "loadh"
$asm.puts "ldrh #{armV7FlippedOperands(operands)}"
when "loadhs"
$asm.puts "ldrsh.w #{armV7FlippedOperands(operands)}"
when "storeh"
$asm.puts "strh #{armV7Operands(operands)}"
when "loadd"
$asm.puts "vldr.64 #{armV7FlippedOperands(operands)}"
when "stored"
$asm.puts "vstr.64 #{armV7Operands(operands)}"
when "addd"
emitArmV7("vadd.f64", operands)
when "divd"
emitArmV7("vdiv.f64", operands)
when "subd"
emitArmV7("vsub.f64", operands)
when "muld"
emitArmV7("vmul.f64", operands)
when "sqrtd"
$asm.puts "vsqrt.f64 #{armV7FlippedOperands(operands)}"
when "ci2d"
$asm.puts "vmov #{operands[1].armV7Single}, #{operands[0].armV7Operand}"
$asm.puts "vcvt.f64.s32 #{operands[1].armV7Operand}, #{operands[1].armV7Single}"
when "bdeq"
emitArmV7DoubleBranch("beq", operands)
when "bdneq"
$asm.puts "vcmpe.f64 #{armV7Operands(operands[0..1])}"
$asm.puts "vmrs apsr_nzcv, fpscr"
isUnordered = LocalLabel.unique("bdneq")
$asm.puts "bvs #{LabelReference.new(codeOrigin, isUnordered).asmLabel}"
$asm.puts "bne #{operands[2].asmLabel}"
isUnordered.lower("ARMv7")
when "bdgt"
emitArmV7DoubleBranch("bgt", operands)
when "bdgteq"
emitArmV7DoubleBranch("bge", operands)
when "bdlt"
emitArmV7DoubleBranch("bmi", operands)
when "bdlteq"
emitArmV7DoubleBranch("bls", operands)
when "bdequn"
$asm.puts "vcmpe.f64 #{armV7Operands(operands[0..1])}"
$asm.puts "vmrs apsr_nzcv, fpscr"
$asm.puts "bvs #{operands[2].asmLabel}"
$asm.puts "beq #{operands[2].asmLabel}"
when "bdnequn"
emitArmV7DoubleBranch("bne", operands)
when "bdgtun"
emitArmV7DoubleBranch("bhi", operands)
when "bdgtequn"
emitArmV7DoubleBranch("bpl", operands)
when "bdltun"
emitArmV7DoubleBranch("blt", operands)
when "bdltequn"
emitArmV7DoubleBranch("ble", operands)
when "btd2i"
# FIXME: may be a good idea to just get rid of this instruction, since the interpreter
# currently does not use it.
raise "ARMv7 does not support this opcode yet, #{codeOrigin}"
when "td2i"
$asm.puts "vcvt.s32.f64 #{ARMv7_SCRATCH_FPR.armV7Single}, #{operands[0].armV7Operand}"
$asm.puts "vmov #{operands[1].armV7Operand}, #{ARMv7_SCRATCH_FPR.armV7Single}"
when "bcd2i"
$asm.puts "vcvt.s32.f64 #{ARMv7_SCRATCH_FPR.armV7Single}, #{operands[0].armV7Operand}"
$asm.puts "vmov #{operands[1].armV7Operand}, #{ARMv7_SCRATCH_FPR.armV7Single}"
$asm.puts "vcvt.f64.s32 #{ARMv7_SCRATCH_FPR.armV7Operand}, #{ARMv7_SCRATCH_FPR.armV7Single}"
emitArmV7DoubleBranch("bne", [ARMv7_SCRATCH_FPR, operands[0], operands[2]])
$asm.puts "tst #{operands[1].armV7Operand}, #{operands[1].armV7Operand}"
$asm.puts "beq #{operands[2].asmLabel}"
when "movdz"
# FIXME: either support this or remove it.
raise "ARMv7 does not support this opcode yet, #{codeOrigin}"
when "pop"
$asm.puts "pop #{operands[0].armV7Operand}"
when "push"
$asm.puts "push #{operands[0].armV7Operand}"
when "move", "sxi2p", "zxi2p"
if operands[0].is_a? Immediate
armV7MoveImmediate(operands[0].value, operands[1])
else
$asm.puts "mov #{armV7FlippedOperands(operands)}"
end
when "nop"
$asm.puts "nop"
when "bieq", "bpeq", "bbeq"
if Immediate.new(nil, 0) == operands[0]
$asm.puts "tst #{operands[1].armV7Operand}, #{operands[1].armV7Operand}"
elsif Immediate.new(nil, 0) == operands[1]
$asm.puts "tst #{operands[0].armV7Operand}, #{operands[0].armV7Operand}"
else
$asm.puts "cmp #{armV7Operands(operands[0..1])}"
end
$asm.puts "beq #{operands[2].asmLabel}"
when "bineq", "bpneq", "bbneq"
if Immediate.new(nil, 0) == operands[0]
$asm.puts "tst #{operands[1].armV7Operand}, #{operands[1].armV7Operand}"
elsif Immediate.new(nil, 0) == operands[1]
$asm.puts "tst #{operands[0].armV7Operand}, #{operands[0].armV7Operand}"
else
$asm.puts "cmp #{armV7Operands(operands[0..1])}"
end
$asm.puts "bne #{operands[2].asmLabel}"
when "bia", "bpa", "bba"
$asm.puts "cmp #{armV7Operands(operands[0..1])}"
$asm.puts "bhi #{operands[2].asmLabel}"
when "biaeq", "bpaeq", "bbaeq"
$asm.puts "cmp #{armV7Operands(operands[0..1])}"
$asm.puts "bhs #{operands[2].asmLabel}"
when "bib", "bpb", "bbb"
$asm.puts "cmp #{armV7Operands(operands[0..1])}"
$asm.puts "blo #{operands[2].asmLabel}"
when "bibeq", "bpbeq", "bbbeq"
$asm.puts "cmp #{armV7Operands(operands[0..1])}"
$asm.puts "bls #{operands[2].asmLabel}"
when "bigt", "bpgt", "bbgt"
$asm.puts "cmp #{armV7Operands(operands[0..1])}"
$asm.puts "bgt #{operands[2].asmLabel}"
when "bigteq", "bpgteq", "bbgteq"
$asm.puts "cmp #{armV7Operands(operands[0..1])}"
$asm.puts "bge #{operands[2].asmLabel}"
when "bilt", "bplt", "bblt"
$asm.puts "cmp #{armV7Operands(operands[0..1])}"
$asm.puts "blt #{operands[2].asmLabel}"
when "bilteq", "bplteq", "bblteq"
$asm.puts "cmp #{armV7Operands(operands[0..1])}"
$asm.puts "ble #{operands[2].asmLabel}"
when "btiz", "btpz", "btbz"
emitArmV7Test(operands)
$asm.puts "beq #{operands[-1].asmLabel}"
when "btinz", "btpnz", "btbnz"
emitArmV7Test(operands)
$asm.puts "bne #{operands[-1].asmLabel}"
when "btis", "btps", "btbs"
emitArmV7Test(operands)
$asm.puts "bmi #{operands[-1].asmLabel}"
when "jmp"
if operands[0].label?
$asm.puts "b #{operands[0].asmLabel}"
else
$asm.puts "mov pc, #{operands[0].armV7Operand}"
end
when "call"
if operands[0].label?
$asm.puts "blx #{operands[0].asmLabel}"
else
$asm.puts "blx #{operands[0].armV7Operand}"
end
when "break"
$asm.puts "bkpt #0"
when "ret"
$asm.puts "bx lr"
when "cieq", "cpeq", "cbeq"
emitArmV7Compare(operands, "eq")
when "cineq", "cpneq", "cbneq"
emitArmV7Compare(operands, "ne")
when "cia", "cpa", "cba"
emitArmV7Compare(operands, "hi")
when "ciaeq", "cpaeq", "cbaeq"
emitArmV7Compare(operands, "hs")
when "cib", "cpb", "cbb"
emitArmV7Compare(operands, "lo")
when "cibeq", "cpbeq", "cbbeq"
emitArmV7Compare(operands, "ls")
when "cigt", "cpgt", "cbgt"
emitArmV7Compare(operands, "gt")
when "cigteq", "cpgteq", "cbgteq"
emitArmV7Compare(operands, "ge")
when "cilt", "cplt", "cblt"
emitArmV7Compare(operands, "lt")
when "cilteq", "cplteq", "cblteq"
emitArmV7Compare(operands, "le")
when "tis", "tbs", "tps"
emitArmV7TestSet(operands, "mi")
when "tiz", "tbz", "tpz"
emitArmV7TestSet(operands, "eq")
when "tinz", "tbnz", "tpnz"
emitArmV7TestSet(operands, "ne")
when "peek"
$asm.puts "ldr #{operands[1].armV7Operand}, [sp, \##{operands[0].value * 4}]"
when "poke"
$asm.puts "str #{operands[1].armV7Operand}, [sp, \##{operands[0].value * 4}]"
when "fii2d"
$asm.puts "vmov #{operands[2].armV7Operand}, #{operands[0].armV7Operand}, #{operands[1].armV7Operand}"
when "fd2ii"
$asm.puts "vmov #{operands[1].armV7Operand}, #{operands[2].armV7Operand}, #{operands[0].armV7Operand}"
when "bo"
$asm.puts "bvs #{operands[0].asmLabel}"
when "bs"
$asm.puts "bmi #{operands[0].asmLabel}"
when "bz"
$asm.puts "beq #{operands[0].asmLabel}"
when "bnz"
$asm.puts "bne #{operands[0].asmLabel}"
when "leai", "leap"
operands[0].armV7EmitLea(operands[1])
when "smulli"
raise "Wrong number of arguments to smull in #{self.inspect} at #{codeOriginString}" unless operands.length == 4
$asm.puts "smull #{operands[2].armV7Operand}, #{operands[3].armV7Operand}, #{operands[0].armV7Operand}, #{operands[1].armV7Operand}"
else
lowerDefault
end
end
end