blob: d553aaec4ef6ad1e259f280e7f720bc6e117e6ee [file] [log] [blame]
/*
* 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
*
*/
#include "Codegen.h"
static int leadingZeros(u4 val)
{
u4 alt;
int n;
int count;
count = 16;
n = 32;
do {
alt = val >> count;
if (alt != 0) {
n = n - count;
val = alt;
}
count >>= 1;
} while (count);
return n - val;
}
/*
* Determine whether value can be encoded as a Thumb2 modified
* immediate. If not, return -1. If so, return i:imm3:a:bcdefgh form.
*/
static int modifiedImmediate(u4 value)
{
int zLeading;
int zTrailing;
u4 b0 = value & 0xff;
/* Note: case of value==0 must use 0:000:0:0000000 encoding */
if (value <= 0xFF)
return b0; // 0:000:a:bcdefgh
if (value == ((b0 << 16) | b0))
return (0x1 << 8) | b0; /* 0:001:a:bcdefgh */
if (value == ((b0 << 24) | (b0 << 16) | (b0 << 8) | b0))
return (0x3 << 8) | b0; /* 0:011:a:bcdefgh */
b0 = (value >> 8) & 0xff;
if (value == ((b0 << 24) | (b0 << 8)))
return (0x2 << 8) | b0; /* 0:010:a:bcdefgh */
/* Can we do it with rotation? */
zLeading = leadingZeros(value);
zTrailing = 32 - leadingZeros(~value & (value - 1));
/* A run of eight or fewer active bits? */
if ((zLeading + zTrailing) < 24)
return -1; /* No - bail */
/* left-justify the constant, discarding msb (known to be 1) */
value <<= zLeading + 1;
/* Create bcdefgh */
value >>= 25;
/* Put it all together */
return value | ((0x8 + zLeading) << 7); /* [01000..11111]:bcdefgh */
}
/*
* Determine whether value can be encoded as a Thumb2 floating point
* immediate. If not, return -1. If so return encoded 8-bit value.
*/
static int encodeImmDoubleHigh(int value)
{
int res;
int bitA = (value & 0x80000000) >> 31;
int notBitB = (value & 0x40000000) >> 30;
int bitB = (value & 0x20000000) >> 29;
int bSmear = (value & 0x3fc00000) >> 22;
int slice = (value & 0x003f0000) >> 16;
int zeroes = (value & 0x0000ffff);
if (zeroes != 0)
return -1;
if (bitB) {
if ((notBitB != 0) || (bSmear != 0x1f))
return -1;
} else {
if ((notBitB != 1) || (bSmear != 0x0))
return -1;
}
res = (bitA << 7) | (bitB << 6) | slice;
return res;
}
static int encodeImmSingle(int value)
{
int res;
int bitA = (value & 0x80000000) >> 31;
int notBitB = (value & 0x40000000) >> 30;
int bitB = (value & 0x20000000) >> 29;
int bSmear = (value & 0x3e000000) >> 25;
int slice = (value & 0x01f80000) >> 19;
int zeroes = (value & 0x0007ffff);
if (zeroes != 0)
return -1;
if (bitB) {
if ((notBitB != 0) || (bSmear != 0x1f))
return -1;
} else {
if ((notBitB != 1) || (bSmear != 0x0))
return -1;
}
res = (bitA << 7) | (bitB << 6) | slice;
return res;
}
static int encodeImmDouble(int valLo, int valHi)
{
int res = -1;
if (valLo == 0)
res = encodeImmDoubleHigh(valHi);
return res;
}
void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
int srcLo, int srcHi)
{
bool destFP = FPREG(destLo) && FPREG(destHi);
bool srcFP = FPREG(srcLo) && FPREG(srcHi);
assert(FPREG(srcLo) == FPREG(srcHi));
assert(FPREG(destLo) == FPREG(destHi));
if (destFP) {
if (srcFP) {
genRegCopy(cUnit, S2D(destLo, destHi), S2D(srcLo, srcHi));
} else {
newLIR3(cUnit, kThumb2Fmdrr, S2D(destLo, destHi), srcLo, srcHi);
}
} else {
if (srcFP) {
newLIR3(cUnit, kThumb2Fmrrd, destLo, destHi, S2D(srcLo, srcHi));
} else {
// Handle overlap
if (srcHi == destLo) {
genRegCopy(cUnit, destHi, srcHi);
genRegCopy(cUnit, destLo, srcLo);
} else {
genRegCopy(cUnit, destLo, srcLo);
genRegCopy(cUnit, destHi, srcHi);
}
}
}
}
static int coreTemps[] = {r0, r1, r2, r3, r4PC, r7, r8, r9, r10, r11, r12};
static int corePreserved[] = {};
static int fpTemps[] = {fr16, fr17, fr18, fr19, fr20, fr21, fr22, fr23,
fr24, fr25, fr26, fr27, fr28, fr29, fr30, fr31};
static int fpPreserved[] = {};
void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit)
{
int i;
int numTemps = sizeof(coreTemps)/sizeof(int);
int numFPTemps = sizeof(fpTemps)/sizeof(int);
RegisterPool *pool = dvmCompilerNew(sizeof(*pool), true);
cUnit->regPool = pool;
pool->numCoreTemps = numTemps;
pool->coreTemps =
dvmCompilerNew(numTemps * sizeof(*cUnit->regPool->coreTemps), true);
pool->numFPTemps = numFPTemps;
pool->FPTemps =
dvmCompilerNew(numFPTemps * sizeof(*cUnit->regPool->FPTemps), true);
pool->numCoreRegs = 0;
pool->coreRegs = NULL;
pool->numFPRegs = 0;
pool->FPRegs = NULL;
initPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
initPool(pool->FPTemps, fpTemps, pool->numFPTemps);
initPool(pool->coreRegs, NULL, 0);
initPool(pool->FPRegs, NULL, 0);
pool->nullCheckedRegs =
dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
}
/*
* Alloc a pair of core registers, or a double. Low reg in low byte,
* high reg in next byte.
*/
static int allocTypedTempPair(CompilationUnit *cUnit, bool fpHint, int regClass)
{
int highReg;
int lowReg;
int res = 0;
if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg)) {
lowReg = allocTempDouble(cUnit);
highReg = lowReg + 1;
} else {
lowReg = allocTemp(cUnit);
highReg = allocTemp(cUnit);
}
res = (lowReg & 0xff) | ((highReg & 0xff) << 8);
return res;
}
static int allocTypedTemp(CompilationUnit *cUnit, bool fpHint, int regClass)
{
if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg))
return allocTempFloat(cUnit);
return allocTemp(cUnit);
}
static int encodeShift(int code, int amount) {
return ((amount & 0x1f) << 2) | code;
}
/*
* Generate a Thumb2 IT instruction, which can nullify up to
* four subsequent instructions based on a condition and its
* inverse. The condition applies to the first instruction, which
* is executed if the condition is met. The string "guide" consists
* of 0 to 3 chars, and applies to the 2nd through 4th instruction.
* A "T" means the instruction is executed if the condition is
* met, and an "E" means the instruction is executed if the condition
* is not met.
*/
static ArmLIR *genIT(CompilationUnit *cUnit, ArmConditionCode code,
char *guide)
{
int mask;
int condBit = code & 1;
int altBit = condBit ^ 1;
int mask3 = 0;
int mask2 = 0;
int mask1 = 0;
//Note: case fallthroughs intentional
switch(strlen(guide)) {
case 3:
mask1 = (guide[2] == 'T') ? condBit : altBit;
case 2:
mask2 = (guide[1] == 'T') ? condBit : altBit;
case 1:
mask3 = (guide[0] == 'T') ? condBit : altBit;
break;
case 0:
break;
default:
assert(0);
dvmAbort();
}
mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
(1 << (3 - strlen(guide)));
return newLIR2(cUnit, kThumb2It, code, mask);
}
static ArmLIR *fpRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
{
ArmLIR* res = dvmCompilerNew(sizeof(ArmLIR), true);
res->operands[0] = rDest;
res->operands[1] = rSrc;
if (rDest == rSrc) {
res->isNop = true;
} else {
assert(DOUBLEREG(rDest) == DOUBLEREG(rSrc));
if (DOUBLEREG(rDest)) {
res->opCode = kThumb2Vmovd;
} else {
if (SINGLEREG(rDest)) {
res->opCode = SINGLEREG(rSrc) ? kThumb2Vmovs : kThumb2Fmsr;
} else {
assert(SINGLEREG(rSrc));
res->opCode = kThumb2Fmrs;
}
}
res->operands[0] = rDest;
res->operands[1] = rSrc;
}
setupResourceMasks(res);
return res;
}
ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
{
ArmLIR* res;
ArmOpCode opCode;
if (FPREG(rDest) || FPREG(rSrc))
return fpRegCopy(cUnit, rDest, rSrc);
res = 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->isNop = true;
}
return res;
}
/* Export the Dalvik PC assicated with an instruction to the StackSave area */
static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir)
{
ArmLIR *res;
int offset = offsetof(StackSaveArea, xtra.currentPc);
int rDPC = allocTemp(cUnit);
res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
newLIR3(cUnit, kThumb2StrRRI8Predec, rDPC, rFP,
sizeof(StackSaveArea) - offset);
freeTemp(cUnit, rDPC);
return res;
}
static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
{
ArmOpCode opCode = kThumbBkpt;
switch (op) {
case kOpUncondBr:
opCode = kThumbBUncond;
break;
default:
assert(0);
}
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 = ((value & 0xff00) != 0) ? kThumb2Push : kThumbPush;
break;
case kOpPop:
opCode = ((value & 0xff00) != 0) ? kThumb2Pop : kThumbPop;
break;
default:
assert(0);
}
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:
assert(0);
}
return newLIR1(cUnit, opCode, rDestSrc);
}
static ArmLIR *opRegRegShift(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
int rSrc2, int shift)
{
bool thumbForm = ((shift == 0) && LOWREG(rDestSrc1) && LOWREG(rSrc2));
ArmOpCode opCode = kThumbBkpt;
switch (op) {
case kOpAdc:
opCode = (thumbForm) ? kThumbAdcRR : kThumb2AdcRRR;
break;
case kOpAnd:
opCode = (thumbForm) ? kThumbAndRR : kThumb2AndRRR;
break;
case kOpBic:
opCode = (thumbForm) ? kThumbBicRR : kThumb2BicRRR;
break;
case kOpCmn:
assert(shift == 0);
opCode = (thumbForm) ? kThumbCmnRR : kThumb2CmnRR;
break;
case kOpCmp:
if (thumbForm)
opCode = kThumbCmpRR;
else if ((shift == 0) && !LOWREG(rDestSrc1) && !LOWREG(rSrc2))
opCode = kThumbCmpHH;
else if ((shift == 0) && LOWREG(rDestSrc1))
opCode = kThumbCmpLH;
else if (shift == 0)
opCode = kThumbCmpHL;
else
opCode = kThumb2CmpRR;
break;
case kOpXor:
opCode = (thumbForm) ? kThumbEorRR : kThumb2EorRRR;
break;
case kOpMov:
assert(shift == 0);
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:
assert(shift == 0);
opCode = (thumbForm) ? kThumbMul : kThumb2MulRRR;
break;
case kOpMvn:
opCode = (thumbForm) ? kThumbMvn : kThumb2MnvRR;
break;
case kOpNeg:
assert(shift == 0);
opCode = (thumbForm) ? kThumbNeg : kThumb2NegRR;
break;
case kOpOr:
opCode = (thumbForm) ? kThumbOrr : kThumb2OrrRRR;
break;
case kOpSbc:
opCode = (thumbForm) ? kThumbSbc : kThumb2SbcRRR;
break;
case kOpTst:
opCode = (thumbForm) ? kThumbTst : kThumb2TstRR;
break;
case kOpLsl:
assert(shift == 0);
opCode = (thumbForm) ? kThumbLslRR : kThumb2LslRRR;
break;
case kOpLsr:
assert(shift == 0);
opCode = (thumbForm) ? kThumbLsrRR : kThumb2LsrRRR;
break;
case kOpAsr:
assert(shift == 0);
opCode = (thumbForm) ? kThumbAsrRR : kThumb2AsrRRR;
break;
case kOpRor:
assert(shift == 0);
opCode = (thumbForm) ? kThumbRorRR : kThumb2RorRRR;
break;
case kOpAdd:
opCode = (thumbForm) ? kThumbAddRRR : kThumb2AddRRR;
break;
case kOpSub:
opCode = (thumbForm) ? kThumbSubRRR : kThumb2SubRRR;
break;
case kOp2Byte:
assert(shift == 0);
return newLIR4(cUnit, kThumb2Sbfx, rDestSrc1, rSrc2, 0, 8);
case kOp2Short:
assert(shift == 0);
return newLIR4(cUnit, kThumb2Sbfx, rDestSrc1, rSrc2, 0, 16);
case kOp2Char:
assert(shift == 0);
return newLIR4(cUnit, kThumb2Ubfx, rDestSrc1, rSrc2, 0, 16);
default:
assert(0);
break;
}
assert(opCode >= 0);
if (EncodingMap[opCode].flags & IS_BINARY_OP)
return newLIR2(cUnit, opCode, rDestSrc1, rSrc2);
else if (EncodingMap[opCode].flags & IS_TERTIARY_OP) {
if (EncodingMap[opCode].fieldLoc[2].kind == kFmtShift)
return newLIR3(cUnit, opCode, rDestSrc1, rSrc2, shift);
else
return newLIR3(cUnit, opCode, rDestSrc1, rDestSrc1, rSrc2);
} else if (EncodingMap[opCode].flags & IS_QUAD_OP)
return newLIR4(cUnit, opCode, rDestSrc1, rDestSrc1, rSrc2, shift);
else {
assert(0);
return NULL;
}
}
static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
int rSrc2)
{
return opRegRegShift(cUnit, op, rDestSrc1, rSrc2, 0);
}
static ArmLIR *opRegRegRegShift(CompilationUnit *cUnit, OpKind op,
int rDest, int rSrc1, int rSrc2, int shift)
{
ArmOpCode opCode = kThumbBkpt;
bool thumbForm = (shift == 0) && LOWREG(rDest) && LOWREG(rSrc1) &&
LOWREG(rSrc2);
switch (op) {
case kOpAdd:
opCode = (thumbForm) ? kThumbAddRRR : kThumb2AddRRR;
break;
case kOpSub:
opCode = (thumbForm) ? kThumbSubRRR : kThumb2SubRRR;
break;
case kOpAdc:
opCode = kThumb2AdcRRR;
break;
case kOpAnd:
opCode = kThumb2AndRRR;
break;
case kOpBic:
opCode = kThumb2BicRRR;
break;
case kOpXor:
opCode = kThumb2EorRRR;
break;
case kOpMul:
assert(shift == 0);
opCode = kThumb2MulRRR;
break;
case kOpOr:
opCode = kThumb2OrrRRR;
break;
case kOpSbc:
opCode = kThumb2SbcRRR;
break;
case kOpLsl:
assert(shift == 0);
opCode = kThumb2LslRRR;
break;
case kOpLsr:
assert(shift == 0);
opCode = kThumb2LsrRRR;
break;
case kOpAsr:
assert(shift == 0);
opCode = kThumb2AsrRRR;
break;
case kOpRor:
assert(shift == 0);
opCode = kThumb2RorRRR;
break;
default:
assert(0);
break;
}
assert(opCode >= 0);
if (EncodingMap[opCode].flags & IS_QUAD_OP)
return newLIR4(cUnit, opCode, rDest, rSrc1, rSrc2, shift);
else {
assert(EncodingMap[opCode].flags & IS_TERTIARY_OP);
return newLIR3(cUnit, opCode, rDest, rSrc1, rSrc2);
}
}
static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
int rSrc1, int rSrc2)
{
return opRegRegRegShift(cUnit, op, rDest, rSrc1, rSrc2, 0);
}
static void genLong3Addr(CompilationUnit *cUnit, OpKind firstOp,
OpKind secondOp, RegLocation rlDest,
RegLocation rlSrc1, RegLocation rlSrc2)
{
RegLocation rlResult;
rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
rlResult = evalLoc(cUnit, rlDest, kCoreReg, true);
opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
rlSrc2.highReg);
storeValueWide(cUnit, rlDest, rlResult);
}
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;
ArmOpCode altOpCode = kThumbBkpt;
bool allLowRegs = (LOWREG(rDest) && LOWREG(rSrc1));
int modImm = modifiedImmediate(value);
int modImmNeg = modifiedImmediate(-value);
switch(op) {
case kOpLsl:
if (allLowRegs)
return newLIR3(cUnit, kThumbLslRRI5, rDest, rSrc1, value);
else
return newLIR3(cUnit, kThumb2LslRRI5, rDest, rSrc1, value);
case kOpLsr:
if (allLowRegs)
return newLIR3(cUnit, kThumbLsrRRI5, rDest, rSrc1, value);
else
return newLIR3(cUnit, kThumb2LsrRRI5, rDest, rSrc1, value);
case kOpAsr:
if (allLowRegs)
return newLIR3(cUnit, kThumbAsrRRI5, rDest, rSrc1, value);
else
return newLIR3(cUnit, kThumb2AsrRRI5, rDest, rSrc1, value);
case kOpRor:
return newLIR3(cUnit, kThumb2RorRRI5, rDest, rSrc1, value);
case kOpAdd:
if (LOWREG(rDest) && (rSrc1 == 13) &&
(value <= 1020) && ((value & 0x3)==0)) {
return newLIR3(cUnit, kThumbAddSpRel, rDest, rSrc1,
value >> 2);
} else if (LOWREG(rDest) && (rSrc1 == rpc) &&
(value <= 1020) && ((value & 0x3)==0)) {
return newLIR3(cUnit, kThumbAddPcRel, rDest, rSrc1,
value >> 2);
}
opCode = kThumb2AddRRI8;
altOpCode = kThumb2AddRRR;
// Note: intentional fallthrough
case kOpSub:
if (allLowRegs && ((absValue & 0x7) == absValue)) {
if (op == kOpAdd)
opCode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
else
opCode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
return newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
} else if ((absValue & 0xff) == absValue) {
if (op == kOpAdd)
opCode = (neg) ? kThumb2SubRRI12 : kThumb2AddRRI12;
else
opCode = (neg) ? kThumb2AddRRI12 : kThumb2SubRRI12;
return newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
}
if (modImmNeg >= 0) {
op = (op == kOpAdd) ? kOpSub : kOpAdd;
modImm = modImmNeg;
}
if (op == kOpSub) {
opCode = kThumb2SubRRI8;
altOpCode = kThumb2SubRRR;
}
break;
case kOpAdc:
opCode = kThumb2AdcRRI8;
altOpCode = kThumb2AdcRRR;
break;
case kOpSbc:
opCode = kThumb2SbcRRI8;
altOpCode = kThumb2SbcRRR;
break;
case kOpOr:
opCode = kThumb2OrrRRI8;
altOpCode = kThumb2OrrRRR;
break;
case kOpAnd:
opCode = kThumb2AndRRI8;
altOpCode = kThumb2AndRRR;
break;
case kOpXor:
opCode = kThumb2EorRRI8;
altOpCode = kThumb2EorRRR;
break;
case kOpMul:
//TUNING: power of 2, shift & add
modImm = -1;
altOpCode = kThumb2MulRRR;
break;
case kOpCmp: {
int modImm = modifiedImmediate(value);
ArmLIR *res;
if (modImm >= 0) {
res = newLIR2(cUnit, kThumb2CmpRI8, rSrc1, modImm);
} else {
int rTmp = allocTemp(cUnit);
res = loadConstant(cUnit, rTmp, value);
opRegReg(cUnit, kOpCmp, rSrc1, rTmp);
freeTemp(cUnit, rTmp);
}
return res;
}
default:
assert(0);
}
if (modImm >= 0) {
return newLIR3(cUnit, opCode, rDest, rSrc1, modImm);
} else {
int rScratch = allocTemp(cUnit);
loadConstant(cUnit, rScratch, value);
if (EncodingMap[altOpCode].flags & IS_QUAD_OP)
res = newLIR4(cUnit, altOpCode, rDest, rSrc1, rScratch, 0);
else
res = newLIR3(cUnit, altOpCode, rDest, rSrc1, rScratch);
freeTemp(cUnit, rScratch);
return res;
}
}
/* Handle Thumb-only variants here - otherwise punt to opRegRegImm */
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) && LOWREG(rDestSrc1));
ArmOpCode opCode = kThumbBkpt;
switch (op) {
case kOpAdd:
if ( !neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
assert((value & 0x3) == 0);
return newLIR1(cUnit, kThumbAddSpI7, value >> 2);
} else if (shortForm) {
opCode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
}
break;
case kOpSub:
if (!neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
assert((value & 0x3) == 0);
return newLIR1(cUnit, kThumbSubSpI7, value >> 2);
} else if (shortForm) {
opCode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
}
break;
case kOpCmp:
if (LOWREG(rDestSrc1) && shortForm)
opCode = (shortForm) ? kThumbCmpRI8 : kThumbCmpRR;
else if (LOWREG(rDestSrc1))
opCode = kThumbCmpRR;
else {
shortForm = false;
opCode = kThumbCmpHL;
}
break;
default:
/* Punt to opRegRegImm - if bad case catch it there */
shortForm = false;
break;
}
if (shortForm)
return newLIR2(cUnit, opCode, rDestSrc1, absValue);
else {
return opRegRegImm(cUnit, op, rDestSrc1, rDestSrc1, value);
}
}
static void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest,
RegLocation rlSrc)
{
RegLocation rlResult;
rlSrc = loadValue(cUnit, rlSrc, kFPReg);
rlResult = evalLoc(cUnit, rlDest, kFPReg, true);
newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
storeValue(cUnit, rlDest, rlResult);
}
static void genNegDouble(CompilationUnit *cUnit, RegLocation rlDest,
RegLocation rlSrc)
{
RegLocation rlResult;
rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
rlResult = evalLoc(cUnit, rlDest, kFPReg, true);
newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
S2D(rlSrc.lowReg, rlSrc.highReg));
storeValueWide(cUnit, rlDest, rlResult);
}
/*
* To avoid possible conflicts, we use a lot of temps here. Note that
* our usage of Thumb2 instruction forms avoids the problems with register
* reuse for multiply instructions prior to arm6.
*/
static void genMulLong(CompilationUnit *cUnit, RegLocation rlDest,
RegLocation rlSrc1, RegLocation rlSrc2)
{
RegLocation rlResult;
int resLo = allocTemp(cUnit);
int resHi = allocTemp(cUnit);
int tmp1 = allocTemp(cUnit);
rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
newLIR3(cUnit, kThumb2MulRRR, tmp1, rlSrc2.lowReg, rlSrc1.highReg);
newLIR4(cUnit, kThumb2Umull, resLo, resHi, rlSrc2.lowReg, rlSrc1.lowReg);
newLIR4(cUnit, kThumb2Mla, tmp1, rlSrc1.lowReg, rlSrc2.highReg, tmp1);
newLIR4(cUnit, kThumb2AddRRR, resHi, tmp1, resHi, 0);
freeTemp(cUnit, tmp1);
rlResult = getReturnLocWide(cUnit); // Just as a template, will patch
rlResult.lowReg = resLo;
rlResult.highReg = resHi;
storeValueWide(cUnit, rlDest, rlResult);
}
/*
* Handle simple case (thin lock) inline. If it's complicated, bail
* out to the heavyweight lock/unlock routines. We'll use dedicated
* registers here in order to be in the right position in case we
* to bail to dvm[Lock/Unlock]Object(self, object)
*
* r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
* r1 -> object [arg1 for dvm[Lock/Unlock]Object
* r2 -> intial contents of object->lock.thin, later result of strex
* r3 -> self->threadId
* r7 -> temp to hold new lock value [unlock only]
* r4 -> allow to be used by utilities as general temp
*
* The result of the strex is 0 if we acquire the lock.
*/
static void handleMonitor(CompilationUnit *cUnit, MIR *mir)
{
#if defined (THIN_LOCKING)
RegLocation rlSrc = getSrcLoc(cUnit, mir, 0);
bool enter = (mir->dalvikInsn.opCode == OP_MONITOR_ENTER);
ArmLIR *target;
ArmLIR *branch;
loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj
lockAllTemps(cUnit); // Prepare for explicit register usage
freeTemp(cUnit, r4PC); // Free up r4 for general use
loadWordDisp(cUnit, rGLUE, offsetof(InterpState, self), r0); // Get self
genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
loadWordDisp(cUnit, r0, offsetof(Thread, threadId), r3); // Get threadId
newLIR3(cUnit, kThumb2Ldrex, r2, r1,
offsetof(Object, lock.thin) >> 2); // Get object->lock.thin
// Is lock.thin unheld on lock or held by us (==threadId) on unlock?
if (enter) {
opRegImm(cUnit, kOpSub, r2, DVM_LOCK_INITIAL_THIN_VALUE);
} else {
loadConstant(cUnit, r7, DVM_LOCK_INITIAL_THIN_VALUE);
opRegReg(cUnit, kOpSub, r2, r3);
}
// Note: start of IT block. If last sub result != clear, else strex
genIT(cUnit, kArmCondNe, "E");
newLIR0(cUnit, kThumb2Clrex);
if (enter) {
newLIR4(cUnit, kThumb2Strex, r2, r3, r1,
offsetof(Object, lock.thin) >> 2);
} else {
newLIR4(cUnit, kThumb2Strex, r2, r7, r1,
offsetof(Object, lock.thin) >> 2);
}
// Note: end of IT block
branch = newLIR2(cUnit, kThumb2Cbz, r2, 0);
if (enter) {
loadConstant(cUnit, r7, (int)dvmLockObject);
} else {
loadConstant(cUnit, r7, (int)dvmUnlockObject);
}
genExportPC(cUnit, mir);
opReg(cUnit, kOpBlx, r7);
clobberCallRegs(cUnit);
// Resume here
target = newLIR0(cUnit, kArmPseudoTargetLabel);
target->defMask = ENCODE_ALL;
branch->generic.target = (LIR *)target;
#else
handleMonitorPortable(cUnit, mir);
#endif
}
/*
* 64-bit 3way compare function.
* mov r7, #-1
* cmp op1hi, op2hi
* blt done
* bgt flip
* sub r7, op1lo, op2lo (treat as unsigned)
* beq done
* ite hi
* mov(hi) r7, #-1
* mov(!hi) r7, #1
* flip:
* neg r7
* done:
*/
static void genCmpLong(CompilationUnit *cUnit, MIR *mir,
RegLocation rlDest, RegLocation rlSrc1,
RegLocation rlSrc2)
{
RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
ArmLIR *target1;
ArmLIR *target2;
rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
rlTemp.lowReg = allocTemp(cUnit);
loadConstant(cUnit, rlTemp.lowReg, -1);
opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
ArmLIR *branch1 = opCondBranch(cUnit, kArmCondLt);
ArmLIR *branch2 = opCondBranch(cUnit, kArmCondGt);
opRegRegReg(cUnit, kOpSub, rlTemp.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
ArmLIR *branch3 = opCondBranch(cUnit, kArmCondEq);
genIT(cUnit, kArmCondHi, "E");
newLIR2(cUnit, kThumb2MovImmShift, rlTemp.lowReg, modifiedImmediate(-1));
loadConstant(cUnit, rlTemp.lowReg, 1);
genBarrier(cUnit);
target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
target2->defMask = -1;
opRegReg(cUnit, kOpNeg, rlTemp.lowReg, rlTemp.lowReg);
target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
target1->defMask = -1;
storeValue(cUnit, rlDest, rlTemp);
branch1->generic.target = (LIR *)target1;
branch2->generic.target = (LIR *)target2;
branch3->generic.target = branch1->generic.target;
}
static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
int rIndex, int rDest, int scale, OpSize size)
{
bool allLowRegs = LOWREG(rBase) && LOWREG(rIndex) && LOWREG(rDest);
ArmOpCode opCode = kThumbBkpt;
bool thumbForm = (allLowRegs && (scale == 0));
int regPtr;
if (FPREG(rDest)) {
assert(SINGLEREG(rDest));
assert((size == kWord) || (size == kSingle));
opCode = kThumb2Vldrs;
size = kSingle;
} else {
if (size == kSingle)
size = kWord;
}
switch (size) {
case kSingle:
regPtr = allocTemp(cUnit);
if (scale) {
newLIR4(cUnit, kThumb2AddRRR, regPtr, rBase, rIndex,
encodeShift(kArmLsl, scale));
} else {
opRegRegReg(cUnit, kOpAdd, regPtr, rBase, rIndex);
}
return newLIR3(cUnit, opCode, rDest, regPtr, 0);
case kWord:
opCode = (thumbForm) ? kThumbLdrRRR : kThumb2LdrRRR;
break;
case kUnsignedHalf:
opCode = (thumbForm) ? kThumbLdrhRRR : kThumb2LdrhRRR;
break;
case kSignedHalf:
opCode = (thumbForm) ? kThumbLdrshRRR : kThumb2LdrshRRR;
break;
case kUnsignedByte:
opCode = (thumbForm) ? kThumbLdrbRRR : kThumb2LdrbRRR;
break;
case kSignedByte:
opCode = (thumbForm) ? kThumbLdrsbRRR : kThumb2LdrsbRRR;
break;
default:
assert(0);
}
if (thumbForm)
return newLIR3(cUnit, opCode, rDest, rBase, rIndex);
else
return newLIR4(cUnit, opCode, rDest, rBase, rIndex, scale);
}
static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
int rIndex, int rSrc, int scale, OpSize size)
{
bool allLowRegs = LOWREG(rBase) && LOWREG(rIndex) && LOWREG(rSrc);
ArmOpCode opCode = kThumbBkpt;
bool thumbForm = (allLowRegs && (scale == 0));
int regPtr;
if (FPREG(rSrc)) {
assert(SINGLEREG(rSrc));
assert((size == kWord) || (size == kSingle));
opCode = kThumb2Vstrs;
size = kSingle;
} else {
if (size == kSingle)
size = kWord;
}
switch (size) {
case kSingle:
regPtr = allocTemp(cUnit);
if (scale) {
newLIR4(cUnit, kThumb2AddRRR, regPtr, rBase, rIndex,
encodeShift(kArmLsl, scale));
} else {
opRegRegReg(cUnit, kOpAdd, regPtr, rBase, rIndex);
}
return newLIR3(cUnit, opCode, rSrc, regPtr, 0);
case kWord:
opCode = (thumbForm) ? kThumbStrRRR : kThumb2StrRRR;
break;
case kUnsignedHalf:
case kSignedHalf:
opCode = (thumbForm) ? kThumbStrhRRR : kThumb2StrhRRR;
break;
case kUnsignedByte:
case kSignedByte:
opCode = (thumbForm) ? kThumbStrbRRR : kThumb2StrbRRR;
break;
default:
assert(0);
}
if (thumbForm)
return newLIR3(cUnit, opCode, rSrc, rBase, rIndex);
else
return newLIR4(cUnit, opCode, rSrc, rBase, rIndex, scale);
}
/* Load a float to a Dalvik register. */
static ArmLIR *fpVarAccess(CompilationUnit *cUnit, int vSrcDest,
int rSrcDest, ArmOpCode opCode)
{
ArmLIR *res;
if (vSrcDest > 255) {
int rTmp = allocTemp(cUnit);
opRegRegImm(cUnit, kOpAdd, rTmp, rFP, vSrcDest * 4);
res = newLIR3(cUnit, opCode, rSrcDest, rTmp, 0);
freeTemp(cUnit, rTmp);
} else {
res = newLIR3(cUnit, opCode, rSrcDest, rFP, vSrcDest);
}
return res;
}
/*
* 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.
*/
static ArmLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
int displacement, int rDest, int rDestHi,
OpSize size, bool nullCheck, int sReg)
{
ArmLIR *first = NULL;
ArmLIR *res, *load;
ArmOpCode opCode = kThumbBkpt;
bool shortForm = false;
bool thumb2Form = (displacement < 4092 && displacement >= 0);
int shortMax = 128;
bool allLowRegs = (LOWREG(rBase) && LOWREG(rDest));
int encodedDisp = displacement;
switch (size) {
case kDouble:
case kLong:
if (FPREG(rDest)) {
if (SINGLEREG(rDest)) {
assert(FPREG(rDestHi));
rDest = S2D(rDest, rDestHi);
}
opCode = kThumb2Vldrd;
if (displacement <= 1020) {
shortForm = true;
encodedDisp >>= 2;
}
break;
} else {
res = loadBaseDispBody(cUnit, mir, rBase, displacement, rDest,
-1, kWord, nullCheck, sReg);
loadBaseDispBody(cUnit, NULL, rBase, displacement + 4, rDestHi,
-1, kWord, false, INVALID_SREG);
return res;
}
case kSingle:
case kWord:
if (FPREG(rDest)) {
opCode = kThumb2Vldrs;
if (displacement <= 1020) {
shortForm = true;
encodedDisp >>= 2;
}
break;
}
if (LOWREG(rDest) && (rBase == rpc) &&
(displacement <= 1020) && (displacement >= 0)) {
shortForm = true;
encodedDisp >>= 2;
opCode = kThumbLdrPcRel;
} else if (LOWREG(rDest) && (rBase == r13) &&
(displacement <= 1020) && (displacement >= 0)) {
shortForm = true;
encodedDisp >>= 2;
opCode = kThumbLdrSpRel;
} else if (allLowRegs && displacement < 128 && displacement >= 0) {
assert((displacement & 0x3) == 0);
shortForm = true;
encodedDisp >>= 2;
opCode = kThumbLdrRRI5;
} else if (thumb2Form) {
shortForm = true;
opCode = kThumb2LdrRRI12;
}
break;
case kUnsignedHalf:
if (allLowRegs && displacement < 64 && displacement >= 0) {
assert((displacement & 0x1) == 0);
shortForm = true;
encodedDisp >>= 1;
opCode = kThumbLdrhRRI5;
} else if (displacement < 4092 && displacement >= 0) {
shortForm = true;
opCode = kThumb2LdrhRRI12;
}
break;
case kSignedHalf:
if (thumb2Form) {
shortForm = true;
opCode = kThumb2LdrshRRI12;
}
break;
case kUnsignedByte:
if (allLowRegs && displacement < 32 && displacement >= 0) {
shortForm = true;
opCode = kThumbLdrbRRI5;
} else if (thumb2Form) {
shortForm = true;
opCode = kThumb2LdrbRRI12;
}
break;
case kSignedByte:
if (thumb2Form) {
shortForm = true;
opCode = kThumb2LdrsbRRI12;
}
break;
default:
assert(0);
}
if (nullCheck)
first = genNullCheck(cUnit, sReg, rBase, mir->offset, NULL);
if (shortForm) {
load = res = newLIR3(cUnit, opCode, rDest, rBase, encodedDisp);
} else {
int regOffset = allocTemp(cUnit);
res = loadConstant(cUnit, regOffset, encodedDisp);
load = loadBaseIndexed(cUnit, rBase, regOffset, rDest, 0, size);
freeTemp(cUnit, regOffset);
}
if (rBase == rFP) {
annotateDalvikRegAccess(load, displacement >> 2, true /* isLoad */);
}
return (first) ? first : res;
}
static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
int displacement, int rDest, OpSize size,
bool nullCheck, int sReg)
{
return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
size, nullCheck, sReg);
}
static ArmLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
int displacement, int rDestLo, int rDestHi,
bool nullCheck, int sReg)
{
return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
kLong, nullCheck, sReg);
}
static ArmLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
int displacement, int rSrc, int rSrcHi,
OpSize size)
{
ArmLIR *res, *store;
ArmOpCode opCode = kThumbBkpt;
bool shortForm = false;
bool thumb2Form = (displacement < 4092 && displacement >= 0);
int shortMax = 128;
bool allLowRegs = (LOWREG(rBase) && LOWREG(rSrc));
int encodedDisp = displacement;
switch (size) {
case kLong:
case kDouble:
if (!FPREG(rSrc)) {
res = storeBaseDispBody(cUnit, rBase, displacement, rSrc,
-1, kWord);
storeBaseDispBody(cUnit, rBase, displacement + 4, rSrcHi,
-1, kWord);
return res;
}
if (SINGLEREG(rSrc)) {
assert(FPREG(rSrcHi));
rSrc = S2D(rSrc, rSrcHi);
}
opCode = kThumb2Vstrd;
if (displacement <= 1020) {
shortForm = true;
encodedDisp >>= 2;
}
break;
case kSingle:
case kWord:
if (FPREG(rSrc)) {
assert(SINGLEREG(rSrc));
opCode = kThumb2Vstrs;
if (displacement <= 1020) {
shortForm = true;
encodedDisp >>= 2;
}
break;
}
if (allLowRegs && displacement < 128 && displacement >= 0) {
assert((displacement & 0x3) == 0);
shortForm = true;
encodedDisp >>= 2;
opCode = kThumbStrRRI5;
} else if (thumb2Form) {
shortForm = true;
opCode = kThumb2StrRRI12;
}
break;
case kUnsignedHalf:
case kSignedHalf:
if (allLowRegs && displacement < 64 && displacement >= 0) {
assert((displacement & 0x1) == 0);
shortForm = true;
encodedDisp >>= 1;
opCode = kThumbStrhRRI5;
} else if (thumb2Form) {
shortForm = true;
opCode = kThumb2StrhRRI12;
}
break;
case kUnsignedByte:
case kSignedByte:
if (allLowRegs && displacement < 32 && displacement >= 0) {
shortForm = true;
opCode = kThumbStrbRRI5;
} else if (thumb2Form) {
shortForm = true;
opCode = kThumb2StrhRRI12;
}
break;
default:
assert(0);
}
if (shortForm) {
store = res = newLIR3(cUnit, opCode, rSrc, rBase, encodedDisp);
} else {
int rScratch = allocTemp(cUnit);
res = loadConstant(cUnit, rScratch, encodedDisp);
store = storeBaseIndexed(cUnit, rBase, rScratch, rSrc, 0, size);
freeTemp(cUnit, rScratch);
}
if (rBase == rFP) {
annotateDalvikRegAccess(store, displacement >> 2, false /* isLoad */);
}
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 ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
{
ArmLIR *res;
genBarrier(cUnit);
if (LOWREG(rBase) && ((rMask & 0xff)==rMask)) {
res = newLIR2(cUnit, kThumbLdmia, rBase, rMask);
} else {
res = newLIR2(cUnit, kThumb2Ldmia, rBase, rMask);
}
genBarrier(cUnit);
return res;
}
static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
{
ArmLIR *res;
genBarrier(cUnit);
if (LOWREG(rBase) && ((rMask & 0xff)==rMask)) {
res = newLIR2(cUnit, kThumbStmia, rBase, rMask);
} else {
res = newLIR2(cUnit, kThumb2Stmia, rBase, rMask);
}
genBarrier(cUnit);
return res;
}
static ArmLIR *loadFPConstantValue(CompilationUnit *cUnit, int rDest,
int value)
{
int encodedImm = encodeImmSingle(value);
assert(SINGLEREG(rDest));
if (encodedImm >= 0) {
return newLIR2(cUnit, kThumb2Vmovs_IMM8, rDest, encodedImm);
}
ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 0);
if (dataTarget == NULL) {
dataTarget = addWordData(cUnit, value, false);
}
ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
loadPcRel->opCode = kThumb2Vldrs;
loadPcRel->generic.target = (LIR *) dataTarget;
loadPcRel->operands[0] = rDest;
loadPcRel->operands[1] = rpc;
setupResourceMasks(loadPcRel);
dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
return loadPcRel;
}
static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
{
storeBaseDispWide(cUnit, base, 0, lowReg, highReg);
}
static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
{
loadBaseDispWide(cUnit, NULL, base, 0, lowReg, highReg, false,
INVALID_SREG);
}
/*
* Load a immediate using a shortcut if possible; otherwise
* grab from the per-translation literal pool.
*/
static ArmLIR *loadConstantValue(CompilationUnit *cUnit, int rDest, int value)
{
ArmLIR *res;
int modImm;
if (FPREG(rDest)) {
return loadFPConstantValue(cUnit, rDest, value);
}
/* See if the value can be constructed cheaply */
if (LOWREG(rDest) && (value >= 0) && (value <= 255)) {
return newLIR2(cUnit, kThumbMovImm, rDest, value);
}
/* Check Modified immediate special cases */
modImm = modifiedImmediate(value);
if (modImm >= 0) {
res = newLIR2(cUnit, kThumb2MovImmShift, rDest, modImm);
return res;
}
modImm = modifiedImmediate(~value);
if (modImm >= 0) {
res = newLIR2(cUnit, kThumb2MvnImmShift, rDest, modImm);
return res;
}
/* 16-bit immediate? */
if ((value & 0xffff) == value) {
res = newLIR2(cUnit, kThumb2MovImm16, rDest, value);
return res;
}
/* No shortcut - go ahead and use literal pool */
ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 255);
if (dataTarget == NULL) {
dataTarget = addWordData(cUnit, value, false);
}
ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
loadPcRel->opCode = LOWREG(rDest) ? kThumbLdrPcRel : kThumb2LdrPcRel12;
loadPcRel->generic.target = (LIR *) dataTarget;
loadPcRel->operands[0] = rDest;
setupResourceMasks(loadPcRel);
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) {
opRegImm(cUnit, kOpAdd, rDest, value - dataTarget->operands[0]);
}
return res;
}
static ArmLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
int rDestHi, int valLo, int valHi)
{
int encodedImm = encodeImmDouble(valLo, valHi);
ArmLIR *res;
if (FPREG(rDestLo) && (encodedImm >= 0)) {
res = newLIR2(cUnit, kThumb2Vmovd_IMM8, S2D(rDestLo, rDestHi),
encodedImm);
} else {
res = loadConstantValue(cUnit, rDestLo, valLo);
loadConstantValue(cUnit, rDestHi, valHi);
}
return res;
}
/*
* Perform a "reg cmp imm" operation and jump to the PCR region if condition
* satisfies.
*/
static ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
ArmConditionCode cond, int reg,
int checkValue, int dOffset,
ArmLIR *pcrLabel)
{
ArmLIR *branch;
int modImm;
/*
* TODO: re-enable usage of kThumb2Cbz & kThumb2Cbnz once assembler is
* enhanced to allow us to replace code patterns when instructions don't
* reach. Currently, CB[N]Z is causing too many assembler aborts.
* What we want to do is emit the short forms, and then replace them with
* longer versions when needed.
*/
if (0 && (LOWREG(reg)) && (checkValue == 0) &&
((cond == kArmCondEq) || (cond == kArmCondNe))) {
branch = newLIR2(cUnit,
(cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
reg, 0);
} else {
modImm = modifiedImmediate(checkValue);
if (LOWREG(reg) && ((checkValue & 0xff) == checkValue)) {
newLIR2(cUnit, kThumbCmpRI8, reg, checkValue);
} else if (modImm >= 0) {
newLIR2(cUnit, kThumb2CmpRI8, reg, modImm);
} else {
int tReg = allocTemp(cUnit);
loadConstant(cUnit, tReg, checkValue);
opRegReg(cUnit, kOpCmp, reg, tReg);
}
branch = newLIR2(cUnit, kThumbBCond, 0, cond);
}
return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
}
static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
{
RegLocation rlObj = getSrcLoc(cUnit, mir, 0);
RegLocation rlDest = inlinedTarget(cUnit, mir, false);
rlObj = loadValue(cUnit, rlObj, kCoreReg);
RegLocation rlResult = evalLoc(cUnit, rlDest, kCoreReg, true);
genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, NULL);
loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count,
rlResult.lowReg);
storeValue(cUnit, rlDest, rlResult);
return false;
}
static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
{
int contents = offsetof(ArrayObject, contents);
RegLocation rlObj = getSrcLoc(cUnit, mir, 0);
RegLocation rlIdx = getSrcLoc(cUnit, mir, 1);
RegLocation rlDest = inlinedTarget(cUnit, mir, false);
RegLocation rlResult;
rlObj = loadValue(cUnit, rlObj, kCoreReg);
rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
int regMax = allocTemp(cUnit);
int regOff = allocTemp(cUnit);
int regPtr = allocTemp(cUnit);
ArmLIR *pcrLabel = genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg,
mir->offset, NULL);
loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count, regMax);
loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_offset, regOff);
loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_value, regPtr);
genBoundsCheck(cUnit, rlIdx.lowReg, regMax, mir->offset, pcrLabel);
freeTemp(cUnit, regMax);
opRegImm(cUnit, kOpAdd, regPtr, contents);
opRegReg(cUnit, kOpAdd, regOff, rlIdx.lowReg);
rlResult = evalLoc(cUnit, rlDest, kCoreReg, true);
loadBaseIndexed(cUnit, regPtr, regOff, rlResult.lowReg, 1, kUnsignedHalf);
storeValue(cUnit, rlDest, rlResult);
return false;
}
static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
{
RegLocation rlSrc = getSrcLoc(cUnit, mir, 0);
rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
RegLocation rlDest = inlinedTarget(cUnit, mir, false);;
RegLocation rlResult = evalLoc(cUnit, rlDest, kCoreReg, true);
int signReg = allocTemp(cUnit);
/*
* abs(x) = y<=x>>31, (x+y)^y.
* Thumb2's IT block also yields 3 instructions, but imposes
* scheduling constraints.
*/
opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.lowReg, 31);
opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
storeValue(cUnit, rlDest, rlResult);
return false;
}
static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
{
RegLocation rlSrc = getSrcLoc(cUnit, mir, 0);
RegLocation rlDest = inlinedTarget(cUnit, mir, true);
rlSrc = loadValue(cUnit, rlSrc, kFPReg);
RegLocation rlResult = evalLoc(cUnit, rlDest, kFPReg, true);
newLIR2(cUnit, kThumb2Vabss, rlResult.lowReg, rlSrc.lowReg);
storeValue(cUnit, rlDest, rlResult);
return true;
}
static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
{
RegLocation rlSrc = getSrcLocWide(cUnit, mir, 0, 1);
RegLocation rlDest = inlinedTargetWide(cUnit, mir, true);
rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
RegLocation rlResult = evalLoc(cUnit, rlDest, kFPReg, true);
newLIR2(cUnit, kThumb2Vabsd, S2D(rlResult.lowReg, rlResult.highReg),
S2D(rlSrc.lowReg, rlSrc.highReg));
storeValueWide(cUnit, rlDest, rlResult);
return true;
}
static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
{
RegLocation rlSrc1 = getSrcLoc(cUnit, mir, 0);
RegLocation rlSrc2 = getSrcLoc(cUnit, mir, 1);
rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
RegLocation rlDest = inlinedTarget(cUnit, mir, false);
RegLocation rlResult = evalLoc(cUnit, rlDest, kCoreReg, true);
opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg);
genIT(cUnit, (isMin) ? kArmCondGt : kArmCondLt, "E");
opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc2.lowReg);
opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc1.lowReg);
genBarrier(cUnit);
storeValue(cUnit, rlDest, rlResult);
return false;
}
static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
{
RegLocation rlSrc = getSrcLocWide(cUnit, mir, 0, 1);
RegLocation rlDest = inlinedTargetWide(cUnit, mir, false);
rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
RegLocation rlResult = evalLoc(cUnit, rlDest, kCoreReg, true);
int signReg = allocTemp(cUnit);
/*
* abs(x) = y<=x>>31, (x+y)^y.
* Thumb2 IT block allows slightly shorter sequence,
* but introduces a scheduling barrier. Stick with this
* mechanism for now.
*/
opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.highReg, 31);
opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
opRegRegReg(cUnit, kOpAdc, rlResult.highReg, rlSrc.highReg, signReg);
opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
opRegReg(cUnit, kOpXor, rlResult.highReg, signReg);
storeValueWide(cUnit, rlDest, rlResult);
return false;
}