diff options
| author | 2012-11-14 16:31:52 -0800 | |
|---|---|---|
| committer | 2012-11-15 09:47:38 -0800 | |
| commit | efc6369224b036a1fb77849f7ae65b3492c832c0 (patch) | |
| tree | e61d88a5b3b787d782b2fa02b90947ba9a115458 /src/compiler/codegen/gen_common.cc | |
| parent | a3c6849b160cd5d2b2ca6e6fa9d9df834def85ee (diff) | |
Quick compiler source reorganizatio - part 1
A step towards cleanup of the quick compiler source. In this
CL we rename all files to Art standards, combine some of the
old target-specific files that may have made sense in the
JIT, but no longer do. Also removed some codegen/<target>/
subdirectories, combined and deleted some existing files.
Still quite a bit of work to do in cleaning up header files,
getting some better consistency in what codegen functions
go where. That will happen in later CLs.
No logic changes in this CL - just renaming and moving stuff around
Change-Id: Ic172cd3b76d4c670f8e4d5fdd4a3e967db3f4c1e
Diffstat (limited to 'src/compiler/codegen/gen_common.cc')
| -rw-r--r-- | src/compiler/codegen/gen_common.cc | 2321 |
1 files changed, 2321 insertions, 0 deletions
diff --git a/src/compiler/codegen/gen_common.cc b/src/compiler/codegen/gen_common.cc new file mode 100644 index 0000000000..bc61c54f1f --- /dev/null +++ b/src/compiler/codegen/gen_common.cc @@ -0,0 +1,2321 @@ +/* + * 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); +bool smallLiteralDivide(CompilationUnit* cUnit, Instruction::Code dalvikOpcode, + RegLocation rlSrc, RegLocation rlDest, int lit); + +void markSafepointPC(CompilationUnit* cUnit, LIR* inst) +{ + inst->defMask = ENCODE_ALL; + LIR* safepointPC = newLIR0(cUnit, kPseudoSafepointPC); + DCHECK_EQ(safepointPC->defMask, ENCODE_ALL); +} + +/* + * To save scheduling time, helper calls are broken into two parts: generation of + * the helper target address, and the actuall call to the helper. Because x86 + * has a memory call operation, part 1 is a NOP for x86. For other targets, + * load arguments between the two parts. + */ +int callHelperSetup(CompilationUnit* cUnit, int helperOffset) +{ + return (cUnit->instructionSet == kX86) ? 0 : loadHelper(cUnit, helperOffset); +} + +/* NOTE: if rTgt is a temp, it will be freed following use */ +LIR* callHelper(CompilationUnit* cUnit, int rTgt, int helperOffset, bool safepointPC) +{ + LIR* callInst; + if (cUnit->instructionSet == kX86) { + callInst = opThreadMem(cUnit, kOpBlx, helperOffset); + } else { + callInst = opReg(cUnit, kOpBlx, rTgt); + oatFreeTemp(cUnit, rTgt); + } + if (safepointPC) { + markSafepointPC(cUnit, callInst); + } + return callInst; +} + +void callRuntimeHelperImm(CompilationUnit* cUnit, int helperOffset, int arg0, bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + loadConstant(cUnit, targetReg(kArg0), arg0); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperReg(CompilationUnit* cUnit, int helperOffset, int arg0, bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + opRegCopy(cUnit, targetReg(kArg0), arg0); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperRegLocation(CompilationUnit* cUnit, int helperOffset, RegLocation arg0, + bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + if (arg0.wide == 0) { + loadValueDirectFixed(cUnit, arg0, targetReg(kArg0)); + } else { + loadValueDirectWideFixed(cUnit, arg0, targetReg(kArg0), targetReg(kArg1)); + } + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperImmImm(CompilationUnit* cUnit, int helperOffset, int arg0, int arg1, + bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + loadConstant(cUnit, targetReg(kArg0), arg0); + loadConstant(cUnit, targetReg(kArg1), arg1); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperImmRegLocation(CompilationUnit* cUnit, int helperOffset, int arg0, + RegLocation arg1, bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + if (arg1.wide == 0) { + loadValueDirectFixed(cUnit, arg1, targetReg(kArg1)); + } else { + loadValueDirectWideFixed(cUnit, arg1, targetReg(kArg1), targetReg(kArg2)); + } + loadConstant(cUnit, targetReg(kArg0), arg0); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperRegLocationImm(CompilationUnit* cUnit, int helperOffset, RegLocation arg0, + int arg1, bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + loadValueDirectFixed(cUnit, arg0, targetReg(kArg0)); + loadConstant(cUnit, targetReg(kArg1), arg1); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperImmReg(CompilationUnit* cUnit, int helperOffset, int arg0, int arg1, + bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + opRegCopy(cUnit, targetReg(kArg1), arg1); + loadConstant(cUnit, targetReg(kArg0), arg0); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperRegImm(CompilationUnit* cUnit, int helperOffset, int arg0, int arg1, + bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + opRegCopy(cUnit, targetReg(kArg0), arg0); + loadConstant(cUnit, targetReg(kArg1), arg1); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperImmMethod(CompilationUnit* cUnit, int helperOffset, int arg0, bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + loadCurrMethodDirect(cUnit, targetReg(kArg1)); + loadConstant(cUnit, targetReg(kArg0), arg0); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperRegLocationRegLocation(CompilationUnit* cUnit, int helperOffset, + RegLocation arg0, RegLocation arg1, bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + if (arg0.wide == 0) { + loadValueDirectFixed(cUnit, arg0, arg0.fp ? targetReg(kFArg0) : targetReg(kArg0)); + if (arg1.wide == 0) { + if (cUnit->instructionSet == kMips) { + loadValueDirectFixed(cUnit, arg1, arg1.fp ? targetReg(kFArg2) : targetReg(kArg1)); + } else { + loadValueDirectFixed(cUnit, arg1, targetReg(kArg1)); + } + } else { + if (cUnit->instructionSet == kMips) { + loadValueDirectWideFixed(cUnit, arg1, arg1.fp ? targetReg(kFArg2) : targetReg(kArg1), arg1.fp ? targetReg(kFArg3) : targetReg(kArg2)); + } else { + loadValueDirectWideFixed(cUnit, arg1, targetReg(kArg1), targetReg(kArg2)); + } + } + } else { + loadValueDirectWideFixed(cUnit, arg0, arg0.fp ? targetReg(kFArg0) : targetReg(kArg0), arg0.fp ? targetReg(kFArg1) : targetReg(kArg1)); + if (arg1.wide == 0) { + loadValueDirectFixed(cUnit, arg1, arg1.fp ? targetReg(kFArg2) : targetReg(kArg2)); + } else { + loadValueDirectWideFixed(cUnit, arg1, arg1.fp ? targetReg(kFArg2) : targetReg(kArg2), arg1.fp ? targetReg(kFArg3) : targetReg(kArg3)); + } + } + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperRegReg(CompilationUnit* cUnit, int helperOffset, int arg0, int arg1, + bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + DCHECK_NE((int)targetReg(kArg0), arg1); // check copy into arg0 won't clobber arg1 + opRegCopy(cUnit, targetReg(kArg0), arg0); + opRegCopy(cUnit, targetReg(kArg1), arg1); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperRegRegImm(CompilationUnit* cUnit, int helperOffset, int arg0, int arg1, + int arg2, bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + DCHECK_NE((int)targetReg(kArg0), arg1); // check copy into arg0 won't clobber arg1 + opRegCopy(cUnit, targetReg(kArg0), arg0); + opRegCopy(cUnit, targetReg(kArg1), arg1); + loadConstant(cUnit, targetReg(kArg2), arg2); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperImmMethodRegLocation(CompilationUnit* cUnit, int helperOffset, int arg0, + RegLocation arg2, bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + loadValueDirectFixed(cUnit, arg2, targetReg(kArg2)); + loadCurrMethodDirect(cUnit, targetReg(kArg1)); + loadConstant(cUnit, targetReg(kArg0), arg0); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperImmMethodImm(CompilationUnit* cUnit, int helperOffset, int arg0, int arg2, + bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + loadCurrMethodDirect(cUnit, targetReg(kArg1)); + loadConstant(cUnit, targetReg(kArg2), arg2); + loadConstant(cUnit, targetReg(kArg0), arg0); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +void callRuntimeHelperImmRegLocationRegLocation(CompilationUnit* cUnit, int helperOffset, + int arg0, RegLocation arg1, RegLocation arg2, + bool safepointPC) { + int rTgt = callHelperSetup(cUnit, helperOffset); + loadValueDirectFixed(cUnit, arg1, targetReg(kArg1)); + if (arg2.wide == 0) { + loadValueDirectFixed(cUnit, arg2, targetReg(kArg2)); + } else { + loadValueDirectWideFixed(cUnit, arg2, targetReg(kArg2), targetReg(kArg3)); + } + loadConstant(cUnit, targetReg(kArg0), arg0); + oatClobberCalleeSave(cUnit); + callHelper(cUnit, rTgt, helperOffset, safepointPC); +} + +/* + * 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 +LIR* genCheck(CompilationUnit* cUnit, ConditionCode cCode, + ThrowKind kind) +{ + DCHECK_NE(cUnit->instructionSet, kMips); + 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; +} + +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); + LIR* branch = opCmpBranch(cUnit, cCode, reg1, reg2, tgt); + // 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; + } + opCmpBranch(cUnit, cond, rlSrc1.lowReg, rlSrc2.lowReg, taken); + 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 (cUnit->instructionSet == kThumb2) { + opRegImm(cUnit, kOpCmp, rlSrc.lowReg, 0); + opCondBranch(cUnit, cond, taken); + } else { + opCmpImmBranch(cUnit, cond, rlSrc.lowReg, 0, taken); + } + 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, true); + 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, true); + oatFreeTemp(cUnit, targetReg(kArg2)); + oatFreeTemp(cUnit, targetReg(kArg1)); + /* + * NOTE: the implicit target for Instruction::FILLED_NEW_ARRAY is the + * return region. Because AllocFromCode placed the new array + * in kRet0, 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, targetReg(kRet0)); + + // 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, targetReg(kSp), 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); + int rVal = INVALID_REG; + switch(cUnit->instructionSet) { + case kThumb2: + rVal = targetReg(kLr); + break; + case kX86: + oatFreeTemp(cUnit, targetReg(kRet0)); + rVal = oatAllocTemp(cUnit); + break; + case kMips: + rVal = oatAllocTemp(cUnit); + break; + default: LOG(FATAL) << "Unexpected instruction set: " << cUnit->instructionSet; + } + // Set up source pointer + RegLocation rlFirst = info->args[0]; + opRegRegImm(cUnit, kOpAdd, rSrc, targetReg(kSp), + oatSRegOffset(cUnit, rlFirst.sRegLow)); + // Set up the target pointer + opRegRegImm(cUnit, kOpAdd, rDst, targetReg(kRet0), + 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); + oatFreeTemp(cUnit, rVal); + opDecAndBranch(cUnit, kCondGe, rIdx, target); + if (cUnit->instructionSet == kX86) { + // Restore the target pointer + opRegRegImm(cUnit, kOpAdd, targetReg(kRet0), rDst, -Array::DataOffset(component_size).Int32Value()); + } + } else if (!info->isRange) { + // TUNING: interleave + for (int i = 0; i < elems; i++) { + RegLocation rlArg = loadValue(cUnit, info->args[i], kCoreReg); + storeBaseDisp(cUnit, targetReg(kRet0), + 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, + AbstractMethod::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 = targetReg(kArg1); + oatLockTemp(cUnit, rMethod); + loadCurrMethodDirect(cUnit, rMethod); + rBase = targetReg(kArg0); + oatLockTemp(cUnit, rBase); + loadWordDisp(cUnit, rMethod, + AbstractMethod::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, targetReg(kArg0), ssbIndex); + callRuntimeHelperImm(cUnit, ENTRYPOINT_OFFSET(pInitializeStaticStorage), ssbIndex, true); + if (cUnit->instructionSet == kMips) { + // For Arm, kRet0 = kArg0 = rBase, for Mips, we need to copy + opRegCopy(cUnit, rBase, targetReg(kRet0)); + } + 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, true); + } +} + +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, + AbstractMethod::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 = targetReg(kArg1); + oatLockTemp(cUnit, rMethod); + loadCurrMethodDirect(cUnit, rMethod); + rBase = targetReg(kArg0); + oatLockTemp(cUnit, rBase); + loadWordDisp(cUnit, rMethod, + AbstractMethod::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, true); + if (cUnit->instructionSet == kMips) { + // For Arm, kRet0 = kArg0 = rBase, for Mips, we need to copy + opRegCopy(cUnit, rBase, targetReg(kRet0)); + } + 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, true); + 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) +{ + DCHECK_NE(cUnit->instructionSet, kX86) << "unimplemented genShowTarget"; + LIR* branchOver = opCmpImmBranch(cUnit, kCondNe, targetReg(kInvokeTgt), 0, NULL); + loadWordDisp(cUnit, targetReg(kSelf), ENTRYPOINT_OFFSET(pDebugMe), targetReg(kInvokeTgt)); + LIR* target = newLIR0(cUnit, kPseudoTargetLabel); + branchOver->target = (LIR*)target; +} + +void handleSuspendLaunchpads(CompilationUnit *cUnit) +{ + LIR** suspendLabel = (LIR **)cUnit->suspendLaunchpads.elemList; + int numElems = cUnit->suspendLaunchpads.numUsed; + int helperOffset = ENTRYPOINT_OFFSET(pTestSuspendFromCode); + 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); + int rTgt = callHelperSetup(cUnit, helperOffset); + callHelper(cUnit, rTgt, helperOffset, true /* markSafepointPC */); + 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); + // NOTE: genInvoke handles markSafepointPC + 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]; + bool targetX86 = (cUnit->instructionSet == kX86); + switch (lab->operands[0]) { + case kThrowNullPointer: + funcOffset = ENTRYPOINT_OFFSET(pThrowNullPointerFromCode); + break; + case kThrowArrayBounds: + // Move v1 (array index) to kArg0 and v2 (array length) to kArg1 + if (v2 != targetReg(kArg0)) { + opRegCopy(cUnit, targetReg(kArg0), v1); + if (targetX86) { + // x86 leaves the array pointer in v2, so load the array length that the handler expects + opRegMem(cUnit, kOpMov, targetReg(kArg1), v2, Array::LengthOffset().Int32Value()); + } else { + opRegCopy(cUnit, targetReg(kArg1), v2); + } + } else { + if (v1 == targetReg(kArg1)) { + // Swap v1 and v2, using kArg2 as a temp + opRegCopy(cUnit, targetReg(kArg2), v1); + if (targetX86) { + // x86 leaves the array pointer in v2; load the array length that the handler expects + opRegMem(cUnit, kOpMov, targetReg(kArg1), v2, Array::LengthOffset().Int32Value()); + } else { + opRegCopy(cUnit, targetReg(kArg1), v2); + } + opRegCopy(cUnit, targetReg(kArg0), targetReg(kArg2)); + } else { + if (targetX86) { + // x86 leaves the array pointer in v2; load the array length that the handler expects + opRegMem(cUnit, kOpMov, targetReg(kArg1), v2, Array::LengthOffset().Int32Value()); + } else { + opRegCopy(cUnit, targetReg(kArg1), v2); + } + opRegCopy(cUnit, targetReg(kArg0), v1); + } + } + funcOffset = ENTRYPOINT_OFFSET(pThrowArrayBoundsFromCode); + break; + case kThrowDivZero: + funcOffset = ENTRYPOINT_OFFSET(pThrowDivZeroFromCode); + break; + case kThrowNoSuchMethod: + opRegCopy(cUnit, targetReg(kArg0), v1); + funcOffset = + ENTRYPOINT_OFFSET(pThrowNoSuchMethodFromCode); + break; + case kThrowStackOverflow: + funcOffset = ENTRYPOINT_OFFSET(pThrowStackOverflowFromCode); + // Restore stack alignment + if (targetX86) { + opRegImm(cUnit, kOpAdd, targetReg(kSp), cUnit->frameSize); + } else { + opRegImm(cUnit, kOpAdd, targetReg(kSp), (cUnit->numCoreSpills + cUnit->numFPSpills) * 4); + } + break; + default: + LOG(FATAL) << "Unexpected throw kind: " << lab->operands[0]; + } + oatClobberCalleeSave(cUnit); + int rTgt = callHelperSetup(cUnit, funcOffset); + callHelper(cUnit, rTgt, funcOffset, true /* markSafepointPC */); + } +} + +/* Needed by the Assembler */ +void oatSetupResourceMasks(CompilationUnit* cUnit, LIR* lir) +{ + setupResourceMasks(cUnit, 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 (cUnit->instructionSet == kX86) { + 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); + } + 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, true); + 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, true); + } +} + +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 kRet0. + callRuntimeHelperImmReg(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode), + type_idx, rlMethod.lowReg, true); + 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 = + AbstractMethod::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 kArg0 + callRuntimeHelperImmReg(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeFromCode), type_idx, + rlMethod.lowReg, true); + 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, targetReg(kArg2)); + loadWordDisp(cUnit, targetReg(kArg2), + AbstractMethod::DexCacheStringsOffset().Int32Value(), targetReg(kArg0)); + // Might call out to helper, which will return resolved string in kRet0 + int rTgt = callHelperSetup(cUnit, ENTRYPOINT_OFFSET(pResolveStringFromCode)); + loadWordDisp(cUnit, targetReg(kArg0), offset_of_string, targetReg(kRet0)); + loadConstant(cUnit, targetReg(kArg1), string_idx); + if (cUnit->instructionSet == kThumb2) { + opRegImm(cUnit, kOpCmp, targetReg(kRet0), 0); // Is resolved? + genBarrier(cUnit); + // For testing, always force through helper + if (!EXERCISE_SLOWEST_STRING_PATH) { + opIT(cUnit, kArmCondEq, "T"); + } + opRegCopy(cUnit, targetReg(kArg0), targetReg(kArg2)); // .eq + LIR* callInst = opReg(cUnit, kOpBlx, rTgt); // .eq, helper(Method*, string_idx) + markSafepointPC(cUnit, callInst); + oatFreeTemp(cUnit, rTgt); + } else if (cUnit->instructionSet == kMips) { + LIR* branch = opCmpImmBranch(cUnit, kCondNe, targetReg(kRet0), 0, NULL); + opRegCopy(cUnit, targetReg(kArg0), targetReg(kArg2)); // .eq + LIR* callInst = opReg(cUnit, kOpBlx, rTgt); + markSafepointPC(cUnit, callInst); + oatFreeTemp(cUnit, rTgt); + LIR* target = newLIR0(cUnit, kPseudoTargetLabel); + branch->target = target; + } else { + DCHECK_EQ(cUnit->instructionSet, kX86); + callRuntimeHelperRegReg(cUnit, ENTRYPOINT_OFFSET(pResolveStringFromCode), targetReg(kArg2), targetReg(kArg1), true); + } + 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, + AbstractMethod::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, true); + RegLocation rlResult = oatGetReturn(cUnit, false); + storeValue(cUnit, rlDest, rlResult); +} + +void genMoveException(CompilationUnit* cUnit, RegLocation rlDest) +{ + oatFlushAllRegs(cUnit); /* Everything to home location */ + int funcOffset = ENTRYPOINT_OFFSET(pGetAndClearException); + if (cUnit->instructionSet == kX86) { + // Runtime helper will load argument for x86. + callRuntimeHelperReg(cUnit, funcOffset, targetReg(kArg0), false); + } else { + callRuntimeHelperReg(cUnit, funcOffset, targetReg(kSelf), false); + } + RegLocation rlResult = oatGetReturn(cUnit, false); + storeValue(cUnit, rlDest, rlResult); +} + +void genThrow(CompilationUnit* cUnit, RegLocation rlSrc) +{ + oatFlushAllRegs(cUnit); + callRuntimeHelperRegLocation(cUnit, ENTRYPOINT_OFFSET(pDeliverException), rlSrc, true); +} + +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, targetReg(kArg1)); // kArg1 <= current Method* + int classReg = targetReg(kArg2); // kArg2 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 kArg0 + callRuntimeHelperImm(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode), + type_idx, true); + opRegCopy(cUnit, classReg, targetReg(kRet0)); // Align usage with fast path + loadValueDirectFixed(cUnit, rlSrc, targetReg(kArg0)); // kArg0 <= ref + } else { + // Load dex cache entry into classReg (kArg2) + loadValueDirectFixed(cUnit, rlSrc, targetReg(kArg0)); // kArg0 <= ref + loadWordDisp(cUnit, targetReg(kArg1), + AbstractMethod::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 kRet0 + callRuntimeHelperImm(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeFromCode), type_idx, true); + opRegCopy(cUnit, targetReg(kArg2), targetReg(kRet0)); // Align usage with fast path + loadValueDirectFixed(cUnit, rlSrc, targetReg(kArg0)); /* reload Ref */ + // Rejoin code paths + LIR* hopTarget = newLIR0(cUnit, kPseudoTargetLabel); + hopBranch->target = (LIR*)hopTarget; + } + } + /* kArg0 is ref, kArg2 is class. If ref==null, use directly as bool result */ + RegLocation rlResult = oatGetReturn(cUnit, false); + if (cUnit->instructionSet == kMips) { + loadConstant(cUnit, rlResult.lowReg, 0); // store false result for if branch is taken + } + LIR* branch1 = opCmpImmBranch(cUnit, kCondEq, targetReg(kArg0), 0, NULL); + /* load object->klass_ */ + DCHECK_EQ(Object::ClassOffset().Int32Value(), 0); + loadWordDisp(cUnit, targetReg(kArg0), Object::ClassOffset().Int32Value(), targetReg(kArg1)); + /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class */ + LIR* callInst; + LIR* branchover = NULL; + if (cUnit->instructionSet == kThumb2) { + /* Uses conditional nullification */ + int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); + opRegReg(cUnit, kOpCmp, targetReg(kArg1), targetReg(kArg2)); // Same? + opIT(cUnit, kArmCondEq, "EE"); // if-convert the test + loadConstant(cUnit, targetReg(kArg0), 1); // .eq case - load true + opRegCopy(cUnit, targetReg(kArg0), targetReg(kArg2)); // .ne case - arg0 <= class + callInst = opReg(cUnit, kOpBlx, rTgt); // .ne case: helper(class, ref->class) + oatFreeTemp(cUnit, rTgt); + } else { + /* Uses branchovers */ + loadConstant(cUnit, rlResult.lowReg, 1); // assume true + branchover = opCmpBranch(cUnit, kCondEq, targetReg(kArg1), targetReg(kArg2), NULL); + if (cUnit->instructionSet != kX86) { + int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); + opRegCopy(cUnit, targetReg(kArg0), targetReg(kArg2)); // .ne case - arg0 <= class + callInst = opReg(cUnit, kOpBlx, rTgt); // .ne case: helper(class, ref->class) + oatFreeTemp(cUnit, rTgt); + } else { + opRegCopy(cUnit, targetReg(kArg0), targetReg(kArg2)); + callInst = opThreadMem(cUnit, kOpBlx, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); + } + } + markSafepointPC(cUnit, callInst); + oatClobberCalleeSave(cUnit); + /* branch targets here */ + LIR* target = newLIR0(cUnit, kPseudoTargetLabel); + storeValue(cUnit, rlDest, rlResult); + branch1->target = target; + if (cUnit->instructionSet != kThumb2) { + branchover->target = target; + } +} + +void genCheckCast(CompilationUnit* cUnit, uint32_t type_idx, RegLocation rlSrc) +{ + oatFlushAllRegs(cUnit); + // May generate a call - use explicit registers + oatLockCallTemps(cUnit); + loadCurrMethodDirect(cUnit, targetReg(kArg1)); // kArg1 <= current Method* + int classReg = targetReg(kArg2); // kArg2 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 kRet0 + // InitializeTypeAndVerifyAccess(idx, method) + callRuntimeHelperImmReg(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode), + type_idx, targetReg(kArg1), true); + opRegCopy(cUnit, classReg, targetReg(kRet0)); // Align usage with fast path + } else { + // Load dex cache entry into classReg (kArg2) + loadWordDisp(cUnit, targetReg(kArg1), + AbstractMethod::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 kArg0 + // InitializeTypeFromCode(idx, method) + callRuntimeHelperImmReg(cUnit, ENTRYPOINT_OFFSET(pInitializeTypeFromCode), type_idx, targetReg(kArg1), + true); + opRegCopy(cUnit, classReg, targetReg(kRet0)); // Align usage with fast path + // Rejoin code paths + LIR* hopTarget = newLIR0(cUnit, kPseudoTargetLabel); + hopBranch->target = (LIR*)hopTarget; + } + } + // At this point, classReg (kArg2) has class + loadValueDirectFixed(cUnit, rlSrc, targetReg(kArg0)); // kArg0 <= ref + /* Null is OK - continue */ + LIR* branch1 = opCmpImmBranch(cUnit, kCondEq, targetReg(kArg0), 0, NULL); + /* load object->klass_ */ + DCHECK_EQ(Object::ClassOffset().Int32Value(), 0); + loadWordDisp(cUnit, targetReg(kArg0), Object::ClassOffset().Int32Value(), targetReg(kArg1)); + /* kArg1 now contains object->klass_ */ + LIR* branch2; + if (cUnit->instructionSet == kThumb2) { + int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pCheckCastFromCode)); + opRegReg(cUnit, kOpCmp, targetReg(kArg1), classReg); + branch2 = opCondBranch(cUnit, kCondEq, NULL); /* If eq, trivial yes */ + opRegCopy(cUnit, targetReg(kArg0), targetReg(kArg1)); + opRegCopy(cUnit, targetReg(kArg1), targetReg(kArg2)); + oatClobberCalleeSave(cUnit); + LIR* callInst = opReg(cUnit, kOpBlx, rTgt); + markSafepointPC(cUnit, callInst); + oatFreeTemp(cUnit, rTgt); + } else { + branch2 = opCmpBranch(cUnit, kCondEq, targetReg(kArg1), classReg, NULL); + callRuntimeHelperRegReg(cUnit, ENTRYPOINT_OFFSET(pCheckCastFromCode), targetReg(kArg1), targetReg(kArg2), true); + } + /* 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 = targetReg(kArg0); // Register holding value + int rArrayClass = targetReg(kArg1); // Register holding array's Class + int rArray = targetReg(kArg2); // Register holding array + int rIndex = targetReg(kArg3); // 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, true); + // 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 (cUnit->instructionSet == kX86) { + // make an extra temp available for card mark below + oatFreeTemp(cUnit, targetReg(kArg1)); + 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 = targetReg(kArg1); + 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); + } + 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 (cUnit->instructionSet == kX86) { + 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); + opLea(cUnit, 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); + } + } +} + +/* + * 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); + int regPtr = INVALID_REG; + if (cUnit->instructionSet != kX86) { + if (oatIsTemp(cUnit, rlArray.lowReg)) { + oatClobber(cUnit, rlArray.lowReg); + regPtr = rlArray.lowReg; + } else { + regPtr = oatAllocTemp(cUnit); + opRegCopy(cUnit, regPtr, rlArray.lowReg); + } + } + + /* null object? */ + genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, optFlags); + + if (cUnit->instructionSet == kX86) { + 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); + } + } +} + +void genLong3Addr(CompilationUnit* cUnit, OpKind firstOp, + OpKind secondOp, RegLocation rlDest, + RegLocation rlSrc1, RegLocation rlSrc2) +{ + RegLocation rlResult; + if (cUnit->instructionSet == kThumb2) { + /* + * 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, targetReg(kLr)); // Add lr to the temp pool + oatFreeTemp(cUnit, targetReg(kLr)); // and make it available + } + 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) || (rlResult.lowReg == rlSrc2.highReg)){ + int tReg = oatAllocTemp(cUnit); + opRegRegReg(cUnit, firstOp, tReg, rlSrc1.lowReg, rlSrc2.lowReg); + opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg, rlSrc2.highReg); + opRegCopy(cUnit, rlResult.lowReg, tReg); + 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 (cUnit->instructionSet == kThumb2) { + oatClobber(cUnit, targetReg(kLr)); + oatUnmarkTemp(cUnit, targetReg(kLr)); // Remove lr from the temp pool + } +} + + +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, false); + 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 isDivRem = false; + bool checkZero = false; + bool unary = false; + RegLocation rlResult; + bool shiftOp = false; + 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; + isDivRem = true; + break; + /* NOTE: returns in kArg1 */ + case Instruction::REM_INT: + case Instruction::REM_INT_2ADDR: + checkZero = true; + op = kOpRem; + isDivRem = true; + 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 (!isDivRem) { + if (unary) { + rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg); + rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); + opRegReg(cUnit, op, rlResult.lowReg, rlSrc1.lowReg); + } else { + if (shiftOp) { + int tReg = INVALID_REG; + if (cUnit->instructionSet == kX86) { + // X86 doesn't require masking and must use ECX + tReg = targetReg(kCount); // rCX + loadValueDirectFixed(cUnit, rlSrc2, tReg); + } else { + rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg); + tReg = oatAllocTemp(cUnit); + opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31); + } + 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 { + if (cUnit->instructionSet == kMips) { + rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg); + rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg); + if (checkZero) { + genImmedCheck(cUnit, kCondEq, rlSrc2.lowReg, 0, kThrowDivZero); + } + rlResult = genDivRem(cUnit, rlDest, rlSrc1.lowReg, rlSrc2.lowReg, op == kOpDiv); + } else { + int funcOffset = ENTRYPOINT_OFFSET(pIdivmod); + oatFlushAllRegs(cUnit); /* Send everything to home location */ + loadValueDirectFixed(cUnit, rlSrc2, targetReg(kArg1)); + int rTgt = callHelperSetup(cUnit, funcOffset); + loadValueDirectFixed(cUnit, rlSrc1, targetReg(kArg0)); + if (checkZero) { + genImmedCheck(cUnit, kCondEq, targetReg(kArg1), 0, kThrowDivZero); + } + // NOTE: callout here is not a safepoint + callHelper(cUnit, rTgt, funcOffset, false /* not a safepoint */ ); + if (op == kOpDiv) + 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 ((lit < 2) || ((cUnit->instructionSet != kThumb2) && !isPowerOfTwo(lit))) { + return false; + } + // No divide instruction for Arm, so check for more special cases + if ((cUnit->instructionSet == kThumb2) && !isPowerOfTwo(lit)) { + return smallLiteralDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit); + } + 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; +} + +// 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; + + 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; + } + if ((opcode == Instruction::DIV_INT_LIT8) || + (opcode == Instruction::DIV_INT_LIT16)) { + isDiv = true; + } else { + isDiv = false; + } + if (cUnit->instructionSet == kMips) { + rlSrc = loadValue(cUnit, rlSrc, kCoreReg); + rlResult = genDivRemLit(cUnit, rlDest, rlSrc.lowReg, lit, isDiv); + } else { + oatFlushAllRegs(cUnit); /* Everything to home location */ + loadValueDirectFixed(cUnit, rlSrc, targetReg(kArg0)); + oatClobber(cUnit, targetReg(kArg0)); + int funcOffset = ENTRYPOINT_OFFSET(pIdivmod); + callRuntimeHelperRegImm(cUnit, funcOffset, targetReg(kArg0), lit, false); + 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 = targetReg(kRet0); + + 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 (cUnit->instructionSet != kThumb2) { + return genAddLong(cUnit, rlDest, rlSrc1, rlSrc2); + } + firstOp = kOpAdd; + secondOp = kOpAdc; + break; + case Instruction::SUB_LONG: + case Instruction::SUB_LONG_2ADDR: + if (cUnit->instructionSet != kThumb2) { + return genSubLong(cUnit, rlDest, rlSrc1, rlSrc2); + } + firstOp = kOpSub; + secondOp = kOpSbc; + break; + case Instruction::MUL_LONG: + case Instruction::MUL_LONG_2ADDR: + callOut = true; + retReg = targetReg(kRet0); + funcOffset = ENTRYPOINT_OFFSET(pLmul); + break; + case Instruction::DIV_LONG: + case Instruction::DIV_LONG_2ADDR: + callOut = true; + checkZero = true; + retReg = targetReg(kRet0); + funcOffset = ENTRYPOINT_OFFSET(pLdiv); + break; + case Instruction::REM_LONG: + case Instruction::REM_LONG_2ADDR: + callOut = true; + checkZero = true; + funcOffset = ENTRYPOINT_OFFSET(pLdivmod); + /* NOTE - for Arm, result is in kArg2/kArg3 instead of kRet0/kRet1 */ + retReg = (cUnit->instructionSet == kThumb2) ? targetReg(kArg2) : targetReg(kRet0); + break; + case Instruction::AND_LONG_2ADDR: + case Instruction::AND_LONG: + if (cUnit->instructionSet == kX86) { + return genAndLong(cUnit, rlDest, rlSrc1, rlSrc2); + } + firstOp = kOpAnd; + secondOp = kOpAnd; + break; + case Instruction::OR_LONG: + case Instruction::OR_LONG_2ADDR: + if (cUnit->instructionSet == kX86) { + return genOrLong(cUnit, rlDest, rlSrc1, rlSrc2); + } + firstOp = kOpOr; + secondOp = kOpOr; + break; + case Instruction::XOR_LONG: + case Instruction::XOR_LONG_2ADDR: + if (cUnit->instructionSet == kX86) { + return genXorLong(cUnit, rlDest, rlSrc1, rlSrc2); + } + firstOp = kOpXor; + secondOp = kOpXor; + break; + 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, targetReg(kArg2), targetReg(kArg3)); + int rTgt = callHelperSetup(cUnit, funcOffset); + genDivZeroCheck(cUnit, targetReg(kArg2), targetReg(kArg3)); + loadValueDirectWideFixed(cUnit, rlSrc1, targetReg(kArg0), targetReg(kArg1)); + // NOTE: callout here is not a safepoint + callHelper(cUnit, rTgt, funcOffset, false /* not safepoint */); + } else { + callRuntimeHelperRegLocationRegLocation(cUnit, funcOffset, + rlSrc1, rlSrc2, false); + } + // Adjust return regs in to handle case of rem returning kArg2/kArg3 + if (retReg == targetReg(kRet0)) + 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, rlSrc.fp ? targetReg(kFArg0) : targetReg(kArg0), + rlSrc.fp ? targetReg(kFArg1) : targetReg(kArg1)); + } else { + loadValueDirectFixed(cUnit, rlSrc, rlSrc.fp ? targetReg(kFArg0) : targetReg(kArg0)); + } + callRuntimeHelperRegLocation(cUnit, funcOffset, rlSrc, false); + 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, false); + 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, false); + 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; +} + +/* 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); + LIR* branch = opTestSuspend(cUnit, NULL); + 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; + } + opTestSuspend(cUnit, target); + 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 |