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, SideEffects, Exceptions) \
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, SideEffects, Exceptions) \
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
66// Function that returns whether an intrinsic has side effects.
67static inline IntrinsicSideEffects GetSideEffects(Intrinsics i) {
68  switch (i) {
69    case Intrinsics::kNone:
70      return kAllSideEffects;
71#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
72    case Intrinsics::k ## Name: \
73      return SideEffects;
74#include "intrinsics_list.h"
75INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
76#undef INTRINSICS_LIST
77#undef OPTIMIZING_INTRINSICS
78  }
79  return kAllSideEffects;
80}
81
82// Function that returns whether an intrinsic can throw exceptions.
83static inline IntrinsicExceptions GetExceptions(Intrinsics i) {
84  switch (i) {
85    case Intrinsics::kNone:
86      return kCanThrow;
87#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
88    case Intrinsics::k ## Name: \
89      return Exceptions;
90#include "intrinsics_list.h"
91INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
92#undef INTRINSICS_LIST
93#undef OPTIMIZING_INTRINSICS
94  }
95  return kCanThrow;
96}
97
98static Primitive::Type GetType(uint64_t data, bool is_op_size) {
99  if (is_op_size) {
100    switch (static_cast<OpSize>(data)) {
101      case kSignedByte:
102        return Primitive::kPrimByte;
103      case kSignedHalf:
104        return Primitive::kPrimShort;
105      case k32:
106        return Primitive::kPrimInt;
107      case k64:
108        return Primitive::kPrimLong;
109      default:
110        LOG(FATAL) << "Unknown/unsupported op size " << data;
111        UNREACHABLE();
112    }
113  } else {
114    if ((data & kIntrinsicFlagIsLong) != 0) {
115      return Primitive::kPrimLong;
116    }
117    if ((data & kIntrinsicFlagIsObject) != 0) {
118      return Primitive::kPrimNot;
119    }
120    return Primitive::kPrimInt;
121  }
122}
123
124static Intrinsics GetIntrinsic(InlineMethod method) {
125  switch (method.opcode) {
126    // Floating-point conversions.
127    case kIntrinsicDoubleCvt:
128      return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
129          Intrinsics::kDoubleDoubleToRawLongBits : Intrinsics::kDoubleLongBitsToDouble;
130    case kIntrinsicFloatCvt:
131      return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
132          Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat;
133    case kIntrinsicFloat2Int:
134      return Intrinsics::kFloatFloatToIntBits;
135    case kIntrinsicDouble2Long:
136      return Intrinsics::kDoubleDoubleToLongBits;
137
138    // Floating-point tests.
139    case kIntrinsicFloatIsInfinite:
140      return Intrinsics::kFloatIsInfinite;
141    case kIntrinsicDoubleIsInfinite:
142      return Intrinsics::kDoubleIsInfinite;
143    case kIntrinsicFloatIsNaN:
144      return Intrinsics::kFloatIsNaN;
145    case kIntrinsicDoubleIsNaN:
146      return Intrinsics::kDoubleIsNaN;
147
148    // Bit manipulations.
149    case kIntrinsicReverseBits:
150      switch (GetType(method.d.data, true)) {
151        case Primitive::kPrimInt:
152          return Intrinsics::kIntegerReverse;
153        case Primitive::kPrimLong:
154          return Intrinsics::kLongReverse;
155        default:
156          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
157          UNREACHABLE();
158      }
159    case kIntrinsicReverseBytes:
160      switch (GetType(method.d.data, true)) {
161        case Primitive::kPrimShort:
162          return Intrinsics::kShortReverseBytes;
163        case Primitive::kPrimInt:
164          return Intrinsics::kIntegerReverseBytes;
165        case Primitive::kPrimLong:
166          return Intrinsics::kLongReverseBytes;
167        default:
168          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
169          UNREACHABLE();
170      }
171    case kIntrinsicRotateRight:
172      switch (GetType(method.d.data, true)) {
173        case Primitive::kPrimInt:
174          return Intrinsics::kIntegerRotateRight;
175        case Primitive::kPrimLong:
176          return Intrinsics::kLongRotateRight;
177        default:
178          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
179          UNREACHABLE();
180      }
181    case kIntrinsicRotateLeft:
182      switch (GetType(method.d.data, true)) {
183        case Primitive::kPrimInt:
184          return Intrinsics::kIntegerRotateLeft;
185        case Primitive::kPrimLong:
186          return Intrinsics::kLongRotateLeft;
187        default:
188          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
189          UNREACHABLE();
190      }
191
192    // Misc data processing.
193    case kIntrinsicBitCount:
194      switch (GetType(method.d.data, true)) {
195        case Primitive::kPrimInt:
196          return Intrinsics::kIntegerBitCount;
197        case Primitive::kPrimLong:
198          return Intrinsics::kLongBitCount;
199        default:
200          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
201          UNREACHABLE();
202      }
203    case kIntrinsicCompare:
204      switch (GetType(method.d.data, true)) {
205        case Primitive::kPrimInt:
206          return Intrinsics::kIntegerCompare;
207        case Primitive::kPrimLong:
208          return Intrinsics::kLongCompare;
209        default:
210          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
211          UNREACHABLE();
212      }
213    case kIntrinsicHighestOneBit:
214      switch (GetType(method.d.data, true)) {
215        case Primitive::kPrimInt:
216          return Intrinsics::kIntegerHighestOneBit;
217        case Primitive::kPrimLong:
218          return Intrinsics::kLongHighestOneBit;
219        default:
220          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
221          UNREACHABLE();
222      }
223    case kIntrinsicLowestOneBit:
224      switch (GetType(method.d.data, true)) {
225        case Primitive::kPrimInt:
226          return Intrinsics::kIntegerLowestOneBit;
227        case Primitive::kPrimLong:
228          return Intrinsics::kLongLowestOneBit;
229        default:
230          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
231          UNREACHABLE();
232      }
233    case kIntrinsicNumberOfLeadingZeros:
234      switch (GetType(method.d.data, true)) {
235        case Primitive::kPrimInt:
236          return Intrinsics::kIntegerNumberOfLeadingZeros;
237        case Primitive::kPrimLong:
238          return Intrinsics::kLongNumberOfLeadingZeros;
239        default:
240          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
241          UNREACHABLE();
242      }
243    case kIntrinsicNumberOfTrailingZeros:
244      switch (GetType(method.d.data, true)) {
245        case Primitive::kPrimInt:
246          return Intrinsics::kIntegerNumberOfTrailingZeros;
247        case Primitive::kPrimLong:
248          return Intrinsics::kLongNumberOfTrailingZeros;
249        default:
250          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
251          UNREACHABLE();
252      }
253    case kIntrinsicSignum:
254      switch (GetType(method.d.data, true)) {
255        case Primitive::kPrimInt:
256          return Intrinsics::kIntegerSignum;
257        case Primitive::kPrimLong:
258          return Intrinsics::kLongSignum;
259        default:
260          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
261          UNREACHABLE();
262      }
263
264    // Abs.
265    case kIntrinsicAbsDouble:
266      return Intrinsics::kMathAbsDouble;
267    case kIntrinsicAbsFloat:
268      return Intrinsics::kMathAbsFloat;
269    case kIntrinsicAbsInt:
270      return Intrinsics::kMathAbsInt;
271    case kIntrinsicAbsLong:
272      return Intrinsics::kMathAbsLong;
273
274    // Min/max.
275    case kIntrinsicMinMaxDouble:
276      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
277          Intrinsics::kMathMaxDoubleDouble : Intrinsics::kMathMinDoubleDouble;
278    case kIntrinsicMinMaxFloat:
279      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
280          Intrinsics::kMathMaxFloatFloat : Intrinsics::kMathMinFloatFloat;
281    case kIntrinsicMinMaxInt:
282      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
283          Intrinsics::kMathMaxIntInt : Intrinsics::kMathMinIntInt;
284    case kIntrinsicMinMaxLong:
285      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
286          Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong;
287
288    // More math builtins.
289    case kIntrinsicCos:
290      return Intrinsics::kMathCos;
291    case kIntrinsicSin:
292      return Intrinsics::kMathSin;
293    case kIntrinsicAcos:
294      return Intrinsics::kMathAcos;
295    case kIntrinsicAsin:
296      return Intrinsics::kMathAsin;
297    case kIntrinsicAtan:
298      return Intrinsics::kMathAtan;
299    case kIntrinsicAtan2:
300      return Intrinsics::kMathAtan2;
301    case kIntrinsicCbrt:
302      return Intrinsics::kMathCbrt;
303    case kIntrinsicCosh:
304      return Intrinsics::kMathCosh;
305    case kIntrinsicExp:
306      return Intrinsics::kMathExp;
307    case kIntrinsicExpm1:
308      return Intrinsics::kMathExpm1;
309    case kIntrinsicHypot:
310      return Intrinsics::kMathHypot;
311    case kIntrinsicLog:
312      return Intrinsics::kMathLog;
313    case kIntrinsicLog10:
314      return Intrinsics::kMathLog10;
315    case kIntrinsicNextAfter:
316      return Intrinsics::kMathNextAfter;
317    case kIntrinsicSinh:
318      return Intrinsics::kMathSinh;
319    case kIntrinsicTan:
320      return Intrinsics::kMathTan;
321    case kIntrinsicTanh:
322      return Intrinsics::kMathTanh;
323
324    // Misc math.
325    case kIntrinsicSqrt:
326      return Intrinsics::kMathSqrt;
327    case kIntrinsicCeil:
328      return Intrinsics::kMathCeil;
329    case kIntrinsicFloor:
330      return Intrinsics::kMathFloor;
331    case kIntrinsicRint:
332      return Intrinsics::kMathRint;
333    case kIntrinsicRoundDouble:
334      return Intrinsics::kMathRoundDouble;
335    case kIntrinsicRoundFloat:
336      return Intrinsics::kMathRoundFloat;
337
338    // System.arraycopy.
339    case kIntrinsicSystemArrayCopyCharArray:
340      return Intrinsics::kSystemArrayCopyChar;
341
342    case kIntrinsicSystemArrayCopy:
343      return Intrinsics::kSystemArrayCopy;
344
345    // Thread.currentThread.
346    case kIntrinsicCurrentThread:
347      return Intrinsics::kThreadCurrentThread;
348
349    // Memory.peek.
350    case kIntrinsicPeek:
351      switch (GetType(method.d.data, true)) {
352        case Primitive::kPrimByte:
353          return Intrinsics::kMemoryPeekByte;
354        case Primitive::kPrimShort:
355          return Intrinsics::kMemoryPeekShortNative;
356        case Primitive::kPrimInt:
357          return Intrinsics::kMemoryPeekIntNative;
358        case Primitive::kPrimLong:
359          return Intrinsics::kMemoryPeekLongNative;
360        default:
361          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
362          UNREACHABLE();
363      }
364
365    // Memory.poke.
366    case kIntrinsicPoke:
367      switch (GetType(method.d.data, true)) {
368        case Primitive::kPrimByte:
369          return Intrinsics::kMemoryPokeByte;
370        case Primitive::kPrimShort:
371          return Intrinsics::kMemoryPokeShortNative;
372        case Primitive::kPrimInt:
373          return Intrinsics::kMemoryPokeIntNative;
374        case Primitive::kPrimLong:
375          return Intrinsics::kMemoryPokeLongNative;
376        default:
377          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
378          UNREACHABLE();
379      }
380
381    // String.
382    case kIntrinsicCharAt:
383      return Intrinsics::kStringCharAt;
384    case kIntrinsicCompareTo:
385      return Intrinsics::kStringCompareTo;
386    case kIntrinsicEquals:
387      return Intrinsics::kStringEquals;
388    case kIntrinsicGetCharsNoCheck:
389      return Intrinsics::kStringGetCharsNoCheck;
390    case kIntrinsicIsEmptyOrLength:
391      // The inliner can handle these two cases - and this is the preferred approach
392      // since after inlining the call is no longer visible (as opposed to waiting
393      // until codegen to handle intrinsic).
394      return Intrinsics::kNone;
395    case kIntrinsicIndexOf:
396      return ((method.d.data & kIntrinsicFlagBase0) == 0) ?
397          Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf;
398    case kIntrinsicNewStringFromBytes:
399      return Intrinsics::kStringNewStringFromBytes;
400    case kIntrinsicNewStringFromChars:
401      return Intrinsics::kStringNewStringFromChars;
402    case kIntrinsicNewStringFromString:
403      return Intrinsics::kStringNewStringFromString;
404
405    case kIntrinsicCas:
406      switch (GetType(method.d.data, false)) {
407        case Primitive::kPrimNot:
408          return Intrinsics::kUnsafeCASObject;
409        case Primitive::kPrimInt:
410          return Intrinsics::kUnsafeCASInt;
411        case Primitive::kPrimLong:
412          return Intrinsics::kUnsafeCASLong;
413        default:
414          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
415          UNREACHABLE();
416      }
417    case kIntrinsicUnsafeGet: {
418      const bool is_volatile = (method.d.data & kIntrinsicFlagIsVolatile);
419      switch (GetType(method.d.data, false)) {
420        case Primitive::kPrimInt:
421          return is_volatile ? Intrinsics::kUnsafeGetVolatile : Intrinsics::kUnsafeGet;
422        case Primitive::kPrimLong:
423          return is_volatile ? Intrinsics::kUnsafeGetLongVolatile : Intrinsics::kUnsafeGetLong;
424        case Primitive::kPrimNot:
425          return is_volatile ? Intrinsics::kUnsafeGetObjectVolatile : Intrinsics::kUnsafeGetObject;
426        default:
427          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
428          UNREACHABLE();
429      }
430    }
431    case kIntrinsicUnsafePut: {
432      enum Sync { kNoSync, kVolatile, kOrdered };
433      const Sync sync =
434          ((method.d.data & kIntrinsicFlagIsVolatile) != 0) ? kVolatile :
435          ((method.d.data & kIntrinsicFlagIsOrdered) != 0)  ? kOrdered :
436                                                              kNoSync;
437      switch (GetType(method.d.data, false)) {
438        case Primitive::kPrimInt:
439          switch (sync) {
440            case kNoSync:
441              return Intrinsics::kUnsafePut;
442            case kVolatile:
443              return Intrinsics::kUnsafePutVolatile;
444            case kOrdered:
445              return Intrinsics::kUnsafePutOrdered;
446          }
447          break;
448        case Primitive::kPrimLong:
449          switch (sync) {
450            case kNoSync:
451              return Intrinsics::kUnsafePutLong;
452            case kVolatile:
453              return Intrinsics::kUnsafePutLongVolatile;
454            case kOrdered:
455              return Intrinsics::kUnsafePutLongOrdered;
456          }
457          break;
458        case Primitive::kPrimNot:
459          switch (sync) {
460            case kNoSync:
461              return Intrinsics::kUnsafePutObject;
462            case kVolatile:
463              return Intrinsics::kUnsafePutObjectVolatile;
464            case kOrdered:
465              return Intrinsics::kUnsafePutObjectOrdered;
466          }
467          break;
468        default:
469          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
470          UNREACHABLE();
471      }
472      break;
473    }
474
475    // 1.8.
476    case kIntrinsicUnsafeGetAndAddInt:
477      return Intrinsics::kUnsafeGetAndAddInt;
478    case kIntrinsicUnsafeGetAndAddLong:
479      return Intrinsics::kUnsafeGetAndAddLong;
480    case kIntrinsicUnsafeGetAndSetInt:
481      return Intrinsics::kUnsafeGetAndSetInt;
482    case kIntrinsicUnsafeGetAndSetLong:
483      return Intrinsics::kUnsafeGetAndSetLong;
484    case kIntrinsicUnsafeGetAndSetObject:
485      return Intrinsics::kUnsafeGetAndSetObject;
486    case kIntrinsicUnsafeLoadFence:
487      return Intrinsics::kUnsafeLoadFence;
488    case kIntrinsicUnsafeStoreFence:
489      return Intrinsics::kUnsafeStoreFence;
490    case kIntrinsicUnsafeFullFence:
491      return Intrinsics::kUnsafeFullFence;
492
493    // Virtual cases.
494
495    case kIntrinsicReferenceGetReferent:
496      return Intrinsics::kReferenceGetReferent;
497
498    // Quick inliner cases. Remove after refactoring. They are here so that we can use the
499    // compiler to warn on missing cases.
500
501    case kInlineOpNop:
502    case kInlineOpReturnArg:
503    case kInlineOpNonWideConst:
504    case kInlineOpIGet:
505    case kInlineOpIPut:
506    case kInlineOpConstructor:
507      return Intrinsics::kNone;
508
509    // String init cases, not intrinsics.
510
511    case kInlineStringInit:
512      return Intrinsics::kNone;
513
514    // No default case to make the compiler warn on missing cases.
515  }
516  return Intrinsics::kNone;
517}
518
519static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke, const DexFile& dex_file) {
520  // The DexFileMethodInliner should have checked whether the methods are agreeing with
521  // what we expect, i.e., static methods are called as such. Add another check here for
522  // our expectations:
523  //
524  // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual.
525  //
526  // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization
527  // failure occured. We might be in a situation where we have inlined a method that calls an
528  // intrinsic, but that method is in a different dex file on which we do not have a
529  // verified_method that would have helped the compiler driver sharpen the call. In that case,
530  // make sure that the intrinsic is actually for some final method (or in a final class), as
531  // otherwise the intrinsics setup is broken.
532  //
533  // For the last direction, we have intrinsics for virtual functions that will perform a check
534  // inline. If the precise type is known, however, the instruction will be sharpened to an
535  // InvokeStaticOrDirect.
536  InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
537  InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ?
538      invoke->AsInvokeStaticOrDirect()->GetOptimizedInvokeType() :
539      invoke->IsInvokeVirtual() ? kVirtual : kSuper;
540  switch (intrinsic_type) {
541    case kStatic:
542      return (invoke_type == kStatic);
543
544    case kDirect:
545      if (invoke_type == kDirect) {
546        return true;
547      }
548      if (invoke_type == kVirtual) {
549        ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
550        ScopedObjectAccess soa(Thread::Current());
551        ArtMethod* art_method =
552            class_linker->FindDexCache(soa.Self(), dex_file)->GetResolvedMethod(
553                invoke->GetDexMethodIndex(), class_linker->GetImagePointerSize());
554        return art_method != nullptr &&
555            (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
556      }
557      return false;
558
559    case kVirtual:
560      // Call might be devirtualized.
561      return (invoke_type == kVirtual || invoke_type == kDirect);
562
563    default:
564      return false;
565  }
566}
567
568// TODO: Refactor DexFileMethodInliner and have something nicer than InlineMethod.
569void IntrinsicsRecognizer::Run() {
570  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
571    HBasicBlock* block = it.Current();
572    for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
573         inst_it.Advance()) {
574      HInstruction* inst = inst_it.Current();
575      if (inst->IsInvoke()) {
576        HInvoke* invoke = inst->AsInvoke();
577        InlineMethod method;
578        const DexFile& dex_file = invoke->GetDexFile();
579        DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(&dex_file);
580        DCHECK(inliner != nullptr);
581        if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) {
582          Intrinsics intrinsic = GetIntrinsic(method);
583
584          if (intrinsic != Intrinsics::kNone) {
585            if (!CheckInvokeType(intrinsic, invoke, dex_file)) {
586              LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
587                  << intrinsic << " for "
588                  << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile())
589                  << invoke->DebugName();
590            } else {
591              invoke->SetIntrinsic(intrinsic,
592                                   NeedsEnvironmentOrCache(intrinsic),
593                                   GetSideEffects(intrinsic),
594                                   GetExceptions(intrinsic));
595              MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
596            }
597          }
598        }
599      }
600    }
601  }
602}
603
604std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
605  switch (intrinsic) {
606    case Intrinsics::kNone:
607      os << "None";
608      break;
609#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
610    case Intrinsics::k ## Name: \
611      os << # Name; \
612      break;
613#include "intrinsics_list.h"
614INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
615#undef STATIC_INTRINSICS_LIST
616#undef VIRTUAL_INTRINSICS_LIST
617#undef OPTIMIZING_INTRINSICS
618  }
619  return os;
620}
621
622}  // namespace art
623