Home | History | Annotate | Download | only in mips
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "assembler_mips.h"
     18 
     19 #include "base/casts.h"
     20 #include "entrypoints/quick/quick_entrypoints.h"
     21 #include "memory_region.h"
     22 #include "thread.h"
     23 
     24 namespace art {
     25 namespace mips {
     26 
     27 std::ostream& operator<<(std::ostream& os, const DRegister& rhs) {
     28   if (rhs >= D0 && rhs < kNumberOfDRegisters) {
     29     os << "d" << static_cast<int>(rhs);
     30   } else {
     31     os << "DRegister[" << static_cast<int>(rhs) << "]";
     32   }
     33   return os;
     34 }
     35 
     36 void MipsAssembler::Emit(int32_t value) {
     37   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
     38   buffer_.Emit<int32_t>(value);
     39 }
     40 
     41 void MipsAssembler::EmitR(int opcode, Register rs, Register rt, Register rd, int shamt, int funct) {
     42   CHECK_NE(rs, kNoRegister);
     43   CHECK_NE(rt, kNoRegister);
     44   CHECK_NE(rd, kNoRegister);
     45   int32_t encoding = opcode << kOpcodeShift |
     46                      static_cast<int32_t>(rs) << kRsShift |
     47                      static_cast<int32_t>(rt) << kRtShift |
     48                      static_cast<int32_t>(rd) << kRdShift |
     49                      shamt << kShamtShift |
     50                      funct;
     51   Emit(encoding);
     52 }
     53 
     54 void MipsAssembler::EmitI(int opcode, Register rs, Register rt, uint16_t imm) {
     55   CHECK_NE(rs, kNoRegister);
     56   CHECK_NE(rt, kNoRegister);
     57   int32_t encoding = opcode << kOpcodeShift |
     58                      static_cast<int32_t>(rs) << kRsShift |
     59                      static_cast<int32_t>(rt) << kRtShift |
     60                      imm;
     61   Emit(encoding);
     62 }
     63 
     64 void MipsAssembler::EmitJ(int opcode, int address) {
     65   int32_t encoding = opcode << kOpcodeShift |
     66                      address;
     67   Emit(encoding);
     68 }
     69 
     70 void MipsAssembler::EmitFR(int opcode, int fmt, FRegister ft, FRegister fs, FRegister fd, int funct) {
     71   CHECK_NE(ft, kNoFRegister);
     72   CHECK_NE(fs, kNoFRegister);
     73   CHECK_NE(fd, kNoFRegister);
     74   int32_t encoding = opcode << kOpcodeShift |
     75                      fmt << kFmtShift |
     76                      static_cast<int32_t>(ft) << kFtShift |
     77                      static_cast<int32_t>(fs) << kFsShift |
     78                      static_cast<int32_t>(fd) << kFdShift |
     79                      funct;
     80   Emit(encoding);
     81 }
     82 
     83 void MipsAssembler::EmitFI(int opcode, int fmt, FRegister rt, uint16_t imm) {
     84   CHECK_NE(rt, kNoFRegister);
     85   int32_t encoding = opcode << kOpcodeShift |
     86                      fmt << kFmtShift |
     87                      static_cast<int32_t>(rt) << kRtShift |
     88                      imm;
     89   Emit(encoding);
     90 }
     91 
     92 void MipsAssembler::EmitBranch(Register rt, Register rs, Label* label, bool equal) {
     93   int offset;
     94   if (label->IsBound()) {
     95     offset = label->Position() - buffer_.Size();
     96   } else {
     97     // Use the offset field of the branch instruction for linking the sites.
     98     offset = label->position_;
     99     label->LinkTo(buffer_.Size());
    100   }
    101   if (equal) {
    102     Beq(rt, rs, (offset >> 2) & kBranchOffsetMask);
    103   } else {
    104     Bne(rt, rs, (offset >> 2) & kBranchOffsetMask);
    105   }
    106 }
    107 
    108 void MipsAssembler::EmitJump(Label* label, bool link) {
    109   int offset;
    110   if (label->IsBound()) {
    111     offset = label->Position() - buffer_.Size();
    112   } else {
    113     // Use the offset field of the jump instruction for linking the sites.
    114     offset = label->position_;
    115     label->LinkTo(buffer_.Size());
    116   }
    117   if (link) {
    118     Jal((offset >> 2) & kJumpOffsetMask);
    119   } else {
    120     J((offset >> 2) & kJumpOffsetMask);
    121   }
    122 }
    123 
    124 int32_t MipsAssembler::EncodeBranchOffset(int offset, int32_t inst, bool is_jump) {
    125   CHECK_ALIGNED(offset, 4);
    126   CHECK(IsInt(POPCOUNT(kBranchOffsetMask), offset)) << offset;
    127 
    128   // Properly preserve only the bits supported in the instruction.
    129   offset >>= 2;
    130   if (is_jump) {
    131     offset &= kJumpOffsetMask;
    132     return (inst & ~kJumpOffsetMask) | offset;
    133   } else {
    134     offset &= kBranchOffsetMask;
    135     return (inst & ~kBranchOffsetMask) | offset;
    136   }
    137 }
    138 
    139 int MipsAssembler::DecodeBranchOffset(int32_t inst, bool is_jump) {
    140   // Sign-extend, then left-shift by 2.
    141   if (is_jump) {
    142     return (((inst & kJumpOffsetMask) << 6) >> 4);
    143   } else {
    144     return (((inst & kBranchOffsetMask) << 16) >> 14);
    145   }
    146 }
    147 
    148 void MipsAssembler::Bind(Label* label, bool is_jump) {
    149   CHECK(!label->IsBound());
    150   int bound_pc = buffer_.Size();
    151   while (label->IsLinked()) {
    152     int32_t position = label->Position();
    153     int32_t next = buffer_.Load<int32_t>(position);
    154     int32_t offset = is_jump ? bound_pc - position : bound_pc - position - 4;
    155     int32_t encoded = MipsAssembler::EncodeBranchOffset(offset, next, is_jump);
    156     buffer_.Store<int32_t>(position, encoded);
    157     label->position_ = MipsAssembler::DecodeBranchOffset(next, is_jump);
    158   }
    159   label->BindTo(bound_pc);
    160 }
    161 
    162 void MipsAssembler::Add(Register rd, Register rs, Register rt) {
    163   EmitR(0, rs, rt, rd, 0, 0x20);
    164 }
    165 
    166 void MipsAssembler::Addu(Register rd, Register rs, Register rt) {
    167   EmitR(0, rs, rt, rd, 0, 0x21);
    168 }
    169 
    170 void MipsAssembler::Addi(Register rt, Register rs, uint16_t imm16) {
    171   EmitI(0x8, rs, rt, imm16);
    172 }
    173 
    174 void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) {
    175   EmitI(0x9, rs, rt, imm16);
    176 }
    177 
    178 void MipsAssembler::Sub(Register rd, Register rs, Register rt) {
    179   EmitR(0, rs, rt, rd, 0, 0x22);
    180 }
    181 
    182 void MipsAssembler::Subu(Register rd, Register rs, Register rt) {
    183   EmitR(0, rs, rt, rd, 0, 0x23);
    184 }
    185 
    186 void MipsAssembler::Mult(Register rs, Register rt) {
    187   EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x18);
    188 }
    189 
    190 void MipsAssembler::Multu(Register rs, Register rt) {
    191   EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19);
    192 }
    193 
    194 void MipsAssembler::Div(Register rs, Register rt) {
    195   EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a);
    196 }
    197 
    198 void MipsAssembler::Divu(Register rs, Register rt) {
    199   EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1b);
    200 }
    201 
    202 void MipsAssembler::And(Register rd, Register rs, Register rt) {
    203   EmitR(0, rs, rt, rd, 0, 0x24);
    204 }
    205 
    206 void MipsAssembler::Andi(Register rt, Register rs, uint16_t imm16) {
    207   EmitI(0xc, rs, rt, imm16);
    208 }
    209 
    210 void MipsAssembler::Or(Register rd, Register rs, Register rt) {
    211   EmitR(0, rs, rt, rd, 0, 0x25);
    212 }
    213 
    214 void MipsAssembler::Ori(Register rt, Register rs, uint16_t imm16) {
    215   EmitI(0xd, rs, rt, imm16);
    216 }
    217 
    218 void MipsAssembler::Xor(Register rd, Register rs, Register rt) {
    219   EmitR(0, rs, rt, rd, 0, 0x26);
    220 }
    221 
    222 void MipsAssembler::Xori(Register rt, Register rs, uint16_t imm16) {
    223   EmitI(0xe, rs, rt, imm16);
    224 }
    225 
    226 void MipsAssembler::Nor(Register rd, Register rs, Register rt) {
    227   EmitR(0, rs, rt, rd, 0, 0x27);
    228 }
    229 
    230 void MipsAssembler::Sll(Register rd, Register rs, int shamt) {
    231   EmitR(0, rs, static_cast<Register>(0), rd, shamt, 0x00);
    232 }
    233 
    234 void MipsAssembler::Srl(Register rd, Register rs, int shamt) {
    235   EmitR(0, rs, static_cast<Register>(0), rd, shamt, 0x02);
    236 }
    237 
    238 void MipsAssembler::Sra(Register rd, Register rs, int shamt) {
    239   EmitR(0, rs, static_cast<Register>(0), rd, shamt, 0x03);
    240 }
    241 
    242 void MipsAssembler::Sllv(Register rd, Register rs, Register rt) {
    243   EmitR(0, rs, rt, rd, 0, 0x04);
    244 }
    245 
    246 void MipsAssembler::Srlv(Register rd, Register rs, Register rt) {
    247   EmitR(0, rs, rt, rd, 0, 0x06);
    248 }
    249 
    250 void MipsAssembler::Srav(Register rd, Register rs, Register rt) {
    251   EmitR(0, rs, rt, rd, 0, 0x07);
    252 }
    253 
    254 void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
    255   EmitI(0x20, rs, rt, imm16);
    256 }
    257 
    258 void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) {
    259   EmitI(0x21, rs, rt, imm16);
    260 }
    261 
    262 void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) {
    263   EmitI(0x23, rs, rt, imm16);
    264 }
    265 
    266 void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) {
    267   EmitI(0x24, rs, rt, imm16);
    268 }
    269 
    270 void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) {
    271   EmitI(0x25, rs, rt, imm16);
    272 }
    273 
    274 void MipsAssembler::Lui(Register rt, uint16_t imm16) {
    275   EmitI(0xf, static_cast<Register>(0), rt, imm16);
    276 }
    277 
    278 void MipsAssembler::Mfhi(Register rd) {
    279   EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rd, 0, 0x10);
    280 }
    281 
    282 void MipsAssembler::Mflo(Register rd) {
    283   EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rd, 0, 0x12);
    284 }
    285 
    286 void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) {
    287   EmitI(0x28, rs, rt, imm16);
    288 }
    289 
    290 void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) {
    291   EmitI(0x29, rs, rt, imm16);
    292 }
    293 
    294 void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) {
    295   EmitI(0x2b, rs, rt, imm16);
    296 }
    297 
    298 void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
    299   EmitR(0, rs, rt, rd, 0, 0x2a);
    300 }
    301 
    302 void MipsAssembler::Sltu(Register rd, Register rs, Register rt) {
    303   EmitR(0, rs, rt, rd, 0, 0x2b);
    304 }
    305 
    306 void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) {
    307   EmitI(0xa, rs, rt, imm16);
    308 }
    309 
    310 void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) {
    311   EmitI(0xb, rs, rt, imm16);
    312 }
    313 
    314 void MipsAssembler::Beq(Register rt, Register rs, uint16_t imm16) {
    315   EmitI(0x4, rs, rt, imm16);
    316   Nop();
    317 }
    318 
    319 void MipsAssembler::Bne(Register rt, Register rs, uint16_t imm16) {
    320   EmitI(0x5, rs, rt, imm16);
    321   Nop();
    322 }
    323 
    324 void MipsAssembler::J(uint32_t address) {
    325   EmitJ(0x2, address);
    326   Nop();
    327 }
    328 
    329 void MipsAssembler::Jal(uint32_t address) {
    330   EmitJ(0x2, address);
    331   Nop();
    332 }
    333 
    334 void MipsAssembler::Jr(Register rs) {
    335   EmitR(0, rs, static_cast<Register>(0), static_cast<Register>(0), 0, 0x08);
    336   Nop();
    337 }
    338 
    339 void MipsAssembler::Jalr(Register rs) {
    340   EmitR(0, rs, static_cast<Register>(0), RA, 0, 0x09);
    341   Nop();
    342 }
    343 
    344 void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) {
    345   EmitFR(0x11, 0x10, ft, fs, fd, 0x0);
    346 }
    347 
    348 void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) {
    349   EmitFR(0x11, 0x10, ft, fs, fd, 0x1);
    350 }
    351 
    352 void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) {
    353   EmitFR(0x11, 0x10, ft, fs, fd, 0x2);
    354 }
    355 
    356 void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) {
    357   EmitFR(0x11, 0x10, ft, fs, fd, 0x3);
    358 }
    359 
    360 void MipsAssembler::AddD(DRegister fd, DRegister fs, DRegister ft) {
    361   EmitFR(0x11, 0x11, static_cast<FRegister>(ft), static_cast<FRegister>(fs),
    362          static_cast<FRegister>(fd), 0x0);
    363 }
    364 
    365 void MipsAssembler::SubD(DRegister fd, DRegister fs, DRegister ft) {
    366   EmitFR(0x11, 0x11, static_cast<FRegister>(ft), static_cast<FRegister>(fs),
    367          static_cast<FRegister>(fd), 0x1);
    368 }
    369 
    370 void MipsAssembler::MulD(DRegister fd, DRegister fs, DRegister ft) {
    371   EmitFR(0x11, 0x11, static_cast<FRegister>(ft), static_cast<FRegister>(fs),
    372          static_cast<FRegister>(fd), 0x2);
    373 }
    374 
    375 void MipsAssembler::DivD(DRegister fd, DRegister fs, DRegister ft) {
    376   EmitFR(0x11, 0x11, static_cast<FRegister>(ft), static_cast<FRegister>(fs),
    377          static_cast<FRegister>(fd), 0x3);
    378 }
    379 
    380 void MipsAssembler::MovS(FRegister fd, FRegister fs) {
    381   EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6);
    382 }
    383 
    384 void MipsAssembler::MovD(DRegister fd, DRegister fs) {
    385   EmitFR(0x11, 0x11, static_cast<FRegister>(0), static_cast<FRegister>(fs),
    386          static_cast<FRegister>(fd), 0x6);
    387 }
    388 
    389 void MipsAssembler::Mfc1(Register rt, FRegister fs) {
    390   EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
    391 }
    392 
    393 void MipsAssembler::Mtc1(FRegister ft, Register rs) {
    394   EmitFR(0x11, 0x04, ft, static_cast<FRegister>(rs), static_cast<FRegister>(0), 0x0);
    395 }
    396 
    397 void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) {
    398   EmitI(0x31, rs, static_cast<Register>(ft), imm16);
    399 }
    400 
    401 void MipsAssembler::Ldc1(DRegister ft, Register rs, uint16_t imm16) {
    402   EmitI(0x35, rs, static_cast<Register>(ft), imm16);
    403 }
    404 
    405 void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) {
    406   EmitI(0x39, rs, static_cast<Register>(ft), imm16);
    407 }
    408 
    409 void MipsAssembler::Sdc1(DRegister ft, Register rs, uint16_t imm16) {
    410   EmitI(0x3d, rs, static_cast<Register>(ft), imm16);
    411 }
    412 
    413 void MipsAssembler::Break() {
    414   EmitR(0, static_cast<Register>(0), static_cast<Register>(0),
    415         static_cast<Register>(0), 0, 0xD);
    416 }
    417 
    418 void MipsAssembler::Nop() {
    419   EmitR(0x0, static_cast<Register>(0), static_cast<Register>(0), static_cast<Register>(0), 0, 0x0);
    420 }
    421 
    422 void MipsAssembler::Move(Register rt, Register rs) {
    423   EmitI(0x8, rs, rt, 0);
    424 }
    425 
    426 void MipsAssembler::Clear(Register rt) {
    427   EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rt, 0, 0x20);
    428 }
    429 
    430 void MipsAssembler::Not(Register rt, Register rs) {
    431   EmitR(0, static_cast<Register>(0), rs, rt, 0, 0x27);
    432 }
    433 
    434 void MipsAssembler::Mul(Register rd, Register rs, Register rt) {
    435   Mult(rs, rt);
    436   Mflo(rd);
    437 }
    438 
    439 void MipsAssembler::Div(Register rd, Register rs, Register rt) {
    440   Div(rs, rt);
    441   Mflo(rd);
    442 }
    443 
    444 void MipsAssembler::Rem(Register rd, Register rs, Register rt) {
    445   Div(rs, rt);
    446   Mfhi(rd);
    447 }
    448 
    449 void MipsAssembler::AddConstant(Register rt, Register rs, int32_t value) {
    450   Addi(rt, rs, value);
    451 }
    452 
    453 void MipsAssembler::LoadImmediate(Register rt, int32_t value) {
    454   Addi(rt, ZERO, value);
    455 }
    456 
    457 void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
    458                              size_t size) {
    459   MipsManagedRegister dst = m_dst.AsMips();
    460   if (dst.IsNoRegister()) {
    461     CHECK_EQ(0u, size) << dst;
    462   } else if (dst.IsCoreRegister()) {
    463     CHECK_EQ(4u, size) << dst;
    464     LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
    465   } else if (dst.IsRegisterPair()) {
    466     CHECK_EQ(8u, size) << dst;
    467     LoadFromOffset(kLoadWord, dst.AsRegisterPairLow(), src_register, src_offset);
    468     LoadFromOffset(kLoadWord, dst.AsRegisterPairHigh(), src_register, src_offset + 4);
    469   } else if (dst.IsFRegister()) {
    470     LoadSFromOffset(dst.AsFRegister(), src_register, src_offset);
    471   } else {
    472     CHECK(dst.IsDRegister()) << dst;
    473     LoadDFromOffset(dst.AsDRegister(), src_register, src_offset);
    474   }
    475 }
    476 
    477 void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register base,
    478                                    int32_t offset) {
    479   switch (type) {
    480     case kLoadSignedByte:
    481       Lb(reg, base, offset);
    482       break;
    483     case kLoadUnsignedByte:
    484       Lbu(reg, base, offset);
    485       break;
    486     case kLoadSignedHalfword:
    487       Lh(reg, base, offset);
    488       break;
    489     case kLoadUnsignedHalfword:
    490       Lhu(reg, base, offset);
    491       break;
    492     case kLoadWord:
    493       Lw(reg, base, offset);
    494       break;
    495     case kLoadWordPair:
    496       LOG(FATAL) << "UNREACHABLE";
    497       break;
    498     default:
    499       LOG(FATAL) << "UNREACHABLE";
    500   }
    501 }
    502 
    503 void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
    504   Lwc1(reg, base, offset);
    505 }
    506 
    507 void MipsAssembler::LoadDFromOffset(DRegister reg, Register base, int32_t offset) {
    508   Ldc1(reg, base, offset);
    509 }
    510 
    511 void MipsAssembler::StoreToOffset(StoreOperandType type, Register reg, Register base,
    512                                   int32_t offset) {
    513   switch (type) {
    514     case kStoreByte:
    515       Sb(reg, base, offset);
    516       break;
    517     case kStoreHalfword:
    518       Sh(reg, base, offset);
    519       break;
    520     case kStoreWord:
    521       Sw(reg, base, offset);
    522       break;
    523     case kStoreWordPair:
    524       LOG(FATAL) << "UNREACHABLE";
    525       break;
    526     default:
    527       LOG(FATAL) << "UNREACHABLE";
    528   }
    529 }
    530 
    531 void MipsAssembler::StoreFToOffset(FRegister reg, Register base, int32_t offset) {
    532   Swc1(reg, base, offset);
    533 }
    534 
    535 void MipsAssembler::StoreDToOffset(DRegister reg, Register base, int32_t offset) {
    536   Sdc1(reg, base, offset);
    537 }
    538 
    539 constexpr size_t kFramePointerSize = 4;
    540 
    541 void MipsAssembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
    542                                const std::vector<ManagedRegister>& callee_save_regs,
    543                                const ManagedRegisterEntrySpills& entry_spills) {
    544   CHECK_ALIGNED(frame_size, kStackAlignment);
    545 
    546   // Increase frame to required size.
    547   IncreaseFrameSize(frame_size);
    548 
    549   // Push callee saves and return address
    550   int stack_offset = frame_size - kFramePointerSize;
    551   StoreToOffset(kStoreWord, RA, SP, stack_offset);
    552   for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
    553     stack_offset -= kFramePointerSize;
    554     Register reg = callee_save_regs.at(i).AsMips().AsCoreRegister();
    555     StoreToOffset(kStoreWord, reg, SP, stack_offset);
    556   }
    557 
    558   // Write out Method*.
    559   StoreToOffset(kStoreWord, method_reg.AsMips().AsCoreRegister(), SP, 0);
    560 
    561   // Write out entry spills.
    562   for (size_t i = 0; i < entry_spills.size(); ++i) {
    563     Register reg = entry_spills.at(i).AsMips().AsCoreRegister();
    564     StoreToOffset(kStoreWord, reg, SP, frame_size + kFramePointerSize + (i * kFramePointerSize));
    565   }
    566 }
    567 
    568 void MipsAssembler::RemoveFrame(size_t frame_size,
    569                                 const std::vector<ManagedRegister>& callee_save_regs) {
    570   CHECK_ALIGNED(frame_size, kStackAlignment);
    571 
    572   // Pop callee saves and return address
    573   int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
    574   for (size_t i = 0; i < callee_save_regs.size(); ++i) {
    575     Register reg = callee_save_regs.at(i).AsMips().AsCoreRegister();
    576     LoadFromOffset(kLoadWord, reg, SP, stack_offset);
    577     stack_offset += kFramePointerSize;
    578   }
    579   LoadFromOffset(kLoadWord, RA, SP, stack_offset);
    580 
    581   // Decrease frame to required size.
    582   DecreaseFrameSize(frame_size);
    583 
    584   // Then jump to the return address.
    585   Jr(RA);
    586 }
    587 
    588 void MipsAssembler::IncreaseFrameSize(size_t adjust) {
    589   CHECK_ALIGNED(adjust, kStackAlignment);
    590   AddConstant(SP, SP, -adjust);
    591 }
    592 
    593 void MipsAssembler::DecreaseFrameSize(size_t adjust) {
    594   CHECK_ALIGNED(adjust, kStackAlignment);
    595   AddConstant(SP, SP, adjust);
    596 }
    597 
    598 void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
    599   MipsManagedRegister src = msrc.AsMips();
    600   if (src.IsNoRegister()) {
    601     CHECK_EQ(0u, size);
    602   } else if (src.IsCoreRegister()) {
    603     CHECK_EQ(4u, size);
    604     StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    605   } else if (src.IsRegisterPair()) {
    606     CHECK_EQ(8u, size);
    607     StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
    608     StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
    609                   SP, dest.Int32Value() + 4);
    610   } else if (src.IsFRegister()) {
    611     StoreFToOffset(src.AsFRegister(), SP, dest.Int32Value());
    612   } else {
    613     CHECK(src.IsDRegister());
    614     StoreDToOffset(src.AsDRegister(), SP, dest.Int32Value());
    615   }
    616 }
    617 
    618 void MipsAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
    619   MipsManagedRegister src = msrc.AsMips();
    620   CHECK(src.IsCoreRegister());
    621   StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    622 }
    623 
    624 void MipsAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
    625   MipsManagedRegister src = msrc.AsMips();
    626   CHECK(src.IsCoreRegister());
    627   StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    628 }
    629 
    630 void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
    631                                           ManagedRegister mscratch) {
    632   MipsManagedRegister scratch = mscratch.AsMips();
    633   CHECK(scratch.IsCoreRegister()) << scratch;
    634   LoadImmediate(scratch.AsCoreRegister(), imm);
    635   StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    636 }
    637 
    638 void MipsAssembler::StoreImmediateToThread32(ThreadOffset<4> dest, uint32_t imm,
    639                                            ManagedRegister mscratch) {
    640   MipsManagedRegister scratch = mscratch.AsMips();
    641   CHECK(scratch.IsCoreRegister()) << scratch;
    642   LoadImmediate(scratch.AsCoreRegister(), imm);
    643   StoreToOffset(kStoreWord, scratch.AsCoreRegister(), S1, dest.Int32Value());
    644 }
    645 
    646 void MipsAssembler::StoreStackOffsetToThread32(ThreadOffset<4> thr_offs,
    647                                              FrameOffset fr_offs,
    648                                              ManagedRegister mscratch) {
    649   MipsManagedRegister scratch = mscratch.AsMips();
    650   CHECK(scratch.IsCoreRegister()) << scratch;
    651   AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
    652   StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
    653                 S1, thr_offs.Int32Value());
    654 }
    655 
    656 void MipsAssembler::StoreStackPointerToThread32(ThreadOffset<4> thr_offs) {
    657   StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value());
    658 }
    659 
    660 void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
    661                                   FrameOffset in_off, ManagedRegister mscratch) {
    662   MipsManagedRegister src = msrc.AsMips();
    663   MipsManagedRegister scratch = mscratch.AsMips();
    664   StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    665   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
    666   StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4);
    667 }
    668 
    669 void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
    670   return EmitLoad(mdest, SP, src.Int32Value(), size);
    671 }
    672 
    673 void MipsAssembler::LoadFromThread32(ManagedRegister mdest, ThreadOffset<4> src, size_t size) {
    674   return EmitLoad(mdest, S1, src.Int32Value(), size);
    675 }
    676 
    677 void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
    678   MipsManagedRegister dest = mdest.AsMips();
    679   CHECK(dest.IsCoreRegister());
    680   LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
    681 }
    682 
    683 void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base,
    684                             MemberOffset offs) {
    685   MipsManagedRegister dest = mdest.AsMips();
    686   CHECK(dest.IsCoreRegister() && dest.IsCoreRegister());
    687   LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
    688                  base.AsMips().AsCoreRegister(), offs.Int32Value());
    689   if (kPoisonHeapReferences) {
    690     Subu(dest.AsCoreRegister(), ZERO, dest.AsCoreRegister());
    691   }
    692 }
    693 
    694 void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base,
    695                                Offset offs) {
    696   MipsManagedRegister dest = mdest.AsMips();
    697   CHECK(dest.IsCoreRegister() && dest.IsCoreRegister()) << dest;
    698   LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
    699                  base.AsMips().AsCoreRegister(), offs.Int32Value());
    700 }
    701 
    702 void MipsAssembler::LoadRawPtrFromThread32(ManagedRegister mdest,
    703                                          ThreadOffset<4> offs) {
    704   MipsManagedRegister dest = mdest.AsMips();
    705   CHECK(dest.IsCoreRegister());
    706   LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value());
    707 }
    708 
    709 void MipsAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
    710   UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
    711 }
    712 
    713 void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
    714   UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
    715 }
    716 
    717 void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t /*size*/) {
    718   MipsManagedRegister dest = mdest.AsMips();
    719   MipsManagedRegister src = msrc.AsMips();
    720   if (!dest.Equals(src)) {
    721     if (dest.IsCoreRegister()) {
    722       CHECK(src.IsCoreRegister()) << src;
    723       Move(dest.AsCoreRegister(), src.AsCoreRegister());
    724     } else if (dest.IsFRegister()) {
    725       CHECK(src.IsFRegister()) << src;
    726       MovS(dest.AsFRegister(), src.AsFRegister());
    727     } else if (dest.IsDRegister()) {
    728       CHECK(src.IsDRegister()) << src;
    729       MovD(dest.AsDRegister(), src.AsDRegister());
    730     } else {
    731       CHECK(dest.IsRegisterPair()) << dest;
    732       CHECK(src.IsRegisterPair()) << src;
    733       // Ensure that the first move doesn't clobber the input of the second
    734       if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) {
    735         Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
    736         Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
    737       } else {
    738         Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
    739         Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
    740       }
    741     }
    742   }
    743 }
    744 
    745 void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src,
    746                             ManagedRegister mscratch) {
    747   MipsManagedRegister scratch = mscratch.AsMips();
    748   CHECK(scratch.IsCoreRegister()) << scratch;
    749   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
    750   StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    751 }
    752 
    753 void MipsAssembler::CopyRawPtrFromThread32(FrameOffset fr_offs,
    754                                          ThreadOffset<4> thr_offs,
    755                                          ManagedRegister mscratch) {
    756   MipsManagedRegister scratch = mscratch.AsMips();
    757   CHECK(scratch.IsCoreRegister()) << scratch;
    758   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    759                  S1, thr_offs.Int32Value());
    760   StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
    761                 SP, fr_offs.Int32Value());
    762 }
    763 
    764 void MipsAssembler::CopyRawPtrToThread32(ThreadOffset<4> thr_offs,
    765                                        FrameOffset fr_offs,
    766                                        ManagedRegister mscratch) {
    767   MipsManagedRegister scratch = mscratch.AsMips();
    768   CHECK(scratch.IsCoreRegister()) << scratch;
    769   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    770                  SP, fr_offs.Int32Value());
    771   StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
    772                 S1, thr_offs.Int32Value());
    773 }
    774 
    775 void MipsAssembler::Copy(FrameOffset dest, FrameOffset src,
    776                          ManagedRegister mscratch, size_t size) {
    777   MipsManagedRegister scratch = mscratch.AsMips();
    778   CHECK(scratch.IsCoreRegister()) << scratch;
    779   CHECK(size == 4 || size == 8) << size;
    780   if (size == 4) {
    781     LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
    782     StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    783   } else if (size == 8) {
    784     LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
    785     StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    786     LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + 4);
    787     StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4);
    788   }
    789 }
    790 
    791 void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
    792                          ManagedRegister mscratch, size_t size) {
    793   Register scratch = mscratch.AsMips().AsCoreRegister();
    794   CHECK_EQ(size, 4u);
    795   LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value());
    796   StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
    797 }
    798 
    799 void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
    800                          ManagedRegister mscratch, size_t size) {
    801   Register scratch = mscratch.AsMips().AsCoreRegister();
    802   CHECK_EQ(size, 4u);
    803   LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
    804   StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value());
    805 }
    806 
    807 void MipsAssembler::Copy(FrameOffset /*dest*/, FrameOffset /*src_base*/, Offset /*src_offset*/,
    808                          ManagedRegister /*mscratch*/, size_t /*size*/) {
    809   UNIMPLEMENTED(FATAL) << "no mips implementation";
    810 }
    811 
    812 void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset,
    813                          ManagedRegister src, Offset src_offset,
    814                          ManagedRegister mscratch, size_t size) {
    815   CHECK_EQ(size, 4u);
    816   Register scratch = mscratch.AsMips().AsCoreRegister();
    817   LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value());
    818   StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value());
    819 }
    820 
    821 void MipsAssembler::Copy(FrameOffset /*dest*/, Offset /*dest_offset*/, FrameOffset /*src*/, Offset /*src_offset*/,
    822                          ManagedRegister /*mscratch*/, size_t /*size*/) {
    823   UNIMPLEMENTED(FATAL) << "no mips implementation";
    824 }
    825 
    826 void MipsAssembler::MemoryBarrier(ManagedRegister) {
    827   UNIMPLEMENTED(FATAL) << "no mips implementation";
    828 }
    829 
    830 void MipsAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
    831                                     FrameOffset handle_scope_offset,
    832                                     ManagedRegister min_reg, bool null_allowed) {
    833   MipsManagedRegister out_reg = mout_reg.AsMips();
    834   MipsManagedRegister in_reg = min_reg.AsMips();
    835   CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
    836   CHECK(out_reg.IsCoreRegister()) << out_reg;
    837   if (null_allowed) {
    838     Label null_arg;
    839     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
    840     // the address in the handle scope holding the reference.
    841     // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
    842     if (in_reg.IsNoRegister()) {
    843       LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
    844                      SP, handle_scope_offset.Int32Value());
    845       in_reg = out_reg;
    846     }
    847     if (!out_reg.Equals(in_reg)) {
    848       LoadImmediate(out_reg.AsCoreRegister(), 0);
    849     }
    850     EmitBranch(in_reg.AsCoreRegister(), ZERO, &null_arg, true);
    851     AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
    852     Bind(&null_arg, false);
    853   } else {
    854     AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
    855   }
    856 }
    857 
    858 void MipsAssembler::CreateHandleScopeEntry(FrameOffset out_off,
    859                                     FrameOffset handle_scope_offset,
    860                                     ManagedRegister mscratch,
    861                                     bool null_allowed) {
    862   MipsManagedRegister scratch = mscratch.AsMips();
    863   CHECK(scratch.IsCoreRegister()) << scratch;
    864   if (null_allowed) {
    865     Label null_arg;
    866     LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP,
    867                    handle_scope_offset.Int32Value());
    868     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
    869     // the address in the handle scope holding the reference.
    870     // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
    871     EmitBranch(scratch.AsCoreRegister(), ZERO, &null_arg, true);
    872     AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
    873     Bind(&null_arg, false);
    874   } else {
    875     AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
    876   }
    877   StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
    878 }
    879 
    880 // Given a handle scope entry, load the associated reference.
    881 void MipsAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
    882                                           ManagedRegister min_reg) {
    883   MipsManagedRegister out_reg = mout_reg.AsMips();
    884   MipsManagedRegister in_reg = min_reg.AsMips();
    885   CHECK(out_reg.IsCoreRegister()) << out_reg;
    886   CHECK(in_reg.IsCoreRegister()) << in_reg;
    887   Label null_arg;
    888   if (!out_reg.Equals(in_reg)) {
    889     LoadImmediate(out_reg.AsCoreRegister(), 0);
    890   }
    891   EmitBranch(in_reg.AsCoreRegister(), ZERO, &null_arg, true);
    892   LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
    893                  in_reg.AsCoreRegister(), 0);
    894   Bind(&null_arg, false);
    895 }
    896 
    897 void MipsAssembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) {
    898   // TODO: not validating references
    899 }
    900 
    901 void MipsAssembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) {
    902   // TODO: not validating references
    903 }
    904 
    905 void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
    906   MipsManagedRegister base = mbase.AsMips();
    907   MipsManagedRegister scratch = mscratch.AsMips();
    908   CHECK(base.IsCoreRegister()) << base;
    909   CHECK(scratch.IsCoreRegister()) << scratch;
    910   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    911                  base.AsCoreRegister(), offset.Int32Value());
    912   Jalr(scratch.AsCoreRegister());
    913   // TODO: place reference map on call
    914 }
    915 
    916 void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
    917   MipsManagedRegister scratch = mscratch.AsMips();
    918   CHECK(scratch.IsCoreRegister()) << scratch;
    919   // Call *(*(SP + base) + offset)
    920   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    921                  SP, base.Int32Value());
    922   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    923                  scratch.AsCoreRegister(), offset.Int32Value());
    924   Jalr(scratch.AsCoreRegister());
    925   // TODO: place reference map on call
    926 }
    927 
    928 void MipsAssembler::CallFromThread32(ThreadOffset<4> /*offset*/, ManagedRegister /*mscratch*/) {
    929   UNIMPLEMENTED(FATAL) << "no mips implementation";
    930 }
    931 
    932 void MipsAssembler::GetCurrentThread(ManagedRegister tr) {
    933   Move(tr.AsMips().AsCoreRegister(), S1);
    934 }
    935 
    936 void MipsAssembler::GetCurrentThread(FrameOffset offset,
    937                                      ManagedRegister /*mscratch*/) {
    938   StoreToOffset(kStoreWord, S1, SP, offset.Int32Value());
    939 }
    940 
    941 void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
    942   MipsManagedRegister scratch = mscratch.AsMips();
    943   MipsExceptionSlowPath* slow = new MipsExceptionSlowPath(scratch, stack_adjust);
    944   buffer_.EnqueueSlowPath(slow);
    945   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    946                  S1, Thread::ExceptionOffset<4>().Int32Value());
    947   EmitBranch(scratch.AsCoreRegister(), ZERO, slow->Entry(), false);
    948 }
    949 
    950 void MipsExceptionSlowPath::Emit(Assembler* sasm) {
    951   MipsAssembler* sp_asm = down_cast<MipsAssembler*>(sasm);
    952 #define __ sp_asm->
    953   __ Bind(&entry_, false);
    954   if (stack_adjust_ != 0) {  // Fix up the frame.
    955     __ DecreaseFrameSize(stack_adjust_);
    956   }
    957   // Pass exception object as argument
    958   // Don't care about preserving A0 as this call won't return
    959   __ Move(A0, scratch_.AsCoreRegister());
    960   // Set up call to Thread::Current()->pDeliverException
    961   __ LoadFromOffset(kLoadWord, T9, S1, QUICK_ENTRYPOINT_OFFSET(4, pDeliverException).Int32Value());
    962   __ Jr(T9);
    963   // Call never returns
    964   __ Break();
    965 #undef __
    966 }
    967 
    968 }  // namespace mips
    969 }  // namespace art
    970