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