summaryrefslogtreecommitdiff
path: root/src/compiler/codegen/GenInvoke.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/codegen/GenInvoke.cc')
-rw-r--r--src/compiler/codegen/GenInvoke.cc337
1 files changed, 337 insertions, 0 deletions
diff --git a/src/compiler/codegen/GenInvoke.cc b/src/compiler/codegen/GenInvoke.cc
index ebc8bc28a4..ba027f0d07 100644
--- a/src/compiler/codegen/GenInvoke.cc
+++ b/src/compiler/codegen/GenInvoke.cc
@@ -25,6 +25,8 @@ namespace art {
typedef int (*NextCallInsn)(CompilationUnit*, MIR*, int, uint32_t dexIdx,
uint32_t methodIdx, uintptr_t directCode,
uintptr_t directMethod, InvokeType type);
+LIR* opCondBranch(CompilationUnit* cUnit, ConditionCode cc, LIR* target);
+
/*
* If there are any ins passed in registers that have not been promoted
* to a callee-save register, flush them to the frame. Perform intial
@@ -596,4 +598,339 @@ int genDalvikArgsRange(CompilationUnit* cUnit, MIR* mir,
return callState;
}
+RegLocation inlineTarget(CompilationUnit* cUnit, BasicBlock* bb, MIR* mir)
+{
+ RegLocation res;
+ mir = oatFindMoveResult(cUnit, bb, mir, false);
+ if (mir == NULL) {
+ res = oatGetReturn(cUnit, false);
+ } else {
+ res = oatGetDest(cUnit, mir, 0);
+ mir->dalvikInsn.opcode = Instruction::NOP;
+ }
+ return res;
+}
+
+RegLocation inlineTargetWide(CompilationUnit* cUnit, BasicBlock* bb, MIR* mir)
+{
+ RegLocation res;
+ mir = oatFindMoveResult(cUnit, bb, mir, true);
+ if (mir == NULL) {
+ res = oatGetReturnWide(cUnit, false);
+ } else {
+ res = oatGetDestWide(cUnit, mir, 0, 1);
+ mir->dalvikInsn.opcode = Instruction::NOP;
+ }
+ return res;
+}
+
+bool genInlinedCharAt(CompilationUnit* cUnit, BasicBlock* bb, MIR* mir,
+ InvokeType type, bool isRange)
+{
+#if defined(TARGET_ARM)
+ // Location of reference to data array
+ int valueOffset = String::ValueOffset().Int32Value();
+ // Location of count
+ int countOffset = String::CountOffset().Int32Value();
+ // Starting offset within data array
+ int offsetOffset = String::OffsetOffset().Int32Value();
+ // Start of char data with array_
+ int dataOffset = Array::DataOffset(sizeof(uint16_t)).Int32Value();
+
+ RegLocation rlObj = oatGetSrc(cUnit, mir, 0);
+ RegLocation rlIdx = oatGetSrc(cUnit, mir, 1);
+ rlObj = loadValue(cUnit, rlObj, kCoreReg);
+ rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
+ int regMax;
+ int regOff = oatAllocTemp(cUnit);
+ int regPtr = oatAllocTemp(cUnit);
+ genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);
+ bool rangeCheck = (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK));
+ if (rangeCheck) {
+ regMax = oatAllocTemp(cUnit);
+ loadWordDisp(cUnit, rlObj.lowReg, countOffset, regMax);
+ }
+ loadWordDisp(cUnit, rlObj.lowReg, offsetOffset, regOff);
+ loadWordDisp(cUnit, rlObj.lowReg, valueOffset, regPtr);
+ LIR* launchPad = NULL;
+ if (rangeCheck) {
+ // Set up a launch pad to allow retry in case of bounds violation */
+ launchPad = rawLIR(cUnit, 0, kPseudoIntrinsicRetry, (int)mir, type);
+ oatInsertGrowableList(cUnit, &cUnit->intrinsicLaunchpads,
+ (intptr_t)launchPad);
+ opRegReg(cUnit, kOpCmp, rlIdx.lowReg, regMax);
+ oatFreeTemp(cUnit, regMax);
+ opCondBranch(cUnit, kCondCs, launchPad);
+ }
+ opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
+ opRegReg(cUnit, kOpAdd, regOff, rlIdx.lowReg);
+ RegLocation rlDest = inlineTarget(cUnit, bb, mir);
+ RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadBaseIndexed(cUnit, regPtr, regOff, rlResult.lowReg, 1, kUnsignedHalf);
+ oatFreeTemp(cUnit, regOff);
+ oatFreeTemp(cUnit, regPtr);
+ storeValue(cUnit, rlDest, rlResult);
+ if (rangeCheck) {
+ launchPad->operands[2] = NULL; // no resumption
+ launchPad->operands[3] = (uintptr_t)bb;
+ }
+ // Record that we've already inlined & null checked
+ mir->optimizationFlags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK);
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool genInlinedMinMaxInt(CompilationUnit *cUnit, BasicBlock* bb, MIR *mir,
+ bool isMin)
+{
+#if defined(TARGET_ARM)
+ RegLocation rlSrc1 = oatGetSrc(cUnit, mir, 0);
+ RegLocation rlSrc2 = oatGetSrc(cUnit, mir, 1);
+ rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+ rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+ RegLocation rlDest = inlineTarget(cUnit, bb, mir);
+ RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg);
+ opIT(cUnit, (isMin) ? kArmCondGt : kArmCondLt, "E");
+ opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc2.lowReg);
+ opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc1.lowReg);
+ genBarrier(cUnit);
+ storeValue(cUnit, rlDest, rlResult);
+ return true;
+#else
+ return false;
+#endif
+}
+
+// Generates an inlined String.isEmpty or String.length.
+bool genInlinedStringIsEmptyOrLength(CompilationUnit* cUnit,
+ BasicBlock* bb, MIR* mir,
+ bool isEmpty)
+{
+#if defined(TARGET_ARM)
+ // dst = src.length();
+ RegLocation rlObj = oatGetSrc(cUnit, mir, 0);
+ rlObj = loadValue(cUnit, rlObj, kCoreReg);
+ RegLocation rlDest = inlineTarget(cUnit, bb, mir);
+ RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
+ genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);
+ loadWordDisp(cUnit, rlObj.lowReg, String::CountOffset().Int32Value(),
+ rlResult.lowReg);
+ if (isEmpty) {
+ // dst = (dst == 0);
+ int tReg = oatAllocTemp(cUnit);
+ opRegReg(cUnit, kOpNeg, tReg, rlResult.lowReg);
+ opRegRegReg(cUnit, kOpAdc, rlResult.lowReg, rlResult.lowReg, tReg);
+ }
+ storeValue(cUnit, rlDest, rlResult);
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool genInlinedAbsInt(CompilationUnit *cUnit, BasicBlock* bb, MIR *mir)
+{
+#if defined(TARGET_ARM)
+ RegLocation rlSrc = oatGetSrc(cUnit, mir, 0);
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ RegLocation rlDest = inlineTarget(cUnit, bb, mir);
+ RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
+ int signReg = oatAllocTemp(cUnit);
+ // abs(x) = y<=x>>31, (x+y)^y.
+ opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.lowReg, 31);
+ opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+ opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+ storeValue(cUnit, rlDest, rlResult);
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool genInlinedAbsLong(CompilationUnit *cUnit, BasicBlock* bb, MIR *mir)
+{
+#if defined(TARGET_ARM)
+ RegLocation rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
+ rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+ RegLocation rlDest = inlineTargetWide(cUnit, bb, mir);
+ RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
+ int signReg = oatAllocTemp(cUnit);
+ // abs(x) = y<=x>>31, (x+y)^y.
+ opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.highReg, 31);
+ opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+ opRegRegReg(cUnit, kOpAdc, rlResult.highReg, rlSrc.highReg, signReg);
+ opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+ opRegReg(cUnit, kOpXor, rlResult.highReg, signReg);
+ storeValueWide(cUnit, rlDest, rlResult);
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool genInlinedFloatCvt(CompilationUnit *cUnit, BasicBlock* bb, MIR *mir)
+{
+#if defined(TARGET_ARM)
+ RegLocation rlSrc = oatGetSrc(cUnit, mir, 0);
+ RegLocation rlDest = inlineTarget(cUnit, bb, mir);
+ storeValue(cUnit, rlDest, rlSrc);
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool genInlinedDoubleCvt(CompilationUnit *cUnit, BasicBlock* bb, MIR *mir)
+{
+#if defined(TARGET_ARM)
+ RegLocation rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
+ RegLocation rlDest = inlineTargetWide(cUnit, bb, mir);
+ storeValueWide(cUnit, rlDest, rlSrc);
+ return true;
+#else
+ return false;
+#endif
+}
+
+/*
+ * Fast string.indexOf(I) & (II). Tests for simple case of char <= 0xffff,
+ * otherwise bails to standard library code.
+ */
+bool genInlinedIndexOf(CompilationUnit* cUnit, BasicBlock* bb, MIR* mir,
+ InvokeType type, bool zeroBased)
+{
+#if defined(TARGET_ARM)
+
+ oatClobberCalleeSave(cUnit);
+ oatLockCallTemps(cUnit); // Using fixed registers
+ int regPtr = rARG0;
+ int regChar = rARG1;
+ int regStart = rARG2;
+
+ RegLocation rlObj = oatGetSrc(cUnit, mir, 0);
+ RegLocation rlChar = oatGetSrc(cUnit, mir, 1);
+ RegLocation rlStart = oatGetSrc(cUnit, mir, 2);
+ loadValueDirectFixed(cUnit, rlObj, regPtr);
+ loadValueDirectFixed(cUnit, rlChar, regChar);
+ if (zeroBased) {
+ loadConstant(cUnit, regStart, 0);
+ } else {
+ loadValueDirectFixed(cUnit, rlStart, regStart);
+ }
+ int rTgt = loadHelper(cUnit, OFFSETOF_MEMBER(Thread, pIndexOf));
+ genNullCheck(cUnit, rlObj.sRegLow, regPtr, mir);
+ LIR* launchPad = rawLIR(cUnit, 0, kPseudoIntrinsicRetry, (int)mir, type);
+ oatInsertGrowableList(cUnit, &cUnit->intrinsicLaunchpads,
+ (intptr_t)launchPad);
+ opCmpImmBranch(cUnit, kCondGt, regChar, 0xFFFF, launchPad);
+ opReg(cUnit, kOpBlx, rTgt);
+ LIR* resumeTgt = newLIR0(cUnit, kPseudoTargetLabel);
+ launchPad->operands[2] = (uintptr_t)resumeTgt;
+ launchPad->operands[3] = (uintptr_t)bb;
+ // Record that we've already inlined & null checked
+ mir->optimizationFlags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK);
+ return true;
+#else
+ return false;
+#endif
+}
+
+/* Fast string.compareTo(Ljava/lang/string;)I. */
+bool genInlinedStringCompareTo(CompilationUnit* cUnit, BasicBlock* bb,
+ MIR* mir, InvokeType type)
+{
+#if defined(TARGET_ARM)
+ oatClobberCalleeSave(cUnit);
+ oatLockCallTemps(cUnit); // Using fixed registers
+ int regThis = rARG0;
+ int regCmp = rARG1;
+
+ RegLocation rlThis = oatGetSrc(cUnit, mir, 0);
+ RegLocation rlCmp = oatGetSrc(cUnit, mir, 1);
+ loadValueDirectFixed(cUnit, rlThis, regThis);
+ loadValueDirectFixed(cUnit, rlCmp, regCmp);
+ int rTgt = loadHelper(cUnit, OFFSETOF_MEMBER(Thread, pStringCompareTo));
+ genNullCheck(cUnit, rlThis.sRegLow, regThis, mir);
+ //TUNING: check if rlCmp.sRegLow is already null checked
+ LIR* launchPad = rawLIR(cUnit, 0, kPseudoIntrinsicRetry, (int)mir, type);
+ oatInsertGrowableList(cUnit, &cUnit->intrinsicLaunchpads,
+ (intptr_t)launchPad);
+ opCmpImmBranch(cUnit, kCondEq, regCmp, 0, launchPad);
+ opReg(cUnit, kOpBlx, rTgt);
+ launchPad->operands[2] = NULL; // No return possible
+ launchPad->operands[3] = (uintptr_t)bb;
+ // Record that we've already inlined & null checked
+ mir->optimizationFlags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK);
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool genIntrinsic(CompilationUnit* cUnit, BasicBlock* bb, MIR* mir,
+ InvokeType type, bool isRange)
+{
+ if ((mir->optimizationFlags & MIR_INLINED) || isRange) {
+ return false;
+ }
+ /*
+ * TODO: move these to a target-specific structured constant array
+ * and use a generic match function. The list of intrinsics may be
+ * slightly different depending on target.
+ * TODO: Fold this into a matching function that runs during
+ * basic block building. This should be part of the action for
+ * small method inlining and recognition of the special object init
+ * method. By doing this during basic block construction, we can also
+ * take advantage of/generate new useful dataflow info.
+ */
+ std::string tgtMethod = PrettyMethod(mir->dalvikInsn.vB, *cUnit->dex_file);
+ if (tgtMethod.compare("char java.lang.String.charAt(int)") == 0) {
+ return genInlinedCharAt(cUnit, bb, mir, type, isRange);
+ }
+ if (tgtMethod.compare("int java.lang.Math.min(int, int)") == 0) {
+ return genInlinedMinMaxInt(cUnit, bb, mir, true /* isMin */);
+ }
+ if (tgtMethod.compare("int java.lang.Math.max(int, int)") == 0) {
+ return genInlinedMinMaxInt(cUnit, bb, mir, false /* isMin */);
+ }
+ if (tgtMethod.compare("int java.lang.String.length()") == 0) {
+ return genInlinedStringIsEmptyOrLength(cUnit, bb, mir, false /* isEmpty */);
+ }
+ if (tgtMethod.compare("boolean java.lang.String.isEmpty()") == 0) {
+ return genInlinedStringIsEmptyOrLength(cUnit, bb, mir, true /* isEmpty */);
+ }
+ if (tgtMethod.compare("int java.lang.Math.abs(int)") == 0) {
+ return genInlinedAbsInt(cUnit, bb, mir);
+ }
+ if (tgtMethod.compare("long java.lang.Math.abs(long)") == 0) {
+ return genInlinedAbsLong(cUnit, bb, mir);
+ }
+ if (tgtMethod.compare("int java.lang.Float.floatToRawIntBits(float)") == 0) {
+ return genInlinedFloatCvt(cUnit, bb, mir);
+ }
+ if (tgtMethod.compare("float java.lang.Float.intBitsToFloat(int)") == 0) {
+ return genInlinedFloatCvt(cUnit, bb, mir);
+ }
+ if (tgtMethod.compare("long java.lang.Double.doubleToRawLongBits(double)") == 0) {
+ return genInlinedDoubleCvt(cUnit, bb, mir);
+ }
+ if (tgtMethod.compare("double java.lang.Double.longBitsToDouble(long)") == 0) {
+ return genInlinedDoubleCvt(cUnit, bb, mir);
+ }
+ if (tgtMethod.compare("int java.lang.String.indexOf(int, int)") == 0) {
+ return genInlinedIndexOf(cUnit, bb, mir, type, false /* base 0 */);
+ }
+ if (tgtMethod.compare("int java.lang.String.indexOf(int)") == 0) {
+ return genInlinedIndexOf(cUnit, bb, mir, type, true /* base 0 */);
+ }
+ if (tgtMethod.compare("int java.lang.String.compareTo(java.lang.String)") == 0) {
+ return genInlinedStringCompareTo(cUnit, bb, mir, type);
+ }
+ return false;
+}
+
+
} // namespace art