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