]>
Commit | Line | Data |
---|---|---|
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 "a2" | |
86 | "$a2" | |
87 | when "a3" | |
88 | "$a3" | |
89 | when "r0", "t0" | |
90 | "$v0" | |
91 | when "r1", "t1" | |
92 | "$v1" | |
93 | when "t2" | |
94 | "$t2" | |
95 | when "t3" | |
96 | "$s3" | |
97 | when "t4" # PC reg in llint | |
98 | "$s2" | |
99 | when "t5" | |
100 | "$t5" | |
101 | when "t6" | |
102 | "$t6" | |
103 | when "t7" | |
104 | "$t7" | |
105 | when "t8" | |
106 | "$t8" | |
107 | when "cfr" | |
108 | "$fp" | |
109 | when "lr" | |
110 | "$ra" | |
111 | when "sp" | |
112 | "$sp" | |
113 | else | |
114 | raise "Bad register #{name} for MIPS at #{codeOriginString}" | |
115 | end | |
116 | end | |
117 | end | |
118 | ||
119 | class 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 | |
142 | end | |
143 | ||
144 | class Immediate | |
145 | def mipsOperand | |
146 | raise "Invalid immediate #{value} at #{codeOriginString}" if value < -0x7fff or value > 0xffff | |
147 | "#{value}" | |
148 | end | |
149 | end | |
150 | ||
151 | class 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 | |
156 | end | |
157 | ||
158 | class AbsoluteAddress | |
159 | def mipsOperand | |
160 | raise "Unconverted absolute address at #{codeOriginString}" | |
161 | end | |
162 | end | |
163 | ||
164 | # | |
165 | # Lower 'and' masked branches | |
166 | # | |
167 | ||
168 | def 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 | |
186 | end | |
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 | ||
199 | def 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 | |
307 | end | |
308 | ||
309 | # | |
310 | # Specialization of lowering of malformed BaseIndex addresses. | |
311 | # | |
312 | ||
313 | class Node | |
314 | def mipsLowerMalformedAddressesRecurse(list) | |
315 | mapChildren { | |
316 | | subNode | | |
317 | subNode.mipsLowerMalformedAddressesRecurse(list) | |
318 | } | |
319 | end | |
320 | ||
321 | def mipsLowerShiftedAddressesRecurse(list, isFirst, tmp) | |
322 | mapChildren { | |
323 | | subNode | | |
324 | subNode.mipsLowerShiftedAddressesRecurse(list, isFirst, tmp) | |
325 | } | |
326 | end | |
327 | end | |
328 | ||
329 | class BaseIndex | |
330 | def mipsLowerMalformedAddressesRecurse(list) | |
331 | tmp = Tmp.new(codeOrigin, :gpr) | |
332 | if scaleShift == 0 | |
333 | list << Instruction.new(codeOrigin, "addp", [base, index, tmp]) | |
334 | Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, offset.value)); | |
335 | end | |
336 | end | |
337 | ||
338 | def mipsLowerShiftedAddressesRecurse(list, isFirst, tmp) | |
339 | if isFirst | |
340 | list << Instruction.new(codeOrigin, "lshifti", [index, Immediate.new(codeOrigin, scaleShift), tmp]); | |
341 | list << Instruction.new(codeOrigin, "addp", [base, tmp]) | |
342 | end | |
343 | Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, offset.value)); | |
344 | end | |
345 | end | |
346 | ||
347 | # | |
348 | # Lowering of BaseIndex addresses with optimization for MIPS. | |
349 | # | |
350 | # offline asm instruction pair: | |
351 | # loadi 4[cfr, t0, 8], t2 | |
352 | # loadi 0[cfr, t0, 8], t0 | |
353 | # | |
354 | # lowered instructions: | |
355 | # lshifti t0, 3, tmp | |
356 | # addp cfr, tmp | |
357 | # loadi 4[tmp], t2 | |
358 | # loadi 0[tmp], t0 | |
359 | # | |
360 | ||
361 | def 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 | |
369 | end | |
370 | ||
371 | def mipsScaleOfBaseIndexMatches(baseIndex0, baseIndex1) | |
372 | baseIndex0.base == baseIndex1.base and | |
373 | baseIndex0.index == baseIndex1.index and | |
374 | baseIndex0.scale == baseIndex1.scale | |
375 | end | |
376 | ||
377 | def mipsLowerBaseIndexAddresses(list) | |
378 | newList = [ list[0] ] | |
379 | tmp = nil | |
380 | list.each_cons(2) { | |
381 | | nodes | | |
382 | if nodes[1].is_a? Instruction | |
383 | ind = mipsHasShiftedBaseIndexAddress(nodes[1]) | |
384 | if ind != -1 | |
385 | if nodes[0].is_a? Instruction and | |
386 | nodes[0].opcode == nodes[1].opcode and | |
387 | ind == mipsHasShiftedBaseIndexAddress(nodes[0]) and | |
388 | mipsScaleOfBaseIndexMatches(nodes[0].operands[ind], nodes[1].operands[ind]) | |
389 | ||
390 | newList << nodes[1].mipsLowerShiftedAddressesRecurse(newList, false, tmp) | |
391 | else | |
392 | tmp = Tmp.new(codeOrigin, :gpr) | |
393 | newList << nodes[1].mipsLowerShiftedAddressesRecurse(newList, true, tmp) | |
394 | end | |
395 | else | |
396 | newList << nodes[1].mipsLowerMalformedAddressesRecurse(newList) | |
397 | end | |
398 | else | |
399 | newList << nodes[1] | |
400 | end | |
401 | } | |
402 | newList | |
403 | end | |
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 | ||
416 | def mipsLowerMisplacedImmediates(list) | |
417 | newList = [] | |
418 | list.each { | |
419 | | node | | |
420 | if node.is_a? Instruction | |
421 | case node.opcode | |
422 | when "slt", "sltu", "sltb", "sltub" | |
423 | if node.operands[1].is_a? Immediate | |
424 | tmp = Tmp.new(node.codeOrigin, :gpr) | |
425 | newList << Instruction.new(node.codeOrigin, "move", [node.operands[1], tmp], node.annotation) | |
426 | newList << Instruction.new(node.codeOrigin, node.opcode, | |
427 | [node.operands[0], tmp, node.operands[2]], | |
428 | node.annotation) | |
429 | else | |
430 | newList << node | |
431 | end | |
432 | when /^(addi|subi)/ | |
433 | newList << node.riscLowerMalformedImmediatesRecurse(newList, -0x7fff..0x7fff) | |
434 | else | |
435 | newList << node | |
436 | end | |
437 | else | |
438 | newList << node | |
439 | end | |
440 | } | |
441 | newList | |
442 | end | |
443 | ||
444 | # | |
445 | # Specialization of lowering of misplaced addresses. | |
446 | # | |
447 | ||
448 | def 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 | |
506 | end | |
507 | ||
508 | # | |
509 | # Lowering compares and tests. | |
510 | # | |
511 | ||
512 | def 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]) | |
519 | end | |
520 | ||
521 | def 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 | |
551 | end | |
552 | ||
553 | # | |
554 | # Lea support. | |
555 | # | |
556 | ||
557 | class 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 | |
565 | end | |
566 | ||
567 | # | |
568 | # Add PIC compatible header code to prologue/entry rutins. | |
569 | # | |
570 | ||
571 | def 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 | |
587 | end | |
588 | ||
589 | # | |
590 | # Actual lowering code follows. | |
591 | # | |
592 | ||
593 | class Sequence | |
594 | def getModifiedListMIPS | |
595 | result = @list | |
596 | ||
597 | # Verify that we will only see instructions and labels. | |
598 | result.each { | |
599 | | node | | |
600 | unless node.is_a? Instruction or | |
601 | node.is_a? Label or | |
602 | node.is_a? LocalLabel or | |
603 | node.is_a? Skip | |
604 | raise "Unexpected #{node.inspect} at #{node.codeOrigin}" | |
605 | end | |
606 | } | |
607 | ||
608 | result = mipsAddPICCode(result) | |
609 | result = mipsLowerSimpleBranchOps(result) | |
610 | result = riscLowerSimpleBranchOps(result) | |
611 | result = riscLowerHardBranchOps(result) | |
612 | result = riscLowerShiftOps(result) | |
613 | result = mipsLowerBaseIndexAddresses(result) | |
614 | result = riscLowerMalformedAddresses(result) { | |
615 | | node, address | | |
616 | if address.is_a? Address | |
617 | (-0xffff..0xffff).include? address.offset.value | |
618 | else | |
619 | false | |
620 | end | |
621 | } | |
622 | result = riscLowerMalformedAddressesDouble(result) | |
623 | result = riscLowerMisplacedImmediates(result, ["storeb", "storei", "storep"]) | |
624 | result = mipsLowerMisplacedImmediates(result) | |
625 | result = riscLowerMalformedImmediates(result, -0xffff..0xffff) | |
626 | result = mipsLowerMisplacedAddresses(result) | |
627 | result = riscLowerMisplacedAddresses(result) | |
628 | result = riscLowerRegisterReuse(result) | |
629 | result = mipsLowerCompares(result) | |
630 | result = assignRegistersToTemporaries(result, :gpr, MIPS_TEMP_GPRS) | |
631 | result = assignRegistersToTemporaries(result, :fpr, MIPS_TEMP_FPRS) | |
632 | ||
633 | return result | |
634 | end | |
635 | end | |
636 | ||
637 | def mipsOperands(operands) | |
638 | operands.map{|v| v.mipsOperand}.join(", ") | |
639 | end | |
640 | ||
641 | def mipsFlippedOperands(operands) | |
642 | mipsOperands([operands[-1]] + operands[0..-2]) | |
643 | end | |
644 | ||
645 | def getMIPSOpcode(opcode, suffix) | |
646 | ||
647 | end | |
648 | ||
649 | def 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 | |
680 | end | |
681 | ||
682 | def 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 | |
697 | end | |
698 | ||
699 | def 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 | |
706 | end | |
707 | ||
708 | def 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 | |
715 | end | |
716 | ||
717 | class Instruction | |
718 | def lowerMIPS | |
719 | $asm.comment codeOriginString | |
720 | case opcode | |
721 | when "addi", "addp", "addis" | |
722 | if operands.size == 3 and operands[0].is_a? Immediate | |
723 | raise unless operands[1].register? | |
724 | raise unless operands[2].register? | |
725 | if operands[0].value == 0 #and suffix.empty? | |
726 | unless operands[1] == operands[2] | |
727 | $asm.puts "move #{operands[2].mipsOperand}, #{operands[1].mipsOperand}" | |
728 | end | |
729 | else | |
730 | $asm.puts "addiu #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}" | |
731 | end | |
732 | elsif operands.size == 3 and operands[0].register? | |
733 | raise unless operands[1].register? | |
734 | raise unless operands[2].register? | |
735 | $asm.puts "addu #{mipsFlippedOperands(operands)}" | |
736 | else | |
737 | if operands[0].is_a? Immediate | |
738 | unless Immediate.new(nil, 0) == operands[0] | |
739 | $asm.puts "addiu #{operands[1].mipsOperand}, #{mipsFlippedOperands(operands)}" | |
740 | end | |
741 | else | |
742 | $asm.puts "addu #{operands[1].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}" | |
743 | end | |
744 | end | |
745 | when "andi", "andp" | |
746 | emitMIPSCompact("and", "and", operands) | |
747 | when "ori", "orp" | |
748 | emitMIPSCompact("or", "orr", operands) | |
749 | when "oris" | |
750 | emitMIPSCompact("or", "orrs", operands) | |
751 | when "xori", "xorp" | |
752 | emitMIPSCompact("xor", "eor", operands) | |
753 | when "lshifti", "lshiftp" | |
754 | emitMIPSShiftCompact("sll", operands) | |
755 | when "rshifti", "rshiftp" | |
756 | emitMIPSShiftCompact("sra", operands) | |
757 | when "urshifti", "urshiftp" | |
758 | emitMIPSShiftCompact("srl", operands) | |
759 | when "muli", "mulp" | |
760 | emitMIPS("mul", operands) | |
761 | when "subi", "subp", "subis" | |
762 | emitMIPSCompact("sub", "subs", operands) | |
763 | when "negi", "negp" | |
764 | $asm.puts "negu #{operands[0].mipsOperand}, #{operands[0].mipsOperand}" | |
765 | when "noti" | |
766 | $asm.puts "nor #{operands[0].mipsOperand}, #{operands[0].mipsOperand}, $zero" | |
767 | when "loadi", "loadis", "loadp" | |
768 | $asm.puts "lw #{mipsFlippedOperands(operands)}" | |
769 | when "storei", "storep" | |
770 | $asm.puts "sw #{mipsOperands(operands)}" | |
771 | when "loadb" | |
772 | $asm.puts "lbu #{mipsFlippedOperands(operands)}" | |
773 | when "loadbs" | |
774 | $asm.puts "lb #{mipsFlippedOperands(operands)}" | |
775 | when "storeb" | |
776 | $asm.puts "sb #{mipsOperands(operands)}" | |
777 | when "loadh" | |
778 | $asm.puts "lhu #{mipsFlippedOperands(operands)}" | |
779 | when "loadhs" | |
780 | $asm.puts "lh #{mipsFlippedOperands(operands)}" | |
781 | when "storeh" | |
782 | $asm.puts "shv #{mipsOperands(operands)}" | |
783 | when "loadd" | |
784 | $asm.puts "ldc1 #{mipsFlippedOperands(operands)}" | |
785 | when "stored" | |
786 | $asm.puts "sdc1 #{mipsOperands(operands)}" | |
787 | when "addd" | |
788 | emitMIPS("add.d", operands) | |
789 | when "divd" | |
790 | emitMIPS("div.d", operands) | |
791 | when "subd" | |
792 | emitMIPS("sub.d", operands) | |
793 | when "muld" | |
794 | emitMIPS("mul.d", operands) | |
795 | when "sqrtd" | |
796 | $asm.puts "sqrt.d #{mipsFlippedOperands(operands)}" | |
797 | when "ci2d" | |
798 | raise "invalid ops of #{self.inspect} at #{codeOriginString}" unless operands[1].is_a? FPRegisterID and operands[0].register? | |
799 | $asm.puts "mtc1 #{operands[0].mipsOperand}, #{operands[1].mipsOperand}" | |
800 | $asm.puts "cvt.d.w #{operands[1].mipsOperand}, #{operands[1].mipsOperand}" | |
801 | when "bdeq" | |
802 | emitMIPSDoubleBranch("eq", false, operands) | |
803 | when "bdneq" | |
804 | emitMIPSDoubleBranch("ueq", true, operands) | |
805 | when "bdgt" | |
806 | emitMIPSDoubleBranch("ule", true, operands) | |
807 | when "bdgteq" | |
808 | emitMIPSDoubleBranch("ult", true, operands) | |
809 | when "bdlt" | |
810 | emitMIPSDoubleBranch("olt", false, operands) | |
811 | when "bdlteq" | |
812 | emitMIPSDoubleBranch("ole", false, operands) | |
813 | when "bdequn" | |
814 | emitMIPSDoubleBranch("ueq", false, operands) | |
815 | when "bdnequn" | |
816 | emitMIPSDoubleBranch("eq", true, operands) | |
817 | when "bdgtun" | |
818 | emitMIPSDoubleBranch("ole", true, operands) | |
819 | when "bdgtequn" | |
820 | emitMIPSDoubleBranch("olt", true, operands) | |
821 | when "bdltun" | |
822 | emitMIPSDoubleBranch("ult", false, operands) | |
823 | when "bdltequn" | |
824 | emitMIPSDoubleBranch("ule", false, operands) | |
825 | when "btd2i" | |
826 | # FIXME: may be a good idea to just get rid of this instruction, since the interpreter | |
827 | # currently does not use it. | |
828 | raise "MIPS does not support this opcode yet, #{codeOrigin}" | |
829 | when "td2i" | |
830 | $asm.puts "cvt.w.d #{MIPS_SCRATCH_FPR.mipsSingleLo}, #{operands[0].mipsOperand}" | |
831 | $asm.puts "mfc1 #{operands[1].mipsOperand}, #{MIPS_SCRATCH_FPR.mipsSingleLo}" | |
832 | when "bcd2i" | |
833 | $asm.puts "cvt.w.d #{MIPS_SCRATCH_FPR.mipsSingleLo}, #{operands[0].mipsOperand}" | |
834 | $asm.puts "mfc1 #{operands[1].mipsOperand}, #{MIPS_SCRATCH_FPR.mipsSingleLo}" | |
835 | $asm.puts "cvt.d.w #{MIPS_SCRATCH_FPR.mipsOperand}, #{MIPS_SCRATCH_FPR.mipsSingleLo}" | |
836 | emitMIPSDoubleBranch("eq", true, [MIPS_SCRATCH_FPR, operands[0], operands[2]]) | |
837 | $asm.puts "beq #{operands[1].mipsOperand}, $zero, #{operands[2].asmLabel}" | |
838 | when "movdz" | |
839 | # FIXME: either support this or remove it. | |
840 | raise "MIPS does not support this opcode yet, #{codeOrigin}" | |
841 | when "pop" | |
842 | operands.each { | |
843 | | op | | |
844 | $asm.puts "lw #{op.mipsOperand}, 0($sp)" | |
845 | $asm.puts "addiu $sp, $sp, 4" | |
846 | } | |
847 | when "push" | |
848 | operands.each { | |
849 | | op | | |
850 | $asm.puts "addiu $sp, $sp, -4" | |
851 | $asm.puts "sw #{op.mipsOperand}, 0($sp)" | |
852 | } | |
853 | when "popCalleeSaves" | |
854 | $asm.puts "lw $16, 0($sp)" | |
855 | $asm.puts "lw $17, 4($sp)" | |
856 | $asm.puts "lw $18, 8($sp)" | |
857 | $asm.puts "lw $19, 12($sp)" | |
858 | $asm.puts "lw $20, 16($sp)" | |
859 | $asm.puts "addiu $sp, $sp, 20" | |
860 | when "pushCalleeSaves" | |
861 | $asm.puts "addiu $sp, $sp, -20" | |
862 | $asm.puts "sw $20, 16($sp)" | |
863 | $asm.puts "sw $19, 12($sp)" | |
864 | $asm.puts "sw $18, 8($sp)" | |
865 | $asm.puts "sw $17, 4($sp)" | |
866 | $asm.puts "sw $16, 0($sp)" | |
867 | when "move", "sxi2p", "zxi2p" | |
868 | if operands[0].is_a? Immediate | |
869 | mipsMoveImmediate(operands[0].value, operands[1]) | |
870 | else | |
871 | $asm.puts "move #{mipsFlippedOperands(operands)}" | |
872 | end | |
873 | when "nop" | |
874 | $asm.puts "nop" | |
875 | when "bieq", "bpeq", "bbeq" | |
876 | $asm.puts "beq #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}" | |
877 | when "bineq", "bpneq", "bbneq" | |
878 | $asm.puts "bne #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}" | |
879 | when "bigt", "bpgt", "bbgt" | |
880 | $asm.puts "bgt #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}" | |
881 | when "bigteq", "bpgteq", "bbgteq" | |
882 | $asm.puts "bge #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}" | |
883 | when "bilt", "bplt", "bblt" | |
884 | $asm.puts "blt #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}" | |
885 | when "bilteq", "bplteq", "bblteq" | |
886 | $asm.puts "ble #{mipsOperands(operands[0..1])}, #{operands[2].asmLabel}" | |
887 | when "jmp" | |
888 | if operands[0].label? | |
889 | $asm.puts "j #{operands[0].asmLabel}" | |
890 | else | |
891 | $asm.puts "jr #{operands[0].mipsOperand}" | |
892 | end | |
893 | when "call" | |
894 | if operands[0].label? | |
895 | $asm.puts "jal #{operands[0].asmLabel}" | |
896 | else | |
897 | $asm.puts "jalr #{operands[0].mipsOperand}" | |
898 | end | |
899 | when "break" | |
900 | $asm.puts "break" | |
901 | when "ret" | |
902 | $asm.puts "jr $ra" | |
903 | when "cia", "cpa", "cba" | |
904 | $asm.puts "sltu #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}" | |
905 | when "ciaeq", "cpaeq", "cbaeq" | |
906 | $asm.puts "sltu #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}" | |
907 | $asm.puts "xori #{operands[2].mipsOperand}, 1" | |
908 | when "cib", "cpb", "cbb" | |
909 | $asm.puts "sltu #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}" | |
910 | when "cibeq", "cpbeq", "cbbeq" | |
911 | $asm.puts "sltu #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}" | |
912 | $asm.puts "xori #{operands[2].mipsOperand}, 1" | |
913 | when "cigt", "cpgt", "cbgt" | |
914 | $asm.puts "slt #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}" | |
915 | when "cigteq", "cpgteq", "cbgteq" | |
916 | $asm.puts "slt #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}" | |
917 | $asm.puts "xori #{operands[2].mipsOperand}, 1" | |
918 | when "cilt", "cplt", "cblt" | |
919 | $asm.puts "slt #{operands[2].mipsOperand}, #{operands[0].mipsOperand}, #{operands[1].mipsOperand}" | |
920 | when "cilteq", "cplteq", "cblteq" | |
921 | $asm.puts "slt #{operands[2].mipsOperand}, #{operands[1].mipsOperand}, #{operands[0].mipsOperand}" | |
922 | $asm.puts "xori #{operands[2].mipsOperand}, 1" | |
923 | when "peek" | |
924 | $asm.puts "lw #{operands[1].mipsOperand}, #{operands[0].value * 4}($sp)" | |
925 | when "poke" | |
926 | $asm.puts "sw #{operands[1].mipsOperand}, #{operands[0].value * 4}($sp)" | |
927 | when "fii2d" | |
928 | $asm.puts "mtc1 #{operands[0].mipsOperand}, #{operands[2].mipsSingleLo}" | |
929 | $asm.puts "mtc1 #{operands[1].mipsOperand}, #{operands[2].mipsSingleHi}" | |
930 | when "fd2ii" | |
931 | $asm.puts "mfc1 #{operands[1].mipsOperand}, #{operands[0].mipsSingleLo}" | |
932 | $asm.puts "mfc1 #{operands[2].mipsOperand}, #{operands[0].mipsSingleHi}" | |
933 | when /^bo/ | |
934 | $asm.puts "bgt #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}" | |
935 | when /^bs/ | |
936 | $asm.puts "blt #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}" | |
937 | when /^bz/ | |
938 | $asm.puts "beq #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}" | |
939 | when /^bnz/ | |
940 | $asm.puts "bne #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].asmLabel}" | |
941 | when "leai", "leap" | |
942 | operands[0].mipsEmitLea(operands[1]) | |
943 | when "smulli" | |
944 | raise "Wrong number of arguments to smull in #{self.inspect} at #{codeOriginString}" unless operands.length == 4 | |
945 | $asm.puts "mult #{operands[0].mipsOperand}, #{operands[1].mipsOperand}" | |
946 | $asm.puts "mflo #{operands[2].mipsOperand}" | |
947 | $asm.puts "mfhi #{operands[3].mipsOperand}" | |
948 | when "movz" | |
949 | $asm.puts "movz #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}" | |
950 | when "movn" | |
951 | $asm.puts "movn #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}" | |
952 | when "slt", "sltb" | |
953 | $asm.puts "slt #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}" | |
954 | when "sltu", "sltub" | |
955 | $asm.puts "sltu #{operands[0].mipsOperand}, #{operands[1].mipsOperand}, #{operands[2].mipsOperand}" | |
956 | when "pichdr" | |
957 | $asm.putStr("OFFLINE_ASM_CPLOAD($25)") | |
958 | $asm.puts "move $s4, $gp" | |
959 | when "pichdrra" | |
960 | $asm.putStr("OFFLINE_ASM_CPLOAD($31)") | |
961 | $asm.puts "move $s4, $gp" | |
962 | when "memfence" | |
963 | $asm.puts "sync" | |
964 | else | |
965 | lowerDefault | |
966 | end | |
967 | end | |
968 | end |