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