blob: 0be162180bd2b627f442c39a36ddaf6ad10e74d1 [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"
/* Forward decls */
static ArmLIR *genNullCheck(CompilationUnit *cUnit, int vReg, int mReg,
int dOffset, ArmLIR *pcrLabel);
static ArmLIR *loadValueAddress(CompilationUnit *cUnit, int vSrc, int rDest);
static ArmLIR *loadValue(CompilationUnit *cUnit, int vSrc, int rDest);
static ArmLIR *loadWordDisp(CompilationUnit *cUnit, int rBase,
int displacement, int rDest);
static ArmLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
int displacement, int rSrc, int rScratch);
static ArmLIR *storeValue(CompilationUnit *cUnit, int rSrc, int vDest,
int rScratch);
static ArmLIR *genConditionalBranch(CompilationUnit *cUnit,
ArmConditionCode cond,
ArmLIR *target);
static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target);
static ArmLIR *loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo,
int rDestHi);
static ArmLIR *storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi,
int vDest, int rScratch);
static ArmLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
int rBound, int dOffset, ArmLIR *pcrLabel);
static ArmLIR *genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
static int inlinedTarget(MIR *mir);
/* Routines which must be supplied here */
static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value);
static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC,
int rAddr);
static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
int displacement, int rDest, OpSize size,
bool nullCheck, int vReg);
static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
int displacement, int rSrc, OpSize size,
int rScratch);
static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
ArmConditionCode cond, int reg,
int checkValue, int dOffset,
ArmLIR *pcrLabel);
static inline ArmLIR *genRegRegCheck(CompilationUnit *cUnit,
ArmConditionCode cond,
int reg1, int reg2, int dOffset,
ArmLIR *pcrLabel);
ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask);
static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask);
static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op);
static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value);
static ArmLIR *opImmImm(CompilationUnit *cUnit, OpKind op, int value1,
int value2);
static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc);
static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
int rSrc2);
static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
int value, int rScratch);
static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
int rSrc1, int value, int rScratch);
static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
int rSrc1, int rSrc2);
static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
int rIndex, int rDest, int scale, OpSize size);
static void genCmpLong(CompilationUnit *cUnit, MIR *mir, int vDest, int vSrc1,
int vSrc2);
static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir);
static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir);
static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir);
static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir);
static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir);
static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin);
static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir);
/*
* Support for register allocation
*/
/* get the next register in r0..r3 in a round-robin fashion */
#define NEXT_REG(reg) ((reg + 1) & 3)
/*
* The following are utility routines to help maintain the RegisterScoreboard
* state to facilitate register renaming.
*/
/* Reset the tracker to unknown state */
static inline void resetRegisterScoreboard(CompilationUnit *cUnit)
{
RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
dvmClearAllBits(registerScoreboard->nullCheckedRegs);
registerScoreboard->liveDalvikReg = vNone;
registerScoreboard->nativeReg = vNone;
registerScoreboard->nativeRegHi = vNone;
}
/* Kill the corresponding bit in the null-checked register list */
static inline void killNullCheckedRegister(CompilationUnit *cUnit, int vReg)
{
dvmClearBit(cUnit->registerScoreboard.nullCheckedRegs, vReg);
}
/* The Dalvik register pair held in native registers have changed */
static inline void updateLiveRegisterPair(CompilationUnit *cUnit,
int vReg, int mRegLo, int mRegHi)
{
cUnit->registerScoreboard.liveDalvikReg = vReg;
cUnit->registerScoreboard.nativeReg = mRegLo;
cUnit->registerScoreboard.nativeRegHi = mRegHi;
cUnit->registerScoreboard.isWide = true;
}
/* The Dalvik register held in a native register has changed */
static inline void updateLiveRegister(CompilationUnit *cUnit,
int vReg, int mReg)
{
cUnit->registerScoreboard.liveDalvikReg = vReg;
cUnit->registerScoreboard.nativeReg = mReg;
cUnit->registerScoreboard.isWide = false;
}
/*
* Given a Dalvik register id vSrc, use a very simple algorithm to increase
* the lifetime of cached Dalvik value in a native register.
*/
static inline int selectFirstRegister(CompilationUnit *cUnit, int vSrc,
bool isWide)
{
RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
/* No live value - suggest to use r0 */
if (registerScoreboard->liveDalvikReg == vNone)
return r0;
/* Reuse the previously used native reg */
if (registerScoreboard->liveDalvikReg == vSrc) {
if (isWide != true) {
return registerScoreboard->nativeReg;
} else {
/* Return either r0 or r2 */
return (registerScoreboard->nativeReg + 1) & 2;
}
}
/* No reuse - choose the next one among r0..r3 in the round-robin fashion */
if (isWide) {
return (registerScoreboard->nativeReg + 2) & 2;
} else {
return (registerScoreboard->nativeReg + 1) & 3;
}
}
ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
{
ArmLIR* res;
ArmOpCode opCode;
res = dvmCompilerNew(sizeof(ArmLIR), true);
if (LOWREG(rDest) && LOWREG(rSrc))
opCode = THUMB_MOV_RR;
else if (!LOWREG(rDest) && !LOWREG(rSrc))
opCode = THUMB_MOV_RR_H2H;
else if (LOWREG(rDest))
opCode = THUMB_MOV_RR_H2L;
else
opCode = THUMB_MOV_RR_L2H;
rDest &= THUMB_REG_MASK;
rSrc &= THUMB_REG_MASK;
res->operands[0] = rDest & THUMB_REG_MASK;
res->operands[1] = rSrc & THUMB_REG_MASK;
res->opCode = opCode;
if (rDest == rSrc) {
res->isNop = true;
}
return res;
}
/*
* Load a immediate using a shortcut if possible; otherwise
* grab from the per-translation literal pool
*/
static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
{
ArmLIR *res;
/* See if the value can be constructed cheaply */
if ((value >= 0) && (value <= 255)) {
return newLIR2(cUnit, THUMB_MOV_IMM, rDest, value);
} else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
res = newLIR2(cUnit, THUMB_MOV_IMM, rDest, ~value);
newLIR2(cUnit, THUMB_MVN, rDest, rDest);
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 = THUMB_LDR_PC_REL;
loadPcRel->generic.target = (LIR *) dataTarget;
loadPcRel->operands[0] = rDest;
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, THUMB_ADD_RI8, rDest, value - dataTarget->operands[0]);
}
return res;
}
/* Export the Dalvik PC assicated with an instruction to the StackSave area */
static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC,
int rAddr)
{
ArmLIR *res;
int offset = offsetof(StackSaveArea, xtra.currentPc);
res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
newLIR2(cUnit, THUMB_MOV_RR, rAddr, rFP);
newLIR2(cUnit, THUMB_SUB_RI8, rAddr, sizeof(StackSaveArea) - offset);
storeWordDisp( cUnit, rAddr, 0, rDPC, -1);
return res;
}
/* Load value from base + scaled index. Note: index reg killed */
static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
int rIndex, int rDest, int scale, OpSize size)
{
ArmLIR *first = NULL;
ArmLIR *res;
ArmOpCode opCode = THUMB_BKPT;
if (scale)
first = opRegRegImm(cUnit, OP_LSL, rIndex, rIndex, scale, rNone);
switch (size) {
case WORD:
opCode = THUMB_LDR_RRR;
break;
case UNSIGNED_HALF:
opCode = THUMB_LDRH_RRR;
break;
case SIGNED_HALF:
opCode = THUMB_LDRSH_RRR;
break;
case UNSIGNED_BYTE:
opCode = THUMB_LDRB_RRR;
break;
case SIGNED_BYTE:
opCode = THUMB_LDRSB_RRR;
break;
default:
assert(0);
}
res = newLIR3(cUnit, opCode, rDest, rBase, rIndex);
return (first) ? first : res;
}
/* store value base base + scaled index. Note: index reg killed */
static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
int rIndex, int rSrc, int scale, OpSize size)
{
ArmLIR *first = NULL;
ArmLIR *res;
ArmOpCode opCode = THUMB_BKPT;
if (scale)
first = opRegRegImm(cUnit, OP_LSL, rIndex, rIndex, scale, rNone);
switch (size) {
case WORD:
opCode = THUMB_STR_RRR;
break;
case UNSIGNED_HALF:
case SIGNED_HALF:
opCode = THUMB_STRH_RRR;
break;
case UNSIGNED_BYTE:
case SIGNED_BYTE:
opCode = THUMB_STRB_RRR;
break;
default:
assert(0);
}
res = newLIR3(cUnit, opCode, rSrc, rBase, rIndex);
return (first) ? first : res;
}
/*
* Load value from base + displacement. Optionally perform null check
* on base (which must have an associated vReg and MIR). If not
* performing null check, incoming MIR can be null. Note: base and
* dest must not be the same if there is any chance that the long
* form must be used.
*/
static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
int displacement, int rDest, OpSize size,
bool nullCheck, int vReg)
{
ArmLIR *first = NULL;
ArmLIR *res;
ArmOpCode opCode = THUMB_BKPT;
bool shortForm = false;
int shortMax = 128;
switch (size) {
case WORD:
if (LOWREG(rDest) && (rBase == rpc) &&
(displacement <= 1020) && (displacement >= 0)) {
shortForm = true;
displacement >>= 2;
opCode = THUMB_LDR_PC_REL;
} else if (LOWREG(rDest) && (rBase == r13) &&
(displacement <= 1020) && (displacement >= 0)) {
shortForm = true;
displacement >>= 2;
opCode = THUMB_LDR_SP_REL;
} else if (displacement < 128 && displacement >= 0) {
assert((displacement & 0x3) == 0);
shortForm = true;
displacement >>= 2;
opCode = THUMB_LDR_RRI5;
} else {
opCode = THUMB_LDR_RRR;
}
break;
case UNSIGNED_HALF:
if (displacement < 64 && displacement >= 0) {
assert((displacement & 0x1) == 0);
shortForm = true;
displacement >>= 1;
opCode = THUMB_LDRH_RRI5;
} else {
opCode = THUMB_LDRH_RRR;
}
break;
case SIGNED_HALF:
opCode = THUMB_LDRSH_RRR;
break;
case UNSIGNED_BYTE:
if (displacement < 32 && displacement >= 0) {
shortForm = true;
opCode = THUMB_LDRB_RRI5;
} else {
opCode = THUMB_LDRB_RRR;
}
break;
case SIGNED_BYTE:
opCode = THUMB_LDRSB_RRR;
break;
default:
assert(0);
}
if (nullCheck)
first = genNullCheck(cUnit, vReg, rBase, mir->offset, NULL);
if (shortForm) {
res = newLIR3(cUnit, opCode, rDest, rBase, displacement);
} else {
assert(rBase != rDest);
res = loadConstant(cUnit, rDest, displacement);
newLIR3(cUnit, opCode, rDest, rBase, rDest);
}
return (first) ? first : res;
}
static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
int displacement, int rSrc, OpSize size,
int rScratch)
{
ArmLIR *res;
ArmOpCode opCode = THUMB_BKPT;
bool shortForm = false;
int shortMax = 128;
switch (size) {
case WORD:
if (displacement < 128 && displacement >= 0) {
assert((displacement & 0x3) == 0);
shortForm = true;
displacement >>= 2;
opCode = THUMB_STR_RRI5;
} else {
opCode = THUMB_STR_RRR;
}
break;
case UNSIGNED_HALF:
case SIGNED_HALF:
if (displacement < 64 && displacement >= 0) {
assert((displacement & 0x1) == 0);
shortForm = true;
displacement >>= 1;
opCode = THUMB_STRH_RRI5;
} else {
opCode = THUMB_STRH_RRR;
}
break;
case UNSIGNED_BYTE:
case SIGNED_BYTE:
if (displacement < 32 && displacement >= 0) {
shortForm = true;
opCode = THUMB_STRB_RRI5;
} else {
opCode = THUMB_STRB_RRR;
}
break;
default:
assert(0);
}
if (shortForm) {
res = newLIR3(cUnit, opCode, rSrc, rBase, displacement);
} else {
assert(rScratch != -1);
res = loadConstant(cUnit, rScratch, displacement);
newLIR3(cUnit, opCode, rSrc, rBase, rScratch);
}
return res;
}
/*
* Perform a "reg cmp imm" operation and jump to the PCR region if condition
* satisfies.
*/
static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
ArmConditionCode cond, int reg,
int checkValue, int dOffset,
ArmLIR *pcrLabel)
{
if ((checkValue & 0xff) != checkValue) {
/* NOTE: direct use of hot temp r7 here. Revisit. */
loadConstant(cUnit, r7, checkValue);
return genRegRegCheck(cUnit, cond, reg, r7, dOffset, pcrLabel);
}
newLIR2(cUnit, THUMB_CMP_RI8, reg, checkValue);
ArmLIR *branch = newLIR2(cUnit, THUMB_B_COND, 0, cond);
return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
}
static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
{
return newLIR2(cUnit, THUMB_LDMIA, rBase, rMask);
}
static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
{
return newLIR2(cUnit, THUMB_STMIA, rBase, rMask);
}
static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
{
ArmOpCode opCode = THUMB_BKPT;
switch (op) {
case OP_UNCOND_BR:
opCode = THUMB_B_UNCOND;
break;
default:
assert(0);
}
return newLIR0(cUnit, opCode);
}
static ArmLIR *opImmImm(CompilationUnit *cUnit, OpKind op, int value1,
int value2)
{
ArmOpCode opCode = THUMB_BKPT;
switch (op) {
case OP_COND_BR:
opCode = THUMB_B_COND;
break;
default:
assert(0);
}
return newLIR2(cUnit, opCode, value1, value2);
}
static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
{
ArmOpCode opCode = THUMB_BKPT;
switch (op) {
case OP_PUSH:
opCode = THUMB_PUSH;
break;
case OP_POP:
opCode = THUMB_POP;
break;
default:
assert(0);
}
return newLIR1(cUnit, opCode, value);
}
static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
{
ArmOpCode opCode = THUMB_BKPT;
switch (op) {
case OP_BLX:
opCode = THUMB_BLX_R;
break;
default:
assert(0);
}
return newLIR1(cUnit, opCode, rDestSrc);
}
static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
int rSrc2)
{
ArmLIR *res;
ArmOpCode opCode = THUMB_BKPT;
switch (op) {
case OP_ADC:
opCode = THUMB_ADC;
break;
case OP_AND:
opCode = THUMB_AND_RR;
break;
case OP_BIC:
opCode = THUMB_BIC;
break;
case OP_CMN:
opCode = THUMB_CMN;
break;
case OP_CMP:
opCode = THUMB_CMP_RR;
break;
case OP_XOR:
opCode = THUMB_EOR;
break;
case OP_MOV:
if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
opCode = THUMB_MOV_RR;
else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
opCode = THUMB_MOV_RR_H2H;
else if (LOWREG(rDestSrc1))
opCode = THUMB_MOV_RR_H2L;
else
opCode = THUMB_MOV_RR_L2H;
rDestSrc1 &= THUMB_REG_MASK;
rSrc2 &= THUMB_REG_MASK;
break;
case OP_MUL:
opCode = THUMB_MUL;
break;
case OP_MVN:
opCode = THUMB_MVN;
break;
case OP_NEG:
opCode = THUMB_NEG;
break;
case OP_OR:
opCode = THUMB_ORR;
break;
case OP_SBC:
opCode = THUMB_SBC;
break;
case OP_TST:
opCode = THUMB_TST;
break;
case OP_LSL:
opCode = THUMB_LSLV;
break;
case OP_LSR:
opCode = THUMB_LSRV;
break;
case OP_ASR:
opCode = THUMB_ASRV;
break;
case OP_ROR:
opCode = THUMB_RORV;
case OP_ADD:
case OP_SUB:
return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
case OP_2BYTE:
res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 24, rNone);
opRegRegImm(cUnit, OP_ASR, rDestSrc1, rDestSrc1, 24, rNone);
return res;
case OP_2SHORT:
res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 16, rNone);
opRegRegImm(cUnit, OP_ASR, rDestSrc1, rDestSrc1, 16, rNone);
return res;
case OP_2CHAR:
res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 16, rNone);
opRegRegImm(cUnit, OP_LSR, rDestSrc1, rDestSrc1, 16, rNone);
return res;
default:
assert(0);
break;
}
return newLIR2(cUnit, opCode, rDestSrc1, rSrc2);
}
static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
int value, int rScratch)
{
ArmLIR *res;
bool neg = (value < 0);
int absValue = (neg) ? -value : value;
bool shortForm = (absValue & 0xff) == absValue;
ArmOpCode opCode = THUMB_BKPT;
switch (op) {
case OP_ADD:
if ( !neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
assert((value & 0x3) == 0);
return newLIR1(cUnit, THUMB_ADD_SPI7, value >> 2);
} else if (shortForm) {
opCode = (neg) ? THUMB_SUB_RI8 : THUMB_ADD_RI8;
} else
opCode = THUMB_ADD_RRR;
break;
case OP_SUB:
if (!neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
assert((value & 0x3) == 0);
return newLIR1(cUnit, THUMB_SUB_SPI7, value >> 2);
} else if (shortForm) {
opCode = (neg) ? THUMB_ADD_RI8 : THUMB_SUB_RI8;
} else
opCode = THUMB_SUB_RRR;
break;
case OP_CMP:
if (LOWREG(rDestSrc1) && shortForm)
opCode = (shortForm) ? THUMB_CMP_RI8 : THUMB_CMP_RR;
else if (LOWREG(rDestSrc1))
opCode = THUMB_CMP_RR;
else {
shortForm = false;
opCode = THUMB_CMP_HL;
}
break;
default:
assert(0);
break;
}
if (shortForm)
res = newLIR2(cUnit, opCode, rDestSrc1, absValue);
else {
assert(rScratch != rNone);
res = loadConstant(cUnit, rScratch, value);
newLIR3(cUnit, opCode, rDestSrc1, rDestSrc1, rScratch);
}
return res;
}
static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
int rSrc1, int rSrc2)
{
ArmOpCode opCode = THUMB_BKPT;
switch (op) {
case OP_ADD:
opCode = THUMB_ADD_RRR;
break;
case OP_SUB:
opCode = THUMB_SUB_RRR;
break;
default:
assert(0);
break;
}
return newLIR3(cUnit, opCode, rDest, rSrc1, rSrc2);
}
static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
int rSrc1, int value, int rScratch)
{
ArmLIR *res;
bool neg = (value < 0);
int absValue = (neg) ? -value : value;
ArmOpCode opCode = THUMB_BKPT;
bool shortForm = (absValue & 0x7) == absValue;
switch(op) {
case OP_ADD:
if ((rSrc1 == 13) && (value <= 1020)) { /* sp */
assert((value & 0x3) == 0);
shortForm = true;
opCode = THUMB_ADD_SP_REL;
value >>= 2;
} else if ((rSrc1 == 15) && (value <= 1020)) { /* pc */
assert((value & 0x3) == 0);
shortForm = true;
opCode = THUMB_ADD_PC_REL;
value >>= 2;
} else if (shortForm) {
opCode = (neg) ? THUMB_SUB_RRI3 : THUMB_ADD_RRI3;
} else if ((absValue > 0) && (absValue <= (255 + 7))) {
/* Two shots - 1st handle the 7 */
opCode = (neg) ? THUMB_SUB_RRI3 : THUMB_ADD_RRI3;
res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
opCode = (neg) ? THUMB_SUB_RI8 : THUMB_ADD_RI8;
newLIR2(cUnit, opCode, rDest, absValue - 7);
return res;
} else
opCode = THUMB_ADD_RRR;
break;
case OP_SUB:
if (shortForm) {
opCode = (neg) ? THUMB_ADD_RRI3 : THUMB_SUB_RRI3;
} else if ((absValue > 0) && (absValue <= (255 + 7))) {
/* Two shots - 1st handle the 7 */
opCode = (neg) ? THUMB_ADD_RRI3 : THUMB_SUB_RRI3;
res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
opCode = (neg) ? THUMB_ADD_RI8 : THUMB_SUB_RI8;
newLIR2(cUnit, opCode, rDest, absValue - 7);
return res;
} else
opCode = THUMB_SUB_RRR;
break;
case OP_LSL:
shortForm = (!neg && value <= 31);
opCode = THUMB_LSL;
break;
case OP_LSR:
shortForm = (!neg && value <= 31);
opCode = THUMB_LSR;
break;
case OP_ASR:
shortForm = (!neg && value <= 31);
opCode = THUMB_ASR;
break;
case OP_MUL:
case OP_AND:
case OP_OR:
case OP_XOR:
if (rDest == rSrc1) {
res = loadConstant(cUnit, rScratch, value);
opRegReg(cUnit, op, rDest, rScratch);
} else {
res = loadConstant(cUnit, rDest, value);
opRegReg(cUnit, op, rDest, rSrc1);
}
return res;
default:
assert(0);
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 {
assert(rScratch != rNone);
res = loadConstant(cUnit, rScratch, value);
newLIR3(cUnit, opCode, rDest, rSrc1, rScratch);
}
}
return res;
}
static void genCmpLong(CompilationUnit *cUnit, MIR *mir,
int vDest, int vSrc1, int vSrc2)
{
loadValuePair(cUnit, vSrc1, r0, r1);
loadValuePair(cUnit, vSrc2, r2, r3);
genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG);
storeValue(cUnit, r0, vDest, r1);
}
static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
{
DecodedInstruction *dInsn = &mir->dalvikInsn;
int offset = offsetof(InterpState, retval);
int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
int reg1 = NEXT_REG(regObj);
loadValue(cUnit, dInsn->arg[0], regObj);
genNullCheck(cUnit, dInsn->arg[0], regObj, mir->offset, NULL);
loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, reg1);
storeWordDisp(cUnit, rGLUE, offset, reg1, regObj);
return false;
}
static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
{
DecodedInstruction *dInsn = &mir->dalvikInsn;
int offset = offsetof(InterpState, retval);
int contents = offsetof(ArrayObject, contents);
int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
int regIdx = NEXT_REG(regObj);
int regMax = NEXT_REG(regIdx);
int regOff = NEXT_REG(regMax);
loadValue(cUnit, dInsn->arg[0], regObj);
loadValue(cUnit, dInsn->arg[1], regIdx);
ArmLIR * pcrLabel = genNullCheck(cUnit, dInsn->arg[0], regObj,
mir->offset, NULL);
loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, regMax);
loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_offset, regOff);
loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_value, regObj);
genBoundsCheck(cUnit, regIdx, regMax, mir->offset, pcrLabel);
newLIR2(cUnit, THUMB_ADD_RI8, regObj, contents);
newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regOff);
newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regIdx);
newLIR3(cUnit, THUMB_LDRH_RRR, regMax, regObj, regIdx);
storeWordDisp(cUnit, rGLUE, offset, regMax, regObj);
return false;
}
static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
{
int offset = offsetof(InterpState, retval);
DecodedInstruction *dInsn = &mir->dalvikInsn;
int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
int sign = NEXT_REG(reg0);
/* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */
loadValue(cUnit, dInsn->arg[0], reg0);
newLIR3(cUnit, THUMB_ASR, sign, reg0, 31);
newLIR3(cUnit, THUMB_ADD_RRR, reg0, reg0, sign);
newLIR2(cUnit, THUMB_EOR, reg0, sign);
storeWordDisp(cUnit, rGLUE, offset, reg0, sign);
return false;
}
static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
{
int offset = offsetof(InterpState, retval);
DecodedInstruction *dInsn = &mir->dalvikInsn;
int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
int signMask = NEXT_REG(reg0);
loadValue(cUnit, dInsn->arg[0], reg0);
loadConstant(cUnit, signMask, 0x7fffffff);
newLIR2(cUnit, THUMB_AND_RR, reg0, signMask);
storeWordDisp(cUnit, rGLUE, offset, reg0, signMask);
return false;
}
static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
{
int offset = offsetof(InterpState, retval);
DecodedInstruction *dInsn = &mir->dalvikInsn;
int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
int ophi = NEXT_REG(oplo);
int signMask = NEXT_REG(ophi);
loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
loadConstant(cUnit, signMask, 0x7fffffff);
storeWordDisp(cUnit, rGLUE, offset, oplo, ophi);
newLIR2(cUnit, THUMB_AND_RR, ophi, signMask);
storeWordDisp(cUnit, rGLUE, offset + 4, ophi, oplo);
return false;
}
/* No select in thumb, so we need to branch. Thumb2 will do better */
static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
{
int offset = offsetof(InterpState, retval);
DecodedInstruction *dInsn = &mir->dalvikInsn;
int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
int reg1 = NEXT_REG(reg0);
loadValue(cUnit, dInsn->arg[0], reg0);
loadValue(cUnit, dInsn->arg[1], reg1);
newLIR2(cUnit, THUMB_CMP_RR, reg0, reg1);
ArmLIR *branch1 = newLIR2(cUnit, THUMB_B_COND, 2,
isMin ? ARM_COND_LT : ARM_COND_GT);
newLIR2(cUnit, THUMB_MOV_RR, reg0, reg1);
ArmLIR *target =
newLIR3(cUnit, THUMB_STR_RRI5, reg0, rGLUE, offset >> 2);
branch1->generic.target = (LIR *)target;
return false;
}
static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
{
int offset = offsetof(InterpState, retval);
DecodedInstruction *dInsn = &mir->dalvikInsn;
int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
int ophi = NEXT_REG(oplo);
int sign = NEXT_REG(ophi);
/* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */
loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
newLIR3(cUnit, THUMB_ASR, sign, ophi, 31);
newLIR3(cUnit, THUMB_ADD_RRR, oplo, oplo, sign);
newLIR2(cUnit, THUMB_ADC, ophi, sign);
newLIR2(cUnit, THUMB_EOR, oplo, sign);
newLIR2(cUnit, THUMB_EOR, ophi, sign);
storeWordDisp(cUnit, rGLUE, offset, oplo, sign);
storeWordDisp(cUnit, rGLUE, offset + 4, ophi, sign);
return false;
}