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