/* * 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, value, 255); if (dataTarget == NULL) { dataTarget = addWordData(cUnit, value, false); } ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true); loadPcRel->opCode = kThumbLdrPcRel; loadPcRel->generic.target = (LIR *) dataTarget; loadPcRel->operands[0] = tDest; setupResourceMasks(loadPcRel); /* * Special case for literal loads with a link register target. * Self-cosim mode will insert calls prior to heap references * after optimization, and those will destroy r14. The easy * workaround is to treat literal loads into r14 as heap references * to prevent them from being hoisted. Use of r14 in this manner * is currently rare. Revist if that changes. */ if (rDest != rlr) 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 == 13) && (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 == 13) && (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 == 13) && (value <= 1020)) { /* sp */ assert((value & 0x3) == 0); shortForm = true; opCode = kThumbAddSpRel; value >>= 2; } else if ((rSrc1 == 15) && (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->branchInsertSV = 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->branchInsertSV = 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->branchInsertSV = 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->branchInsertSV = 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 == 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 (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 == rFP) annotateDalvikRegAccess(load, displacement >> 2, true /* isLoad */); if (rTmp != rDest) dvmCompilerFreeTemp(cUnit, rTmp); } } if (rBase == rFP) { 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->branchInsertSV = true; if (load2 != NULL && cUnit->heapMemOp) load2->branchInsertSV = 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 == rFP) { 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->branchInsertSV = true; if (store2 != NULL && cUnit->heapMemOp) store2->branchInsertSV = 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 = 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; } 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 inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit, ArmConditionCode cond, int reg, int checkValue, int dOffset, ArmLIR *pcrLabel) { int tReg; ArmLIR *res; if ((checkValue & 0xff) != checkValue) { tReg = dvmCompilerAllocTemp(cUnit); loadConstant(cUnit, tReg, checkValue); res = genRegRegCheck(cUnit, cond, reg, tReg, dOffset, pcrLabel); dvmCompilerFreeTemp(cUnit, tReg); return res; } newLIR2(cUnit, kThumbCmpRI8, reg, checkValue); ArmLIR *branch = newLIR2(cUnit, kThumbBCond, 0, cond); return genCheckCommon(cUnit, dOffset, branch, pcrLabel); }