]>
Commit | Line | Data |
---|---|---|
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 | ||
25 | require 'risc' | |
26 | ||
27 | class Assembler | |
28 | def putStr(str) | |
29 | @outp.puts str | |
30 | end | |
31 | end | |
32 | ||
33 | class 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 | |
44 | end | |
45 | ||
46 | class 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 | |
58 | end | |
59 | ||
60 | MIPS_TEMP_GPRS = [SpecialRegister.new("$t5"), SpecialRegister.new("$t6"), SpecialRegister.new("$t7"), | |
61 | SpecialRegister.new("$t8")] | |
62 | MIPS_ZERO_REG = SpecialRegister.new("$zero") | |
63 | MIPS_GP_REG = SpecialRegister.new("$gp") | |
64 | MIPS_GPSAVE_REG = SpecialRegister.new("$s4") | |
65 | MIPS_JUMP_REG = SpecialRegister.new("$ra") | |
66 | MIPS_CALL_REG = SpecialRegister.new("$t9") | |
67 | MIPS_TEMP_FPRS = [SpecialRegister.new("$f16")] | |
68 | MIPS_SCRATCH_FPR = SpecialRegister.new("$f18") | |
69 | ||
70 | def 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 | |
76 | end | |
77 | ||
78 | class 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 | |
113 | end | |
114 | ||
115 | class 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 | |
138 | end | |
139 | ||
140 | class Immediate | |
141 | def mipsOperand | |
142 | raise "Invalid immediate #{value} at #{codeOriginString}" if value < -0x7fff or value > 0x7fff | |
143 | "#{value}" | |
144 | end | |
145 | end | |
146 | ||
147 | class 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 | |
152 | end | |
153 | ||
154 | class AbsoluteAddress | |
155 | def mipsOperand | |
156 | raise "Unconverted absolute address at #{codeOriginString}" | |
157 | end | |
158 | end | |
159 | ||
160 | # | |
161 | # Lower 'and' masked branches | |
162 | # | |
163 | ||
164 | def 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 | |
182 | end | |
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 | ||
195 | def 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 | |
303 | end | |
304 | ||
305 | # | |
306 | # Specialization of lowering of malformed BaseIndex addresses. | |
307 | # | |
308 | ||
309 | class Node | |
310 | def mipsLowerMalformedAddressesRecurse(list, topLevelNode, &block) | |
311 | mapChildren { | |
312 | | subNode | | |
313 | subNode.mipsLowerMalformedAddressesRecurse(list, topLevelNode, &block) | |
314 | } | |
315 | end | |
316 | end | |
317 | ||
318 | class Address | |
319 | def mipsLowerMalformedAddressesRecurse(list, node, &block) | |
320 | riscLowerMalformedAddressesRecurse(list, node, &block) | |
321 | end | |
322 | end | |
323 | ||
324 | class 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 | |
337 | end | |
338 | ||
339 | class AbsoluteAddress | |
340 | def mipsLowerMalformedAddressesRecurse(list, node, &block) | |
341 | riscLowerMalformedAddressesRecurse(list, node, &block) | |
342 | end | |
343 | end | |
344 | ||
345 | def mipsLowerMalformedAddresses(list, &block) | |
346 | newList = [] | |
347 | list.each { | |
348 | | node | | |
349 | newList << node.mipsLowerMalformedAddressesRecurse(newList, node, &block) | |
350 | } | |
351 | newList | |
352 | end | |
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 | ||
365 | def 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 | |
389 | end | |
390 | ||
391 | # | |
392 | # Specialization of lowering of misplaced addresses. | |
393 | # | |
394 | ||
395 | def 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 | |
453 | end | |
454 | ||
455 | # | |
456 | # Lowering compares and tests. | |
457 | # | |
458 | ||
459 | def 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]) | |
466 | end | |
467 | ||
468 | def 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 | |
498 | end | |
499 | ||
500 | # | |
501 | # Lea support. | |
502 | # | |
503 | ||
504 | class 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 | |
512 | end | |
513 | ||
514 | # | |
515 | # Add PIC compatible header code to prologue/entry rutins. | |
516 | # | |
517 | ||
518 | def 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 | |
534 | end | |
535 | ||
536 | # | |
537 | # Actual lowering code follows. | |
538 | # | |
539 | ||
540 | class 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 | |
581 | end | |
582 | ||
583 | def mipsOperands(operands) | |
584 | operands.map{|v| v.mipsOperand}.join(", ") | |
585 | end | |
586 | ||
587 | def mipsFlippedOperands(operands) | |
588 | mipsOperands([operands[-1]] + operands[0..-2]) | |
589 | end | |
590 | ||
591 | def getMIPSOpcode(opcode, suffix) | |
592 | ||
593 | end | |
594 | ||
595 | def 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 | |
626 | end | |
627 | ||
628 | def 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 | |
643 | end | |
644 | ||
645 | def 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 | |
652 | end | |
653 | ||
654 | def 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 | |
661 | end | |
662 | ||
663 | class 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 | |
892 | end |