]> git.saurik.com Git - apple/javascriptcore.git/blame - offlineasm/mips.rb
JavaScriptCore-1218.tar.gz
[apple/javascriptcore.git] / offlineasm / mips.rb
CommitLineData
93a37866
A
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