blob: d2ab6139323d842190fa69af3883f2ad383ddc65 [file] [log] [blame]
# Copyright (C) 2012 Apple Inc. All rights reserved.
# Copyright (C) 2012 MIPS Technologies, 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 MIPS TECHNOLOGIES, INC. ``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 MIPS TECHNOLOGIES, INC. OR
# 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 'risc'
class Assembler
def putStr(str)
@outp.puts str
end
end
class Node
def mipsSingleHi
doubleOperand = mipsOperand
raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^\$f/
"$f" + ($~.post_match.to_i + 1).to_s
end
def mipsSingleLo
doubleOperand = mipsOperand
raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^\$f/
doubleOperand
end
end
class SpecialRegister < NoChildren
def mipsOperand
@name
end
def dump
@name
end
def register?
true
end
end
MIPS_TEMP_GPRS = [SpecialRegister.new("$t5"), SpecialRegister.new("$t6"), SpecialRegister.new("$t7"),
SpecialRegister.new("$t8")]
MIPS_ZERO_REG = SpecialRegister.new("$zero")
MIPS_GP_REG = SpecialRegister.new("$gp")
MIPS_GPSAVE_REG = SpecialRegister.new("$s4")
MIPS_JUMP_REG = SpecialRegister.new("$ra")
MIPS_CALL_REG = SpecialRegister.new("$t9")
MIPS_TEMP_FPRS = [SpecialRegister.new("$f16")]
MIPS_SCRATCH_FPR = SpecialRegister.new("$f18")
def mipsMoveImmediate(value, register)
if value == 0
$asm.puts "add #{register.mipsOperand}, $zero, $zero"
else
$asm.puts "li #{register.mipsOperand}, #{value}"
end
end
class RegisterID
def mipsOperand
case name
when "a0"
"$a0"
when "a1"
"$a1"
when "r0", "t0"
"$v0"
when "r1", "t1"
"$v1"
when "t2"
"$t2"
when "t3"
"$s3"
when "t4" # PC reg in llint
"$s2"
when "t5"
"$t5"
when "t6"
"$t6"
when "t7"
"$t7"
when "t8"
"$t8"
when "cfr"
"$s0"
when "lr"
"$ra"
when "sp"
"$sp"
else
raise "Bad register #{name} for MIPS at #{codeOriginString}"
end
end
end
class FPRegisterID
def mipsOperand
case name
when "ft0", "fr"
"$f0"
when "ft1"
"$f2"
when "ft2"
"$f4"
when "ft3"
"$f6"
when "ft4"
"$f8"
when "ft5"
"$f10"
when "fa0"
"$f12"
when "fa1"
"$f14"
else
raise "Bad register #{name} for MIPS at #{codeOriginString}"
end
end
end
class Immediate
def mipsOperand
raise "Invalid immediate #{value} at #{codeOriginString}" if value < -0x7fff or value > 0xffff
"#{value}"
end
end
class Address
def mipsOperand
raise "Bad offset at #{codeOriginString}" if offset.value < -0x7fff or offset.value > 0x7fff
"#{offset.value}(#{base.mipsOperand})"
end
end
class AbsoluteAddress
def mipsOperand
raise "Unconverted absolute address at #{codeOriginString}"
end
end
#
# Lower 'and' masked branches
#
def lowerMIPSCondBranch(list, condOp, node)
if node.operands.size == 2
list << Instruction.new(node.codeOrigin,
condOp,
[node.operands[0], MIPS_ZERO_REG, node.operands[-1]],
node.annotation)
elsif node.operands.size == 3
tmp = Tmp.new(node.codeOrigin, :gpr)
list << Instruction.new(node.codeOrigin,
"andi",
[node.operands[0], node.operands[1], tmp],
node.annotation)
list << Instruction.new(node.codeOrigin,
condOp,
[tmp, MIPS_ZERO_REG, node.operands[-1]])
else
raise "Expected 2 or 3 operands but got #{node.operands.size} at #{node.codeOriginString}"
end
end
#
# Lowering of branch ops. For example:
#
# baddiz foo, bar, baz
#
# will become:
#
# addi foo, bar
# bz baz
#
def mipsLowerSimpleBranchOps(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
annotation = node.annotation
case node.opcode
when /^b(addi|subi|ori|addp)/
op = $1
bc = $~.post_match
branch = "b" + bc
case op
when "addi", "addp"
op = "addi"
when "subi"
op = "subi"
when "ori"
op = "ori"
end
if bc == "o"
case op
when "addi"
# addu $s0, $s1, $s2
# xor $t0, $s1, $s2
# blt $t0, $zero, no overflow
# xor $t0, $s0, $s1
# blt $t0, $zero, overflow
# no overflow:
#
tr = Tmp.new(node.codeOrigin, :gpr)
tmp = Tmp.new(node.codeOrigin, :gpr)
noFlow = LocalLabel.unique("noflow")
noFlowRef = LocalLabelReference.new(node.codeOrigin, noFlow)
newList << Instruction.new(node.codeOrigin, op, [node.operands[0], node.operands[1], tr], annotation)
newList << Instruction.new(node.codeOrigin, "xori", [node.operands[0], node.operands[1], tmp])
newList << Instruction.new(node.codeOrigin, "bilt", [tmp, MIPS_ZERO_REG, noFlowRef])
newList << Instruction.new(node.codeOrigin, "xori", [tr, node.operands[0], tmp])
newList << Instruction.new(node.codeOrigin, "bilt", [tmp, MIPS_ZERO_REG, node.operands[2]])
newList << noFlow
newList << Instruction.new(node.codeOrigin, "move", [tr, node.operands[1]])
when "subi"
# subu $s0, $s1, $s2
# xor $t0, $s1, $s2
# bge $t0, $zero, no overflow
# xor $t0, $s0, $s1
# blt $t0, $zero, overflow
# no overflow:
#
tr = Tmp.new(node.codeOrigin, :gpr)
tmp = Tmp.new(node.codeOrigin, :gpr)
noFlow = LocalLabel.unique("noflow")
noFlowRef = LocalLabelReference.new(node.codeOrigin, noFlow)
newList << Instruction.new(node.codeOrigin, op, [node.operands[1], node.operands[0], tr], annotation)
newList << Instruction.new(node.codeOrigin, "xori", [node.operands[1], node.operands[0], tmp])
newList << Instruction.new(node.codeOrigin, "bigteq", [tmp, MIPS_ZERO_REG, noFlowRef])
newList << Instruction.new(node.codeOrigin, "xori", [tr, node.operands[1], tmp])
newList << Instruction.new(node.codeOrigin, "bilt", [tmp, MIPS_ZERO_REG, node.operands[2]])
newList << noFlow
newList << Instruction.new(node.codeOrigin, "move", [tr, node.operands[1]])
when "ori"
# no ovwerflow at ori
newList << Instruction.new(node.codeOrigin, op, node.operands[0..1], annotation)
end
else
if node.operands[1].is_a? Address
addr = node.operands[1]
tr = Tmp.new(node.codeOrigin, :gpr)
newList << Instruction.new(node.codeOrigin, "loadp", [addr, tr], annotation)
newList << Instruction.new(node.codeOrigin, op, [node.operands[0], tr])
newList << Instruction.new(node.codeOrigin, "storep", [tr, addr])
else
tr = node.operands[1]
newList << Instruction.new(node.codeOrigin, op, node.operands[0..-2], annotation)
end
newList << Instruction.new(node.codeOrigin, branch, [tr, MIPS_ZERO_REG, node.operands[-1]])
end
when "bia", "bpa", "bba"
tmp = Tmp.new(node.codeOrigin, :gpr)
comp = node.opcode[1] == ?b ? "sltub" : "sltu"
newList << Instruction.new(node.codeOrigin, comp, [tmp, node.operands[1], node.operands[0]], annotation)
newList << Instruction.new(node.codeOrigin, "bnz", [tmp, MIPS_ZERO_REG, node.operands[2]])
when "biaeq", "bpaeq", "bbaeq"
tmp = Tmp.new(node.codeOrigin, :gpr)
comp = node.opcode[1] == ?b ? "sltub" : "sltu"
newList << Instruction.new(node.codeOrigin, comp, [tmp, node.operands[0], node.operands[1]], annotation)
newList << Instruction.new(node.codeOrigin, "bz", [tmp, MIPS_ZERO_REG, node.operands[2]])
when "bib", "bpb", "bbb"
tmp = Tmp.new(node.codeOrigin, :gpr)
comp = node.opcode[1] == ?b ? "sltub" : "sltu"
newList << Instruction.new(node.codeOrigin, comp, [tmp, node.operands[0], node.operands[1]], annotation)
newList << Instruction.new(node.codeOrigin, "bnz", [tmp, MIPS_ZERO_REG, node.operands[2]])
when "bibeq", "bpbeq", "bbbeq"
tmp = Tmp.new(node.codeOrigin, :gpr)
comp = node.opcode[1] == ?b ? "sltub" : "sltu"
newList << Instruction.new(node.codeOrigin, comp, [tmp, node.operands[1], node.operands[0]], annotation)
newList << Instruction.new(node.codeOrigin, "bz", [tmp, MIPS_ZERO_REG, node.operands[2]])
when /^bt(i|p|b)/
lowerMIPSCondBranch(newList, "b" + $~.post_match + $1, node)
else
newList << node
end
else
newList << node
end
}
newList
end
#
# Specialization of lowering of malformed BaseIndex addresses.
#
class Node
def mipsLowerMalformedAddressesRecurse(list, topLevelNode, &block)
mapChildren {
| subNode |
subNode.mipsLowerMalformedAddressesRecurse(list, topLevelNode, &block)
}
end
end
class Address
def mipsLowerMalformedAddressesRecurse(list, node, &block)
riscLowerMalformedAddressesRecurse(list, node, &block)
end
end
class BaseIndex
def mipsLowerMalformedAddressesRecurse(list, node, &block)
if scaleShift == 0
tmp0 = Tmp.new(codeOrigin, :gpr)
list << Instruction.new(codeOrigin, "addp", [base, index, tmp0])
Address.new(codeOrigin, tmp0, Immediate.new(codeOrigin, offset.value));
else
tmp0 = Tmp.new(codeOrigin, :gpr)
list << Instruction.new(codeOrigin, "lshifti", [index, Immediate.new(codeOrigin, scaleShift), tmp0]);
list << Instruction.new(codeOrigin, "addp", [base, tmp0])
Address.new(codeOrigin, tmp0, Immediate.new(codeOrigin, offset.value));
end
end
end
class AbsoluteAddress
def mipsLowerMalformedAddressesRecurse(list, node, &block)
riscLowerMalformedAddressesRecurse(list, node, &block)
end
end
def mipsLowerMalformedAddresses(list, &block)
newList = []
list.each {
| node |
newList << node.mipsLowerMalformedAddressesRecurse(newList, node, &block)
}
newList
end
#
# Lowering of misplaced immediates of MIPS specific instructions. For example:
#
# sltu reg, 4, 2
#
# will become:
#
# move 4, tmp
# sltu reg, tmp, 2
#
def mipsLowerMisplacedImmediates(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
case node.opcode
when "slt", "sltu", "sltb", "sltub"
if node.operands[1].is_a? Immediate
tmp = Tmp.new(node.codeOrigin, :gpr)
newList << Instruction.new(node.codeOrigin, "move", [node.operands[1], tmp], node.annotation)
newList << Instruction.new(node.codeOrigin, node.opcode,
[node.operands[0], tmp, node.operands[2]],
node.annotation)
else
newList << node
end
when /^(addi|subi)/
newList << node.riscLowerMalformedImmediatesRecurse(newList, -0x7fff..0x7fff)
else
newList << node
end
else
newList << node
end
}
newList
end
#
# Specialization of lowering of misplaced addresses.
#
def mipsLowerMisplacedAddresses(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
postInstructions = []
annotation = node.annotation
case node.opcode
when "jmp"
if node.operands[0].address?
newList << Instruction.new(node.operands[0].codeOrigin, "loadi", [node.operands[0], MIPS_JUMP_REG])
newList << Instruction.new(node.codeOrigin, node.opcode, [MIPS_JUMP_REG])
else
newList << Instruction.new(node.codeOrigin,
node.opcode,
[riscAsRegister(newList, postInstructions, node.operands[0], "p", false)])
end
when "call"
restoreGP = false;
tmp = MIPS_CALL_REG
if node.operands[0].address?
newList << Instruction.new(node.operands[0].codeOrigin, "loadp", [node.operands[0], MIPS_CALL_REG])
restoreGP = true;
elsif node.operands[0].is_a? LabelReference
tmp = node.operands[0]
restoreGP = true;
elsif node.operands[0].register?
newList << Instruction.new(node.operands[0].codeOrigin, "move", [node.operands[0], MIPS_CALL_REG])
restoreGP = true;
else
tmp = node.operands[0]
end
newList << Instruction.new(node.codeOrigin, node.opcode, [tmp])
if restoreGP
newList << Instruction.new(node.codeOrigin, "move", [MIPS_GPSAVE_REG, MIPS_GP_REG])
end
when "slt", "sltu"
newList << Instruction.new(node.codeOrigin,
node.opcode,
riscAsRegisters(newList, [], node.operands, "i"))
when "sltub", "sltb"
newList << Instruction.new(node.codeOrigin,
node.opcode,
riscAsRegisters(newList, [], node.operands, "b"))
when /^(bz|bnz|bs|bo)/
tl = $~.post_match == "" ? "i" : $~.post_match
newList << Instruction.new(node.codeOrigin,
node.opcode,
riscAsRegisters(newList, [], node.operands, tl))
else
newList << node
end
newList += postInstructions
else
newList << node
end
}
newList
end
#
# Lowering compares and tests.
#
def mipsLowerCompareTemplate(list, node, opCmp, opMov)
tmp0 = Tmp.new(node.codeOrigin, :gpr)
tmp1 = Tmp.new(node.codeOrigin, :gpr)
list << Instruction.new(node.codeOrigin, "move", [Immediate.new(nil, 0), node.operands[2]])
list << Instruction.new(node.codeOrigin, opCmp, [node.operands[1], node.operands[0], tmp0])
list << Instruction.new(node.codeOrigin, "move", [Immediate.new(nil, 1), tmp1])
list << Instruction.new(node.codeOrigin, opMov, [node.operands[2], tmp1, tmp0])
end
def mipsLowerCompares(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
case node.opcode
when "cieq", "cpeq", "cbeq"
mipsLowerCompareTemplate(newList, node, "subp", "movz")
when "cineq", "cpneq", "cbneq"
mipsLowerCompareTemplate(newList, node, "subp", "movn")
when "tiz", "tbz", "tpz"
mipsLowerCompareTemplate(newList, node, "andp", "movz")
when "tinz", "tbnz", "tpnz"
mipsLowerCompareTemplate(newList, node, "andp", "movn")
when "tio", "tbo", "tpo"
tmp = Tmp.new(node.codeOrigin, :gpr)
list << Instruction.new(node.codeOrigin, "andp", [node.operands[1], node.operands[0], tmp])
list << Instruction.new(node.codeOrigin, "slt", [node.operands[2], MIPS_ZERO_REG, tmp])
when "tis", "tbs", "tps"
tmp = Tmp.new(node.codeOrigin, :gpr)
list << Instruction.new(node.codeOrigin, "andp", [node.operands[1], node.operands[0], tmp])
list << Instruction.new(node.codeOrigin, "slt", [node.operands[2], tmp, MIPS_ZERO_REG])
else
newList << node
end
else
newList << node
end
}
newList
end
#
# Lea support.
#
class Address
def mipsEmitLea(destination)
if destination == base
$asm.puts "addiu #{destination.mipsOperand}, #{offset.value}"
else
$asm.puts "addiu #{destination.mipsOperand}, #{base.mipsOperand}, #{offset.value}"
end
end
end
#
# Add PIC compatible header code to prologue/entry rutins.
#
def mipsAddPICCode(list)
myList = []
list.each {
| node |
myList << node
if node.is_a? Label
if /_prologue$/.match(node.name) || /^_llint_function_/.match(node.name)
# Functions called from trampoline/JIT codes.
myList << Instruction.new(node.codeOrigin, "pichdr", [])
elsif /_llint_op_catch/.match(node.name)
# Exception cactcher entry point function.
myList << Instruction.new(node.codeOrigin, "pichdrra", [])
end
end
}
myList
end
#
# Actual lowering code follows.
#
class Sequence
def getModifiedListMIPS
result = @list
# Verify that we will only see instructions and labels.
result.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
}
result = mipsAddPICCode(result)
result = mipsLowerSimpleBranchOps(result)
result = riscLowerSimpleBranchOps(result)
result = riscLowerHardBranchOps(result)
result = riscLowerShiftOps(result)
result = mipsLowerMalformedAddresses(result) {
| node, address |
if address.is_a? Address
(-0xffff..0xffff).include? address.offset.value
else
false
end
}
result = riscLowerMalformedAddressesDouble(result)
result = riscLowerMisplacedImmediates(result, ["storeb", "storei", "storep"])
result = mipsLowerMisplacedImmediates(result)
result = riscLowerMalformedImmediates(result, -0xffff..0xffff)
result = mipsLowerMisplacedAddresses(result)
result = riscLowerMisplacedAddresses(result)
result = riscLowerRegisterReuse(result)
result = mipsLowerCompares(result)
result = assignRegistersToTemporaries(result, :gpr, MIPS_TEMP_GPRS)
result = assignRegistersToTemporaries(result, :fpr, MIPS_TEMP_FPRS)
return result
end
end
def mipsOperands(operands)
operands.map{|v| v.mipsOperand}.join(", ")
end
def mipsFlippedOperands(operands)
mipsOperands([operands[-1]] + operands[0..-2])
end
def getMIPSOpcode(opcode, suffix)
end
def emitMIPSCompact(opcode, opcodei, operands)
postfix = ""
if opcode == "sub"
if operands[0].is_a? Immediate
opcode = "add"
operands[0] = Immediate.new(operands[0].codeOrigin, -1 * operands[0].value)
elsif operands[1].is_a? Immediate
opcode = "add"
operands[1] = Immediate.new(operands[1].codeOrigin, -1 * operands[1].value)
end
postfix = "u"
elsif opcode == "add"
postfix = "u"
end
if operands.size == 3
if operands[0].is_a? Immediate
$asm.puts "#{opcode}i#{postfix} #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].value}"
elsif operands[1].is_a? Immediate
$asm.puts "#{opcode}i#{postfix} #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].value}"
else
$asm.puts "#{opcode}#{postfix} #{mipsFlippedOperands(operands)}"
end
else
raise unless operands.size == 2
raise unless operands[1].register?
if operands[0].is_a? Immediate
$asm.puts "#{opcode}i#{postfix} #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
else
$asm.puts "#{opcode}#{postfix} #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
end
end
end
def emitMIPSShiftCompact(opcode, operands)
if operands.size == 3
if (operands[1].is_a? Immediate)
$asm.puts "#{opcode} #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].value}"
else
$asm.puts "#{opcode}v #{mipsFlippedOperands(operands)}"
end
else
raise unless operands.size == 2
if operands[0].register?
$asm.puts "#{opcode}v #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
else
$asm.puts "#{opcode} #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].value}"
end
end
end
def emitMIPS(opcode, operands)
if operands.size == 3
$asm.puts "#{opcode} #{mipsFlippedOperands(operands)}"
else
raise unless operands.size == 2
$asm.puts "#{opcode} #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
end
end
def emitMIPSDoubleBranch(branchOpcode, neg, operands)
$asm.puts "c.#{branchOpcode}.d #{mipsOperands(operands[0..1])}"
if (!neg)
$asm.puts "bc1t #{operands[2].asmLabel}"
else
$asm.puts "bc1f #{operands[2].asmLabel}"
end
end
class Instruction
def lowerMIPS
$asm.comment codeOriginString
case opcode
when "addi", "addp", "addis"
if operands.size == 3 and operands[0].is_a? Immediate
raise unless operands[1].register?
raise unless operands[2].register?
if operands[0].value == 0 #and suffix.empty?
unless operands[1] == operands[2]
$asm.puts "move #{operands[2].mipsOperand}, #{operands[1].mipsOperand}"
end
else
$asm.puts "addiu #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
end
elsif operands.size == 3 and operands[0].register?
raise unless operands[1].register?
raise unless operands[2].register?
$asm.puts "addu #{mipsFlippedOperands(operands)}"
else
if operands[0].is_a? Immediate
unless Immediate.new(nil, 0) == operands[0]
$asm.puts "addiu #{operands[1].mipsOperand}, #{mipsFlippedOperands(operands)}"
end
else
$asm.puts "addu #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
end
end
when "andi", "andp"
emitMIPSCompact("and", "and", operands)
when "ori", "orp"
emitMIPSCompact("or", "orr", operands)
when "oris"
emitMIPSCompact("or", "orrs", operands)
when "xori", "xorp"
emitMIPSCompact("xor", "eor", operands)
when "lshifti", "lshiftp"
emitMIPSShiftCompact("sll", operands)
when "rshifti", "rshiftp"
emitMIPSShiftCompact("sra", operands)
when "urshifti", "urshiftp"
emitMIPSShiftCompact("srl", operands)
when "muli", "mulp"
emitMIPS("mul", operands)
when "subi", "subp", "subis"
emitMIPSCompact("sub", "subs", operands)
when "negi", "negp"
$asm.puts "negu #{operands[0].mipsOperand}, #{operands[0].mipsOperand}"
when "noti"
$asm.puts "nor #{operands[0].mipsOperand}, #{operands[0].mipsOperand}, $zero"
when "loadi", "loadis", "loadp"
$asm.puts "lw #{mipsFlippedOperands(operands)}"
when "storei", "storep"
$asm.puts "sw #{mipsOperands(operands)}"
when "loadb"
$asm.puts "lbu #{mipsFlippedOperands(operands)}"
when "loadbs"
$asm.puts "lb #{mipsFlippedOperands(operands)}"
when "storeb"
$asm.puts "sb #{mipsOperands(operands)}"
when "loadh"
$asm.puts "lhu #{mipsFlippedOperands(operands)}"
when "loadhs"
$asm.puts "lh #{mipsFlippedOperands(operands)}"
when "storeh"
$asm.puts "shv #{mipsOperands(operands)}"
when "loadd"
$asm.puts "ldc1 #{mipsFlippedOperands(operands)}"
when "stored"
$asm.puts "sdc1 #{mipsOperands(operands)}"
when "addd"
emitMIPS("add.d", operands)
when "divd"
emitMIPS("div.d", operands)
when "subd"
emitMIPS("sub.d", operands)
when "muld"
emitMIPS("mul.d", operands)
when "sqrtd"
$asm.puts "sqrt.d #{mipsFlippedOperands(operands)}"
when "ci2d"
raise "invalid ops of #{self.inspect} at #{codeOriginString}" unless operands[1].is_a? FPRegisterID and operands[0].register?
$asm.puts "mtc1 #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
$asm.puts "cvt.d.w #{operands[1].mipsOperand}, #{operands[1].mipsOperand}"
when "bdeq"
emitMIPSDoubleBranch("eq", false, operands)
when "bdneq"
emitMIPSDoubleBranch("ueq", true, operands)
when "bdgt"
emitMIPSDoubleBranch("ule", true, operands)
when "bdgteq"
emitMIPSDoubleBranch("ult", true, operands)
when "bdlt"
emitMIPSDoubleBranch("olt", false, operands)
when "bdlteq"
emitMIPSDoubleBranch("ole", false, operands)
when "bdequn"
emitMIPSDoubleBranch("ueq", false, operands)
when "bdnequn"
emitMIPSDoubleBranch("eq", true, operands)
when "bdgtun"
emitMIPSDoubleBranch("ole", true, operands)
when "bdgtequn"
emitMIPSDoubleBranch("olt", true, operands)
when "bdltun"
emitMIPSDoubleBranch("ult", false, operands)
when "bdltequn"
emitMIPSDoubleBranch("ule", false, 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 "MIPS does not support this opcode yet, #{codeOrigin}"
when "td2i"
$asm.puts "cvt.w.d #{MIPS_SCRATCH_FPR.mipsSingleLo}, #{operands[0].mipsOperand}"
$asm.puts "mfc1 #{operands[1].mipsOperand}, #{MIPS_SCRATCH_FPR.mipsSingleLo}"
when "bcd2i"
$asm.puts "cvt.w.d #{MIPS_SCRATCH_FPR.mipsSingleLo}, #{operands[0].mipsOperand}"
$asm.puts "mfc1 #{operands[1].mipsOperand}, #{MIPS_SCRATCH_FPR.mipsSingleLo}"
$asm.puts "cvt.d.w #{MIPS_SCRATCH_FPR.mipsOperand}, #{MIPS_SCRATCH_FPR.mipsSingleLo}"
emitMIPSDoubleBranch("eq", true, [MIPS_SCRATCH_FPR, operands[0], operands[2]])
$asm.puts "beq #{operands[1].mipsOperand}, $zero, #{operands[2].asmLabel}"
when "movdz"
# FIXME: either support this or remove it.
raise "MIPS does not support this opcode yet, #{codeOrigin}"
when "pop"
$asm.puts "lw #{operands[0].mipsOperand}, 0($sp)"
$asm.puts "addiu $sp, $sp, 4"
when "push"
$asm.puts "addiu $sp, $sp, -4"
$asm.puts "sw #{operands[0].mipsOperand}, 0($sp)"
when "move", "sxi2p", "zxi2p"
if operands[0].is_a? Immediate
mipsMoveImmediate(operands[0].value, operands[1])
else
$asm.puts "move #{mipsFlippedOperands(operands)}"
end
when "nop"
$asm.puts "nop"
when "bieq", "bpeq", "bbeq"
$asm.puts "beq #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
when "bineq", "bpneq", "bbneq"
$asm.puts "bne #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
when "bigt", "bpgt", "bbgt"
$asm.puts "bgt #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
when "bigteq", "bpgteq", "bbgteq"
$asm.puts "bge #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
when "bilt", "bplt", "bblt"
$asm.puts "blt #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
when "bilteq", "bplteq", "bblteq"
$asm.puts "ble #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
when "jmp"
if operands[0].label?
$asm.puts "j #{operands[0].asmLabel}"
else
$asm.puts "jr #{operands[0].mipsOperand}"
end
when "call"
if operands[0].label?
$asm.puts "jal #{operands[0].asmLabel}"
else
$asm.puts "jalr #{operands[0].mipsOperand}"
end
when "break"
$asm.puts "break"
when "ret"
$asm.puts "jr $ra"
when "cia", "cpa", "cba"
$asm.puts "sltu #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
when "ciaeq", "cpaeq", "cbaeq"
$asm.puts "sltu #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
$asm.puts "xori #{operands[2].mipsOperand}, 1"
when "cib", "cpb", "cbb"
$asm.puts "sltu #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
when "cibeq", "cpbeq", "cbbeq"
$asm.puts "sltu #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
$asm.puts "xori #{operands[2].mipsOperand}, 1"
when "cigt", "cpgt", "cbgt"
$asm.puts "slt #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
when "cigteq", "cpgteq", "cbgteq"
$asm.puts "slt #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
$asm.puts "xori #{operands[2].mipsOperand}, 1"
when "cilt", "cplt", "cblt"
$asm.puts "slt #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
when "cilteq", "cplteq", "cblteq"
$asm.puts "slt #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
$asm.puts "xori #{operands[2].mipsOperand}, 1"
when "peek"
$asm.puts "lw #{operands[1].mipsOperand}, #{operands[0].value * 4}($sp)"
when "poke"
$asm.puts "sw #{operands[1].mipsOperand}, #{operands[0].value * 4}($sp)"
when "fii2d"
$asm.puts "mtc1 #{operands[0].mipsOperand}, #{operands[2].mipsSingleLo}"
$asm.puts "mtc1 #{operands[1].mipsOperand}, #{operands[2].mipsSingleHi}"
when "fd2ii"
$asm.puts "mfc1 #{operands[1].mipsOperand}, #{operands[0].mipsSingleLo}"
$asm.puts "mfc1 #{operands[2].mipsOperand}, #{operands[0].mipsSingleHi}"
when /^bo/
$asm.puts "bgt #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}"
when /^bs/
$asm.puts "blt #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}"
when /^bz/
$asm.puts "beq #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}"
when /^bnz/
$asm.puts "bne #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}"
when "leai", "leap"
operands[0].mipsEmitLea(operands[1])
when "smulli"
raise "Wrong number of arguments to smull in #{self.inspect} at #{codeOriginString}" unless operands.length == 4
$asm.puts "mult #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
$asm.puts "mflo #{operands[2].mipsOperand}"
$asm.puts "mfhi #{operands[3].mipsOperand}"
when "movz"
$asm.puts "movz #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}"
when "movn"
$asm.puts "movn #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}"
when "slt", "sltb"
$asm.puts "slt #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}"
when "sltu", "sltub"
$asm.puts "sltu #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}"
when "pichdr"
$asm.putStr("OFFLINE_ASM_CPLOAD($25)")
$asm.puts "move $s4, $gp"
when "pichdrra"
$asm.putStr("OFFLINE_ASM_CPLOAD($31)")
$asm.puts "move $s4, $gp"
else
lowerDefault
end
end
end