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