| /* |
| * Copyright (C) 2013 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 "dex_file_method_inliner.h" |
| |
| #include <algorithm> |
| |
| #include "base/macros.h" |
| #include "base/mutex.h" |
| #include "base/mutex-inl.h" |
| #include "dex/frontend.h" |
| #include "thread.h" |
| #include "thread-inl.h" |
| #include "dex/mir_graph.h" |
| #include "dex/quick/mir_to_lir.h" |
| #include "dex_instruction.h" |
| #include "dex_instruction-inl.h" |
| #include "driver/dex_compilation_unit.h" |
| #include "verifier/method_verifier.h" |
| #include "verifier/method_verifier-inl.h" |
| |
| namespace art { |
| |
| namespace { // anonymous namespace |
| |
| static constexpr bool kIntrinsicIsStatic[] = { |
| true, // kIntrinsicDoubleCvt |
| true, // kIntrinsicFloatCvt |
| true, // kIntrinsicReverseBits |
| true, // kIntrinsicReverseBytes |
| true, // kIntrinsicAbsInt |
| true, // kIntrinsicAbsLong |
| true, // kIntrinsicAbsFloat |
| true, // kIntrinsicAbsDouble |
| true, // kIntrinsicMinMaxInt |
| true, // kIntrinsicMinMaxLong |
| true, // kIntrinsicMinMaxFloat |
| true, // kIntrinsicMinMaxDouble |
| true, // kIntrinsicSqrt |
| true, // kIntrinsicCeil |
| true, // kIntrinsicFloor |
| true, // kIntrinsicRint |
| true, // kIntrinsicRoundFloat |
| true, // kIntrinsicRoundDouble |
| false, // kIntrinsicReferenceGetReferent |
| false, // kIntrinsicCharAt |
| false, // kIntrinsicCompareTo |
| false, // kIntrinsicIsEmptyOrLength |
| false, // kIntrinsicIndexOf |
| true, // kIntrinsicCurrentThread |
| true, // kIntrinsicPeek |
| true, // kIntrinsicPoke |
| false, // kIntrinsicCas |
| false, // kIntrinsicUnsafeGet |
| false, // kIntrinsicUnsafePut |
| true, // kIntrinsicSystemArrayCopyCharArray |
| }; |
| COMPILE_ASSERT(arraysize(kIntrinsicIsStatic) == kInlineOpNop, check_arraysize_kIntrinsicIsStatic); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicDoubleCvt], DoubleCvt_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicFloatCvt], FloatCvt_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicReverseBits], ReverseBits_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicReverseBytes], ReverseBytes_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsInt], AbsInt_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsLong], AbsLong_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsFloat], AbsFloat_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsDouble], AbsDouble_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxInt], MinMaxInt_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxLong], MinMaxLong_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxFloat], MinMaxFloat_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxDouble], MinMaxDouble_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicSqrt], Sqrt_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicCeil], Ceil_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicFloor], Floor_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRint], Rint_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRoundFloat], RoundFloat_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRoundDouble], RoundDouble_must_be_static); |
| COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicReferenceGetReferent], Get_must_not_be_static); |
| COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCharAt], CharAt_must_not_be_static); |
| COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCompareTo], CompareTo_must_not_be_static); |
| COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicIsEmptyOrLength], IsEmptyOrLength_must_not_be_static); |
| COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicIndexOf], IndexOf_must_not_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicCurrentThread], CurrentThread_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicPeek], Peek_must_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicPoke], Poke_must_be_static); |
| COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCas], Cas_must_not_be_static); |
| COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], UnsafeGet_must_not_be_static); |
| COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicUnsafePut], UnsafePut_must_not_be_static); |
| COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray], |
| SystemArrayCopyCharArray_must_be_static); |
| |
| MIR* AllocReplacementMIR(MIRGraph* mir_graph, MIR* invoke, MIR* move_return) { |
| MIR* insn = mir_graph->NewMIR(); |
| insn->offset = invoke->offset; |
| insn->optimization_flags = MIR_CALLEE; |
| return insn; |
| } |
| |
| uint32_t GetInvokeReg(MIR* invoke, uint32_t arg) { |
| DCHECK_LT(arg, invoke->dalvikInsn.vA); |
| DCHECK(!MIR::DecodedInstruction::IsPseudoMirOp(invoke->dalvikInsn.opcode)); |
| if (Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc) { |
| return invoke->dalvikInsn.vC + arg; // Non-range invoke. |
| } else { |
| DCHECK_EQ(Instruction::FormatOf(invoke->dalvikInsn.opcode), Instruction::k35c); |
| return invoke->dalvikInsn.arg[arg]; // Range invoke. |
| } |
| } |
| |
| bool WideArgIsInConsecutiveDalvikRegs(MIR* invoke, uint32_t arg) { |
| DCHECK_LT(arg + 1, invoke->dalvikInsn.vA); |
| DCHECK(!MIR::DecodedInstruction::IsPseudoMirOp(invoke->dalvikInsn.opcode)); |
| return Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc || |
| invoke->dalvikInsn.arg[arg + 1u] == invoke->dalvikInsn.arg[arg] + 1u; |
| } |
| |
| } // anonymous namespace |
| |
| const uint32_t DexFileMethodInliner::kIndexUnresolved; |
| const char* const DexFileMethodInliner::kClassCacheNames[] = { |
| "Z", // kClassCacheBoolean |
| "B", // kClassCacheByte |
| "C", // kClassCacheChar |
| "S", // kClassCacheShort |
| "I", // kClassCacheInt |
| "J", // kClassCacheLong |
| "F", // kClassCacheFloat |
| "D", // kClassCacheDouble |
| "V", // kClassCacheVoid |
| "Ljava/lang/Object;", // kClassCacheJavaLangObject |
| "Ljava/lang/ref/Reference;", // kClassCacheJavaLangRefReference |
| "Ljava/lang/String;", // kClassCacheJavaLangString |
| "Ljava/lang/Double;", // kClassCacheJavaLangDouble |
| "Ljava/lang/Float;", // kClassCacheJavaLangFloat |
| "Ljava/lang/Integer;", // kClassCacheJavaLangInteger |
| "Ljava/lang/Long;", // kClassCacheJavaLangLong |
| "Ljava/lang/Short;", // kClassCacheJavaLangShort |
| "Ljava/lang/Math;", // kClassCacheJavaLangMath |
| "Ljava/lang/StrictMath;", // kClassCacheJavaLangStrictMath |
| "Ljava/lang/Thread;", // kClassCacheJavaLangThread |
| "Llibcore/io/Memory;", // kClassCacheLibcoreIoMemory |
| "Lsun/misc/Unsafe;", // kClassCacheSunMiscUnsafe |
| "Ljava/lang/System;", // kClassCacheJavaLangSystem |
| "[C" // kClassCacheJavaLangCharArray |
| }; |
| |
| const char* const DexFileMethodInliner::kNameCacheNames[] = { |
| "reverse", // kNameCacheReverse |
| "reverseBytes", // kNameCacheReverseBytes |
| "doubleToRawLongBits", // kNameCacheDoubleToRawLongBits |
| "longBitsToDouble", // kNameCacheLongBitsToDouble |
| "floatToRawIntBits", // kNameCacheFloatToRawIntBits |
| "intBitsToFloat", // kNameCacheIntBitsToFloat |
| "abs", // kNameCacheAbs |
| "max", // kNameCacheMax |
| "min", // kNameCacheMin |
| "sqrt", // kNameCacheSqrt |
| "ceil", // kNameCacheCeil |
| "floor", // kNameCacheFloor |
| "rint", // kNameCacheRint |
| "round", // kNameCacheRound |
| "getReferent", // kNameCacheReferenceGet |
| "charAt", // kNameCacheCharAt |
| "compareTo", // kNameCacheCompareTo |
| "isEmpty", // kNameCacheIsEmpty |
| "indexOf", // kNameCacheIndexOf |
| "length", // kNameCacheLength |
| "currentThread", // kNameCacheCurrentThread |
| "peekByte", // kNameCachePeekByte |
| "peekIntNative", // kNameCachePeekIntNative |
| "peekLongNative", // kNameCachePeekLongNative |
| "peekShortNative", // kNameCachePeekShortNative |
| "pokeByte", // kNameCachePokeByte |
| "pokeIntNative", // kNameCachePokeIntNative |
| "pokeLongNative", // kNameCachePokeLongNative |
| "pokeShortNative", // kNameCachePokeShortNative |
| "compareAndSwapInt", // kNameCacheCompareAndSwapInt |
| "compareAndSwapLong", // kNameCacheCompareAndSwapLong |
| "compareAndSwapObject", // kNameCacheCompareAndSwapObject |
| "getInt", // kNameCacheGetInt |
| "getIntVolatile", // kNameCacheGetIntVolatile |
| "putInt", // kNameCachePutInt |
| "putIntVolatile", // kNameCachePutIntVolatile |
| "putOrderedInt", // kNameCachePutOrderedInt |
| "getLong", // kNameCacheGetLong |
| "getLongVolatile", // kNameCacheGetLongVolatile |
| "putLong", // kNameCachePutLong |
| "putLongVolatile", // kNameCachePutLongVolatile |
| "putOrderedLong", // kNameCachePutOrderedLong |
| "getObject", // kNameCacheGetObject |
| "getObjectVolatile", // kNameCacheGetObjectVolatile |
| "putObject", // kNameCachePutObject |
| "putObjectVolatile", // kNameCachePutObjectVolatile |
| "putOrderedObject", // kNameCachePutOrderedObject |
| "arraycopy", // kNameCacheArrayCopy |
| }; |
| |
| const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { |
| // kProtoCacheI_I |
| { kClassCacheInt, 1, { kClassCacheInt } }, |
| // kProtoCacheJ_J |
| { kClassCacheLong, 1, { kClassCacheLong } }, |
| // kProtoCacheS_S |
| { kClassCacheShort, 1, { kClassCacheShort } }, |
| // kProtoCacheD_D |
| { kClassCacheDouble, 1, { kClassCacheDouble } }, |
| // kProtoCacheDD_D |
| { kClassCacheDouble, 2, { kClassCacheDouble, kClassCacheDouble } }, |
| // kProtoCacheF_F |
| { kClassCacheFloat, 1, { kClassCacheFloat } }, |
| // kProtoCacheFF_F |
| { kClassCacheFloat, 2, { kClassCacheFloat, kClassCacheFloat } }, |
| // kProtoCacheD_J |
| { kClassCacheLong, 1, { kClassCacheDouble } }, |
| // kProtoCacheJ_D |
| { kClassCacheDouble, 1, { kClassCacheLong } }, |
| // kProtoCacheF_I |
| { kClassCacheInt, 1, { kClassCacheFloat } }, |
| // kProtoCacheI_F |
| { kClassCacheFloat, 1, { kClassCacheInt } }, |
| // kProtoCacheII_I |
| { kClassCacheInt, 2, { kClassCacheInt, kClassCacheInt } }, |
| // kProtoCacheI_C |
| { kClassCacheChar, 1, { kClassCacheInt } }, |
| // kProtoCacheString_I |
| { kClassCacheInt, 1, { kClassCacheJavaLangString } }, |
| // kProtoCache_Z |
| { kClassCacheBoolean, 0, { } }, |
| // kProtoCache_I |
| { kClassCacheInt, 0, { } }, |
| // kProtoCache_Object |
| { kClassCacheJavaLangObject, 0, { } }, |
| // kProtoCache_Thread |
| { kClassCacheJavaLangThread, 0, { } }, |
| // kProtoCacheJ_B |
| { kClassCacheByte, 1, { kClassCacheLong } }, |
| // kProtoCacheJ_I |
| { kClassCacheInt, 1, { kClassCacheLong } }, |
| // kProtoCacheJ_S |
| { kClassCacheShort, 1, { kClassCacheLong } }, |
| // kProtoCacheJB_V |
| { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheByte } }, |
| // kProtoCacheJI_V |
| { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheInt } }, |
| // kProtoCacheJJ_J |
| { kClassCacheLong, 2, { kClassCacheLong, kClassCacheLong } }, |
| // kProtoCacheJJ_V |
| { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheLong } }, |
| // kProtoCacheJS_V |
| { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheShort } }, |
| // kProtoCacheObjectJII_Z |
| { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong, |
| kClassCacheInt, kClassCacheInt } }, |
| // kProtoCacheObjectJJJ_Z |
| { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong, |
| kClassCacheLong, kClassCacheLong } }, |
| // kProtoCacheObjectJObjectObject_Z |
| { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong, |
| kClassCacheJavaLangObject, kClassCacheJavaLangObject } }, |
| // kProtoCacheObjectJ_I |
| { kClassCacheInt, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, |
| // kProtoCacheObjectJI_V |
| { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } }, |
| // kProtoCacheObjectJ_J |
| { kClassCacheLong, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, |
| // kProtoCacheObjectJJ_V |
| { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } }, |
| // kProtoCacheObjectJ_Object |
| { kClassCacheJavaLangObject, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, |
| // kProtoCacheObjectJObject_V |
| { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, |
| kClassCacheJavaLangObject } }, |
| // kProtoCacheCharArrayICharArrayII_V |
| { kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt, |
| kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt}} |
| }; |
| |
| const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = { |
| #define INTRINSIC(c, n, p, o, d) \ |
| { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineIntrinsic, { d } } } |
| |
| INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), |
| INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), |
| INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), |
| INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), |
| |
| INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, k32), |
| INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, k64), |
| INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), |
| INTRINSIC(JavaLangInteger, Reverse, I_I, kIntrinsicReverseBits, k32), |
| INTRINSIC(JavaLangLong, Reverse, J_J, kIntrinsicReverseBits, k64), |
| |
| INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), |
| INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), |
| INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0), |
| INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0), |
| INTRINSIC(JavaLangMath, Abs, F_F, kIntrinsicAbsFloat, 0), |
| INTRINSIC(JavaLangStrictMath, Abs, F_F, kIntrinsicAbsFloat, 0), |
| INTRINSIC(JavaLangMath, Abs, D_D, kIntrinsicAbsDouble, 0), |
| INTRINSIC(JavaLangStrictMath, Abs, D_D, kIntrinsicAbsDouble, 0), |
| INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), |
| INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), |
| INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), |
| INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), |
| INTRINSIC(JavaLangMath, Min, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMin), |
| INTRINSIC(JavaLangStrictMath, Min, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMin), |
| INTRINSIC(JavaLangMath, Max, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMax), |
| INTRINSIC(JavaLangStrictMath, Max, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMax), |
| INTRINSIC(JavaLangMath, Min, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMin), |
| INTRINSIC(JavaLangStrictMath, Min, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMin), |
| INTRINSIC(JavaLangMath, Max, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMax), |
| INTRINSIC(JavaLangStrictMath, Max, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMax), |
| INTRINSIC(JavaLangMath, Min, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMin), |
| INTRINSIC(JavaLangStrictMath, Min, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMin), |
| INTRINSIC(JavaLangMath, Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax), |
| INTRINSIC(JavaLangStrictMath, Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax), |
| |
| INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), |
| INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), |
| |
| INTRINSIC(JavaLangMath, Ceil, D_D, kIntrinsicCeil, 0), |
| INTRINSIC(JavaLangStrictMath, Ceil, D_D, kIntrinsicCeil, 0), |
| INTRINSIC(JavaLangMath, Floor, D_D, kIntrinsicFloor, 0), |
| INTRINSIC(JavaLangStrictMath, Floor, D_D, kIntrinsicFloor, 0), |
| INTRINSIC(JavaLangMath, Rint, D_D, kIntrinsicRint, 0), |
| INTRINSIC(JavaLangStrictMath, Rint, D_D, kIntrinsicRint, 0), |
| INTRINSIC(JavaLangMath, Round, F_I, kIntrinsicRoundFloat, 0), |
| INTRINSIC(JavaLangStrictMath, Round, F_I, kIntrinsicRoundFloat, 0), |
| INTRINSIC(JavaLangMath, Round, D_J, kIntrinsicRoundDouble, 0), |
| INTRINSIC(JavaLangStrictMath, Round, D_J, kIntrinsicRoundDouble, 0), |
| |
| INTRINSIC(JavaLangRefReference, ReferenceGetReferent, _Object, kIntrinsicReferenceGetReferent, 0), |
| |
| INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), |
| INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), |
| INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), |
| INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), |
| INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), |
| INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), |
| |
| INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), |
| |
| INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), |
| INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, k32), |
| INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, k64), |
| INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), |
| INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte), |
| INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, k32), |
| INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, k64), |
| INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), |
| |
| INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, |
| kIntrinsicFlagNone), |
| INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, |
| kIntrinsicFlagIsLong), |
| INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, |
| kIntrinsicFlagIsObject), |
| |
| #define UNSAFE_GET_PUT(type, code, type_flags) \ |
| INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ |
| type_flags & ~kIntrinsicFlagIsObject), \ |
| INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ |
| (type_flags | kIntrinsicFlagIsVolatile) & ~kIntrinsicFlagIsObject), \ |
| INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ |
| type_flags), \ |
| INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ |
| type_flags | kIntrinsicFlagIsVolatile), \ |
| INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ |
| type_flags | kIntrinsicFlagIsOrdered) |
| |
| UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), |
| UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), |
| UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), |
| #undef UNSAFE_GET_PUT |
| |
| INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray, |
| 0), |
| |
| |
| #undef INTRINSIC |
| }; |
| |
| DexFileMethodInliner::DexFileMethodInliner() |
| : lock_("DexFileMethodInliner lock", kDexFileMethodInlinerLock), |
| dex_file_(NULL) { |
| COMPILE_ASSERT(kClassCacheFirst == 0, kClassCacheFirst_not_0); |
| COMPILE_ASSERT(arraysize(kClassCacheNames) == kClassCacheLast, bad_arraysize_kClassCacheNames); |
| COMPILE_ASSERT(kNameCacheFirst == 0, kNameCacheFirst_not_0); |
| COMPILE_ASSERT(arraysize(kNameCacheNames) == kNameCacheLast, bad_arraysize_kNameCacheNames); |
| COMPILE_ASSERT(kProtoCacheFirst == 0, kProtoCacheFirst_not_0); |
| COMPILE_ASSERT(arraysize(kProtoCacheDefs) == kProtoCacheLast, bad_arraysize_kProtoCacheNames); |
| } |
| |
| DexFileMethodInliner::~DexFileMethodInliner() { |
| } |
| |
| bool DexFileMethodInliner::AnalyseMethodCode(verifier::MethodVerifier* verifier) { |
| InlineMethod method; |
| bool success = InlineMethodAnalyser::AnalyseMethodCode(verifier, &method); |
| return success && AddInlineMethod(verifier->GetMethodReference().dex_method_index, method); |
| } |
| |
| bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index, InlineMethod* intrinsic) { |
| ReaderMutexLock mu(Thread::Current(), lock_); |
| auto it = inline_methods_.find(method_index); |
| bool res = (it != inline_methods_.end() && (it->second.flags & kInlineIntrinsic) != 0); |
| if (res && intrinsic != nullptr) { |
| *intrinsic = it->second; |
| } |
| return res; |
| } |
| |
| bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { |
| InlineMethod intrinsic; |
| { |
| ReaderMutexLock mu(Thread::Current(), lock_); |
| auto it = inline_methods_.find(info->index); |
| if (it == inline_methods_.end() || (it->second.flags & kInlineIntrinsic) == 0) { |
| return false; |
| } |
| intrinsic = it->second; |
| } |
| if (kIntrinsicIsStatic[intrinsic.opcode] != (info->type == kStatic)) { |
| // Invoke type mismatch. |
| return false; |
| } |
| switch (intrinsic.opcode) { |
| case kIntrinsicDoubleCvt: |
| return backend->GenInlinedDoubleCvt(info); |
| case kIntrinsicFloatCvt: |
| return backend->GenInlinedFloatCvt(info); |
| case kIntrinsicReverseBytes: |
| return backend->GenInlinedReverseBytes(info, static_cast<OpSize>(intrinsic.d.data)); |
| case kIntrinsicReverseBits: |
| return backend->GenInlinedReverseBits(info, static_cast<OpSize>(intrinsic.d.data)); |
| case kIntrinsicAbsInt: |
| return backend->GenInlinedAbsInt(info); |
| case kIntrinsicAbsLong: |
| return backend->GenInlinedAbsLong(info); |
| case kIntrinsicAbsFloat: |
| return backend->GenInlinedAbsFloat(info); |
| case kIntrinsicAbsDouble: |
| return backend->GenInlinedAbsDouble(info); |
| case kIntrinsicMinMaxInt: |
| return backend->GenInlinedMinMax(info, intrinsic.d.data & kIntrinsicFlagMin, false /* is_long */); |
| case kIntrinsicMinMaxLong: |
| return backend->GenInlinedMinMax(info, intrinsic.d.data & kIntrinsicFlagMin, true /* is_long */); |
| case kIntrinsicMinMaxFloat: |
| return backend->GenInlinedMinMaxFP(info, intrinsic.d.data & kIntrinsicFlagMin, false /* is_double */); |
| case kIntrinsicMinMaxDouble: |
| return backend->GenInlinedMinMaxFP(info, intrinsic.d.data & kIntrinsicFlagMin, true /* is_double */); |
| case kIntrinsicSqrt: |
| return backend->GenInlinedSqrt(info); |
| case kIntrinsicCeil: |
| return backend->GenInlinedCeil(info); |
| case kIntrinsicFloor: |
| return backend->GenInlinedFloor(info); |
| case kIntrinsicRint: |
| return backend->GenInlinedRint(info); |
| case kIntrinsicRoundFloat: |
| return backend->GenInlinedRound(info, false /* is_double */); |
| case kIntrinsicRoundDouble: |
| return backend->GenInlinedRound(info, true /* is_double */); |
| case kIntrinsicReferenceGetReferent: |
| return backend->GenInlinedReferenceGetReferent(info); |
| case kIntrinsicCharAt: |
| return backend->GenInlinedCharAt(info); |
| case kIntrinsicCompareTo: |
| return backend->GenInlinedStringCompareTo(info); |
| case kIntrinsicIsEmptyOrLength: |
| return backend->GenInlinedStringIsEmptyOrLength( |
| info, intrinsic.d.data & kIntrinsicFlagIsEmpty); |
| case kIntrinsicIndexOf: |
| return backend->GenInlinedIndexOf(info, intrinsic.d.data & kIntrinsicFlagBase0); |
| case kIntrinsicCurrentThread: |
| return backend->GenInlinedCurrentThread(info); |
| case kIntrinsicPeek: |
| return backend->GenInlinedPeek(info, static_cast<OpSize>(intrinsic.d.data)); |
| case kIntrinsicPoke: |
| return backend->GenInlinedPoke(info, static_cast<OpSize>(intrinsic.d.data)); |
| case kIntrinsicCas: |
| return backend->GenInlinedCas(info, intrinsic.d.data & kIntrinsicFlagIsLong, |
| intrinsic.d.data & kIntrinsicFlagIsObject); |
| case kIntrinsicUnsafeGet: |
| return backend->GenInlinedUnsafeGet(info, intrinsic.d.data & kIntrinsicFlagIsLong, |
| intrinsic.d.data & kIntrinsicFlagIsVolatile); |
| case kIntrinsicUnsafePut: |
| return backend->GenInlinedUnsafePut(info, intrinsic.d.data & kIntrinsicFlagIsLong, |
| intrinsic.d.data & kIntrinsicFlagIsObject, |
| intrinsic.d.data & kIntrinsicFlagIsVolatile, |
| intrinsic.d.data & kIntrinsicFlagIsOrdered); |
| case kIntrinsicSystemArrayCopyCharArray: |
| return backend->GenInlinedArrayCopyCharArray(info); |
| default: |
| LOG(FATAL) << "Unexpected intrinsic opcode: " << intrinsic.opcode; |
| return false; // avoid warning "control reaches end of non-void function" |
| } |
| } |
| |
| bool DexFileMethodInliner::IsSpecial(uint32_t method_index) { |
| ReaderMutexLock mu(Thread::Current(), lock_); |
| auto it = inline_methods_.find(method_index); |
| return it != inline_methods_.end() && (it->second.flags & kInlineSpecial) != 0; |
| } |
| |
| bool DexFileMethodInliner::GenSpecial(Mir2Lir* backend, uint32_t method_idx) { |
| InlineMethod special; |
| { |
| ReaderMutexLock mu(Thread::Current(), lock_); |
| auto it = inline_methods_.find(method_idx); |
| if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) { |
| return false; |
| } |
| special = it->second; |
| } |
| return backend->SpecialMIR2LIR(special); |
| } |
| |
| bool DexFileMethodInliner::GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, |
| uint32_t method_idx) { |
| InlineMethod method; |
| { |
| ReaderMutexLock mu(Thread::Current(), lock_); |
| auto it = inline_methods_.find(method_idx); |
| if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) { |
| return false; |
| } |
| method = it->second; |
| } |
| |
| MIR* move_result = nullptr; |
| bool result = true; |
| switch (method.opcode) { |
| case kInlineOpNop: |
| break; |
| case kInlineOpNonWideConst: |
| move_result = mir_graph->FindMoveResult(bb, invoke); |
| result = GenInlineConst(mir_graph, bb, invoke, move_result, method); |
| break; |
| case kInlineOpReturnArg: |
| move_result = mir_graph->FindMoveResult(bb, invoke); |
| result = GenInlineReturnArg(mir_graph, bb, invoke, move_result, method); |
| break; |
| case kInlineOpIGet: |
| move_result = mir_graph->FindMoveResult(bb, invoke); |
| result = GenInlineIGet(mir_graph, bb, invoke, move_result, method, method_idx); |
| break; |
| case kInlineOpIPut: |
| move_result = mir_graph->FindMoveResult(bb, invoke); |
| result = GenInlineIPut(mir_graph, bb, invoke, move_result, method, method_idx); |
| break; |
| default: |
| LOG(FATAL) << "Unexpected inline op: " << method.opcode; |
| break; |
| } |
| if (result) { |
| invoke->optimization_flags |= MIR_INLINED; |
| // If the invoke has not been eliminated yet, check now whether we should do it. |
| // This is done so that dataflow analysis does not get tripped up seeing nop invoke. |
| if (static_cast<int>(invoke->dalvikInsn.opcode) != kMirOpNop) { |
| bool is_static = invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC || |
| invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE; |
| if (is_static || (invoke->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) { |
| // No null object register involved here so we can eliminate the invoke. |
| invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); |
| } else { |
| // Invoke was kept around because null check needed to be done. |
| invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNullCheck); |
| // For invokes, the object register is in vC. For null check mir, it is in vA. |
| invoke->dalvikInsn.vA = invoke->dalvikInsn.vC; |
| } |
| } |
| if (move_result != nullptr) { |
| move_result->optimization_flags |= MIR_INLINED; |
| move_result->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); |
| } |
| } |
| return result; |
| } |
| |
| uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCache* cache, |
| ClassCacheIndex index) { |
| uint32_t* class_index = &cache->class_indexes[index]; |
| if (*class_index != kIndexUnresolved) { |
| return *class_index; |
| } |
| |
| const DexFile::StringId* string_id = dex_file->FindStringId(kClassCacheNames[index]); |
| if (string_id == nullptr) { |
| *class_index = kIndexNotFound; |
| return *class_index; |
| } |
| uint32_t string_index = dex_file->GetIndexForStringId(*string_id); |
| |
| const DexFile::TypeId* type_id = dex_file->FindTypeId(string_index); |
| if (type_id == nullptr) { |
| *class_index = kIndexNotFound; |
| return *class_index; |
| } |
| *class_index = dex_file->GetIndexForTypeId(*type_id); |
| return *class_index; |
| } |
| |
| uint32_t DexFileMethodInliner::FindNameIndex(const DexFile* dex_file, IndexCache* cache, |
| NameCacheIndex index) { |
| uint32_t* name_index = &cache->name_indexes[index]; |
| if (*name_index != kIndexUnresolved) { |
| return *name_index; |
| } |
| |
| const DexFile::StringId* string_id = dex_file->FindStringId(kNameCacheNames[index]); |
| if (string_id == nullptr) { |
| *name_index = kIndexNotFound; |
| return *name_index; |
| } |
| *name_index = dex_file->GetIndexForStringId(*string_id); |
| return *name_index; |
| } |
| |
| uint32_t DexFileMethodInliner::FindProtoIndex(const DexFile* dex_file, IndexCache* cache, |
| ProtoCacheIndex index) { |
| uint32_t* proto_index = &cache->proto_indexes[index]; |
| if (*proto_index != kIndexUnresolved) { |
| return *proto_index; |
| } |
| |
| const ProtoDef& proto_def = kProtoCacheDefs[index]; |
| uint32_t return_index = FindClassIndex(dex_file, cache, proto_def.return_type); |
| if (return_index == kIndexNotFound) { |
| *proto_index = kIndexNotFound; |
| return *proto_index; |
| } |
| uint16_t return_type = static_cast<uint16_t>(return_index); |
| DCHECK_EQ(static_cast<uint32_t>(return_type), return_index); |
| |
| uint32_t signature_length = proto_def.param_count; |
| uint16_t signature_type_idxs[kProtoMaxParams]; |
| for (uint32_t i = 0; i != signature_length; ++i) { |
| uint32_t param_index = FindClassIndex(dex_file, cache, proto_def.params[i]); |
| if (param_index == kIndexNotFound) { |
| *proto_index = kIndexNotFound; |
| return *proto_index; |
| } |
| signature_type_idxs[i] = static_cast<uint16_t>(param_index); |
| DCHECK_EQ(static_cast<uint32_t>(signature_type_idxs[i]), param_index); |
| } |
| |
| const DexFile::ProtoId* proto_id = dex_file->FindProtoId(return_type, signature_type_idxs, |
| signature_length); |
| if (proto_id == nullptr) { |
| *proto_index = kIndexNotFound; |
| return *proto_index; |
| } |
| *proto_index = dex_file->GetIndexForProtoId(*proto_id); |
| return *proto_index; |
| } |
| |
| uint32_t DexFileMethodInliner::FindMethodIndex(const DexFile* dex_file, IndexCache* cache, |
| const MethodDef& method_def) { |
| uint32_t declaring_class_index = FindClassIndex(dex_file, cache, method_def.declaring_class); |
| if (declaring_class_index == kIndexNotFound) { |
| return kIndexNotFound; |
| } |
| uint32_t name_index = FindNameIndex(dex_file, cache, method_def.name); |
| if (name_index == kIndexNotFound) { |
| return kIndexNotFound; |
| } |
| uint32_t proto_index = FindProtoIndex(dex_file, cache, method_def.proto); |
| if (proto_index == kIndexNotFound) { |
| return kIndexNotFound; |
| } |
| const DexFile::MethodId* method_id = |
| dex_file->FindMethodId(dex_file->GetTypeId(declaring_class_index), |
| dex_file->GetStringId(name_index), |
| dex_file->GetProtoId(proto_index)); |
| if (method_id == nullptr) { |
| return kIndexNotFound; |
| } |
| return dex_file->GetIndexForMethodId(*method_id); |
| } |
| |
| DexFileMethodInliner::IndexCache::IndexCache() { |
| std::fill_n(class_indexes, arraysize(class_indexes), kIndexUnresolved); |
| std::fill_n(name_indexes, arraysize(name_indexes), kIndexUnresolved); |
| std::fill_n(proto_indexes, arraysize(proto_indexes), kIndexUnresolved); |
| } |
| |
| void DexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { |
| DCHECK(dex_file != nullptr); |
| DCHECK(dex_file_ == nullptr); |
| IndexCache cache; |
| for (const IntrinsicDef& def : kIntrinsicMethods) { |
| uint32_t method_idx = FindMethodIndex(dex_file, &cache, def.method_def); |
| if (method_idx != kIndexNotFound) { |
| DCHECK(inline_methods_.find(method_idx) == inline_methods_.end()); |
| inline_methods_.Put(method_idx, def.intrinsic); |
| } |
| } |
| dex_file_ = dex_file; |
| } |
| |
| bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, const InlineMethod& method) { |
| WriterMutexLock mu(Thread::Current(), lock_); |
| if (LIKELY(inline_methods_.find(method_idx) == inline_methods_.end())) { |
| inline_methods_.Put(method_idx, method); |
| return true; |
| } else { |
| if (PrettyMethod(method_idx, *dex_file_) == "int java.lang.String.length()") { |
| // TODO: String.length is both kIntrinsicIsEmptyOrLength and kInlineOpIGet. |
| } else { |
| LOG(ERROR) << "Inliner: " << PrettyMethod(method_idx, *dex_file_) << " already inline"; |
| } |
| return false; |
| } |
| } |
| |
| bool DexFileMethodInliner::GenInlineConst(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, |
| MIR* move_result, const InlineMethod& method) { |
| if (move_result == nullptr) { |
| // Result is unused. |
| return true; |
| } |
| |
| // Check the opcode and for MOVE_RESULT_OBJECT check also that the constant is null. |
| DCHECK(move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT || |
| (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT && |
| method.d.data == 0u)); |
| |
| // Insert the CONST instruction. |
| MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result); |
| insn->dalvikInsn.opcode = Instruction::CONST; |
| insn->dalvikInsn.vA = move_result->dalvikInsn.vA; |
| insn->dalvikInsn.vB = method.d.data; |
| bb->InsertMIRAfter(move_result, insn); |
| return true; |
| } |
| |
| bool DexFileMethodInliner::GenInlineReturnArg(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, |
| MIR* move_result, const InlineMethod& method) { |
| if (move_result == nullptr) { |
| // Result is unused. |
| return true; |
| } |
| |
| // Select opcode and argument. |
| const InlineReturnArgData& data = method.d.return_data; |
| Instruction::Code opcode = Instruction::MOVE_FROM16; |
| uint32_t arg = GetInvokeReg(invoke, data.arg); |
| if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { |
| DCHECK_EQ(data.is_object, 1u); |
| DCHECK_EQ(data.is_wide, 0u); |
| opcode = Instruction::MOVE_OBJECT_FROM16; |
| } else if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE) { |
| DCHECK_EQ(data.is_wide, 1u); |
| DCHECK_EQ(data.is_object, 0u); |
| opcode = Instruction::MOVE_WIDE_FROM16; |
| if (!WideArgIsInConsecutiveDalvikRegs(invoke, data.arg)) { |
| // The two halfs of the source value are not in consecutive dalvik registers in INVOKE. |
| return false; |
| } |
| } else { |
| DCHECK(move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT); |
| DCHECK_EQ(data.is_wide, 0u); |
| DCHECK_EQ(data.is_object, 0u); |
| } |
| |
| // Insert the move instruction |
| MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result); |
| insn->dalvikInsn.opcode = opcode; |
| insn->dalvikInsn.vA = move_result->dalvikInsn.vA; |
| insn->dalvikInsn.vB = arg; |
| bb->InsertMIRAfter(move_result, insn); |
| return true; |
| } |
| |
| bool DexFileMethodInliner::GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, |
| MIR* move_result, const InlineMethod& method, |
| uint32_t method_idx) { |
| CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit(); |
| if (cu->enable_debug & (1 << kDebugSlowFieldPath)) { |
| return false; |
| } |
| |
| const InlineIGetIPutData& data = method.d.ifield_data; |
| Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IGET + data.op_variant); |
| DCHECK_EQ(InlineMethodAnalyser::IGetVariant(opcode), data.op_variant); |
| uint32_t object_reg = GetInvokeReg(invoke, data.object_arg); |
| |
| if (move_result == nullptr) { |
| // Result is unused. If volatile, we still need to emit the IGET but we have no destination. |
| return !data.is_volatile; |
| } |
| |
| DCHECK_EQ(data.method_is_static != 0u, |
| invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC || |
| invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE); |
| bool object_is_this = (data.method_is_static == 0u && data.object_arg == 0u); |
| if (!object_is_this) { |
| // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE). |
| // Allow synthetic accessors. We don't care about losing their stack frame in NPE. |
| if (!InlineMethodAnalyser::IsSyntheticAccessor( |
| mir_graph->GetMethodLoweringInfo(invoke).GetTargetMethod())) { |
| return false; |
| } |
| } |
| |
| if (object_is_this) { |
| // Mark invoke as NOP, null-check is done on IGET. No aborts after this. |
| invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); |
| } |
| |
| MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result); |
| insn->offset = invoke->offset; |
| insn->dalvikInsn.opcode = opcode; |
| insn->dalvikInsn.vA = move_result->dalvikInsn.vA; |
| insn->dalvikInsn.vB = object_reg; |
| mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn); |
| |
| DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved()); |
| DCHECK(mir_graph->GetIFieldLoweringInfo(insn).FastGet()); |
| DCHECK_EQ(data.field_offset, mir_graph->GetIFieldLoweringInfo(insn).FieldOffset().Uint32Value()); |
| DCHECK_EQ(data.is_volatile, mir_graph->GetIFieldLoweringInfo(insn).IsVolatile() ? 1u : 0u); |
| |
| bb->InsertMIRAfter(move_result, insn); |
| return true; |
| } |
| |
| bool DexFileMethodInliner::GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, |
| MIR* move_result, const InlineMethod& method, |
| uint32_t method_idx) { |
| CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit(); |
| if (cu->enable_debug & (1 << kDebugSlowFieldPath)) { |
| return false; |
| } |
| |
| const InlineIGetIPutData& data = method.d.ifield_data; |
| Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IPUT + data.op_variant); |
| DCHECK_EQ(InlineMethodAnalyser::IPutVariant(opcode), data.op_variant); |
| uint32_t object_reg = GetInvokeReg(invoke, data.object_arg); |
| uint32_t src_reg = GetInvokeReg(invoke, data.src_arg); |
| uint32_t return_reg = |
| data.return_arg_plus1 != 0u ? GetInvokeReg(invoke, data.return_arg_plus1 - 1u) : 0u; |
| |
| if (opcode == Instruction::IPUT_WIDE && !WideArgIsInConsecutiveDalvikRegs(invoke, data.src_arg)) { |
| // The two halfs of the source value are not in consecutive dalvik registers in INVOKE. |
| return false; |
| } |
| |
| DCHECK(move_result == nullptr || data.return_arg_plus1 != 0u); |
| if (move_result != nullptr && move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE && |
| !WideArgIsInConsecutiveDalvikRegs(invoke, data.return_arg_plus1 - 1u)) { |
| // The two halfs of the return value are not in consecutive dalvik registers in INVOKE. |
| return false; |
| } |
| |
| DCHECK_EQ(data.method_is_static != 0u, |
| invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC || |
| invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE); |
| bool object_is_this = (data.method_is_static == 0u && data.object_arg == 0u); |
| if (!object_is_this) { |
| // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE). |
| // Allow synthetic accessors. We don't care about losing their stack frame in NPE. |
| if (!InlineMethodAnalyser::IsSyntheticAccessor( |
| mir_graph->GetMethodLoweringInfo(invoke).GetTargetMethod())) { |
| return false; |
| } |
| } |
| |
| if (object_is_this) { |
| // Mark invoke as NOP, null-check is done on IPUT. No aborts after this. |
| invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); |
| } |
| |
| MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result); |
| insn->dalvikInsn.opcode = opcode; |
| insn->dalvikInsn.vA = src_reg; |
| insn->dalvikInsn.vB = object_reg; |
| mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn); |
| |
| DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved()); |
| DCHECK(mir_graph->GetIFieldLoweringInfo(insn).FastPut()); |
| DCHECK_EQ(data.field_offset, mir_graph->GetIFieldLoweringInfo(insn).FieldOffset().Uint32Value()); |
| DCHECK_EQ(data.is_volatile, mir_graph->GetIFieldLoweringInfo(insn).IsVolatile() ? 1u : 0u); |
| |
| bb->InsertMIRAfter(invoke, insn); |
| |
| if (move_result != nullptr) { |
| MIR* move = AllocReplacementMIR(mir_graph, invoke, move_result); |
| move->offset = move_result->offset; |
| if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT) { |
| move->dalvikInsn.opcode = Instruction::MOVE_FROM16; |
| } else if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { |
| move->dalvikInsn.opcode = Instruction::MOVE_OBJECT_FROM16; |
| } else { |
| DCHECK_EQ(move_result->dalvikInsn.opcode, Instruction::MOVE_RESULT_WIDE); |
| move->dalvikInsn.opcode = Instruction::MOVE_WIDE_FROM16; |
| } |
| move->dalvikInsn.vA = move_result->dalvikInsn.vA; |
| move->dalvikInsn.vB = return_reg; |
| bb->InsertMIRAfter(insn, move); |
| } |
| return true; |
| } |
| |
| } // namespace art |