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