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   kIntrinsicReverseBits,
41   kIntrinsicReverseBytes,
42   kIntrinsicAbsInt,
43   kIntrinsicAbsLong,
44   kIntrinsicAbsFloat,
45   kIntrinsicAbsDouble,
46   kIntrinsicMinMaxInt,
47   kIntrinsicMinMaxLong,
48   kIntrinsicMinMaxFloat,
49   kIntrinsicMinMaxDouble,
50   kIntrinsicSqrt,
51   kIntrinsicCeil,
52   kIntrinsicFloor,
53   kIntrinsicRint,
54   kIntrinsicRoundFloat,
55   kIntrinsicRoundDouble,
56   kIntrinsicReferenceGetReferent,
57   kIntrinsicCharAt,
58   kIntrinsicCompareTo,
59   kIntrinsicGetCharsNoCheck,
60   kIntrinsicIsEmptyOrLength,
61   kIntrinsicIndexOf,
62   kIntrinsicNewStringFromBytes,
63   kIntrinsicNewStringFromChars,
64   kIntrinsicNewStringFromString,
65   kIntrinsicCurrentThread,
66   kIntrinsicPeek,
67   kIntrinsicPoke,
68   kIntrinsicCas,
69   kIntrinsicUnsafeGet,
70   kIntrinsicUnsafePut,
71   kIntrinsicSystemArrayCopyCharArray,
72 
73   kInlineOpNop,
74   kInlineOpReturnArg,
75   kInlineOpNonWideConst,
76   kInlineOpIGet,
77   kInlineOpIPut,
78   kInlineStringInit,
79 };
80 std::ostream& operator<<(std::ostream& os, const InlineMethodOpcode& rhs);
81 
82 enum InlineMethodFlags : uint16_t {
83   kNoInlineMethodFlags = 0x0000,
84   kInlineIntrinsic     = 0x0001,
85   kInlineSpecial       = 0x0002,
86 };
87 
88 // IntrinsicFlags are stored in InlineMethod::d::raw_data
89 enum IntrinsicFlags {
90   kIntrinsicFlagNone = 0,
91 
92   // kIntrinsicMinMaxInt
93   kIntrinsicFlagMax = kIntrinsicFlagNone,
94   kIntrinsicFlagMin = 1,
95 
96   // kIntrinsicIsEmptyOrLength
97   kIntrinsicFlagLength  = kIntrinsicFlagNone,
98   kIntrinsicFlagIsEmpty = kIntrinsicFlagMin,
99 
100   // kIntrinsicIndexOf
101   kIntrinsicFlagBase0 = kIntrinsicFlagMin,
102 
103   // kIntrinsicUnsafeGet, kIntrinsicUnsafePut, kIntrinsicUnsafeCas
104   kIntrinsicFlagIsLong     = kIntrinsicFlagMin,
105   // kIntrinsicUnsafeGet, kIntrinsicUnsafePut
106   kIntrinsicFlagIsVolatile = 2,
107   // kIntrinsicUnsafePut, kIntrinsicUnsafeCas
108   kIntrinsicFlagIsObject   = 4,
109   // kIntrinsicUnsafePut
110   kIntrinsicFlagIsOrdered  = 8,
111 
112   // kIntrinsicDoubleCvt, kIntrinsicFloatCvt.
113   kIntrinsicFlagToFloatingPoint = kIntrinsicFlagMin,
114 };
115 
116 struct InlineIGetIPutData {
117   // The op_variant below is DexMemAccessType but the runtime doesn't know that enumeration.
118   uint16_t op_variant : 3;
119   uint16_t method_is_static : 1;
120   uint16_t object_arg : 4;
121   uint16_t src_arg : 4;  // iput only
122   uint16_t return_arg_plus1 : 4;  // iput only, method argument to return + 1, 0 = return void.
123   uint16_t field_idx;
124   uint32_t is_volatile : 1;
125   uint32_t field_offset : 31;
126 };
127 static_assert(sizeof(InlineIGetIPutData) == sizeof(uint64_t), "Invalid size of InlineIGetIPutData");
128 
129 struct InlineReturnArgData {
130   uint16_t arg;
131   uint16_t is_wide : 1;
132   uint16_t is_object : 1;
133   uint16_t reserved : 14;
134   uint32_t reserved2;
135 };
136 static_assert(sizeof(InlineReturnArgData) == sizeof(uint64_t),
137               "Invalid size of InlineReturnArgData");
138 
139 struct InlineMethod {
140   InlineMethodOpcode opcode;
141   InlineMethodFlags flags;
142   union {
143     uint64_t data;
144     InlineIGetIPutData ifield_data;
145     InlineReturnArgData return_data;
146   } d;
147 };
148 
149 class InlineMethodAnalyser {
150  public:
151   /**
152    * Analyse method code to determine if the method is a candidate for inlining.
153    * If it is, record the inlining data.
154    *
155    * @param verifier the method verifier holding data about the method to analyse.
156    * @param method placeholder for the inline method data.
157    * @return true if the method is a candidate for inlining, false otherwise.
158    */
159   static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* method)
160       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
161 
IsInstructionIGet(Instruction::Code opcode)162   static constexpr bool IsInstructionIGet(Instruction::Code opcode) {
163     return Instruction::IGET <= opcode && opcode <= Instruction::IGET_SHORT;
164   }
165 
IsInstructionIPut(Instruction::Code opcode)166   static constexpr bool IsInstructionIPut(Instruction::Code opcode) {
167     return Instruction::IPUT <= opcode && opcode <= Instruction::IPUT_SHORT;
168   }
169 
IGetVariant(Instruction::Code opcode)170   static constexpr uint16_t IGetVariant(Instruction::Code opcode) {
171     return opcode - Instruction::IGET;
172   }
173 
IPutVariant(Instruction::Code opcode)174   static constexpr uint16_t IPutVariant(Instruction::Code opcode) {
175     return opcode - Instruction::IPUT;
176   }
177 
178   // Determines whether the method is a synthetic accessor (method name starts with "access$").
179   static bool IsSyntheticAccessor(MethodReference ref);
180 
181  private:
182   static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
183   static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
184   static bool AnalyseIGetMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
185       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
186   static bool AnalyseIPutMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
187       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
188 
189   // Can we fast path instance field access in a verified accessor?
190   // If yes, computes field's offset and volatility and whether the method is static or not.
191   static bool ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
192                                          verifier::MethodVerifier* verifier,
193                                          InlineIGetIPutData* result)
194       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
195 };
196 
197 }  // namespace art
198 
199 #endif  // ART_RUNTIME_QUICK_INLINE_METHOD_ANALYSER_H_
200