gen_loadstore.cc revision cbd18b7f4da7677ca48c48c23ed9a7de3a8b0354
1/* 2 * Copyright (C) 2011 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#include "dex/compiler_ir.h" 18#include "dex/compiler_internals.h" 19#include "dex/quick/mir_to_lir-inl.h" 20#include "invoke_type.h" 21 22namespace art { 23 24/* This file contains target-independent codegen and support. */ 25 26/* 27 * Load an immediate value into a fixed or temp register. Target 28 * register is clobbered, and marked in_use. 29 */ 30LIR* Mir2Lir::LoadConstant(RegStorage r_dest, int value) { 31 if (IsTemp(r_dest)) { 32 Clobber(r_dest); 33 MarkInUse(r_dest); 34 } 35 return LoadConstantNoClobber(r_dest, value); 36} 37 38/* 39 * Temporary workaround for Issue 7250540. If we're loading a constant zero into a 40 * promoted floating point register, also copy a zero into the int/ref identity of 41 * that sreg. 42 */ 43void Mir2Lir::Workaround7250540(RegLocation rl_dest, RegStorage zero_reg) { 44 if (rl_dest.fp) { 45 int pmap_index = SRegToPMap(rl_dest.s_reg_low); 46 if (promotion_map_[pmap_index].fp_location == kLocPhysReg) { 47 // Now, determine if this vreg is ever used as a reference. If not, we're done. 48 bool used_as_reference = false; 49 int base_vreg = mir_graph_->SRegToVReg(rl_dest.s_reg_low); 50 for (int i = 0; !used_as_reference && (i < mir_graph_->GetNumSSARegs()); i++) { 51 if (mir_graph_->SRegToVReg(mir_graph_->reg_location_[i].s_reg_low) == base_vreg) { 52 used_as_reference |= mir_graph_->reg_location_[i].ref; 53 } 54 } 55 if (!used_as_reference) { 56 return; 57 } 58 RegStorage temp_reg = zero_reg; 59 if (!temp_reg.Valid()) { 60 temp_reg = AllocTemp(); 61 LoadConstant(temp_reg, 0); 62 } 63 if (promotion_map_[pmap_index].core_location == kLocPhysReg) { 64 // Promoted - just copy in a zero 65 OpRegCopy(RegStorage::Solo32(promotion_map_[pmap_index].core_reg), temp_reg); 66 } else { 67 // Lives in the frame, need to store. 68 StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), temp_reg, kWord); 69 } 70 if (!zero_reg.Valid()) { 71 FreeTemp(temp_reg); 72 } 73 } 74 } 75} 76 77/* Load a word at base + displacement. Displacement must be word multiple */ 78LIR* Mir2Lir::LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) { 79 return LoadBaseDisp(r_base, displacement, r_dest, kWord, INVALID_SREG); 80} 81 82LIR* Mir2Lir::StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src) { 83 return StoreBaseDisp(r_base, displacement, r_src, kWord); 84} 85 86/* 87 * Load a Dalvik register into a physical register. Take care when 88 * using this routine, as it doesn't perform any bookkeeping regarding 89 * register liveness. That is the responsibility of the caller. 90 */ 91void Mir2Lir::LoadValueDirect(RegLocation rl_src, RegStorage r_dest) { 92 rl_src = UpdateLoc(rl_src); 93 if (rl_src.location == kLocPhysReg) { 94 OpRegCopy(r_dest, rl_src.reg); 95 } else if (IsInexpensiveConstant(rl_src)) { 96 LoadConstantNoClobber(r_dest, mir_graph_->ConstantValue(rl_src)); 97 } else { 98 DCHECK((rl_src.location == kLocDalvikFrame) || 99 (rl_src.location == kLocCompilerTemp)); 100 LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest); 101 } 102} 103 104/* 105 * Similar to LoadValueDirect, but clobbers and allocates the target 106 * register. Should be used when loading to a fixed register (for example, 107 * loading arguments to an out of line call. 108 */ 109void Mir2Lir::LoadValueDirectFixed(RegLocation rl_src, RegStorage r_dest) { 110 Clobber(r_dest); 111 MarkInUse(r_dest); 112 LoadValueDirect(rl_src, r_dest); 113} 114 115/* 116 * Load a Dalvik register pair into a physical register[s]. Take care when 117 * using this routine, as it doesn't perform any bookkeeping regarding 118 * register liveness. That is the responsibility of the caller. 119 */ 120void Mir2Lir::LoadValueDirectWide(RegLocation rl_src, RegStorage r_dest) { 121 rl_src = UpdateLocWide(rl_src); 122 if (rl_src.location == kLocPhysReg) { 123 OpRegCopyWide(r_dest, rl_src.reg); 124 } else if (IsInexpensiveConstant(rl_src)) { 125 LoadConstantWide(r_dest, mir_graph_->ConstantValueWide(rl_src)); 126 } else { 127 DCHECK((rl_src.location == kLocDalvikFrame) || 128 (rl_src.location == kLocCompilerTemp)); 129 LoadBaseDispWide(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest, INVALID_SREG); 130 } 131} 132 133/* 134 * Similar to LoadValueDirect, but clobbers and allocates the target 135 * registers. Should be used when loading to a fixed registers (for example, 136 * loading arguments to an out of line call. 137 */ 138void Mir2Lir::LoadValueDirectWideFixed(RegLocation rl_src, RegStorage r_dest) { 139 Clobber(r_dest); 140 MarkInUse(r_dest); 141 LoadValueDirectWide(rl_src, r_dest); 142} 143 144RegLocation Mir2Lir::LoadValue(RegLocation rl_src, RegisterClass op_kind) { 145 rl_src = EvalLoc(rl_src, op_kind, false); 146 if (IsInexpensiveConstant(rl_src) || rl_src.location != kLocPhysReg) { 147 LoadValueDirect(rl_src, rl_src.reg); 148 rl_src.location = kLocPhysReg; 149 MarkLive(rl_src.reg, rl_src.s_reg_low); 150 } 151 return rl_src; 152} 153 154void Mir2Lir::StoreValue(RegLocation rl_dest, RegLocation rl_src) { 155 /* 156 * Sanity checking - should never try to store to the same 157 * ssa name during the compilation of a single instruction 158 * without an intervening ClobberSReg(). 159 */ 160 if (kIsDebugBuild) { 161 DCHECK((live_sreg_ == INVALID_SREG) || 162 (rl_dest.s_reg_low != live_sreg_)); 163 live_sreg_ = rl_dest.s_reg_low; 164 } 165 LIR* def_start; 166 LIR* def_end; 167 DCHECK(!rl_dest.wide); 168 DCHECK(!rl_src.wide); 169 rl_src = UpdateLoc(rl_src); 170 rl_dest = UpdateLoc(rl_dest); 171 if (rl_src.location == kLocPhysReg) { 172 if (IsLive(rl_src.reg) || 173 IsPromoted(rl_src.reg) || 174 (rl_dest.location == kLocPhysReg)) { 175 // Src is live/promoted or Dest has assigned reg. 176 rl_dest = EvalLoc(rl_dest, kAnyReg, false); 177 OpRegCopy(rl_dest.reg, rl_src.reg); 178 } else { 179 // Just re-assign the registers. Dest gets Src's regs 180 rl_dest.reg = rl_src.reg; 181 Clobber(rl_src.reg); 182 } 183 } else { 184 // Load Src either into promoted Dest or temps allocated for Dest 185 rl_dest = EvalLoc(rl_dest, kAnyReg, false); 186 LoadValueDirect(rl_src, rl_dest.reg); 187 } 188 189 // Dest is now live and dirty (until/if we flush it to home location) 190 MarkLive(rl_dest.reg, rl_dest.s_reg_low); 191 MarkDirty(rl_dest); 192 193 194 ResetDefLoc(rl_dest); 195 if (IsDirty(rl_dest.reg) && oat_live_out(rl_dest.s_reg_low)) { 196 def_start = last_lir_insn_; 197 StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, kWord); 198 MarkClean(rl_dest); 199 def_end = last_lir_insn_; 200 if (!rl_dest.ref) { 201 // Exclude references from store elimination 202 MarkDef(rl_dest, def_start, def_end); 203 } 204 } 205} 206 207RegLocation Mir2Lir::LoadValueWide(RegLocation rl_src, RegisterClass op_kind) { 208 DCHECK(rl_src.wide); 209 rl_src = EvalLoc(rl_src, op_kind, false); 210 if (IsInexpensiveConstant(rl_src) || rl_src.location != kLocPhysReg) { 211 LoadValueDirectWide(rl_src, rl_src.reg); 212 rl_src.location = kLocPhysReg; 213 MarkLive(rl_src.reg.GetLow(), rl_src.s_reg_low); 214 if (rl_src.reg.GetLowReg() != rl_src.reg.GetHighReg()) { 215 MarkLive(rl_src.reg.GetHigh(), GetSRegHi(rl_src.s_reg_low)); 216 } else { 217 // This must be an x86 vector register value. 218 DCHECK(IsFpReg(rl_src.reg) && (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64)); 219 } 220 } 221 return rl_src; 222} 223 224void Mir2Lir::StoreValueWide(RegLocation rl_dest, RegLocation rl_src) { 225 /* 226 * Sanity checking - should never try to store to the same 227 * ssa name during the compilation of a single instruction 228 * without an intervening ClobberSReg(). 229 */ 230 if (kIsDebugBuild) { 231 DCHECK((live_sreg_ == INVALID_SREG) || 232 (rl_dest.s_reg_low != live_sreg_)); 233 live_sreg_ = rl_dest.s_reg_low; 234 } 235 LIR* def_start; 236 LIR* def_end; 237 DCHECK(rl_dest.wide); 238 DCHECK(rl_src.wide); 239 rl_src = UpdateLocWide(rl_src); 240 rl_dest = UpdateLocWide(rl_dest); 241 if (rl_src.location == kLocPhysReg) { 242 if (IsLive(rl_src.reg) || 243 IsPromoted(rl_src.reg) || 244 (rl_dest.location == kLocPhysReg)) { 245 // Src is live or promoted or Dest has assigned reg. 246 rl_dest = EvalLoc(rl_dest, kAnyReg, false); 247 OpRegCopyWide(rl_dest.reg, rl_src.reg); 248 } else { 249 // Just re-assign the registers. Dest gets Src's regs 250 rl_dest.reg = rl_src.reg; 251 Clobber(rl_src.reg); 252 } 253 } else { 254 // Load Src either into promoted Dest or temps allocated for Dest 255 rl_dest = EvalLoc(rl_dest, kAnyReg, false); 256 LoadValueDirectWide(rl_src, rl_dest.reg); 257 } 258 259 // Dest is now live and dirty (until/if we flush it to home location) 260 MarkLive(rl_dest.reg.GetLow(), rl_dest.s_reg_low); 261 262 // Does this wide value live in two registers (or one vector one)? 263 // FIXME: wide reg update. 264 if (rl_dest.reg.GetLowReg() != rl_dest.reg.GetHighReg()) { 265 MarkLive(rl_dest.reg.GetHigh(), GetSRegHi(rl_dest.s_reg_low)); 266 MarkDirty(rl_dest); 267 MarkPair(rl_dest.reg.GetLowReg(), rl_dest.reg.GetHighReg()); 268 } else { 269 // This must be an x86 vector register value, 270 DCHECK(IsFpReg(rl_dest.reg) && (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64)); 271 MarkDirty(rl_dest); 272 } 273 274 275 ResetDefLocWide(rl_dest); 276 if (IsDirty(rl_dest.reg) && (oat_live_out(rl_dest.s_reg_low) || 277 oat_live_out(GetSRegHi(rl_dest.s_reg_low)))) { 278 def_start = last_lir_insn_; 279 DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1), 280 mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low))); 281 StoreBaseDispWide(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg); 282 MarkClean(rl_dest); 283 def_end = last_lir_insn_; 284 MarkDefWide(rl_dest, def_start, def_end); 285 } 286} 287 288void Mir2Lir::StoreFinalValue(RegLocation rl_dest, RegLocation rl_src) { 289 DCHECK_EQ(rl_src.location, kLocPhysReg); 290 291 if (rl_dest.location == kLocPhysReg) { 292 OpRegCopy(rl_dest.reg, rl_src.reg); 293 } else { 294 // Just re-assign the register. Dest gets Src's reg. 295 rl_dest.location = kLocPhysReg; 296 rl_dest.reg = rl_src.reg; 297 Clobber(rl_src.reg); 298 } 299 300 // Dest is now live and dirty (until/if we flush it to home location) 301 MarkLive(rl_dest.reg, rl_dest.s_reg_low); 302 MarkDirty(rl_dest); 303 304 305 ResetDefLoc(rl_dest); 306 if (IsDirty(rl_dest.reg) && 307 oat_live_out(rl_dest.s_reg_low)) { 308 LIR *def_start = last_lir_insn_; 309 StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, kWord); 310 MarkClean(rl_dest); 311 LIR *def_end = last_lir_insn_; 312 if (!rl_dest.ref) { 313 // Exclude references from store elimination 314 MarkDef(rl_dest, def_start, def_end); 315 } 316 } 317} 318 319void Mir2Lir::StoreFinalValueWide(RegLocation rl_dest, RegLocation rl_src) { 320 DCHECK_EQ(IsFpReg(rl_src.reg.GetLowReg()), IsFpReg(rl_src.reg.GetHighReg())); 321 DCHECK(rl_dest.wide); 322 DCHECK(rl_src.wide); 323 DCHECK_EQ(rl_src.location, kLocPhysReg); 324 325 if (rl_dest.location == kLocPhysReg) { 326 OpRegCopyWide(rl_dest.reg, rl_src.reg); 327 } else { 328 // Just re-assign the registers. Dest gets Src's regs. 329 rl_dest.location = kLocPhysReg; 330 rl_dest.reg = rl_src.reg; 331 Clobber(rl_src.reg.GetLowReg()); 332 Clobber(rl_src.reg.GetHighReg()); 333 } 334 335 // Dest is now live and dirty (until/if we flush it to home location). 336 MarkLive(rl_dest.reg.GetLow(), rl_dest.s_reg_low); 337 338 // Does this wide value live in two registers (or one vector one)? 339 // FIXME: wide reg. 340 if (rl_dest.reg.GetLowReg() != rl_dest.reg.GetHighReg()) { 341 MarkLive(rl_dest.reg.GetHigh(), GetSRegHi(rl_dest.s_reg_low)); 342 MarkDirty(rl_dest); 343 MarkPair(rl_dest.reg.GetLowReg(), rl_dest.reg.GetHighReg()); 344 } else { 345 // This must be an x86 vector register value, 346 DCHECK(IsFpReg(rl_dest.reg) && (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64)); 347 MarkDirty(rl_dest); 348 } 349 350 ResetDefLocWide(rl_dest); 351 if (IsDirty(rl_dest.reg) && (oat_live_out(rl_dest.s_reg_low) || 352 oat_live_out(GetSRegHi(rl_dest.s_reg_low)))) { 353 LIR *def_start = last_lir_insn_; 354 DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1), 355 mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low))); 356 StoreBaseDispWide(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg); 357 MarkClean(rl_dest); 358 LIR *def_end = last_lir_insn_; 359 MarkDefWide(rl_dest, def_start, def_end); 360 } 361} 362 363/* Utilities to load the current Method* */ 364void Mir2Lir::LoadCurrMethodDirect(RegStorage r_tgt) { 365 LoadValueDirectFixed(mir_graph_->GetMethodLoc(), r_tgt); 366} 367 368RegLocation Mir2Lir::LoadCurrMethod() { 369 return LoadValue(mir_graph_->GetMethodLoc(), kCoreReg); 370} 371 372RegLocation Mir2Lir::ForceTemp(RegLocation loc) { 373 DCHECK(!loc.wide); 374 DCHECK(loc.location == kLocPhysReg); 375 DCHECK(!IsFpReg(loc.reg)); 376 if (IsTemp(loc.reg)) { 377 Clobber(loc.reg); 378 } else { 379 RegStorage temp_low = AllocTemp(); 380 OpRegCopy(temp_low, loc.reg); 381 loc.reg = temp_low; 382 } 383 384 // Ensure that this doesn't represent the original SR any more. 385 loc.s_reg_low = INVALID_SREG; 386 return loc; 387} 388 389// FIXME: wide regs. 390RegLocation Mir2Lir::ForceTempWide(RegLocation loc) { 391 DCHECK(loc.wide); 392 DCHECK(loc.location == kLocPhysReg); 393 DCHECK(!IsFpReg(loc.reg.GetLowReg())); 394 DCHECK(!IsFpReg(loc.reg.GetHighReg())); 395 if (IsTemp(loc.reg.GetLowReg())) { 396 Clobber(loc.reg.GetLowReg()); 397 } else { 398 RegStorage temp_low = AllocTemp(); 399 OpRegCopy(temp_low, loc.reg.GetLow()); 400 loc.reg.SetLowReg(temp_low.GetReg()); 401 } 402 if (IsTemp(loc.reg.GetHighReg())) { 403 Clobber(loc.reg.GetHighReg()); 404 } else { 405 RegStorage temp_high = AllocTemp(); 406 OpRegCopy(temp_high, loc.reg.GetHigh()); 407 loc.reg.SetHighReg(temp_high.GetReg()); 408 } 409 410 // Ensure that this doesn't represent the original SR any more. 411 loc.s_reg_low = INVALID_SREG; 412 return loc; 413} 414 415} // namespace art 416