1/* 2 * Copyright (C) 2012 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/* This file contains codegen for the Mips ISA */ 18 19#include "codegen_mips.h" 20 21#include "art_method.h" 22#include "base/logging.h" 23#include "dex/mir_graph.h" 24#include "dex/quick/dex_file_to_method_inliner_map.h" 25#include "dex/quick/mir_to_lir-inl.h" 26#include "driver/compiler_driver.h" 27#include "driver/compiler_options.h" 28#include "entrypoints/quick/quick_entrypoints.h" 29#include "gc/accounting/card_table.h" 30#include "mips_lir.h" 31#include "mirror/object_array-inl.h" 32 33namespace art { 34 35bool MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special) { 36 // TODO 37 UNUSED(bb, mir, special); 38 return false; 39} 40 41/* 42 * The lack of pc-relative loads on Mips presents somewhat of a challenge 43 * for our PIC switch table strategy. To materialize the current location 44 * we'll do a dummy JAL and reference our tables using rRA as the 45 * base register. Note that rRA will be used both as the base to 46 * locate the switch table data and as the reference base for the switch 47 * target offsets stored in the table. We'll use a special pseudo-instruction 48 * to represent the jal and trigger the construction of the 49 * switch table offsets (which will happen after final assembly and all 50 * labels are fixed). 51 * 52 * The test loop will look something like: 53 * 54 * ori r_end, rZERO, #table_size ; size in bytes 55 * jal BaseLabel ; stores "return address" (BaseLabel) in rRA 56 * nop ; opportunistically fill 57 * BaseLabel: 58 * addiu r_base, rRA, <table> - <BaseLabel> ; table relative to BaseLabel 59 addu r_end, r_end, r_base ; end of table 60 * lw r_val, [rSP, v_reg_off] ; Test Value 61 * loop: 62 * beq r_base, r_end, done 63 * lw r_key, 0(r_base) 64 * addu r_base, 8 65 * bne r_val, r_key, loop 66 * lw r_disp, -4(r_base) 67 * addu rRA, r_disp 68 * jalr rZERO, rRA 69 * done: 70 * 71 */ 72void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) { 73 const uint16_t* table = mir_graph_->GetTable(mir, table_offset); 74 // Add the table to the list - we'll process it later. 75 SwitchTable* tab_rec = 76 static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData)); 77 tab_rec->switch_mir = mir; 78 tab_rec->table = table; 79 tab_rec->vaddr = current_dalvik_offset_; 80 int elements = table[1]; 81 switch_tables_.push_back(tab_rec); 82 83 // The table is composed of 8-byte key/disp pairs. 84 int byte_size = elements * 8; 85 86 int size_hi = byte_size >> 16; 87 int size_lo = byte_size & 0xffff; 88 89 RegStorage r_end = AllocPtrSizeTemp(); 90 if (size_hi) { 91 NewLIR2(kMipsLui, r_end.GetReg(), size_hi); 92 } 93 // Must prevent code motion for the curr pc pair. 94 GenBarrier(); // Scheduling barrier 95 NewLIR0(kMipsCurrPC); // Really a jal to .+8. 96 // Now, fill the branch delay slot. 97 if (size_hi) { 98 NewLIR3(kMipsOri, r_end.GetReg(), r_end.GetReg(), size_lo); 99 } else { 100 NewLIR3(kMipsOri, r_end.GetReg(), rZERO, size_lo); 101 } 102 GenBarrier(); // Scheduling barrier. 103 104 // Construct BaseLabel and set up table base register. 105 LIR* base_label = NewLIR0(kPseudoTargetLabel); 106 // Remember base label so offsets can be computed later. 107 tab_rec->anchor = base_label; 108 RegStorage r_base = AllocPtrSizeTemp(); 109 NewLIR4(kMipsDelta, r_base.GetReg(), 0, WrapPointer(base_label), WrapPointer(tab_rec)); 110 OpRegRegReg(kOpAdd, r_end, r_end, r_base); 111 112 // Grab switch test value. 113 rl_src = LoadValue(rl_src, kCoreReg); 114 115 // Test loop. 116 RegStorage r_key = AllocTemp(); 117 LIR* loop_label = NewLIR0(kPseudoTargetLabel); 118 LIR* exit_branch = OpCmpBranch(kCondEq, r_base, r_end, nullptr); 119 Load32Disp(r_base, 0, r_key); 120 OpRegImm(kOpAdd, r_base, 8); 121 OpCmpBranch(kCondNe, rl_src.reg, r_key, loop_label); 122 RegStorage r_disp = AllocTemp(); 123 Load32Disp(r_base, -4, r_disp); 124 const RegStorage rs_ra = TargetPtrReg(kLr); 125 OpRegRegReg(kOpAdd, rs_ra, rs_ra, r_disp); 126 OpReg(kOpBx, rs_ra); 127 // Loop exit. 128 LIR* exit_label = NewLIR0(kPseudoTargetLabel); 129 exit_branch->target = exit_label; 130} 131 132/* 133 * Code pattern will look something like: 134 * 135 * lw r_val 136 * jal BaseLabel ; stores "return address" (BaseLabel) in rRA 137 * nop ; opportunistically fill 138 * [subiu r_val, bias] ; Remove bias if low_val != 0 139 * bound check -> done 140 * lw r_disp, [rRA, r_val] 141 * addu rRA, r_disp 142 * jalr rZERO, rRA 143 * done: 144 */ 145void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) { 146 const uint16_t* table = mir_graph_->GetTable(mir, table_offset); 147 // Add the table to the list - we'll process it later. 148 SwitchTable* tab_rec = 149 static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData)); 150 tab_rec->switch_mir = mir; 151 tab_rec->table = table; 152 tab_rec->vaddr = current_dalvik_offset_; 153 int size = table[1]; 154 switch_tables_.push_back(tab_rec); 155 156 // Get the switch value. 157 rl_src = LoadValue(rl_src, kCoreReg); 158 159 // Prepare the bias. If too big, handle 1st stage here. 160 int low_key = s4FromSwitchData(&table[2]); 161 bool large_bias = false; 162 RegStorage r_key; 163 if (low_key == 0) { 164 r_key = rl_src.reg; 165 } else if ((low_key & 0xffff) != low_key) { 166 r_key = AllocTemp(); 167 LoadConstant(r_key, low_key); 168 large_bias = true; 169 } else { 170 r_key = AllocTemp(); 171 } 172 173 // Must prevent code motion for the curr pc pair. 174 GenBarrier(); 175 NewLIR0(kMipsCurrPC); // Really a jal to .+8. 176 // Now, fill the branch delay slot with bias strip. 177 if (low_key == 0) { 178 NewLIR0(kMipsNop); 179 } else { 180 if (large_bias) { 181 OpRegRegReg(kOpSub, r_key, rl_src.reg, r_key); 182 } else { 183 OpRegRegImm(kOpSub, r_key, rl_src.reg, low_key); 184 } 185 } 186 GenBarrier(); // Scheduling barrier. 187 188 // Construct BaseLabel and set up table base register. 189 LIR* base_label = NewLIR0(kPseudoTargetLabel); 190 // Remember base label so offsets can be computed later. 191 tab_rec->anchor = base_label; 192 193 // Bounds check - if < 0 or >= size continue following switch. 194 LIR* branch_over = OpCmpImmBranch(kCondHi, r_key, size-1, nullptr); 195 196 // Materialize the table base pointer. 197 RegStorage r_base = AllocPtrSizeTemp(); 198 NewLIR4(kMipsDelta, r_base.GetReg(), 0, WrapPointer(base_label), WrapPointer(tab_rec)); 199 200 // Load the displacement from the switch table. 201 RegStorage r_disp = AllocTemp(); 202 LoadBaseIndexed(r_base, r_key, r_disp, 2, k32); 203 204 // Add to rRA and go. 205 const RegStorage rs_ra = TargetPtrReg(kLr); 206 OpRegRegReg(kOpAdd, rs_ra, rs_ra, r_disp); 207 OpReg(kOpBx, rs_ra); 208 209 // Branch_over target here. 210 LIR* target = NewLIR0(kPseudoTargetLabel); 211 branch_over->target = target; 212} 213 214void MipsMir2Lir::GenMoveException(RegLocation rl_dest) { 215 int ex_offset = cu_->target64 ? Thread::ExceptionOffset<8>().Int32Value() : 216 Thread::ExceptionOffset<4>().Int32Value(); 217 RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true); 218 RegStorage reset_reg = AllocTempRef(); 219 LoadRefDisp(TargetPtrReg(kSelf), ex_offset, rl_result.reg, kNotVolatile); 220 LoadConstant(reset_reg, 0); 221 StoreRefDisp(TargetPtrReg(kSelf), ex_offset, reset_reg, kNotVolatile); 222 FreeTemp(reset_reg); 223 StoreValue(rl_dest, rl_result); 224} 225 226void MipsMir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) { 227 RegStorage reg_card_base = AllocPtrSizeTemp(); 228 RegStorage reg_card_no = AllocPtrSizeTemp(); 229 if (cu_->target64) { 230 // NOTE: native pointer. 231 LoadWordDisp(TargetPtrReg(kSelf), Thread::CardTableOffset<8>().Int32Value(), reg_card_base); 232 OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); 233 StoreBaseIndexed(reg_card_base, reg_card_no, As32BitReg(reg_card_base), 0, kUnsignedByte); 234 } else { 235 // NOTE: native pointer. 236 LoadWordDisp(TargetPtrReg(kSelf), Thread::CardTableOffset<4>().Int32Value(), reg_card_base); 237 OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); 238 StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte); 239 } 240 FreeTemp(reg_card_base); 241 FreeTemp(reg_card_no); 242} 243 244static dwarf::Reg DwarfCoreReg(int num) { 245 return dwarf::Reg::MipsCore(num); 246} 247 248void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { 249 DCHECK_EQ(cfi_.GetCurrentCFAOffset(), 0); 250 int spill_count = num_core_spills_ + num_fp_spills_; 251 /* 252 * On entry, A0, A1, A2 & A3 are live. On Mips64, A4, A5, A6 & A7 are also live. 253 * Let the register allocation mechanism know so it doesn't try to use any of them when 254 * expanding the frame or flushing. 255 */ 256 const RegStorage arg0 = TargetReg(kArg0); 257 const RegStorage arg1 = TargetReg(kArg1); 258 const RegStorage arg2 = TargetReg(kArg2); 259 const RegStorage arg3 = TargetReg(kArg3); 260 const RegStorage arg4 = TargetReg(kArg4); 261 const RegStorage arg5 = TargetReg(kArg5); 262 const RegStorage arg6 = TargetReg(kArg6); 263 const RegStorage arg7 = TargetReg(kArg7); 264 265 LockTemp(arg0); 266 LockTemp(arg1); 267 LockTemp(arg2); 268 LockTemp(arg3); 269 if (cu_->target64) { 270 LockTemp(arg4); 271 LockTemp(arg5); 272 LockTemp(arg6); 273 LockTemp(arg7); 274 } 275 276 bool skip_overflow_check; 277 InstructionSet target = (cu_->target64) ? kMips64 : kMips; 278 int ptr_size = cu_->target64 ? 8 : 4; 279 280 /* 281 * We can safely skip the stack overflow check if we're 282 * a leaf *and* our frame size < fudge factor. 283 */ 284 285 skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, target); 286 RegStorage check_reg = AllocPtrSizeTemp(); 287 RegStorage new_sp = AllocPtrSizeTemp(); 288 const RegStorage rs_sp = TargetPtrReg(kSp); 289 const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(target); 290 const bool large_frame = static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes; 291 bool generate_explicit_stack_overflow_check = large_frame || 292 !cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks(); 293 294 if (!skip_overflow_check) { 295 if (generate_explicit_stack_overflow_check) { 296 // Load stack limit. 297 if (cu_->target64) { 298 LoadWordDisp(TargetPtrReg(kSelf), Thread::StackEndOffset<8>().Int32Value(), check_reg); 299 } else { 300 Load32Disp(TargetPtrReg(kSelf), Thread::StackEndOffset<4>().Int32Value(), check_reg); 301 } 302 } else { 303 // Implicit stack overflow check. 304 // Generate a load from [sp, #-overflowsize]. If this is in the stack 305 // redzone we will get a segmentation fault. 306 Load32Disp(rs_sp, -kStackOverflowReservedUsableBytes, rs_rZERO); 307 MarkPossibleStackOverflowException(); 308 } 309 } 310 // Spill core callee saves. 311 SpillCoreRegs(); 312 // NOTE: promotion of FP regs currently unsupported, thus no FP spill. 313 DCHECK_EQ(num_fp_spills_, 0); 314 const int frame_sub = frame_size_ - spill_count * ptr_size; 315 if (!skip_overflow_check && generate_explicit_stack_overflow_check) { 316 class StackOverflowSlowPath : public LIRSlowPath { 317 public: 318 StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace) 319 : LIRSlowPath(m2l, branch), sp_displace_(sp_displace) { 320 } 321 void Compile() OVERRIDE { 322 m2l_->ResetRegPool(); 323 m2l_->ResetDefTracking(); 324 GenerateTargetLabel(kPseudoThrowTarget); 325 // RA is offset 0 since we push in reverse order. 326 m2l_->LoadWordDisp(m2l_->TargetPtrReg(kSp), 0, m2l_->TargetPtrReg(kLr)); 327 m2l_->OpRegImm(kOpAdd, m2l_->TargetPtrReg(kSp), sp_displace_); 328 m2l_->cfi().AdjustCFAOffset(-sp_displace_); 329 m2l_->ClobberCallerSave(); 330 RegStorage r_tgt = m2l_->CallHelperSetup(kQuickThrowStackOverflow); // Doesn't clobber LR. 331 m2l_->CallHelper(r_tgt, kQuickThrowStackOverflow, false /* MarkSafepointPC */, 332 false /* UseLink */); 333 m2l_->cfi().AdjustCFAOffset(sp_displace_); 334 } 335 336 private: 337 const size_t sp_displace_; 338 }; 339 OpRegRegImm(kOpSub, new_sp, rs_sp, frame_sub); 340 LIR* branch = OpCmpBranch(kCondUlt, new_sp, check_reg, nullptr); 341 AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, spill_count * ptr_size)); 342 // TODO: avoid copy for small frame sizes. 343 OpRegCopy(rs_sp, new_sp); // Establish stack. 344 cfi_.AdjustCFAOffset(frame_sub); 345 } else { 346 // Here if skip_overflow_check or doing implicit stack overflow check. 347 // Just make room on the stack for the frame now. 348 OpRegImm(kOpSub, rs_sp, frame_sub); 349 cfi_.AdjustCFAOffset(frame_sub); 350 } 351 352 FlushIns(ArgLocs, rl_method); 353 354 FreeTemp(arg0); 355 FreeTemp(arg1); 356 FreeTemp(arg2); 357 FreeTemp(arg3); 358 if (cu_->target64) { 359 FreeTemp(arg4); 360 FreeTemp(arg5); 361 FreeTemp(arg6); 362 FreeTemp(arg7); 363 } 364} 365 366void MipsMir2Lir::GenExitSequence() { 367 cfi_.RememberState(); 368 /* 369 * In the exit path, rMIPS_RET0/rMIPS_RET1 are live - make sure they aren't 370 * allocated by the register utilities as temps. 371 */ 372 LockTemp(TargetPtrReg(kRet0)); 373 LockTemp(TargetPtrReg(kRet1)); 374 375 UnSpillCoreRegs(); 376 OpReg(kOpBx, TargetPtrReg(kLr)); 377 // The CFI should be restored for any code that follows the exit block. 378 cfi_.RestoreState(); 379 cfi_.DefCFAOffset(frame_size_); 380} 381 382void MipsMir2Lir::GenSpecialExitSequence() { 383 OpReg(kOpBx, TargetPtrReg(kLr)); 384} 385 386void MipsMir2Lir::GenSpecialEntryForSuspend() { 387 // Keep 16-byte stack alignment - push A0, i.e. ArtMethod*, 2 filler words and RA for mips32, 388 // but A0 and RA for mips64. 389 core_spill_mask_ = (1u << TargetPtrReg(kLr).GetRegNum()); 390 num_core_spills_ = 1u; 391 fp_spill_mask_ = 0u; 392 num_fp_spills_ = 0u; 393 frame_size_ = 16u; 394 core_vmap_table_.clear(); 395 fp_vmap_table_.clear(); 396 const RegStorage rs_sp = TargetPtrReg(kSp); 397 OpRegImm(kOpSub, rs_sp, frame_size_); 398 cfi_.AdjustCFAOffset(frame_size_); 399 StoreWordDisp(rs_sp, frame_size_ - (cu_->target64 ? 8 : 4), TargetPtrReg(kLr)); 400 cfi_.RelOffset(DwarfCoreReg(rRA), frame_size_ - (cu_->target64 ? 8 : 4)); 401 StoreWordDisp(rs_sp, 0, TargetPtrReg(kArg0)); 402 // Do not generate CFI for scratch register A0. 403} 404 405void MipsMir2Lir::GenSpecialExitForSuspend() { 406 // Pop the frame. Don't pop ArtMethod*, it's no longer needed. 407 const RegStorage rs_sp = TargetPtrReg(kSp); 408 LoadWordDisp(rs_sp, frame_size_ - (cu_->target64 ? 8 : 4), TargetPtrReg(kLr)); 409 cfi_.Restore(DwarfCoreReg(rRA)); 410 OpRegImm(kOpAdd, rs_sp, frame_size_); 411 cfi_.AdjustCFAOffset(-frame_size_); 412} 413 414/* 415 * Bit of a hack here - in the absence of a real scheduling pass, 416 * emit the next instruction in static & direct invoke sequences. 417 */ 418static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state, 419 const MethodReference& target_method, uint32_t, uintptr_t direct_code, 420 uintptr_t direct_method, InvokeType type) { 421 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 422 if (info->string_init_offset != 0) { 423 RegStorage arg0_ref = cg->TargetReg(kArg0, kRef); 424 switch (state) { 425 case 0: { // Grab target method* from thread pointer 426 cg->LoadWordDisp(cg->TargetPtrReg(kSelf), info->string_init_offset, arg0_ref); 427 break; 428 } 429 case 1: // Grab the code from the method* 430 if (direct_code == 0) { 431 int32_t offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset( 432 InstructionSetPointerSize(cu->instruction_set)).Int32Value(); 433 cg->LoadWordDisp(arg0_ref, offset, cg->TargetPtrReg(kInvokeTgt)); 434 } 435 break; 436 default: 437 return -1; 438 } 439 } else if (direct_code != 0 && direct_method != 0) { 440 switch (state) { 441 case 0: // Get the current Method* [sets kArg0] 442 if (direct_code != static_cast<uintptr_t>(-1)) { 443 if (cu->target64) { 444 cg->LoadConstantWide(cg->TargetPtrReg(kInvokeTgt), direct_code); 445 } else { 446 cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code); 447 } 448 } else { 449 cg->LoadCodeAddress(target_method, type, kInvokeTgt); 450 } 451 if (direct_method != static_cast<uintptr_t>(-1)) { 452 if (cu->target64) { 453 cg->LoadConstantWide(cg->TargetReg(kArg0, kRef), direct_method); 454 } else { 455 cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method); 456 } 457 } else { 458 cg->LoadMethodAddress(target_method, type, kArg0); 459 } 460 break; 461 default: 462 return -1; 463 } 464 } else { 465 RegStorage arg0_ref = cg->TargetReg(kArg0, kRef); 466 switch (state) { 467 case 0: // Get the current Method* [sets kArg0] 468 // TUNING: we can save a reg copy if Method* has been promoted. 469 cg->LoadCurrMethodDirect(arg0_ref); 470 break; 471 case 1: // Get method->dex_cache_resolved_methods_ 472 cg->LoadRefDisp(arg0_ref, 473 ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(), 474 arg0_ref, 475 kNotVolatile); 476 // Set up direct code if known. 477 if (direct_code != 0) { 478 if (direct_code != static_cast<uintptr_t>(-1)) { 479 if (cu->target64) { 480 cg->LoadConstantWide(cg->TargetPtrReg(kInvokeTgt), direct_code); 481 } else { 482 cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code); 483 } 484 } else { 485 CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); 486 cg->LoadCodeAddress(target_method, type, kInvokeTgt); 487 } 488 } 489 break; 490 case 2: { 491 // Grab target method* 492 CHECK_EQ(cu->dex_file, target_method.dex_file); 493 const size_t pointer_size = GetInstructionSetPointerSize(cu->instruction_set); 494 cg->LoadWordDisp(arg0_ref, 495 mirror::Array::DataOffset(pointer_size).Uint32Value() + 496 target_method.dex_method_index * pointer_size, arg0_ref); 497 break; 498 } 499 case 3: // Grab the code from the method* 500 if (direct_code == 0) { 501 int32_t offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset( 502 InstructionSetPointerSize(cu->instruction_set)).Int32Value(); 503 // Get the compiled code address [use *alt_from or kArg0, set kInvokeTgt] 504 cg->LoadWordDisp(arg0_ref, offset, cg->TargetPtrReg(kInvokeTgt)); 505 } 506 break; 507 default: 508 return -1; 509 } 510 } 511 return state + 1; 512} 513 514NextCallInsn MipsMir2Lir::GetNextSDCallInsn() { 515 return NextSDCallInsn; 516} 517 518LIR* MipsMir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info ATTRIBUTE_UNUSED) { 519 return OpReg(kOpBlx, TargetPtrReg(kInvokeTgt)); 520} 521 522} // namespace art 523