call_x86.cc revision 83cc7ae96d4176533dd0391a1591d321b0a87f4f
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.GetReg(), key, 44 &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 int 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 rl_method = LoadValue(rl_method, kCoreReg); 90 start_of_method_reg = rl_method.reg.GetReg(); 91 store_method_addr_used_ = true; 92 } else { 93 start_of_method_reg = AllocTemp(); 94 NewLIR1(kX86StartOfMethod, start_of_method_reg); 95 } 96 int low_key = s4FromSwitchData(&table[2]); 97 int keyReg; 98 // Remove the bias, if necessary 99 if (low_key == 0) { 100 keyReg = rl_src.reg.GetReg(); 101 } else { 102 keyReg = AllocTemp(); 103 OpRegRegImm(kOpSub, keyReg, rl_src.reg.GetReg(), low_key); 104 } 105 // Bounds check - if < 0 or >= size continue following switch 106 OpRegImm(kOpCmp, keyReg, size-1); 107 LIR* branch_over = OpCondBranch(kCondHi, NULL); 108 109 // Load the displacement from the switch table 110 int disp_reg = AllocTemp(); 111 NewLIR5(kX86PcRelLoadRA, disp_reg, start_of_method_reg, keyReg, 2, WrapPointer(tab_rec)); 112 // Add displacement to start of method 113 OpRegReg(kOpAdd, start_of_method_reg, disp_reg); 114 // ..and go! 115 LIR* switch_branch = NewLIR1(kX86JmpR, start_of_method_reg); 116 tab_rec->anchor = switch_branch; 117 118 /* branch_over target here */ 119 LIR* target = NewLIR0(kPseudoTargetLabel); 120 branch_over->target = target; 121} 122 123/* 124 * Array data table format: 125 * ushort ident = 0x0300 magic value 126 * ushort width width of each element in the table 127 * uint size number of elements in the table 128 * ubyte data[size*width] table of data values (may contain a single-byte 129 * padding at the end) 130 * 131 * Total size is 4+(width * size + 1)/2 16-bit code units. 132 */ 133void X86Mir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) { 134 const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; 135 // Add the table to the list - we'll process it later 136 FillArrayData* tab_rec = 137 static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), kArenaAllocData)); 138 tab_rec->table = table; 139 tab_rec->vaddr = current_dalvik_offset_; 140 uint16_t width = tab_rec->table[1]; 141 uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16); 142 tab_rec->size = (size * width) + 8; 143 144 fill_array_data_.Insert(tab_rec); 145 146 // Making a call - use explicit registers 147 FlushAllRegs(); /* Everything to home location */ 148 LoadValueDirectFixed(rl_src, rX86_ARG0); 149 // Materialize a pointer to the fill data image 150 if (base_of_code_ != nullptr) { 151 // We can use the saved value. 152 RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low); 153 LoadValueDirect(rl_method, rX86_ARG2); 154 store_method_addr_used_ = true; 155 } else { 156 NewLIR1(kX86StartOfMethod, rX86_ARG2); 157 } 158 NewLIR2(kX86PcRelAdr, rX86_ARG1, WrapPointer(tab_rec)); 159 NewLIR2(kX86Add32RR, rX86_ARG1, rX86_ARG2); 160 CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pHandleFillArrayData), rX86_ARG0, 161 rX86_ARG1, true); 162} 163 164void X86Mir2Lir::GenMoveException(RegLocation rl_dest) { 165 int ex_offset = Thread::ExceptionOffset().Int32Value(); 166 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 167 NewLIR2(kX86Mov32RT, rl_result.reg.GetReg(), ex_offset); 168 NewLIR2(kX86Mov32TI, ex_offset, 0); 169 StoreValue(rl_dest, rl_result); 170} 171 172/* 173 * Mark garbage collection card. Skip if the value we're storing is null. 174 */ 175void X86Mir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg) { 176 int reg_card_base = AllocTemp(); 177 int reg_card_no = AllocTemp(); 178 LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); 179 NewLIR2(kX86Mov32RT, reg_card_base, Thread::CardTableOffset().Int32Value()); 180 OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); 181 StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, 182 kUnsignedByte); 183 LIR* target = NewLIR0(kPseudoTargetLabel); 184 branch_over->target = target; 185 FreeTemp(reg_card_base); 186 FreeTemp(reg_card_no); 187} 188 189void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { 190 /* 191 * On entry, rX86_ARG0, rX86_ARG1, rX86_ARG2 are live. Let the register 192 * allocation mechanism know so it doesn't try to use any of them when 193 * expanding the frame or flushing. This leaves the utility 194 * code with no spare temps. 195 */ 196 LockTemp(rX86_ARG0); 197 LockTemp(rX86_ARG1); 198 LockTemp(rX86_ARG2); 199 200 /* Build frame, return address already on stack */ 201 stack_decrement_ = OpRegImm(kOpSub, rX86_SP, frame_size_ - 4); 202 203 /* 204 * We can safely skip the stack overflow check if we're 205 * a leaf *and* our frame size < fudge factor. 206 */ 207 bool skip_overflow_check = (mir_graph_->MethodIsLeaf() && 208 (static_cast<size_t>(frame_size_) < 209 Thread::kStackOverflowReservedBytes)); 210 NewLIR0(kPseudoMethodEntry); 211 /* Spill core callee saves */ 212 SpillCoreRegs(); 213 /* NOTE: promotion of FP regs currently unsupported, thus no FP spill */ 214 DCHECK_EQ(num_fp_spills_, 0); 215 if (!skip_overflow_check) { 216 // cmp rX86_SP, fs:[stack_end_]; jcc throw_launchpad 217 LIR* tgt = RawLIR(0, kPseudoThrowTarget, kThrowStackOverflow, 0, 0, 0, 0); 218 OpRegThreadMem(kOpCmp, rX86_SP, Thread::StackEndOffset()); 219 OpCondBranch(kCondUlt, tgt); 220 // Remember branch target - will process later 221 throw_launchpads_.Insert(tgt); 222 } 223 224 FlushIns(ArgLocs, rl_method); 225 226 if (base_of_code_ != nullptr) { 227 // We have been asked to save the address of the method start for later use. 228 setup_method_address_[0] = NewLIR1(kX86StartOfMethod, rX86_ARG0); 229 int displacement = SRegOffset(base_of_code_->s_reg_low); 230 setup_method_address_[1] = StoreBaseDisp(rX86_SP, displacement, rX86_ARG0, kWord); 231 } 232 233 FreeTemp(rX86_ARG0); 234 FreeTemp(rX86_ARG1); 235 FreeTemp(rX86_ARG2); 236} 237 238void X86Mir2Lir::GenExitSequence() { 239 /* 240 * In the exit path, rX86_RET0/rX86_RET1 are live - make sure they aren't 241 * allocated by the register utilities as temps. 242 */ 243 LockTemp(rX86_RET0); 244 LockTemp(rX86_RET1); 245 246 NewLIR0(kPseudoMethodExit); 247 UnSpillCoreRegs(); 248 /* Remove frame except for return address */ 249 stack_increment_ = OpRegImm(kOpAdd, rX86_SP, frame_size_ - 4); 250 NewLIR0(kX86Ret); 251} 252 253void X86Mir2Lir::GenSpecialExitSequence() { 254 NewLIR0(kX86Ret); 255} 256 257} // namespace art 258