/*
* Copyright (C) 2009 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.
*/
/*
* This file contains codegen and support common to all supported
* Mips variants. It is included by:
*
* Codegen-$(TARGET_ARCH_VARIANT).c
*
* which combines this common code with specific support found in the
* applicable directory below this one.
*/
/* Load a word at base + displacement. Displacement must be word multiple */
static MipsLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
int rDest)
{
return loadBaseDisp(cUnit, NULL, rBase, displacement, rDest, kWord,
INVALID_SREG);
}
static MipsLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
int displacement, int rSrc)
{
return storeBaseDisp(cUnit, rBase, displacement, rSrc, 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.
*/
static void loadValueDirect(CompilationUnit *cUnit, RegLocation rlSrc,
int reg1)
{
rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
if (rlSrc.location == kLocPhysReg) {
genRegCopy(cUnit, reg1, rlSrc.lowReg);
} else if (rlSrc.location == kLocRetval) {
loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), reg1);
} else {
assert(rlSrc.location == kLocDalvikFrame);
loadWordDisp(cUnit, rFP, dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
reg1);
}
}
/*
* 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.
*/
static void loadValueDirectFixed(CompilationUnit *cUnit, RegLocation rlSrc,
int reg1)
{
dvmCompilerClobber(cUnit, reg1);
dvmCompilerMarkInUse(cUnit, reg1);
loadValueDirect(cUnit, rlSrc, reg1);
}
/*
* 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.
*/
static void loadValueDirectWide(CompilationUnit *cUnit, RegLocation rlSrc,
int regLo, int regHi)
{
rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
if (rlSrc.location == kLocPhysReg) {
genRegCopyWide(cUnit, regLo, regHi, rlSrc.lowReg, rlSrc.highReg);
} else if (rlSrc.location == kLocRetval) {
loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval),
regLo, regHi, INVALID_SREG);
} else {
assert(rlSrc.location == kLocDalvikFrame);
loadBaseDispWide(cUnit, NULL, rFP,
dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
regLo, regHi, 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.
*/
static void loadValueDirectWideFixed(CompilationUnit *cUnit, RegLocation rlSrc,
int regLo, int regHi)
{
dvmCompilerClobber(cUnit, regLo);
dvmCompilerClobber(cUnit, regHi);
dvmCompilerMarkInUse(cUnit, regLo);
dvmCompilerMarkInUse(cUnit, regHi);
loadValueDirectWide(cUnit, rlSrc, regLo, regHi);
}
static RegLocation loadValue(CompilationUnit *cUnit, RegLocation rlSrc,
RegisterClass opKind)
{
rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
if (rlSrc.location == kLocDalvikFrame) {
loadValueDirect(cUnit, rlSrc, rlSrc.lowReg);
rlSrc.location = kLocPhysReg;
dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
} else if (rlSrc.location == kLocRetval) {
loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), rlSrc.lowReg);
rlSrc.location = kLocPhysReg;
dvmCompilerClobber(cUnit, rlSrc.lowReg);
}
return rlSrc;
}
static void storeValue(CompilationUnit *cUnit, RegLocation rlDest,
RegLocation rlSrc)
{
LIR *defStart;
LIR *defEnd;
assert(!rlDest.wide);
assert(!rlSrc.wide);
dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
if (rlSrc.location == kLocPhysReg) {
if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
(rlDest.location == kLocPhysReg)) {
// Src is live or Dest has assigned reg.
rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
genRegCopy(cUnit, rlDest.lowReg, rlSrc.lowReg);
} else {
// Just re-assign the registers. Dest gets Src's regs
rlDest.lowReg = rlSrc.lowReg;
dvmCompilerClobber(cUnit, rlSrc.lowReg);
}
} else {
// Load Src either into promoted Dest or temps allocated for Dest
rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
loadValueDirect(cUnit, rlSrc, rlDest.lowReg);
}
// Dest is now live and dirty (until/if we flush it to home location)
dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
if (rlDest.location == kLocRetval) {
storeBaseDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval),
rlDest.lowReg, kWord);
dvmCompilerClobber(cUnit, rlDest.lowReg);
} else {
dvmCompilerResetDefLoc(cUnit, rlDest);
if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow)) {
defStart = (LIR *)cUnit->lastLIRInsn;
int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
storeBaseDisp(cUnit, rFP, vReg << 2, rlDest.lowReg, kWord);
dvmCompilerMarkClean(cUnit, rlDest.lowReg);
defEnd = (LIR *)cUnit->lastLIRInsn;
dvmCompilerMarkDef(cUnit, rlDest, defStart, defEnd);
}
}
}
static RegLocation loadValueWide(CompilationUnit *cUnit, RegLocation rlSrc,
RegisterClass opKind)
{
assert(rlSrc.wide);
rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
if (rlSrc.location == kLocDalvikFrame) {
loadValueDirectWide(cUnit, rlSrc, rlSrc.lowReg, rlSrc.highReg);
rlSrc.location = kLocPhysReg;
dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
dvmCompilerMarkLive(cUnit, rlSrc.highReg,
dvmCompilerSRegHi(rlSrc.sRegLow));
} else if (rlSrc.location == kLocRetval) {
loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval),
rlSrc.lowReg, rlSrc.highReg, INVALID_SREG);
rlSrc.location = kLocPhysReg;
dvmCompilerClobber(cUnit, rlSrc.lowReg);
dvmCompilerClobber(cUnit, rlSrc.highReg);
}
return rlSrc;
}
static void storeValueWide(CompilationUnit *cUnit, RegLocation rlDest,
RegLocation rlSrc)
{
LIR *defStart;
LIR *defEnd;
assert(FPREG(rlSrc.lowReg)==FPREG(rlSrc.highReg));
assert(rlDest.wide);
assert(rlSrc.wide);
dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
if (rlSrc.location == kLocPhysReg) {
if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
dvmCompilerIsLive(cUnit, rlSrc.highReg) ||
(rlDest.location == kLocPhysReg)) {
// Src is live or Dest has assigned reg.
rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
genRegCopyWide(cUnit, rlDest.lowReg, rlDest.highReg,
rlSrc.lowReg, rlSrc.highReg);
} else {
// Just re-assign the registers. Dest gets Src's regs
rlDest.lowReg = rlSrc.lowReg;
rlDest.highReg = rlSrc.highReg;
dvmCompilerClobber(cUnit, rlSrc.lowReg);
dvmCompilerClobber(cUnit, rlSrc.highReg);
}
} else {
// Load Src either into promoted Dest or temps allocated for Dest
rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
loadValueDirectWide(cUnit, rlSrc, rlDest.lowReg,
rlDest.highReg);
}
// Dest is now live and dirty (until/if we flush it to home location)
dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
dvmCompilerMarkLive(cUnit, rlDest.highReg,
dvmCompilerSRegHi(rlDest.sRegLow));
dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
dvmCompilerMarkDirty(cUnit, rlDest.highReg);
dvmCompilerMarkPair(cUnit, rlDest.lowReg, rlDest.highReg);
if (rlDest.location == kLocRetval) {
storeBaseDispWide(cUnit, rSELF, offsetof(Thread, interpSave.retval),
rlDest.lowReg, rlDest.highReg);
dvmCompilerClobber(cUnit, rlDest.lowReg);
dvmCompilerClobber(cUnit, rlDest.highReg);
} else {
dvmCompilerResetDefLocWide(cUnit, rlDest);
if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow) ||
dvmCompilerLiveOut(cUnit, dvmCompilerSRegHi(rlDest.sRegLow))) {
defStart = (LIR *)cUnit->lastLIRInsn;
int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
assert((vReg+1) == dvmCompilerS2VReg(cUnit,
dvmCompilerSRegHi(rlDest.sRegLow)));
storeBaseDispWide(cUnit, rFP, vReg << 2, rlDest.lowReg,
rlDest.highReg);
dvmCompilerMarkClean(cUnit, rlDest.lowReg);
dvmCompilerMarkClean(cUnit, rlDest.highReg);
defEnd = (LIR *)cUnit->lastLIRInsn;
dvmCompilerMarkDefWide(cUnit, rlDest, defStart, defEnd);
}
}
}
/*
* Perform null-check on a register. sReg is the ssa register being checked,
* and mReg is the machine register holding the actual value. If internal state
* indicates that sReg has been checked before the check request is ignored.
*/
static MipsLIR *genNullCheck(CompilationUnit *cUnit, int sReg, int mReg,
int dOffset, MipsLIR *pcrLabel)
{
/* This particular Dalvik register has been null-checked */
if (dvmIsBitSet(cUnit->regPool->nullCheckedRegs, sReg)) {
return pcrLabel;
}
dvmSetBit(cUnit->regPool->nullCheckedRegs, sReg);
return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel);
}
/*
* Perform a "reg cmp reg" operation and jump to the PCR region if condition
* satisfies.
*/
static MipsLIR *genRegRegCheck(CompilationUnit *cUnit,
MipsConditionCode cond,
int reg1, int reg2, int dOffset,
MipsLIR *pcrLabel)
{
MipsLIR *res = NULL;
if (cond == kMipsCondGe) { /* signed >= case */
int tReg = dvmCompilerAllocTemp(cUnit);
res = newLIR3(cUnit, kMipsSlt, tReg, reg1, reg2);
MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1);
genCheckCommon(cUnit, dOffset, branch, pcrLabel);
} else if (cond == kMipsCondCs) { /* unsigned >= case */
int tReg = dvmCompilerAllocTemp(cUnit);
res = newLIR3(cUnit, kMipsSltu, tReg, reg1, reg2);
MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1);
genCheckCommon(cUnit, dOffset, branch, pcrLabel);
} else {
ALOGE("Unexpected condition in genRegRegCheck: %d\n", (int) cond);
dvmAbort();
}
return res;
}
/*
* Perform zero-check on a register. Similar to genNullCheck but the value being
* checked does not have a corresponding Dalvik register.
*/
static MipsLIR *genZeroCheck(CompilationUnit *cUnit, int mReg,
int dOffset, MipsLIR *pcrLabel)
{
return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel);
}
/* Perform bound check on two registers */
static MipsLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
int rBound, int dOffset, MipsLIR *pcrLabel)
{
return genRegRegCheck(cUnit, kMipsCondCs, rIndex, rBound, dOffset,
pcrLabel);
}
/*
* Jump to the out-of-line handler to finish executing the
* remaining of more complex instructions.
*/
static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpcode opCode)
{
/*
* We're jumping from a trace to a template. Using jal is preferable to jalr,
* but we need to ensure source and target addresses allow the use of jal.
* This should almost always be the case, but if source and target are in
* different 256mb regions then use jalr. The test below is very conservative
* since we don't have a source address yet, but this is ok for now given that
* we expect this case to be very rare. The test can be made less conservative
* as needed in the future in coordination with address assignment during
* the assembly process.
*/
dvmCompilerClobberHandlerRegs(cUnit);
int targetAddr = (int) gDvmJit.codeCache + templateEntryOffsets[opCode];
int maxSourceAddr = (int) gDvmJit.codeCache + gDvmJit.codeCacheSize;
if ((targetAddr & 0xF0000000) == (maxSourceAddr & 0xF0000000)) {
newLIR1(cUnit, kMipsJal, targetAddr);
} else {
loadConstant(cUnit, r_T9, targetAddr);
newLIR2(cUnit, kMipsJalr, r_RA, r_T9);
}
}