diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/dex/compiler_enums.h | 21 | ||||
-rw-r--r-- | compiler/dex/frontend.cc | 11 | ||||
-rw-r--r-- | compiler/dex/frontend.h | 13 | ||||
-rw-r--r-- | compiler/dex/mir_analysis.cc | 6 | ||||
-rw-r--r-- | compiler/dex/mir_graph.cc | 59 | ||||
-rw-r--r-- | compiler/dex/mir_graph.h | 9 | ||||
-rw-r--r-- | compiler/dex/quick/arm/call_arm.cc | 72 | ||||
-rw-r--r-- | compiler/dex/quick/arm/codegen_arm.h | 2 | ||||
-rw-r--r-- | compiler/dex/quick/codegen_util.cc | 24 | ||||
-rw-r--r-- | compiler/dex/quick/dex_file_method_inliner.cc | 235 | ||||
-rw-r--r-- | compiler/dex/quick/dex_file_method_inliner.h | 89 | ||||
-rw-r--r-- | compiler/dex/quick/gen_invoke.cc | 9 | ||||
-rw-r--r-- | compiler/dex/quick/mips/call_mips.cc | 2 | ||||
-rw-r--r-- | compiler/dex/quick/mips/codegen_mips.h | 2 | ||||
-rw-r--r-- | compiler/dex/quick/mir_to_lir.cc | 5 | ||||
-rw-r--r-- | compiler/dex/quick/mir_to_lir.h | 7 | ||||
-rw-r--r-- | compiler/dex/quick/x86/call_x86.cc | 2 | ||||
-rw-r--r-- | compiler/dex/quick/x86/codegen_x86.h | 2 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.cc | 2 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.h | 7 |
20 files changed, 380 insertions, 199 deletions
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index d73f148dcf..35d04ae137 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -302,27 +302,6 @@ enum ThrowKind { kThrowStackOverflow, }; -enum SpecialCaseHandler { - kNoHandler, - kNullMethod, - kConstFunction, - kIGet, - kIGetBoolean, - kIGetObject, - kIGetByte, - kIGetChar, - kIGetShort, - kIGetWide, - kIPut, - kIPutBoolean, - kIPutObject, - kIPutByte, - kIPutChar, - kIPutShort, - kIPutWide, - kIdentity, -}; - enum DividePattern { DivideNone, Divide3, diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 6aabb2ae89..adfbf2f3b5 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -63,21 +63,12 @@ LLVMInfo::LLVMInfo() { LLVMInfo::~LLVMInfo() { } -QuickCompilerContext::QuickCompilerContext() - : inliner_map_(new DexFileToMethodInlinerMap()) { -} - -QuickCompilerContext::~QuickCompilerContext() { -} - extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& driver) { CHECK(driver.GetCompilerContext() == NULL); - driver.SetCompilerContext(new QuickCompilerContext()); } extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& driver) { - delete reinterpret_cast<QuickCompilerContext*>(driver.GetCompilerContext()); - driver.SetCompilerContext(NULL); + CHECK(driver.GetCompilerContext() == NULL); } /* Default optimizer/debug setting for the compiler. */ diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h index bcb8bf05a9..8eb6684d7f 100644 --- a/compiler/dex/frontend.h +++ b/compiler/dex/frontend.h @@ -113,19 +113,6 @@ class LLVMInfo { UniquePtr<art::llvm::IRBuilder> ir_builder_; }; -class QuickCompilerContext { - public: - QuickCompilerContext(); - ~QuickCompilerContext(); - - DexFileToMethodInlinerMap* GetInlinerMap() { - return inliner_map_.get(); - } - - private: - UniquePtr<DexFileToMethodInlinerMap> inliner_map_; -}; - struct CompilationUnit; struct BasicBlock; diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index 89af06e085..ab55333fa7 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -16,6 +16,8 @@ #include "compiler_internals.h" #include "dataflow_iterator-inl.h" +#include "dex/quick/dex_file_method_inliner.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" namespace art { @@ -1052,7 +1054,9 @@ bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) { } // Filter 3: if this method is a special pattern, go ahead and emit the canned pattern. - if (IsSpecialCase()) { + if (cu_->compiler_driver->GetMethodInlinerMap() != nullptr && + cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file) + ->IsSpecial(cu_->method_idx)) { return false; } diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 2a18280133..79edb871fe 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -20,40 +20,13 @@ #include "leb128.h" #include "mir_graph.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" +#include "dex/quick/dex_file_method_inliner.h" + namespace art { #define MAX_PATTERN_LEN 5 -struct CodePattern { - const Instruction::Code opcodes[MAX_PATTERN_LEN]; - const SpecialCaseHandler handler_code; -}; - -static const CodePattern special_patterns[] = { - {{Instruction::RETURN_VOID}, kNullMethod}, - {{Instruction::CONST, Instruction::RETURN}, kConstFunction}, - {{Instruction::CONST_4, Instruction::RETURN}, kConstFunction}, - {{Instruction::CONST_4, Instruction::RETURN_OBJECT}, kConstFunction}, - {{Instruction::CONST_16, Instruction::RETURN}, kConstFunction}, - {{Instruction::IGET, Instruction:: RETURN}, kIGet}, - {{Instruction::IGET_BOOLEAN, Instruction::RETURN}, kIGetBoolean}, - {{Instruction::IGET_OBJECT, Instruction::RETURN_OBJECT}, kIGetObject}, - {{Instruction::IGET_BYTE, Instruction::RETURN}, kIGetByte}, - {{Instruction::IGET_CHAR, Instruction::RETURN}, kIGetChar}, - {{Instruction::IGET_SHORT, Instruction::RETURN}, kIGetShort}, - {{Instruction::IGET_WIDE, Instruction::RETURN_WIDE}, kIGetWide}, - {{Instruction::IPUT, Instruction::RETURN_VOID}, kIPut}, - {{Instruction::IPUT_BOOLEAN, Instruction::RETURN_VOID}, kIPutBoolean}, - {{Instruction::IPUT_OBJECT, Instruction::RETURN_VOID}, kIPutObject}, - {{Instruction::IPUT_BYTE, Instruction::RETURN_VOID}, kIPutByte}, - {{Instruction::IPUT_CHAR, Instruction::RETURN_VOID}, kIPutChar}, - {{Instruction::IPUT_SHORT, Instruction::RETURN_VOID}, kIPutShort}, - {{Instruction::IPUT_WIDE, Instruction::RETURN_VOID}, kIPutWide}, - {{Instruction::RETURN}, kIdentity}, - {{Instruction::RETURN_OBJECT}, kIdentity}, - {{Instruction::RETURN_WIDE}, kIdentity}, -}; - const char* MIRGraph::extended_mir_op_names_[kMirOpLast - kMirOpFirst] = { "Phi", "Copy", @@ -107,7 +80,6 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) method_sreg_(0), attributes_(METHOD_IS_LEAF), // Start with leaf assumption, change on encountering invoke. checkstats_(NULL), - special_case_(kNoHandler), arena_(arena), backward_branches_(0), forward_branches_(0) { @@ -612,13 +584,6 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ /* Identify code range in try blocks and set up the empty catch blocks */ ProcessTryCatchBlocks(); - /* Set up for simple method detection */ - int num_patterns = sizeof(special_patterns)/sizeof(special_patterns[0]); - bool live_pattern = (num_patterns > 0) && !(cu_->disable_opt & (1 << kMatch)); - bool* dead_pattern = - static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_patterns, ArenaAllocator::kAllocMisc)); - int pattern_pos = 0; - /* Parse all instructions and put them into containing basic blocks */ while (code_ptr < code_end) { MIR *insn = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocMIR)); @@ -631,23 +596,6 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ opcode_count_[static_cast<int>(opcode)]++; } - /* Possible simple method? */ - if (live_pattern) { - live_pattern = false; - special_case_ = kNoHandler; - for (int i = 0; i < num_patterns; i++) { - if (!dead_pattern[i]) { - if (special_patterns[i].opcodes[pattern_pos] == opcode) { - live_pattern = true; - special_case_ = special_patterns[i].handler_code; - } else { - dead_pattern[i] = true; - } - } - } - pattern_pos++; - } - int flags = Instruction::FlagsOf(insn->dalvikInsn.opcode); uint64_t df_flags = oat_data_flow_attributes_[insn->dalvikInsn.opcode]; @@ -736,6 +684,7 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ cur_block = next_block; } } + if (cu_->enable_debug & (1 << kDebugDumpCFG)) { DumpCFG("/sdcard/1_post_parse_cfg/", true); } diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index bffec394d6..f9980056b8 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -577,14 +577,6 @@ class MIRGraph { return reg_location_[method_sreg_]; } - bool IsSpecialCase() { - return special_case_ != kNoHandler; - } - - SpecialCaseHandler GetSpecialCase() { - return special_case_; - } - bool IsBackedge(BasicBlock* branch_bb, BasicBlockId target_bb_id) { return ((target_bb_id != NullBasicBlockId) && (GetBasicBlock(target_bb_id)->start_offset <= branch_bb->start_offset)); @@ -789,7 +781,6 @@ class MIRGraph { int method_sreg_; unsigned int attributes_; Checkstats* checkstats_; - SpecialCaseHandler special_case_; ArenaAllocator* arena_; int backward_branches_; int forward_branches_; diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 23ea40712f..8226b24fe9 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -18,6 +18,7 @@ #include "arm_lir.h" #include "codegen_arm.h" +#include "dex/quick/dex_file_method_inliner.h" #include "dex/quick/mir_to_lir-inl.h" #include "entrypoints/quick/quick_entrypoints.h" @@ -217,58 +218,43 @@ MIR* ArmMir2Lir::SpecialIdentity(MIR* mir) { * Special-case code genration for simple non-throwing leaf methods. */ void ArmMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, - SpecialCaseHandler special_case) { + const InlineMethod& special) { + // TODO: Generate the method using only the data in special. (Requires FastInstance() field + // validation in DexFileMethodInliner::AnalyseIGetMethod()/AnalyseIPutMethod().) + DCHECK(special.flags & kInlineSpecial); current_dalvik_offset_ = mir->offset; MIR* next_mir = NULL; - switch (special_case) { - case kNullMethod: + switch (special.opcode) { + case kInlineOpNop: DCHECK(mir->dalvikInsn.opcode == Instruction::RETURN_VOID); next_mir = mir; break; - case kConstFunction: + case kInlineOpConst: ArmMir2Lir::GenPrintLabel(mir); - LoadConstant(rARM_RET0, mir->dalvikInsn.vB); + LoadConstant(rARM_RET0, special.data); next_mir = GetNextMir(&bb, mir); break; - case kIGet: - next_mir = SpecialIGet(&bb, mir, kWord, false, false); + case kInlineOpIGet: { + InlineIGetIPutData data; + data.data = special.data; + OpSize op_size = static_cast<OpSize>(data.d.op_size); + DCHECK_NE(data.d.op_size, kDouble); // The inliner doesn't distinguish kDouble, uses kLong. + bool long_or_double = (data.d.op_size == kLong); + bool is_object = data.d.is_object; + next_mir = SpecialIGet(&bb, mir, op_size, long_or_double, is_object); break; - case kIGetBoolean: - case kIGetByte: - next_mir = SpecialIGet(&bb, mir, kUnsignedByte, false, false); - break; - case kIGetObject: - next_mir = SpecialIGet(&bb, mir, kWord, false, true); - break; - case kIGetChar: - next_mir = SpecialIGet(&bb, mir, kUnsignedHalf, false, false); - break; - case kIGetShort: - next_mir = SpecialIGet(&bb, mir, kSignedHalf, false, false); - break; - case kIGetWide: - next_mir = SpecialIGet(&bb, mir, kLong, true, false); - break; - case kIPut: - next_mir = SpecialIPut(&bb, mir, kWord, false, false); - break; - case kIPutBoolean: - case kIPutByte: - next_mir = SpecialIPut(&bb, mir, kUnsignedByte, false, false); - break; - case kIPutObject: - next_mir = SpecialIPut(&bb, mir, kWord, false, true); - break; - case kIPutChar: - next_mir = SpecialIPut(&bb, mir, kUnsignedHalf, false, false); - break; - case kIPutShort: - next_mir = SpecialIPut(&bb, mir, kSignedHalf, false, false); - break; - case kIPutWide: - next_mir = SpecialIPut(&bb, mir, kLong, true, false); + } + case kInlineOpIPut: { + InlineIGetIPutData data; + data.data = special.data; + OpSize op_size = static_cast<OpSize>(data.d.op_size); + DCHECK_NE(data.d.op_size, kDouble); // The inliner doesn't distinguish kDouble, uses kLong. + bool long_or_double = (data.d.op_size == kLong); + bool is_object = data.d.is_object; + next_mir = SpecialIPut(&bb, mir, op_size, long_or_double, is_object); break; - case kIdentity: + } + case kInlineOpReturnArg: next_mir = SpecialIdentity(mir); break; default: @@ -276,7 +262,7 @@ void ArmMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, } if (next_mir != NULL) { current_dalvik_offset_ = next_mir->offset; - if (special_case != kIdentity) { + if (special.opcode != kInlineOpReturnArg) { ArmMir2Lir::GenPrintLabel(next_mir); } NewLIR1(kThumbBx, rARM_LR); diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 25ddc9451d..c04f1d6abf 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -135,7 +135,7 @@ class ArmMir2Lir : public Mir2Lir { void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); - void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); + void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special); // Required for target - single operation generators. LIR* OpUnconditionalBranch(LIR* target); diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index bae8ff339a..65286d5d2f 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -19,6 +19,8 @@ #include "gc_map.h" #include "mapping_table.h" #include "mir_to_lir-inl.h" +#include "dex/quick/dex_file_method_inliner.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" #include "dex/verified_methods_data.h" #include "verifier/dex_gc_map.h" #include "verifier/method_verifier.h" @@ -983,8 +985,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena core_spill_mask_(0), fp_spill_mask_(0), first_lir_insn_(NULL), - last_lir_insn_(NULL), - inliner_(nullptr) { + last_lir_insn_(NULL) { promotion_map_ = static_cast<PromotionMap*> (arena_->Alloc((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) * sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc)); @@ -1000,15 +1001,16 @@ void Mir2Lir::Materialize() { /* Allocate Registers using simple local allocation scheme */ SimpleRegAlloc(); - if (mir_graph_->IsSpecialCase()) { - /* - * Custom codegen for special cases. If for any reason the - * special codegen doesn't succeed, first_lir_insn_ will - * set to NULL; - */ - cu_->NewTimingSplit("SpecialMIR2LIR"); - SpecialMIR2LIR(mir_graph_->GetSpecialCase()); - } + /* + * Custom codegen for special cases. If for any reason the + * special codegen doesn't succeed, first_lir_insn_ will + * set to NULL; + */ + // TODO: Clean up GenSpecial() and return true only if special implementation is emitted. + // Currently, GenSpecial() returns IsSpecial() but doesn't check after SpecialMIR2LIR(). + DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr); + cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file) + ->GenSpecial(this, cu_->method_idx); /* Convert MIR to LIR, etc. */ if (first_lir_insn_ == NULL) { diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index ba98225024..3c331e12d9 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -18,16 +18,19 @@ #include "base/macros.h" #include "base/mutex.h" #include "base/mutex-inl.h" +#include "locks.h" #include "thread.h" #include "thread-inl.h" #include "dex/mir_graph.h" +#include "dex_instruction.h" +#include "dex_instruction-inl.h" #include "dex_file_method_inliner.h" namespace art { const uint32_t DexFileMethodInliner::kIndexUnresolved; -const char* DexFileMethodInliner::kClassCacheNames[] = { +const char* const DexFileMethodInliner::kClassCacheNames[] = { "Z", // kClassCacheBoolean "B", // kClassCacheByte "C", // kClassCacheChar @@ -51,7 +54,7 @@ const char* DexFileMethodInliner::kClassCacheNames[] = { "Lsun/misc/Unsafe;", // kClassCacheSunMiscUnsafe }; -const char* DexFileMethodInliner::kNameCacheNames[] = { +const char* const DexFileMethodInliner::kNameCacheNames[] = { "reverseBytes", // kNameCacheReverseBytes "doubleToRawLongBits", // kNameCacheDoubleToRawLongBits "longBitsToDouble", // kNameCacheLongBitsToDouble @@ -164,7 +167,7 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = { #define INTRINSIC(c, n, p, o, d) \ - { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } } + { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineIntrinsic, d } } INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), @@ -245,17 +248,71 @@ DexFileMethodInliner::DexFileMethodInliner() DexFileMethodInliner::~DexFileMethodInliner() { } +bool DexFileMethodInliner::AnalyseMethodCode(uint32_t method_idx, + const DexFile::CodeItem* code_item) { + // We currently support only plain return or 2-instruction methods. + + DCHECK_NE(code_item->insns_size_in_code_units_, 0u); + const Instruction* instruction = Instruction::At(code_item->insns_); + Instruction::Code opcode = instruction->Opcode(); + + switch (opcode) { + case Instruction::RETURN_VOID: + return AddInlineMethod(method_idx, kInlineOpNop, kInlineSpecial, 0); + case Instruction::RETURN: + case Instruction::RETURN_OBJECT: + return AnalyseReturnMethod(method_idx, code_item, kWord); + case Instruction::RETURN_WIDE: + return AnalyseReturnMethod(method_idx, code_item, kLong); + case Instruction::CONST: + case Instruction::CONST_4: + case Instruction::CONST_16: + case Instruction::CONST_HIGH16: + // TODO: Support wide constants (RETURN_WIDE). + return AnalyseConstMethod(method_idx, code_item); + case Instruction::IGET: + return AnalyseIGetMethod(method_idx, code_item, kWord, false); + case Instruction::IGET_OBJECT: + return AnalyseIGetMethod(method_idx, code_item, kWord, true); + case Instruction::IGET_BOOLEAN: + case Instruction::IGET_BYTE: + return AnalyseIGetMethod(method_idx, code_item, kSignedByte, false); + case Instruction::IGET_CHAR: + return AnalyseIGetMethod(method_idx, code_item, kUnsignedHalf, false); + case Instruction::IGET_SHORT: + return AnalyseIGetMethod(method_idx, code_item, kSignedHalf, false); + case Instruction::IGET_WIDE: + return AnalyseIGetMethod(method_idx, code_item, kLong, false); + case Instruction::IPUT: + return AnalyseIPutMethod(method_idx, code_item, kWord, false); + case Instruction::IPUT_OBJECT: + return AnalyseIPutMethod(method_idx, code_item, kWord, true); + case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BYTE: + return AnalyseIPutMethod(method_idx, code_item, kSignedByte, false); + case Instruction::IPUT_CHAR: + return AnalyseIPutMethod(method_idx, code_item, kUnsignedHalf, false); + case Instruction::IPUT_SHORT: + return AnalyseIPutMethod(method_idx, code_item, kSignedHalf, false); + case Instruction::IPUT_WIDE: + return AnalyseIPutMethod(method_idx, code_item, kLong, false); + default: + return false; + } +} + bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) { ReaderMutexLock mu(Thread::Current(), lock_); - return intrinsics_.find(method_index) != intrinsics_.end(); + auto it = inline_methods_.find(method_index); + return it != inline_methods_.end() && (it->second.flags & kInlineIntrinsic) != 0; } bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { - Intrinsic intrinsic; + InlineMethod intrinsic; { ReaderMutexLock mu(Thread::Current(), lock_); - auto it = intrinsics_.find(info->index); - if (it == intrinsics_.end()) { + auto it = inline_methods_.find(info->index); + if (it == inline_methods_.end() || (it->second.flags & kInlineIntrinsic) == 0) { return false; } intrinsic = it->second; @@ -306,6 +363,27 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { } } +bool DexFileMethodInliner::IsSpecial(uint32_t method_index) { + ReaderMutexLock mu(Thread::Current(), lock_); + auto it = inline_methods_.find(method_index); + return it != inline_methods_.end() && (it->second.flags & kInlineSpecial) != 0; +} + +bool DexFileMethodInliner::GenSpecial(Mir2Lir* backend, uint32_t method_idx) { + InlineMethod special; + { + ReaderMutexLock mu(Thread::Current(), lock_); + auto it = inline_methods_.find(method_idx); + if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) { + return false; + } + special = it->second; + } + // TODO: Return true only if special implementation is emitted. + backend->SpecialMIR2LIR(special); + return true; +} + uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCache* cache, ClassCacheIndex index) { uint32_t* class_index = &cache->class_indexes[index]; @@ -418,13 +496,148 @@ void DexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { DCHECK(dex_file_ == nullptr); IndexCache cache; for (const IntrinsicDef& def : kIntrinsicMethods) { - uint32_t method_id = FindMethodIndex(dex_file, &cache, def.method_def); - if (method_id != kIndexNotFound) { - DCHECK(intrinsics_.find(method_id) == intrinsics_.end()); - intrinsics_[method_id] = def.intrinsic; + uint32_t method_idx = FindMethodIndex(dex_file, &cache, def.method_def); + if (method_idx != kIndexNotFound) { + DCHECK(inline_methods_.find(method_idx) == inline_methods_.end()); + inline_methods_[method_idx] = def.intrinsic; } } dex_file_ = dex_file; } +bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, InlineMethodOpcode opcode, + uint16_t flags, uint32_t data) { + WriterMutexLock mu(Thread::Current(), lock_); + InlineMethod* im = &inline_methods_[method_idx]; + if (im->flags == 0) { + *im = InlineMethod{opcode, flags, data}; + return true; + } else { + // TODO: Warning about a method being already inlined? + LOG(WARNING) << "Inliner: " << PrettyMethod(method_idx, *dex_file_) << " already inline, " + << im->flags; + return false; + } +} + +bool DexFileMethodInliner::AnalyseReturnMethod(int32_t method_idx, + const DexFile::CodeItem* code_item, OpSize size) { + const Instruction* return_instruction = Instruction::At(code_item->insns_); + if (return_instruction->Opcode() == Instruction::RETURN_VOID) { + return AddInlineMethod(method_idx, kInlineOpNop, kInlineSpecial, 0); + } + uint32_t reg = return_instruction->VRegA_11x(); + uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; + DCHECK_GE(reg, arg_start); + DCHECK_LT(size == kLong ? reg + 1 : reg, code_item->registers_size_); + + InlineReturnArgData data; + data.d.arg = reg - arg_start; + data.d.op_size = size; + data.d.reserved = 0; + return AddInlineMethod(method_idx, kInlineOpReturnArg, kInlineSpecial, data.data); +} + +bool DexFileMethodInliner::AnalyseConstMethod(int32_t method_idx, + const DexFile::CodeItem* code_item) { + const Instruction* instruction = Instruction::At(code_item->insns_); + const Instruction* return_instruction = instruction->Next(); + Instruction::Code return_opcode = return_instruction->Opcode(); + if (return_opcode != Instruction::RETURN && + return_opcode != Instruction::RETURN_OBJECT) { + return false; + } + + uint32_t return_reg = return_instruction->VRegA_11x(); + DCHECK_LT(return_reg, code_item->registers_size_); + + uint32_t vA, vB, dummy; + uint64_t dummy_wide; + instruction->Decode(vA, vB, dummy_wide, dummy, nullptr); + if (instruction->Opcode() == Instruction::CONST_HIGH16) { + vB <<= 16; + } + DCHECK_LT(vA, code_item->registers_size_); + if (vA != return_reg) { + return false; // Not returning the value set by const? + } + if (return_opcode == Instruction::RETURN_OBJECT && vB != 0) { + return false; // Returning non-null reference constant? + } + return AddInlineMethod(method_idx, kInlineOpConst, kInlineSpecial, vB); +} + +bool DexFileMethodInliner::AnalyseIGetMethod(int32_t method_idx, const DexFile::CodeItem* code_item, + OpSize size, bool is_object) { + const Instruction* instruction = Instruction::At(code_item->insns_); + Instruction::Code opcode = instruction->Opcode(); + const Instruction* return_instruction = instruction->Next(); + Instruction::Code return_opcode = return_instruction->Opcode(); + if (!(return_opcode == Instruction::RETURN && size != kLong) && + !(return_opcode == Instruction::RETURN_WIDE && size == kLong) && + !(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT)) { + return false; + } + + uint32_t return_reg = return_instruction->VRegA_11x(); + DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg, + code_item->registers_size_); + + uint32_t vA, vB, vC; + uint64_t dummy_wide; + instruction->Decode(vA, vB, dummy_wide, vC, nullptr); + uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; + DCHECK_GE(vB, arg_start); + DCHECK_LT(vB, code_item->registers_size_); + DCHECK_LT(size == kLong ? vA + 1 : vA, code_item->registers_size_); + if (vA != return_reg) { + return false; // Not returning the value retrieved by iget? + } + + // TODO: Check that the field is FastInstance(). + + InlineIGetIPutData data; + data.d.field = vC; + data.d.op_size = size; + data.d.is_object = is_object; + data.d.object_arg = vB - arg_start; // Allow iget on any register, not just "this" + data.d.src_arg = 0; + data.d.reserved = 0; + return AddInlineMethod(method_idx, kInlineOpIGet, kInlineSpecial, data.data); +} + +bool DexFileMethodInliner::AnalyseIPutMethod(int32_t method_idx, const DexFile::CodeItem* code_item, + OpSize size, bool is_object) { + const Instruction* instruction = Instruction::At(code_item->insns_); + const Instruction* return_instruction = instruction->Next(); + if (return_instruction->Opcode() != Instruction::RETURN_VOID) { + // TODO: Support returning an argument. + // This is needed by builder classes and generated accessor setters. + // builder.setX(value): iput value, this, fieldX; return-object this; + // object.access$nnn(value): iput value, this, fieldX; return value; + // Use InlineIGetIPutData::d::reserved to hold the information. + return false; + } + + uint32_t vA, vB, vC; + uint64_t dummy_wide; + instruction->Decode(vA, vB, dummy_wide, vC, nullptr); + uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; + DCHECK_GE(vB, arg_start); + DCHECK_GE(vA, arg_start); + DCHECK_LT(vB, code_item->registers_size_); + DCHECK_LT(size == kLong ? vA + 1 : vA, code_item->registers_size_); + + // TODO: Check that the field (vC) is FastInstance(). + + InlineIGetIPutData data; + data.d.field = vC; + data.d.op_size = size; + data.d.is_object = is_object; + data.d.object_arg = vB - arg_start; // Allow iput on any register, not just "this" + data.d.src_arg = vA - arg_start; + data.d.reserved = 0; + return AddInlineMethod(method_idx, kInlineOpIPut, kInlineSpecial, data.data); +} + } // namespace art diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 9198f2a29d..06de4fef27 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -21,15 +21,16 @@ #include <map> #include "base/mutex.h" #include "base/macros.h" +#include "dex/compiler_enums.h" +#include "dex_file.h" #include "locks.h" namespace art { class CallInfo; -class DexFile; class Mir2Lir; -enum IntrinsicOpcode { +enum InlineMethodOpcode : uint16_t { kIntrinsicDoubleCvt, kIntrinsicFloatCvt, kIntrinsicReverseBytes, @@ -47,8 +48,26 @@ enum IntrinsicOpcode { kIntrinsicCas, kIntrinsicUnsafeGet, kIntrinsicUnsafePut, + + kInlineOpNop, + kInlineOpReturnArg, + kInlineOpConst, + kInlineOpIGet, + kInlineOpIPut, +}; + +enum InlineMethodFlags { + kInlineIntrinsic = 0x0001, + kInlineSpecial = 0x0002, +}; + +struct InlineMethod { + uint16_t opcode; + uint16_t flags; + uint32_t data; }; +// IntrinsicFlags are stored in InlineMethod::data enum IntrinsicFlags { kIntrinsicFlagNone = 0, @@ -73,10 +92,32 @@ enum IntrinsicFlags { kIntrinsicFlagIsOrdered = 8, }; -struct Intrinsic { - IntrinsicOpcode opcode; +// 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. @@ -96,6 +137,16 @@ class 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_); @@ -105,6 +156,16 @@ class DexFileMethodInliner { */ 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 @@ -261,7 +322,7 @@ class DexFileMethodInliner { */ struct IntrinsicDef { MethodDef method_def; - Intrinsic intrinsic; + InlineMethod intrinsic; }; /** @@ -281,8 +342,8 @@ class DexFileMethodInliner { uint32_t proto_indexes[kProtoCacheLast - kProtoCacheFirst]; }; - static const char* kClassCacheNames[]; - static const char* kNameCacheNames[]; + static const char* const kClassCacheNames[]; + static const char* const kNameCacheNames[]; static const ProtoDef kProtoCacheDefs[]; static const IntrinsicDef kIntrinsicMethods[]; @@ -307,11 +368,23 @@ class DexFileMethodInliner { friend class DexFileToMethodInlinerMap; + bool AddInlineMethod(int32_t method_idx, InlineMethodOpcode opcode, + uint16_t 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. */ - std::map<uint32_t, Intrinsic> intrinsics_ GUARDED_BY(lock_); + std::map<uint32_t, InlineMethod> inline_methods_ GUARDED_BY(lock_); const DexFile* dex_file_; DISALLOW_COPY_AND_ASSIGN(DexFileMethodInliner); diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index ee6f9c8fe9..82a1932fe1 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1239,12 +1239,9 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, void Mir2Lir::GenInvoke(CallInfo* info) { if (!(info->opt_flags & MIR_INLINED)) { - if (inliner_ == nullptr) { - QuickCompilerContext* context = reinterpret_cast<QuickCompilerContext*>( - cu_->compiler_driver->GetCompilerContext()); - inliner_ = context->GetInlinerMap()->GetMethodInliner(cu_->dex_file); - } - if (inliner_->GenIntrinsic(this, info)) { + DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr); + if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file) + ->GenIntrinsic(this, info)) { return; } } diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index 21d55633df..14f49aa367 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -24,7 +24,7 @@ namespace art { void MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, - SpecialCaseHandler special_case) { + const InlineMethod& special) { // TODO } diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 450a44f788..97dc2b3ad1 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -133,7 +133,7 @@ class MipsMir2Lir : public Mir2Lir { void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); + void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special); // Required for target - single operation generators. LIR* OpUnconditionalBranch(LIR* target); diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 19d04be6df..c5bbae1923 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -789,7 +789,8 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { return false; } -void Mir2Lir::SpecialMIR2LIR(SpecialCaseHandler special_case) { +void Mir2Lir::SpecialMIR2LIR(const InlineMethod& special) { + cu_->NewTimingSplit("SpecialMIR2LIR"); // Find the first DalvikByteCode block. int num_reachable_blocks = mir_graph_->GetNumReachableBlocks(); BasicBlock*bb = NULL; @@ -815,7 +816,7 @@ void Mir2Lir::SpecialMIR2LIR(SpecialCaseHandler special_case) { ResetDefTracking(); ClobberAllRegs(); - GenSpecialCase(bb, mir, special_case); + GenSpecialCase(bb, mir, special); } void Mir2Lir::MethodMIR2LIR() { diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 8415cbfb6d..2a25f2f910 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -105,6 +105,7 @@ typedef uint32_t CodeOffset; // Native code offset in bytes. struct BasicBlock; struct CallInfo; struct CompilationUnit; +struct InlineMethod; struct MIR; struct LIR; struct RegLocation; @@ -582,7 +583,7 @@ class Mir2Lir : public Backend { void CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list); void HandleExtendedMethodMIR(BasicBlock* bb, MIR* mir); bool MethodBlockCodeGen(BasicBlock* bb); - void SpecialMIR2LIR(SpecialCaseHandler special_case); + void SpecialMIR2LIR(const InlineMethod& special); void MethodMIR2LIR(); @@ -703,7 +704,7 @@ class Mir2Lir : public Backend { virtual void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) = 0; virtual void GenSpecialCase(BasicBlock* bb, MIR* mir, - SpecialCaseHandler special_case) = 0; + const InlineMethod& special) = 0; virtual void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_dest, int scale) = 0; virtual void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, @@ -826,8 +827,6 @@ class Mir2Lir : public Backend { unsigned int fp_spill_mask_; LIR* first_lir_insn_; LIR* last_lir_insn_; - // Lazily retrieved method inliner for intrinsics. - DexFileMethodInliner* inliner_; }; // Class Mir2Lir } // namespace art diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 17924b0f08..4267b5b784 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -23,7 +23,7 @@ namespace art { void X86Mir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, - SpecialCaseHandler special_case) { + const InlineMethod& special) { // TODO } diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 6552607d9e..2d625c2134 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -133,7 +133,7 @@ class X86Mir2Lir : public Mir2Lir { void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); - void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); + void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special); // Single operation generators. LIR* OpUnconditionalBranch(LIR* target); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index dbebd26db8..11245419f7 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -337,11 +337,13 @@ extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver& driver, std::string const& filename); CompilerDriver::CompilerDriver(VerifiedMethodsData* verified_methods_data, + DexFileToMethodInlinerMap* method_inliner_map, CompilerBackend compiler_backend, InstructionSet instruction_set, InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, size_t thread_count, bool dump_stats) : verified_methods_data_(verified_methods_data), + method_inliner_map_(method_inliner_map), compiler_backend_(compiler_backend), instruction_set_(instruction_set), instruction_set_features_(instruction_set_features), diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 14bfde672d..f4cc84dfe7 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -41,6 +41,7 @@ namespace art { class AOTCompilationStats; class ParallelCompilationManager; class DexCompilationUnit; +class DexFileToMethodInlinerMap; class OatWriter; class TimingLogger; class VerifiedMethodsData; @@ -92,6 +93,7 @@ class CompilerDriver { // can assume will be in the image, with NULL implying all available // classes. explicit CompilerDriver(VerifiedMethodsData* verified_methods_data, + DexFileToMethodInlinerMap* method_inliner_map, CompilerBackend compiler_backend, InstructionSet instruction_set, InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, @@ -111,6 +113,10 @@ class CompilerDriver { return verified_methods_data_; } + DexFileToMethodInlinerMap* GetMethodInlinerMap() const { + return method_inliner_map_; + } + const InstructionSet& GetInstructionSet() const { return instruction_set_; } @@ -397,6 +403,7 @@ class CompilerDriver { std::vector<const PatchInformation*> methods_to_patch_; VerifiedMethodsData* verified_methods_data_; + DexFileToMethodInlinerMap* method_inliner_map_; CompilerBackend compiler_backend_; |