/* * Copyright (C) 2013 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. */ #ifndef ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_ #define ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_ #include #include "base/mutex.h" #include "base/macros.h" #include "safe_map.h" #include "dex/compiler_enums.h" #include "dex_file.h" #include "locks.h" namespace art { class CallInfo; class Mir2Lir; enum InlineMethodOpcode : uint16_t { kIntrinsicDoubleCvt, kIntrinsicFloatCvt, kIntrinsicReverseBytes, kIntrinsicAbsInt, kIntrinsicAbsLong, kIntrinsicAbsFloat, kIntrinsicAbsDouble, kIntrinsicMinMaxInt, kIntrinsicSqrt, kIntrinsicCharAt, kIntrinsicCompareTo, kIntrinsicIsEmptyOrLength, kIntrinsicIndexOf, kIntrinsicCurrentThread, kIntrinsicPeek, kIntrinsicPoke, kIntrinsicCas, kIntrinsicUnsafeGet, kIntrinsicUnsafePut, kInlineOpNop, kInlineOpReturnArg, kInlineOpConst, kInlineOpIGet, kInlineOpIPut, }; enum InlineMethodFlags : uint16_t { kNoInlineMethodFlags = 0x0000, kInlineIntrinsic = 0x0001, kInlineSpecial = 0x0002, }; struct InlineMethod { InlineMethodOpcode opcode; InlineMethodFlags flags; uint32_t data; }; // IntrinsicFlags are stored in InlineMethod::data enum IntrinsicFlags { kIntrinsicFlagNone = 0, // kIntrinsicMinMaxInt kIntrinsicFlagMax = kIntrinsicFlagNone, kIntrinsicFlagMin = 1, // kIntrinsicIsEmptyOrLength kIntrinsicFlagLength = kIntrinsicFlagNone, kIntrinsicFlagIsEmpty = 1, // kIntrinsicIndexOf kIntrinsicFlagBase0 = 1, // kIntrinsicUnsafeGet, kIntrinsicUnsafePut, kIntrinsicUnsafeCas kIntrinsicFlagIsLong = 1, // kIntrinsicUnsafeGet, kIntrinsicUnsafePut kIntrinsicFlagIsVolatile = 2, // kIntrinsicUnsafePut, kIntrinsicUnsafeCas kIntrinsicFlagIsObject = 4, // kIntrinsicUnsafePut kIntrinsicFlagIsOrdered = 8, }; // Check that OpSize fits into 3 bits (at least the values the inliner uses). COMPILE_ASSERT(kWord < 8 && kLong < 8 && kSingle < 8 && kDouble < 8 && kUnsignedHalf < 8 && kSignedHalf < 8 && kUnsignedByte < 8 && kSignedByte < 8, op_size_field_too_narrow); union InlineIGetIPutData { uint32_t data; struct { uint16_t field; uint32_t op_size : 3; // OpSize uint32_t is_object : 1; uint32_t object_arg : 4; uint32_t src_arg : 4; // iput only uint32_t reserved : 4; } d; }; COMPILE_ASSERT(sizeof(InlineIGetIPutData) == sizeof(uint32_t), InvalidSizeOfInlineIGetIPutData); union InlineReturnArgData { uint32_t data; struct { uint16_t arg; uint32_t op_size : 3; // OpSize uint32_t reserved : 13; } d; }; COMPILE_ASSERT(sizeof(InlineReturnArgData) == sizeof(uint32_t), InvalidSizeOfInlineReturnArgData); /** * Handles inlining of methods from a particular DexFile. * * Intrinsics are a special case of inline methods. The DexFile indices for * all the supported intrinsic methods are looked up once by the FindIntrinsics * function and cached by this class for quick lookup by the method index. * * TODO: Detect short methods (at least getters, setters and empty functions) * from the verifier and mark them for inlining. Inline these methods early * during compilation to allow further optimizations. Similarly, provide * additional information about intrinsics to the early phases of compilation. */ class DexFileMethodInliner { public: DexFileMethodInliner(); ~DexFileMethodInliner(); /** * Analyse method code to determine if the method is a candidate for inlining. * If it is, record its data for later. * * @param method_idx the index of the inlining candidate. * @param code_item a previously verified code item of the method. */ bool AnalyseMethodCode(uint32_t method_idx, const DexFile::CodeItem* code_item) LOCKS_EXCLUDED(lock_); /** * Check whether a particular method index corresponds to an intrinsic function. */ bool IsIntrinsic(uint32_t method_index) LOCKS_EXCLUDED(lock_); /** * Generate code for an intrinsic function invocation. */ bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) LOCKS_EXCLUDED(lock_); /** * Check whether a particular method index corresponds to a special function. */ bool IsSpecial(uint32_t method_index) LOCKS_EXCLUDED(lock_); /** * Generate code for a special function. */ bool GenSpecial(Mir2Lir* backend, uint32_t method_idx); private: /** * To avoid multiple lookups of a class by its descriptor, we cache its * type index in the IndexCache. These are the indexes into the IndexCache * class_indexes array. */ enum ClassCacheIndex : uint8_t { // unit8_t to save space, make larger if needed kClassCacheFirst = 0, kClassCacheBoolean = kClassCacheFirst, kClassCacheByte, kClassCacheChar, kClassCacheShort, kClassCacheInt, kClassCacheLong, kClassCacheFloat, kClassCacheDouble, kClassCacheVoid, kClassCacheJavaLangObject, kClassCacheJavaLangString, kClassCacheJavaLangDouble, kClassCacheJavaLangFloat, kClassCacheJavaLangInteger, kClassCacheJavaLangLong, kClassCacheJavaLangShort, kClassCacheJavaLangMath, kClassCacheJavaLangStrictMath, kClassCacheJavaLangThread, kClassCacheLibcoreIoMemory, kClassCacheSunMiscUnsafe, kClassCacheLast }; /** * To avoid multiple lookups of a method name string, we cache its string * index in the IndexCache. These are the indexes into the IndexCache * name_indexes array. */ enum NameCacheIndex : uint8_t { // unit8_t to save space, make larger if needed kNameCacheFirst = 0, kNameCacheReverseBytes = kNameCacheFirst, kNameCacheDoubleToRawLongBits, kNameCacheLongBitsToDouble, kNameCacheFloatToRawIntBits, kNameCacheIntBitsToFloat, kNameCacheAbs, kNameCacheMax, kNameCacheMin, kNameCacheSqrt, kNameCacheCharAt, kNameCacheCompareTo, kNameCacheIsEmpty, kNameCacheIndexOf, kNameCacheLength, kNameCacheCurrentThread, kNameCachePeekByte, kNameCachePeekIntNative, kNameCachePeekLongNative, kNameCachePeekShortNative, kNameCachePokeByte, kNameCachePokeIntNative, kNameCachePokeLongNative, kNameCachePokeShortNative, kNameCacheCompareAndSwapInt, kNameCacheCompareAndSwapLong, kNameCacheCompareAndSwapObject, kNameCacheGetInt, kNameCacheGetIntVolatile, kNameCachePutInt, kNameCachePutIntVolatile, kNameCachePutOrderedInt, kNameCacheGetLong, kNameCacheGetLongVolatile, kNameCachePutLong, kNameCachePutLongVolatile, kNameCachePutOrderedLong, kNameCacheGetObject, kNameCacheGetObjectVolatile, kNameCachePutObject, kNameCachePutObjectVolatile, kNameCachePutOrderedObject, kNameCacheLast }; /** * To avoid multiple lookups of a method signature, we cache its proto * index in the IndexCache. These are the indexes into the IndexCache * proto_indexes array. */ enum ProtoCacheIndex : uint8_t { // unit8_t to save space, make larger if needed kProtoCacheFirst = 0, kProtoCacheI_I = kProtoCacheFirst, kProtoCacheJ_J, kProtoCacheS_S, kProtoCacheD_D, kProtoCacheF_F, kProtoCacheD_J, kProtoCacheJ_D, kProtoCacheF_I, kProtoCacheI_F, kProtoCacheII_I, kProtoCacheI_C, kProtoCacheString_I, kProtoCache_Z, kProtoCache_I, kProtoCache_Thread, kProtoCacheJ_B, kProtoCacheJ_I, kProtoCacheJ_S, kProtoCacheJB_V, kProtoCacheJI_V, kProtoCacheJJ_V, kProtoCacheJS_V, kProtoCacheObjectJII_Z, kProtoCacheObjectJJJ_Z, kProtoCacheObjectJObjectObject_Z, kProtoCacheObjectJ_I, kProtoCacheObjectJI_V, kProtoCacheObjectJ_J, kProtoCacheObjectJJ_V, kProtoCacheObjectJ_Object, kProtoCacheObjectJObject_V, kProtoCacheLast }; /** * The maximum number of method parameters we support in the ProtoDef. */ static constexpr uint32_t kProtoMaxParams = 6; /** * The method signature (proto) definition using cached class indexes. * The return_type and params are used with the IndexCache to look up * appropriate class indexes to be passed to DexFile::FindProtoId(). */ struct ProtoDef { ClassCacheIndex return_type; uint8_t param_count; ClassCacheIndex params[kProtoMaxParams]; }; /** * The method definition using cached class, name and proto indexes. * The class index, method name index and proto index are used with * IndexCache to look up appropriate parameters for DexFile::FindMethodId(). */ struct MethodDef { ClassCacheIndex declaring_class; NameCacheIndex name; ProtoCacheIndex proto; }; /** * The definition of an intrinsic function binds the method definition * to an Intrinsic. */ struct IntrinsicDef { MethodDef method_def; InlineMethod intrinsic; }; /** * Cache for class, method name and method signature indexes used during * intrinsic function lookup to avoid multiple lookups of the same items. * * Many classes have multiple intrinsics and/or they are used in multiple * method signatures and we want to avoid repeated lookups since they are * not exactly cheap. The method names and method signatures are sometimes * reused and therefore cached as well. */ struct IndexCache { IndexCache(); uint32_t class_indexes[kClassCacheLast - kClassCacheFirst]; uint32_t name_indexes[kNameCacheLast - kNameCacheFirst]; uint32_t proto_indexes[kProtoCacheLast - kProtoCacheFirst]; }; static const char* const kClassCacheNames[]; static const char* const kNameCacheNames[]; static const ProtoDef kProtoCacheDefs[]; static const IntrinsicDef kIntrinsicMethods[]; static const uint32_t kIndexNotFound = static_cast(-1); static const uint32_t kIndexUnresolved = static_cast(-2); static uint32_t FindClassIndex(const DexFile* dex_file, IndexCache* cache, ClassCacheIndex index); static uint32_t FindNameIndex(const DexFile* dex_file, IndexCache* cache, NameCacheIndex index); static uint32_t FindProtoIndex(const DexFile* dex_file, IndexCache* cache, ProtoCacheIndex index); static uint32_t FindMethodIndex(const DexFile* dex_file, IndexCache* cache, const MethodDef& method_def); /** * Find all known intrinsic methods in the dex_file and cache their indices. * * Only DexFileToMethodInlinerMap may call this function to initialize the inliner. */ void FindIntrinsics(const DexFile* dex_file) EXCLUSIVE_LOCKS_REQUIRED(lock_); friend class DexFileToMethodInlinerMap; bool AddInlineMethod(int32_t method_idx, InlineMethodOpcode opcode, InlineMethodFlags flags, uint32_t data) LOCKS_EXCLUDED(lock_); bool AnalyseReturnMethod(int32_t method_idx, const DexFile::CodeItem* code_item, OpSize size) LOCKS_EXCLUDED(lock_); bool AnalyseConstMethod(int32_t method_idx, const DexFile::CodeItem* code_item) LOCKS_EXCLUDED(lock_); bool AnalyseIGetMethod(int32_t method_idx, const DexFile::CodeItem* code_item, OpSize size, bool is_object) LOCKS_EXCLUDED(lock_); bool AnalyseIPutMethod(int32_t method_idx, const DexFile::CodeItem* code_item, OpSize size, bool is_object) LOCKS_EXCLUDED(lock_); ReaderWriterMutex lock_; /* * Maps method indexes (for the particular DexFile) to Intrinsic defintions. */ SafeMap inline_methods_ GUARDED_BY(lock_); const DexFile* dex_file_; DISALLOW_COPY_AND_ASSIGN(DexFileMethodInliner); }; } // namespace art #endif // ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_