| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "codegen_util.h" |
| #include "compiler/compiler_ir.h" |
| #include "ralloc_util.h" |
| |
| namespace art { |
| |
| /* This file contains target-independent codegen and support. */ |
| |
| /* |
| * Load an immediate value into a fixed or temp register. Target |
| * register is clobbered, and marked in_use. |
| */ |
| LIR* Codegen::LoadConstant(CompilationUnit* cu, int r_dest, int value) |
| { |
| if (IsTemp(cu, r_dest)) { |
| Clobber(cu, r_dest); |
| MarkInUse(cu, r_dest); |
| } |
| return LoadConstantNoClobber(cu, r_dest, value); |
| } |
| |
| /* |
| * Temporary workaround for Issue 7250540. If we're loading a constant zero into a |
| * promoted floating point register, also copy a zero into the int/ref identity of |
| * that sreg. |
| */ |
| void Codegen::Workaround7250540(CompilationUnit* cu, RegLocation rl_dest, int zero_reg) |
| { |
| if (rl_dest.fp) { |
| int pmap_index = SRegToPMap(cu, rl_dest.s_reg_low); |
| if (cu->promotion_map[pmap_index].fp_location == kLocPhysReg) { |
| // Now, determine if this vreg is ever used as a reference. If not, we're done. |
| if (!cu->gen_bitcode) { |
| // TUNING: We no longer have this info for QuickGBC - assume the worst |
| bool used_as_reference = false; |
| int base_vreg = SRegToVReg(cu, rl_dest.s_reg_low); |
| for (int i = 0; !used_as_reference && (i < cu->num_ssa_regs); i++) { |
| if (SRegToVReg(cu, cu->reg_location[i].s_reg_low) == base_vreg) { |
| used_as_reference |= cu->reg_location[i].ref; |
| } |
| } |
| if (!used_as_reference) { |
| return; |
| } |
| } |
| int temp_reg = zero_reg; |
| if (temp_reg == INVALID_REG) { |
| temp_reg = AllocTemp(cu); |
| cu->cg->LoadConstant(cu, temp_reg, 0); |
| } |
| if (cu->promotion_map[pmap_index].core_location == kLocPhysReg) { |
| // Promoted - just copy in a zero |
| OpRegCopy(cu, cu->promotion_map[pmap_index].core_reg, temp_reg); |
| } else { |
| // Lives in the frame, need to store. |
| StoreBaseDisp(cu, TargetReg(kSp), SRegOffset(cu, rl_dest.s_reg_low), temp_reg, kWord); |
| } |
| if (zero_reg == INVALID_REG) { |
| FreeTemp(cu, temp_reg); |
| } |
| } |
| } |
| } |
| |
| /* Load a word at base + displacement. Displacement must be word multiple */ |
| LIR* Codegen::LoadWordDisp(CompilationUnit* cu, int rBase, int displacement, int r_dest) |
| { |
| return LoadBaseDisp(cu, rBase, displacement, r_dest, kWord, |
| INVALID_SREG); |
| } |
| |
| LIR* Codegen::StoreWordDisp(CompilationUnit* cu, int rBase, int displacement, int r_src) |
| { |
| return StoreBaseDisp(cu, rBase, displacement, r_src, kWord); |
| } |
| |
| /* |
| * Load a Dalvik register into a physical register. Take care when |
| * using this routine, as it doesn't perform any bookkeeping regarding |
| * register liveness. That is the responsibility of the caller. |
| */ |
| void Codegen::LoadValueDirect(CompilationUnit* cu, RegLocation rl_src, int r_dest) |
| { |
| rl_src = UpdateLoc(cu, rl_src); |
| if (rl_src.location == kLocPhysReg) { |
| OpRegCopy(cu, r_dest, rl_src.low_reg); |
| } else if (IsInexpensiveConstant(cu, rl_src)) { |
| LoadConstantNoClobber(cu, r_dest, ConstantValue(cu, rl_src)); |
| } else { |
| DCHECK((rl_src.location == kLocDalvikFrame) || |
| (rl_src.location == kLocCompilerTemp)); |
| LoadWordDisp(cu, TargetReg(kSp), SRegOffset(cu, rl_src.s_reg_low), r_dest); |
| } |
| } |
| |
| /* |
| * Similar to LoadValueDirect, but clobbers and allocates the target |
| * register. Should be used when loading to a fixed register (for example, |
| * loading arguments to an out of line call. |
| */ |
| void Codegen::LoadValueDirectFixed(CompilationUnit* cu, RegLocation rl_src, int r_dest) |
| { |
| Clobber(cu, r_dest); |
| MarkInUse(cu, r_dest); |
| LoadValueDirect(cu, rl_src, r_dest); |
| } |
| |
| /* |
| * Load a Dalvik register pair into a physical register[s]. Take care when |
| * using this routine, as it doesn't perform any bookkeeping regarding |
| * register liveness. That is the responsibility of the caller. |
| */ |
| void Codegen::LoadValueDirectWide(CompilationUnit* cu, RegLocation rl_src, int reg_lo, |
| int reg_hi) |
| { |
| rl_src = UpdateLocWide(cu, rl_src); |
| if (rl_src.location == kLocPhysReg) { |
| OpRegCopyWide(cu, reg_lo, reg_hi, rl_src.low_reg, rl_src.high_reg); |
| } else if (IsInexpensiveConstant(cu, rl_src)) { |
| LoadConstantWide(cu, reg_lo, reg_hi, ConstantValueWide(cu, rl_src)); |
| } else { |
| DCHECK((rl_src.location == kLocDalvikFrame) || |
| (rl_src.location == kLocCompilerTemp)); |
| LoadBaseDispWide(cu, TargetReg(kSp), SRegOffset(cu, rl_src.s_reg_low), |
| reg_lo, reg_hi, INVALID_SREG); |
| } |
| } |
| |
| /* |
| * Similar to LoadValueDirect, but clobbers and allocates the target |
| * registers. Should be used when loading to a fixed registers (for example, |
| * loading arguments to an out of line call. |
| */ |
| void Codegen::LoadValueDirectWideFixed(CompilationUnit* cu, RegLocation rl_src, int reg_lo, |
| int reg_hi) |
| { |
| Clobber(cu, reg_lo); |
| Clobber(cu, reg_hi); |
| MarkInUse(cu, reg_lo); |
| MarkInUse(cu, reg_hi); |
| LoadValueDirectWide(cu, rl_src, reg_lo, reg_hi); |
| } |
| |
| RegLocation Codegen::LoadValue(CompilationUnit* cu, RegLocation rl_src, RegisterClass op_kind) |
| { |
| rl_src = EvalLoc(cu, rl_src, op_kind, false); |
| if (IsInexpensiveConstant(cu, rl_src) || rl_src.location != kLocPhysReg) { |
| LoadValueDirect(cu, rl_src, rl_src.low_reg); |
| rl_src.location = kLocPhysReg; |
| MarkLive(cu, rl_src.low_reg, rl_src.s_reg_low); |
| } |
| return rl_src; |
| } |
| |
| void Codegen::StoreValue(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src) |
| { |
| #ifndef NDEBUG |
| /* |
| * Sanity checking - should never try to store to the same |
| * ssa name during the compilation of a single instruction |
| * without an intervening ClobberSReg(). |
| */ |
| DCHECK((cu->live_sreg == INVALID_SREG) || |
| (rl_dest.s_reg_low != cu->live_sreg)); |
| cu->live_sreg = rl_dest.s_reg_low; |
| #endif |
| LIR* def_start; |
| LIR* def_end; |
| DCHECK(!rl_dest.wide); |
| DCHECK(!rl_src.wide); |
| rl_src = UpdateLoc(cu, rl_src); |
| rl_dest = UpdateLoc(cu, rl_dest); |
| if (rl_src.location == kLocPhysReg) { |
| if (IsLive(cu, rl_src.low_reg) || |
| IsPromoted(cu, rl_src.low_reg) || |
| (rl_dest.location == kLocPhysReg)) { |
| // Src is live/promoted or Dest has assigned reg. |
| rl_dest = EvalLoc(cu, rl_dest, kAnyReg, false); |
| OpRegCopy(cu, rl_dest.low_reg, rl_src.low_reg); |
| } else { |
| // Just re-assign the registers. Dest gets Src's regs |
| rl_dest.low_reg = rl_src.low_reg; |
| Clobber(cu, rl_src.low_reg); |
| } |
| } else { |
| // Load Src either into promoted Dest or temps allocated for Dest |
| rl_dest = EvalLoc(cu, rl_dest, kAnyReg, false); |
| LoadValueDirect(cu, rl_src, rl_dest.low_reg); |
| } |
| |
| // Dest is now live and dirty (until/if we flush it to home location) |
| MarkLive(cu, rl_dest.low_reg, rl_dest.s_reg_low); |
| MarkDirty(cu, rl_dest); |
| |
| |
| ResetDefLoc(cu, rl_dest); |
| if (IsDirty(cu, rl_dest.low_reg) && |
| oat_live_out(cu, rl_dest.s_reg_low)) { |
| def_start = cu->last_lir_insn; |
| StoreBaseDisp(cu, TargetReg(kSp), SRegOffset(cu, rl_dest.s_reg_low), |
| rl_dest.low_reg, kWord); |
| MarkClean(cu, rl_dest); |
| def_end = cu->last_lir_insn; |
| if (!rl_dest.ref) { |
| // Exclude references from store elimination |
| MarkDef(cu, rl_dest, def_start, def_end); |
| } |
| } |
| } |
| |
| RegLocation Codegen::LoadValueWide(CompilationUnit* cu, RegLocation rl_src, RegisterClass op_kind) |
| { |
| DCHECK(rl_src.wide); |
| rl_src = EvalLoc(cu, rl_src, op_kind, false); |
| if (IsInexpensiveConstant(cu, rl_src) || rl_src.location != kLocPhysReg) { |
| LoadValueDirectWide(cu, rl_src, rl_src.low_reg, rl_src.high_reg); |
| rl_src.location = kLocPhysReg; |
| MarkLive(cu, rl_src.low_reg, rl_src.s_reg_low); |
| MarkLive(cu, rl_src.high_reg, GetSRegHi(rl_src.s_reg_low)); |
| } |
| return rl_src; |
| } |
| |
| void Codegen::StoreValueWide(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src) |
| { |
| #ifndef NDEBUG |
| /* |
| * Sanity checking - should never try to store to the same |
| * ssa name during the compilation of a single instruction |
| * without an intervening ClobberSReg(). |
| */ |
| DCHECK((cu->live_sreg == INVALID_SREG) || |
| (rl_dest.s_reg_low != cu->live_sreg)); |
| cu->live_sreg = rl_dest.s_reg_low; |
| #endif |
| LIR* def_start; |
| LIR* def_end; |
| DCHECK_EQ(IsFpReg(rl_src.low_reg), IsFpReg(rl_src.high_reg)); |
| DCHECK(rl_dest.wide); |
| DCHECK(rl_src.wide); |
| if (rl_src.location == kLocPhysReg) { |
| if (IsLive(cu, rl_src.low_reg) || |
| IsLive(cu, rl_src.high_reg) || |
| IsPromoted(cu, rl_src.low_reg) || |
| IsPromoted(cu, rl_src.high_reg) || |
| (rl_dest.location == kLocPhysReg)) { |
| // Src is live or promoted or Dest has assigned reg. |
| rl_dest = EvalLoc(cu, rl_dest, kAnyReg, false); |
| OpRegCopyWide(cu, rl_dest.low_reg, rl_dest.high_reg, |
| rl_src.low_reg, rl_src.high_reg); |
| } else { |
| // Just re-assign the registers. Dest gets Src's regs |
| rl_dest.low_reg = rl_src.low_reg; |
| rl_dest.high_reg = rl_src.high_reg; |
| Clobber(cu, rl_src.low_reg); |
| Clobber(cu, rl_src.high_reg); |
| } |
| } else { |
| // Load Src either into promoted Dest or temps allocated for Dest |
| rl_dest = EvalLoc(cu, rl_dest, kAnyReg, false); |
| LoadValueDirectWide(cu, rl_src, rl_dest.low_reg, rl_dest.high_reg); |
| } |
| |
| // Dest is now live and dirty (until/if we flush it to home location) |
| MarkLive(cu, rl_dest.low_reg, rl_dest.s_reg_low); |
| MarkLive(cu, rl_dest.high_reg, GetSRegHi(rl_dest.s_reg_low)); |
| MarkDirty(cu, rl_dest); |
| MarkPair(cu, rl_dest.low_reg, rl_dest.high_reg); |
| |
| |
| ResetDefLocWide(cu, rl_dest); |
| if ((IsDirty(cu, rl_dest.low_reg) || |
| IsDirty(cu, rl_dest.high_reg)) && |
| (oat_live_out(cu, rl_dest.s_reg_low) || |
| oat_live_out(cu, GetSRegHi(rl_dest.s_reg_low)))) { |
| def_start = cu->last_lir_insn; |
| DCHECK_EQ((SRegToVReg(cu, rl_dest.s_reg_low)+1), |
| SRegToVReg(cu, GetSRegHi(rl_dest.s_reg_low))); |
| StoreBaseDispWide(cu, TargetReg(kSp), SRegOffset(cu, rl_dest.s_reg_low), |
| rl_dest.low_reg, rl_dest.high_reg); |
| MarkClean(cu, rl_dest); |
| def_end = cu->last_lir_insn; |
| MarkDefWide(cu, rl_dest, def_start, def_end); |
| } |
| } |
| |
| /* Utilities to load the current Method* */ |
| void Codegen::LoadCurrMethodDirect(CompilationUnit *cu, int r_tgt) |
| { |
| LoadValueDirectFixed(cu, cu->method_loc, r_tgt); |
| } |
| |
| RegLocation Codegen::LoadCurrMethod(CompilationUnit *cu) |
| { |
| return LoadValue(cu, cu->method_loc, kCoreReg); |
| } |
| |
| } // namespace art |