blob: c8f9c51ffd75ff1705dd3cffd3695b832384d6b8 [file] [log] [blame]
/*
* 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