intrinsics.cc revision e523423a053af5cb55837f07ceae9ff2fd581712
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_method.h"
20#include "class_linker.h"
21#include "dex/quick/dex_file_method_inliner.h"
22#include "dex/quick/dex_file_to_method_inliner_map.h"
23#include "driver/compiler_driver.h"
24#include "invoke_type.h"
25#include "mirror/dex_cache-inl.h"
26#include "nodes.h"
27#include "quick/inline_method_analyser.h"
28#include "scoped_thread_state_change.h"
29#include "thread-inl.h"
30#include "utils.h"
31
32namespace art {
33
34// Function that returns whether an intrinsic is static/direct or virtual.
35static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) {
36  switch (i) {
37    case Intrinsics::kNone:
38      return kInterface;  // Non-sensical for intrinsic.
39#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache) \
40    case Intrinsics::k ## Name:               \
41      return IsStatic;
42#include "intrinsics_list.h"
43INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
44#undef INTRINSICS_LIST
45#undef OPTIMIZING_INTRINSICS
46  }
47  return kInterface;
48}
49
50// Function that returns whether an intrinsic needs an environment or not.
51static inline IntrinsicNeedsEnvironmentOrCache NeedsEnvironmentOrCache(Intrinsics i) {
52  switch (i) {
53    case Intrinsics::kNone:
54      return kNeedsEnvironmentOrCache;  // Non-sensical for intrinsic.
55#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache) \
56    case Intrinsics::k ## Name:               \
57      return NeedsEnvironmentOrCache;
58#include "intrinsics_list.h"
59INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
60#undef INTRINSICS_LIST
61#undef OPTIMIZING_INTRINSICS
62  }
63  return kNeedsEnvironmentOrCache;
64}
65
66static Primitive::Type GetType(uint64_t data, bool is_op_size) {
67  if (is_op_size) {
68    switch (static_cast<OpSize>(data)) {
69      case kSignedByte:
70        return Primitive::kPrimByte;
71      case kSignedHalf:
72        return Primitive::kPrimShort;
73      case k32:
74        return Primitive::kPrimInt;
75      case k64:
76        return Primitive::kPrimLong;
77      default:
78        LOG(FATAL) << "Unknown/unsupported op size " << data;
79        UNREACHABLE();
80    }
81  } else {
82    if ((data & kIntrinsicFlagIsLong) != 0) {
83      return Primitive::kPrimLong;
84    }
85    if ((data & kIntrinsicFlagIsObject) != 0) {
86      return Primitive::kPrimNot;
87    }
88    return Primitive::kPrimInt;
89  }
90}
91
92static Intrinsics GetIntrinsic(InlineMethod method) {
93  switch (method.opcode) {
94    // Floating-point conversions.
95    case kIntrinsicDoubleCvt:
96      return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
97          Intrinsics::kDoubleDoubleToRawLongBits : Intrinsics::kDoubleLongBitsToDouble;
98    case kIntrinsicFloatCvt:
99      return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
100          Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat;
101
102    // Bit manipulations.
103    case kIntrinsicReverseBits:
104      switch (GetType(method.d.data, true)) {
105        case Primitive::kPrimInt:
106          return Intrinsics::kIntegerReverse;
107        case Primitive::kPrimLong:
108          return Intrinsics::kLongReverse;
109        default:
110          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
111          UNREACHABLE();
112      }
113    case kIntrinsicReverseBytes:
114      switch (GetType(method.d.data, true)) {
115        case Primitive::kPrimShort:
116          return Intrinsics::kShortReverseBytes;
117        case Primitive::kPrimInt:
118          return Intrinsics::kIntegerReverseBytes;
119        case Primitive::kPrimLong:
120          return Intrinsics::kLongReverseBytes;
121        default:
122          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
123          UNREACHABLE();
124      }
125    case kIntrinsicRotateRight:
126      switch (GetType(method.d.data, true)) {
127        case Primitive::kPrimInt:
128          return Intrinsics::kIntegerRotateRight;
129        case Primitive::kPrimLong:
130          return Intrinsics::kLongRotateRight;
131        default:
132          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
133          UNREACHABLE();
134      }
135    case kIntrinsicRotateLeft:
136      switch (GetType(method.d.data, true)) {
137        case Primitive::kPrimInt:
138          return Intrinsics::kIntegerRotateLeft;
139        case Primitive::kPrimLong:
140          return Intrinsics::kLongRotateLeft;
141        default:
142          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
143          UNREACHABLE();
144      }
145
146    // Misc data processing.
147    case kIntrinsicNumberOfLeadingZeros:
148      switch (GetType(method.d.data, true)) {
149        case Primitive::kPrimInt:
150          return Intrinsics::kIntegerNumberOfLeadingZeros;
151        case Primitive::kPrimLong:
152          return Intrinsics::kLongNumberOfLeadingZeros;
153        default:
154          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
155          UNREACHABLE();
156      }
157    case kIntrinsicNumberOfTrailingZeros:
158      switch (GetType(method.d.data, true)) {
159        case Primitive::kPrimInt:
160          return Intrinsics::kIntegerNumberOfTrailingZeros;
161        case Primitive::kPrimLong:
162          return Intrinsics::kLongNumberOfTrailingZeros;
163        default:
164          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
165          UNREACHABLE();
166      }
167
168    // Abs.
169    case kIntrinsicAbsDouble:
170      return Intrinsics::kMathAbsDouble;
171    case kIntrinsicAbsFloat:
172      return Intrinsics::kMathAbsFloat;
173    case kIntrinsicAbsInt:
174      return Intrinsics::kMathAbsInt;
175    case kIntrinsicAbsLong:
176      return Intrinsics::kMathAbsLong;
177
178    // Min/max.
179    case kIntrinsicMinMaxDouble:
180      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
181          Intrinsics::kMathMaxDoubleDouble : Intrinsics::kMathMinDoubleDouble;
182    case kIntrinsicMinMaxFloat:
183      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
184          Intrinsics::kMathMaxFloatFloat : Intrinsics::kMathMinFloatFloat;
185    case kIntrinsicMinMaxInt:
186      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
187          Intrinsics::kMathMaxIntInt : Intrinsics::kMathMinIntInt;
188    case kIntrinsicMinMaxLong:
189      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
190          Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong;
191
192    // Misc math.
193    case kIntrinsicSqrt:
194      return Intrinsics::kMathSqrt;
195    case kIntrinsicCeil:
196      return Intrinsics::kMathCeil;
197    case kIntrinsicFloor:
198      return Intrinsics::kMathFloor;
199    case kIntrinsicRint:
200      return Intrinsics::kMathRint;
201    case kIntrinsicRoundDouble:
202      return Intrinsics::kMathRoundDouble;
203    case kIntrinsicRoundFloat:
204      return Intrinsics::kMathRoundFloat;
205
206    // System.arraycopy.
207    case kIntrinsicSystemArrayCopyCharArray:
208      return Intrinsics::kSystemArrayCopyChar;
209
210    case kIntrinsicSystemArrayCopy:
211      return Intrinsics::kSystemArrayCopy;
212
213    // Thread.currentThread.
214    case kIntrinsicCurrentThread:
215      return  Intrinsics::kThreadCurrentThread;
216
217    // Memory.peek.
218    case kIntrinsicPeek:
219      switch (GetType(method.d.data, true)) {
220        case Primitive::kPrimByte:
221          return Intrinsics::kMemoryPeekByte;
222        case Primitive::kPrimShort:
223          return Intrinsics::kMemoryPeekShortNative;
224        case Primitive::kPrimInt:
225          return Intrinsics::kMemoryPeekIntNative;
226        case Primitive::kPrimLong:
227          return Intrinsics::kMemoryPeekLongNative;
228        default:
229          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
230          UNREACHABLE();
231      }
232
233    // Memory.poke.
234    case kIntrinsicPoke:
235      switch (GetType(method.d.data, true)) {
236        case Primitive::kPrimByte:
237          return Intrinsics::kMemoryPokeByte;
238        case Primitive::kPrimShort:
239          return Intrinsics::kMemoryPokeShortNative;
240        case Primitive::kPrimInt:
241          return Intrinsics::kMemoryPokeIntNative;
242        case Primitive::kPrimLong:
243          return Intrinsics::kMemoryPokeLongNative;
244        default:
245          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
246          UNREACHABLE();
247      }
248
249    // String.
250    case kIntrinsicCharAt:
251      return Intrinsics::kStringCharAt;
252    case kIntrinsicCompareTo:
253      return Intrinsics::kStringCompareTo;
254    case kIntrinsicEquals:
255      return Intrinsics::kStringEquals;
256    case kIntrinsicGetCharsNoCheck:
257      return Intrinsics::kStringGetCharsNoCheck;
258    case kIntrinsicIsEmptyOrLength:
259      // The inliner can handle these two cases - and this is the preferred approach
260      // since after inlining the call is no longer visible (as opposed to waiting
261      // until codegen to handle intrinsic).
262      return Intrinsics::kNone;
263    case kIntrinsicIndexOf:
264      return ((method.d.data & kIntrinsicFlagBase0) == 0) ?
265          Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf;
266    case kIntrinsicNewStringFromBytes:
267      return Intrinsics::kStringNewStringFromBytes;
268    case kIntrinsicNewStringFromChars:
269      return Intrinsics::kStringNewStringFromChars;
270    case kIntrinsicNewStringFromString:
271      return Intrinsics::kStringNewStringFromString;
272
273    case kIntrinsicCas:
274      switch (GetType(method.d.data, false)) {
275        case Primitive::kPrimNot:
276          return Intrinsics::kUnsafeCASObject;
277        case Primitive::kPrimInt:
278          return Intrinsics::kUnsafeCASInt;
279        case Primitive::kPrimLong:
280          return Intrinsics::kUnsafeCASLong;
281        default:
282          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
283          UNREACHABLE();
284      }
285    case kIntrinsicUnsafeGet: {
286      const bool is_volatile = (method.d.data & kIntrinsicFlagIsVolatile);
287      switch (GetType(method.d.data, false)) {
288        case Primitive::kPrimInt:
289          return is_volatile ? Intrinsics::kUnsafeGetVolatile : Intrinsics::kUnsafeGet;
290        case Primitive::kPrimLong:
291          return is_volatile ? Intrinsics::kUnsafeGetLongVolatile : Intrinsics::kUnsafeGetLong;
292        case Primitive::kPrimNot:
293          return is_volatile ? Intrinsics::kUnsafeGetObjectVolatile : Intrinsics::kUnsafeGetObject;
294        default:
295          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
296          UNREACHABLE();
297      }
298    }
299    case kIntrinsicUnsafePut: {
300      enum Sync { kNoSync, kVolatile, kOrdered };
301      const Sync sync =
302          ((method.d.data & kIntrinsicFlagIsVolatile) != 0) ? kVolatile :
303          ((method.d.data & kIntrinsicFlagIsOrdered) != 0)  ? kOrdered :
304                                                              kNoSync;
305      switch (GetType(method.d.data, false)) {
306        case Primitive::kPrimInt:
307          switch (sync) {
308            case kNoSync:
309              return Intrinsics::kUnsafePut;
310            case kVolatile:
311              return Intrinsics::kUnsafePutVolatile;
312            case kOrdered:
313              return Intrinsics::kUnsafePutOrdered;
314          }
315          break;
316        case Primitive::kPrimLong:
317          switch (sync) {
318            case kNoSync:
319              return Intrinsics::kUnsafePutLong;
320            case kVolatile:
321              return Intrinsics::kUnsafePutLongVolatile;
322            case kOrdered:
323              return Intrinsics::kUnsafePutLongOrdered;
324          }
325          break;
326        case Primitive::kPrimNot:
327          switch (sync) {
328            case kNoSync:
329              return Intrinsics::kUnsafePutObject;
330            case kVolatile:
331              return Intrinsics::kUnsafePutObjectVolatile;
332            case kOrdered:
333              return Intrinsics::kUnsafePutObjectOrdered;
334          }
335          break;
336        default:
337          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
338          UNREACHABLE();
339      }
340      break;
341    }
342
343    // Virtual cases.
344
345    case kIntrinsicReferenceGetReferent:
346      return Intrinsics::kReferenceGetReferent;
347
348    // Quick inliner cases. Remove after refactoring. They are here so that we can use the
349    // compiler to warn on missing cases.
350
351    case kInlineOpNop:
352    case kInlineOpReturnArg:
353    case kInlineOpNonWideConst:
354    case kInlineOpIGet:
355    case kInlineOpIPut:
356      return Intrinsics::kNone;
357
358    // String init cases, not intrinsics.
359
360    case kInlineStringInit:
361      return Intrinsics::kNone;
362
363    // No default case to make the compiler warn on missing cases.
364  }
365  return Intrinsics::kNone;
366}
367
368static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke, const DexFile& dex_file) {
369  // The DexFileMethodInliner should have checked whether the methods are agreeing with
370  // what we expect, i.e., static methods are called as such. Add another check here for
371  // our expectations:
372  //
373  // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual.
374  //
375  // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization
376  // failure occured. We might be in a situation where we have inlined a method that calls an
377  // intrinsic, but that method is in a different dex file on which we do not have a
378  // verified_method that would have helped the compiler driver sharpen the call. In that case,
379  // make sure that the intrinsic is actually for some final method (or in a final class), as
380  // otherwise the intrinsics setup is broken.
381  //
382  // For the last direction, we have intrinsics for virtual functions that will perform a check
383  // inline. If the precise type is known, however, the instruction will be sharpened to an
384  // InvokeStaticOrDirect.
385  InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
386  InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ?
387      invoke->AsInvokeStaticOrDirect()->GetOptimizedInvokeType() :
388      invoke->IsInvokeVirtual() ? kVirtual : kSuper;
389  switch (intrinsic_type) {
390    case kStatic:
391      return (invoke_type == kStatic);
392
393    case kDirect:
394      if (invoke_type == kDirect) {
395        return true;
396      }
397      if (invoke_type == kVirtual) {
398        ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
399        ScopedObjectAccess soa(Thread::Current());
400        ArtMethod* art_method =
401            class_linker->FindDexCache(soa.Self(), dex_file)->GetResolvedMethod(
402                invoke->GetDexMethodIndex(), class_linker->GetImagePointerSize());
403        return art_method != nullptr &&
404            (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
405      }
406      return false;
407
408    case kVirtual:
409      // Call might be devirtualized.
410      return (invoke_type == kVirtual || invoke_type == kDirect);
411
412    default:
413      return false;
414  }
415}
416
417// TODO: Refactor DexFileMethodInliner and have something nicer than InlineMethod.
418void IntrinsicsRecognizer::Run() {
419  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
420    HBasicBlock* block = it.Current();
421    for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
422         inst_it.Advance()) {
423      HInstruction* inst = inst_it.Current();
424      if (inst->IsInvoke()) {
425        HInvoke* invoke = inst->AsInvoke();
426        InlineMethod method;
427        const DexFile& dex_file = invoke->GetDexFile();
428        DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(&dex_file);
429        DCHECK(inliner != nullptr);
430        if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) {
431          Intrinsics intrinsic = GetIntrinsic(method);
432
433          if (intrinsic != Intrinsics::kNone) {
434            if (!CheckInvokeType(intrinsic, invoke, dex_file)) {
435              LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
436                  << intrinsic << " for "
437                  << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile())
438                  << invoke->DebugName();
439            } else {
440              invoke->SetIntrinsic(intrinsic, NeedsEnvironmentOrCache(intrinsic));
441            }
442          }
443        }
444      }
445    }
446  }
447}
448
449std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
450  switch (intrinsic) {
451    case Intrinsics::kNone:
452      os << "None";
453      break;
454#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache) \
455    case Intrinsics::k ## Name: \
456      os << # Name; \
457      break;
458#include "intrinsics_list.h"
459INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
460#undef STATIC_INTRINSICS_LIST
461#undef VIRTUAL_INTRINSICS_LIST
462#undef OPTIMIZING_INTRINSICS
463  }
464  return os;
465}
466
467}  // namespace art
468