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