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
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.
38#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
39    case Intrinsics::k ## Name: \
40      return IsStatic;
41#include "intrinsics_list.h"
42INTRINSICS_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.
50static 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"
58INTRINSICS_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.
66static 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"
74INTRINSICS_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.
82static 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"
90INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
91#undef INTRINSICS_LIST
92#undef OPTIMIZING_INTRINSICS
93  }
94  return kCanThrow;
95}
96
97static 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
136void 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
165std::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"
175INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
176#undef STATIC_INTRINSICS_LIST
177#undef VIRTUAL_INTRINSICS_LIST
178#undef OPTIMIZING_INTRINSICS
179  }
180  return os;
181}
182
183void 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
220IntrinsicVisitor::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