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