blob: 11725f43c3380b3f52f7e0054202014b976c0673 [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"
Andreas Gampebfb5ba92015-09-01 15:45:02 +000021#include "class_linker.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080022#include "driver/compiler_driver.h"
Nicolas Geoffray331605a2017-03-01 11:01:41 +000023#include "driver/compiler_options.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080024#include "invoke_type.h"
Andreas Gampebfb5ba92015-09-01 15:45:02 +000025#include "mirror/dex_cache-inl.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080026#include "nodes.h"
Mathieu Chartier0795f232016-09-27 18:43:30 -070027#include "scoped_thread_state_change-inl.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070028#include "thread-current-inl.h"
Vladimir Marko80afd022015-05-19 18:08:00 +010029#include "utils.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080030
31namespace art {
32
33// Function that returns whether an intrinsic is static/direct or virtual.
34static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) {
35 switch (i) {
36 case Intrinsics::kNone:
37 return kInterface; // Non-sensical for intrinsic.
Nicolas Geoffray762869d2016-07-15 15:28:35 +010038#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080039 case Intrinsics::k ## Name: \
Andreas Gampe71fb52f2014-12-29 17:43:08 -080040 return IsStatic;
41#include "intrinsics_list.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070042 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
Andreas Gampe71fb52f2014-12-29 17:43:08 -080043#undef INTRINSICS_LIST
44#undef OPTIMIZING_INTRINSICS
45 }
46 return kInterface;
47}
48
agicsaki57b81ec2015-08-11 17:39:37 -070049// Function that returns whether an intrinsic needs an environment or not.
Agi Csaki05f20562015-08-19 14:58:14 -070050static inline IntrinsicNeedsEnvironmentOrCache NeedsEnvironmentOrCache(Intrinsics i) {
agicsaki57b81ec2015-08-11 17:39:37 -070051 switch (i) {
52 case Intrinsics::kNone:
Agi Csaki05f20562015-08-19 14:58:14 -070053 return kNeedsEnvironmentOrCache; // Non-sensical for intrinsic.
Nicolas Geoffray762869d2016-07-15 15:28:35 +010054#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080055 case Intrinsics::k ## Name: \
Agi Csaki05f20562015-08-19 14:58:14 -070056 return NeedsEnvironmentOrCache;
agicsaki57b81ec2015-08-11 17:39:37 -070057#include "intrinsics_list.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070058 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
agicsaki57b81ec2015-08-11 17:39:37 -070059#undef INTRINSICS_LIST
60#undef OPTIMIZING_INTRINSICS
61 }
Agi Csaki05f20562015-08-19 14:58:14 -070062 return kNeedsEnvironmentOrCache;
agicsaki57b81ec2015-08-11 17:39:37 -070063}
Andreas Gampe71fb52f2014-12-29 17:43:08 -080064
Aart Bik5d75afe2015-12-14 11:57:01 -080065// Function that returns whether an intrinsic has side effects.
66static inline IntrinsicSideEffects GetSideEffects(Intrinsics i) {
67 switch (i) {
68 case Intrinsics::kNone:
69 return kAllSideEffects;
Nicolas Geoffray762869d2016-07-15 15:28:35 +010070#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080071 case Intrinsics::k ## Name: \
72 return SideEffects;
73#include "intrinsics_list.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070074 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
Aart Bik5d75afe2015-12-14 11:57:01 -080075#undef INTRINSICS_LIST
76#undef OPTIMIZING_INTRINSICS
77 }
78 return kAllSideEffects;
79}
80
81// Function that returns whether an intrinsic can throw exceptions.
82static inline IntrinsicExceptions GetExceptions(Intrinsics i) {
83 switch (i) {
84 case Intrinsics::kNone:
85 return kCanThrow;
Nicolas Geoffray762869d2016-07-15 15:28:35 +010086#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080087 case Intrinsics::k ## Name: \
88 return Exceptions;
89#include "intrinsics_list.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070090 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
Aart Bik5d75afe2015-12-14 11:57:01 -080091#undef INTRINSICS_LIST
92#undef OPTIMIZING_INTRINSICS
93 }
94 return kCanThrow;
95}
96
Nicolas Geoffray762869d2016-07-15 15:28:35 +010097static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
Andreas Gampebfb5ba92015-09-01 15:45:02 +000098 // 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.
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800110 InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
Nicolas Geoffray5e4e11e2016-09-22 13:17:41 +0100111 InvokeType invoke_type = invoke->GetInvokeType();
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800112 switch (intrinsic_type) {
113 case kStatic:
114 return (invoke_type == kStatic);
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000115
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800116 case kDirect:
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000117 if (invoke_type == kDirect) {
118 return true;
119 }
120 if (invoke_type == kVirtual) {
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100121 ArtMethod* art_method = invoke->GetResolvedMethod();
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000122 ScopedObjectAccess soa(Thread::Current());
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100123 return (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000124 }
125 return false;
126
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800127 case kVirtual:
128 // Call might be devirtualized.
129 return (invoke_type == kVirtual || invoke_type == kDirect);
130
131 default:
132 return false;
133 }
134}
135
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800136void IntrinsicsRecognizer::Run() {
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100137 ScopedObjectAccess soa(Thread::Current());
Vladimir Marko2c45bc92016-10-25 16:54:12 +0100138 for (HBasicBlock* block : graph_->GetReversePostOrder()) {
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800139 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();
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100144 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: "
Mathieu Chartierf044c222017-05-31 15:27:54 -0700149 << static_cast<uint32_t>(intrinsic) << " for "
Nicolas Geoffray5d37c152017-01-12 13:25:19 +0000150 << art_method->PrettyMethod()
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100151 << invoke->DebugName();
152 } else {
153 invoke->SetIntrinsic(intrinsic,
154 NeedsEnvironmentOrCache(intrinsic),
155 GetSideEffects(intrinsic),
156 GetExceptions(intrinsic));
Igor Murashkin1e065a52017-08-09 13:20:34 -0700157 MaybeRecordStat(stats_,
158 MethodCompilationStat::kIntrinsicRecognized);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800159 }
160 }
161 }
162 }
163 }
164}
165
166std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
167 switch (intrinsic) {
168 case Intrinsics::kNone:
David Brazdil109c89a2015-07-31 17:10:43 +0100169 os << "None";
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800170 break;
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100171#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800172 case Intrinsics::k ## Name: \
173 os << # Name; \
174 break;
175#include "intrinsics_list.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -0700176 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800177#undef STATIC_INTRINSICS_LIST
178#undef VIRTUAL_INTRINSICS_LIST
179#undef OPTIMIZING_INTRINSICS
180 }
181 return os;
182}
183
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000184void IntrinsicVisitor::ComputeIntegerValueOfLocations(HInvoke* invoke,
185 CodeGenerator* codegen,
186 Location return_location,
187 Location first_argument_location) {
188 if (Runtime::Current()->IsAotCompiler()) {
189 if (codegen->GetCompilerOptions().IsBootImage() ||
190 codegen->GetCompilerOptions().GetCompilePic()) {
191 // TODO(ngeoffray): Support boot image compilation.
192 return;
193 }
194 }
195
196 IntegerValueOfInfo info = ComputeIntegerValueOfInfo();
197
198 // Most common case is that we have found all we needed (classes are initialized
199 // and in the boot image). Bail if not.
200 if (info.integer_cache == nullptr ||
201 info.integer == nullptr ||
202 info.cache == nullptr ||
203 info.value_offset == 0 ||
204 // low and high cannot be 0, per the spec.
205 info.low == 0 ||
206 info.high == 0) {
207 LOG(INFO) << "Integer.valueOf will not be optimized";
208 return;
209 }
210
211 // The intrinsic will call if it needs to allocate a j.l.Integer.
212 LocationSummary* locations = new (invoke->GetBlock()->GetGraph()->GetArena()) LocationSummary(
213 invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
214 if (!invoke->InputAt(0)->IsConstant()) {
215 locations->SetInAt(0, Location::RequiresRegister());
216 }
217 locations->AddTemp(first_argument_location);
218 locations->SetOut(return_location);
219}
220
221IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo() {
222 // Note that we could cache all of the data looked up here. but there's no good
223 // location for it. We don't want to add it to WellKnownClasses, to avoid creating global
224 // jni values. Adding it as state to the compiler singleton seems like wrong
225 // separation of concerns.
226 // The need for this data should be pretty rare though.
227
228 // The most common case is that the classes are in the boot image and initialized,
229 // which is easy to generate code for. We bail if not.
230 Thread* self = Thread::Current();
231 ScopedObjectAccess soa(self);
232 Runtime* runtime = Runtime::Current();
233 ClassLinker* class_linker = runtime->GetClassLinker();
234 gc::Heap* heap = runtime->GetHeap();
235 IntegerValueOfInfo info;
236 info.integer_cache = class_linker->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;");
237 if (info.integer_cache == nullptr) {
238 self->ClearException();
239 return info;
240 }
241 if (!heap->ObjectIsInBootImageSpace(info.integer_cache) || !info.integer_cache->IsInitialized()) {
242 // Optimization only works if the class is initialized and in the boot image.
243 return info;
244 }
245 info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;");
246 if (info.integer == nullptr) {
247 self->ClearException();
248 return info;
249 }
250 if (!heap->ObjectIsInBootImageSpace(info.integer) || !info.integer->IsInitialized()) {
251 // Optimization only works if the class is initialized and in the boot image.
252 return info;
253 }
254
255 ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
256 if (field == nullptr) {
257 return info;
258 }
259 info.cache = static_cast<mirror::ObjectArray<mirror::Object>*>(
260 field->GetObject(info.integer_cache).Ptr());
261 if (info.cache == nullptr) {
262 return info;
263 }
264
265 if (!heap->ObjectIsInBootImageSpace(info.cache)) {
266 // Optimization only works if the object is in the boot image.
267 return info;
268 }
269
270 field = info.integer->FindDeclaredInstanceField("value", "I");
271 if (field == nullptr) {
272 return info;
273 }
274 info.value_offset = field->GetOffset().Int32Value();
275
276 field = info.integer_cache->FindDeclaredStaticField("low", "I");
277 if (field == nullptr) {
278 return info;
279 }
280 info.low = field->GetInt(info.integer_cache);
281
282 field = info.integer_cache->FindDeclaredStaticField("high", "I");
283 if (field == nullptr) {
284 return info;
285 }
286 info.high = field->GetInt(info.integer_cache);
287
288 DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1);
289 return info;
290}
291
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800292} // namespace art