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