call_x86.cc revision 984305917bf57b3f8d92965e4715a0370cc5bcfb
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 X86 ISA */ 18 19#include "codegen_x86.h" 20#include "dex/quick/mir_to_lir-inl.h" 21#include "gc/accounting/card_table.h" 22#include "x86_lir.h" 23 24namespace art { 25 26/* 27 * The sparse table in the literal pool is an array of <key,displacement> 28 * pairs. 29 */ 30void X86Mir2Lir::GenSparseSwitch(MIR* mir, DexOffset table_offset, 31 RegLocation rl_src) { 32 const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; 33 if (cu_->verbose) { 34 DumpSparseSwitchTable(table); 35 } 36 int entries = table[1]; 37 const int32_t* keys = reinterpret_cast<const int32_t*>(&table[2]); 38 const int32_t* targets = &keys[entries]; 39 rl_src = LoadValue(rl_src, kCoreReg); 40 for (int i = 0; i < entries; i++) { 41 int key = keys[i]; 42 BasicBlock* case_block = 43 mir_graph_->FindBlock(current_dalvik_offset_ + targets[i]); 44 OpCmpImmBranch(kCondEq, rl_src.reg, key, &block_label_list_[case_block->id]); 45 } 46} 47 48/* 49 * Code pattern will look something like: 50 * 51 * mov r_val, .. 52 * call 0 53 * pop r_start_of_method 54 * sub r_start_of_method, .. 55 * mov r_key_reg, r_val 56 * sub r_key_reg, low_key 57 * cmp r_key_reg, size-1 ; bound check 58 * ja done 59 * mov r_disp, [r_start_of_method + r_key_reg * 4 + table_offset] 60 * add r_start_of_method, r_disp 61 * jmp r_start_of_method 62 * done: 63 */ 64void X86Mir2Lir::GenPackedSwitch(MIR* mir, DexOffset table_offset, 65 RegLocation rl_src) { 66 const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; 67 if (cu_->verbose) { 68 DumpPackedSwitchTable(table); 69 } 70 // Add the table to the list - we'll process it later 71 SwitchTable* tab_rec = 72 static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData)); 73 tab_rec->table = table; 74 tab_rec->vaddr = current_dalvik_offset_; 75 int size = table[1]; 76 tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), 77 kArenaAllocLIR)); 78 switch_tables_.Insert(tab_rec); 79 80 // Get the switch value 81 rl_src = LoadValue(rl_src, kCoreReg); 82 // NewLIR0(kX86Bkpt); 83 84 // Materialize a pointer to the switch table 85 RegStorage start_of_method_reg; 86 if (base_of_code_ != nullptr) { 87 // We can use the saved value. 88 RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low); 89 if (rl_method.wide) { 90 rl_method = LoadValueWide(rl_method, kCoreReg); 91 } else { 92 rl_method = LoadValue(rl_method, kCoreReg); 93 } 94 start_of_method_reg = rl_method.reg; 95 store_method_addr_used_ = true; 96 } else { 97 start_of_method_reg = AllocTempRef(); 98 NewLIR1(kX86StartOfMethod, start_of_method_reg.GetReg()); 99 } 100 DCHECK_EQ(start_of_method_reg.Is64Bit(), cu_->target64); 101 int low_key = s4FromSwitchData(&table[2]); 102 RegStorage keyReg; 103 // Remove the bias, if necessary 104 if (low_key == 0) { 105 keyReg = rl_src.reg; 106 } else { 107 keyReg = AllocTemp(); 108 OpRegRegImm(kOpSub, keyReg, rl_src.reg, low_key); 109 } 110 // Bounds check - if < 0 or >= size continue following switch 111 OpRegImm(kOpCmp, keyReg, size - 1); 112 LIR* branch_over = OpCondBranch(kCondHi, NULL); 113 114 // Load the displacement from the switch table 115 RegStorage disp_reg = AllocTemp(); 116 NewLIR5(kX86PcRelLoadRA, disp_reg.GetReg(), start_of_method_reg.GetReg(), keyReg.GetReg(), 117 2, WrapPointer(tab_rec)); 118 // Add displacement to start of method 119 OpRegReg(kOpAdd, start_of_method_reg, cu_->target64 ? As64BitReg(disp_reg) : disp_reg); 120 // ..and go! 121 LIR* switch_branch = NewLIR1(kX86JmpR, start_of_method_reg.GetReg()); 122 tab_rec->anchor = switch_branch; 123 124 /* branch_over target here */ 125 LIR* target = NewLIR0(kPseudoTargetLabel); 126 branch_over->target = target; 127} 128 129/* 130 * Array data table format: 131 * ushort ident = 0x0300 magic value 132 * ushort width width of each element in the table 133 * uint size number of elements in the table 134 * ubyte data[size*width] table of data values (may contain a single-byte 135 * padding at the end) 136 * 137 * Total size is 4+(width * size + 1)/2 16-bit code units. 138 */ 139void X86Mir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) { 140 const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; 141 // Add the table to the list - we'll process it later 142 FillArrayData* tab_rec = 143 static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), kArenaAllocData)); 144 tab_rec->table = table; 145 tab_rec->vaddr = current_dalvik_offset_; 146 uint16_t width = tab_rec->table[1]; 147 uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16); 148 tab_rec->size = (size * width) + 8; 149 150 fill_array_data_.Insert(tab_rec); 151 152 // Making a call - use explicit registers 153 FlushAllRegs(); /* Everything to home location */ 154 RegStorage array_ptr = TargetReg(kArg0, kRef); 155 RegStorage payload = TargetPtrReg(kArg1); 156 RegStorage method_start = TargetPtrReg(kArg2); 157 158 LoadValueDirectFixed(rl_src, array_ptr); 159 // Materialize a pointer to the fill data image 160 if (base_of_code_ != nullptr) { 161 // We can use the saved value. 162 RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low); 163 if (rl_method.wide) { 164 LoadValueDirectWide(rl_method, method_start); 165 } else { 166 LoadValueDirect(rl_method, method_start); 167 } 168 store_method_addr_used_ = true; 169 } else { 170 NewLIR1(kX86StartOfMethod, method_start.GetReg()); 171 } 172 NewLIR2(kX86PcRelAdr, payload.GetReg(), WrapPointer(tab_rec)); 173 OpRegReg(kOpAdd, payload, method_start); 174 CallRuntimeHelperRegReg(kQuickHandleFillArrayData, array_ptr, payload, true); 175} 176 177void X86Mir2Lir::GenMoveException(RegLocation rl_dest) { 178 int ex_offset = cu_->target64 ? 179 Thread::ExceptionOffset<8>().Int32Value() : 180 Thread::ExceptionOffset<4>().Int32Value(); 181 RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true); 182 NewLIR2(cu_->target64 ? kX86Mov64RT : kX86Mov32RT, rl_result.reg.GetReg(), ex_offset); 183 NewLIR2(cu_->target64 ? kX86Mov64TI : kX86Mov32TI, ex_offset, 0); 184 StoreValue(rl_dest, rl_result); 185} 186 187/* 188 * Mark garbage collection card. Skip if the value we're storing is null. 189 */ 190void X86Mir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) { 191 DCHECK_EQ(tgt_addr_reg.Is64Bit(), cu_->target64); 192 DCHECK_EQ(val_reg.Is64Bit(), cu_->target64); 193 RegStorage reg_card_base = AllocTempRef(); 194 RegStorage reg_card_no = AllocTempRef(); 195 LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); 196 int ct_offset = cu_->target64 ? 197 Thread::CardTableOffset<8>().Int32Value() : 198 Thread::CardTableOffset<4>().Int32Value(); 199 NewLIR2(cu_->target64 ? kX86Mov64RT : kX86Mov32RT, reg_card_base.GetReg(), ct_offset); 200 OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); 201 StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte); 202 LIR* target = NewLIR0(kPseudoTargetLabel); 203 branch_over->target = target; 204 FreeTemp(reg_card_base); 205 FreeTemp(reg_card_no); 206} 207 208void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { 209 /* 210 * On entry, rX86_ARG0, rX86_ARG1, rX86_ARG2 are live. Let the register 211 * allocation mechanism know so it doesn't try to use any of them when 212 * expanding the frame or flushing. This leaves the utility 213 * code with no spare temps. 214 */ 215 LockTemp(rs_rX86_ARG0); 216 LockTemp(rs_rX86_ARG1); 217 LockTemp(rs_rX86_ARG2); 218 219 /* 220 * We can safely skip the stack overflow check if we're 221 * a leaf *and* our frame size < fudge factor. 222 */ 223 InstructionSet isa = cu_->target64 ? kX86_64 : kX86; 224 const bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, isa); 225 226 // If we doing an implicit stack overflow check, perform the load immediately 227 // before the stack pointer is decremented and anything is saved. 228 if (!skip_overflow_check && 229 cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { 230 // Implicit stack overflow check. 231 // test eax,[esp + -overflow] 232 int overflow = GetStackOverflowReservedBytes(isa); 233 NewLIR3(kX86Test32RM, rs_rAX.GetReg(), rs_rX86_SP.GetReg(), -overflow); 234 MarkPossibleStackOverflowException(); 235 } 236 237 /* Build frame, return address already on stack */ 238 stack_decrement_ = OpRegImm(kOpSub, rs_rX86_SP, frame_size_ - 239 GetInstructionSetPointerSize(cu_->instruction_set)); 240 241 NewLIR0(kPseudoMethodEntry); 242 /* Spill core callee saves */ 243 SpillCoreRegs(); 244 SpillFPRegs(); 245 if (!skip_overflow_check) { 246 class StackOverflowSlowPath : public LIRSlowPath { 247 public: 248 StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace) 249 : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch, nullptr), sp_displace_(sp_displace) { 250 } 251 void Compile() OVERRIDE { 252 m2l_->ResetRegPool(); 253 m2l_->ResetDefTracking(); 254 GenerateTargetLabel(kPseudoThrowTarget); 255 m2l_->OpRegImm(kOpAdd, rs_rX86_SP, sp_displace_); 256 m2l_->ClobberCallerSave(); 257 // Assumes codegen and target are in thumb2 mode. 258 m2l_->CallHelper(RegStorage::InvalidReg(), kQuickThrowStackOverflow, 259 false /* MarkSafepointPC */, false /* UseLink */); 260 } 261 262 private: 263 const size_t sp_displace_; 264 }; 265 if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { 266 // TODO: for large frames we should do something like: 267 // spill ebp 268 // lea ebp, [esp + frame_size] 269 // cmp ebp, fs:[stack_end_] 270 // jcc stack_overflow_exception 271 // mov esp, ebp 272 // in case a signal comes in that's not using an alternate signal stack and the large frame 273 // may have moved us outside of the reserved area at the end of the stack. 274 // cmp rs_rX86_SP, fs:[stack_end_]; jcc throw_slowpath 275 if (cu_->target64) { 276 OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<8>()); 277 } else { 278 OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<4>()); 279 } 280 LIR* branch = OpCondBranch(kCondUlt, nullptr); 281 AddSlowPath( 282 new(arena_)StackOverflowSlowPath(this, branch, 283 frame_size_ - 284 GetInstructionSetPointerSize(cu_->instruction_set))); 285 } 286 } 287 288 FlushIns(ArgLocs, rl_method); 289 290 if (base_of_code_ != nullptr) { 291 RegStorage method_start = TargetPtrReg(kArg0); 292 // We have been asked to save the address of the method start for later use. 293 setup_method_address_[0] = NewLIR1(kX86StartOfMethod, method_start.GetReg()); 294 int displacement = SRegOffset(base_of_code_->s_reg_low); 295 // Native pointer - must be natural word size. 296 setup_method_address_[1] = StoreBaseDisp(rs_rX86_SP, displacement, method_start, 297 cu_->target64 ? k64 : k32, kNotVolatile); 298 } 299 300 FreeTemp(rs_rX86_ARG0); 301 FreeTemp(rs_rX86_ARG1); 302 FreeTemp(rs_rX86_ARG2); 303} 304 305void X86Mir2Lir::GenExitSequence() { 306 /* 307 * In the exit path, rX86_RET0/rX86_RET1 are live - make sure they aren't 308 * allocated by the register utilities as temps. 309 */ 310 LockTemp(rs_rX86_RET0); 311 LockTemp(rs_rX86_RET1); 312 313 NewLIR0(kPseudoMethodExit); 314 UnSpillCoreRegs(); 315 UnSpillFPRegs(); 316 /* Remove frame except for return address */ 317 stack_increment_ = OpRegImm(kOpAdd, rs_rX86_SP, frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set)); 318 NewLIR0(kX86Ret); 319} 320 321void X86Mir2Lir::GenSpecialExitSequence() { 322 NewLIR0(kX86Ret); 323} 324 325void X86Mir2Lir::GenImplicitNullCheck(RegStorage reg, int opt_flags) { 326 if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { 327 return; 328 } 329 // Implicit null pointer check. 330 // test eax,[arg1+0] 331 NewLIR3(kX86Test32RM, rs_rAX.GetReg(), reg.GetReg(), 0); 332 MarkPossibleNullPointerException(opt_flags); 333} 334 335} // namespace art 336