/*
* 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 for the Thumb ISA and is intended to be
* includes by:
*
* Codegen-$(TARGET_ARCH_VARIANT).c
*
*/
static int coreTemps[] = {r0, r1, r2, r3, r4PC, r7};
static void storePair(CompilationUnit *cUnit, int base, int lowReg,
int highReg);
static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg);
static ArmLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
int rDest);
static ArmLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
int displacement, int rSrc);
static ArmLIR *genRegRegCheck(CompilationUnit *cUnit,
ArmConditionCode cond,
int reg1, int reg2, int dOffset,
ArmLIR *pcrLabel);
/*
* Load a immediate using a shortcut if possible; otherwise
* grab from the per-translation literal pool. If target is
* a high register, build constant into a low register and copy.
*
* No additional register clobbering operation performed. Use this version when
* 1) rDest is freshly returned from dvmCompilerAllocTemp or
* 2) The codegen is under fixed register usage
*/
static ArmLIR *loadConstantNoClobber(CompilationUnit *cUnit, int rDest,
int value)
{
ArmLIR *res;
int tDest = LOWREG(rDest) ? rDest : dvmCompilerAllocTemp(cUnit);
/* See if the value can be constructed cheaply */
if ((value >= 0) && (value <= 255)) {
res = newLIR2(cUnit, kThumbMovImm, tDest, value);
if (rDest != tDest) {
opRegReg(cUnit, kOpMov, rDest, tDest);
dvmCompilerFreeTemp(cUnit, tDest);
}
return res;
} else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
res = newLIR2(cUnit, kThumbMovImm, tDest, ~value);
newLIR2(cUnit, kThumbMvn, tDest, tDest);
if (rDest != tDest) {
opRegReg(cUnit, kOpMov, rDest, tDest);
dvmCompilerFreeTemp(cUnit, tDest);
}
return res;
}
/* No shortcut - go ahead and use literal pool */
ArmLIR *dataTarget = scanLiteralPool(cUnit->literalList, value, 255);
if (dataTarget == NULL) {
dataTarget = addWordData(cUnit, &cUnit->literalList, value);
}
ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
loadPcRel->opcode = kThumbLdrPcRel;
loadPcRel->generic.target = (LIR *) dataTarget;
loadPcRel->operands[0] = tDest;
setupResourceMasks(loadPcRel);
setMemRefType(loadPcRel, true, kLiteral);
loadPcRel->aliasInfo = dataTarget->operands[0];
res = loadPcRel;
dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
/*
* To save space in the constant pool, we use the ADD_RRI8 instruction to
* add up to 255 to an existing constant value.
*/
if (dataTarget->operands[0] != value) {
newLIR2(cUnit, kThumbAddRI8, tDest, value - dataTarget->operands[0]);
}
if (rDest != tDest) {
opRegReg(cUnit, kOpMov, rDest, tDest);
dvmCompilerFreeTemp(cUnit, tDest);
}
return res;
}
/*
* Load an immediate value into a fixed or temp register. Target
* register is clobbered, and marked inUse.
*/
static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
{
if (dvmCompilerIsTemp(cUnit, rDest)) {
dvmCompilerClobber(cUnit, rDest);
dvmCompilerMarkInUse(cUnit, rDest);
}
return loadConstantNoClobber(cUnit, rDest, value);
}
/*
* Load a class pointer value into a fixed or temp register. Target
* register is clobbered, and marked inUse.
*/
static ArmLIR *loadClassPointer(CompilationUnit *cUnit, int rDest, int value)
{
ArmLIR *res;
cUnit->hasClassLiterals = true;
if (dvmCompilerIsTemp(cUnit, rDest)) {
dvmCompilerClobber(cUnit, rDest);
dvmCompilerMarkInUse(cUnit, rDest);
}
ArmLIR *dataTarget = scanLiteralPool(cUnit->classPointerList, value, 0);
if (dataTarget == NULL) {
dataTarget = addWordData(cUnit, &cUnit->classPointerList, value);
/* Counts the number of class pointers in this translation */
cUnit->numClassPointers++;
}
ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
loadPcRel->opcode = kThumbLdrPcRel;
loadPcRel->generic.target = (LIR *) dataTarget;
loadPcRel->operands[0] = rDest;
setupResourceMasks(loadPcRel);
setMemRefType(loadPcRel, true, kLiteral);
loadPcRel->aliasInfo = dataTarget->operands[0];
res = loadPcRel;
dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
return res;
}
static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
{
ArmOpcode opcode = kThumbBkpt;
switch (op) {
case kOpUncondBr:
opcode = kThumbBUncond;
break;
default:
ALOGE("Jit: bad case in opNone");
dvmCompilerAbort(cUnit);
}
return newLIR0(cUnit, opcode);
}
static ArmLIR *opCondBranch(CompilationUnit *cUnit, ArmConditionCode cc)
{
return newLIR2(cUnit, kThumbBCond, 0 /* offset to be patched */, cc);
}
static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
{
ArmOpcode opcode = kThumbBkpt;
switch (op) {
case kOpPush:
opcode = kThumbPush;
break;
case kOpPop:
opcode = kThumbPop;
break;
default:
ALOGE("Jit: bad case in opCondBranch");
dvmCompilerAbort(cUnit);
}
return newLIR1(cUnit, opcode, value);
}
static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
{
ArmOpcode opcode = kThumbBkpt;
switch (op) {
case kOpBlx:
opcode = kThumbBlxR;
break;
default:
ALOGE("Jit: bad case in opReg");
dvmCompilerAbort(cUnit);
}
return newLIR1(cUnit, opcode, rDestSrc);
}
static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
int value)
{
ArmLIR *res;
bool neg = (value < 0);
int absValue = (neg) ? -value : value;
bool shortForm = (absValue & 0xff) == absValue;
ArmOpcode opcode = kThumbBkpt;
switch (op) {
case kOpAdd:
if ( !neg && (rDestSrc1 == r13sp) && (value <= 508)) { /* sp */
assert((value & 0x3) == 0);
return newLIR1(cUnit, kThumbAddSpI7, value >> 2);
} else if (shortForm) {
opcode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
} else
opcode = kThumbAddRRR;
break;
case kOpSub:
if (!neg && (rDestSrc1 == r13sp) && (value <= 508)) { /* sp */
assert((value & 0x3) == 0);
return newLIR1(cUnit, kThumbSubSpI7, value >> 2);
} else if (shortForm) {
opcode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
} else
opcode = kThumbSubRRR;
break;
case kOpCmp:
if (neg)
shortForm = false;
if (LOWREG(rDestSrc1) && shortForm) {
opcode = kThumbCmpRI8;
} else if (LOWREG(rDestSrc1)) {
opcode = kThumbCmpRR;
} else {
shortForm = false;
opcode = kThumbCmpHL;
}
break;
default:
ALOGE("Jit: bad case in opRegImm");
dvmCompilerAbort(cUnit);
break;
}
if (shortForm)
res = newLIR2(cUnit, opcode, rDestSrc1, absValue);
else {
int rScratch = dvmCompilerAllocTemp(cUnit);
res = loadConstant(cUnit, rScratch, value);
if (op == kOpCmp)
newLIR2(cUnit, opcode, rDestSrc1, rScratch);
else
newLIR3(cUnit, opcode, rDestSrc1, rDestSrc1, rScratch);
}
return res;
}
static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
int rSrc1, int rSrc2)
{
ArmOpcode opcode = kThumbBkpt;
switch (op) {
case kOpAdd:
opcode = kThumbAddRRR;
break;
case kOpSub:
opcode = kThumbSubRRR;
break;
default:
if (rDest == rSrc1) {
return opRegReg(cUnit, op, rDest, rSrc2);
} else if (rDest == rSrc2) {
assert(dvmCompilerIsTemp(cUnit, rSrc1));
dvmCompilerClobber(cUnit, rSrc1);
opRegReg(cUnit, op, rSrc1, rSrc2);
return opRegReg(cUnit, kOpMov, rDest, rSrc1);
} else {
opRegReg(cUnit, kOpMov, rDest, rSrc1);
return opRegReg(cUnit, op, rDest, rSrc2);
}
break;
}
return newLIR3(cUnit, opcode, rDest, rSrc1, rSrc2);
}
static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
int rSrc1, int value)
{
ArmLIR *res;
bool neg = (value < 0);
int absValue = (neg) ? -value : value;
ArmOpcode opcode = kThumbBkpt;
bool shortForm = (absValue & 0x7) == absValue;
switch(op) {
case kOpAdd:
if (rDest == rSrc1)
return opRegImm(cUnit, op, rDest, value);
if ((rSrc1 == r13sp) && (value <= 1020)) { /* sp */
assert((value & 0x3) == 0);
shortForm = true;
opcode = kThumbAddSpRel;
value >>= 2;
} else if ((rSrc1 == r15pc) && (value <= 1020)) { /* pc */
assert((value & 0x3) == 0);
shortForm = true;
opcode = kThumbAddPcRel;
value >>= 2;
} else if (shortForm) {
opcode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
} else if ((absValue > 0) && (absValue <= (255 + 7))) {
/* Two shots - 1st handle the 7 */
opcode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
res = newLIR3(cUnit, opcode, rDest, rSrc1, 7);
opcode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
newLIR2(cUnit, opcode, rDest, absValue - 7);
return res;
} else
opcode = kThumbAddRRR;
break;
case kOpSub:
if (rDest == rSrc1)
return opRegImm(cUnit, op, rDest, value);
if (shortForm) {
opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
} else if ((absValue > 0) && (absValue <= (255 + 7))) {
/* Two shots - 1st handle the 7 */
opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
res = newLIR3(cUnit, opcode, rDest, rSrc1, 7);
opcode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
newLIR2(cUnit, opcode, rDest, absValue - 7);
return res;
} else
opcode = kThumbSubRRR;
break;
case kOpLsl:
shortForm = (!neg && value <= 31);
opcode = kThumbLslRRI5;
break;
case kOpLsr:
shortForm = (!neg && value <= 31);
opcode = kThumbLsrRRI5;
break;
case kOpAsr:
shortForm = (!neg && value <= 31);
opcode = kThumbAsrRRI5;
break;
case kOpMul:
case kOpAnd:
case kOpOr:
case kOpXor:
if (rDest == rSrc1) {
int rScratch = dvmCompilerAllocTemp(cUnit);
res = loadConstant(cUnit, rScratch, value);
opRegReg(cUnit, op, rDest, rScratch);
} else {
res = loadConstant(cUnit, rDest, value);
opRegReg(cUnit, op, rDest, rSrc1);
}
return res;
default:
ALOGE("Jit: bad case in opRegRegImm");
dvmCompilerAbort(cUnit);
break;
}
if (shortForm)
res = newLIR3(cUnit, opcode, rDest, rSrc1, absValue);
else {
if (rDest != rSrc1) {
res = loadConstant(cUnit, rDest, value);
newLIR3(cUnit, opcode, rDest, rSrc1, rDest);
} else {
int rScratch = dvmCompilerAllocTemp(cUnit);
res = loadConstant(cUnit, rScratch, value);
newLIR3(cUnit, opcode, rDest, rSrc1, rScratch);
}
}
return res;
}
static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
int rSrc2)
{
ArmLIR *res;
ArmOpcode opcode = kThumbBkpt;
switch (op) {
case kOpAdc:
opcode = kThumbAdcRR;
break;
case kOpAnd:
opcode = kThumbAndRR;
break;
case kOpBic:
opcode = kThumbBicRR;
break;
case kOpCmn:
opcode = kThumbCmnRR;
break;
case kOpCmp:
opcode = kThumbCmpRR;
break;
case kOpXor:
opcode = kThumbEorRR;
break;
case kOpMov:
if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
opcode = kThumbMovRR;
else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
opcode = kThumbMovRR_H2H;
else if (LOWREG(rDestSrc1))
opcode = kThumbMovRR_H2L;
else
opcode = kThumbMovRR_L2H;
break;
case kOpMul:
opcode = kThumbMul;
break;
case kOpMvn:
opcode = kThumbMvn;
break;
case kOpNeg:
opcode = kThumbNeg;
break;
case kOpOr:
opcode = kThumbOrr;
break;
case kOpSbc:
opcode = kThumbSbc;
break;
case kOpTst:
opcode = kThumbTst;
break;
case kOpLsl:
opcode = kThumbLslRR;
break;
case kOpLsr:
opcode = kThumbLsrRR;
break;
case kOpAsr:
opcode = kThumbAsrRR;
break;
case kOpRor:
opcode = kThumbRorRR;
case kOpAdd:
case kOpSub:
return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
case kOp2Byte:
res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 24);
opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 24);
return res;
case kOp2Short:
res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 16);
return res;
case kOp2Char:
res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
opRegRegImm(cUnit, kOpLsr, rDestSrc1, rDestSrc1, 16);
return res;
default:
ALOGE("Jit: bad case in opRegReg");
dvmCompilerAbort(cUnit);
break;
}
return newLIR2(cUnit, opcode, rDestSrc1, rSrc2);
}
static ArmLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
int rDestHi, int valLo, int valHi)
{
ArmLIR *res;
res = loadConstantNoClobber(cUnit, rDestLo, valLo);
loadConstantNoClobber(cUnit, rDestHi, valHi);
return res;
}
/* Load value from base + scaled index. */
static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
int rIndex, int rDest, int scale, OpSize size)
{
ArmLIR *first = NULL;
ArmLIR *res;
ArmOpcode opcode = kThumbBkpt;
int rNewIndex = rIndex;
if (scale) {
// Scale the index, but can't trash the original.
rNewIndex = dvmCompilerAllocTemp(cUnit);
first = opRegRegImm(cUnit, kOpLsl, rNewIndex, rIndex, scale);
}
switch (size) {
case kWord:
opcode = kThumbLdrRRR;
break;
case kUnsignedHalf:
opcode = kThumbLdrhRRR;
break;
case kSignedHalf:
opcode = kThumbLdrshRRR;
break;
case kUnsignedByte:
opcode = kThumbLdrbRRR;
break;
case kSignedByte:
opcode = kThumbLdrsbRRR;
break;
default:
ALOGE("Jit: bad case in loadBaseIndexed");
dvmCompilerAbort(cUnit);
}
res = newLIR3(cUnit, opcode, rDest, rBase, rNewIndex);
#if defined(WITH_SELF_VERIFICATION)
if (cUnit->heapMemOp)
res->flags.insertWrapper = true;
#endif
if (scale)
dvmCompilerFreeTemp(cUnit, rNewIndex);
return (first) ? first : res;
}
/* store value base base + scaled index. */
static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
int rIndex, int rSrc, int scale, OpSize size)
{
ArmLIR *first = NULL;
ArmLIR *res;
ArmOpcode opcode = kThumbBkpt;
int rNewIndex = rIndex;
if (scale) {
rNewIndex = dvmCompilerAllocTemp(cUnit);
first = opRegRegImm(cUnit, kOpLsl, rNewIndex, rIndex, scale);
}
switch (size) {
case kWord:
opcode = kThumbStrRRR;
break;
case kUnsignedHalf:
case kSignedHalf:
opcode = kThumbStrhRRR;
break;
case kUnsignedByte:
case kSignedByte:
opcode = kThumbStrbRRR;
break;
default:
ALOGE("Jit: bad case in storeBaseIndexed");
dvmCompilerAbort(cUnit);
}
res = newLIR3(cUnit, opcode, rSrc, rBase, rNewIndex);
#if defined(WITH_SELF_VERIFICATION)
if (cUnit->heapMemOp)
res->flags.insertWrapper = true;
#endif
if (scale)
dvmCompilerFreeTemp(cUnit, rNewIndex);
return (first) ? first : res;
}
static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
{
ArmLIR *res;
genBarrier(cUnit);
res = newLIR2(cUnit, kThumbLdmia, rBase, rMask);
#if defined(WITH_SELF_VERIFICATION)
if (cUnit->heapMemOp)
res->flags.insertWrapper = true;
#endif
genBarrier(cUnit);
return res;
}
static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
{
ArmLIR *res;
genBarrier(cUnit);
res = newLIR2(cUnit, kThumbStmia, rBase, rMask);
#if defined(WITH_SELF_VERIFICATION)
if (cUnit->heapMemOp)
res->flags.insertWrapper = true;
#endif
genBarrier(cUnit);
return res;
}
static ArmLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
int displacement, int rDest, int rDestHi,
OpSize size, int sReg)
/*
* Load value from base + displacement. Optionally perform null check
* on base (which must have an associated sReg and MIR). If not
* performing null check, incoming MIR can be null. IMPORTANT: this
* code must not allocate any new temps. If a new register is needed
* and base and dest are the same, spill some other register to
* rlp and then restore.
*/
{
ArmLIR *res;
ArmLIR *load = NULL;
ArmLIR *load2 = NULL;
ArmOpcode opcode = kThumbBkpt;
bool shortForm = false;
int encodedDisp = displacement;
bool pair = false;
switch (size) {
case kLong:
case kDouble:
pair = true;
if ((displacement < 124) && (displacement >= 0)) {
assert((displacement & 0x3) == 0);
shortForm = true;
encodedDisp >>= 2;
opcode = kThumbLdrRRI5;
} else {
opcode = kThumbLdrRRR;
}
break;
case kWord:
if (LOWREG(rDest) && (rBase == r15pc) &&
(displacement <= 1020) && (displacement >= 0)) {
shortForm = true;
encodedDisp >>= 2;
opcode = kThumbLdrPcRel;
} else if (LOWREG(rDest) && (rBase == r13sp) &&
(displacement <= 1020) && (displacement >= 0)) {
shortForm = true;
encodedDisp >>= 2;
opcode = kThumbLdrSpRel;
} else if (displacement < 128 && displacement >= 0) {
assert((displacement & 0x3) == 0);
shortForm = true;
encodedDisp >>= 2;
opcode = kThumbLdrRRI5;
} else {
opcode = kThumbLdrRRR;
}
break;
case kUnsignedHalf:
if (displacement < 64 && displacement >= 0) {
assert((displacement & 0x1) == 0);
shortForm = true;
encodedDisp >>= 1;
opcode = kThumbLdrhRRI5;
} else {
opcode = kThumbLdrhRRR;
}
break;
case kSignedHalf:
opcode = kThumbLdrshRRR;
break;
case kUnsignedByte:
if (displacement < 32 && displacement >= 0) {
shortForm = true;
opcode = kThumbLdrbRRI5;
} else {
opcode = kThumbLdrbRRR;
}
break;
case kSignedByte:
opcode = kThumbLdrsbRRR;
break;
default:
ALOGE("Jit: bad case in loadBaseIndexedBody");
dvmCompilerAbort(cUnit);
}
if (shortForm) {
load = res = newLIR3(cUnit, opcode, rDest, rBase, encodedDisp);
if (pair) {
load2 = newLIR3(cUnit, opcode, rDestHi, rBase, encodedDisp+1);
}
} else {
if (pair) {
int rTmp = dvmCompilerAllocFreeTemp(cUnit);
res = opRegRegImm(cUnit, kOpAdd, rTmp, rBase, displacement);
load = newLIR3(cUnit, kThumbLdrRRI5, rDest, rTmp, 0);
load2 = newLIR3(cUnit, kThumbLdrRRI5, rDestHi, rTmp, 1);
dvmCompilerFreeTemp(cUnit, rTmp);
} else {
int rTmp = (rBase == rDest) ? dvmCompilerAllocFreeTemp(cUnit)
: rDest;
res = loadConstant(cUnit, rTmp, displacement);
load = newLIR3(cUnit, opcode, rDest, rBase, rTmp);
if (rBase == r5FP)
annotateDalvikRegAccess(load, displacement >> 2,
true /* isLoad */);
if (rTmp != rDest)
dvmCompilerFreeTemp(cUnit, rTmp);
}
}
if (rBase == r5FP) {
if (load != NULL)
annotateDalvikRegAccess(load, displacement >> 2,
true /* isLoad */);
if (load2 != NULL)
annotateDalvikRegAccess(load2, (displacement >> 2) + 1,
true /* isLoad */);
}
#if defined(WITH_SELF_VERIFICATION)
if (load != NULL && cUnit->heapMemOp)
load->flags.insertWrapper = true;
if (load2 != NULL && cUnit->heapMemOp)
load2->flags.insertWrapper = true;
#endif
return load;
}
static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
int displacement, int rDest, OpSize size,
int sReg)
{
return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
size, sReg);
}
static ArmLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
int displacement, int rDestLo, int rDestHi,
int sReg)
{
return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
kLong, sReg);
}
static ArmLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
int displacement, int rSrc, int rSrcHi,
OpSize size)
{
ArmLIR *res;
ArmLIR *store = NULL;
ArmLIR *store2 = NULL;
ArmOpcode opcode = kThumbBkpt;
bool shortForm = false;
int encodedDisp = displacement;
bool pair = false;
switch (size) {
case kLong:
case kDouble:
pair = true;
if ((displacement < 124) && (displacement >= 0)) {
assert((displacement & 0x3) == 0);
pair = true;
shortForm = true;
encodedDisp >>= 2;
opcode = kThumbStrRRI5;
} else {
opcode = kThumbStrRRR;
}
break;
case kWord:
if (displacement < 128 && displacement >= 0) {
assert((displacement & 0x3) == 0);
shortForm = true;
encodedDisp >>= 2;
opcode = kThumbStrRRI5;
} else {
opcode = kThumbStrRRR;
}
break;
case kUnsignedHalf:
case kSignedHalf:
if (displacement < 64 && displacement >= 0) {
assert((displacement & 0x1) == 0);
shortForm = true;
encodedDisp >>= 1;
opcode = kThumbStrhRRI5;
} else {
opcode = kThumbStrhRRR;
}
break;
case kUnsignedByte:
case kSignedByte:
if (displacement < 32 && displacement >= 0) {
shortForm = true;
opcode = kThumbStrbRRI5;
} else {
opcode = kThumbStrbRRR;
}
break;
default:
ALOGE("Jit: bad case in storeBaseIndexedBody");
dvmCompilerAbort(cUnit);
}
if (shortForm) {
store = res = newLIR3(cUnit, opcode, rSrc, rBase, encodedDisp);
if (pair) {
store2 = newLIR3(cUnit, opcode, rSrcHi, rBase, encodedDisp + 1);
}
} else {
int rScratch = dvmCompilerAllocTemp(cUnit);
if (pair) {
res = opRegRegImm(cUnit, kOpAdd, rScratch, rBase, displacement);
store = newLIR3(cUnit, kThumbStrRRI5, rSrc, rScratch, 0);
store2 = newLIR3(cUnit, kThumbStrRRI5, rSrcHi, rScratch, 1);
} else {
res = loadConstant(cUnit, rScratch, displacement);
store = newLIR3(cUnit, opcode, rSrc, rBase, rScratch);
}
dvmCompilerFreeTemp(cUnit, rScratch);
}
if (rBase == r5FP) {
if (store != NULL)
annotateDalvikRegAccess(store, displacement >> 2,
false /* isLoad */);
if (store2 != NULL)
annotateDalvikRegAccess(store2, (displacement >> 2) + 1,
false /* isLoad */);
}
#if defined(WITH_SELF_VERIFICATION)
if (store != NULL && cUnit->heapMemOp)
store->flags.insertWrapper = true;
if (store2 != NULL && cUnit->heapMemOp)
store2->flags.insertWrapper = true;
#endif
return res;
}
static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
int displacement, int rSrc, OpSize size)
{
return storeBaseDispBody(cUnit, rBase, displacement, rSrc, -1, size);
}
static ArmLIR *storeBaseDispWide(CompilationUnit *cUnit, int rBase,
int displacement, int rSrcLo, int rSrcHi)
{
return storeBaseDispBody(cUnit, rBase, displacement, rSrcLo, rSrcHi, kLong);
}
static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
{
if (lowReg < highReg) {
storeMultiple(cUnit, base, (1 << lowReg) | (1 << highReg));
} else {
storeWordDisp(cUnit, base, 0, lowReg);
storeWordDisp(cUnit, base, 4, highReg);
}
}
static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
{
if (lowReg < highReg) {
loadMultiple(cUnit, base, (1 << lowReg) | (1 << highReg));
} else {
loadWordDisp(cUnit, base, 0 , lowReg);
loadWordDisp(cUnit, base, 4 , highReg);
}
}
static ArmLIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
{
ArmLIR* res;
ArmOpcode opcode;
res = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
if (LOWREG(rDest) && LOWREG(rSrc))
opcode = kThumbMovRR;
else if (!LOWREG(rDest) && !LOWREG(rSrc))
opcode = kThumbMovRR_H2H;
else if (LOWREG(rDest))
opcode = kThumbMovRR_H2L;
else
opcode = kThumbMovRR_L2H;
res->operands[0] = rDest;
res->operands[1] = rSrc;
res->opcode = opcode;
setupResourceMasks(res);
if (rDest == rSrc) {
res->flags.isNop = true;
}
return res;
}
static ArmLIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
{
ArmLIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
dvmCompilerAppendLIR(cUnit, (LIR*)res);
return res;
}
static void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
int srcLo, int srcHi)
{
// Handle overlap
if (srcHi == destLo) {
genRegCopy(cUnit, destHi, srcHi);
genRegCopy(cUnit, destLo, srcLo);
} else {
genRegCopy(cUnit, destLo, srcLo);
genRegCopy(cUnit, destHi, srcHi);
}
}
static ArmLIR *genCmpImmBranch(CompilationUnit *cUnit,
ArmConditionCode cond, int reg,
int checkValue)
{
if ((checkValue & 0xff) != checkValue) {
int tReg = dvmCompilerAllocTemp(cUnit);
loadConstant(cUnit, tReg, checkValue);
newLIR2(cUnit, kThumbCmpRR, reg, tReg);
dvmCompilerFreeTemp(cUnit, tReg);
} else {
newLIR2(cUnit, kThumbCmpRI8, reg, checkValue);
}
ArmLIR *branch = newLIR2(cUnit, kThumbBCond, 0, cond);
return branch;
}
#if defined(WITH_SELF_VERIFICATION)
static void genSelfVerificationPreBranch(CompilationUnit *cUnit,
ArmLIR *origLIR) {
/*
* We need two separate pushes, since we want r5 to be pushed first.
* Store multiple will push LR first.
*/
ArmLIR *pushFP = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
pushFP->opcode = kThumbPush;
pushFP->operands[0] = 1 << r5FP;
setupResourceMasks(pushFP);
dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushFP);
ArmLIR *pushLR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
pushLR->opcode = kThumbPush;
/* Thumb push can handle LR, but is encoded differently at bit 8 */
pushLR->operands[0] = 1 << 8;
setupResourceMasks(pushLR);
dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushLR);
}
static void genSelfVerificationPostBranch(CompilationUnit *cUnit,
ArmLIR *origLIR) {
/*
* Since Thumb cannot pop memory content into LR, we have to pop LR
* to a temp first (r5 in this case). Then we move r5 to LR, then pop the
* original r5 from stack.
*/
/* Pop memory content(LR) into r5 first */
ArmLIR *popForLR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
popForLR->opcode = kThumbPop;
popForLR->operands[0] = 1 << r5FP;
setupResourceMasks(popForLR);
dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) popForLR);
ArmLIR *copy = genRegCopyNoInsert(cUnit, r14lr, r5FP);
dvmCompilerInsertLIRAfter((LIR *) popForLR, (LIR *) copy);
/* Now restore the original r5 */
ArmLIR *popFP = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
popFP->opcode = kThumbPop;
popFP->operands[0] = 1 << r5FP;
setupResourceMasks(popFP);
dvmCompilerInsertLIRAfter((LIR *) copy, (LIR *) popFP);
}
#endif