1 /*
2 * Copyright (C) 2015 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 #include "intrinsics.h"
18
19 #include "art_field-inl.h"
20 #include "art_method-inl.h"
21 #include "class_linker.h"
22 #include "driver/compiler_driver.h"
23 #include "driver/compiler_options.h"
24 #include "invoke_type.h"
25 #include "mirror/dex_cache-inl.h"
26 #include "nodes.h"
27 #include "scoped_thread_state_change-inl.h"
28 #include "thread-inl.h"
29 #include "utils.h"
30
31 namespace art {
32
33 // Function that returns whether an intrinsic is static/direct or virtual.
GetIntrinsicInvokeType(Intrinsics i)34 static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) {
35 switch (i) {
36 case Intrinsics::kNone:
37 return kInterface; // Non-sensical for intrinsic.
38 #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
39 case Intrinsics::k ## Name: \
40 return IsStatic;
41 #include "intrinsics_list.h"
42 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
43 #undef INTRINSICS_LIST
44 #undef OPTIMIZING_INTRINSICS
45 }
46 return kInterface;
47 }
48
49 // Function that returns whether an intrinsic needs an environment or not.
NeedsEnvironmentOrCache(Intrinsics i)50 static inline IntrinsicNeedsEnvironmentOrCache NeedsEnvironmentOrCache(Intrinsics i) {
51 switch (i) {
52 case Intrinsics::kNone:
53 return kNeedsEnvironmentOrCache; // Non-sensical for intrinsic.
54 #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
55 case Intrinsics::k ## Name: \
56 return NeedsEnvironmentOrCache;
57 #include "intrinsics_list.h"
58 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
59 #undef INTRINSICS_LIST
60 #undef OPTIMIZING_INTRINSICS
61 }
62 return kNeedsEnvironmentOrCache;
63 }
64
65 // Function that returns whether an intrinsic has side effects.
GetSideEffects(Intrinsics i)66 static inline IntrinsicSideEffects GetSideEffects(Intrinsics i) {
67 switch (i) {
68 case Intrinsics::kNone:
69 return kAllSideEffects;
70 #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
71 case Intrinsics::k ## Name: \
72 return SideEffects;
73 #include "intrinsics_list.h"
74 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
75 #undef INTRINSICS_LIST
76 #undef OPTIMIZING_INTRINSICS
77 }
78 return kAllSideEffects;
79 }
80
81 // Function that returns whether an intrinsic can throw exceptions.
GetExceptions(Intrinsics i)82 static inline IntrinsicExceptions GetExceptions(Intrinsics i) {
83 switch (i) {
84 case Intrinsics::kNone:
85 return kCanThrow;
86 #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
87 case Intrinsics::k ## Name: \
88 return Exceptions;
89 #include "intrinsics_list.h"
90 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
91 #undef INTRINSICS_LIST
92 #undef OPTIMIZING_INTRINSICS
93 }
94 return kCanThrow;
95 }
96
CheckInvokeType(Intrinsics intrinsic,HInvoke * invoke)97 static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
98 // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual.
99 //
100 // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization
101 // failure occured. We might be in a situation where we have inlined a method that calls an
102 // intrinsic, but that method is in a different dex file on which we do not have a
103 // verified_method that would have helped the compiler driver sharpen the call. In that case,
104 // make sure that the intrinsic is actually for some final method (or in a final class), as
105 // otherwise the intrinsics setup is broken.
106 //
107 // For the last direction, we have intrinsics for virtual functions that will perform a check
108 // inline. If the precise type is known, however, the instruction will be sharpened to an
109 // InvokeStaticOrDirect.
110 InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
111 InvokeType invoke_type = invoke->GetInvokeType();
112 switch (intrinsic_type) {
113 case kStatic:
114 return (invoke_type == kStatic);
115
116 case kDirect:
117 if (invoke_type == kDirect) {
118 return true;
119 }
120 if (invoke_type == kVirtual) {
121 ArtMethod* art_method = invoke->GetResolvedMethod();
122 ScopedObjectAccess soa(Thread::Current());
123 return (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
124 }
125 return false;
126
127 case kVirtual:
128 // Call might be devirtualized.
129 return (invoke_type == kVirtual || invoke_type == kDirect);
130
131 default:
132 return false;
133 }
134 }
135
Run()136 void IntrinsicsRecognizer::Run() {
137 ScopedObjectAccess soa(Thread::Current());
138 for (HBasicBlock* block : graph_->GetReversePostOrder()) {
139 for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
140 inst_it.Advance()) {
141 HInstruction* inst = inst_it.Current();
142 if (inst->IsInvoke()) {
143 HInvoke* invoke = inst->AsInvoke();
144 ArtMethod* art_method = invoke->GetResolvedMethod();
145 if (art_method != nullptr && art_method->IsIntrinsic()) {
146 Intrinsics intrinsic = static_cast<Intrinsics>(art_method->GetIntrinsic());
147 if (!CheckInvokeType(intrinsic, invoke)) {
148 LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
149 << intrinsic << " for "
150 << art_method->PrettyMethod()
151 << invoke->DebugName();
152 } else {
153 invoke->SetIntrinsic(intrinsic,
154 NeedsEnvironmentOrCache(intrinsic),
155 GetSideEffects(intrinsic),
156 GetExceptions(intrinsic));
157 MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
158 }
159 }
160 }
161 }
162 }
163 }
164
operator <<(std::ostream & os,const Intrinsics & intrinsic)165 std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
166 switch (intrinsic) {
167 case Intrinsics::kNone:
168 os << "None";
169 break;
170 #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
171 case Intrinsics::k ## Name: \
172 os << # Name; \
173 break;
174 #include "intrinsics_list.h"
175 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
176 #undef STATIC_INTRINSICS_LIST
177 #undef VIRTUAL_INTRINSICS_LIST
178 #undef OPTIMIZING_INTRINSICS
179 }
180 return os;
181 }
182
ComputeIntegerValueOfLocations(HInvoke * invoke,CodeGenerator * codegen,Location return_location,Location first_argument_location)183 void IntrinsicVisitor::ComputeIntegerValueOfLocations(HInvoke* invoke,
184 CodeGenerator* codegen,
185 Location return_location,
186 Location first_argument_location) {
187 if (Runtime::Current()->IsAotCompiler()) {
188 if (codegen->GetCompilerOptions().IsBootImage() ||
189 codegen->GetCompilerOptions().GetCompilePic()) {
190 // TODO(ngeoffray): Support boot image compilation.
191 return;
192 }
193 }
194
195 IntegerValueOfInfo info = ComputeIntegerValueOfInfo();
196
197 // Most common case is that we have found all we needed (classes are initialized
198 // and in the boot image). Bail if not.
199 if (info.integer_cache == nullptr ||
200 info.integer == nullptr ||
201 info.cache == nullptr ||
202 info.value_offset == 0 ||
203 // low and high cannot be 0, per the spec.
204 info.low == 0 ||
205 info.high == 0) {
206 LOG(INFO) << "Integer.valueOf will not be optimized";
207 return;
208 }
209
210 // The intrinsic will call if it needs to allocate a j.l.Integer.
211 LocationSummary* locations = new (invoke->GetBlock()->GetGraph()->GetArena()) LocationSummary(
212 invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
213 if (!invoke->InputAt(0)->IsConstant()) {
214 locations->SetInAt(0, Location::RequiresRegister());
215 }
216 locations->AddTemp(first_argument_location);
217 locations->SetOut(return_location);
218 }
219
ComputeIntegerValueOfInfo()220 IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo() {
221 // Note that we could cache all of the data looked up here. but there's no good
222 // location for it. We don't want to add it to WellKnownClasses, to avoid creating global
223 // jni values. Adding it as state to the compiler singleton seems like wrong
224 // separation of concerns.
225 // The need for this data should be pretty rare though.
226
227 // The most common case is that the classes are in the boot image and initialized,
228 // which is easy to generate code for. We bail if not.
229 Thread* self = Thread::Current();
230 ScopedObjectAccess soa(self);
231 Runtime* runtime = Runtime::Current();
232 ClassLinker* class_linker = runtime->GetClassLinker();
233 gc::Heap* heap = runtime->GetHeap();
234 IntegerValueOfInfo info;
235 info.integer_cache = class_linker->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;");
236 if (info.integer_cache == nullptr) {
237 self->ClearException();
238 return info;
239 }
240 if (!heap->ObjectIsInBootImageSpace(info.integer_cache) || !info.integer_cache->IsInitialized()) {
241 // Optimization only works if the class is initialized and in the boot image.
242 return info;
243 }
244 info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;");
245 if (info.integer == nullptr) {
246 self->ClearException();
247 return info;
248 }
249 if (!heap->ObjectIsInBootImageSpace(info.integer) || !info.integer->IsInitialized()) {
250 // Optimization only works if the class is initialized and in the boot image.
251 return info;
252 }
253
254 ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
255 if (field == nullptr) {
256 return info;
257 }
258 info.cache = static_cast<mirror::ObjectArray<mirror::Object>*>(
259 field->GetObject(info.integer_cache).Ptr());
260 if (info.cache == nullptr) {
261 return info;
262 }
263
264 if (!heap->ObjectIsInBootImageSpace(info.cache)) {
265 // Optimization only works if the object is in the boot image.
266 return info;
267 }
268
269 field = info.integer->FindDeclaredInstanceField("value", "I");
270 if (field == nullptr) {
271 return info;
272 }
273 info.value_offset = field->GetOffset().Int32Value();
274
275 field = info.integer_cache->FindDeclaredStaticField("low", "I");
276 if (field == nullptr) {
277 return info;
278 }
279 info.low = field->GetInt(info.integer_cache);
280
281 field = info.integer_cache->FindDeclaredStaticField("high", "I");
282 if (field == nullptr) {
283 return info;
284 }
285 info.high = field->GetInt(info.integer_cache);
286
287 DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1);
288 return info;
289 }
290
291 } // namespace art
292