]> git.saurik.com Git - apple/javascriptcore.git/blame - offlineasm/mips.rb
JavaScriptCore-7600.1.4.9.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"
81345200
A
85 when "a2"
86 "$a2"
87 when "a3"
88 "$a3"
93a37866
A
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"
81345200 108 "$fp"
93a37866
A
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
81345200 146 raise "Invalid immediate #{value} at #{codeOriginString}" if value < -0x7fff or value > 0xffff
93a37866
A
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
81345200 314 def mipsLowerMalformedAddressesRecurse(list)
93a37866
A
315 mapChildren {
316 | subNode |
81345200 317 subNode.mipsLowerMalformedAddressesRecurse(list)
93a37866
A
318 }
319 end
93a37866 320
81345200
A
321 def mipsLowerShiftedAddressesRecurse(list, isFirst, tmp)
322 mapChildren {
323 | subNode |
324 subNode.mipsLowerShiftedAddressesRecurse(list, isFirst, tmp)
325 }
93a37866
A
326 end
327end
328
329class BaseIndex
81345200
A
330 def mipsLowerMalformedAddressesRecurse(list)
331 tmp = Tmp.new(codeOrigin, :gpr)
93a37866 332 if scaleShift == 0
81345200
A
333 list << Instruction.new(codeOrigin, "addp", [base, index, tmp])
334 Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, offset.value));
93a37866
A
335 end
336 end
93a37866 337
81345200
A
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));
93a37866
A
344 end
345end
346
81345200
A
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
93a37866
A
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
81345200
A
432 when /^(addi|subi)/
433 newList << node.riscLowerMalformedImmediatesRecurse(newList, -0x7fff..0x7fff)
93a37866
A
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)
81345200
A
613 result = mipsLowerBaseIndexAddresses(result)
614 result = riscLowerMalformedAddresses(result) {
93a37866
A
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"
81345200
A
842 operands.each {
843 | op |
844 $asm.puts "lw #{op.mipsOperand}, 0($sp)"
845 $asm.puts "addiu $sp, $sp, 4"
846 }
93a37866 847 when "push"
81345200
A
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)"
93a37866
A
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"
81345200
A
962 when "memfence"
963 $asm.puts "sync"
93a37866 964 else
81345200 965 lowerDefault
93a37866
A
966 end
967 end
968end