blob: f0c91f3ba00c95830508c68b3d95d55e4c4c4f0e [file] [log] [blame]
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001/*
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
Andreas Gampea1d2f952017-04-20 22:53:58 -070019#include "art_field-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080020#include "art_method-inl.h"
David Sehrc431b9d2018-03-02 12:01:51 -080021#include "base/utils.h"
Andreas Gampebfb5ba92015-09-01 15:45:02 +000022#include "class_linker.h"
David Sehr8c0961f2018-01-23 16:11:38 -080023#include "dex/invoke_type.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080024#include "driver/compiler_driver.h"
Nicolas Geoffray331605a2017-03-01 11:01:41 +000025#include "driver/compiler_options.h"
Andreas Gampebfb5ba92015-09-01 15:45:02 +000026#include "mirror/dex_cache-inl.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080027#include "nodes.h"
Mathieu Chartier0795f232016-09-27 18:43:30 -070028#include "scoped_thread_state_change-inl.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070029#include "thread-current-inl.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080030
31namespace art {
32
Orion Hodsoncfcc9cf2017-09-29 15:07:27 +010033// Check that intrinsic enum values fit within space set aside in ArtMethod modifier flags.
34#define CHECK_INTRINSICS_ENUM_VALUES(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
35 static_assert( \
36 static_cast<uint32_t>(Intrinsics::k ## Name) <= (kAccIntrinsicBits >> CTZ(kAccIntrinsicBits)), \
Orion Hodson4a4610a2017-09-28 16:57:55 +010037 "Instrinsics enumeration space overflow.");
Orion Hodsoncfcc9cf2017-09-29 15:07:27 +010038#include "intrinsics_list.h"
39 INTRINSICS_LIST(CHECK_INTRINSICS_ENUM_VALUES)
40#undef INTRINSICS_LIST
41#undef CHECK_INTRINSICS_ENUM_VALUES
42
Andreas Gampe71fb52f2014-12-29 17:43:08 -080043// Function that returns whether an intrinsic is static/direct or virtual.
44static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) {
45 switch (i) {
46 case Intrinsics::kNone:
47 return kInterface; // Non-sensical for intrinsic.
Nicolas Geoffray762869d2016-07-15 15:28:35 +010048#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080049 case Intrinsics::k ## Name: \
Andreas Gampe71fb52f2014-12-29 17:43:08 -080050 return IsStatic;
51#include "intrinsics_list.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070052 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
Andreas Gampe71fb52f2014-12-29 17:43:08 -080053#undef INTRINSICS_LIST
54#undef OPTIMIZING_INTRINSICS
55 }
56 return kInterface;
57}
58
agicsaki57b81ec2015-08-11 17:39:37 -070059// Function that returns whether an intrinsic needs an environment or not.
Agi Csaki05f20562015-08-19 14:58:14 -070060static inline IntrinsicNeedsEnvironmentOrCache NeedsEnvironmentOrCache(Intrinsics i) {
agicsaki57b81ec2015-08-11 17:39:37 -070061 switch (i) {
62 case Intrinsics::kNone:
Agi Csaki05f20562015-08-19 14:58:14 -070063 return kNeedsEnvironmentOrCache; // Non-sensical for intrinsic.
Nicolas Geoffray762869d2016-07-15 15:28:35 +010064#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080065 case Intrinsics::k ## Name: \
Agi Csaki05f20562015-08-19 14:58:14 -070066 return NeedsEnvironmentOrCache;
agicsaki57b81ec2015-08-11 17:39:37 -070067#include "intrinsics_list.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070068 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
agicsaki57b81ec2015-08-11 17:39:37 -070069#undef INTRINSICS_LIST
70#undef OPTIMIZING_INTRINSICS
71 }
Agi Csaki05f20562015-08-19 14:58:14 -070072 return kNeedsEnvironmentOrCache;
agicsaki57b81ec2015-08-11 17:39:37 -070073}
Andreas Gampe71fb52f2014-12-29 17:43:08 -080074
Aart Bik5d75afe2015-12-14 11:57:01 -080075// Function that returns whether an intrinsic has side effects.
76static inline IntrinsicSideEffects GetSideEffects(Intrinsics i) {
77 switch (i) {
78 case Intrinsics::kNone:
79 return kAllSideEffects;
Nicolas Geoffray762869d2016-07-15 15:28:35 +010080#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080081 case Intrinsics::k ## Name: \
82 return SideEffects;
83#include "intrinsics_list.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070084 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
Aart Bik5d75afe2015-12-14 11:57:01 -080085#undef INTRINSICS_LIST
86#undef OPTIMIZING_INTRINSICS
87 }
88 return kAllSideEffects;
89}
90
91// Function that returns whether an intrinsic can throw exceptions.
92static inline IntrinsicExceptions GetExceptions(Intrinsics i) {
93 switch (i) {
94 case Intrinsics::kNone:
95 return kCanThrow;
Nicolas Geoffray762869d2016-07-15 15:28:35 +010096#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080097 case Intrinsics::k ## Name: \
98 return Exceptions;
99#include "intrinsics_list.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -0700100 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
Aart Bik5d75afe2015-12-14 11:57:01 -0800101#undef INTRINSICS_LIST
102#undef OPTIMIZING_INTRINSICS
103 }
104 return kCanThrow;
105}
106
Orion Hodson4c71d002017-11-29 11:03:25 +0000107static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke)
108 REQUIRES_SHARED(Locks::mutator_lock_) {
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000109 // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual.
110 //
111 // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization
112 // failure occured. We might be in a situation where we have inlined a method that calls an
113 // intrinsic, but that method is in a different dex file on which we do not have a
114 // verified_method that would have helped the compiler driver sharpen the call. In that case,
115 // make sure that the intrinsic is actually for some final method (or in a final class), as
116 // otherwise the intrinsics setup is broken.
117 //
118 // For the last direction, we have intrinsics for virtual functions that will perform a check
119 // inline. If the precise type is known, however, the instruction will be sharpened to an
120 // InvokeStaticOrDirect.
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800121 InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
Nicolas Geoffray5e4e11e2016-09-22 13:17:41 +0100122 InvokeType invoke_type = invoke->GetInvokeType();
Orion Hodsoncfcc9cf2017-09-29 15:07:27 +0100123
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800124 switch (intrinsic_type) {
125 case kStatic:
126 return (invoke_type == kStatic);
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000127
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800128 case kDirect:
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000129 if (invoke_type == kDirect) {
130 return true;
131 }
132 if (invoke_type == kVirtual) {
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100133 ArtMethod* art_method = invoke->GetResolvedMethod();
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100134 return (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000135 }
136 return false;
137
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800138 case kVirtual:
139 // Call might be devirtualized.
Mingyao Yang6b1aebe2017-11-27 15:39:04 -0800140 return (invoke_type == kVirtual || invoke_type == kDirect || invoke_type == kInterface);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800141
Orion Hodsonb1b52062017-11-27 11:51:42 +0000142 case kSuper:
143 case kInterface:
144 case kPolymorphic:
Orion Hodson4c8e12e2018-05-18 08:33:20 +0100145 case kCustom:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800146 return false;
147 }
Orion Hodsonb1b52062017-11-27 11:51:42 +0000148 LOG(FATAL) << "Unknown intrinsic invoke type: " << intrinsic_type;
149 UNREACHABLE();
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800150}
151
Mingyao Yang6b1aebe2017-11-27 15:39:04 -0800152bool IntrinsicsRecognizer::Recognize(HInvoke* invoke,
153 ArtMethod* art_method,
154 /*out*/ bool* wrong_invoke_type) {
155 if (art_method == nullptr) {
156 art_method = invoke->GetResolvedMethod();
157 }
Orion Hodsonb1b52062017-11-27 11:51:42 +0000158 *wrong_invoke_type = false;
159 if (art_method == nullptr || !art_method->IsIntrinsic()) {
160 return false;
161 }
162
Orion Hodson4c71d002017-11-29 11:03:25 +0000163 // TODO: b/65872996 The intent is that polymorphic signature methods should
164 // be compiler intrinsics. At present, they are only interpreter intrinsics.
165 if (art_method->IsPolymorphicSignature()) {
166 return false;
Aart Bikf0010dd2017-11-21 16:31:53 -0800167 }
Orion Hodsonb1b52062017-11-27 11:51:42 +0000168
169 Intrinsics intrinsic = static_cast<Intrinsics>(art_method->GetIntrinsic());
170 if (CheckInvokeType(intrinsic, invoke) == false) {
171 *wrong_invoke_type = true;
172 return false;
173 }
174
175 invoke->SetIntrinsic(intrinsic,
176 NeedsEnvironmentOrCache(intrinsic),
177 GetSideEffects(intrinsic),
178 GetExceptions(intrinsic));
179 return true;
Aart Bikf0010dd2017-11-21 16:31:53 -0800180}
181
Aart Bik24773202018-04-26 10:28:51 -0700182bool IntrinsicsRecognizer::Run() {
183 bool didRecognize = false;
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100184 ScopedObjectAccess soa(Thread::Current());
Vladimir Marko2c45bc92016-10-25 16:54:12 +0100185 for (HBasicBlock* block : graph_->GetReversePostOrder()) {
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800186 for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
187 inst_it.Advance()) {
188 HInstruction* inst = inst_it.Current();
189 if (inst->IsInvoke()) {
Aart Bikf0010dd2017-11-21 16:31:53 -0800190 bool wrong_invoke_type = false;
Mingyao Yang6b1aebe2017-11-27 15:39:04 -0800191 if (Recognize(inst->AsInvoke(), /* art_method */ nullptr, &wrong_invoke_type)) {
Aart Bik24773202018-04-26 10:28:51 -0700192 didRecognize = true;
Aart Bikf0010dd2017-11-21 16:31:53 -0800193 MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized);
194 } else if (wrong_invoke_type) {
195 LOG(WARNING)
196 << "Found an intrinsic with unexpected invoke type: "
197 << inst->AsInvoke()->GetResolvedMethod()->PrettyMethod() << " "
198 << inst->DebugName();
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800199 }
200 }
201 }
202 }
Aart Bik24773202018-04-26 10:28:51 -0700203 return didRecognize;
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800204}
205
206std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
207 switch (intrinsic) {
208 case Intrinsics::kNone:
David Brazdil109c89a2015-07-31 17:10:43 +0100209 os << "None";
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800210 break;
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100211#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800212 case Intrinsics::k ## Name: \
213 os << # Name; \
214 break;
215#include "intrinsics_list.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -0700216 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800217#undef STATIC_INTRINSICS_LIST
218#undef VIRTUAL_INTRINSICS_LIST
219#undef OPTIMIZING_INTRINSICS
220 }
221 return os;
222}
223
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000224void IntrinsicVisitor::ComputeIntegerValueOfLocations(HInvoke* invoke,
225 CodeGenerator* codegen,
226 Location return_location,
227 Location first_argument_location) {
228 if (Runtime::Current()->IsAotCompiler()) {
229 if (codegen->GetCompilerOptions().IsBootImage() ||
230 codegen->GetCompilerOptions().GetCompilePic()) {
231 // TODO(ngeoffray): Support boot image compilation.
232 return;
233 }
234 }
235
236 IntegerValueOfInfo info = ComputeIntegerValueOfInfo();
237
238 // Most common case is that we have found all we needed (classes are initialized
239 // and in the boot image). Bail if not.
240 if (info.integer_cache == nullptr ||
241 info.integer == nullptr ||
242 info.cache == nullptr ||
243 info.value_offset == 0 ||
244 // low and high cannot be 0, per the spec.
245 info.low == 0 ||
246 info.high == 0) {
247 LOG(INFO) << "Integer.valueOf will not be optimized";
248 return;
249 }
250
251 // The intrinsic will call if it needs to allocate a j.l.Integer.
Vladimir Markoca6fff82017-10-03 14:49:14 +0100252 LocationSummary* locations = new (invoke->GetBlock()->GetGraph()->GetAllocator()) LocationSummary(
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000253 invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
254 if (!invoke->InputAt(0)->IsConstant()) {
255 locations->SetInAt(0, Location::RequiresRegister());
256 }
257 locations->AddTemp(first_argument_location);
258 locations->SetOut(return_location);
259}
260
261IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo() {
262 // Note that we could cache all of the data looked up here. but there's no good
263 // location for it. We don't want to add it to WellKnownClasses, to avoid creating global
264 // jni values. Adding it as state to the compiler singleton seems like wrong
265 // separation of concerns.
266 // The need for this data should be pretty rare though.
267
268 // The most common case is that the classes are in the boot image and initialized,
269 // which is easy to generate code for. We bail if not.
270 Thread* self = Thread::Current();
271 ScopedObjectAccess soa(self);
272 Runtime* runtime = Runtime::Current();
273 ClassLinker* class_linker = runtime->GetClassLinker();
274 gc::Heap* heap = runtime->GetHeap();
275 IntegerValueOfInfo info;
Vladimir Markof75613c2018-06-05 12:51:04 +0100276 info.integer_cache = class_linker->LookupClass(self,
277 "Ljava/lang/Integer$IntegerCache;",
278 /* class_loader */ nullptr).Ptr();
279 if (info.integer_cache == nullptr || !info.integer_cache->IsInitialized()) {
280 // Optimization only works if the class is initialized.
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000281 return info;
282 }
Vladimir Markof75613c2018-06-05 12:51:04 +0100283 if (!heap->ObjectIsInBootImageSpace(info.integer_cache)) {
284 // Optimization only works if the class is in the boot image.
285 // TODO: Implement the intrinsic for boot image compilation.
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000286 return info;
287 }
Vladimir Markof75613c2018-06-05 12:51:04 +0100288 info.integer =
289 class_linker->LookupClass(self, "Ljava/lang/Integer;", /* class_loader */ nullptr).Ptr();
290 DCHECK(info.integer != nullptr);
291 DCHECK(info.integer->IsInitialized()); // Must be initialized since IntegerCache is initialized.
292 if (!heap->ObjectIsInBootImageSpace(info.integer)) {
293 // Optimization only works if the class is in the boot image.
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000294 return info;
295 }
296
297 ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
Vladimir Markof75613c2018-06-05 12:51:04 +0100298 CHECK(field != nullptr);
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000299 info.cache = static_cast<mirror::ObjectArray<mirror::Object>*>(
300 field->GetObject(info.integer_cache).Ptr());
301 if (info.cache == nullptr) {
Vladimir Markof75613c2018-06-05 12:51:04 +0100302 return info; // Did someone mess up the IntegerCache using reflection?
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000303 }
304
305 if (!heap->ObjectIsInBootImageSpace(info.cache)) {
306 // Optimization only works if the object is in the boot image.
307 return info;
308 }
309
310 field = info.integer->FindDeclaredInstanceField("value", "I");
Vladimir Markof75613c2018-06-05 12:51:04 +0100311 CHECK(field != nullptr);
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000312 info.value_offset = field->GetOffset().Int32Value();
313
314 field = info.integer_cache->FindDeclaredStaticField("low", "I");
Vladimir Markof75613c2018-06-05 12:51:04 +0100315 CHECK(field != nullptr);
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000316 info.low = field->GetInt(info.integer_cache);
317
318 field = info.integer_cache->FindDeclaredStaticField("high", "I");
Vladimir Markof75613c2018-06-05 12:51:04 +0100319 CHECK(field != nullptr);
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000320 info.high = field->GetInt(info.integer_cache);
321
322 DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1);
323 return info;
324}
325
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800326} // namespace art