1# Copyright (C) 2012 Apple Inc. All rights reserved.
2# Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions
6# are met:
7# 1. Redistributions of source code must retain the above copyright
8#    notice, this list of conditions and the following disclaimer.
9# 2. Redistributions in binary form must reproduce the above copyright
10#    notice, this list of conditions and the following disclaimer in the
11#    documentation and/or other materials provided with the distribution.
12#
13# THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY
14# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR
17# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
25require 'risc'
26
27class Assembler
28    def putStr(str)
29        @outp.puts str
30    end
31end
32
33class Node
34    def mipsSingleHi
35        doubleOperand = mipsOperand
36        raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^\$f/
37        "$f" + ($~.post_match.to_i + 1).to_s
38    end
39    def mipsSingleLo
40        doubleOperand = mipsOperand
41        raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^\$f/
42        doubleOperand
43    end
44end
45
46class SpecialRegister < NoChildren
47    def mipsOperand
48        @name
49    end
50
51    def dump
52        @name
53    end
54
55    def register?
56        true
57    end
58end
59
60MIPS_TEMP_GPRS = [SpecialRegister.new("$t5"), SpecialRegister.new("$t6"), SpecialRegister.new("$t7"),
61                    SpecialRegister.new("$t8")]
62MIPS_ZERO_REG = SpecialRegister.new("$zero")
63MIPS_GP_REG = SpecialRegister.new("$gp")
64MIPS_GPSAVE_REG = SpecialRegister.new("$s4")
65MIPS_JUMP_REG = SpecialRegister.new("$ra")
66MIPS_CALL_REG = SpecialRegister.new("$t9")
67MIPS_TEMP_FPRS = [SpecialRegister.new("$f16")]
68MIPS_SCRATCH_FPR = SpecialRegister.new("$f18")
69
70def mipsMoveImmediate(value, register)
71    if value == 0
72        $asm.puts "add #{register.mipsOperand}, $zero, $zero"
73    else
74        $asm.puts "li #{register.mipsOperand}, #{value}"
75    end
76end
77
78class RegisterID
79    def mipsOperand
80        case name
81        when "a0"
82            "$a0"
83        when "a1"
84            "$a1"
85        when "r0", "t0"
86            "$v0"
87        when "r1", "t1"
88            "$v1"
89        when "t2"
90            "$t2"
91        when "t3"
92            "$s3"
93        when "t4"   # PC reg in llint
94            "$s2"
95        when "t5"
96            "$t5"
97        when "t6"
98            "$t6"
99        when "t7"
100            "$t7"
101        when "t8"
102            "$t8"
103        when "cfr"
104            "$s0"
105        when "lr"
106            "$ra"
107        when "sp"
108            "$sp"
109        else
110            raise "Bad register #{name} for MIPS at #{codeOriginString}"
111        end
112    end
113end
114
115class FPRegisterID
116    def mipsOperand
117        case name
118        when "ft0", "fr"
119            "$f0"
120        when "ft1"
121            "$f2"
122        when "ft2"
123            "$f4"
124        when "ft3"
125            "$f6"
126        when "ft4"
127            "$f8"
128        when "ft5"
129            "$f10"
130        when "fa0"
131            "$f12"
132        when "fa1"
133            "$f14"
134        else
135            raise "Bad register #{name} for MIPS at #{codeOriginString}"
136        end
137    end
138end
139
140class Immediate
141    def mipsOperand
142        raise "Invalid immediate #{value} at #{codeOriginString}" if value < -0x7fff or value > 0x7fff
143        "#{value}"
144    end
145end
146
147class Address
148    def mipsOperand
149        raise "Bad offset at #{codeOriginString}" if offset.value < -0x7fff or offset.value > 0x7fff
150        "#{offset.value}(#{base.mipsOperand})"
151    end
152end
153
154class AbsoluteAddress
155    def mipsOperand
156        raise "Unconverted absolute address at #{codeOriginString}"
157    end
158end
159
160#
161# Lower 'and' masked branches
162#
163
164def lowerMIPSCondBranch(list, condOp, node)
165    if node.operands.size == 2
166        list << Instruction.new(node.codeOrigin,
167                                condOp,
168                                [node.operands[0], MIPS_ZERO_REG, node.operands[-1]],
169                                node.annotation)
170    elsif node.operands.size == 3
171        tmp = Tmp.new(node.codeOrigin, :gpr)
172        list << Instruction.new(node.codeOrigin,
173                                "andi",
174                                [node.operands[0], node.operands[1], tmp],
175                                node.annotation)
176        list << Instruction.new(node.codeOrigin,
177                                condOp,
178                                [tmp, MIPS_ZERO_REG, node.operands[-1]])
179    else
180        raise "Expected 2 or 3 operands but got #{node.operands.size} at #{node.codeOriginString}"
181    end
182end
183
184#
185# Lowering of branch ops. For example:
186#
187# baddiz foo, bar, baz
188#
189# will become:
190#
191# addi foo, bar
192# bz baz
193#
194
195def mipsLowerSimpleBranchOps(list)
196    newList = []
197    list.each {
198        | node |
199        if node.is_a? Instruction
200            annotation = node.annotation
201            case node.opcode
202            when /^b(addi|subi|ori|addp)/
203                op = $1
204                bc = $~.post_match
205                branch = "b" + bc
206
207                case op
208                when "addi", "addp"
209                    op = "addi"
210                when "subi"
211                    op = "subi"
212                when "ori"
213                    op = "ori"
214                end
215
216                if bc == "o"
217                    case op
218                    when "addi"
219                        #  addu $s0, $s1, $s2
220                        #  xor $t0, $s1, $s2
221                        #  blt $t0, $zero, no overflow
222                        #  xor $t0, $s0, $s1
223                        #  blt $t0, $zero, overflow
224                        # no overflow:
225                        #
226                        tr = Tmp.new(node.codeOrigin, :gpr)
227                        tmp = Tmp.new(node.codeOrigin, :gpr)
228                        noFlow = LocalLabel.unique("noflow")
229                        noFlowRef = LocalLabelReference.new(node.codeOrigin, noFlow)
230                        newList << Instruction.new(node.codeOrigin, op, [node.operands[0], node.operands[1], tr], annotation)
231                        newList << Instruction.new(node.codeOrigin, "xori", [node.operands[0], node.operands[1], tmp])
232                        newList << Instruction.new(node.codeOrigin, "bilt", [tmp, MIPS_ZERO_REG, noFlowRef])
233                        newList << Instruction.new(node.codeOrigin, "xori", [tr, node.operands[0], tmp])
234                        newList << Instruction.new(node.codeOrigin, "bilt", [tmp, MIPS_ZERO_REG, node.operands[2]])
235                        newList << noFlow
236                        newList << Instruction.new(node.codeOrigin, "move", [tr, node.operands[1]])
237                    when "subi"
238                        #  subu $s0, $s1, $s2
239                        #  xor $t0, $s1, $s2
240                        #  bge $t0, $zero, no overflow
241                        #  xor $t0, $s0, $s1
242                        #  blt $t0, $zero, overflow
243                        # no overflow:
244                        #
245                        tr = Tmp.new(node.codeOrigin, :gpr)
246                        tmp = Tmp.new(node.codeOrigin, :gpr)
247                        noFlow = LocalLabel.unique("noflow")
248                        noFlowRef = LocalLabelReference.new(node.codeOrigin, noFlow)
249                        newList << Instruction.new(node.codeOrigin, op, [node.operands[1], node.operands[0], tr], annotation)
250                        newList << Instruction.new(node.codeOrigin, "xori", [node.operands[1], node.operands[0], tmp])
251                        newList << Instruction.new(node.codeOrigin, "bigteq", [tmp, MIPS_ZERO_REG, noFlowRef])
252                        newList << Instruction.new(node.codeOrigin, "xori", [tr, node.operands[1], tmp])
253                        newList << Instruction.new(node.codeOrigin, "bilt", [tmp, MIPS_ZERO_REG, node.operands[2]])
254                        newList << noFlow
255                        newList << Instruction.new(node.codeOrigin, "move", [tr, node.operands[1]])
256                    when "ori"
257                        # no ovwerflow at ori
258                        newList << Instruction.new(node.codeOrigin, op, node.operands[0..1], annotation)
259                    end
260                else
261                    if node.operands[1].is_a? Address
262                        addr = node.operands[1]
263                        tr = Tmp.new(node.codeOrigin, :gpr)
264                        newList << Instruction.new(node.codeOrigin, "loadp", [addr, tr], annotation)
265                        newList << Instruction.new(node.codeOrigin, op, [node.operands[0], tr])
266                        newList << Instruction.new(node.codeOrigin, "storep", [tr, addr])
267                    else
268                        tr = node.operands[1]
269                        newList << Instruction.new(node.codeOrigin, op, node.operands[0..-2], annotation)
270                    end
271                    newList << Instruction.new(node.codeOrigin, branch, [tr, MIPS_ZERO_REG, node.operands[-1]])
272                end
273            when "bia", "bpa", "bba"
274                tmp = Tmp.new(node.codeOrigin, :gpr)
275                comp = node.opcode[1] == ?b ? "sltub" : "sltu"
276                newList << Instruction.new(node.codeOrigin, comp, [tmp, node.operands[1], node.operands[0]], annotation)
277                newList << Instruction.new(node.codeOrigin, "bnz", [tmp, MIPS_ZERO_REG, node.operands[2]])
278            when "biaeq", "bpaeq", "bbaeq"
279                tmp = Tmp.new(node.codeOrigin, :gpr)
280                comp = node.opcode[1] == ?b ? "sltub" : "sltu"
281                newList << Instruction.new(node.codeOrigin, comp, [tmp, node.operands[0], node.operands[1]], annotation)
282                newList << Instruction.new(node.codeOrigin, "bz", [tmp, MIPS_ZERO_REG, node.operands[2]])
283            when "bib", "bpb", "bbb"
284                tmp = Tmp.new(node.codeOrigin, :gpr)
285                comp = node.opcode[1] == ?b ? "sltub" : "sltu"
286                newList << Instruction.new(node.codeOrigin, comp, [tmp, node.operands[0], node.operands[1]], annotation)
287                newList << Instruction.new(node.codeOrigin, "bnz", [tmp, MIPS_ZERO_REG, node.operands[2]])
288            when "bibeq", "bpbeq", "bbbeq"
289                tmp = Tmp.new(node.codeOrigin, :gpr)
290                comp = node.opcode[1] == ?b ? "sltub" : "sltu"
291                newList << Instruction.new(node.codeOrigin, comp, [tmp, node.operands[1], node.operands[0]], annotation)
292                newList << Instruction.new(node.codeOrigin, "bz", [tmp, MIPS_ZERO_REG, node.operands[2]])
293            when /^bt(i|p|b)/
294                lowerMIPSCondBranch(newList, "b" + $~.post_match + $1, node)
295            else
296                newList << node
297            end
298        else
299            newList << node
300        end
301    }
302    newList
303end
304
305#
306# Specialization of lowering of malformed BaseIndex addresses.
307#
308
309class Node
310    def mipsLowerMalformedAddressesRecurse(list, topLevelNode, &block)
311        mapChildren {
312            | subNode |
313            subNode.mipsLowerMalformedAddressesRecurse(list, topLevelNode, &block)
314        }
315    end
316end
317
318class Address
319    def mipsLowerMalformedAddressesRecurse(list, node, &block)
320        riscLowerMalformedAddressesRecurse(list, node, &block)
321    end
322end
323
324class BaseIndex
325    def mipsLowerMalformedAddressesRecurse(list, node, &block)
326        if scaleShift == 0
327            tmp0 = Tmp.new(codeOrigin, :gpr)
328            list << Instruction.new(codeOrigin, "addp", [base, index, tmp0])
329            Address.new(codeOrigin, tmp0, Immediate.new(codeOrigin, offset.value));
330        else
331            tmp0 = Tmp.new(codeOrigin, :gpr)
332            list << Instruction.new(codeOrigin, "lshifti", [index, Immediate.new(codeOrigin, scaleShift), tmp0]);
333            list << Instruction.new(codeOrigin, "addp", [base, tmp0])
334            Address.new(codeOrigin, tmp0, Immediate.new(codeOrigin, offset.value));
335        end
336    end
337end
338
339class AbsoluteAddress
340    def mipsLowerMalformedAddressesRecurse(list, node, &block)
341        riscLowerMalformedAddressesRecurse(list, node, &block)
342    end
343end
344
345def mipsLowerMalformedAddresses(list, &block)
346    newList = []
347    list.each {
348        | node |
349        newList << node.mipsLowerMalformedAddressesRecurse(newList, node, &block)
350    }
351    newList
352end
353
354#
355# Lowering of misplaced immediates of MIPS specific instructions. For example:
356#
357# sltu reg, 4, 2
358#
359# will become:
360#
361# move 4, tmp
362# sltu reg, tmp, 2
363#
364
365def mipsLowerMisplacedImmediates(list)
366    newList = []
367    list.each {
368        | node |
369        if node.is_a? Instruction
370            case node.opcode
371            when "slt", "sltu", "sltb", "sltub"
372                if node.operands[1].is_a? Immediate
373                    tmp = Tmp.new(node.codeOrigin, :gpr)
374                    newList << Instruction.new(node.codeOrigin, "move", [node.operands[1], tmp], node.annotation)
375                    newList << Instruction.new(node.codeOrigin, node.opcode,
376                                               [node.operands[0], tmp, node.operands[2]],
377                                               node.annotation)
378                else
379                    newList << node
380                end
381            else
382                newList << node
383            end
384        else
385            newList << node
386        end
387    }
388    newList
389end
390
391#
392# Specialization of lowering of misplaced addresses.
393#
394
395def mipsLowerMisplacedAddresses(list)
396    newList = []
397    list.each {
398        | node |
399        if node.is_a? Instruction
400            postInstructions = []
401            annotation = node.annotation
402            case node.opcode
403            when "jmp"
404                if node.operands[0].address?
405                    newList << Instruction.new(node.operands[0].codeOrigin, "loadi", [node.operands[0], MIPS_JUMP_REG])
406                    newList << Instruction.new(node.codeOrigin, node.opcode, [MIPS_JUMP_REG])
407                else
408                    newList << Instruction.new(node.codeOrigin,
409                                               node.opcode,
410                                               [riscAsRegister(newList, postInstructions, node.operands[0], "p", false)])
411                end
412            when "call"
413                restoreGP = false;
414                tmp = MIPS_CALL_REG
415                if node.operands[0].address?
416                    newList << Instruction.new(node.operands[0].codeOrigin, "loadp", [node.operands[0], MIPS_CALL_REG])
417                    restoreGP = true;
418                elsif node.operands[0].is_a? LabelReference
419                    tmp = node.operands[0]
420                    restoreGP = true;
421                elsif node.operands[0].register?
422                    newList << Instruction.new(node.operands[0].codeOrigin, "move", [node.operands[0], MIPS_CALL_REG])
423                    restoreGP = true;
424                else
425                    tmp = node.operands[0]
426                end
427                newList << Instruction.new(node.codeOrigin, node.opcode, [tmp])
428                if restoreGP
429                    newList << Instruction.new(node.codeOrigin, "move", [MIPS_GPSAVE_REG, MIPS_GP_REG])
430                end
431            when "slt", "sltu"
432                newList << Instruction.new(node.codeOrigin,
433                                           node.opcode,
434                                           riscAsRegisters(newList, [], node.operands, "i"))
435            when "sltub", "sltb"
436                newList << Instruction.new(node.codeOrigin,
437                                           node.opcode,
438                                           riscAsRegisters(newList, [], node.operands, "b"))
439            when /^(bz|bnz|bs|bo)/
440                tl = $~.post_match == "" ? "i" : $~.post_match
441                newList << Instruction.new(node.codeOrigin,
442                                           node.opcode,
443                                           riscAsRegisters(newList, [], node.operands, tl))
444            else
445                newList << node
446            end
447            newList += postInstructions
448        else
449            newList << node
450        end
451    }
452    newList
453end
454
455#
456# Lowering compares and tests.
457#
458
459def mipsLowerCompareTemplate(list, node, opCmp, opMov)
460    tmp0 = Tmp.new(node.codeOrigin, :gpr)
461    tmp1 = Tmp.new(node.codeOrigin, :gpr)
462    list << Instruction.new(node.codeOrigin, "move", [Immediate.new(nil, 0), node.operands[2]])
463    list << Instruction.new(node.codeOrigin, opCmp, [node.operands[1], node.operands[0], tmp0])
464    list << Instruction.new(node.codeOrigin, "move", [Immediate.new(nil, 1), tmp1])
465    list << Instruction.new(node.codeOrigin, opMov, [node.operands[2], tmp1, tmp0])
466end
467
468def mipsLowerCompares(list)
469    newList = []
470    list.each {
471        | node |
472        if node.is_a? Instruction
473            case node.opcode
474            when "cieq", "cpeq", "cbeq"
475                mipsLowerCompareTemplate(newList, node, "subp", "movz")
476            when "cineq", "cpneq", "cbneq"
477                mipsLowerCompareTemplate(newList, node, "subp", "movn")
478            when "tiz", "tbz", "tpz"
479                mipsLowerCompareTemplate(newList, node, "andp", "movz")
480            when "tinz", "tbnz", "tpnz"
481                mipsLowerCompareTemplate(newList, node, "andp", "movn")
482            when "tio", "tbo", "tpo"
483                tmp = Tmp.new(node.codeOrigin, :gpr)
484                list << Instruction.new(node.codeOrigin, "andp", [node.operands[1], node.operands[0], tmp])
485                list << Instruction.new(node.codeOrigin, "slt", [node.operands[2], MIPS_ZERO_REG, tmp])
486            when "tis", "tbs", "tps"
487                tmp = Tmp.new(node.codeOrigin, :gpr)
488                list << Instruction.new(node.codeOrigin, "andp", [node.operands[1], node.operands[0], tmp])
489                list << Instruction.new(node.codeOrigin, "slt", [node.operands[2], tmp, MIPS_ZERO_REG])
490            else
491                newList << node
492            end
493        else
494            newList << node
495        end
496    }
497    newList
498end
499
500#
501# Lea support.
502#
503
504class Address
505    def mipsEmitLea(destination)
506        if destination == base
507            $asm.puts "addiu #{destination.mipsOperand}, #{offset.value}"
508        else
509            $asm.puts "addiu #{destination.mipsOperand}, #{base.mipsOperand}, #{offset.value}"
510        end
511    end
512end
513
514#
515# Add PIC compatible header code to prologue/entry rutins.
516#
517
518def mipsAddPICCode(list)
519    myList = []
520    list.each {
521        | node |
522        myList << node
523        if node.is_a? Label
524            if /_prologue$/.match(node.name) || /^_llint_function_/.match(node.name)
525                # Functions called from trampoline/JIT codes.
526                myList << Instruction.new(node.codeOrigin, "pichdr", [])
527            elsif /_llint_op_catch/.match(node.name)
528                # Exception cactcher entry point function.
529                myList << Instruction.new(node.codeOrigin, "pichdrra", [])
530            end
531        end
532    }
533    myList
534end
535
536#
537# Actual lowering code follows.
538#
539
540class Sequence
541    def getModifiedListMIPS
542        result = @list
543
544        # Verify that we will only see instructions and labels.
545        result.each {
546            | node |
547            unless node.is_a? Instruction or
548                    node.is_a? Label or
549                    node.is_a? LocalLabel or
550                    node.is_a? Skip
551                raise "Unexpected #{node.inspect} at #{node.codeOrigin}"
552            end
553        }
554
555        result = mipsAddPICCode(result)
556        result = mipsLowerSimpleBranchOps(result)
557        result = riscLowerSimpleBranchOps(result)
558        result = riscLowerHardBranchOps(result)
559        result = riscLowerShiftOps(result)
560        result = mipsLowerMalformedAddresses(result) {
561            | node, address |
562            if address.is_a? Address
563                (-0xffff..0xffff).include? address.offset.value
564            else
565                false
566            end
567        }
568        result = riscLowerMalformedAddressesDouble(result)
569        result = riscLowerMisplacedImmediates(result, ["storeb", "storei", "storep"])
570        result = mipsLowerMisplacedImmediates(result)
571        result = riscLowerMalformedImmediates(result, -0xffff..0xffff)
572        result = mipsLowerMisplacedAddresses(result)
573        result = riscLowerMisplacedAddresses(result)
574        result = riscLowerRegisterReuse(result)
575        result = mipsLowerCompares(result)
576        result = assignRegistersToTemporaries(result, :gpr, MIPS_TEMP_GPRS)
577        result = assignRegistersToTemporaries(result, :fpr, MIPS_TEMP_FPRS)
578
579        return result
580    end
581end
582
583def mipsOperands(operands)
584    operands.map{|v| v.mipsOperand}.join(", ")
585end
586
587def mipsFlippedOperands(operands)
588    mipsOperands([operands[-1]] + operands[0..-2])
589end
590
591def getMIPSOpcode(opcode, suffix)
592
593end
594
595def emitMIPSCompact(opcode, opcodei, operands)
596    postfix = ""
597    if opcode == "sub"
598        if operands[0].is_a? Immediate
599            opcode = "add"
600            operands[0] = Immediate.new(operands[0].codeOrigin, -1 * operands[0].value)
601        elsif operands[1].is_a? Immediate
602            opcode = "add"
603            operands[1] = Immediate.new(operands[1].codeOrigin, -1 * operands[1].value)
604        end
605        postfix = "u"
606    elsif opcode == "add"
607        postfix = "u"
608    end
609    if operands.size == 3
610        if operands[0].is_a? Immediate
611            $asm.puts "#{opcode}i#{postfix} #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].value}"
612        elsif operands[1].is_a? Immediate
613            $asm.puts "#{opcode}i#{postfix} #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].value}"
614        else
615            $asm.puts "#{opcode}#{postfix} #{mipsFlippedOperands(operands)}"
616        end
617    else
618        raise unless operands.size == 2
619        raise unless operands[1].register?
620        if operands[0].is_a? Immediate
621            $asm.puts "#{opcode}i#{postfix} #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
622        else
623            $asm.puts "#{opcode}#{postfix} #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
624        end
625    end
626end
627
628def emitMIPSShiftCompact(opcode, operands)
629    if operands.size == 3
630        if (operands[1].is_a? Immediate)
631            $asm.puts "#{opcode} #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].value}"
632        else
633            $asm.puts "#{opcode}v #{mipsFlippedOperands(operands)}"
634        end
635    else
636        raise unless operands.size == 2
637        if operands[0].register?
638            $asm.puts "#{opcode}v #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
639        else
640            $asm.puts "#{opcode} #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].value}"
641        end
642    end
643end
644
645def emitMIPS(opcode, operands)
646    if operands.size == 3
647        $asm.puts "#{opcode} #{mipsFlippedOperands(operands)}"
648    else
649        raise unless operands.size == 2
650        $asm.puts "#{opcode} #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
651    end
652end
653
654def emitMIPSDoubleBranch(branchOpcode, neg, operands)
655    $asm.puts "c.#{branchOpcode}.d #{mipsOperands(operands[0..1])}"
656    if (!neg)
657        $asm.puts "bc1t #{operands[2].asmLabel}"
658    else
659        $asm.puts "bc1f #{operands[2].asmLabel}"
660    end
661end
662
663class Instruction
664    def lowerMIPS
665        $asm.comment codeOriginString
666        case opcode
667        when "addi", "addp", "addis"
668            if operands.size == 3 and operands[0].is_a? Immediate
669                raise unless operands[1].register?
670                raise unless operands[2].register?
671                if operands[0].value == 0 #and suffix.empty?
672                    unless operands[1] == operands[2]
673                        $asm.puts "move #{operands[2].mipsOperand}, #{operands[1].mipsOperand}"
674                    end
675                else
676                    $asm.puts "addiu #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
677                end
678            elsif operands.size == 3 and operands[0].register?
679                raise unless operands[1].register?
680                raise unless operands[2].register?
681                $asm.puts "addu #{mipsFlippedOperands(operands)}"
682            else
683                if operands[0].is_a? Immediate
684                    unless Immediate.new(nil, 0) == operands[0]
685                        $asm.puts "addiu #{operands[1].mipsOperand}, #{mipsFlippedOperands(operands)}"
686                    end
687                else
688                    $asm.puts "addu #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
689                end
690            end
691        when "andi", "andp"
692            emitMIPSCompact("and", "and", operands)
693        when "ori", "orp"
694            emitMIPSCompact("or", "orr", operands)
695        when "oris"
696            emitMIPSCompact("or", "orrs", operands)
697        when "xori", "xorp"
698            emitMIPSCompact("xor", "eor", operands)
699        when "lshifti", "lshiftp"
700            emitMIPSShiftCompact("sll", operands)
701        when "rshifti", "rshiftp"
702            emitMIPSShiftCompact("sra", operands)
703        when "urshifti", "urshiftp"
704            emitMIPSShiftCompact("srl", operands)
705        when "muli", "mulp"
706            emitMIPS("mul", operands)
707        when "subi", "subp", "subis"
708            emitMIPSCompact("sub", "subs", operands)
709        when "negi", "negp"
710            $asm.puts "negu #{operands[0].mipsOperand}, #{operands[0].mipsOperand}"
711        when "noti"
712            $asm.puts "nor #{operands[0].mipsOperand}, #{operands[0].mipsOperand}, $zero"
713        when "loadi", "loadis", "loadp"
714            $asm.puts "lw #{mipsFlippedOperands(operands)}"
715        when "storei", "storep"
716            $asm.puts "sw #{mipsOperands(operands)}"
717        when "loadb"
718            $asm.puts "lbu #{mipsFlippedOperands(operands)}"
719        when "loadbs"
720            $asm.puts "lb #{mipsFlippedOperands(operands)}"
721        when "storeb"
722            $asm.puts "sb #{mipsOperands(operands)}"
723        when "loadh"
724            $asm.puts "lhu #{mipsFlippedOperands(operands)}"
725        when "loadhs"
726            $asm.puts "lh #{mipsFlippedOperands(operands)}"
727        when "storeh"
728            $asm.puts "shv #{mipsOperands(operands)}"
729        when "loadd"
730            $asm.puts "ldc1 #{mipsFlippedOperands(operands)}"
731        when "stored"
732            $asm.puts "sdc1 #{mipsOperands(operands)}"
733        when "addd"
734            emitMIPS("add.d", operands)
735        when "divd"
736            emitMIPS("div.d", operands)
737        when "subd"
738            emitMIPS("sub.d", operands)
739        when "muld"
740            emitMIPS("mul.d", operands)
741        when "sqrtd"
742            $asm.puts "sqrt.d #{mipsFlippedOperands(operands)}"
743        when "ci2d"
744            raise "invalid ops of #{self.inspect} at #{codeOriginString}" unless operands[1].is_a? FPRegisterID and operands[0].register?
745            $asm.puts "mtc1 #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
746            $asm.puts "cvt.d.w #{operands[1].mipsOperand}, #{operands[1].mipsOperand}"
747        when "bdeq"
748            emitMIPSDoubleBranch("eq", false, operands)
749        when "bdneq"
750            emitMIPSDoubleBranch("ueq", true, operands)
751        when "bdgt"
752            emitMIPSDoubleBranch("ule", true, operands)
753        when "bdgteq"
754            emitMIPSDoubleBranch("ult", true, operands)
755        when "bdlt"
756            emitMIPSDoubleBranch("olt", false, operands)
757        when "bdlteq"
758            emitMIPSDoubleBranch("ole", false, operands)
759        when "bdequn"
760            emitMIPSDoubleBranch("ueq", false, operands)
761        when "bdnequn"
762            emitMIPSDoubleBranch("eq", true, operands)
763        when "bdgtun"
764            emitMIPSDoubleBranch("ole", true, operands)
765        when "bdgtequn"
766            emitMIPSDoubleBranch("olt", true, operands)
767        when "bdltun"
768            emitMIPSDoubleBranch("ult", false, operands)
769        when "bdltequn"
770            emitMIPSDoubleBranch("ule", false, operands)
771        when "btd2i"
772            # FIXME: may be a good idea to just get rid of this instruction, since the interpreter
773            # currently does not use it.
774            raise "MIPS does not support this opcode yet, #{codeOrigin}"
775        when "td2i"
776            $asm.puts "cvt.w.d #{MIPS_SCRATCH_FPR.mipsSingleLo}, #{operands[0].mipsOperand}"
777            $asm.puts "mfc1 #{operands[1].mipsOperand}, #{MIPS_SCRATCH_FPR.mipsSingleLo}"
778        when "bcd2i"
779            $asm.puts "cvt.w.d #{MIPS_SCRATCH_FPR.mipsSingleLo}, #{operands[0].mipsOperand}"
780            $asm.puts "mfc1 #{operands[1].mipsOperand}, #{MIPS_SCRATCH_FPR.mipsSingleLo}"
781            $asm.puts "cvt.d.w #{MIPS_SCRATCH_FPR.mipsOperand}, #{MIPS_SCRATCH_FPR.mipsSingleLo}"
782            emitMIPSDoubleBranch("eq", true, [MIPS_SCRATCH_FPR, operands[0], operands[2]])
783            $asm.puts "beq #{operands[1].mipsOperand}, $zero, #{operands[2].asmLabel}"
784        when "movdz"
785            # FIXME: either support this or remove it.
786            raise "MIPS does not support this opcode yet, #{codeOrigin}"
787        when "pop"
788            $asm.puts "lw #{operands[0].mipsOperand}, 0($sp)"
789            $asm.puts "addiu $sp, $sp, 4"
790        when "push"
791            $asm.puts "addiu $sp, $sp, -4"
792            $asm.puts "sw #{operands[0].mipsOperand}, 0($sp)"
793        when "move", "sxi2p", "zxi2p"
794            if operands[0].is_a? Immediate
795                mipsMoveImmediate(operands[0].value, operands[1])
796            else
797                $asm.puts "move #{mipsFlippedOperands(operands)}"
798            end
799        when "nop"
800            $asm.puts "nop"
801        when "bieq", "bpeq", "bbeq"
802            $asm.puts "beq #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
803        when "bineq", "bpneq", "bbneq"
804            $asm.puts "bne #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
805        when "bigt", "bpgt", "bbgt"
806            $asm.puts "bgt #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
807        when "bigteq", "bpgteq", "bbgteq"
808            $asm.puts "bge #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
809        when "bilt", "bplt", "bblt"
810            $asm.puts "blt #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
811        when "bilteq", "bplteq", "bblteq"
812            $asm.puts "ble #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}"
813        when "jmp"
814            if operands[0].label?
815                $asm.puts "j #{operands[0].asmLabel}"
816            else
817                $asm.puts "jr #{operands[0].mipsOperand}"
818            end
819        when "call"
820            if operands[0].label?
821                $asm.puts "jal #{operands[0].asmLabel}"
822            else
823                $asm.puts "jalr #{operands[0].mipsOperand}"
824            end
825        when "break"
826            $asm.puts "break"
827        when "ret"
828            $asm.puts "jr $ra"
829        when "cia", "cpa", "cba"
830            $asm.puts "sltu #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
831        when "ciaeq", "cpaeq", "cbaeq"
832            $asm.puts "sltu #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
833            $asm.puts "xori #{operands[2].mipsOperand}, 1"
834        when "cib", "cpb", "cbb"
835            $asm.puts "sltu #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
836        when "cibeq", "cpbeq", "cbbeq"
837            $asm.puts "sltu #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
838            $asm.puts "xori #{operands[2].mipsOperand}, 1"
839        when "cigt", "cpgt", "cbgt"
840            $asm.puts "slt #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
841        when "cigteq", "cpgteq", "cbgteq"
842            $asm.puts "slt #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
843            $asm.puts "xori #{operands[2].mipsOperand}, 1"
844        when "cilt", "cplt", "cblt"
845            $asm.puts "slt #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
846        when "cilteq", "cplteq", "cblteq"
847            $asm.puts "slt #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}"
848            $asm.puts "xori #{operands[2].mipsOperand}, 1"
849        when "peek"
850            $asm.puts "lw #{operands[1].mipsOperand}, #{operands[0].value * 4}($sp)"
851        when "poke"
852            $asm.puts "sw #{operands[1].mipsOperand}, #{operands[0].value * 4}($sp)"
853        when "fii2d"
854            $asm.puts "mtc1 #{operands[0].mipsOperand}, #{operands[2].mipsSingleLo}"
855            $asm.puts "mtc1 #{operands[1].mipsOperand}, #{operands[2].mipsSingleHi}"
856        when "fd2ii"
857            $asm.puts "mfc1 #{operands[1].mipsOperand}, #{operands[0].mipsSingleLo}"
858            $asm.puts "mfc1 #{operands[2].mipsOperand}, #{operands[0].mipsSingleHi}"
859        when /^bo/
860            $asm.puts "bgt #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}"
861        when /^bs/
862            $asm.puts "blt #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}"
863        when /^bz/
864            $asm.puts "beq #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}"
865        when /^bnz/
866            $asm.puts "bne #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}"
867        when "leai", "leap"
868            operands[0].mipsEmitLea(operands[1])
869        when "smulli"
870            raise "Wrong number of arguments to smull in #{self.inspect} at #{codeOriginString}" unless operands.length == 4
871            $asm.puts "mult #{operands[0].mipsOperand}, #{operands[1].mipsOperand}"
872            $asm.puts "mflo #{operands[2].mipsOperand}"
873            $asm.puts "mfhi #{operands[3].mipsOperand}"
874        when "movz"
875            $asm.puts "movz #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}"
876        when "movn"
877            $asm.puts "movn #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}"
878        when "slt", "sltb"
879            $asm.puts "slt #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}"
880        when "sltu", "sltub"
881            $asm.puts "sltu #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}"
882        when "pichdr"
883            $asm.putStr("OFFLINE_ASM_CPLOAD($25)")
884            $asm.puts "move $s4, $gp"
885        when "pichdrra"
886            $asm.putStr("OFFLINE_ASM_CPLOAD($31)")
887            $asm.puts "move $s4, $gp"
888        else
889            raise "Unhandled opcode #{opcode} at #{codeOriginString}"
890        end
891    end
892end
893