call_mips.cc revision 7940e44f4517de5e2634a7e07d58d0fb26160513
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#include "dex/quick/mir_to_lir-inl.h" 21#include "mips_lir.h" 22#include "oat/runtime/oat_support_entrypoints.h" 23 24namespace art { 25 26void MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, 27 SpecialCaseHandler special_case) 28{ 29 // TODO 30} 31 32/* 33 * The lack of pc-relative loads on Mips presents somewhat of a challenge 34 * for our PIC switch table strategy. To materialize the current location 35 * we'll do a dummy JAL and reference our tables using r_RA as the 36 * base register. Note that r_RA will be used both as the base to 37 * locate the switch table data and as the reference base for the switch 38 * target offsets stored in the table. We'll use a special pseudo-instruction 39 * to represent the jal and trigger the construction of the 40 * switch table offsets (which will happen after final assembly and all 41 * labels are fixed). 42 * 43 * The test loop will look something like: 44 * 45 * ori rEnd, r_ZERO, #table_size ; size in bytes 46 * jal BaseLabel ; stores "return address" (BaseLabel) in r_RA 47 * nop ; opportunistically fill 48 * BaseLabel: 49 * addiu rBase, r_RA, <table> - <BaseLabel> ; table relative to BaseLabel 50 addu rEnd, rEnd, rBase ; end of table 51 * lw r_val, [rSP, v_reg_off] ; Test Value 52 * loop: 53 * beq rBase, rEnd, done 54 * lw r_key, 0(rBase) 55 * addu rBase, 8 56 * bne r_val, r_key, loop 57 * lw r_disp, -4(rBase) 58 * addu r_RA, r_disp 59 * jr r_RA 60 * done: 61 * 62 */ 63void MipsMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset, 64 RegLocation rl_src) 65{ 66 const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; 67 if (cu_->verbose) { 68 DumpSparseSwitchTable(table); 69 } 70 // Add the table to the list - we'll process it later 71 SwitchTable *tab_rec = 72 static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true, 73 ArenaAllocator::kAllocData)); 74 tab_rec->table = table; 75 tab_rec->vaddr = current_dalvik_offset_; 76 int elements = table[1]; 77 tab_rec->targets = 78 static_cast<LIR**>(arena_->NewMem(elements * sizeof(LIR*), true, ArenaAllocator::kAllocLIR)); 79 switch_tables_.Insert(tab_rec); 80 81 // The table is composed of 8-byte key/disp pairs 82 int byte_size = elements * 8; 83 84 int size_hi = byte_size >> 16; 85 int size_lo = byte_size & 0xffff; 86 87 int rEnd = AllocTemp(); 88 if (size_hi) { 89 NewLIR2(kMipsLui, rEnd, size_hi); 90 } 91 // Must prevent code motion for the curr pc pair 92 GenBarrier(); // Scheduling barrier 93 NewLIR0(kMipsCurrPC); // Really a jal to .+8 94 // Now, fill the branch delay slot 95 if (size_hi) { 96 NewLIR3(kMipsOri, rEnd, rEnd, size_lo); 97 } else { 98 NewLIR3(kMipsOri, rEnd, r_ZERO, size_lo); 99 } 100 GenBarrier(); // Scheduling barrier 101 102 // Construct BaseLabel and set up table base register 103 LIR* base_label = NewLIR0(kPseudoTargetLabel); 104 // Remember base label so offsets can be computed later 105 tab_rec->anchor = base_label; 106 int rBase = AllocTemp(); 107 NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label), 108 reinterpret_cast<uintptr_t>(tab_rec)); 109 OpRegRegReg(kOpAdd, rEnd, rEnd, rBase); 110 111 // Grab switch test value 112 rl_src = LoadValue(rl_src, kCoreReg); 113 114 // Test loop 115 int r_key = AllocTemp(); 116 LIR* loop_label = NewLIR0(kPseudoTargetLabel); 117 LIR* exit_branch = OpCmpBranch(kCondEq, rBase, rEnd, NULL); 118 LoadWordDisp(rBase, 0, r_key); 119 OpRegImm(kOpAdd, rBase, 8); 120 OpCmpBranch(kCondNe, rl_src.low_reg, r_key, loop_label); 121 int r_disp = AllocTemp(); 122 LoadWordDisp(rBase, -4, r_disp); 123 OpRegRegReg(kOpAdd, r_RA, r_RA, r_disp); 124 OpReg(kOpBx, r_RA); 125 126 // Loop exit 127 LIR* exit_label = NewLIR0(kPseudoTargetLabel); 128 exit_branch->target = exit_label; 129} 130 131/* 132 * Code pattern will look something like: 133 * 134 * lw r_val 135 * jal BaseLabel ; stores "return address" (BaseLabel) in r_RA 136 * nop ; opportunistically fill 137 * [subiu r_val, bias] ; Remove bias if low_val != 0 138 * bound check -> done 139 * lw r_disp, [r_RA, r_val] 140 * addu r_RA, r_disp 141 * jr r_RA 142 * done: 143 */ 144void MipsMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset, 145 RegLocation rl_src) 146{ 147 const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; 148 if (cu_->verbose) { 149 DumpPackedSwitchTable(table); 150 } 151 // Add the table to the list - we'll process it later 152 SwitchTable *tab_rec = 153 static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true, 154 ArenaAllocator::kAllocData)); 155 tab_rec->table = table; 156 tab_rec->vaddr = current_dalvik_offset_; 157 int size = table[1]; 158 tab_rec->targets = static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true, 159 ArenaAllocator::kAllocLIR)); 160 switch_tables_.Insert(tab_rec); 161 162 // Get the switch value 163 rl_src = LoadValue(rl_src, kCoreReg); 164 165 // Prepare the bias. If too big, handle 1st stage here 166 int low_key = s4FromSwitchData(&table[2]); 167 bool large_bias = false; 168 int r_key; 169 if (low_key == 0) { 170 r_key = rl_src.low_reg; 171 } else if ((low_key & 0xffff) != low_key) { 172 r_key = AllocTemp(); 173 LoadConstant(r_key, low_key); 174 large_bias = true; 175 } else { 176 r_key = AllocTemp(); 177 } 178 179 // Must prevent code motion for the curr pc pair 180 GenBarrier(); 181 NewLIR0(kMipsCurrPC); // Really a jal to .+8 182 // Now, fill the branch delay slot with bias strip 183 if (low_key == 0) { 184 NewLIR0(kMipsNop); 185 } else { 186 if (large_bias) { 187 OpRegRegReg(kOpSub, r_key, rl_src.low_reg, r_key); 188 } else { 189 OpRegRegImm(kOpSub, r_key, rl_src.low_reg, low_key); 190 } 191 } 192 GenBarrier(); // Scheduling barrier 193 194 // Construct BaseLabel and set up table base register 195 LIR* base_label = NewLIR0(kPseudoTargetLabel); 196 // Remember base label so offsets can be computed later 197 tab_rec->anchor = base_label; 198 199 // Bounds check - if < 0 or >= size continue following switch 200 LIR* branch_over = OpCmpImmBranch(kCondHi, r_key, size-1, NULL); 201 202 // Materialize the table base pointer 203 int rBase = AllocTemp(); 204 NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label), 205 reinterpret_cast<uintptr_t>(tab_rec)); 206 207 // Load the displacement from the switch table 208 int r_disp = AllocTemp(); 209 LoadBaseIndexed(rBase, r_key, r_disp, 2, kWord); 210 211 // Add to r_AP and go 212 OpRegRegReg(kOpAdd, r_RA, r_RA, r_disp); 213 OpReg(kOpBx, r_RA); 214 215 /* branch_over target here */ 216 LIR* target = NewLIR0(kPseudoTargetLabel); 217 branch_over->target = target; 218} 219 220/* 221 * Array data table format: 222 * ushort ident = 0x0300 magic value 223 * ushort width width of each element in the table 224 * uint size number of elements in the table 225 * ubyte data[size*width] table of data values (may contain a single-byte 226 * padding at the end) 227 * 228 * Total size is 4+(width * size + 1)/2 16-bit code units. 229 */ 230void MipsMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) 231{ 232 const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; 233 // Add the table to the list - we'll process it later 234 FillArrayData *tab_rec = 235 reinterpret_cast<FillArrayData*>(arena_->NewMem(sizeof(FillArrayData), true, 236 ArenaAllocator::kAllocData)); 237 tab_rec->table = table; 238 tab_rec->vaddr = current_dalvik_offset_; 239 uint16_t width = tab_rec->table[1]; 240 uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16); 241 tab_rec->size = (size * width) + 8; 242 243 fill_array_data_.Insert(tab_rec); 244 245 // Making a call - use explicit registers 246 FlushAllRegs(); /* Everything to home location */ 247 LockCallTemps(); 248 LoadValueDirectFixed(rl_src, rMIPS_ARG0); 249 250 // Must prevent code motion for the curr pc pair 251 GenBarrier(); 252 NewLIR0(kMipsCurrPC); // Really a jal to .+8 253 // Now, fill the branch delay slot with the helper load 254 int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pHandleFillArrayDataFromCode)); 255 GenBarrier(); // Scheduling barrier 256 257 // Construct BaseLabel and set up table base register 258 LIR* base_label = NewLIR0(kPseudoTargetLabel); 259 260 // Materialize a pointer to the fill data image 261 NewLIR4(kMipsDelta, rMIPS_ARG1, 0, reinterpret_cast<uintptr_t>(base_label), 262 reinterpret_cast<uintptr_t>(tab_rec)); 263 264 // And go... 265 ClobberCalleeSave(); 266 LIR* call_inst = OpReg(kOpBlx, r_tgt); // ( array*, fill_data* ) 267 MarkSafepointPC(call_inst); 268} 269 270/* 271 * TODO: implement fast path to short-circuit thin-lock case 272 */ 273void MipsMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) 274{ 275 FlushAllRegs(); 276 LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj 277 LockCallTemps(); // Prepare for explicit register usage 278 GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags); 279 // Go expensive route - artLockObjectFromCode(self, obj); 280 int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pLockObjectFromCode)); 281 ClobberCalleeSave(); 282 LIR* call_inst = OpReg(kOpBlx, r_tgt); 283 MarkSafepointPC(call_inst); 284} 285 286/* 287 * TODO: implement fast path to short-circuit thin-lock case 288 */ 289void MipsMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) 290{ 291 FlushAllRegs(); 292 LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj 293 LockCallTemps(); // Prepare for explicit register usage 294 GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags); 295 // Go expensive route - UnlockObjectFromCode(obj); 296 int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pUnlockObjectFromCode)); 297 ClobberCalleeSave(); 298 LIR* call_inst = OpReg(kOpBlx, r_tgt); 299 MarkSafepointPC(call_inst); 300} 301 302void MipsMir2Lir::GenMoveException(RegLocation rl_dest) 303{ 304 int ex_offset = Thread::ExceptionOffset().Int32Value(); 305 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 306 int reset_reg = AllocTemp(); 307 LoadWordDisp(rMIPS_SELF, ex_offset, rl_result.low_reg); 308 LoadConstant(reset_reg, 0); 309 StoreWordDisp(rMIPS_SELF, ex_offset, reset_reg); 310 FreeTemp(reset_reg); 311 StoreValue(rl_dest, rl_result); 312} 313 314/* 315 * Mark garbage collection card. Skip if the value we're storing is null. 316 */ 317void MipsMir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg) 318{ 319 int reg_card_base = AllocTemp(); 320 int reg_card_no = AllocTemp(); 321 LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); 322 LoadWordDisp(rMIPS_SELF, Thread::CardTableOffset().Int32Value(), reg_card_base); 323 OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); 324 StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, 325 kUnsignedByte); 326 LIR* target = NewLIR0(kPseudoTargetLabel); 327 branch_over->target = target; 328 FreeTemp(reg_card_base); 329 FreeTemp(reg_card_no); 330} 331void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) 332{ 333 int spill_count = num_core_spills_ + num_fp_spills_; 334 /* 335 * On entry, rMIPS_ARG0, rMIPS_ARG1, rMIPS_ARG2 & rMIPS_ARG3 are live. Let the register 336 * allocation mechanism know so it doesn't try to use any of them when 337 * expanding the frame or flushing. This leaves the utility 338 * code with a single temp: r12. This should be enough. 339 */ 340 LockTemp(rMIPS_ARG0); 341 LockTemp(rMIPS_ARG1); 342 LockTemp(rMIPS_ARG2); 343 LockTemp(rMIPS_ARG3); 344 345 /* 346 * We can safely skip the stack overflow check if we're 347 * a leaf *and* our frame size < fudge factor. 348 */ 349 bool skip_overflow_check = (mir_graph_->MethodIsLeaf() && 350 (static_cast<size_t>(frame_size_) < Thread::kStackOverflowReservedBytes)); 351 NewLIR0(kPseudoMethodEntry); 352 int check_reg = AllocTemp(); 353 int new_sp = AllocTemp(); 354 if (!skip_overflow_check) { 355 /* Load stack limit */ 356 LoadWordDisp(rMIPS_SELF, Thread::StackEndOffset().Int32Value(), check_reg); 357 } 358 /* Spill core callee saves */ 359 SpillCoreRegs(); 360 /* NOTE: promotion of FP regs currently unsupported, thus no FP spill */ 361 DCHECK_EQ(num_fp_spills_, 0); 362 if (!skip_overflow_check) { 363 OpRegRegImm(kOpSub, new_sp, rMIPS_SP, frame_size_ - (spill_count * 4)); 364 GenRegRegCheck(kCondCc, new_sp, check_reg, kThrowStackOverflow); 365 OpRegCopy(rMIPS_SP, new_sp); // Establish stack 366 } else { 367 OpRegImm(kOpSub, rMIPS_SP, frame_size_ - (spill_count * 4)); 368 } 369 370 FlushIns(ArgLocs, rl_method); 371 372 FreeTemp(rMIPS_ARG0); 373 FreeTemp(rMIPS_ARG1); 374 FreeTemp(rMIPS_ARG2); 375 FreeTemp(rMIPS_ARG3); 376} 377 378void MipsMir2Lir::GenExitSequence() 379{ 380 /* 381 * In the exit path, rMIPS_RET0/rMIPS_RET1 are live - make sure they aren't 382 * allocated by the register utilities as temps. 383 */ 384 LockTemp(rMIPS_RET0); 385 LockTemp(rMIPS_RET1); 386 387 NewLIR0(kPseudoMethodExit); 388 UnSpillCoreRegs(); 389 OpReg(kOpBx, r_RA); 390} 391 392} // namespace art 393