diff options
author | Andreas Gampe <agampe@google.com> | 2014-12-29 17:43:08 -0800 |
---|---|---|
committer | Andreas Gampe <agampe@google.com> | 2015-01-15 10:21:11 -0800 |
commit | 71fb52fee246b7d511f520febbd73dc7a9bbca79 (patch) | |
tree | 444d91e910433aaf887bbdada28dfaa3160bebc2 /compiler/optimizing/intrinsics.cc | |
parent | 420457e6040184a6e1639a4c84fcc8e237bd8a3d (diff) | |
download | art-71fb52fee246b7d511f520febbd73dc7a9bbca79.tar.gz art-71fb52fee246b7d511f520febbd73dc7a9bbca79.tar.bz2 art-71fb52fee246b7d511f520febbd73dc7a9bbca79.zip |
ART: Optimizing compiler intrinsics
Add intrinsics infrastructure to the optimizing compiler.
Add almost all intrinsics supported by Quick to the x86-64 backend.
Further intrinsics require more assembler support.
Change-Id: I48de9b44c82886bb298d16e74e12a9506b8e8807
Diffstat (limited to 'compiler/optimizing/intrinsics.cc')
-rw-r--r-- | compiler/optimizing/intrinsics.cc | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc new file mode 100644 index 000000000..fe0e7f2eb --- /dev/null +++ b/compiler/optimizing/intrinsics.cc @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "intrinsics.h" + +#include "dex/quick/dex_file_method_inliner.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" +#include "driver/compiler_driver.h" +#include "invoke_type.h" +#include "nodes.h" +#include "quick/inline_method_analyser.h" + +namespace art { + +// Function that returns whether an intrinsic is static/direct or virtual. +static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) { + switch (i) { + case Intrinsics::kNone: + return kInterface; // Non-sensical for intrinsic. +#define OPTIMIZING_INTRINSICS(Name, IsStatic) \ + case Intrinsics::k ## Name: \ + return IsStatic; +#include "intrinsics_list.h" +INTRINSICS_LIST(OPTIMIZING_INTRINSICS) +#undef INTRINSICS_LIST +#undef OPTIMIZING_INTRINSICS + } + return kInterface; +} + + + +static Primitive::Type GetType(uint64_t data, bool is_op_size) { + if (is_op_size) { + switch (static_cast<OpSize>(data)) { + case kSignedByte: + return Primitive::Type::kPrimByte; + case kSignedHalf: + return Primitive::Type::kPrimShort; + case k32: + return Primitive::Type::kPrimInt; + case k64: + return Primitive::Type::kPrimLong; + default: + LOG(FATAL) << "Unknown/unsupported op size " << data; + UNREACHABLE(); + } + } else { + if ((data & kIntrinsicFlagIsLong) != 0) { + return Primitive::Type::kPrimLong; + } + if ((data & kIntrinsicFlagIsObject) != 0) { + return Primitive::Type::kPrimNot; + } + return Primitive::Type::kPrimInt; + } +} + +static Intrinsics GetIntrinsic(InlineMethod method) { + switch (method.opcode) { + // Floating-point conversions. + case kIntrinsicDoubleCvt: + return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? + Intrinsics::kDoubleDoubleToRawLongBits : Intrinsics::kDoubleLongBitsToDouble; + case kIntrinsicFloatCvt: + return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? + Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat; + + // Bit manipulations. + case kIntrinsicReverseBits: + switch (GetType(method.d.data, true)) { + case Primitive::Type::kPrimInt: + return Intrinsics::kIntegerReverse; + case Primitive::Type::kPrimLong: + return Intrinsics::kLongReverse; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } + break; + case kIntrinsicReverseBytes: + switch (GetType(method.d.data, true)) { + case Primitive::Type::kPrimShort: + return Intrinsics::kShortReverseBytes; + case Primitive::Type::kPrimInt: + return Intrinsics::kIntegerReverseBytes; + case Primitive::Type::kPrimLong: + return Intrinsics::kLongReverseBytes; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } + break; + + // Abs. + case kIntrinsicAbsDouble: + return Intrinsics::kMathAbsDouble; + case kIntrinsicAbsFloat: + return Intrinsics::kMathAbsFloat; + case kIntrinsicAbsInt: + return Intrinsics::kMathAbsInt; + case kIntrinsicAbsLong: + return Intrinsics::kMathAbsLong; + + // Min/max. + case kIntrinsicMinMaxDouble: + return ((method.d.data & kIntrinsicFlagMin) == 0) ? + Intrinsics::kMathMaxDoubleDouble : Intrinsics::kMathMinDoubleDouble; + case kIntrinsicMinMaxFloat: + return ((method.d.data & kIntrinsicFlagMin) == 0) ? + Intrinsics::kMathMaxFloatFloat : Intrinsics::kMathMinFloatFloat; + case kIntrinsicMinMaxInt: + return ((method.d.data & kIntrinsicFlagMin) == 0) ? + Intrinsics::kMathMaxIntInt : Intrinsics::kMathMinIntInt; + case kIntrinsicMinMaxLong: + return ((method.d.data & kIntrinsicFlagMin) == 0) ? + Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong; + + // Misc math. + case kIntrinsicSqrt: + return Intrinsics::kMathSqrt; + case kIntrinsicCeil: + return Intrinsics::kMathCeil; + case kIntrinsicFloor: + return Intrinsics::kMathFloor; + case kIntrinsicRint: + return Intrinsics::kMathRint; + case kIntrinsicRoundDouble: + return Intrinsics::kMathRoundDouble; + case kIntrinsicRoundFloat: + return Intrinsics::kMathRoundFloat; + + // System.arraycopy. + case kIntrinsicSystemArrayCopyCharArray: + return Intrinsics::kSystemArrayCopyChar; + + // Thread.currentThread. + case kIntrinsicCurrentThread: + return Intrinsics::kThreadCurrentThread; + + // Memory.peek. + case kIntrinsicPeek: + switch (GetType(method.d.data, true)) { + case Primitive::Type::kPrimByte: + return Intrinsics::kMemoryPeekByte; + case Primitive::Type::kPrimShort: + return Intrinsics::kMemoryPeekShortNative; + case Primitive::Type::kPrimInt: + return Intrinsics::kMemoryPeekIntNative; + case Primitive::Type::kPrimLong: + return Intrinsics::kMemoryPeekLongNative; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } + break; + + // Memory.poke. + case kIntrinsicPoke: + switch (GetType(method.d.data, true)) { + case Primitive::Type::kPrimByte: + return Intrinsics::kMemoryPokeByte; + case Primitive::Type::kPrimShort: + return Intrinsics::kMemoryPokeShortNative; + case Primitive::Type::kPrimInt: + return Intrinsics::kMemoryPokeIntNative; + case Primitive::Type::kPrimLong: + return Intrinsics::kMemoryPokeLongNative; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } + break; + + // String. + case kIntrinsicCharAt: + return Intrinsics::kStringCharAt; + case kIntrinsicCompareTo: + return Intrinsics::kStringCompareTo; + case kIntrinsicIsEmptyOrLength: + return ((method.d.data & kIntrinsicFlagIsEmpty) == 0) ? + Intrinsics::kStringLength : Intrinsics::kStringIsEmpty; + case kIntrinsicIndexOf: + return ((method.d.data & kIntrinsicFlagBase0) == 0) ? + Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf; + + case kIntrinsicCas: + switch (GetType(method.d.data, false)) { + case Primitive::Type::kPrimNot: + return Intrinsics::kUnsafeCASObject; + case Primitive::Type::kPrimInt: + return Intrinsics::kUnsafeCASInt; + case Primitive::Type::kPrimLong: + return Intrinsics::kUnsafeCASLong; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } + break; + case kIntrinsicUnsafeGet: { + const bool is_volatile = (method.d.data & kIntrinsicFlagIsVolatile); + switch (GetType(method.d.data, false)) { + case Primitive::Type::kPrimInt: + return is_volatile ? Intrinsics::kUnsafeGetVolatile : Intrinsics::kUnsafeGet; + case Primitive::Type::kPrimLong: + return is_volatile ? Intrinsics::kUnsafeGetLongVolatile : Intrinsics::kUnsafeGetLong; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } + break; + } + case kIntrinsicUnsafePut: { + enum Sync { kNoSync, kVolatile, kOrdered }; + const Sync sync = + ((method.d.data & kIntrinsicFlagIsVolatile) != 0) ? kVolatile : + ((method.d.data & kIntrinsicFlagIsOrdered) != 0) ? kOrdered : + kNoSync; + switch (GetType(method.d.data, false)) { + case Primitive::Type::kPrimInt: + switch (sync) { + case kNoSync: + return Intrinsics::kUnsafePut; + case kVolatile: + return Intrinsics::kUnsafePutVolatile; + case kOrdered: + return Intrinsics::kUnsafePutOrdered; + } + break; + case Primitive::Type::kPrimLong: + switch (sync) { + case kNoSync: + return Intrinsics::kUnsafePutLong; + case kVolatile: + return Intrinsics::kUnsafePutLongVolatile; + case kOrdered: + return Intrinsics::kUnsafePutLongOrdered; + } + break; + case Primitive::Type::kPrimNot: + switch (sync) { + case kNoSync: + return Intrinsics::kUnsafePutObject; + case kVolatile: + return Intrinsics::kUnsafePutObjectVolatile; + case kOrdered: + return Intrinsics::kUnsafePutObjectOrdered; + } + break; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } + break; + } + + // Virtual cases. + + case kIntrinsicReferenceGetReferent: + return Intrinsics::kReferenceGetReferent; + + // Quick inliner cases. Remove after refactoring. They are here so that we can use the + // compiler to warn on missing cases. + + case kInlineOpNop: + case kInlineOpReturnArg: + case kInlineOpNonWideConst: + case kInlineOpIGet: + case kInlineOpIPut: + return Intrinsics::kNone; + + // No default case to make the compiler warn on missing cases. + } + return Intrinsics::kNone; +} + +static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) { + // The DexFileMethodInliner should have checked whether the methods are agreeing with + // what we expect, i.e., static methods are called as such. Add another check here for + // our expectations: + // Whenever the intrinsic is marked as static-or-direct, report an error if we find an + // InvokeVirtual. The other direction is not possible: we have intrinsics for virtual + // functions that will perform a check inline. If the precise type is known, however, + // the instruction will be sharpened to an InvokeStaticOrDirect. + InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic); + InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ? + invoke->AsInvokeStaticOrDirect()->GetInvokeType() : + invoke->IsInvokeVirtual() ? kVirtual : kSuper; + switch (intrinsic_type) { + case kStatic: + return (invoke_type == kStatic); + case kDirect: + return (invoke_type == kDirect); + case kVirtual: + // Call might be devirtualized. + return (invoke_type == kVirtual || invoke_type == kDirect); + + default: + return false; + } +} + +// TODO: Refactor DexFileMethodInliner and have something nicer than InlineMethod. +void IntrinsicsRecognizer::Run() { + DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(dex_file_); + DCHECK(inliner != nullptr); + + for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { + HBasicBlock* block = it.Current(); + for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done(); + inst_it.Advance()) { + HInstruction* inst = inst_it.Current(); + if (inst->IsInvoke()) { + HInvoke* invoke = inst->AsInvoke(); + InlineMethod method; + if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) { + Intrinsics intrinsic = GetIntrinsic(method); + + if (intrinsic != Intrinsics::kNone) { + if (!CheckInvokeType(intrinsic, invoke)) { + LOG(WARNING) << "Found an intrinsic with unexpected invoke type: " + << intrinsic << " for " + << PrettyMethod(invoke->GetDexMethodIndex(), *dex_file_); + } else { + invoke->SetIntrinsic(intrinsic); + } + } + } + } + } + } +} + +std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) { + switch (intrinsic) { + case Intrinsics::kNone: + os << "No intrinsic."; + break; +#define OPTIMIZING_INTRINSICS(Name, IsStatic) \ + case Intrinsics::k ## Name: \ + os << # Name; \ + break; +#include "intrinsics_list.h" +INTRINSICS_LIST(OPTIMIZING_INTRINSICS) +#undef STATIC_INTRINSICS_LIST +#undef VIRTUAL_INTRINSICS_LIST +#undef OPTIMIZING_INTRINSICS + } + return os; +} + +} // namespace art + |