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