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