dex_file_method_inliner.cc revision c91df2d6339dd4adf2da582372451df19ce2ff44
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "dex_file_method_inliner.h"
18
19#include <algorithm>
20
21#include "base/logging.h"
22#include "base/macros.h"
23#include "base/mutex-inl.h"
24#include "dex/compiler_ir.h"
25#include "thread-inl.h"
26#include "dex/mir_graph.h"
27#include "dex/quick/mir_to_lir.h"
28#include "dex_instruction-inl.h"
29#include "driver/dex_compilation_unit.h"
30#include "verifier/method_verifier-inl.h"
31
32namespace art {
33
34namespace {  // anonymous namespace
35
36static constexpr bool kIntrinsicIsStatic[] = {
37    true,   // kIntrinsicDoubleCvt
38    true,   // kIntrinsicFloatCvt
39    true,   // kIntrinsicReverseBits
40    true,   // kIntrinsicReverseBytes
41    true,   // kIntrinsicAbsInt
42    true,   // kIntrinsicAbsLong
43    true,   // kIntrinsicAbsFloat
44    true,   // kIntrinsicAbsDouble
45    true,   // kIntrinsicMinMaxInt
46    true,   // kIntrinsicMinMaxLong
47    true,   // kIntrinsicMinMaxFloat
48    true,   // kIntrinsicMinMaxDouble
49    true,   // kIntrinsicSqrt
50    true,   // kIntrinsicCeil
51    true,   // kIntrinsicFloor
52    true,   // kIntrinsicRint
53    true,   // kIntrinsicRoundFloat
54    true,   // kIntrinsicRoundDouble
55    false,  // kIntrinsicReferenceGetReferent
56    false,  // kIntrinsicCharAt
57    false,  // kIntrinsicCompareTo
58    false,  // kIntrinsicIsEmptyOrLength
59    false,  // kIntrinsicIndexOf
60    true,   // kIntrinsicCurrentThread
61    true,   // kIntrinsicPeek
62    true,   // kIntrinsicPoke
63    false,  // kIntrinsicCas
64    false,  // kIntrinsicUnsafeGet
65    false,  // kIntrinsicUnsafePut
66    true,   // kIntrinsicSystemArrayCopyCharArray
67};
68static_assert(arraysize(kIntrinsicIsStatic) == kInlineOpNop,
69              "arraysize of kIntrinsicIsStatic unexpected");
70static_assert(kIntrinsicIsStatic[kIntrinsicDoubleCvt], "DoubleCvt must be static");
71static_assert(kIntrinsicIsStatic[kIntrinsicFloatCvt], "FloatCvt must be static");
72static_assert(kIntrinsicIsStatic[kIntrinsicReverseBits], "ReverseBits must be static");
73static_assert(kIntrinsicIsStatic[kIntrinsicReverseBytes], "ReverseBytes must be static");
74static_assert(kIntrinsicIsStatic[kIntrinsicAbsInt], "AbsInt must be static");
75static_assert(kIntrinsicIsStatic[kIntrinsicAbsLong], "AbsLong must be static");
76static_assert(kIntrinsicIsStatic[kIntrinsicAbsFloat], "AbsFloat must be static");
77static_assert(kIntrinsicIsStatic[kIntrinsicAbsDouble], "AbsDouble must be static");
78static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxInt], "MinMaxInt must be static");
79static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxLong], "MinMaxLong_must_be_static");
80static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxFloat], "MinMaxFloat_must_be_static");
81static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxDouble], "MinMaxDouble_must_be_static");
82static_assert(kIntrinsicIsStatic[kIntrinsicSqrt], "Sqrt must be static");
83static_assert(kIntrinsicIsStatic[kIntrinsicCeil], "Ceil must be static");
84static_assert(kIntrinsicIsStatic[kIntrinsicFloor], "Floor must be static");
85static_assert(kIntrinsicIsStatic[kIntrinsicRint], "Rint must be static");
86static_assert(kIntrinsicIsStatic[kIntrinsicRoundFloat], "RoundFloat must be static");
87static_assert(kIntrinsicIsStatic[kIntrinsicRoundDouble], "RoundDouble must be static");
88static_assert(!kIntrinsicIsStatic[kIntrinsicReferenceGetReferent], "Get must not be static");
89static_assert(!kIntrinsicIsStatic[kIntrinsicCharAt], "CharAt must not be static");
90static_assert(!kIntrinsicIsStatic[kIntrinsicCompareTo], "CompareTo must not be static");
91static_assert(!kIntrinsicIsStatic[kIntrinsicIsEmptyOrLength], "IsEmptyOrLength must not be static");
92static_assert(!kIntrinsicIsStatic[kIntrinsicIndexOf], "IndexOf must not be static");
93static_assert(kIntrinsicIsStatic[kIntrinsicCurrentThread], "CurrentThread must be static");
94static_assert(kIntrinsicIsStatic[kIntrinsicPeek], "Peek must be static");
95static_assert(kIntrinsicIsStatic[kIntrinsicPoke], "Poke must be static");
96static_assert(!kIntrinsicIsStatic[kIntrinsicCas], "Cas must not be static");
97static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], "UnsafeGet_must_not_be_static");
98static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafePut], "UnsafePut must not be static");
99static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray],
100              "SystemArrayCopyCharArray must be static");
101
102MIR* AllocReplacementMIR(MIRGraph* mir_graph, MIR* invoke) {
103  MIR* insn = mir_graph->NewMIR();
104  insn->offset = invoke->offset;
105  insn->optimization_flags = MIR_CALLEE;
106  return insn;
107}
108
109uint32_t GetInvokeReg(MIR* invoke, uint32_t arg) {
110  DCHECK_LT(arg, invoke->dalvikInsn.vA);
111  DCHECK(!MIR::DecodedInstruction::IsPseudoMirOp(invoke->dalvikInsn.opcode));
112  if (IsInvokeInstructionRange(invoke->dalvikInsn.opcode)) {
113    return invoke->dalvikInsn.vC + arg;  // Range invoke.
114  } else {
115    DCHECK_EQ(Instruction::FormatOf(invoke->dalvikInsn.opcode), Instruction::k35c);
116    return invoke->dalvikInsn.arg[arg];  // Non-range invoke.
117  }
118}
119
120bool WideArgIsInConsecutiveDalvikRegs(MIR* invoke, uint32_t arg) {
121  DCHECK_LT(arg + 1, invoke->dalvikInsn.vA);
122  DCHECK(!MIR::DecodedInstruction::IsPseudoMirOp(invoke->dalvikInsn.opcode));
123  return IsInvokeInstructionRange(invoke->dalvikInsn.opcode) ||
124      invoke->dalvikInsn.arg[arg + 1u] == invoke->dalvikInsn.arg[arg] + 1u;
125}
126
127}  // anonymous namespace
128
129const uint32_t DexFileMethodInliner::kIndexUnresolved;
130const char* const DexFileMethodInliner::kClassCacheNames[] = {
131    "Z",                       // kClassCacheBoolean
132    "B",                       // kClassCacheByte
133    "C",                       // kClassCacheChar
134    "S",                       // kClassCacheShort
135    "I",                       // kClassCacheInt
136    "J",                       // kClassCacheLong
137    "F",                       // kClassCacheFloat
138    "D",                       // kClassCacheDouble
139    "V",                       // kClassCacheVoid
140    "Ljava/lang/Object;",      // kClassCacheJavaLangObject
141    "Ljava/lang/ref/Reference;",  // kClassCacheJavaLangRefReference
142    "Ljava/lang/String;",      // kClassCacheJavaLangString
143    "Ljava/lang/Double;",      // kClassCacheJavaLangDouble
144    "Ljava/lang/Float;",       // kClassCacheJavaLangFloat
145    "Ljava/lang/Integer;",     // kClassCacheJavaLangInteger
146    "Ljava/lang/Long;",        // kClassCacheJavaLangLong
147    "Ljava/lang/Short;",       // kClassCacheJavaLangShort
148    "Ljava/lang/Math;",        // kClassCacheJavaLangMath
149    "Ljava/lang/StrictMath;",  // kClassCacheJavaLangStrictMath
150    "Ljava/lang/Thread;",      // kClassCacheJavaLangThread
151    "Llibcore/io/Memory;",     // kClassCacheLibcoreIoMemory
152    "Lsun/misc/Unsafe;",       // kClassCacheSunMiscUnsafe
153    "Ljava/lang/System;",      // kClassCacheJavaLangSystem
154    "[C"                       // kClassCacheJavaLangCharArray
155};
156
157const char* const DexFileMethodInliner::kNameCacheNames[] = {
158    "reverse",               // kNameCacheReverse
159    "reverseBytes",          // kNameCacheReverseBytes
160    "doubleToRawLongBits",   // kNameCacheDoubleToRawLongBits
161    "longBitsToDouble",      // kNameCacheLongBitsToDouble
162    "floatToRawIntBits",     // kNameCacheFloatToRawIntBits
163    "intBitsToFloat",        // kNameCacheIntBitsToFloat
164    "abs",                   // kNameCacheAbs
165    "max",                   // kNameCacheMax
166    "min",                   // kNameCacheMin
167    "sqrt",                  // kNameCacheSqrt
168    "ceil",                  // kNameCacheCeil
169    "floor",                 // kNameCacheFloor
170    "rint",                  // kNameCacheRint
171    "round",                 // kNameCacheRound
172    "getReferent",           // kNameCacheReferenceGet
173    "charAt",                // kNameCacheCharAt
174    "compareTo",             // kNameCacheCompareTo
175    "isEmpty",               // kNameCacheIsEmpty
176    "indexOf",               // kNameCacheIndexOf
177    "length",                // kNameCacheLength
178    "currentThread",         // kNameCacheCurrentThread
179    "peekByte",              // kNameCachePeekByte
180    "peekIntNative",         // kNameCachePeekIntNative
181    "peekLongNative",        // kNameCachePeekLongNative
182    "peekShortNative",       // kNameCachePeekShortNative
183    "pokeByte",              // kNameCachePokeByte
184    "pokeIntNative",         // kNameCachePokeIntNative
185    "pokeLongNative",        // kNameCachePokeLongNative
186    "pokeShortNative",       // kNameCachePokeShortNative
187    "compareAndSwapInt",     // kNameCacheCompareAndSwapInt
188    "compareAndSwapLong",    // kNameCacheCompareAndSwapLong
189    "compareAndSwapObject",  // kNameCacheCompareAndSwapObject
190    "getInt",                // kNameCacheGetInt
191    "getIntVolatile",        // kNameCacheGetIntVolatile
192    "putInt",                // kNameCachePutInt
193    "putIntVolatile",        // kNameCachePutIntVolatile
194    "putOrderedInt",         // kNameCachePutOrderedInt
195    "getLong",               // kNameCacheGetLong
196    "getLongVolatile",       // kNameCacheGetLongVolatile
197    "putLong",               // kNameCachePutLong
198    "putLongVolatile",       // kNameCachePutLongVolatile
199    "putOrderedLong",        // kNameCachePutOrderedLong
200    "getObject",             // kNameCacheGetObject
201    "getObjectVolatile",     // kNameCacheGetObjectVolatile
202    "putObject",             // kNameCachePutObject
203    "putObjectVolatile",     // kNameCachePutObjectVolatile
204    "putOrderedObject",      // kNameCachePutOrderedObject
205    "arraycopy",             // kNameCacheArrayCopy
206};
207
208const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = {
209    // kProtoCacheI_I
210    { kClassCacheInt, 1, { kClassCacheInt } },
211    // kProtoCacheJ_J
212    { kClassCacheLong, 1, { kClassCacheLong } },
213    // kProtoCacheS_S
214    { kClassCacheShort, 1, { kClassCacheShort } },
215    // kProtoCacheD_D
216    { kClassCacheDouble, 1, { kClassCacheDouble } },
217    // kProtoCacheDD_D
218    { kClassCacheDouble, 2, { kClassCacheDouble, kClassCacheDouble } },
219    // kProtoCacheF_F
220    { kClassCacheFloat, 1, { kClassCacheFloat } },
221    // kProtoCacheFF_F
222    { kClassCacheFloat, 2, { kClassCacheFloat, kClassCacheFloat } },
223    // kProtoCacheD_J
224    { kClassCacheLong, 1, { kClassCacheDouble } },
225    // kProtoCacheJ_D
226    { kClassCacheDouble, 1, { kClassCacheLong } },
227    // kProtoCacheF_I
228    { kClassCacheInt, 1, { kClassCacheFloat } },
229    // kProtoCacheI_F
230    { kClassCacheFloat, 1, { kClassCacheInt } },
231    // kProtoCacheII_I
232    { kClassCacheInt, 2, { kClassCacheInt, kClassCacheInt } },
233    // kProtoCacheI_C
234    { kClassCacheChar, 1, { kClassCacheInt } },
235    // kProtoCacheString_I
236    { kClassCacheInt, 1, { kClassCacheJavaLangString } },
237    // kProtoCache_Z
238    { kClassCacheBoolean, 0, { } },
239    // kProtoCache_I
240    { kClassCacheInt, 0, { } },
241    // kProtoCache_Object
242    { kClassCacheJavaLangObject, 0, { } },
243    // kProtoCache_Thread
244    { kClassCacheJavaLangThread, 0, { } },
245    // kProtoCacheJ_B
246    { kClassCacheByte, 1, { kClassCacheLong } },
247    // kProtoCacheJ_I
248    { kClassCacheInt, 1, { kClassCacheLong } },
249    // kProtoCacheJ_S
250    { kClassCacheShort, 1, { kClassCacheLong } },
251    // kProtoCacheJB_V
252    { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheByte } },
253    // kProtoCacheJI_V
254    { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheInt } },
255    // kProtoCacheJJ_J
256    { kClassCacheLong, 2, { kClassCacheLong, kClassCacheLong } },
257    // kProtoCacheJJ_V
258    { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheLong } },
259    // kProtoCacheJS_V
260    { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheShort } },
261    // kProtoCacheObjectJII_Z
262    { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
263        kClassCacheInt, kClassCacheInt } },
264    // kProtoCacheObjectJJJ_Z
265    { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
266        kClassCacheLong, kClassCacheLong } },
267    // kProtoCacheObjectJObjectObject_Z
268    { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
269        kClassCacheJavaLangObject, kClassCacheJavaLangObject } },
270    // kProtoCacheObjectJ_I
271    { kClassCacheInt, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
272    // kProtoCacheObjectJI_V
273    { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } },
274    // kProtoCacheObjectJ_J
275    { kClassCacheLong, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
276    // kProtoCacheObjectJJ_V
277    { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } },
278    // kProtoCacheObjectJ_Object
279    { kClassCacheJavaLangObject, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
280    // kProtoCacheObjectJObject_V
281    { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong,
282        kClassCacheJavaLangObject } },
283    // kProtoCacheCharArrayICharArrayII_V
284    { kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt,
285                kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt}}
286};
287
288const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = {
289#define INTRINSIC(c, n, p, o, d) \
290    { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineIntrinsic, { d } } }
291
292    INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0),
293    INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, kIntrinsicFlagToFloatingPoint),
294    INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0),
295    INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, kIntrinsicFlagToFloatingPoint),
296
297    INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, k32),
298    INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, k64),
299    INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf),
300    INTRINSIC(JavaLangInteger, Reverse, I_I, kIntrinsicReverseBits, k32),
301    INTRINSIC(JavaLangLong, Reverse, J_J, kIntrinsicReverseBits, k64),
302
303    INTRINSIC(JavaLangMath,       Abs, I_I, kIntrinsicAbsInt, 0),
304    INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0),
305    INTRINSIC(JavaLangMath,       Abs, J_J, kIntrinsicAbsLong, 0),
306    INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0),
307    INTRINSIC(JavaLangMath,       Abs, F_F, kIntrinsicAbsFloat, 0),
308    INTRINSIC(JavaLangStrictMath, Abs, F_F, kIntrinsicAbsFloat, 0),
309    INTRINSIC(JavaLangMath,       Abs, D_D, kIntrinsicAbsDouble, 0),
310    INTRINSIC(JavaLangStrictMath, Abs, D_D, kIntrinsicAbsDouble, 0),
311    INTRINSIC(JavaLangMath,       Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin),
312    INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin),
313    INTRINSIC(JavaLangMath,       Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax),
314    INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax),
315    INTRINSIC(JavaLangMath,       Min, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMin),
316    INTRINSIC(JavaLangStrictMath, Min, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMin),
317    INTRINSIC(JavaLangMath,       Max, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMax),
318    INTRINSIC(JavaLangStrictMath, Max, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMax),
319    INTRINSIC(JavaLangMath,       Min, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMin),
320    INTRINSIC(JavaLangStrictMath, Min, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMin),
321    INTRINSIC(JavaLangMath,       Max, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMax),
322    INTRINSIC(JavaLangStrictMath, Max, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMax),
323    INTRINSIC(JavaLangMath,       Min, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMin),
324    INTRINSIC(JavaLangStrictMath, Min, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMin),
325    INTRINSIC(JavaLangMath,       Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax),
326    INTRINSIC(JavaLangStrictMath, Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax),
327
328    INTRINSIC(JavaLangMath,       Sqrt, D_D, kIntrinsicSqrt, 0),
329    INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0),
330
331    INTRINSIC(JavaLangMath,       Ceil, D_D, kIntrinsicCeil, 0),
332    INTRINSIC(JavaLangStrictMath, Ceil, D_D, kIntrinsicCeil, 0),
333    INTRINSIC(JavaLangMath,       Floor, D_D, kIntrinsicFloor, 0),
334    INTRINSIC(JavaLangStrictMath, Floor, D_D, kIntrinsicFloor, 0),
335    INTRINSIC(JavaLangMath,       Rint, D_D, kIntrinsicRint, 0),
336    INTRINSIC(JavaLangStrictMath, Rint, D_D, kIntrinsicRint, 0),
337    INTRINSIC(JavaLangMath,       Round, F_I, kIntrinsicRoundFloat, 0),
338    INTRINSIC(JavaLangStrictMath, Round, F_I, kIntrinsicRoundFloat, 0),
339    INTRINSIC(JavaLangMath,       Round, D_J, kIntrinsicRoundDouble, 0),
340    INTRINSIC(JavaLangStrictMath, Round, D_J, kIntrinsicRoundDouble, 0),
341
342    INTRINSIC(JavaLangRefReference, ReferenceGetReferent, _Object, kIntrinsicReferenceGetReferent, 0),
343
344    INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0),
345    INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0),
346    INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty),
347    INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone),
348    INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0),
349    INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength),
350
351    INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0),
352
353    INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte),
354    INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, k32),
355    INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, k64),
356    INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf),
357    INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte),
358    INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, k32),
359    INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, k64),
360    INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf),
361
362    INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas,
363              kIntrinsicFlagNone),
364    INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas,
365              kIntrinsicFlagIsLong),
366    INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas,
367              kIntrinsicFlagIsObject),
368
369#define UNSAFE_GET_PUT(type, code, type_flags) \
370    INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \
371              type_flags), \
372    INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \
373              type_flags | kIntrinsicFlagIsVolatile), \
374    INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \
375              type_flags), \
376    INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \
377              type_flags | kIntrinsicFlagIsVolatile), \
378    INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \
379              type_flags | kIntrinsicFlagIsOrdered)
380
381    UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone),
382    UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong),
383    UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject),
384#undef UNSAFE_GET_PUT
385
386    INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray,
387              0),
388
389
390#undef INTRINSIC
391};
392
393DexFileMethodInliner::DexFileMethodInliner()
394    : lock_("DexFileMethodInliner lock", kDexFileMethodInlinerLock),
395      dex_file_(nullptr) {
396  static_assert(kClassCacheFirst == 0, "kClassCacheFirst not 0");
397  static_assert(arraysize(kClassCacheNames) == kClassCacheLast,
398                "bad arraysize for kClassCacheNames");
399  static_assert(kNameCacheFirst == 0, "kNameCacheFirst not 0");
400  static_assert(arraysize(kNameCacheNames) == kNameCacheLast,
401                "bad arraysize for kNameCacheNames");
402  static_assert(kProtoCacheFirst == 0, "kProtoCacheFirst not 0");
403  static_assert(arraysize(kProtoCacheDefs) == kProtoCacheLast,
404                "bad arraysize kProtoCacheNames");
405}
406
407DexFileMethodInliner::~DexFileMethodInliner() {
408}
409
410bool DexFileMethodInliner::AnalyseMethodCode(verifier::MethodVerifier* verifier) {
411  InlineMethod method;
412  bool success = InlineMethodAnalyser::AnalyseMethodCode(verifier, &method);
413  return success && AddInlineMethod(verifier->GetMethodReference().dex_method_index, method);
414}
415
416InlineMethodFlags DexFileMethodInliner::IsIntrinsicOrSpecial(uint32_t method_index) {
417  ReaderMutexLock mu(Thread::Current(), lock_);
418  auto it = inline_methods_.find(method_index);
419  if (it != inline_methods_.end()) {
420    DCHECK_NE(it->second.flags & (kInlineIntrinsic | kInlineSpecial), 0);
421    return it->second.flags;
422  } else {
423    return kNoInlineMethodFlags;
424  }
425}
426
427bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index, InlineMethod* intrinsic) {
428  ReaderMutexLock mu(Thread::Current(), lock_);
429  auto it = inline_methods_.find(method_index);
430  bool res = (it != inline_methods_.end() && (it->second.flags & kInlineIntrinsic) != 0);
431  if (res && intrinsic != nullptr) {
432    *intrinsic = it->second;
433  }
434  return res;
435}
436
437bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) {
438  InlineMethod intrinsic;
439  {
440    ReaderMutexLock mu(Thread::Current(), lock_);
441    auto it = inline_methods_.find(info->method_ref.dex_method_index);
442    if (it == inline_methods_.end() || (it->second.flags & kInlineIntrinsic) == 0) {
443      return false;
444    }
445    intrinsic = it->second;
446  }
447  if (kIntrinsicIsStatic[intrinsic.opcode] != (info->type == kStatic)) {
448    // Invoke type mismatch.
449    return false;
450  }
451  switch (intrinsic.opcode) {
452    case kIntrinsicDoubleCvt:
453      return backend->GenInlinedDoubleCvt(info);
454    case kIntrinsicFloatCvt:
455      return backend->GenInlinedFloatCvt(info);
456    case kIntrinsicReverseBytes:
457      return backend->GenInlinedReverseBytes(info, static_cast<OpSize>(intrinsic.d.data));
458    case kIntrinsicReverseBits:
459      return backend->GenInlinedReverseBits(info, static_cast<OpSize>(intrinsic.d.data));
460    case kIntrinsicAbsInt:
461      return backend->GenInlinedAbsInt(info);
462    case kIntrinsicAbsLong:
463      return backend->GenInlinedAbsLong(info);
464    case kIntrinsicAbsFloat:
465      return backend->GenInlinedAbsFloat(info);
466    case kIntrinsicAbsDouble:
467      return backend->GenInlinedAbsDouble(info);
468    case kIntrinsicMinMaxInt:
469      return backend->GenInlinedMinMax(info, intrinsic.d.data & kIntrinsicFlagMin, false /* is_long */);
470    case kIntrinsicMinMaxLong:
471      return backend->GenInlinedMinMax(info, intrinsic.d.data & kIntrinsicFlagMin, true /* is_long */);
472    case kIntrinsicMinMaxFloat:
473      return backend->GenInlinedMinMaxFP(info, intrinsic.d.data & kIntrinsicFlagMin, false /* is_double */);
474    case kIntrinsicMinMaxDouble:
475      return backend->GenInlinedMinMaxFP(info, intrinsic.d.data & kIntrinsicFlagMin, true /* is_double */);
476    case kIntrinsicSqrt:
477      return backend->GenInlinedSqrt(info);
478    case kIntrinsicCeil:
479      return backend->GenInlinedCeil(info);
480    case kIntrinsicFloor:
481      return backend->GenInlinedFloor(info);
482    case kIntrinsicRint:
483      return backend->GenInlinedRint(info);
484    case kIntrinsicRoundFloat:
485      return backend->GenInlinedRound(info, false /* is_double */);
486    case kIntrinsicRoundDouble:
487      return backend->GenInlinedRound(info, true /* is_double */);
488    case kIntrinsicReferenceGetReferent:
489      return backend->GenInlinedReferenceGetReferent(info);
490    case kIntrinsicCharAt:
491      return backend->GenInlinedCharAt(info);
492    case kIntrinsicCompareTo:
493      return backend->GenInlinedStringCompareTo(info);
494    case kIntrinsicIsEmptyOrLength:
495      return backend->GenInlinedStringIsEmptyOrLength(
496          info, intrinsic.d.data & kIntrinsicFlagIsEmpty);
497    case kIntrinsicIndexOf:
498      return backend->GenInlinedIndexOf(info, intrinsic.d.data & kIntrinsicFlagBase0);
499    case kIntrinsicCurrentThread:
500      return backend->GenInlinedCurrentThread(info);
501    case kIntrinsicPeek:
502      return backend->GenInlinedPeek(info, static_cast<OpSize>(intrinsic.d.data));
503    case kIntrinsicPoke:
504      return backend->GenInlinedPoke(info, static_cast<OpSize>(intrinsic.d.data));
505    case kIntrinsicCas:
506      return backend->GenInlinedCas(info, intrinsic.d.data & kIntrinsicFlagIsLong,
507                                    intrinsic.d.data & kIntrinsicFlagIsObject);
508    case kIntrinsicUnsafeGet:
509      return backend->GenInlinedUnsafeGet(info, intrinsic.d.data & kIntrinsicFlagIsLong,
510                                          intrinsic.d.data & kIntrinsicFlagIsObject,
511                                          intrinsic.d.data & kIntrinsicFlagIsVolatile);
512    case kIntrinsicUnsafePut:
513      return backend->GenInlinedUnsafePut(info, intrinsic.d.data & kIntrinsicFlagIsLong,
514                                          intrinsic.d.data & kIntrinsicFlagIsObject,
515                                          intrinsic.d.data & kIntrinsicFlagIsVolatile,
516                                          intrinsic.d.data & kIntrinsicFlagIsOrdered);
517    case kIntrinsicSystemArrayCopyCharArray:
518      return backend->GenInlinedArrayCopyCharArray(info);
519    default:
520      LOG(FATAL) << "Unexpected intrinsic opcode: " << intrinsic.opcode;
521      return false;  // avoid warning "control reaches end of non-void function"
522  }
523}
524
525bool DexFileMethodInliner::IsSpecial(uint32_t method_index) {
526  ReaderMutexLock mu(Thread::Current(), lock_);
527  auto it = inline_methods_.find(method_index);
528  return it != inline_methods_.end() && (it->second.flags & kInlineSpecial) != 0;
529}
530
531bool DexFileMethodInliner::GenSpecial(Mir2Lir* backend, uint32_t method_idx) {
532  InlineMethod special;
533  {
534    ReaderMutexLock mu(Thread::Current(), lock_);
535    auto it = inline_methods_.find(method_idx);
536    if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) {
537      return false;
538    }
539    special = it->second;
540  }
541  return backend->SpecialMIR2LIR(special);
542}
543
544bool DexFileMethodInliner::GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
545                                     uint32_t method_idx) {
546  InlineMethod method;
547  {
548    ReaderMutexLock mu(Thread::Current(), lock_);
549    auto it = inline_methods_.find(method_idx);
550    if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) {
551      return false;
552    }
553    method = it->second;
554  }
555
556  MIR* move_result = nullptr;
557  bool result = true;
558  switch (method.opcode) {
559    case kInlineOpNop:
560      break;
561    case kInlineOpNonWideConst:
562      move_result = mir_graph->FindMoveResult(bb, invoke);
563      result = GenInlineConst(mir_graph, bb, invoke, move_result, method);
564      break;
565    case kInlineOpReturnArg:
566      move_result = mir_graph->FindMoveResult(bb, invoke);
567      result = GenInlineReturnArg(mir_graph, bb, invoke, move_result, method);
568      break;
569    case kInlineOpIGet:
570      move_result = mir_graph->FindMoveResult(bb, invoke);
571      result = GenInlineIGet(mir_graph, bb, invoke, move_result, method);
572      break;
573    case kInlineOpIPut:
574      move_result = mir_graph->FindMoveResult(bb, invoke);
575      result = GenInlineIPut(mir_graph, bb, invoke, move_result, method);
576      break;
577    default:
578      LOG(FATAL) << "Unexpected inline op: " << method.opcode;
579      break;
580  }
581  if (result) {
582    // If the invoke has not been eliminated yet, check now whether we should do it.
583    // This is done so that dataflow analysis does not get tripped up seeing nop invoke.
584    if (static_cast<int>(invoke->dalvikInsn.opcode) != kMirOpNop) {
585      bool is_static = IsInstructionInvokeStatic(invoke->dalvikInsn.opcode);
586      if (is_static || (invoke->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) {
587        // No null object register involved here so we can eliminate the invoke.
588        invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
589      } else {
590        // Invoke was kept around because null check needed to be done.
591        invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNullCheck);
592        // For invokes, the object register is in vC. For null check mir, it is in vA.
593        invoke->dalvikInsn.vA = invoke->dalvikInsn.vC;
594      }
595    }
596    if (move_result != nullptr) {
597      move_result->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
598    }
599  }
600  return result;
601}
602
603uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCache* cache,
604                                              ClassCacheIndex index) {
605  uint32_t* class_index = &cache->class_indexes[index];
606  if (*class_index != kIndexUnresolved) {
607    return *class_index;
608  }
609
610  const DexFile::StringId* string_id = dex_file->FindStringId(kClassCacheNames[index]);
611  if (string_id == nullptr) {
612    *class_index = kIndexNotFound;
613    return *class_index;
614  }
615  uint32_t string_index = dex_file->GetIndexForStringId(*string_id);
616
617  const DexFile::TypeId* type_id = dex_file->FindTypeId(string_index);
618  if (type_id == nullptr) {
619    *class_index = kIndexNotFound;
620    return *class_index;
621  }
622  *class_index = dex_file->GetIndexForTypeId(*type_id);
623  return *class_index;
624}
625
626uint32_t DexFileMethodInliner::FindNameIndex(const DexFile* dex_file, IndexCache* cache,
627                                             NameCacheIndex index) {
628  uint32_t* name_index = &cache->name_indexes[index];
629  if (*name_index != kIndexUnresolved) {
630    return *name_index;
631  }
632
633  const DexFile::StringId* string_id = dex_file->FindStringId(kNameCacheNames[index]);
634  if (string_id == nullptr) {
635    *name_index = kIndexNotFound;
636    return *name_index;
637  }
638  *name_index = dex_file->GetIndexForStringId(*string_id);
639  return *name_index;
640}
641
642uint32_t DexFileMethodInliner::FindProtoIndex(const DexFile* dex_file, IndexCache* cache,
643                                              ProtoCacheIndex index) {
644  uint32_t* proto_index = &cache->proto_indexes[index];
645  if (*proto_index != kIndexUnresolved) {
646    return *proto_index;
647  }
648
649  const ProtoDef& proto_def = kProtoCacheDefs[index];
650  uint32_t return_index = FindClassIndex(dex_file, cache, proto_def.return_type);
651  if (return_index == kIndexNotFound) {
652    *proto_index = kIndexNotFound;
653    return *proto_index;
654  }
655  uint16_t return_type = static_cast<uint16_t>(return_index);
656  DCHECK_EQ(static_cast<uint32_t>(return_type), return_index);
657
658  uint32_t signature_length = proto_def.param_count;
659  uint16_t signature_type_idxs[kProtoMaxParams];
660  for (uint32_t i = 0; i != signature_length; ++i) {
661    uint32_t param_index = FindClassIndex(dex_file, cache, proto_def.params[i]);
662    if (param_index == kIndexNotFound) {
663      *proto_index = kIndexNotFound;
664      return *proto_index;
665    }
666    signature_type_idxs[i] = static_cast<uint16_t>(param_index);
667    DCHECK_EQ(static_cast<uint32_t>(signature_type_idxs[i]), param_index);
668  }
669
670  const DexFile::ProtoId* proto_id = dex_file->FindProtoId(return_type, signature_type_idxs,
671                                                           signature_length);
672  if (proto_id == nullptr) {
673    *proto_index = kIndexNotFound;
674    return *proto_index;
675  }
676  *proto_index = dex_file->GetIndexForProtoId(*proto_id);
677  return *proto_index;
678}
679
680uint32_t DexFileMethodInliner::FindMethodIndex(const DexFile* dex_file, IndexCache* cache,
681                                               const MethodDef& method_def) {
682  uint32_t declaring_class_index = FindClassIndex(dex_file, cache, method_def.declaring_class);
683  if (declaring_class_index == kIndexNotFound) {
684    return kIndexNotFound;
685  }
686  uint32_t name_index = FindNameIndex(dex_file, cache, method_def.name);
687  if (name_index == kIndexNotFound) {
688    return kIndexNotFound;
689  }
690  uint32_t proto_index = FindProtoIndex(dex_file, cache, method_def.proto);
691  if (proto_index == kIndexNotFound) {
692    return kIndexNotFound;
693  }
694  const DexFile::MethodId* method_id =
695      dex_file->FindMethodId(dex_file->GetTypeId(declaring_class_index),
696                             dex_file->GetStringId(name_index),
697                             dex_file->GetProtoId(proto_index));
698  if (method_id == nullptr) {
699    return kIndexNotFound;
700  }
701  return dex_file->GetIndexForMethodId(*method_id);
702}
703
704DexFileMethodInliner::IndexCache::IndexCache() {
705  std::fill_n(class_indexes, arraysize(class_indexes), kIndexUnresolved);
706  std::fill_n(name_indexes, arraysize(name_indexes), kIndexUnresolved);
707  std::fill_n(proto_indexes, arraysize(proto_indexes), kIndexUnresolved);
708}
709
710void DexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) {
711  DCHECK(dex_file != nullptr);
712  DCHECK(dex_file_ == nullptr);
713  IndexCache cache;
714  for (const IntrinsicDef& def : kIntrinsicMethods) {
715    uint32_t method_idx = FindMethodIndex(dex_file, &cache, def.method_def);
716    if (method_idx != kIndexNotFound) {
717      DCHECK(inline_methods_.find(method_idx) == inline_methods_.end());
718      inline_methods_.Put(method_idx, def.intrinsic);
719    }
720  }
721  dex_file_ = dex_file;
722}
723
724bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, const InlineMethod& method) {
725  WriterMutexLock mu(Thread::Current(), lock_);
726  if (LIKELY(inline_methods_.find(method_idx) == inline_methods_.end())) {
727    inline_methods_.Put(method_idx, method);
728    return true;
729  } else {
730    if (PrettyMethod(method_idx, *dex_file_) == "int java.lang.String.length()") {
731      // TODO: String.length is both kIntrinsicIsEmptyOrLength and kInlineOpIGet.
732    } else {
733      LOG(WARNING) << "Inliner: " << PrettyMethod(method_idx, *dex_file_) << " already inline";
734    }
735    return false;
736  }
737}
738
739bool DexFileMethodInliner::GenInlineConst(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
740                                          MIR* move_result, const InlineMethod& method) {
741  if (move_result == nullptr) {
742    // Result is unused.
743    return true;
744  }
745
746  // Check the opcode and for MOVE_RESULT_OBJECT check also that the constant is null.
747  DCHECK(move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT ||
748         (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT &&
749             method.d.data == 0u));
750
751  // Insert the CONST instruction.
752  MIR* insn = AllocReplacementMIR(mir_graph, invoke);
753  insn->dalvikInsn.opcode = Instruction::CONST;
754  insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
755  insn->dalvikInsn.vB = method.d.data;
756  insn->meta.method_lowering_info = invoke->meta.method_lowering_info;  // Preserve type info.
757  bb->InsertMIRAfter(move_result, insn);
758  return true;
759}
760
761bool DexFileMethodInliner::GenInlineReturnArg(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
762                                              MIR* move_result, const InlineMethod& method) {
763  if (move_result == nullptr) {
764    // Result is unused.
765    return true;
766  }
767
768  // Select opcode and argument.
769  const InlineReturnArgData& data = method.d.return_data;
770  Instruction::Code opcode = Instruction::MOVE_FROM16;
771  uint32_t arg = GetInvokeReg(invoke, data.arg);
772  if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
773    DCHECK_EQ(data.is_object, 1u);
774    DCHECK_EQ(data.is_wide, 0u);
775    opcode = Instruction::MOVE_OBJECT_FROM16;
776  } else if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE) {
777    DCHECK_EQ(data.is_wide, 1u);
778    DCHECK_EQ(data.is_object, 0u);
779    opcode = Instruction::MOVE_WIDE_FROM16;
780    if (!WideArgIsInConsecutiveDalvikRegs(invoke, data.arg)) {
781      // The two halfs of the source value are not in consecutive dalvik registers in INVOKE.
782      return false;
783    }
784  } else {
785    DCHECK(move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT);
786    DCHECK_EQ(data.is_wide, 0u);
787    DCHECK_EQ(data.is_object, 0u);
788  }
789
790  // Insert the move instruction
791  MIR* insn = AllocReplacementMIR(mir_graph, invoke);
792  insn->dalvikInsn.opcode = opcode;
793  insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
794  insn->dalvikInsn.vB = arg;
795  insn->meta.method_lowering_info = invoke->meta.method_lowering_info;  // Preserve type info.
796  bb->InsertMIRAfter(move_result, insn);
797  return true;
798}
799
800bool DexFileMethodInliner::GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
801                                         MIR* move_result, const InlineMethod& method) {
802  CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit();
803  if (cu->enable_debug & (1 << kDebugSlowFieldPath)) {
804    return false;
805  }
806
807  const InlineIGetIPutData& data = method.d.ifield_data;
808  Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IGET + data.op_variant);
809  DCHECK_EQ(InlineMethodAnalyser::IGetVariant(opcode), data.op_variant);
810  uint32_t object_reg = GetInvokeReg(invoke, data.object_arg);
811
812  if (move_result == nullptr) {
813    // Result is unused. If volatile, we still need to emit the IGET but we have no destination.
814    return !data.is_volatile;
815  }
816
817  DCHECK_EQ(data.method_is_static != 0u, IsInstructionInvokeStatic(invoke->dalvikInsn.opcode));
818  bool object_is_this = (data.method_is_static == 0u && data.object_arg == 0u);
819  if (!object_is_this) {
820    // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE).
821    // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
822    if (!InlineMethodAnalyser::IsSyntheticAccessor(
823        mir_graph->GetMethodLoweringInfo(invoke).GetTargetMethod())) {
824      return false;
825    }
826  }
827
828  if (object_is_this) {
829    // Mark invoke as NOP, null-check is done on IGET. No aborts after this.
830    invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
831  }
832
833  MIR* insn = AllocReplacementMIR(mir_graph, invoke);
834  insn->offset = invoke->offset;
835  insn->dalvikInsn.opcode = opcode;
836  insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
837  insn->dalvikInsn.vB = object_reg;
838  mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn);
839
840  DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved());
841  DCHECK(mir_graph->GetIFieldLoweringInfo(insn).FastGet());
842  DCHECK_EQ(data.field_offset, mir_graph->GetIFieldLoweringInfo(insn).FieldOffset().Uint32Value());
843  DCHECK_EQ(data.is_volatile, mir_graph->GetIFieldLoweringInfo(insn).IsVolatile() ? 1u : 0u);
844
845  bb->InsertMIRAfter(move_result, insn);
846  return true;
847}
848
849bool DexFileMethodInliner::GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
850                                         MIR* move_result, const InlineMethod& method) {
851  CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit();
852  if (cu->enable_debug & (1 << kDebugSlowFieldPath)) {
853    return false;
854  }
855
856  const InlineIGetIPutData& data = method.d.ifield_data;
857  Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IPUT + data.op_variant);
858  DCHECK_EQ(InlineMethodAnalyser::IPutVariant(opcode), data.op_variant);
859  uint32_t object_reg = GetInvokeReg(invoke, data.object_arg);
860  uint32_t src_reg = GetInvokeReg(invoke, data.src_arg);
861  uint32_t return_reg =
862      data.return_arg_plus1 != 0u ? GetInvokeReg(invoke, data.return_arg_plus1 - 1u) : 0u;
863
864  if (opcode == Instruction::IPUT_WIDE && !WideArgIsInConsecutiveDalvikRegs(invoke, data.src_arg)) {
865    // The two halfs of the source value are not in consecutive dalvik registers in INVOKE.
866    return false;
867  }
868
869  DCHECK(move_result == nullptr || data.return_arg_plus1 != 0u);
870  if (move_result != nullptr && move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE &&
871      !WideArgIsInConsecutiveDalvikRegs(invoke, data.return_arg_plus1 - 1u)) {
872    // The two halfs of the return value are not in consecutive dalvik registers in INVOKE.
873    return false;
874  }
875
876  DCHECK_EQ(data.method_is_static != 0u, IsInstructionInvokeStatic(invoke->dalvikInsn.opcode));
877  bool object_is_this = (data.method_is_static == 0u && data.object_arg == 0u);
878  if (!object_is_this) {
879    // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE).
880    // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
881    if (!InlineMethodAnalyser::IsSyntheticAccessor(
882        mir_graph->GetMethodLoweringInfo(invoke).GetTargetMethod())) {
883      return false;
884    }
885  }
886
887  if (object_is_this) {
888    // Mark invoke as NOP, null-check is done on IPUT. No aborts after this.
889    invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
890  }
891
892  MIR* insn = AllocReplacementMIR(mir_graph, invoke);
893  insn->dalvikInsn.opcode = opcode;
894  insn->dalvikInsn.vA = src_reg;
895  insn->dalvikInsn.vB = object_reg;
896  mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn);
897
898  DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved());
899  DCHECK(mir_graph->GetIFieldLoweringInfo(insn).FastPut());
900  DCHECK_EQ(data.field_offset, mir_graph->GetIFieldLoweringInfo(insn).FieldOffset().Uint32Value());
901  DCHECK_EQ(data.is_volatile, mir_graph->GetIFieldLoweringInfo(insn).IsVolatile() ? 1u : 0u);
902
903  bb->InsertMIRAfter(invoke, insn);
904
905  if (move_result != nullptr) {
906    MIR* move = AllocReplacementMIR(mir_graph, invoke);
907    move->offset = move_result->offset;
908    if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT) {
909      move->dalvikInsn.opcode = Instruction::MOVE_FROM16;
910    } else if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
911      move->dalvikInsn.opcode = Instruction::MOVE_OBJECT_FROM16;
912    } else {
913      DCHECK_EQ(move_result->dalvikInsn.opcode, Instruction::MOVE_RESULT_WIDE);
914      move->dalvikInsn.opcode = Instruction::MOVE_WIDE_FROM16;
915    }
916    move->dalvikInsn.vA = move_result->dalvikInsn.vA;
917    move->dalvikInsn.vB = return_reg;
918    move->meta.method_lowering_info = invoke->meta.method_lowering_info;  // Preserve type info.
919    bb->InsertMIRAfter(insn, move);
920  }
921  return true;
922}
923
924}  // namespace art
925