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