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 
31 namespace art {
32 
33 namespace verifier {
34 class MethodVerifier;
35 }  // namespace verifier
36 
37 enum 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 };
126 std::ostream& operator<<(std::ostream& os, const InlineMethodOpcode& rhs);
127 
128 enum InlineMethodFlags : uint16_t {
129   kNoInlineMethodFlags = 0x0000,
130   kInlineIntrinsic     = 0x0001,
131   kInlineSpecial       = 0x0002,
132 };
133 
134 // IntrinsicFlags are stored in InlineMethod::d::raw_data
135 enum 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 
162 struct 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 };
173 static_assert(sizeof(InlineIGetIPutData) == sizeof(uint64_t), "Invalid size of InlineIGetIPutData");
174 
175 struct 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 };
182 static_assert(sizeof(InlineReturnArgData) == sizeof(uint64_t),
183               "Invalid size of InlineReturnArgData");
184 
185 struct 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 };
195 static_assert(sizeof(InlineConstructorData) == sizeof(uint64_t),
196               "Invalid size of InlineConstructorData");
197 
198 struct 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 
209 class 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 
IsInstructionIGet(Instruction::Code opcode)224   static constexpr bool IsInstructionIGet(Instruction::Code opcode) {
225     return Instruction::IGET <= opcode && opcode <= Instruction::IGET_SHORT;
226   }
227 
IsInstructionIPut(Instruction::Code opcode)228   static constexpr bool IsInstructionIPut(Instruction::Code opcode) {
229     return Instruction::IPUT <= opcode && opcode <= Instruction::IPUT_SHORT;
230   }
231 
IGetVariant(Instruction::Code opcode)232   static constexpr uint16_t IGetVariant(Instruction::Code opcode) {
233     return opcode - Instruction::IGET;
234   }
235 
IPutVariant(Instruction::Code opcode)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