blob: 8df80adc9f071d132748094526a2550ae20158b4 [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 Gampebfb5ba92015-09-01 15:45:02 +000019#include "art_method.h"
20#include "class_linker.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080021#include "driver/compiler_driver.h"
Nicolas Geoffray331605a2017-03-01 11:01:41 +000022#include "driver/compiler_options.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080023#include "invoke_type.h"
Andreas Gampebfb5ba92015-09-01 15:45:02 +000024#include "mirror/dex_cache-inl.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080025#include "nodes.h"
Mathieu Chartier0795f232016-09-27 18:43:30 -070026#include "scoped_thread_state_change-inl.h"
Andreas Gampebfb5ba92015-09-01 15:45:02 +000027#include "thread-inl.h"
Vladimir Marko80afd022015-05-19 18:08:00 +010028#include "utils.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080029
30namespace art {
31
32// Function that returns whether an intrinsic is static/direct or virtual.
33static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) {
34 switch (i) {
35 case Intrinsics::kNone:
36 return kInterface; // Non-sensical for intrinsic.
Nicolas Geoffray762869d2016-07-15 15:28:35 +010037#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080038 case Intrinsics::k ## Name: \
Andreas Gampe71fb52f2014-12-29 17:43:08 -080039 return IsStatic;
40#include "intrinsics_list.h"
41INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
42#undef INTRINSICS_LIST
43#undef OPTIMIZING_INTRINSICS
44 }
45 return kInterface;
46}
47
agicsaki57b81ec2015-08-11 17:39:37 -070048// Function that returns whether an intrinsic needs an environment or not.
Agi Csaki05f20562015-08-19 14:58:14 -070049static inline IntrinsicNeedsEnvironmentOrCache NeedsEnvironmentOrCache(Intrinsics i) {
agicsaki57b81ec2015-08-11 17:39:37 -070050 switch (i) {
51 case Intrinsics::kNone:
Agi Csaki05f20562015-08-19 14:58:14 -070052 return kNeedsEnvironmentOrCache; // Non-sensical for intrinsic.
Nicolas Geoffray762869d2016-07-15 15:28:35 +010053#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080054 case Intrinsics::k ## Name: \
Agi Csaki05f20562015-08-19 14:58:14 -070055 return NeedsEnvironmentOrCache;
agicsaki57b81ec2015-08-11 17:39:37 -070056#include "intrinsics_list.h"
57INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
58#undef INTRINSICS_LIST
59#undef OPTIMIZING_INTRINSICS
60 }
Agi Csaki05f20562015-08-19 14:58:14 -070061 return kNeedsEnvironmentOrCache;
agicsaki57b81ec2015-08-11 17:39:37 -070062}
Andreas Gampe71fb52f2014-12-29 17:43:08 -080063
Aart Bik5d75afe2015-12-14 11:57:01 -080064// Function that returns whether an intrinsic has side effects.
65static inline IntrinsicSideEffects GetSideEffects(Intrinsics i) {
66 switch (i) {
67 case Intrinsics::kNone:
68 return kAllSideEffects;
Nicolas Geoffray762869d2016-07-15 15:28:35 +010069#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080070 case Intrinsics::k ## Name: \
71 return SideEffects;
72#include "intrinsics_list.h"
73INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
74#undef INTRINSICS_LIST
75#undef OPTIMIZING_INTRINSICS
76 }
77 return kAllSideEffects;
78}
79
80// Function that returns whether an intrinsic can throw exceptions.
81static inline IntrinsicExceptions GetExceptions(Intrinsics i) {
82 switch (i) {
83 case Intrinsics::kNone:
84 return kCanThrow;
Nicolas Geoffray762869d2016-07-15 15:28:35 +010085#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Aart Bik5d75afe2015-12-14 11:57:01 -080086 case Intrinsics::k ## Name: \
87 return Exceptions;
88#include "intrinsics_list.h"
89INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
90#undef INTRINSICS_LIST
91#undef OPTIMIZING_INTRINSICS
92 }
93 return kCanThrow;
94}
95
Nicolas Geoffray762869d2016-07-15 15:28:35 +010096static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
Andreas Gampebfb5ba92015-09-01 15:45:02 +000097 // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual.
98 //
99 // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization
100 // failure occured. We might be in a situation where we have inlined a method that calls an
101 // intrinsic, but that method is in a different dex file on which we do not have a
102 // verified_method that would have helped the compiler driver sharpen the call. In that case,
103 // make sure that the intrinsic is actually for some final method (or in a final class), as
104 // otherwise the intrinsics setup is broken.
105 //
106 // For the last direction, we have intrinsics for virtual functions that will perform a check
107 // inline. If the precise type is known, however, the instruction will be sharpened to an
108 // InvokeStaticOrDirect.
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800109 InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
Nicolas Geoffray5e4e11e2016-09-22 13:17:41 +0100110 InvokeType invoke_type = invoke->GetInvokeType();
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800111 switch (intrinsic_type) {
112 case kStatic:
113 return (invoke_type == kStatic);
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000114
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800115 case kDirect:
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000116 if (invoke_type == kDirect) {
117 return true;
118 }
119 if (invoke_type == kVirtual) {
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100120 ArtMethod* art_method = invoke->GetResolvedMethod();
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000121 ScopedObjectAccess soa(Thread::Current());
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100122 return (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
Andreas Gampebfb5ba92015-09-01 15:45:02 +0000123 }
124 return false;
125
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800126 case kVirtual:
127 // Call might be devirtualized.
128 return (invoke_type == kVirtual || invoke_type == kDirect);
129
130 default:
131 return false;
132 }
133}
134
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800135void IntrinsicsRecognizer::Run() {
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100136 ScopedObjectAccess soa(Thread::Current());
Vladimir Marko2c45bc92016-10-25 16:54:12 +0100137 for (HBasicBlock* block : graph_->GetReversePostOrder()) {
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800138 for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
139 inst_it.Advance()) {
140 HInstruction* inst = inst_it.Current();
141 if (inst->IsInvoke()) {
142 HInvoke* invoke = inst->AsInvoke();
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100143 ArtMethod* art_method = invoke->GetResolvedMethod();
144 if (art_method != nullptr && art_method->IsIntrinsic()) {
145 Intrinsics intrinsic = static_cast<Intrinsics>(art_method->GetIntrinsic());
146 if (!CheckInvokeType(intrinsic, invoke)) {
147 LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
148 << intrinsic << " for "
Nicolas Geoffray5d37c152017-01-12 13:25:19 +0000149 << art_method->PrettyMethod()
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100150 << invoke->DebugName();
151 } else {
152 invoke->SetIntrinsic(intrinsic,
153 NeedsEnvironmentOrCache(intrinsic),
154 GetSideEffects(intrinsic),
155 GetExceptions(intrinsic));
156 MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800157 }
158 }
159 }
160 }
161 }
162}
163
164std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
165 switch (intrinsic) {
166 case Intrinsics::kNone:
David Brazdil109c89a2015-07-31 17:10:43 +0100167 os << "None";
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800168 break;
Nicolas Geoffray762869d2016-07-15 15:28:35 +0100169#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800170 case Intrinsics::k ## Name: \
171 os << # Name; \
172 break;
173#include "intrinsics_list.h"
174INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
175#undef STATIC_INTRINSICS_LIST
176#undef VIRTUAL_INTRINSICS_LIST
177#undef OPTIMIZING_INTRINSICS
178 }
179 return os;
180}
181
Nicolas Geoffray331605a2017-03-01 11:01:41 +0000182void IntrinsicVisitor::ComputeIntegerValueOfLocations(HInvoke* invoke,
183 CodeGenerator* codegen,
184 Location return_location,
185 Location first_argument_location) {
186 if (Runtime::Current()->IsAotCompiler()) {
187 if (codegen->GetCompilerOptions().IsBootImage() ||
188 codegen->GetCompilerOptions().GetCompilePic()) {
189 // TODO(ngeoffray): Support boot image compilation.
190 return;
191 }
192 }
193
194 IntegerValueOfInfo info = ComputeIntegerValueOfInfo();
195
196 // Most common case is that we have found all we needed (classes are initialized
197 // and in the boot image). Bail if not.
198 if (info.integer_cache == nullptr ||
199 info.integer == nullptr ||
200 info.cache == nullptr ||
201 info.value_offset == 0 ||
202 // low and high cannot be 0, per the spec.
203 info.low == 0 ||
204 info.high == 0) {
205 LOG(INFO) << "Integer.valueOf will not be optimized";
206 return;
207 }
208
209 // The intrinsic will call if it needs to allocate a j.l.Integer.
210 LocationSummary* locations = new (invoke->GetBlock()->GetGraph()->GetArena()) LocationSummary(
211 invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
212 if (!invoke->InputAt(0)->IsConstant()) {
213 locations->SetInAt(0, Location::RequiresRegister());
214 }
215 locations->AddTemp(first_argument_location);
216 locations->SetOut(return_location);
217}
218
219IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo() {
220 // Note that we could cache all of the data looked up here. but there's no good
221 // location for it. We don't want to add it to WellKnownClasses, to avoid creating global
222 // jni values. Adding it as state to the compiler singleton seems like wrong
223 // separation of concerns.
224 // The need for this data should be pretty rare though.
225
226 // The most common case is that the classes are in the boot image and initialized,
227 // which is easy to generate code for. We bail if not.
228 Thread* self = Thread::Current();
229 ScopedObjectAccess soa(self);
230 Runtime* runtime = Runtime::Current();
231 ClassLinker* class_linker = runtime->GetClassLinker();
232 gc::Heap* heap = runtime->GetHeap();
233 IntegerValueOfInfo info;
234 info.integer_cache = class_linker->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;");
235 if (info.integer_cache == nullptr) {
236 self->ClearException();
237 return info;
238 }
239 if (!heap->ObjectIsInBootImageSpace(info.integer_cache) || !info.integer_cache->IsInitialized()) {
240 // Optimization only works if the class is initialized and in the boot image.
241 return info;
242 }
243 info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;");
244 if (info.integer == nullptr) {
245 self->ClearException();
246 return info;
247 }
248 if (!heap->ObjectIsInBootImageSpace(info.integer) || !info.integer->IsInitialized()) {
249 // Optimization only works if the class is initialized and in the boot image.
250 return info;
251 }
252
253 ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
254 if (field == nullptr) {
255 return info;
256 }
257 info.cache = static_cast<mirror::ObjectArray<mirror::Object>*>(
258 field->GetObject(info.integer_cache).Ptr());
259 if (info.cache == nullptr) {
260 return info;
261 }
262
263 if (!heap->ObjectIsInBootImageSpace(info.cache)) {
264 // Optimization only works if the object is in the boot image.
265 return info;
266 }
267
268 field = info.integer->FindDeclaredInstanceField("value", "I");
269 if (field == nullptr) {
270 return info;
271 }
272 info.value_offset = field->GetOffset().Int32Value();
273
274 field = info.integer_cache->FindDeclaredStaticField("low", "I");
275 if (field == nullptr) {
276 return info;
277 }
278 info.low = field->GetInt(info.integer_cache);
279
280 field = info.integer_cache->FindDeclaredStaticField("high", "I");
281 if (field == nullptr) {
282 return info;
283 }
284 info.high = field->GetInt(info.integer_cache);
285
286 DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1);
287 return info;
288}
289
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800290} // namespace art