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