dex_file_method_inliner.h revision 5dc5727261e87ba8a418e2d0e970c75f67e4ab79
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#ifndef ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_
18#define ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_
19
20#include <stdint.h>
21#include "base/mutex.h"
22#include "base/macros.h"
23#include "safe_map.h"
24#include "dex/compiler_enums.h"
25#include "dex_file.h"
26#include "locks.h"
27
28namespace art {
29
30namespace verifier {
31class MethodVerifier;
32}  // namespace verifier
33
34class CallInfo;
35class Mir2Lir;
36
37enum InlineMethodOpcode : uint16_t {
38  kIntrinsicDoubleCvt,
39  kIntrinsicFloatCvt,
40  kIntrinsicReverseBytes,
41  kIntrinsicAbsInt,
42  kIntrinsicAbsLong,
43  kIntrinsicMinMaxInt,
44  kIntrinsicSqrt,
45  kIntrinsicCharAt,
46  kIntrinsicCompareTo,
47  kIntrinsicIsEmptyOrLength,
48  kIntrinsicIndexOf,
49  kIntrinsicCurrentThread,
50  kIntrinsicPeek,
51  kIntrinsicPoke,
52  kIntrinsicCas,
53  kIntrinsicUnsafeGet,
54  kIntrinsicUnsafePut,
55
56  kInlineOpNop,
57  kInlineOpReturnArg,
58  kInlineOpConst,
59  kInlineOpIGet,
60  kInlineOpIPut,
61};
62
63enum InlineMethodFlags : uint16_t {
64  kNoInlineMethodFlags = 0x0000,
65  kInlineIntrinsic     = 0x0001,
66  kInlineSpecial       = 0x0002,
67};
68
69// IntrinsicFlags are stored in InlineMethod::d::raw_data
70enum IntrinsicFlags {
71  kIntrinsicFlagNone = 0,
72
73  // kIntrinsicMinMaxInt
74  kIntrinsicFlagMax = kIntrinsicFlagNone,
75  kIntrinsicFlagMin = 1,
76
77  // kIntrinsicIsEmptyOrLength
78  kIntrinsicFlagLength  = kIntrinsicFlagNone,
79  kIntrinsicFlagIsEmpty = 1,
80
81  // kIntrinsicIndexOf
82  kIntrinsicFlagBase0 = 1,
83
84  // kIntrinsicUnsafeGet, kIntrinsicUnsafePut, kIntrinsicUnsafeCas
85  kIntrinsicFlagIsLong     = 1,
86  // kIntrinsicUnsafeGet, kIntrinsicUnsafePut
87  kIntrinsicFlagIsVolatile = 2,
88  // kIntrinsicUnsafePut, kIntrinsicUnsafeCas
89  kIntrinsicFlagIsObject   = 4,
90  // kIntrinsicUnsafePut
91  kIntrinsicFlagIsOrdered  = 8,
92};
93
94// Check that OpSize fits into 3 bits (at least the values the inliner uses).
95COMPILE_ASSERT(kWord < 8 && kLong < 8 && kSingle < 8 && kDouble < 8 && kUnsignedHalf < 8 &&
96               kSignedHalf < 8 && kUnsignedByte < 8 && kSignedByte < 8, op_size_field_too_narrow);
97
98struct InlineIGetIPutData {
99  uint16_t op_size : 3;  // OpSize
100  uint16_t is_object : 1;
101  uint16_t object_arg : 4;
102  uint16_t src_arg : 4;  // iput only
103  uint16_t method_is_static : 1;
104  uint16_t reserved : 3;
105  uint16_t field_idx;
106  uint32_t is_volatile : 1;
107  uint32_t field_offset : 31;
108};
109COMPILE_ASSERT(sizeof(InlineIGetIPutData) == sizeof(uint64_t), InvalidSizeOfInlineIGetIPutData);
110
111struct InlineReturnArgData {
112  uint16_t arg;
113  uint16_t op_size : 3;  // OpSize
114  uint16_t is_object : 1;
115  uint16_t reserved : 12;
116  uint32_t reserved2;
117};
118COMPILE_ASSERT(sizeof(InlineReturnArgData) == sizeof(uint64_t), InvalidSizeOfInlineReturnArgData);
119
120struct InlineMethod {
121  InlineMethodOpcode opcode;
122  InlineMethodFlags flags;
123  union {
124    uint64_t data;
125    InlineIGetIPutData ifield_data;
126    InlineReturnArgData return_data;
127  } d;
128};
129
130/**
131 * Handles inlining of methods from a particular DexFile.
132 *
133 * Intrinsics are a special case of inline methods. The DexFile indices for
134 * all the supported intrinsic methods are looked up once by the FindIntrinsics
135 * function and cached by this class for quick lookup by the method index.
136 *
137 * TODO: Detect short methods (at least getters, setters and empty functions)
138 * from the verifier and mark them for inlining. Inline these methods early
139 * during compilation to allow further optimizations. Similarly, provide
140 * additional information about intrinsics to the early phases of compilation.
141 */
142class DexFileMethodInliner {
143  public:
144    DexFileMethodInliner();
145    ~DexFileMethodInliner();
146
147    /**
148     * Analyse method code to determine if the method is a candidate for inlining.
149     * If it is, record its data for later.
150     *
151     * @param method_idx the index of the inlining candidate.
152     * @param code_item a previously verified code item of the method.
153     */
154    bool AnalyseMethodCode(verifier::MethodVerifier* verifier)
155        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_);
156
157    /**
158     * Check whether a particular method index corresponds to an intrinsic function.
159     */
160    bool IsIntrinsic(uint32_t method_index) LOCKS_EXCLUDED(lock_);
161
162    /**
163     * Generate code for an intrinsic function invocation.
164     */
165    bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) LOCKS_EXCLUDED(lock_);
166
167    /**
168     * Check whether a particular method index corresponds to a special function.
169     */
170    bool IsSpecial(uint32_t method_index) LOCKS_EXCLUDED(lock_);
171
172    /**
173     * Generate code for a special function.
174     */
175    bool GenSpecial(Mir2Lir* backend, uint32_t method_idx);
176
177  private:
178    /**
179     * To avoid multiple lookups of a class by its descriptor, we cache its
180     * type index in the IndexCache. These are the indexes into the IndexCache
181     * class_indexes array.
182     */
183    enum ClassCacheIndex : uint8_t {  // unit8_t to save space, make larger if needed
184      kClassCacheFirst = 0,
185      kClassCacheBoolean = kClassCacheFirst,
186      kClassCacheByte,
187      kClassCacheChar,
188      kClassCacheShort,
189      kClassCacheInt,
190      kClassCacheLong,
191      kClassCacheFloat,
192      kClassCacheDouble,
193      kClassCacheVoid,
194      kClassCacheJavaLangObject,
195      kClassCacheJavaLangString,
196      kClassCacheJavaLangDouble,
197      kClassCacheJavaLangFloat,
198      kClassCacheJavaLangInteger,
199      kClassCacheJavaLangLong,
200      kClassCacheJavaLangShort,
201      kClassCacheJavaLangMath,
202      kClassCacheJavaLangStrictMath,
203      kClassCacheJavaLangThread,
204      kClassCacheLibcoreIoMemory,
205      kClassCacheSunMiscUnsafe,
206      kClassCacheLast
207    };
208
209    /**
210     * To avoid multiple lookups of a method name string, we cache its string
211     * index in the IndexCache. These are the indexes into the IndexCache
212     * name_indexes array.
213     */
214    enum NameCacheIndex : uint8_t {  // unit8_t to save space, make larger if needed
215      kNameCacheFirst = 0,
216      kNameCacheReverseBytes = kNameCacheFirst,
217      kNameCacheDoubleToRawLongBits,
218      kNameCacheLongBitsToDouble,
219      kNameCacheFloatToRawIntBits,
220      kNameCacheIntBitsToFloat,
221      kNameCacheAbs,
222      kNameCacheMax,
223      kNameCacheMin,
224      kNameCacheSqrt,
225      kNameCacheCharAt,
226      kNameCacheCompareTo,
227      kNameCacheIsEmpty,
228      kNameCacheIndexOf,
229      kNameCacheLength,
230      kNameCacheCurrentThread,
231      kNameCachePeekByte,
232      kNameCachePeekIntNative,
233      kNameCachePeekLongNative,
234      kNameCachePeekShortNative,
235      kNameCachePokeByte,
236      kNameCachePokeIntNative,
237      kNameCachePokeLongNative,
238      kNameCachePokeShortNative,
239      kNameCacheCompareAndSwapInt,
240      kNameCacheCompareAndSwapLong,
241      kNameCacheCompareAndSwapObject,
242      kNameCacheGetInt,
243      kNameCacheGetIntVolatile,
244      kNameCachePutInt,
245      kNameCachePutIntVolatile,
246      kNameCachePutOrderedInt,
247      kNameCacheGetLong,
248      kNameCacheGetLongVolatile,
249      kNameCachePutLong,
250      kNameCachePutLongVolatile,
251      kNameCachePutOrderedLong,
252      kNameCacheGetObject,
253      kNameCacheGetObjectVolatile,
254      kNameCachePutObject,
255      kNameCachePutObjectVolatile,
256      kNameCachePutOrderedObject,
257      kNameCacheLast
258    };
259
260    /**
261     * To avoid multiple lookups of a method signature, we cache its proto
262     * index in the IndexCache. These are the indexes into the IndexCache
263     * proto_indexes array.
264     */
265    enum ProtoCacheIndex : uint8_t {  // unit8_t to save space, make larger if needed
266      kProtoCacheFirst = 0,
267      kProtoCacheI_I = kProtoCacheFirst,
268      kProtoCacheJ_J,
269      kProtoCacheS_S,
270      kProtoCacheD_D,
271      kProtoCacheD_J,
272      kProtoCacheJ_D,
273      kProtoCacheF_I,
274      kProtoCacheI_F,
275      kProtoCacheII_I,
276      kProtoCacheI_C,
277      kProtoCacheString_I,
278      kProtoCache_Z,
279      kProtoCache_I,
280      kProtoCache_Thread,
281      kProtoCacheJ_B,
282      kProtoCacheJ_I,
283      kProtoCacheJ_S,
284      kProtoCacheJB_V,
285      kProtoCacheJI_V,
286      kProtoCacheJJ_V,
287      kProtoCacheJS_V,
288      kProtoCacheObjectJII_Z,
289      kProtoCacheObjectJJJ_Z,
290      kProtoCacheObjectJObjectObject_Z,
291      kProtoCacheObjectJ_I,
292      kProtoCacheObjectJI_V,
293      kProtoCacheObjectJ_J,
294      kProtoCacheObjectJJ_V,
295      kProtoCacheObjectJ_Object,
296      kProtoCacheObjectJObject_V,
297      kProtoCacheLast
298    };
299
300    /**
301     * The maximum number of method parameters we support in the ProtoDef.
302     */
303    static constexpr uint32_t kProtoMaxParams = 6;
304
305    /**
306     * The method signature (proto) definition using cached class indexes.
307     * The return_type and params are used with the IndexCache to look up
308     * appropriate class indexes to be passed to DexFile::FindProtoId().
309     */
310    struct ProtoDef {
311      ClassCacheIndex return_type;
312      uint8_t param_count;
313      ClassCacheIndex params[kProtoMaxParams];
314    };
315
316    /**
317     * The method definition using cached class, name and proto indexes.
318     * The class index, method name index and proto index are used with
319     * IndexCache to look up appropriate parameters for DexFile::FindMethodId().
320     */
321    struct MethodDef {
322      ClassCacheIndex declaring_class;
323      NameCacheIndex name;
324      ProtoCacheIndex proto;
325    };
326
327    /**
328     * The definition of an intrinsic function binds the method definition
329     * to an Intrinsic.
330     */
331    struct IntrinsicDef {
332      MethodDef method_def;
333      InlineMethod intrinsic;
334    };
335
336    /**
337     * Cache for class, method name and method signature indexes used during
338     * intrinsic function lookup to avoid multiple lookups of the same items.
339     *
340     * Many classes have multiple intrinsics and/or they are used in multiple
341     * method signatures and we want to avoid repeated lookups since they are
342     * not exactly cheap. The method names and method signatures are sometimes
343     * reused and therefore cached as well.
344     */
345    struct IndexCache {
346      IndexCache();
347
348      uint32_t class_indexes[kClassCacheLast - kClassCacheFirst];
349      uint32_t name_indexes[kNameCacheLast - kNameCacheFirst];
350      uint32_t proto_indexes[kProtoCacheLast - kProtoCacheFirst];
351    };
352
353    static const char* const kClassCacheNames[];
354    static const char* const kNameCacheNames[];
355    static const ProtoDef kProtoCacheDefs[];
356    static const IntrinsicDef kIntrinsicMethods[];
357
358    static const uint32_t kIndexNotFound = static_cast<uint32_t>(-1);
359    static const uint32_t kIndexUnresolved = static_cast<uint32_t>(-2);
360
361    static uint32_t FindClassIndex(const DexFile* dex_file, IndexCache* cache,
362                                   ClassCacheIndex index);
363    static uint32_t FindNameIndex(const DexFile* dex_file, IndexCache* cache,
364                                  NameCacheIndex index);
365    static uint32_t FindProtoIndex(const DexFile* dex_file, IndexCache* cache,
366                                   ProtoCacheIndex index);
367    static uint32_t FindMethodIndex(const DexFile* dex_file, IndexCache* cache,
368                                    const MethodDef& method_def);
369
370    /**
371     * Find all known intrinsic methods in the dex_file and cache their indices.
372     *
373     * Only DexFileToMethodInlinerMap may call this function to initialize the inliner.
374     */
375    void FindIntrinsics(const DexFile* dex_file) EXCLUSIVE_LOCKS_REQUIRED(lock_);
376
377    friend class DexFileToMethodInlinerMap;
378
379    bool AddInlineMethod(int32_t method_idx, const InlineMethod& method) LOCKS_EXCLUDED(lock_);
380
381    static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
382    static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
383    static bool AnalyseIGetMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
384        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
385    static bool AnalyseIPutMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
386        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
387
388    ReaderWriterMutex lock_;
389    /*
390     * Maps method indexes (for the particular DexFile) to Intrinsic defintions.
391     */
392    SafeMap<uint32_t, InlineMethod> inline_methods_ GUARDED_BY(lock_);
393    const DexFile* dex_file_;
394
395    DISALLOW_COPY_AND_ASSIGN(DexFileMethodInliner);
396};
397
398}  // namespace art
399
400#endif  // ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_
401