aboutsummaryrefslogtreecommitdiff
path: root/vm/compiler/codegen/arm/Thumb/Factory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vm/compiler/codegen/arm/Thumb/Factory.cpp')
-rw-r--r--vm/compiler/codegen/arm/Thumb/Factory.cpp914
1 files changed, 914 insertions, 0 deletions
diff --git a/vm/compiler/codegen/arm/Thumb/Factory.cpp b/vm/compiler/codegen/arm/Thumb/Factory.cpp
new file mode 100644
index 000000000..7b51df160
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Factory.cpp
@@ -0,0 +1,914 @@
+/*
+ * 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);
+}
+
+static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
+{
+ ArmOpcode opcode = kThumbBkpt;
+ switch (op) {
+ case kOpUncondBr:
+ opcode = kThumbBUncond;
+ break;
+ default:
+ LOGE("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:
+ LOGE("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:
+ LOGE("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:
+ LOGE("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:
+ LOGE("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:
+ LOGE("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:
+ LOGE("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:
+ LOGE("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:
+ LOGE("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 res;
+}
+
+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:
+ LOGE("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