blob: c5ba8cbd924921f427582f5215420c2f87762dfd [file] [log] [blame]
Andreas Gampe71fb52f2014-12-29 17:43:08 -08001/*
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"
Vladimir Marko80afd022015-05-19 18:08:00 +010025#include "utils.h"
Andreas Gampe71fb52f2014-12-29 17:43:08 -080026
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.
agicsaki57b81ec2015-08-11 17:39:37 -070034#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironment) \
Andreas Gampe71fb52f2014-12-29 17:43:08 -080035 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
agicsaki57b81ec2015-08-11 17:39:37 -070045// Function that returns whether an intrinsic needs an environment or not.
46static inline IntrinsicNeedsEnvironment IntrinsicNeedsEnvironment(Intrinsics i) {
47 switch (i) {
48 case Intrinsics::kNone:
49 return kNeedsEnvironment; // Non-sensical for intrinsic.
50#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironment) \
51 case Intrinsics::k ## Name: \
52 return NeedsEnvironment;
53#include "intrinsics_list.h"
54INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
55#undef INTRINSICS_LIST
56#undef OPTIMIZING_INTRINSICS
57 }
58 return kNeedsEnvironment;
59}
Andreas Gampe71fb52f2014-12-29 17:43:08 -080060
61static Primitive::Type GetType(uint64_t data, bool is_op_size) {
62 if (is_op_size) {
63 switch (static_cast<OpSize>(data)) {
64 case kSignedByte:
Andreas Gampe878d58c2015-01-15 23:24:00 -080065 return Primitive::kPrimByte;
Andreas Gampe71fb52f2014-12-29 17:43:08 -080066 case kSignedHalf:
Andreas Gampe878d58c2015-01-15 23:24:00 -080067 return Primitive::kPrimShort;
Andreas Gampe71fb52f2014-12-29 17:43:08 -080068 case k32:
Andreas Gampe878d58c2015-01-15 23:24:00 -080069 return Primitive::kPrimInt;
Andreas Gampe71fb52f2014-12-29 17:43:08 -080070 case k64:
Andreas Gampe878d58c2015-01-15 23:24:00 -080071 return Primitive::kPrimLong;
Andreas Gampe71fb52f2014-12-29 17:43:08 -080072 default:
73 LOG(FATAL) << "Unknown/unsupported op size " << data;
74 UNREACHABLE();
75 }
76 } else {
77 if ((data & kIntrinsicFlagIsLong) != 0) {
Andreas Gampe878d58c2015-01-15 23:24:00 -080078 return Primitive::kPrimLong;
Andreas Gampe71fb52f2014-12-29 17:43:08 -080079 }
80 if ((data & kIntrinsicFlagIsObject) != 0) {
Andreas Gampe878d58c2015-01-15 23:24:00 -080081 return Primitive::kPrimNot;
Andreas Gampe71fb52f2014-12-29 17:43:08 -080082 }
Andreas Gampe878d58c2015-01-15 23:24:00 -080083 return Primitive::kPrimInt;
Andreas Gampe71fb52f2014-12-29 17:43:08 -080084 }
85}
86
87static Intrinsics GetIntrinsic(InlineMethod method) {
88 switch (method.opcode) {
89 // Floating-point conversions.
90 case kIntrinsicDoubleCvt:
91 return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
92 Intrinsics::kDoubleDoubleToRawLongBits : Intrinsics::kDoubleLongBitsToDouble;
93 case kIntrinsicFloatCvt:
94 return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
95 Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat;
96
97 // Bit manipulations.
98 case kIntrinsicReverseBits:
99 switch (GetType(method.d.data, true)) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800100 case Primitive::kPrimInt:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800101 return Intrinsics::kIntegerReverse;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800102 case Primitive::kPrimLong:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800103 return Intrinsics::kLongReverse;
104 default:
105 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
106 UNREACHABLE();
107 }
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800108 case kIntrinsicReverseBytes:
109 switch (GetType(method.d.data, true)) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800110 case Primitive::kPrimShort:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800111 return Intrinsics::kShortReverseBytes;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800112 case Primitive::kPrimInt:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800113 return Intrinsics::kIntegerReverseBytes;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800114 case Primitive::kPrimLong:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800115 return Intrinsics::kLongReverseBytes;
116 default:
117 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
118 UNREACHABLE();
119 }
Scott Wakeling611d3392015-07-10 11:42:06 +0100120 case kIntrinsicNumberOfLeadingZeros:
121 switch (GetType(method.d.data, true)) {
122 case Primitive::kPrimInt:
123 return Intrinsics::kIntegerNumberOfLeadingZeros;
124 case Primitive::kPrimLong:
125 return Intrinsics::kLongNumberOfLeadingZeros;
126 default:
127 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
128 UNREACHABLE();
129 }
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800130
131 // Abs.
132 case kIntrinsicAbsDouble:
133 return Intrinsics::kMathAbsDouble;
134 case kIntrinsicAbsFloat:
135 return Intrinsics::kMathAbsFloat;
136 case kIntrinsicAbsInt:
137 return Intrinsics::kMathAbsInt;
138 case kIntrinsicAbsLong:
139 return Intrinsics::kMathAbsLong;
140
141 // Min/max.
142 case kIntrinsicMinMaxDouble:
143 return ((method.d.data & kIntrinsicFlagMin) == 0) ?
144 Intrinsics::kMathMaxDoubleDouble : Intrinsics::kMathMinDoubleDouble;
145 case kIntrinsicMinMaxFloat:
146 return ((method.d.data & kIntrinsicFlagMin) == 0) ?
147 Intrinsics::kMathMaxFloatFloat : Intrinsics::kMathMinFloatFloat;
148 case kIntrinsicMinMaxInt:
149 return ((method.d.data & kIntrinsicFlagMin) == 0) ?
150 Intrinsics::kMathMaxIntInt : Intrinsics::kMathMinIntInt;
151 case kIntrinsicMinMaxLong:
152 return ((method.d.data & kIntrinsicFlagMin) == 0) ?
153 Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong;
154
155 // Misc math.
156 case kIntrinsicSqrt:
157 return Intrinsics::kMathSqrt;
158 case kIntrinsicCeil:
159 return Intrinsics::kMathCeil;
160 case kIntrinsicFloor:
161 return Intrinsics::kMathFloor;
162 case kIntrinsicRint:
163 return Intrinsics::kMathRint;
164 case kIntrinsicRoundDouble:
165 return Intrinsics::kMathRoundDouble;
166 case kIntrinsicRoundFloat:
167 return Intrinsics::kMathRoundFloat;
168
169 // System.arraycopy.
170 case kIntrinsicSystemArrayCopyCharArray:
171 return Intrinsics::kSystemArrayCopyChar;
172
173 // Thread.currentThread.
174 case kIntrinsicCurrentThread:
175 return Intrinsics::kThreadCurrentThread;
176
177 // Memory.peek.
178 case kIntrinsicPeek:
179 switch (GetType(method.d.data, true)) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800180 case Primitive::kPrimByte:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800181 return Intrinsics::kMemoryPeekByte;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800182 case Primitive::kPrimShort:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800183 return Intrinsics::kMemoryPeekShortNative;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800184 case Primitive::kPrimInt:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800185 return Intrinsics::kMemoryPeekIntNative;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800186 case Primitive::kPrimLong:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800187 return Intrinsics::kMemoryPeekLongNative;
188 default:
189 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
190 UNREACHABLE();
191 }
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800192
193 // Memory.poke.
194 case kIntrinsicPoke:
195 switch (GetType(method.d.data, true)) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800196 case Primitive::kPrimByte:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800197 return Intrinsics::kMemoryPokeByte;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800198 case Primitive::kPrimShort:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800199 return Intrinsics::kMemoryPokeShortNative;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800200 case Primitive::kPrimInt:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800201 return Intrinsics::kMemoryPokeIntNative;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800202 case Primitive::kPrimLong:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800203 return Intrinsics::kMemoryPokeLongNative;
204 default:
205 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
206 UNREACHABLE();
207 }
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800208
209 // String.
210 case kIntrinsicCharAt:
211 return Intrinsics::kStringCharAt;
212 case kIntrinsicCompareTo:
213 return Intrinsics::kStringCompareTo;
agicsaki7da072f2015-08-12 20:30:17 -0700214 case kIntrinsicEquals:
215 return Intrinsics::kStringEquals;
Jeff Hao848f70a2014-01-15 13:49:50 -0800216 case kIntrinsicGetCharsNoCheck:
217 return Intrinsics::kStringGetCharsNoCheck;
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800218 case kIntrinsicIsEmptyOrLength:
Razvan A Lupusoru3e90a962015-03-27 13:44:44 -0700219 // The inliner can handle these two cases - and this is the preferred approach
220 // since after inlining the call is no longer visible (as opposed to waiting
221 // until codegen to handle intrinsic).
222 return Intrinsics::kNone;
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800223 case kIntrinsicIndexOf:
224 return ((method.d.data & kIntrinsicFlagBase0) == 0) ?
225 Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf;
Jeff Hao848f70a2014-01-15 13:49:50 -0800226 case kIntrinsicNewStringFromBytes:
227 return Intrinsics::kStringNewStringFromBytes;
228 case kIntrinsicNewStringFromChars:
229 return Intrinsics::kStringNewStringFromChars;
230 case kIntrinsicNewStringFromString:
231 return Intrinsics::kStringNewStringFromString;
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800232
233 case kIntrinsicCas:
234 switch (GetType(method.d.data, false)) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800235 case Primitive::kPrimNot:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800236 return Intrinsics::kUnsafeCASObject;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800237 case Primitive::kPrimInt:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800238 return Intrinsics::kUnsafeCASInt;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800239 case Primitive::kPrimLong:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800240 return Intrinsics::kUnsafeCASLong;
241 default:
242 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
243 UNREACHABLE();
244 }
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800245 case kIntrinsicUnsafeGet: {
246 const bool is_volatile = (method.d.data & kIntrinsicFlagIsVolatile);
247 switch (GetType(method.d.data, false)) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800248 case Primitive::kPrimInt:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800249 return is_volatile ? Intrinsics::kUnsafeGetVolatile : Intrinsics::kUnsafeGet;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800250 case Primitive::kPrimLong:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800251 return is_volatile ? Intrinsics::kUnsafeGetLongVolatile : Intrinsics::kUnsafeGetLong;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800252 case Primitive::kPrimNot:
253 return is_volatile ? Intrinsics::kUnsafeGetObjectVolatile : Intrinsics::kUnsafeGetObject;
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800254 default:
255 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
256 UNREACHABLE();
257 }
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800258 }
259 case kIntrinsicUnsafePut: {
260 enum Sync { kNoSync, kVolatile, kOrdered };
261 const Sync sync =
262 ((method.d.data & kIntrinsicFlagIsVolatile) != 0) ? kVolatile :
263 ((method.d.data & kIntrinsicFlagIsOrdered) != 0) ? kOrdered :
264 kNoSync;
265 switch (GetType(method.d.data, false)) {
Andreas Gampe878d58c2015-01-15 23:24:00 -0800266 case Primitive::kPrimInt:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800267 switch (sync) {
268 case kNoSync:
269 return Intrinsics::kUnsafePut;
270 case kVolatile:
271 return Intrinsics::kUnsafePutVolatile;
272 case kOrdered:
273 return Intrinsics::kUnsafePutOrdered;
274 }
275 break;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800276 case Primitive::kPrimLong:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800277 switch (sync) {
278 case kNoSync:
279 return Intrinsics::kUnsafePutLong;
280 case kVolatile:
281 return Intrinsics::kUnsafePutLongVolatile;
282 case kOrdered:
283 return Intrinsics::kUnsafePutLongOrdered;
284 }
285 break;
Andreas Gampe878d58c2015-01-15 23:24:00 -0800286 case Primitive::kPrimNot:
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800287 switch (sync) {
288 case kNoSync:
289 return Intrinsics::kUnsafePutObject;
290 case kVolatile:
291 return Intrinsics::kUnsafePutObjectVolatile;
292 case kOrdered:
293 return Intrinsics::kUnsafePutObjectOrdered;
294 }
295 break;
296 default:
297 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
298 UNREACHABLE();
299 }
300 break;
301 }
302
303 // Virtual cases.
304
305 case kIntrinsicReferenceGetReferent:
306 return Intrinsics::kReferenceGetReferent;
307
308 // Quick inliner cases. Remove after refactoring. They are here so that we can use the
309 // compiler to warn on missing cases.
310
311 case kInlineOpNop:
312 case kInlineOpReturnArg:
313 case kInlineOpNonWideConst:
314 case kInlineOpIGet:
315 case kInlineOpIPut:
316 return Intrinsics::kNone;
317
Jeff Hao848f70a2014-01-15 13:49:50 -0800318 // String init cases, not intrinsics.
319
320 case kInlineStringInit:
321 return Intrinsics::kNone;
322
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800323 // No default case to make the compiler warn on missing cases.
324 }
325 return Intrinsics::kNone;
326}
327
328static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
329 // The DexFileMethodInliner should have checked whether the methods are agreeing with
330 // what we expect, i.e., static methods are called as such. Add another check here for
331 // our expectations:
332 // Whenever the intrinsic is marked as static-or-direct, report an error if we find an
333 // InvokeVirtual. The other direction is not possible: we have intrinsics for virtual
334 // functions that will perform a check inline. If the precise type is known, however,
335 // the instruction will be sharpened to an InvokeStaticOrDirect.
336 InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
337 InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ?
338 invoke->AsInvokeStaticOrDirect()->GetInvokeType() :
339 invoke->IsInvokeVirtual() ? kVirtual : kSuper;
340 switch (intrinsic_type) {
341 case kStatic:
342 return (invoke_type == kStatic);
343 case kDirect:
344 return (invoke_type == kDirect);
345 case kVirtual:
346 // Call might be devirtualized.
347 return (invoke_type == kVirtual || invoke_type == kDirect);
348
349 default:
350 return false;
351 }
352}
353
354// TODO: Refactor DexFileMethodInliner and have something nicer than InlineMethod.
355void IntrinsicsRecognizer::Run() {
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800356 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
357 HBasicBlock* block = it.Current();
358 for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
359 inst_it.Advance()) {
360 HInstruction* inst = inst_it.Current();
361 if (inst->IsInvoke()) {
362 HInvoke* invoke = inst->AsInvoke();
363 InlineMethod method;
Nicolas Geoffrayd5111bf2015-05-22 15:37:09 +0100364 DexFileMethodInliner* inliner =
365 driver_->GetMethodInlinerMap()->GetMethodInliner(&invoke->GetDexFile());
366 DCHECK(inliner != nullptr);
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800367 if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) {
368 Intrinsics intrinsic = GetIntrinsic(method);
369
370 if (intrinsic != Intrinsics::kNone) {
371 if (!CheckInvokeType(intrinsic, invoke)) {
372 LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
373 << intrinsic << " for "
Nicolas Geoffrayd5111bf2015-05-22 15:37:09 +0100374 << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile());
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800375 } else {
agicsaki57b81ec2015-08-11 17:39:37 -0700376 invoke->SetIntrinsic(intrinsic, IntrinsicNeedsEnvironment(intrinsic));
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800377 }
378 }
379 }
380 }
381 }
382 }
383}
384
385std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
386 switch (intrinsic) {
387 case Intrinsics::kNone:
David Brazdil109c89a2015-07-31 17:10:43 +0100388 os << "None";
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800389 break;
agicsaki57b81ec2015-08-11 17:39:37 -0700390#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironment) \
Andreas Gampe71fb52f2014-12-29 17:43:08 -0800391 case Intrinsics::k ## Name: \
392 os << # Name; \
393 break;
394#include "intrinsics_list.h"
395INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
396#undef STATIC_INTRINSICS_LIST
397#undef VIRTUAL_INTRINSICS_LIST
398#undef OPTIMIZING_INTRINSICS
399 }
400 return os;
401}
402
403} // namespace art