1/*
2 * Copyright (C) 2014 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_RUNTIME_QUICK_INLINE_METHOD_ANALYSER_H_
18#define ART_RUNTIME_QUICK_INLINE_METHOD_ANALYSER_H_
19
20#include "base/macros.h"
21#include "base/mutex.h"
22#include "dex_file.h"
23#include "dex_instruction.h"
24#include "method_reference.h"
25
26/*
27 * NOTE: This code is part of the quick compiler. It lives in the runtime
28 * only to allow the debugger to check whether a method has been inlined.
29 */
30
31namespace art {
32
33namespace verifier {
34class MethodVerifier;
35}  // namespace verifier
36
37enum InlineMethodOpcode : uint16_t {
38  kIntrinsicDoubleCvt,
39  kIntrinsicFloatCvt,
40  kIntrinsicFloat2Int,
41  kIntrinsicDouble2Long,
42  kIntrinsicFloatIsInfinite,
43  kIntrinsicDoubleIsInfinite,
44  kIntrinsicFloatIsNaN,
45  kIntrinsicDoubleIsNaN,
46  kIntrinsicReverseBits,
47  kIntrinsicReverseBytes,
48  kIntrinsicBitCount,
49  kIntrinsicCompare,
50  kIntrinsicHighestOneBit,
51  kIntrinsicLowestOneBit,
52  kIntrinsicNumberOfLeadingZeros,
53  kIntrinsicNumberOfTrailingZeros,
54  kIntrinsicRotateRight,
55  kIntrinsicRotateLeft,
56  kIntrinsicSignum,
57  kIntrinsicAbsInt,
58  kIntrinsicAbsLong,
59  kIntrinsicAbsFloat,
60  kIntrinsicAbsDouble,
61  kIntrinsicMinMaxInt,
62  kIntrinsicMinMaxLong,
63  kIntrinsicMinMaxFloat,
64  kIntrinsicMinMaxDouble,
65  kIntrinsicCos,
66  kIntrinsicSin,
67  kIntrinsicAcos,
68  kIntrinsicAsin,
69  kIntrinsicAtan,
70  kIntrinsicAtan2,
71  kIntrinsicCbrt,
72  kIntrinsicCosh,
73  kIntrinsicExp,
74  kIntrinsicExpm1,
75  kIntrinsicHypot,
76  kIntrinsicLog,
77  kIntrinsicLog10,
78  kIntrinsicNextAfter,
79  kIntrinsicSinh,
80  kIntrinsicTan,
81  kIntrinsicTanh,
82  kIntrinsicSqrt,
83  kIntrinsicCeil,
84  kIntrinsicFloor,
85  kIntrinsicRint,
86  kIntrinsicRoundFloat,
87  kIntrinsicRoundDouble,
88  kIntrinsicReferenceGetReferent,
89  kIntrinsicCharAt,
90  kIntrinsicCompareTo,
91  kIntrinsicEquals,
92  kIntrinsicGetCharsNoCheck,
93  kIntrinsicIsEmptyOrLength,
94  kIntrinsicIndexOf,
95  kIntrinsicNewStringFromBytes,
96  kIntrinsicNewStringFromChars,
97  kIntrinsicNewStringFromString,
98  kIntrinsicCurrentThread,
99  kIntrinsicPeek,
100  kIntrinsicPoke,
101  kIntrinsicCas,
102  kIntrinsicUnsafeGet,
103  kIntrinsicUnsafePut,
104
105  // 1.8.
106  kIntrinsicUnsafeGetAndAddInt,
107  kIntrinsicUnsafeGetAndAddLong,
108  kIntrinsicUnsafeGetAndSetInt,
109  kIntrinsicUnsafeGetAndSetLong,
110  kIntrinsicUnsafeGetAndSetObject,
111  kIntrinsicUnsafeLoadFence,
112  kIntrinsicUnsafeStoreFence,
113  kIntrinsicUnsafeFullFence,
114
115  kIntrinsicSystemArrayCopyCharArray,
116  kIntrinsicSystemArrayCopy,
117
118  kInlineOpNop,
119  kInlineOpReturnArg,
120  kInlineOpNonWideConst,
121  kInlineOpIGet,
122  kInlineOpIPut,
123  kInlineOpConstructor,
124  kInlineStringInit,
125};
126std::ostream& operator<<(std::ostream& os, const InlineMethodOpcode& rhs);
127
128enum InlineMethodFlags : uint16_t {
129  kNoInlineMethodFlags = 0x0000,
130  kInlineIntrinsic     = 0x0001,
131  kInlineSpecial       = 0x0002,
132};
133
134// IntrinsicFlags are stored in InlineMethod::d::raw_data
135enum IntrinsicFlags {
136  kIntrinsicFlagNone = 0,
137
138  // kIntrinsicMinMaxInt
139  kIntrinsicFlagMax = kIntrinsicFlagNone,
140  kIntrinsicFlagMin = 1,
141
142  // kIntrinsicIsEmptyOrLength
143  kIntrinsicFlagLength  = kIntrinsicFlagNone,
144  kIntrinsicFlagIsEmpty = kIntrinsicFlagMin,
145
146  // kIntrinsicIndexOf
147  kIntrinsicFlagBase0 = kIntrinsicFlagMin,
148
149  // kIntrinsicUnsafeGet, kIntrinsicUnsafePut, kIntrinsicUnsafeCas
150  kIntrinsicFlagIsLong     = kIntrinsicFlagMin,
151  // kIntrinsicUnsafeGet, kIntrinsicUnsafePut
152  kIntrinsicFlagIsVolatile = 2,
153  // kIntrinsicUnsafePut, kIntrinsicUnsafeCas
154  kIntrinsicFlagIsObject   = 4,
155  // kIntrinsicUnsafePut
156  kIntrinsicFlagIsOrdered  = 8,
157
158  // kIntrinsicDoubleCvt, kIntrinsicFloatCvt.
159  kIntrinsicFlagToFloatingPoint = kIntrinsicFlagMin,
160};
161
162struct InlineIGetIPutData {
163  // The op_variant below is DexMemAccessType but the runtime doesn't know that enumeration.
164  uint16_t op_variant : 3;
165  uint16_t method_is_static : 1;
166  uint16_t object_arg : 4;
167  uint16_t src_arg : 4;  // iput only
168  uint16_t return_arg_plus1 : 4;  // iput only, method argument to return + 1, 0 = return void.
169  uint16_t field_idx;
170  uint32_t is_volatile : 1;
171  uint32_t field_offset : 31;
172};
173static_assert(sizeof(InlineIGetIPutData) == sizeof(uint64_t), "Invalid size of InlineIGetIPutData");
174
175struct InlineReturnArgData {
176  uint16_t arg;
177  uint16_t is_wide : 1;
178  uint16_t is_object : 1;
179  uint16_t reserved : 14;
180  uint32_t reserved2;
181};
182static_assert(sizeof(InlineReturnArgData) == sizeof(uint64_t),
183              "Invalid size of InlineReturnArgData");
184
185struct InlineConstructorData {
186  // There can be up to 3 IPUTs, unused fields are marked with kNoDexIndex16.
187  uint16_t iput0_field_index;
188  uint16_t iput1_field_index;
189  uint16_t iput2_field_index;
190  uint16_t iput0_arg : 4;
191  uint16_t iput1_arg : 4;
192  uint16_t iput2_arg : 4;
193  uint16_t reserved : 4;
194};
195static_assert(sizeof(InlineConstructorData) == sizeof(uint64_t),
196              "Invalid size of InlineConstructorData");
197
198struct InlineMethod {
199  InlineMethodOpcode opcode;
200  InlineMethodFlags flags;
201  union {
202    uint64_t data;
203    InlineIGetIPutData ifield_data;
204    InlineReturnArgData return_data;
205    InlineConstructorData constructor_data;
206  } d;
207};
208
209class InlineMethodAnalyser {
210 public:
211  /**
212   * Analyse method code to determine if the method is a candidate for inlining.
213   * If it is, record the inlining data.
214   *
215   * @param verifier the method verifier holding data about the method to analyse.
216   * @param method placeholder for the inline method data.
217   * @return true if the method is a candidate for inlining, false otherwise.
218   */
219  static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* result)
220      SHARED_REQUIRES(Locks::mutator_lock_);
221  static bool AnalyseMethodCode(ArtMethod* method, InlineMethod* result)
222      SHARED_REQUIRES(Locks::mutator_lock_);
223
224  static constexpr bool IsInstructionIGet(Instruction::Code opcode) {
225    return Instruction::IGET <= opcode && opcode <= Instruction::IGET_SHORT;
226  }
227
228  static constexpr bool IsInstructionIPut(Instruction::Code opcode) {
229    return Instruction::IPUT <= opcode && opcode <= Instruction::IPUT_SHORT;
230  }
231
232  static constexpr uint16_t IGetVariant(Instruction::Code opcode) {
233    return opcode - Instruction::IGET;
234  }
235
236  static constexpr uint16_t IPutVariant(Instruction::Code opcode) {
237    return opcode - Instruction::IPUT;
238  }
239
240  // Determines whether the method is a synthetic accessor (method name starts with "access$").
241  static bool IsSyntheticAccessor(MethodReference ref);
242
243 private:
244  static bool AnalyseMethodCode(const DexFile::CodeItem* code_item,
245                                const MethodReference& method_ref,
246                                bool is_static,
247                                ArtMethod* method,
248                                InlineMethod* result)
249      SHARED_REQUIRES(Locks::mutator_lock_);
250  static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
251  static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
252  static bool AnalyseIGetMethod(const DexFile::CodeItem* code_item,
253                                const MethodReference& method_ref,
254                                bool is_static,
255                                ArtMethod* method,
256                                InlineMethod* result)
257      SHARED_REQUIRES(Locks::mutator_lock_);
258  static bool AnalyseIPutMethod(const DexFile::CodeItem* code_item,
259                                const MethodReference& method_ref,
260                                bool is_static,
261                                ArtMethod* method,
262                                InlineMethod* result)
263      SHARED_REQUIRES(Locks::mutator_lock_);
264
265  // Can we fast path instance field access in a verified accessor?
266  // If yes, computes field's offset and volatility and whether the method is static or not.
267  static bool ComputeSpecialAccessorInfo(ArtMethod* method,
268                                         uint32_t field_idx,
269                                         bool is_put,
270                                         InlineIGetIPutData* result)
271      SHARED_REQUIRES(Locks::mutator_lock_);
272};
273
274}  // namespace art
275
276#endif  // ART_RUNTIME_QUICK_INLINE_METHOD_ANALYSER_H_
277