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