/* * 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 <stdint.h> #include "base/mutex.h" #include "base/macros.h" #include "safe_map.h" #include "dex/compiler_enums.h" #include "dex_file.h" #include "quick/inline_method_analyser.h" namespace art { namespace verifier { class MethodVerifier; } // namespace verifier struct BasicBlock; struct CallInfo; struct MIR; class MIRGraph; class Mir2Lir; /** * 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 verifier the method verifier holding data about the method to analyse. * @return true if the method is a candidate for inlining, false otherwise. */ bool AnalyseMethodCode(verifier::MethodVerifier* verifier) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_); /** * Check whether a particular method index corresponds to an intrinsic function. */ bool IsIntrinsic(uint32_t method_index, InlineMethod* intrinsic) 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) LOCKS_EXCLUDED(lock_); /** * Try to inline an invoke. */ bool GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, uint32_t method_idx) LOCKS_EXCLUDED(lock_); /** * 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, kClassCacheJavaLangRefReference, kClassCacheJavaLangString, kClassCacheJavaLangDouble, kClassCacheJavaLangFloat, kClassCacheJavaLangInteger, kClassCacheJavaLangLong, kClassCacheJavaLangShort, kClassCacheJavaLangMath, kClassCacheJavaLangStrictMath, kClassCacheJavaLangThread, kClassCacheLibcoreIoMemory, kClassCacheSunMiscUnsafe, kClassCacheJavaLangSystem, kClassCacheJavaLangCharArray, 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, kNameCacheReverse = kNameCacheFirst, kNameCacheReverseBytes, kNameCacheDoubleToRawLongBits, kNameCacheLongBitsToDouble, kNameCacheFloatToRawIntBits, kNameCacheIntBitsToFloat, kNameCacheAbs, kNameCacheMax, kNameCacheMin, kNameCacheSqrt, kNameCacheCeil, kNameCacheFloor, kNameCacheRint, kNameCacheRound, kNameCacheReferenceGetReferent, 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, kNameCacheArrayCopy, 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, kProtoCacheDD_D, kProtoCacheF_F, kProtoCacheFF_F, kProtoCacheD_J, kProtoCacheJ_D, kProtoCacheF_I, kProtoCacheI_F, kProtoCacheII_I, kProtoCacheI_C, kProtoCacheString_I, kProtoCache_Z, kProtoCache_I, kProtoCache_Object, kProtoCache_Thread, kProtoCacheJ_B, kProtoCacheJ_I, kProtoCacheJ_S, kProtoCacheJB_V, kProtoCacheJI_V, kProtoCacheJJ_J, kProtoCacheJJ_V, kProtoCacheJS_V, kProtoCacheObjectJII_Z, kProtoCacheObjectJJJ_Z, kProtoCacheObjectJObjectObject_Z, kProtoCacheObjectJ_I, kProtoCacheObjectJI_V, kProtoCacheObjectJ_J, kProtoCacheObjectJJ_V, kProtoCacheObjectJ_Object, kProtoCacheObjectJObject_V, kProtoCacheCharArrayICharArrayII_V, kProtoCacheLast }; private: /** * 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<uint32_t>(-1); static const uint32_t kIndexUnresolved = static_cast<uint32_t>(-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, const InlineMethod& method) LOCKS_EXCLUDED(lock_); static bool GenInlineConst(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, MIR* move_result, const InlineMethod& method); static bool GenInlineReturnArg(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, MIR* move_result, const InlineMethod& method); static bool GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, MIR* move_result, const InlineMethod& method, uint32_t method_idx); static bool GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, MIR* move_result, const InlineMethod& method, uint32_t method_idx); ReaderWriterMutex lock_; /* * Maps method indexes (for the particular DexFile) to Intrinsic defintions. */ SafeMap<uint32_t, InlineMethod> 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_