| # Copyright (C) 2012-2018 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' | 
 |  | 
 | # GPR conventions, to match the baseline JIT | 
 | # | 
 | # $a0 => a0, t7 | 
 | # $a1 => a1, t8 | 
 | # $a2 => a2, t9 | 
 | # $a3 => a3, t10 | 
 | # $v0 => t0, r0 | 
 | # $v1 => t1, r1 | 
 | # $t0 =>            (scratch) | 
 | # $t1 =>            (scratch) | 
 | # $t2 =>         t2 | 
 | # $t3 =>         t3 | 
 | # $t4 =>         t4 | 
 | # $t5 =>         t5 | 
 | # $t6 =>         t6 | 
 | # $t7 =>            (scratch) | 
 | # $t8 =>            (scratch) | 
 | # $t9 =>            (stores the callee of a call opcode) | 
 | # $gp =>            (globals) | 
 | # $s4 =>            (callee-save used to preserve $gp across calls) | 
 | # $ra => lr | 
 | # $sp => sp | 
 | # $fp => cfr | 
 | # | 
 | # FPR conventions, to match the baseline JIT | 
 | # We don't have fa2 or fa3! | 
 | #  $f0 => ft0, fr | 
 | #  $f2 => ft1 | 
 | #  $f4 => ft2 | 
 | #  $f6 => ft3 | 
 | #  $f8 => ft4 | 
 | # $f10 => ft5 | 
 | # $f12 =>        fa0 | 
 | # $f14 =>        fa1 | 
 | # $f16 =>            (scratch) | 
 | # $f18 =>            (scratch) | 
 |  | 
 | 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("$t0"), SpecialRegister.new("$t1"), 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_CALL_REG = SpecialRegister.new("$t9") | 
 | MIPS_RETURN_ADDRESS_REG = SpecialRegister.new("$ra") | 
 | 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", "t7" | 
 |             "$a0" | 
 |         when "a1", "t8" | 
 |             "$a1" | 
 |         when "a2", "t9" | 
 |             "$a2" | 
 |         when "a3", "t10" | 
 |             "$a3" | 
 |         when "t0", "r0" | 
 |             "$v0" | 
 |         when "t1", "r1" | 
 |             "$v1" | 
 |         when "t2" | 
 |             "$t2" | 
 |         when "t3" | 
 |             "$t3" | 
 |         when "t4" | 
 |             "$t4" | 
 |         when "t5" | 
 |             "$t5" | 
 |         when "cfr" | 
 |             "$fp" | 
 |         when "csr0" | 
 |             "$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 | 
 |  | 
 | # | 
 | # Negate condition of branches to labels. | 
 | # | 
 |  | 
 | class Instruction | 
 |     def mipsNegateCondition(list) | 
 |         /^(b(add|sub|or|mul|t)?)([ipb])/.match(opcode) | 
 |         case $~.post_match | 
 |         when "eq" | 
 |             op = "neq" | 
 |         when "neq" | 
 |             op = "eq" | 
 |         when "z" | 
 |             op = "nz" | 
 |         when "nz" | 
 |             op = "z" | 
 |         when "gt" | 
 |             op = "lteq" | 
 |         when "gteq" | 
 |             op = "lt" | 
 |         when "lt" | 
 |             op = "gteq" | 
 |         when "lteq" | 
 |             op = "gt" | 
 |         when "a" | 
 |             op = "beq" | 
 |         when "b" | 
 |             op = "aeq" | 
 |         when "aeq" | 
 |             op = "b" | 
 |         when "beq" | 
 |             op = "a" | 
 |         else | 
 |             raise "Can't negate #{opcode} branch." | 
 |         end | 
 |         noBranch = LocalLabel.unique("nobranch") | 
 |         noBranchRef = LocalLabelReference.new(codeOrigin, noBranch) | 
 |         toRef = operands[-1] | 
 |         list << Instruction.new(codeOrigin, "#{$1}#{$3}#{op}", operands[0..-2].push(noBranchRef), annotation) | 
 |         list << Instruction.new(codeOrigin, "la", [toRef, MIPS_CALL_REG]) | 
 |         list << Instruction.new(codeOrigin, "jmp", [MIPS_CALL_REG]) | 
 |         list << noBranch | 
 |     end | 
 | end | 
 |  | 
 | def mipsLowerFarBranchOps(list) | 
 |     newList = [] | 
 |     list.each { | 
 |         | node | | 
 |         if node.is_a? Instruction | 
 |             annotation = node.annotation | 
 |             case node.opcode | 
 |             when /^b(add|sub|or|mul|t)?([ipb])/ | 
 |                 if node.operands[-1].is_a? LabelReference | 
 |                     node.mipsNegateCondition(newList) | 
 |                     next | 
 |                 end | 
 |             end | 
 |         end | 
 |         newList << node | 
 |     } | 
 |     newList | 
 | 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 | 
 |         tl = condOp[-1, 1] | 
 |         tmp = Tmp.new(node.codeOrigin, :gpr) | 
 |         list << Instruction.new(node.codeOrigin, | 
 |                                 "and" + tl, | 
 |                                 [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) | 
 |         mapChildren { | 
 |             | subNode | | 
 |             subNode.mipsLowerMalformedAddressesRecurse(list) | 
 |         } | 
 |     end | 
 |  | 
 |     def mipsLowerShiftedAddressesRecurse(list, isFirst, tmp) | 
 |         mapChildren { | 
 |             | subNode | | 
 |             subNode.mipsLowerShiftedAddressesRecurse(list, isFirst, tmp) | 
 |         } | 
 |     end | 
 | end | 
 |  | 
 | class BaseIndex | 
 |     def mipsLowerMalformedAddressesRecurse(list) | 
 |         tmp = Tmp.new(codeOrigin, :gpr) | 
 |         if scaleShift == 0 | 
 |             list << Instruction.new(codeOrigin, "addp", [base, index, tmp]) | 
 |             Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, offset.value)); | 
 |         end | 
 |     end | 
 |  | 
 |     def mipsLowerShiftedAddressesRecurse(list, isFirst, tmp) | 
 |         if isFirst | 
 |             list << Instruction.new(codeOrigin, "lshifti", [index, Immediate.new(codeOrigin, scaleShift), tmp]); | 
 |             list << Instruction.new(codeOrigin, "addp", [base, tmp]) | 
 |         end | 
 |         Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, offset.value)); | 
 |     end | 
 | end | 
 |  | 
 | # | 
 | # Lowering of BaseIndex addresses with optimization for MIPS. | 
 | # | 
 | # offline asm instruction pair: | 
 | #   loadi 4[cfr, t0, 8], t2 | 
 | #   loadi 0[cfr, t0, 8], t0 | 
 | # | 
 | # lowered instructions:  | 
 | #   lshifti t0, 3, tmp | 
 | #   addp    cfr, tmp | 
 | #   loadi   4[tmp], t2 | 
 | #   loadi   0[tmp], t0 | 
 | # | 
 |  | 
 | def mipsHasShiftedBaseIndexAddress(instruction) | 
 |     instruction.operands.each_with_index { | 
 |         | operand, index | | 
 |         if operand.is_a? BaseIndex and operand.scaleShift != 0 | 
 |             return index | 
 |         end | 
 |     } | 
 |     -1 | 
 | end | 
 |  | 
 | def mipsScaleOfBaseIndexMatches(baseIndex0, baseIndex1) | 
 |     baseIndex0.base == baseIndex1.base and | 
 |     baseIndex0.index == baseIndex1.index and | 
 |     baseIndex0.scale == baseIndex1.scale | 
 | end | 
 |  | 
 | def mipsLowerBaseIndexAddresses(list) | 
 |     newList = [ list[0] ] | 
 |     tmp = nil | 
 |     list.each_cons(2) { | 
 |         | nodes | | 
 |         if nodes[1].is_a? Instruction | 
 |             ind = mipsHasShiftedBaseIndexAddress(nodes[1]) | 
 |             if ind != -1 | 
 |                 if nodes[0].is_a? Instruction and | 
 |                     nodes[0].opcode == nodes[1].opcode and | 
 |                     ind == mipsHasShiftedBaseIndexAddress(nodes[0]) and | 
 |                     mipsScaleOfBaseIndexMatches(nodes[0].operands[ind], nodes[1].operands[ind]) | 
 |  | 
 |                     newList << nodes[1].mipsLowerShiftedAddressesRecurse(newList, false, tmp) | 
 |                 else | 
 |                     tmp = Tmp.new(codeOrigin, :gpr) | 
 |                     newList << nodes[1].mipsLowerShiftedAddressesRecurse(newList, true, tmp) | 
 |                 end | 
 |             else | 
 |                 newList << nodes[1].mipsLowerMalformedAddressesRecurse(newList) | 
 |             end | 
 |         else | 
 |             newList << nodes[1] | 
 |         end | 
 |     } | 
 |     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) | 
 |             when "andi", "andp", "ori", "orp", "orh", "xori", "xorp" | 
 |                 newList << node.riscLowerMalformedImmediatesRecurse(newList, 0..0xffff) | 
 |             else | 
 |                 newList << node | 
 |             end | 
 |         else | 
 |             newList << node | 
 |         end | 
 |     } | 
 |     newList | 
 | end | 
 |  | 
 | # | 
 | # Specialization of lowering of misplaced addresses. | 
 | # | 
 |  | 
 | class LocalLabelReference | 
 |     def register? | 
 |         false | 
 |     end | 
 | end | 
 |  | 
 | def mipsAsRegister(preList, postList, operand, needRestore) | 
 |     tmp = MIPS_CALL_REG | 
 |     if operand.address? | 
 |         preList << Instruction.new(operand.codeOrigin, "loadp", [operand, MIPS_CALL_REG]) | 
 |     elsif operand.is_a? LabelReference | 
 |         preList << Instruction.new(operand.codeOrigin, "la", [operand, MIPS_CALL_REG]) | 
 |     elsif operand.register? and operand != MIPS_CALL_REG | 
 |         preList << Instruction.new(operand.codeOrigin, "move", [operand, MIPS_CALL_REG]) | 
 |     else | 
 |         needRestore = false | 
 |         tmp = operand | 
 |     end | 
 |     if needRestore | 
 |         postList << Instruction.new(operand.codeOrigin, "move", [MIPS_GPSAVE_REG, MIPS_GP_REG]) | 
 |     end | 
 |     tmp | 
 | end | 
 |  | 
 | def mipsLowerMisplacedAddresses(list) | 
 |     newList = [] | 
 |     list.each { | 
 |         | node | | 
 |         if node.is_a? Instruction | 
 |             postInstructions = [] | 
 |             annotation = node.annotation | 
 |             case node.opcode | 
 |             when "jmp" | 
 |                 newList << Instruction.new(node.codeOrigin, | 
 |                                            node.opcode, | 
 |                                            [mipsAsRegister(newList, [], node.operands[0], false)]) | 
 |             when "call" | 
 |                 newList << Instruction.new(node.codeOrigin, | 
 |                                            node.opcode, | 
 |                                            [mipsAsRegister(newList, postInstructions, node.operands[0], true)]) | 
 |             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 "andb" | 
 |                 newList << Instruction.new(node.codeOrigin, | 
 |                                            "andi", | 
 |                                            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 all the LLInt rutins. | 
 | # | 
 |  | 
 | def mipsAddPICCode(list) | 
 |     myList = [] | 
 |     list.each { | 
 |         | node | | 
 |         myList << node | 
 |         if node.is_a? Label | 
 |             if node.name =~ /^.*_return_location(?:_(?:wide16|wide32))?$/ | 
 |                 # We need to have a special case for return location labels because they are always | 
 |                 # reached from a `ret` instruction. In this case, we need to proper reconfigure `$gp` | 
 |                 # using `$ra` instead of using `$t9`. | 
 |                 myList << Instruction.new(node.codeOrigin, "pichdr", [MIPS_RETURN_ADDRESS_REG]) | 
 |             else | 
 |                 myList << Instruction.new(node.codeOrigin, "pichdr", [MIPS_CALL_REG]) | 
 |             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 = mipsLowerFarBranchOps(result) | 
 |         result = mipsLowerSimpleBranchOps(result) | 
 |         result = riscLowerSimpleBranchOps(result) | 
 |         result = riscLowerHardBranchOps(result) | 
 |         result = riscLowerShiftOps(result) | 
 |         result = mipsLowerBaseIndexAddresses(result) | 
 |         result = riscLowerMalformedAddresses(result) { | 
 |             | node, address | | 
 |             if address.is_a? Address | 
 |                 (-0x7fff..0x7fff).include? address.offset.value | 
 |             else | 
 |                 false | 
 |             end | 
 |         } | 
 |         result = riscLowerMalformedAddressesDouble(result) | 
 |         result = riscLowerMisplacedImmediates(result, ["storeb", "storei", "storep"]) | 
 |         result = mipsLowerMisplacedImmediates(result) | 
 |         result = riscLowerMalformedImmediates(result, -0x7fff..0x7fff, -0x7fff..0x7fff) | 
 |         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 | 
 |  | 
 | def emitMIPSJumpOrCall(opcode, operand) | 
 |     if operand.label? | 
 |         raise "Direct call/jump to a not local label." unless operand.is_a? LocalLabelReference | 
 |         $asm.puts "#{opcode} #{operand.asmLabel}" | 
 |     else | 
 |         raise "Invalid call/jump register." unless operand == MIPS_CALL_REG | 
 |         $asm.puts "#{opcode}r #{MIPS_CALL_REG.mipsOperand}" | 
 |     end | 
 | end | 
 |  | 
 | class Instruction | 
 |     def lowerMIPS | 
 |         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", "orh" | 
 |             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 "loadbsi" | 
 |             $asm.puts "lb #{mipsFlippedOperands(operands)}" | 
 |         when "storeb" | 
 |             $asm.puts "sb #{mipsOperands(operands)}" | 
 |         when "loadh" | 
 |             $asm.puts "lhu #{mipsFlippedOperands(operands)}" | 
 |         when "loadhsi" | 
 |             $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 "la" | 
 |             $asm.puts "la #{operands[1].mipsOperand}, #{operands[0].asmLabel}" | 
 |         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 "ci2ds" | 
 |             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" | 
 |             operands.each { | 
 |                 | op | | 
 |                 $asm.puts "lw #{op.mipsOperand}, 0($sp)" | 
 |                 $asm.puts "addiu $sp, $sp, 4" | 
 |             } | 
 |         when "push" | 
 |             operands.each { | 
 |                 | op | | 
 |                 $asm.puts "addiu $sp, $sp, -4" | 
 |                 $asm.puts "sw #{op.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" | 
 |             emitMIPSJumpOrCall("j", operands[0]) | 
 |         when "call" | 
 |             emitMIPSJumpOrCall("jal", operands[0]) | 
 |         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.putStr("#if WTF_MIPS_ISA_REV_AT_LEAST(2)") | 
 |             $asm.puts "mthc1 #{operands[1].mipsOperand}, #{operands[2].mipsSingleLo}" | 
 |             $asm.putStr("#else") | 
 |             $asm.puts "mtc1 #{operands[1].mipsOperand}, #{operands[2].mipsSingleHi}" | 
 |             $asm.putStr("#endif") | 
 |         when "fd2ii" | 
 |             $asm.puts "mfc1 #{operands[1].mipsOperand}, #{operands[0].mipsSingleLo}" | 
 |             $asm.putStr("#if WTF_MIPS_ISA_REV_AT_LEAST(2)") | 
 |             $asm.puts "mfhc1 #{operands[2].mipsOperand}, #{operands[0].mipsSingleLo}" | 
 |             $asm.putStr("#else") | 
 |             $asm.puts "mfc1 #{operands[2].mipsOperand}, #{operands[0].mipsSingleHi}" | 
 |             $asm.putStr("#endif") | 
 |         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" | 
 |             if operands[0].is_a? LabelReference | 
 |                 labelRef = operands[0] | 
 |                 raise unless labelRef.offset == 0 | 
 |                 $asm.puts "lw #{operands[1].mipsOperand}, %got(#{labelRef.asmLabel})($gp)" | 
 |             else | 
 |                 operands[0].mipsEmitLea(operands[1]) | 
 |             end | 
 |  | 
 |         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 "setcallreg" | 
 |             $asm.puts "move #{MIPS_CALL_REG.mipsOperand}, #{operands[0].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(#{operands[0].mipsOperand})") | 
 |         when "memfence" | 
 |             $asm.puts "sync" | 
 |         else | 
 |             lowerDefault | 
 |         end | 
 |     end | 
 | end |