/* * Copyright (C) 2012 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. */ #include "oat/runtime/oat_support_entrypoints.h" namespace art { /* * This source files contains "gen" codegen routines that should * be applicable to most targets. Only mid-level support utilities * and "op" calls may be used here. */ void genInvoke(CompilationUnit* cUnit, CallInfo* info); #if defined(TARGET_ARM) LIR* opIT(CompilationUnit* cUnit, ArmConditionCode cond, const char* guide); bool smallLiteralDivide(CompilationUnit* cUnit, Instruction::Code dalvikOpcode, RegLocation rlSrc, RegLocation rlDest, int lit); #endif void callRuntimeHelperImm(CompilationUnit* cUnit, int helperOffset, int arg0) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif loadConstant(cUnit, rARG0, arg0); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperReg(CompilationUnit* cUnit, int helperOffset, int arg0) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif opRegCopy(cUnit, rARG0, arg0); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperRegLocation(CompilationUnit* cUnit, int helperOffset, RegLocation arg0) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif if (arg0.wide == 0) { loadValueDirectFixed(cUnit, arg0, rARG0); } else { loadValueDirectWideFixed(cUnit, arg0, rARG0, rARG1); } oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperImmImm(CompilationUnit* cUnit, int helperOffset, int arg0, int arg1) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif loadConstant(cUnit, rARG0, arg0); loadConstant(cUnit, rARG1, arg1); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperImmRegLocation(CompilationUnit* cUnit, int helperOffset, int arg0, RegLocation arg1) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif if (arg1.wide == 0) { loadValueDirectFixed(cUnit, arg1, rARG1); } else { loadValueDirectWideFixed(cUnit, arg1, rARG1, rARG2); } loadConstant(cUnit, rARG0, arg0); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperRegLocationImm(CompilationUnit* cUnit, int helperOffset, RegLocation arg0, int arg1) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif loadValueDirectFixed(cUnit, arg0, rARG0); loadConstant(cUnit, rARG1, arg1); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperImmReg(CompilationUnit* cUnit, int helperOffset, int arg0, int arg1) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif opRegCopy(cUnit, rARG1, arg1); loadConstant(cUnit, rARG0, arg0); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperRegImm(CompilationUnit* cUnit, int helperOffset, int arg0, int arg1) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif opRegCopy(cUnit, rARG0, arg0); loadConstant(cUnit, rARG1, arg1); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperImmMethod(CompilationUnit* cUnit, int helperOffset, int arg0) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif loadCurrMethodDirect(cUnit, rARG1); loadConstant(cUnit, rARG0, arg0); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperRegLocationRegLocation(CompilationUnit* cUnit, int helperOffset, RegLocation arg0, RegLocation arg1) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif if (arg0.wide == 0) { loadValueDirectFixed(cUnit, arg0, rARG0); if (arg1.wide == 0) { loadValueDirectFixed(cUnit, arg1, rARG1); } else { loadValueDirectWideFixed(cUnit, arg1, rARG1, rARG2); } } else { loadValueDirectWideFixed(cUnit, arg0, rARG0, rARG1); if (arg1.wide == 0) { loadValueDirectFixed(cUnit, arg1, rARG2); } else { loadValueDirectWideFixed(cUnit, arg1, rARG2, rARG3); } } oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperRegReg(CompilationUnit* cUnit, int helperOffset, int arg0, int arg1) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif DCHECK_NE((int)rARG0, arg1); // check copy into arg0 won't clobber arg1 opRegCopy(cUnit, rARG0, arg0); opRegCopy(cUnit, rARG1, arg1); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperRegRegImm(CompilationUnit* cUnit, int helperOffset, int arg0, int arg1, int arg2) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif DCHECK_NE((int)rARG0, arg1); // check copy into arg0 won't clobber arg1 opRegCopy(cUnit, rARG0, arg0); opRegCopy(cUnit, rARG1, arg1); loadConstant(cUnit, rARG2, arg2); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperImmMethodRegLocation(CompilationUnit* cUnit, int helperOffset, int arg0, RegLocation arg2) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif loadValueDirectFixed(cUnit, arg2, rARG2); loadCurrMethodDirect(cUnit, rARG1); loadConstant(cUnit, rARG0, arg0); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperImmMethodImm(CompilationUnit* cUnit, int helperOffset, int arg0, int arg2) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif loadCurrMethodDirect(cUnit, rARG1); loadConstant(cUnit, rARG2, arg2); loadConstant(cUnit, rARG0, arg0); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } void callRuntimeHelperImmRegLocationRegLocation(CompilationUnit* cUnit, int helperOffset, int arg0, RegLocation arg1, RegLocation arg2) { #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, helperOffset); #endif loadValueDirectFixed(cUnit, arg1, rARG1); if (arg2.wide == 0) { loadValueDirectFixed(cUnit, arg2, rARG2); } else { loadValueDirectWideFixed(cUnit, arg2, rARG2, rARG3); } loadConstant(cUnit, rARG0, arg0); oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, helperOffset); #endif } /* * Generate an kPseudoBarrier marker to indicate the boundary of special * blocks. */ void genBarrier(CompilationUnit* cUnit) { LIR* barrier = newLIR0(cUnit, kPseudoBarrier); /* Mark all resources as being clobbered */ barrier->defMask = -1; } /* Generate unconditional branch instructions */ LIR* opUnconditionalBranch(CompilationUnit* cUnit, LIR* target) { LIR* branch = opBranchUnconditional(cUnit, kOpUncondBr); branch->target = (LIR*) target; return branch; } // FIXME: need to do some work to split out targets with // condition codes and those without #if defined(TARGET_ARM) || defined(TARGET_X86) LIR* genCheck(CompilationUnit* cUnit, ConditionCode cCode, ThrowKind kind) { LIR* tgt = rawLIR(cUnit, 0, kPseudoThrowTarget, kind, cUnit->currentDalvikOffset); LIR* branch = opCondBranch(cUnit, cCode, tgt); // Remember branch target - will process later oatInsertGrowableList(cUnit, &cUnit->throwLaunchpads, (intptr_t)tgt); return branch; } #endif LIR* genImmedCheck(CompilationUnit* cUnit, ConditionCode cCode, int reg, int immVal, ThrowKind kind) { LIR* tgt = rawLIR(cUnit, 0, kPseudoThrowTarget, kind, cUnit->currentDalvikOffset); LIR* branch; if (cCode == kCondAl) { branch = opUnconditionalBranch(cUnit, tgt); } else { branch = opCmpImmBranch(cUnit, cCode, reg, immVal, tgt); } // Remember branch target - will process later oatInsertGrowableList(cUnit, &cUnit->throwLaunchpads, (intptr_t)tgt); return branch; } /* Perform null-check on a register. */ LIR* genNullCheck(CompilationUnit* cUnit, int sReg, int mReg, int optFlags) { if (!(cUnit->disableOpt & (1 << kNullCheckElimination)) && optFlags & MIR_IGNORE_NULL_CHECK) { return NULL; } return genImmedCheck(cUnit, kCondEq, mReg, 0, kThrowNullPointer); } /* Perform check on two registers */ LIR* genRegRegCheck(CompilationUnit* cUnit, ConditionCode cCode, int reg1, int reg2, ThrowKind kind) { LIR* tgt = rawLIR(cUnit, 0, kPseudoThrowTarget, kind, cUnit->currentDalvikOffset, reg1, reg2); #if defined(TARGET_MIPS) LIR* branch = opCmpBranch(cUnit, cCode, reg1, reg2, tgt); #else opRegReg(cUnit, kOpCmp, reg1, reg2); LIR* branch = opCondBranch(cUnit, cCode, tgt); #endif // Remember branch target - will process later oatInsertGrowableList(cUnit, &cUnit->throwLaunchpads, (intptr_t)tgt); return branch; } void genCompareAndBranch(CompilationUnit* cUnit, Instruction::Code opcode, RegLocation rlSrc1, RegLocation rlSrc2, LIR* taken, LIR* fallThrough) { ConditionCode cond; rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg); rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg); switch (opcode) { case Instruction::IF_EQ: cond = kCondEq; break; case Instruction::IF_NE: cond = kCondNe; break; case Instruction::IF_LT: cond = kCondLt; break; case Instruction::IF_GE: cond = kCondGe; break; case Instruction::IF_GT: cond = kCondGt; break; case Instruction::IF_LE: cond = kCondLe; break; default: cond = (ConditionCode)0; LOG(FATAL) << "Unexpected opcode " << (int)opcode; } #if defined(TARGET_MIPS) opCmpBranch(cUnit, cond, rlSrc1.lowReg, rlSrc2.lowReg, taken); #else opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg); opCondBranch(cUnit, cond, taken); #endif opUnconditionalBranch(cUnit, fallThrough); } void genCompareZeroAndBranch(CompilationUnit* cUnit, Instruction::Code opcode, RegLocation rlSrc, LIR* taken, LIR* fallThrough) { ConditionCode cond; rlSrc = loadValue(cUnit, rlSrc, kCoreReg); switch (opcode) { case Instruction::IF_EQZ: cond = kCondEq; break; case Instruction::IF_NEZ: cond = kCondNe; break; case Instruction::IF_LTZ: cond = kCondLt; break; case Instruction::IF_GEZ: cond = kCondGe; break; case Instruction::IF_GTZ: cond = kCondGt; break; case Instruction::IF_LEZ: cond = kCondLe; break; default: cond = (ConditionCode)0; LOG(FATAL) << "Unexpected opcode " << (int)opcode; } #if defined(TARGET_MIPS) || defined(TARGET_X86) opCmpImmBranch(cUnit, cond, rlSrc.lowReg, 0, taken); #else opRegImm(cUnit, kOpCmp, rlSrc.lowReg, 0); opCondBranch(cUnit, cond, taken); #endif opUnconditionalBranch(cUnit, fallThrough); } void genIntToLong(CompilationUnit* cUnit, RegLocation rlDest, RegLocation rlSrc) { RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); if (rlSrc.location == kLocPhysReg) { opRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg); } else { loadValueDirect(cUnit, rlSrc, rlResult.lowReg); } opRegRegImm(cUnit, kOpAsr, rlResult.highReg, rlResult.lowReg, 31); storeValueWide(cUnit, rlDest, rlResult); } void genIntNarrowing(CompilationUnit* cUnit, Instruction::Code opcode, RegLocation rlDest, RegLocation rlSrc) { rlSrc = loadValue(cUnit, rlSrc, kCoreReg); RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); OpKind op = kOpInvalid; switch (opcode) { case Instruction::INT_TO_BYTE: op = kOp2Byte; break; case Instruction::INT_TO_SHORT: op = kOp2Short; break; case Instruction::INT_TO_CHAR: op = kOp2Char; break; default: LOG(ERROR) << "Bad int conversion type"; } opRegReg(cUnit, op, rlResult.lowReg, rlSrc.lowReg); storeValue(cUnit, rlDest, rlResult); } /* * Let helper function take care of everything. Will call * Array::AllocFromCode(type_idx, method, count); * Note: AllocFromCode will handle checks for errNegativeArraySize. */ void genNewArray(CompilationUnit* cUnit, uint32_t type_idx, RegLocation rlDest, RegLocation rlSrc) { oatFlushAllRegs(cUnit); /* Everything to home location */ int funcOffset; if (cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx, *cUnit->dex_file, type_idx)) { funcOffset = ENTRYPOINT_OFFSET(pAllocArrayFromCode); } else { funcOffset= ENTRYPOINT_OFFSET(pAllocArrayFromCodeWithAccessCheck); } callRuntimeHelperImmMethodRegLocation(cUnit, funcOffset, type_idx, rlSrc); RegLocation rlResult = oatGetReturn(cUnit, false); storeValue(cUnit, rlDest, rlResult); } /* * Similar to genNewArray, but with post-allocation initialization. * Verifier guarantees we're dealing with an array class. Current * code throws runtime exception "bad Filled array req" for 'D' and 'J'. * Current code also throws internal unimp if not 'L', '[' or 'I'. */ void genFilledNewArray(CompilationUnit* cUnit, CallInfo* info) { int elems = info->numArgWords; int typeIdx = info->index; oatFlushAllRegs(cUnit); /* Everything to home location */ int funcOffset; if (cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx, *cUnit->dex_file, typeIdx)) { funcOffset = ENTRYPOINT_OFFSET(pCheckAndAllocArrayFromCode); } else { funcOffset = ENTRYPOINT_OFFSET(pCheckAndAllocArrayFromCodeWithAccessCheck); } callRuntimeHelperImmMethodImm(cUnit, funcOffset, typeIdx, elems); oatFreeTemp(cUnit, rARG2); oatFreeTemp(cUnit, rARG1); /* * NOTE: the implicit target for Instruction::FILLED_NEW_ARRAY is the * return region. Because AllocFromCode placed the new array * in rRET0, we'll just lock it into place. When debugger support is * added, it may be necessary to additionally copy all return * values to a home location in thread-local storage */ oatLockTemp(cUnit, rRET0); // TODO: use the correct component size, currently all supported types // share array alignment with ints (see comment at head of function) size_t component_size = sizeof(int32_t); // Having a range of 0 is legal if (info->isRange && (elems > 0)) { /* * Bit of ugliness here. We're going generate a mem copy loop * on the register range, but it is possible that some regs * in the range have been promoted. This is unlikely, but * before generating the copy, we'll just force a flush * of any regs in the source range that have been promoted to * home location. */ for (int i = 0; i < elems; i++) { RegLocation loc = oatUpdateLoc(cUnit, info->args[i]); if (loc.location == kLocPhysReg) { storeBaseDisp(cUnit, rSP, oatSRegOffset(cUnit, loc.sRegLow), loc.lowReg, kWord); } } /* * TUNING note: generated code here could be much improved, but * this is an uncommon operation and isn't especially performance * critical. */ int rSrc = oatAllocTemp(cUnit); int rDst = oatAllocTemp(cUnit); int rIdx = oatAllocTemp(cUnit); #if defined(TARGET_ARM) int rVal = rLR; // Using a lot of temps, rLR is known free here #elif defined(TARGET_X86) oatFreeTemp(cUnit, rRET0); int rVal = oatAllocTemp(cUnit); #else int rVal = oatAllocTemp(cUnit); #endif // Set up source pointer RegLocation rlFirst = info->args[0]; opRegRegImm(cUnit, kOpAdd, rSrc, rSP, oatSRegOffset(cUnit, rlFirst.sRegLow)); // Set up the target pointer opRegRegImm(cUnit, kOpAdd, rDst, rRET0, Array::DataOffset(component_size).Int32Value()); // Set up the loop counter (known to be > 0) loadConstant(cUnit, rIdx, elems - 1); // Generate the copy loop. Going backwards for convenience LIR* target = newLIR0(cUnit, kPseudoTargetLabel); // Copy next element loadBaseIndexed(cUnit, rSrc, rIdx, rVal, 2, kWord); storeBaseIndexed(cUnit, rDst, rIdx, rVal, 2, kWord); #if defined(TARGET_ARM) // Combine sub & test using sub setflags encoding here newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1); opCondBranch(cUnit, kCondGe, target); #else oatFreeTemp(cUnit, rVal); opRegImm(cUnit, kOpSub, rIdx, 1); opCmpImmBranch(cUnit, kCondGe, rIdx, 0, target); #endif #if defined(TARGET_X86) // Restore the target pointer opRegRegImm(cUnit, kOpAdd, rRET0, rDst, -Array::DataOffset(component_size).Int32Value()); #endif } else if (!info->isRange) { // TUNING: interleave for (int i = 0; i < elems; i++) { RegLocation rlArg = loadValue(cUnit, info->args[i], kCoreReg); storeBaseDisp(cUnit, rRET0, Array::DataOffset(component_size).Int32Value() + i * 4, rlArg.lowReg, kWord); // If the loadValue caused a temp to be allocated, free it if (oatIsTemp(cUnit, rlArg.lowReg)) { oatFreeTemp(cUnit, rlArg.lowReg); } } } if (info->result.location != kLocInvalid) { storeValue(cUnit, info->result, oatGetReturn(cUnit, false /* not fp */)); } } void genSput(CompilationUnit* cUnit, uint32_t fieldIdx, RegLocation rlSrc, bool isLongOrDouble, bool isObject) { int fieldOffset; int ssbIndex; bool isVolatile; bool isReferrersClass; OatCompilationUnit mUnit(cUnit->class_loader, cUnit->class_linker, *cUnit->dex_file, cUnit->code_item, cUnit->method_idx, cUnit->access_flags); bool fastPath = cUnit->compiler->ComputeStaticFieldInfo(fieldIdx, &mUnit, fieldOffset, ssbIndex, isReferrersClass, isVolatile, true); if (fastPath && !SLOW_FIELD_PATH) { DCHECK_GE(fieldOffset, 0); int rBase; if (isReferrersClass) { // Fast path, static storage base is this method's class RegLocation rlMethod = loadCurrMethod(cUnit); rBase = oatAllocTemp(cUnit); loadWordDisp(cUnit, rlMethod.lowReg, Method::DeclaringClassOffset().Int32Value(), rBase); if (oatIsTemp(cUnit, rlMethod.lowReg)) { oatFreeTemp(cUnit, rlMethod.lowReg); } } else { // Medium path, static storage base in a different class which // requires checks that the other class is initialized. DCHECK_GE(ssbIndex, 0); // May do runtime call so everything to home locations. oatFlushAllRegs(cUnit); // Using fixed register to sync with possible call to runtime // support. int rMethod = rARG1; oatLockTemp(cUnit, rMethod); loadCurrMethodDirect(cUnit, rMethod); rBase = rARG0; oatLockTemp(cUnit, rBase); loadWordDisp(cUnit, rMethod, Method::DexCacheInitializedStaticStorageOffset().Int32Value(), rBase); loadWordDisp(cUnit, rBase, Array::DataOffset(sizeof(Object*)).Int32Value() + sizeof(int32_t*) * ssbIndex, rBase); // rBase now points at appropriate static storage base (Class*) // or NULL if not initialized. Check for NULL and call helper if NULL. // TUNING: fast path should fall through LIR* branchOver = opCmpImmBranch(cUnit, kCondNe, rBase, 0, NULL); loadConstant(cUnit, rARG0, ssbIndex); callRuntimeHelperImm(cUnit, ENTRYPOINT_OFFSET(pInitializeStaticStorage), ssbIndex); #if defined(TARGET_MIPS) // For Arm, rRET0 = rARG0 = rBASE, for Mips, we need to copy opRegCopy(cUnit, rBase, rRET0); #endif LIR* skipTarget = newLIR0(cUnit, kPseudoTargetLabel); branchOver->target = (LIR*)skipTarget; oatFreeTemp(cUnit, rMethod); } // rBase now holds static storage base if (isLongOrDouble) { rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg); } else { rlSrc = loadValue(cUnit, rlSrc, kAnyReg); } //FIXME: need to generalize the barrier call if (isVolatile) { oatGenMemBarrier(cUnit, kST); } if (isLongOrDouble) { storeBaseDispWide(cUnit, rBase, fieldOffset, rlSrc.lowReg, rlSrc.highReg); } else { storeWordDisp(cUnit, rBase, fieldOffset, rlSrc.lowReg); } if (isVolatile) { oatGenMemBarrier(cUnit, kSY); } if (isObject) { markGCCard(cUnit, rlSrc.lowReg, rBase); } oatFreeTemp(cUnit, rBase); } else { oatFlushAllRegs(cUnit); // Everything to home locations int setterOffset = isLongOrDouble ? ENTRYPOINT_OFFSET(pSet64Static) : (isObject ? ENTRYPOINT_OFFSET(pSetObjStatic) : ENTRYPOINT_OFFSET(pSet32Static)); callRuntimeHelperImmRegLocation(cUnit, setterOffset, fieldIdx, rlSrc); } } void genSget(CompilationUnit* cUnit, uint32_t fieldIdx, RegLocation rlDest, bool isLongOrDouble, bool isObject) { int fieldOffset; int ssbIndex; bool isVolatile; bool isReferrersClass; OatCompilationUnit mUnit(cUnit->class_loader, cUnit->class_linker, *cUnit->dex_file, cUnit->code_item, cUnit->method_idx, cUnit->access_flags); bool fastPath = cUnit->compiler->ComputeStaticFieldInfo(fieldIdx, &mUnit, fieldOffset, ssbIndex, isReferrersClass, isVolatile, false); if (fastPath && !SLOW_FIELD_PATH) { DCHECK_GE(fieldOffset, 0); int rBase; if (isReferrersClass) { // Fast path, static storage base is this method's class RegLocation rlMethod = loadCurrMethod(cUnit); rBase = oatAllocTemp(cUnit); loadWordDisp(cUnit, rlMethod.lowReg, Method::DeclaringClassOffset().Int32Value(), rBase); } else { // Medium path, static storage base in a different class which // requires checks that the other class is initialized DCHECK_GE(ssbIndex, 0); // May do runtime call so everything to home locations. oatFlushAllRegs(cUnit); // Using fixed register to sync with possible call to runtime // support int rMethod = rARG1; oatLockTemp(cUnit, rMethod); loadCurrMethodDirect(cUnit, rMethod); rBase = rARG0; oatLockTemp(cUnit, rBase); loadWordDisp(cUnit, rMethod, Method::DexCacheInitializedStaticStorageOffset().Int32Value(), rBase); loadWordDisp(cUnit, rBase, Array::DataOffset(sizeof(Object*)).Int32Value() + sizeof(int32_t*) * ssbIndex, rBase); // rBase now points at appropriate static storage base (Class*) // or NULL if not initialized. Check for NULL and call helper if NULL. // TUNING: fast path should fall through LIR* branchOver = opCmpImmBranch(cUnit, kCondNe, rBase, 0, NULL); callRuntimeHelperImm(cUnit, ENTRYPOINT_OFFSET(pInitializeStaticStorage), ssbIndex); #if defined(TARGET_MIPS) // For Arm, rRET0 = rARG0 = rBASE, for Mips, we need to copy opRegCopy(cUnit, rBase, rRET0); #endif LIR* skipTarget = newLIR0(cUnit, kPseudoTargetLabel); branchOver->target = (LIR*)skipTarget; oatFreeTemp(cUnit, rMethod); } // rBase now holds static storage base RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true); if (isVolatile) { oatGenMemBarrier(cUnit, kSY); } if (isLongOrDouble) { loadBaseDispWide(cUnit, rBase, fieldOffset, rlResult.lowReg, rlResult.highReg, INVALID_SREG); } else { loadWordDisp(cUnit, rBase, fieldOffset, rlResult.lowReg); } oatFreeTemp(cUnit, rBase); if (isLongOrDouble) { storeValueWide(cUnit, rlDest, rlResult); } else { storeValue(cUnit, rlDest, rlResult); } } else { oatFlushAllRegs(cUnit); // Everything to home locations int getterOffset = isLongOrDouble ? ENTRYPOINT_OFFSET(pGet64Static) : (isObject ? ENTRYPOINT_OFFSET(pGetObjStatic) : ENTRYPOINT_OFFSET(pGet32Static)); callRuntimeHelperImm(cUnit, getterOffset, fieldIdx); if (isLongOrDouble) { RegLocation rlResult = oatGetReturnWide(cUnit, rlDest.fp); storeValueWide(cUnit, rlDest, rlResult); } else { RegLocation rlResult = oatGetReturn(cUnit, rlDest.fp); storeValue(cUnit, rlDest, rlResult); } } } // Debugging routine - if null target, branch to DebugMe void genShowTarget(CompilationUnit* cUnit) { #if defined(TARGET_X86) UNIMPLEMENTED(WARNING) << "genShowTarget"; #else LIR* branchOver = opCmpImmBranch(cUnit, kCondNe, rINVOKE_TGT, 0, NULL); loadWordDisp(cUnit, rSELF, ENTRYPOINT_OFFSET(pDebugMe), rINVOKE_TGT); LIR* target = newLIR0(cUnit, kPseudoTargetLabel); branchOver->target = (LIR*)target; #endif } void genThrowVerificationError(CompilationUnit* cUnit, int info1, int info2) { callRuntimeHelperImmImm(cUnit, ENTRYPOINT_OFFSET(pThrowVerificationErrorFromCode), info1, info2); } void handleSuspendLaunchpads(CompilationUnit *cUnit) { LIR** suspendLabel = (LIR **)cUnit->suspendLaunchpads.elemList; int numElems = cUnit->suspendLaunchpads.numUsed; for (int i = 0; i < numElems; i++) { oatResetRegPool(cUnit); oatResetDefTracking(cUnit); LIR* lab = suspendLabel[i]; LIR* resumeLab = (LIR*)lab->operands[0]; cUnit->currentDalvikOffset = lab->operands[1]; oatAppendLIR(cUnit, lab); #if defined(TARGET_X86) opThreadMem(cUnit, kOpBlx, ENTRYPOINT_OFFSET(pTestSuspendFromCode)); #else int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pTestSuspendFromCode)); opReg(cUnit, kOpBlx, rTgt); #endif opUnconditionalBranch(cUnit, resumeLab); } } void handleIntrinsicLaunchpads(CompilationUnit *cUnit) { LIR** intrinsicLabel = (LIR **)cUnit->intrinsicLaunchpads.elemList; int numElems = cUnit->intrinsicLaunchpads.numUsed; for (int i = 0; i < numElems; i++) { oatResetRegPool(cUnit); oatResetDefTracking(cUnit); LIR* lab = intrinsicLabel[i]; CallInfo* info = (CallInfo*)lab->operands[0]; cUnit->currentDalvikOffset = info->offset; oatAppendLIR(cUnit, lab); genInvoke(cUnit, info); LIR* resumeLab = (LIR*)lab->operands[2]; if (resumeLab != NULL) { opUnconditionalBranch(cUnit, resumeLab); } } } void handleThrowLaunchpads(CompilationUnit *cUnit) { LIR** throwLabel = (LIR **)cUnit->throwLaunchpads.elemList; int numElems = cUnit->throwLaunchpads.numUsed; for (int i = 0; i < numElems; i++) { oatResetRegPool(cUnit); oatResetDefTracking(cUnit); LIR* lab = throwLabel[i]; cUnit->currentDalvikOffset = lab->operands[1]; oatAppendLIR(cUnit, lab); int funcOffset = 0; int v1 = lab->operands[2]; int v2 = lab->operands[3]; switch (lab->operands[0]) { case kThrowNullPointer: funcOffset = ENTRYPOINT_OFFSET(pThrowNullPointerFromCode); break; case kThrowArrayBounds: // Move v1 (array index) to rARG0 and v2 (array length) to rARG1 if (v2 != rARG0) { opRegCopy(cUnit, rARG0, v1); #if defined (TARGET_X86) // x86 leaves the array pointer in v2, so load the array length that the handler expects opRegMem(cUnit, kOpMov, rARG1, v2, Array::LengthOffset().Int32Value()); #else opRegCopy(cUnit, rARG1, v2); #endif } else { if (v1 == rARG1) { // Swap v1 and v2, using rARG2 as a temp opRegCopy(cUnit, rARG2, v1); #if defined (TARGET_X86) // x86 leaves the array pointer in v2, so load the array length that the handler expects opRegMem(cUnit, kOpMov, rARG1, v2, Array::LengthOffset().Int32Value()); #else opRegCopy(cUnit, rARG1, v2); #endif opRegCopy(cUnit, rARG0, rARG2); } else { #if defined (TARGET_X86) // x86 leaves the array pointer in v2, so load the array length that the handler expects opRegMem(cUnit, kOpMov, rARG1, v2, Array::LengthOffset().Int32Value()); #else opRegCopy(cUnit, rARG1, v2); #endif opRegCopy(cUnit, rARG0, v1); } } funcOffset = ENTRYPOINT_OFFSET(pThrowArrayBoundsFromCode); break; case kThrowDivZero: funcOffset = ENTRYPOINT_OFFSET(pThrowDivZeroFromCode); break; case kThrowVerificationError: loadConstant(cUnit, rARG0, v1); loadConstant(cUnit, rARG1, v2); funcOffset = ENTRYPOINT_OFFSET(pThrowVerificationErrorFromCode); break; case kThrowNoSuchMethod: opRegCopy(cUnit, rARG0, v1); funcOffset = ENTRYPOINT_OFFSET(pThrowNoSuchMethodFromCode); break; case kThrowStackOverflow: funcOffset = ENTRYPOINT_OFFSET(pThrowStackOverflowFromCode); // Restore stack alignment #if !defined(TARGET_X86) opRegImm(cUnit, kOpAdd, rSP, (cUnit->numCoreSpills + cUnit->numFPSpills) * 4); #else opRegImm(cUnit, kOpAdd, rSP, cUnit->frameSize); #endif break; default: LOG(FATAL) << "Unexpected throw kind: " << lab->operands[0]; } oatClobberCalleeSave(cUnit); #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, funcOffset); opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, funcOffset); #endif } } /* Needed by the Assembler */ void oatSetupResourceMasks(LIR* lir) { setupResourceMasks(lir); } bool fastInstance(CompilationUnit* cUnit, uint32_t fieldIdx, int& fieldOffset, bool& isVolatile, bool isPut) { OatCompilationUnit mUnit(cUnit->class_loader, cUnit->class_linker, *cUnit->dex_file, cUnit->code_item, cUnit->method_idx, cUnit->access_flags); return cUnit->compiler->ComputeInstanceFieldInfo(fieldIdx, &mUnit, fieldOffset, isVolatile, isPut); } void genIGet(CompilationUnit* cUnit, uint32_t fieldIdx, int optFlags, OpSize size, RegLocation rlDest, RegLocation rlObj, bool isLongOrDouble, bool isObject) { int fieldOffset; bool isVolatile; bool fastPath = fastInstance(cUnit, fieldIdx, fieldOffset, isVolatile, false); if (fastPath && !SLOW_FIELD_PATH) { RegLocation rlResult; RegisterClass regClass = oatRegClassBySize(size); DCHECK_GE(fieldOffset, 0); rlObj = loadValue(cUnit, rlObj, kCoreReg); if (isLongOrDouble) { DCHECK(rlDest.wide); genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, optFlags); #if defined(TARGET_X86) rlResult = oatEvalLoc(cUnit, rlDest, regClass, true); genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, optFlags); loadBaseDispWide(cUnit, rlObj.lowReg, fieldOffset, rlResult.lowReg, rlResult.highReg, rlObj.sRegLow); if (isVolatile) { oatGenMemBarrier(cUnit, kSY); } #else int regPtr = oatAllocTemp(cUnit); opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset); rlResult = oatEvalLoc(cUnit, rlDest, regClass, true); loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg); if (isVolatile) { oatGenMemBarrier(cUnit, kSY); } oatFreeTemp(cUnit, regPtr); #endif storeValueWide(cUnit, rlDest, rlResult); } else { rlResult = oatEvalLoc(cUnit, rlDest, regClass, true); genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, optFlags); loadBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlResult.lowReg, kWord, rlObj.sRegLow); if (isVolatile) { oatGenMemBarrier(cUnit, kSY); } storeValue(cUnit, rlDest, rlResult); } } else { int getterOffset = isLongOrDouble ? ENTRYPOINT_OFFSET(pGet64Instance) : (isObject ? ENTRYPOINT_OFFSET(pGetObjInstance) : ENTRYPOINT_OFFSET(pGet32Instance)); callRuntimeHelperImmRegLocation(cUnit, getterOffset, fieldIdx, rlObj); if (isLongOrDouble) { RegLocation rlResult = oatGetReturnWide(cUnit, rlDest.fp); storeValueWide(cUnit, rlDest, rlResult); } else { RegLocation rlResult = oatGetReturn(cUnit, rlDest.fp); storeValue(cUnit, rlDest, rlResult); } } } void genIPut(CompilationUnit* cUnit, uint32_t fieldIdx, int optFlags, OpSize size, RegLocation rlSrc, RegLocation rlObj, bool isLongOrDouble, bool isObject) { int fieldOffset; bool isVolatile; bool fastPath = fastInstance(cUnit, fieldIdx, fieldOffset, isVolatile, true); if (fastPath && !SLOW_FIELD_PATH) { RegisterClass regClass = oatRegClassBySize(size); DCHECK_GE(fieldOffset, 0); rlObj = loadValue(cUnit, rlObj, kCoreReg); if (isLongOrDouble) { int regPtr; rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg); genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, optFlags); regPtr = oatAllocTemp(cUnit); opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset); if (isVolatile) { oatGenMemBarrier(cUnit, kST); } storeBaseDispWide(cUnit, regPtr, 0, rlSrc.lowReg, rlSrc.highReg); if (isVolatile) { oatGenMemBarrier(cUnit, kSY); } oatFreeTemp(cUnit, regPtr); } else { rlSrc = loadValue(cUnit, rlSrc, regClass); genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, optFlags); if (isVolatile) { oatGenMemBarrier(cUnit, kST); } storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, kWord); if (isVolatile) { oatGenMemBarrier(cUnit, kSY); } if (isObject) { markGCCard(cUnit, rlSrc.lowReg, rlObj.lowReg); } } } else { int setterOffset = isLongOrDouble ? ENTRYPOINT_OFFSET(pSet64Instance) : (isObject ? ENTRYPOINT_OFFSET(pSetObjInstance) : ENTRYPOINT_OFFSET(pSet32Instance)); callRuntimeHelperImmRegLocationRegLocation(cUnit, setterOffset, fieldIdx, rlObj, rlSrc); } } void genConstClass(CompilationUnit* cUnit, uint32_t type_idx, RegLocation rlDest) { RegLocation rlMethod = loadCurrMethod(cUnit); int resReg = oatAllocTemp(cUnit); RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx, *cUnit->dex_file, type_idx)) { // Call out to helper which resolves type and verifies access. // Resolved type returned in rRET0. callRuntimeHelperImmReg(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode), type_idx, rlMethod.lowReg); RegLocation rlResult = oatGetReturn(cUnit, false); storeValue(cUnit, rlDest, rlResult); } else { // We're don't need access checks, load type from dex cache int32_t dex_cache_offset = Method::DexCacheResolvedTypesOffset().Int32Value(); loadWordDisp(cUnit, rlMethod.lowReg, dex_cache_offset, resReg); int32_t offset_of_type = Array::DataOffset(sizeof(Class*)).Int32Value() + (sizeof(Class*) * type_idx); loadWordDisp(cUnit, resReg, offset_of_type, rlResult.lowReg); if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(*cUnit->dex_file, type_idx) || SLOW_TYPE_PATH) { // Slow path, at runtime test if type is null and if so initialize oatFlushAllRegs(cUnit); LIR* branch1 = opCmpImmBranch(cUnit, kCondEq, rlResult.lowReg, 0, NULL); // Resolved, store and hop over following code storeValue(cUnit, rlDest, rlResult); /* * Because we have stores of the target value on two paths, * clobber temp tracking for the destination using the ssa name */ oatClobberSReg(cUnit, rlDest.sRegLow); LIR* branch2 = opUnconditionalBranch(cUnit,0); // TUNING: move slow path to end & remove unconditional branch LIR* target1 = newLIR0(cUnit, kPseudoTargetLabel); // Call out to helper, which will return resolved type in rARG0 callRuntimeHelperImmReg(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeFromCode), type_idx, rlMethod.lowReg); RegLocation rlResult = oatGetReturn(cUnit, false); storeValue(cUnit, rlDest, rlResult); /* * Because we have stores of the target value on two paths, * clobber temp tracking for the destination using the ssa name */ oatClobberSReg(cUnit, rlDest.sRegLow); // Rejoin code paths LIR* target2 = newLIR0(cUnit, kPseudoTargetLabel); branch1->target = (LIR*)target1; branch2->target = (LIR*)target2; } else { // Fast path, we're done - just store result storeValue(cUnit, rlDest, rlResult); } } } void genConstString(CompilationUnit* cUnit, uint32_t string_idx, RegLocation rlDest) { /* NOTE: Most strings should be available at compile time */ int32_t offset_of_string = Array::DataOffset(sizeof(String*)).Int32Value() + (sizeof(String*) * string_idx); if (!cUnit->compiler->CanAssumeStringIsPresentInDexCache( *cUnit->dex_file, string_idx) || SLOW_STRING_PATH) { // slow path, resolve string if not in dex cache oatFlushAllRegs(cUnit); oatLockCallTemps(cUnit); // Using explicit registers loadCurrMethodDirect(cUnit, rARG2); loadWordDisp(cUnit, rARG2, Method::DexCacheStringsOffset().Int32Value(), rARG0); // Might call out to helper, which will return resolved string in rRET0 #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pResolveStringFromCode)); #endif loadWordDisp(cUnit, rRET0, offset_of_string, rARG0); loadConstant(cUnit, rARG1, string_idx); #if defined(TARGET_ARM) opRegImm(cUnit, kOpCmp, rRET0, 0); // Is resolved? genBarrier(cUnit); // For testing, always force through helper if (!EXERCISE_SLOWEST_STRING_PATH) { opIT(cUnit, kArmCondEq, "T"); } opRegCopy(cUnit, rARG0, rARG2); // .eq opReg(cUnit, kOpBlx, rTgt); // .eq, helper(Method*, string_idx) oatFreeTemp(cUnit, rTgt); #elif defined(TARGET_MIPS) LIR* branch = opCmpImmBranch(cUnit, kCondNe, rRET0, 0, NULL); opRegCopy(cUnit, rARG0, rARG2); // .eq opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); LIR* target = newLIR0(cUnit, kPseudoTargetLabel); branch->target = target; #else callRuntimeHelperRegReg(cUnit, ENTRYPOINT_OFFSET(pResolveStringFromCode), rARG2, rARG1); #endif genBarrier(cUnit); storeValue(cUnit, rlDest, oatGetReturn(cUnit, false)); } else { RegLocation rlMethod = loadCurrMethod(cUnit); int resReg = oatAllocTemp(cUnit); RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); loadWordDisp(cUnit, rlMethod.lowReg, Method::DexCacheStringsOffset().Int32Value(), resReg); loadWordDisp(cUnit, resReg, offset_of_string, rlResult.lowReg); storeValue(cUnit, rlDest, rlResult); } } /* * Let helper function take care of everything. Will * call Class::NewInstanceFromCode(type_idx, method); */ void genNewInstance(CompilationUnit* cUnit, uint32_t type_idx, RegLocation rlDest) { oatFlushAllRegs(cUnit); /* Everything to home location */ // alloc will always check for resolution, do we also need to verify // access because the verifier was unable to? int funcOffset; if (cUnit->compiler->CanAccessInstantiableTypeWithoutChecks( cUnit->method_idx, *cUnit->dex_file, type_idx)) { funcOffset = ENTRYPOINT_OFFSET(pAllocObjectFromCode); } else { funcOffset = ENTRYPOINT_OFFSET(pAllocObjectFromCodeWithAccessCheck); } callRuntimeHelperImmMethod(cUnit, funcOffset, type_idx); RegLocation rlResult = oatGetReturn(cUnit, false); storeValue(cUnit, rlDest, rlResult); } void genThrow(CompilationUnit* cUnit, RegLocation rlSrc) { oatFlushAllRegs(cUnit); callRuntimeHelperRegLocation(cUnit, ENTRYPOINT_OFFSET(pDeliverException), rlSrc); } void genInstanceof(CompilationUnit* cUnit, uint32_t type_idx, RegLocation rlDest, RegLocation rlSrc) { oatFlushAllRegs(cUnit); // May generate a call - use explicit registers oatLockCallTemps(cUnit); loadCurrMethodDirect(cUnit, rARG1); // rARG1 <= current Method* int classReg = rARG2; // rARG2 will hold the Class* if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx, *cUnit->dex_file, type_idx)) { // Check we have access to type_idx and if not throw IllegalAccessError, // returns Class* in rARG0 callRuntimeHelperImm(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode), type_idx); opRegCopy(cUnit, classReg, rRET0); // Align usage with fast path loadValueDirectFixed(cUnit, rlSrc, rARG0); // rARG0 <= ref } else { // Load dex cache entry into classReg (rARG2) loadValueDirectFixed(cUnit, rlSrc, rARG0); // rARG0 <= ref loadWordDisp(cUnit, rARG1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg); int32_t offset_of_type = Array::DataOffset(sizeof(Class*)).Int32Value() + (sizeof(Class*) * type_idx); loadWordDisp(cUnit, classReg, offset_of_type, classReg); if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache( *cUnit->dex_file, type_idx)) { // Need to test presence of type in dex cache at runtime LIR* hopBranch = opCmpImmBranch(cUnit, kCondNe, classReg, 0, NULL); // Not resolved // Call out to helper, which will return resolved type in rRET0 callRuntimeHelperImm(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeFromCode), type_idx); opRegCopy(cUnit, rARG2, rRET0); // Align usage with fast path loadValueDirectFixed(cUnit, rlSrc, rARG0); /* reload Ref */ // Rejoin code paths LIR* hopTarget = newLIR0(cUnit, kPseudoTargetLabel); hopBranch->target = (LIR*)hopTarget; } } /* rARG0 is ref, rARG2 is class. If ref==null, use directly as bool result */ LIR* branch1 = opCmpImmBranch(cUnit, kCondEq, rARG0, 0, NULL); /* load object->klass_ */ DCHECK_EQ(Object::ClassOffset().Int32Value(), 0); loadWordDisp(cUnit, rARG0, Object::ClassOffset().Int32Value(), rARG1); /* rARG0 is ref, rARG1 is ref->klass_, rARG2 is class */ #if defined(TARGET_ARM) /* Uses conditional nullification */ int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); opRegReg(cUnit, kOpCmp, rARG1, rARG2); // Same? opIT(cUnit, kArmCondEq, "EE"); // if-convert the test loadConstant(cUnit, rARG0, 1); // .eq case - load true opRegCopy(cUnit, rARG0, rARG2); // .ne case - arg0 <= class opReg(cUnit, kOpBlx, rTgt); // .ne case: helper(class, ref->class) oatFreeTemp(cUnit, rTgt); #else /* Uses branchovers */ loadConstant(cUnit, rARG0, 1); // assume true LIR* branchover = opCmpBranch(cUnit, kCondEq, rARG1, rARG2, NULL); #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); opRegCopy(cUnit, rARG0, rARG2); // .ne case - arg0 <= class opReg(cUnit, kOpBlx, rTgt); // .ne case: helper(class, ref->class) oatFreeTemp(cUnit, rTgt); #else opRegCopy(cUnit, rARG0, rARG2); opThreadMem(cUnit, kOpBlx, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); #endif #endif oatClobberCalleeSave(cUnit); /* branch targets here */ LIR* target = newLIR0(cUnit, kPseudoTargetLabel); RegLocation rlResult = oatGetReturn(cUnit, false); storeValue(cUnit, rlDest, rlResult); branch1->target = target; #if !defined(TARGET_ARM) branchover->target = target; #endif } void genCheckCast(CompilationUnit* cUnit, uint32_t type_idx, RegLocation rlSrc) { oatFlushAllRegs(cUnit); // May generate a call - use explicit registers oatLockCallTemps(cUnit); loadCurrMethodDirect(cUnit, rARG1); // rARG1 <= current Method* int classReg = rARG2; // rARG2 will hold the Class* if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx, *cUnit->dex_file, type_idx)) { // Check we have access to type_idx and if not throw IllegalAccessError, // returns Class* in rRET0 // InitializeTypeAndVerifyAccess(idx, method) callRuntimeHelperImmReg(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode), type_idx, rARG1); opRegCopy(cUnit, classReg, rRET0); // Align usage with fast path } else { // Load dex cache entry into classReg (rARG2) loadWordDisp(cUnit, rARG1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg); int32_t offset_of_type = Array::DataOffset(sizeof(Class*)).Int32Value() + (sizeof(Class*) * type_idx); loadWordDisp(cUnit, classReg, offset_of_type, classReg); if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache( *cUnit->dex_file, type_idx)) { // Need to test presence of type in dex cache at runtime LIR* hopBranch = opCmpImmBranch(cUnit, kCondNe, classReg, 0, NULL); // Not resolved // Call out to helper, which will return resolved type in rARG0 // InitializeTypeFromCode(idx, method) callRuntimeHelperImmReg(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeFromCode), type_idx, rARG1); opRegCopy(cUnit, classReg, rARG0); // Align usage with fast path // Rejoin code paths LIR* hopTarget = newLIR0(cUnit, kPseudoTargetLabel); hopBranch->target = (LIR*)hopTarget; } } // At this point, classReg (rARG2) has class loadValueDirectFixed(cUnit, rlSrc, rARG0); // rARG0 <= ref /* Null is OK - continue */ LIR* branch1 = opCmpImmBranch(cUnit, kCondEq, rARG0, 0, NULL); /* load object->klass_ */ DCHECK_EQ(Object::ClassOffset().Int32Value(), 0); loadWordDisp(cUnit, rARG0, Object::ClassOffset().Int32Value(), rARG1); /* rARG1 now contains object->klass_ */ #if defined(TARGET_MIPS) || defined(TARGET_X86) LIR* branch2 = opCmpBranch(cUnit, kCondEq, rARG1, classReg, NULL); callRuntimeHelperRegReg(cUnit, ENTRYPOINT_OFFSET(pCheckCastFromCode), rARG1, rARG2); #else // defined(TARGET_ARM) int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pCheckCastFromCode)); opRegReg(cUnit, kOpCmp, rARG1, classReg); LIR* branch2 = opCondBranch(cUnit, kCondEq, NULL); /* If eq, trivial yes */ opRegCopy(cUnit, rARG0, rARG1); opRegCopy(cUnit, rARG1, rARG2); oatClobberCalleeSave(cUnit); opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #endif /* branch target here */ LIR* target = newLIR0(cUnit, kPseudoTargetLabel); branch1->target = target; branch2->target = target; } /* * Generate array store * */ void genArrayObjPut(CompilationUnit* cUnit, int optFlags, RegLocation rlArray, RegLocation rlIndex, RegLocation rlSrc, int scale) { int lenOffset = Array::LengthOffset().Int32Value(); int dataOffset = Array::DataOffset(sizeof(Object*)).Int32Value(); oatFlushAllRegs(cUnit); // Use explicit registers oatLockCallTemps(cUnit); int rValue = rARG0; // Register holding value int rArrayClass = rARG1; // Register holding array's Class int rArray = rARG2; // Register holding array int rIndex = rARG3; // Register holding index into array loadValueDirectFixed(cUnit, rlArray, rArray); // Grab array loadValueDirectFixed(cUnit, rlSrc, rValue); // Grab value loadValueDirectFixed(cUnit, rlIndex, rIndex); // Grab index genNullCheck(cUnit, rlArray.sRegLow, rArray, optFlags); // NPE? // Store of null? LIR* null_value_check = opCmpImmBranch(cUnit, kCondEq, rValue, 0, NULL); // Get the array's class. loadWordDisp(cUnit, rArray, Object::ClassOffset().Int32Value(), rArrayClass); callRuntimeHelperRegReg(cUnit, ENTRYPOINT_OFFSET(pCanPutArrayElementFromCode), rValue, rArrayClass); // Redo loadValues in case they didn't survive the call. loadValueDirectFixed(cUnit, rlArray, rArray); // Reload array loadValueDirectFixed(cUnit, rlIndex, rIndex); // Reload index loadValueDirectFixed(cUnit, rlSrc, rValue); // Reload value rArrayClass = INVALID_REG; // Branch here if value to be stored == null LIR* target = newLIR0(cUnit, kPseudoTargetLabel); null_value_check->target = target; #if defined(TARGET_X86) // make an extra temp available for card mark below oatFreeTemp(cUnit, rARG1); if (!(optFlags & MIR_IGNORE_RANGE_CHECK)) { /* if (rlIndex >= [rlArray + lenOffset]) goto kThrowArrayBounds */ genRegMemCheck(cUnit, kCondUge, rIndex, rArray, lenOffset, kThrowArrayBounds); } storeBaseIndexedDisp(cUnit, rArray, rIndex, scale, dataOffset, rValue, INVALID_REG, kWord, INVALID_SREG); #else bool needsRangeCheck = (!(optFlags & MIR_IGNORE_RANGE_CHECK)); int regLen = INVALID_REG; if (needsRangeCheck) { regLen = rARG1; loadWordDisp(cUnit, rArray, lenOffset, regLen); // Get len } /* rPtr -> array data */ int rPtr = oatAllocTemp(cUnit); opRegRegImm(cUnit, kOpAdd, rPtr, rArray, dataOffset); if (needsRangeCheck) { genRegRegCheck(cUnit, kCondCs, rIndex, regLen, kThrowArrayBounds); } storeBaseIndexed(cUnit, rPtr, rIndex, rValue, scale, kWord); oatFreeTemp(cUnit, rPtr); #endif oatFreeTemp(cUnit, rIndex); markGCCard(cUnit, rValue, rArray); } /* * Generate array load */ void genArrayGet(CompilationUnit* cUnit, int optFlags, OpSize size, RegLocation rlArray, RegLocation rlIndex, RegLocation rlDest, int scale) { RegisterClass regClass = oatRegClassBySize(size); int lenOffset = Array::LengthOffset().Int32Value(); int dataOffset; RegLocation rlResult; rlArray = loadValue(cUnit, rlArray, kCoreReg); rlIndex = loadValue(cUnit, rlIndex, kCoreReg); if (size == kLong || size == kDouble) { dataOffset = Array::DataOffset(sizeof(int64_t)).Int32Value(); } else { dataOffset = Array::DataOffset(sizeof(int32_t)).Int32Value(); } /* null object? */ genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, optFlags); #if defined(TARGET_X86) if (!(optFlags & MIR_IGNORE_RANGE_CHECK)) { /* if (rlIndex >= [rlArray + lenOffset]) goto kThrowArrayBounds */ genRegMemCheck(cUnit, kCondUge, rlIndex.lowReg, rlArray.lowReg, lenOffset, kThrowArrayBounds); } if ((size == kLong) || (size == kDouble)) { int regAddr = oatAllocTemp(cUnit); newLIR5(cUnit, kX86Lea32RA, regAddr, rlArray.lowReg, rlIndex.lowReg, scale, dataOffset); oatFreeTemp(cUnit, rlArray.lowReg); oatFreeTemp(cUnit, rlIndex.lowReg); rlResult = oatEvalLoc(cUnit, rlDest, regClass, true); loadBaseIndexedDisp(cUnit, regAddr, INVALID_REG, 0, 0, rlResult.lowReg, rlResult.highReg, size, INVALID_SREG); storeValueWide(cUnit, rlDest, rlResult); } else { rlResult = oatEvalLoc(cUnit, rlDest, regClass, true); loadBaseIndexedDisp(cUnit, rlArray.lowReg, rlIndex.lowReg, scale, dataOffset, rlResult.lowReg, INVALID_REG, size, INVALID_SREG); storeValue(cUnit, rlDest, rlResult); } #else int regPtr = oatAllocTemp(cUnit); bool needsRangeCheck = (!(optFlags & MIR_IGNORE_RANGE_CHECK)); int regLen = INVALID_REG; if (needsRangeCheck) { regLen = oatAllocTemp(cUnit); /* Get len */ loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen); } /* regPtr -> array data */ opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset); oatFreeTemp(cUnit, rlArray.lowReg); if ((size == kLong) || (size == kDouble)) { if (scale) { int rNewIndex = oatAllocTemp(cUnit); opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale); opRegReg(cUnit, kOpAdd, regPtr, rNewIndex); oatFreeTemp(cUnit, rNewIndex); } else { opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg); } oatFreeTemp(cUnit, rlIndex.lowReg); rlResult = oatEvalLoc(cUnit, rlDest, regClass, true); if (needsRangeCheck) { // TODO: change kCondCS to a more meaningful name, is the sense of // carry-set/clear flipped? genRegRegCheck(cUnit, kCondCs, rlIndex.lowReg, regLen, kThrowArrayBounds); oatFreeTemp(cUnit, regLen); } loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg); oatFreeTemp(cUnit, regPtr); storeValueWide(cUnit, rlDest, rlResult); } else { rlResult = oatEvalLoc(cUnit, rlDest, regClass, true); if (needsRangeCheck) { // TODO: change kCondCS to a more meaningful name, is the sense of // carry-set/clear flipped? genRegRegCheck(cUnit, kCondCs, rlIndex.lowReg, regLen, kThrowArrayBounds); oatFreeTemp(cUnit, regLen); } loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg, scale, size); oatFreeTemp(cUnit, regPtr); storeValue(cUnit, rlDest, rlResult); } #endif } /* * Generate array store * */ void genArrayPut(CompilationUnit* cUnit, int optFlags, OpSize size, RegLocation rlArray, RegLocation rlIndex, RegLocation rlSrc, int scale) { RegisterClass regClass = oatRegClassBySize(size); int lenOffset = Array::LengthOffset().Int32Value(); int dataOffset; if (size == kLong || size == kDouble) { dataOffset = Array::DataOffset(sizeof(int64_t)).Int32Value(); } else { dataOffset = Array::DataOffset(sizeof(int32_t)).Int32Value(); } rlArray = loadValue(cUnit, rlArray, kCoreReg); rlIndex = loadValue(cUnit, rlIndex, kCoreReg); #if !defined(TARGET_X86) int regPtr; if (oatIsTemp(cUnit, rlArray.lowReg)) { oatClobber(cUnit, rlArray.lowReg); regPtr = rlArray.lowReg; } else { regPtr = oatAllocTemp(cUnit); opRegCopy(cUnit, regPtr, rlArray.lowReg); } #endif /* null object? */ genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, optFlags); #if defined(TARGET_X86) if (!(optFlags & MIR_IGNORE_RANGE_CHECK)) { /* if (rlIndex >= [rlArray + lenOffset]) goto kThrowArrayBounds */ genRegMemCheck(cUnit, kCondUge, rlIndex.lowReg, rlArray.lowReg, lenOffset, kThrowArrayBounds); } if ((size == kLong) || (size == kDouble)) { rlSrc = loadValueWide(cUnit, rlSrc, regClass); } else { rlSrc = loadValue(cUnit, rlSrc, regClass); } // If the src reg can't be byte accessed, move it to a temp first. if ((size == kSignedByte || size == kUnsignedByte) && rlSrc.lowReg >= 4) { int temp = oatAllocTemp(cUnit); opRegCopy(cUnit, temp, rlSrc.lowReg); storeBaseIndexedDisp(cUnit, rlArray.lowReg, rlIndex.lowReg, scale, dataOffset, temp, INVALID_REG, size, INVALID_SREG); } else { storeBaseIndexedDisp(cUnit, rlArray.lowReg, rlIndex.lowReg, scale, dataOffset, rlSrc.lowReg, rlSrc.highReg, size, INVALID_SREG); } #else bool needsRangeCheck = (!(optFlags & MIR_IGNORE_RANGE_CHECK)); int regLen = INVALID_REG; if (needsRangeCheck) { regLen = oatAllocTemp(cUnit); //NOTE: max live temps(4) here. /* Get len */ loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen); } /* regPtr -> array data */ opRegImm(cUnit, kOpAdd, regPtr, dataOffset); /* at this point, regPtr points to array, 2 live temps */ if ((size == kLong) || (size == kDouble)) { //TUNING: specific wide routine that can handle fp regs if (scale) { int rNewIndex = oatAllocTemp(cUnit); opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale); opRegReg(cUnit, kOpAdd, regPtr, rNewIndex); oatFreeTemp(cUnit, rNewIndex); } else { opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg); } rlSrc = loadValueWide(cUnit, rlSrc, regClass); if (needsRangeCheck) { genRegRegCheck(cUnit, kCondCs, rlIndex.lowReg, regLen, kThrowArrayBounds); oatFreeTemp(cUnit, regLen); } storeBaseDispWide(cUnit, regPtr, 0, rlSrc.lowReg, rlSrc.highReg); oatFreeTemp(cUnit, regPtr); } else { rlSrc = loadValue(cUnit, rlSrc, regClass); if (needsRangeCheck) { genRegRegCheck(cUnit, kCondCs, rlIndex.lowReg, regLen, kThrowArrayBounds); oatFreeTemp(cUnit, regLen); } storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg, scale, size); } #endif } void genLong3Addr(CompilationUnit* cUnit, OpKind firstOp, OpKind secondOp, RegLocation rlDest, RegLocation rlSrc1, RegLocation rlSrc2) { RegLocation rlResult; #if defined(TARGET_ARM) /* * NOTE: This is the one place in the code in which we might have * as many as six live temporary registers. There are 5 in the normal * set for Arm. Until we have spill capabilities, temporarily add * lr to the temp set. It is safe to do this locally, but note that * lr is used explicitly elsewhere in the code generator and cannot * normally be used as a general temp register. */ oatMarkTemp(cUnit, rLR); // Add lr to the temp pool oatFreeTemp(cUnit, rLR); // and make it available #endif rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg); rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg); rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); // The longs may overlap - use intermediate temp if so if (rlResult.lowReg == rlSrc1.highReg) { int tReg = oatAllocTemp(cUnit); opRegCopy(cUnit, tReg, rlSrc1.highReg); opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg); opRegRegReg(cUnit, secondOp, rlResult.highReg, tReg, rlSrc2.highReg); oatFreeTemp(cUnit, tReg); } else { opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg); opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg, rlSrc2.highReg); } /* * NOTE: If rlDest refers to a frame variable in a large frame, the * following storeValueWide might need to allocate a temp register. * To further work around the lack of a spill capability, explicitly * free any temps from rlSrc1 & rlSrc2 that aren't still live in rlResult. * Remove when spill is functional. */ freeRegLocTemps(cUnit, rlResult, rlSrc1); freeRegLocTemps(cUnit, rlResult, rlSrc2); storeValueWide(cUnit, rlDest, rlResult); #if defined(TARGET_ARM) oatClobber(cUnit, rLR); oatUnmarkTemp(cUnit, rLR); // Remove lr from the temp pool #endif } bool genShiftOpLong(CompilationUnit* cUnit, Instruction::Code opcode, RegLocation rlDest, RegLocation rlSrc1, RegLocation rlShift) { int funcOffset; switch (opcode) { case Instruction::SHL_LONG: case Instruction::SHL_LONG_2ADDR: funcOffset = ENTRYPOINT_OFFSET(pShlLong); break; case Instruction::SHR_LONG: case Instruction::SHR_LONG_2ADDR: funcOffset = ENTRYPOINT_OFFSET(pShrLong); break; case Instruction::USHR_LONG: case Instruction::USHR_LONG_2ADDR: funcOffset = ENTRYPOINT_OFFSET(pUshrLong); break; default: LOG(FATAL) << "Unexpected case"; return true; } oatFlushAllRegs(cUnit); /* Send everything to home location */ callRuntimeHelperRegLocationRegLocation(cUnit, funcOffset, rlSrc1, rlShift); RegLocation rlResult = oatGetReturnWide(cUnit, false); storeValueWide(cUnit, rlDest, rlResult); return false; } bool genArithOpInt(CompilationUnit* cUnit, Instruction::Code opcode, RegLocation rlDest, RegLocation rlSrc1, RegLocation rlSrc2) { OpKind op = kOpBkpt; bool callOut = false; bool checkZero = false; bool unary = false; RegLocation rlResult; bool shiftOp = false; int funcOffset; int retReg = rRET0; switch (opcode) { case Instruction::NEG_INT: op = kOpNeg; unary = true; break; case Instruction::NOT_INT: op = kOpMvn; unary = true; break; case Instruction::ADD_INT: case Instruction::ADD_INT_2ADDR: op = kOpAdd; break; case Instruction::SUB_INT: case Instruction::SUB_INT_2ADDR: op = kOpSub; break; case Instruction::MUL_INT: case Instruction::MUL_INT_2ADDR: op = kOpMul; break; case Instruction::DIV_INT: case Instruction::DIV_INT_2ADDR: checkZero = true; op = kOpDiv; callOut = true; funcOffset = ENTRYPOINT_OFFSET(pIdivmod); retReg = rRET0; break; /* NOTE: returns in rARG1 */ case Instruction::REM_INT: case Instruction::REM_INT_2ADDR: checkZero = true; op = kOpRem; callOut = true; funcOffset = ENTRYPOINT_OFFSET(pIdivmod); retReg = rRET1; break; case Instruction::AND_INT: case Instruction::AND_INT_2ADDR: op = kOpAnd; break; case Instruction::OR_INT: case Instruction::OR_INT_2ADDR: op = kOpOr; break; case Instruction::XOR_INT: case Instruction::XOR_INT_2ADDR: op = kOpXor; break; case Instruction::SHL_INT: case Instruction::SHL_INT_2ADDR: shiftOp = true; op = kOpLsl; break; case Instruction::SHR_INT: case Instruction::SHR_INT_2ADDR: shiftOp = true; op = kOpAsr; break; case Instruction::USHR_INT: case Instruction::USHR_INT_2ADDR: shiftOp = true; op = kOpLsr; break; default: LOG(FATAL) << "Invalid word arith op: " << (int)opcode; } if (!callOut) { if (unary) { rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg); rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); opRegReg(cUnit, op, rlResult.lowReg, rlSrc1.lowReg); } else { if (shiftOp) { #if !defined(TARGET_X86) rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg); int tReg = oatAllocTemp(cUnit); opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31); #else // X86 doesn't require masking and must use ECX loadValueDirectFixed(cUnit, rlSrc2, rCX); int tReg = rCX; #endif rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg); rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); opRegRegReg(cUnit, op, rlResult.lowReg, rlSrc1.lowReg, tReg); oatFreeTemp(cUnit, tReg); } else { rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg); rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg); rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); opRegRegReg(cUnit, op, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg); } } storeValue(cUnit, rlDest, rlResult); } else { RegLocation rlResult; oatFlushAllRegs(cUnit); /* Send everything to home location */ loadValueDirectFixed(cUnit, rlSrc2, rARG1); #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, funcOffset); #endif loadValueDirectFixed(cUnit, rlSrc1, rARG0); if (checkZero) { genImmedCheck(cUnit, kCondEq, rARG1, 0, kThrowDivZero); } #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, funcOffset); #endif if (retReg == rRET0) rlResult = oatGetReturn(cUnit, false); else rlResult = oatGetReturnAlt(cUnit); storeValue(cUnit, rlDest, rlResult); } return false; } /* * The following are the first-level codegen routines that analyze the format * of each bytecode then either dispatch special purpose codegen routines * or produce corresponding Thumb instructions directly. */ bool isPowerOfTwo(int x) { return (x & (x - 1)) == 0; } // Returns true if no more than two bits are set in 'x'. bool isPopCountLE2(unsigned int x) { x &= x - 1; return (x & (x - 1)) == 0; } // Returns the index of the lowest set bit in 'x'. int lowestSetBit(unsigned int x) { int bit_posn = 0; while ((x & 0xf) == 0) { bit_posn += 4; x >>= 4; } while ((x & 1) == 0) { bit_posn++; x >>= 1; } return bit_posn; } // Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit' // and store the result in 'rlDest'. bool handleEasyDivide(CompilationUnit* cUnit, Instruction::Code dalvikOpcode, RegLocation rlSrc, RegLocation rlDest, int lit) { #if defined(TARGET_ARM) // No divide instruction for Arm, so check for more special cases if (lit < 2) { return false; } if (!isPowerOfTwo(lit)) { return smallLiteralDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit); } #else if (lit < 2 || !isPowerOfTwo(lit)) { return false; } #endif int k = lowestSetBit(lit); if (k >= 30) { // Avoid special cases. return false; } bool div = (dalvikOpcode == Instruction::DIV_INT_LIT8 || dalvikOpcode == Instruction::DIV_INT_LIT16); rlSrc = loadValue(cUnit, rlSrc, kCoreReg); RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); if (div) { int tReg = oatAllocTemp(cUnit); if (lit == 2) { // Division by 2 is by far the most common division by constant. opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k); opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg); opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k); } else { opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31); opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k); opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg); opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k); } } else { int tReg1 = oatAllocTemp(cUnit); int tReg2 = oatAllocTemp(cUnit); if (lit == 2) { opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k); opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg); opRegRegImm(cUnit, kOpAnd, tReg2, tReg2, lit -1); opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1); } else { opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31); opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k); opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg); opRegRegImm(cUnit, kOpAnd, tReg2, tReg2, lit - 1); opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1); } } storeValue(cUnit, rlDest, rlResult); return true; } void genMultiplyByTwoBitMultiplier(CompilationUnit* cUnit, RegLocation rlSrc, RegLocation rlResult, int lit, int firstBit, int secondBit) { #if defined(TARGET_ARM) opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg, encodeShift(kArmLsl, secondBit - firstBit)); #else int tReg = oatAllocTemp(cUnit); opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, secondBit - firstBit); opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, tReg); oatFreeTemp(cUnit, tReg); #endif if (firstBit != 0) { opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit); } } // Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit' // and store the result in 'rlDest'. bool handleEasyMultiply(CompilationUnit* cUnit, RegLocation rlSrc, RegLocation rlDest, int lit) { // Can we simplify this multiplication? bool powerOfTwo = false; bool popCountLE2 = false; bool powerOfTwoMinusOne = false; if (lit < 2) { // Avoid special cases. return false; } else if (isPowerOfTwo(lit)) { powerOfTwo = true; } else if (isPopCountLE2(lit)) { popCountLE2 = true; } else if (isPowerOfTwo(lit + 1)) { powerOfTwoMinusOne = true; } else { return false; } rlSrc = loadValue(cUnit, rlSrc, kCoreReg); RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); if (powerOfTwo) { // Shift. opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg, lowestSetBit(lit)); } else if (popCountLE2) { // Shift and add and shift. int firstBit = lowestSetBit(lit); int secondBit = lowestSetBit(lit ^ (1 << firstBit)); genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit, firstBit, secondBit); } else { // Reverse subtract: (src << (shift + 1)) - src. DCHECK(powerOfTwoMinusOne); // TUNING: rsb dst, src, src lsl#lowestSetBit(lit + 1) int tReg = oatAllocTemp(cUnit); opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1)); opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg); } storeValue(cUnit, rlDest, rlResult); return true; } bool genArithOpIntLit(CompilationUnit* cUnit, Instruction::Code opcode, RegLocation rlDest, RegLocation rlSrc, int lit) { RegLocation rlResult; OpKind op = (OpKind)0; /* Make gcc happy */ int shiftOp = false; bool isDiv = false; int funcOffset; switch (opcode) { case Instruction::RSUB_INT_LIT8: case Instruction::RSUB_INT: { int tReg; //TUNING: add support for use of Arm rsub op rlSrc = loadValue(cUnit, rlSrc, kCoreReg); tReg = oatAllocTemp(cUnit); loadConstant(cUnit, tReg, lit); rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg); storeValue(cUnit, rlDest, rlResult); return false; break; } case Instruction::ADD_INT_LIT8: case Instruction::ADD_INT_LIT16: op = kOpAdd; break; case Instruction::MUL_INT_LIT8: case Instruction::MUL_INT_LIT16: { if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) { return false; } op = kOpMul; break; } case Instruction::AND_INT_LIT8: case Instruction::AND_INT_LIT16: op = kOpAnd; break; case Instruction::OR_INT_LIT8: case Instruction::OR_INT_LIT16: op = kOpOr; break; case Instruction::XOR_INT_LIT8: case Instruction::XOR_INT_LIT16: op = kOpXor; break; case Instruction::SHL_INT_LIT8: case Instruction::SHL_INT: lit &= 31; shiftOp = true; op = kOpLsl; break; case Instruction::SHR_INT_LIT8: case Instruction::SHR_INT: lit &= 31; shiftOp = true; op = kOpAsr; break; case Instruction::USHR_INT_LIT8: case Instruction::USHR_INT: lit &= 31; shiftOp = true; op = kOpLsr; break; case Instruction::DIV_INT_LIT8: case Instruction::DIV_INT_LIT16: case Instruction::REM_INT_LIT8: case Instruction::REM_INT_LIT16: if (lit == 0) { genImmedCheck(cUnit, kCondAl, 0, 0, kThrowDivZero); return false; } if (handleEasyDivide(cUnit, opcode, rlSrc, rlDest, lit)) { return false; } oatFlushAllRegs(cUnit); /* Everything to home location */ loadValueDirectFixed(cUnit, rlSrc, rARG0); oatClobber(cUnit, rARG0); funcOffset = ENTRYPOINT_OFFSET(pIdivmod); if ((opcode == Instruction::DIV_INT_LIT8) || (opcode == Instruction::DIV_INT_LIT16)) { isDiv = true; } else { isDiv = false; } callRuntimeHelperRegImm(cUnit, funcOffset, rARG0, lit); if (isDiv) rlResult = oatGetReturn(cUnit, false); else rlResult = oatGetReturnAlt(cUnit); storeValue(cUnit, rlDest, rlResult); return false; break; default: return true; } rlSrc = loadValue(cUnit, rlSrc, kCoreReg); rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); // Avoid shifts by literal 0 - no support in Thumb. Change to copy if (shiftOp && (lit == 0)) { opRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg); } else { opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit); } storeValue(cUnit, rlDest, rlResult); return false; } bool genArithOpLong(CompilationUnit* cUnit, Instruction::Code opcode, RegLocation rlDest, RegLocation rlSrc1, RegLocation rlSrc2) { RegLocation rlResult; OpKind firstOp = kOpBkpt; OpKind secondOp = kOpBkpt; bool callOut = false; bool checkZero = false; int funcOffset; int retReg = rRET0; switch (opcode) { case Instruction::NOT_LONG: rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg); rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); // Check for destructive overlap if (rlResult.lowReg == rlSrc2.highReg) { int tReg = oatAllocTemp(cUnit); opRegCopy(cUnit, tReg, rlSrc2.highReg); opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg); opRegReg(cUnit, kOpMvn, rlResult.highReg, tReg); oatFreeTemp(cUnit, tReg); } else { opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg); opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg); } storeValueWide(cUnit, rlDest, rlResult); return false; break; case Instruction::ADD_LONG: case Instruction::ADD_LONG_2ADDR: #if defined(TARGET_MIPS) || defined(TARGET_X86) return genAddLong(cUnit, rlDest, rlSrc1, rlSrc2); #else firstOp = kOpAdd; secondOp = kOpAdc; break; #endif case Instruction::SUB_LONG: case Instruction::SUB_LONG_2ADDR: #if defined(TARGET_MIPS) || defined(TARGET_X86) return genSubLong(cUnit, rlDest, rlSrc1, rlSrc2); #else firstOp = kOpSub; secondOp = kOpSbc; break; #endif case Instruction::MUL_LONG: case Instruction::MUL_LONG_2ADDR: callOut = true; retReg = rRET0; funcOffset = ENTRYPOINT_OFFSET(pLmul); break; case Instruction::DIV_LONG: case Instruction::DIV_LONG_2ADDR: callOut = true; checkZero = true; retReg = rRET0; funcOffset = ENTRYPOINT_OFFSET(pLdiv); break; case Instruction::REM_LONG: case Instruction::REM_LONG_2ADDR: callOut = true; checkZero = true; funcOffset = ENTRYPOINT_OFFSET(pLdivmod); #if defined(TARGET_ARM) /* NOTE - result is in rARG2/rARG3 instead of rRET0/rRET1 */ retReg = rARG2; #else retReg = rRET0; #endif break; case Instruction::AND_LONG_2ADDR: case Instruction::AND_LONG: #if defined(TARGET_X86) return genAndLong(cUnit, rlDest, rlSrc1, rlSrc2); #else firstOp = kOpAnd; secondOp = kOpAnd; break; #endif case Instruction::OR_LONG: case Instruction::OR_LONG_2ADDR: #if defined(TARGET_X86) return genOrLong(cUnit, rlDest, rlSrc1, rlSrc2); #else firstOp = kOpOr; secondOp = kOpOr; break; #endif case Instruction::XOR_LONG: case Instruction::XOR_LONG_2ADDR: #if defined(TARGET_X86) return genXorLong(cUnit, rlDest, rlSrc1, rlSrc2); #else firstOp = kOpXor; secondOp = kOpXor; break; #endif case Instruction::NEG_LONG: { return genNegLong(cUnit, rlDest, rlSrc2); } default: LOG(FATAL) << "Invalid long arith op"; } if (!callOut) { genLong3Addr(cUnit, firstOp, secondOp, rlDest, rlSrc1, rlSrc2); } else { oatFlushAllRegs(cUnit); /* Send everything to home location */ if (checkZero) { loadValueDirectWideFixed(cUnit, rlSrc2, rARG2, rARG3); #if !defined(TARGET_X86) int rTgt = loadHelper(cUnit, funcOffset); #endif int tReg = oatAllocTemp(cUnit); #if defined(TARGET_ARM) newLIR4(cUnit, kThumb2OrrRRRs, tReg, rARG2, rARG3, 0); oatFreeTemp(cUnit, tReg); genCheck(cUnit, kCondEq, kThrowDivZero); #else opRegRegReg(cUnit, kOpOr, tReg, rARG2, rARG3); #endif genImmedCheck(cUnit, kCondEq, tReg, 0, kThrowDivZero); oatFreeTemp(cUnit, tReg); loadValueDirectWideFixed(cUnit, rlSrc1, rARG0, rARG1); #if !defined(TARGET_X86) opReg(cUnit, kOpBlx, rTgt); oatFreeTemp(cUnit, rTgt); #else opThreadMem(cUnit, kOpBlx, funcOffset); #endif } else { callRuntimeHelperRegLocationRegLocation(cUnit, funcOffset, rlSrc1, rlSrc2); } // Adjust return regs in to handle case of rem returning rARG2/rARG3 if (retReg == rRET0) rlResult = oatGetReturnWide(cUnit, false); else rlResult = oatGetReturnWideAlt(cUnit); storeValueWide(cUnit, rlDest, rlResult); } return false; } bool genConversionCall(CompilationUnit* cUnit, int funcOffset, RegLocation rlDest, RegLocation rlSrc) { /* * Don't optimize the register usage since it calls out to support * functions */ oatFlushAllRegs(cUnit); /* Send everything to home location */ if (rlSrc.wide) { loadValueDirectWideFixed(cUnit, rlSrc, rARG0, rARG1); } else { loadValueDirectFixed(cUnit, rlSrc, rARG0); } callRuntimeHelperRegLocation(cUnit, funcOffset, rlSrc); if (rlDest.wide) { RegLocation rlResult; rlResult = oatGetReturnWide(cUnit, rlDest.fp); storeValueWide(cUnit, rlDest, rlResult); } else { RegLocation rlResult; rlResult = oatGetReturn(cUnit, rlDest.fp); storeValue(cUnit, rlDest, rlResult); } return false; } void genNegFloat(CompilationUnit* cUnit, RegLocation rlDest, RegLocation rlSrc); bool genArithOpFloatPortable(CompilationUnit* cUnit, Instruction::Code opcode, RegLocation rlDest, RegLocation rlSrc1, RegLocation rlSrc2) { RegLocation rlResult; int funcOffset; switch (opcode) { case Instruction::ADD_FLOAT_2ADDR: case Instruction::ADD_FLOAT: funcOffset = ENTRYPOINT_OFFSET(pFadd); break; case Instruction::SUB_FLOAT_2ADDR: case Instruction::SUB_FLOAT: funcOffset = ENTRYPOINT_OFFSET(pFsub); break; case Instruction::DIV_FLOAT_2ADDR: case Instruction::DIV_FLOAT: funcOffset = ENTRYPOINT_OFFSET(pFdiv); break; case Instruction::MUL_FLOAT_2ADDR: case Instruction::MUL_FLOAT: funcOffset = ENTRYPOINT_OFFSET(pFmul); break; case Instruction::REM_FLOAT_2ADDR: case Instruction::REM_FLOAT: funcOffset = ENTRYPOINT_OFFSET(pFmodf); break; case Instruction::NEG_FLOAT: { genNegFloat(cUnit, rlDest, rlSrc1); return false; } default: return true; } oatFlushAllRegs(cUnit); /* Send everything to home location */ callRuntimeHelperRegLocationRegLocation(cUnit, funcOffset, rlSrc1, rlSrc2); rlResult = oatGetReturn(cUnit, true); storeValue(cUnit, rlDest, rlResult); return false; } void genNegDouble(CompilationUnit* cUnit, RegLocation rlDst, RegLocation rlSrc); bool genArithOpDoublePortable(CompilationUnit* cUnit, Instruction::Code opcode, RegLocation rlDest, RegLocation rlSrc1, RegLocation rlSrc2) { RegLocation rlResult; int funcOffset; switch (opcode) { case Instruction::ADD_DOUBLE_2ADDR: case Instruction::ADD_DOUBLE: funcOffset = ENTRYPOINT_OFFSET(pDadd); break; case Instruction::SUB_DOUBLE_2ADDR: case Instruction::SUB_DOUBLE: funcOffset = ENTRYPOINT_OFFSET(pDsub); break; case Instruction::DIV_DOUBLE_2ADDR: case Instruction::DIV_DOUBLE: funcOffset = ENTRYPOINT_OFFSET(pDdiv); break; case Instruction::MUL_DOUBLE_2ADDR: case Instruction::MUL_DOUBLE: funcOffset = ENTRYPOINT_OFFSET(pDmul); break; case Instruction::REM_DOUBLE_2ADDR: case Instruction::REM_DOUBLE: funcOffset = ENTRYPOINT_OFFSET(pFmod); break; case Instruction::NEG_DOUBLE: { genNegDouble(cUnit, rlDest, rlSrc1); return false; } default: return true; } oatFlushAllRegs(cUnit); /* Send everything to home location */ callRuntimeHelperRegLocationRegLocation(cUnit, funcOffset, rlSrc1, rlSrc2); rlResult = oatGetReturnWide(cUnit, true); storeValueWide(cUnit, rlDest, rlResult); return false; } bool genConversionPortable(CompilationUnit* cUnit, Instruction::Code opcode, RegLocation rlDest, RegLocation rlSrc) { switch (opcode) { case Instruction::INT_TO_FLOAT: return genConversionCall(cUnit, ENTRYPOINT_OFFSET(pI2f), rlDest, rlSrc); case Instruction::FLOAT_TO_INT: return genConversionCall(cUnit, ENTRYPOINT_OFFSET(pF2iz), rlDest, rlSrc); case Instruction::DOUBLE_TO_FLOAT: return genConversionCall(cUnit, ENTRYPOINT_OFFSET(pD2f), rlDest, rlSrc); case Instruction::FLOAT_TO_DOUBLE: return genConversionCall(cUnit, ENTRYPOINT_OFFSET(pF2d), rlDest, rlSrc); case Instruction::INT_TO_DOUBLE: return genConversionCall(cUnit, ENTRYPOINT_OFFSET(pI2d), rlDest, rlSrc); case Instruction::DOUBLE_TO_INT: return genConversionCall(cUnit, ENTRYPOINT_OFFSET(pD2iz), rlDest, rlSrc); case Instruction::FLOAT_TO_LONG: return genConversionCall(cUnit, ENTRYPOINT_OFFSET(pF2l), rlDest, rlSrc); case Instruction::LONG_TO_FLOAT: return genConversionCall(cUnit, ENTRYPOINT_OFFSET(pL2f), rlDest, rlSrc); case Instruction::DOUBLE_TO_LONG: return genConversionCall(cUnit, ENTRYPOINT_OFFSET(pD2l), rlDest, rlSrc); case Instruction::LONG_TO_DOUBLE: return genConversionCall(cUnit, ENTRYPOINT_OFFSET(pL2d), rlDest, rlSrc); default: return true; } return false; } /* * Generate callout to updateDebugger. Note that we're overloading * the use of rSUSPEND here. When the debugger is active, this * register holds the address of the update function. So, if it's * non-null, we call out to it. * * Note also that rRET0 and rRET1 must be preserved across this * code. This must be handled by the stub. */ void genDebuggerUpdate(CompilationUnit* cUnit, int32_t offset) { // Following DCHECK verifies that dPC is in range of single load immediate DCHECK((offset == DEBUGGER_METHOD_ENTRY) || (offset == DEBUGGER_METHOD_EXIT) || ((offset & 0xffff) == offset)); oatClobberCalleeSave(cUnit); #if defined(TARGET_ARM) opRegImm(cUnit, kOpCmp, rSUSPEND, 0); opIT(cUnit, kArmCondNe, "T"); loadConstant(cUnit, rARG2, offset); // arg2 <- Entry code opReg(cUnit, kOpBlx, rSUSPEND); #elif defined(TARGET_X86) UNIMPLEMENTED(FATAL); #else LIR* branch = opCmpImmBranch(cUnit, kCondEq, rSUSPEND, 0, NULL); loadConstant(cUnit, rARG2, offset); opReg(cUnit, kOpBlx, rSUSPEND); LIR* target = newLIR0(cUnit, kPseudoTargetLabel); branch->target = (LIR*)target; #endif oatFreeTemp(cUnit, rARG2); } /* Check if we need to check for pending suspend request */ void genSuspendTest(CompilationUnit* cUnit, int optFlags) { if (NO_SUSPEND || (optFlags & MIR_IGNORE_SUSPEND_CHECK)) { return; } oatFlushAllRegs(cUnit); if (cUnit->genDebugger) { // If generating code for the debugger, always check for suspension #if defined(TARGET_X86) UNIMPLEMENTED(FATAL); #else int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pTestSuspendFromCode)); opReg(cUnit, kOpBlx, rTgt); // Refresh rSUSPEND loadWordDisp(cUnit, rSELF, ENTRYPOINT_OFFSET(pUpdateDebuggerFromCode), rSUSPEND); #endif } else { LIR* branch = NULL; #if defined(TARGET_ARM) // In non-debug case, only check periodically newLIR2(cUnit, kThumbSubRI8, rSUSPEND, 1); branch = opCondBranch(cUnit, kCondEq, NULL); #elif defined(TARGET_X86) newLIR2(cUnit, kX86Cmp32TI8, Thread::SuspendCountOffset().Int32Value(), 0); branch = opCondBranch(cUnit, kCondNe, NULL); #else opRegImm(cUnit, kOpSub, rSUSPEND, 1); branch = opCmpImmBranch(cUnit, kCondEq, rSUSPEND, 0, NULL); #endif LIR* retLab = newLIR0(cUnit, kPseudoTargetLabel); LIR* target = rawLIR(cUnit, cUnit->currentDalvikOffset, kPseudoSuspendTarget, (intptr_t)retLab, cUnit->currentDalvikOffset); branch->target = (LIR*)target; oatInsertGrowableList(cUnit, &cUnit->suspendLaunchpads, (intptr_t)target); } } /* Check if we need to check for pending suspend request */ void genSuspendTestAndBranch(CompilationUnit* cUnit, int optFlags, LIR* target) { if (NO_SUSPEND || (optFlags & MIR_IGNORE_SUSPEND_CHECK)) { opUnconditionalBranch(cUnit, target); return; } if (cUnit->genDebugger) { genSuspendTest(cUnit, optFlags); opUnconditionalBranch(cUnit, target); } else { #if defined(TARGET_ARM) // In non-debug case, only check periodically newLIR2(cUnit, kThumbSubRI8, rSUSPEND, 1); opCondBranch(cUnit, kCondNe, target); #elif defined(TARGET_X86) newLIR2(cUnit, kX86Cmp32TI8, Thread::SuspendCountOffset().Int32Value(), 0); opCondBranch(cUnit, kCondEq, target); #else opRegImm(cUnit, kOpSub, rSUSPEND, 1); opCmpImmBranch(cUnit, kCondNe, rSUSPEND, 0, target); #endif LIR* launchPad = rawLIR(cUnit, cUnit->currentDalvikOffset, kPseudoSuspendTarget, (intptr_t)target, cUnit->currentDalvikOffset); oatFlushAllRegs(cUnit); opUnconditionalBranch(cUnit, launchPad); oatInsertGrowableList(cUnit, &cUnit->suspendLaunchpads, (intptr_t)launchPad); } } } // namespace art