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