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