diff options
author | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-06-04 19:52:29 +0200 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-06-04 19:52:29 +0200 |
commit | a3494fbd8bde2ebda243527c2be1515170595e80 (patch) | |
tree | e26955b654e65b741aba2a95ae055889dd959459 /Source/JavaScriptCore/offlineasm/mips.rb | |
parent | a5b43f4f03d88d0fca8fb4531f49d1fecff582b7 (diff) | |
parent | 289da5616df56912fa6f8c319d61fb4489c51add (diff) | |
download | qtwebkit-a3494fbd8bde2ebda243527c2be1515170595e80.tar.gz |
Merge remote-tracking branch 'origin/stable' into dev
Conflicts:
.qmake.conf
Source/sync.profile
Change-Id: Ia5f054c8e60b4b10a041de67f4752b999561fa56
Diffstat (limited to 'Source/JavaScriptCore/offlineasm/mips.rb')
-rw-r--r-- | Source/JavaScriptCore/offlineasm/mips.rb | 892 |
1 files changed, 892 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/offlineasm/mips.rb b/Source/JavaScriptCore/offlineasm/mips.rb new file mode 100644 index 000000000..c6daa3c7d --- /dev/null +++ b/Source/JavaScriptCore/offlineasm/mips.rb @@ -0,0 +1,892 @@ +# 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 > 0x7fff + "#{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 + 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) + 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 + raise "Unhandled opcode #{opcode} at #{codeOriginString}" + end + end +end |