diff options
author | 2014-11-24 13:42:34 +0000 | |
---|---|---|
committer | 2014-11-24 13:42:35 +0000 | |
commit | 1c51b2b01f7ac9d64838f46d895a534ea243f625 (patch) | |
tree | a60f1c689f6dd41cb56f089ff13343eb38efae64 | |
parent | c2155048075b4916536f3aa23a88cd483b0f64a2 (diff) | |
parent | e0951144d71a4791a5319ec507d84fce373018e0 (diff) |
Merge "ART: Add div-zero check elimination to LVN/GVN"
-rw-r--r-- | compiler/dex/global_value_numbering.cc | 16 | ||||
-rw-r--r-- | compiler/dex/global_value_numbering.h | 2 | ||||
-rw-r--r-- | compiler/dex/global_value_numbering_test.cc | 43 | ||||
-rw-r--r-- | compiler/dex/local_value_numbering.cc | 69 | ||||
-rw-r--r-- | compiler/dex/local_value_numbering.h | 7 | ||||
-rw-r--r-- | compiler/dex/local_value_numbering_test.cc | 26 | ||||
-rw-r--r-- | compiler/dex/mir_graph.cc | 3 |
7 files changed, 156 insertions, 10 deletions
diff --git a/compiler/dex/global_value_numbering.cc b/compiler/dex/global_value_numbering.cc index dbe98506b7..c6bac2c7f7 100644 --- a/compiler/dex/global_value_numbering.cc +++ b/compiler/dex/global_value_numbering.cc @@ -191,4 +191,20 @@ bool GlobalValueNumbering::NullCheckedInAllPredecessors( return true; } +bool GlobalValueNumbering::DivZeroCheckedInAllPredecessors( + const ScopedArenaVector<uint16_t>& merge_names) const { + // Implicit parameters: + // - *work_lvn: the LVN for which we're checking predecessors. + // - merge_lvns_: the predecessor LVNs. + DCHECK_EQ(merge_lvns_.size(), merge_names.size()); + for (size_t i = 0, size = merge_lvns_.size(); i != size; ++i) { + const LocalValueNumbering* pred_lvn = merge_lvns_[i]; + uint16_t value_name = merge_names[i]; + if (!pred_lvn->IsValueDivZeroChecked(value_name)) { + return false; + } + } + return true; +} + } // namespace art diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h index 8a93afb872..d72144a051 100644 --- a/compiler/dex/global_value_numbering.h +++ b/compiler/dex/global_value_numbering.h @@ -195,6 +195,8 @@ class GlobalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { bool NullCheckedInAllPredecessors(const ScopedArenaVector<uint16_t>& merge_names) const; + bool DivZeroCheckedInAllPredecessors(const ScopedArenaVector<uint16_t>& merge_names) const; + CompilationUnit* GetCompilationUnit() const { return cu_; } diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc index a788129795..7e3b4d8adf 100644 --- a/compiler/dex/global_value_numbering_test.cc +++ b/compiler/dex/global_value_numbering_test.cc @@ -134,6 +134,8 @@ class GlobalValueNumberingTest : public testing::Test { { bb, opcode, 0u, 0u, 2, { src, src + 1 }, 2, { reg, reg + 1 } } #define DEF_PHI2(bb, reg, src1, src2) \ { bb, static_cast<Instruction::Code>(kMirOpPhi), 0, 0u, 2u, { src1, src2 }, 1, { reg } } +#define DEF_DIV_REM(bb, opcode, result, dividend, divisor) \ + { bb, opcode, 0u, 0u, 2, { dividend, divisor }, 1, { result } } void DoPrepareIFields(const IFieldDef* defs, size_t count) { cu_.mir_graph->ifield_lowering_infos_.clear(); @@ -2222,4 +2224,45 @@ TEST_F(GlobalValueNumberingTest, NormalPathToCatchEntry) { PerformGVN(); } +TEST_F(GlobalValueNumberingTestDiamond, DivZeroCheckDiamond) { + static const MIRDef mirs[] = { + DEF_DIV_REM(3u, Instruction::DIV_INT, 1u, 20u, 21u), + DEF_DIV_REM(3u, Instruction::DIV_INT, 2u, 24u, 21u), + DEF_DIV_REM(3u, Instruction::DIV_INT, 3u, 20u, 23u), + DEF_DIV_REM(4u, Instruction::DIV_INT, 4u, 24u, 22u), + DEF_DIV_REM(4u, Instruction::DIV_INT, 9u, 24u, 25u), + DEF_DIV_REM(5u, Instruction::DIV_INT, 5u, 24u, 21u), + DEF_DIV_REM(5u, Instruction::DIV_INT, 10u, 24u, 26u), + DEF_PHI2(6u, 27u, 25u, 26u), + DEF_DIV_REM(6u, Instruction::DIV_INT, 12u, 20u, 27u), + DEF_DIV_REM(6u, Instruction::DIV_INT, 6u, 24u, 21u), + DEF_DIV_REM(6u, Instruction::DIV_INT, 7u, 20u, 23u), + DEF_DIV_REM(6u, Instruction::DIV_INT, 8u, 20u, 22u), + }; + + static const bool expected_ignore_div_zero_check[] = { + false, // New divisor seen. + true, // Eliminated since it has first divisor as first one. + false, // New divisor seen. + false, // New divisor seen. + false, // New divisor seen. + true, // Eliminated in dominating block. + false, // New divisor seen. + false, // Phi node. + true, // Eliminated on both sides of diamond and merged via phi. + true, // Eliminated in dominating block. + true, // Eliminated in dominating block. + false, // Only eliminated on one path of diamond. + }; + + PrepareMIRs(mirs); + PerformGVN(); + PerformGVNCodeModifications(); + ASSERT_EQ(arraysize(expected_ignore_div_zero_check), mir_count_); + for (size_t i = 0u; i != mir_count_; ++i) { + int expected = expected_ignore_div_zero_check[i] ? MIR_IGNORE_DIV_ZERO_CHECK : 0u; + EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; + } +} + } // namespace art diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc index 90b91bc954..c502b0cb37 100644 --- a/compiler/dex/local_value_numbering.cc +++ b/compiler/dex/local_value_numbering.cc @@ -339,6 +339,7 @@ LocalValueNumbering::LocalValueNumbering(GlobalValueNumbering* gvn, uint16_t id, escaped_array_clobber_set_(EscapedArrayClobberKeyComparator(), allocator->Adapter()), range_checked_(RangeCheckKeyComparator() , allocator->Adapter()), null_checked_(std::less<uint16_t>(), allocator->Adapter()), + div_zero_checked_(std::less<uint16_t>(), allocator->Adapter()), merge_names_(allocator->Adapter()), merge_map_(std::less<ScopedArenaVector<BasicBlockId>>(), allocator->Adapter()), merge_new_memory_version_(kNoValue) { @@ -362,7 +363,8 @@ bool LocalValueNumbering::Equals(const LocalValueNumbering& other) const { escaped_ifield_clobber_set_ == other.escaped_ifield_clobber_set_ && escaped_array_clobber_set_ == other.escaped_array_clobber_set_ && range_checked_ == other.range_checked_ && - null_checked_ == other.null_checked_; + null_checked_ == other.null_checked_ && + div_zero_checked_ == other.div_zero_checked_; } void LocalValueNumbering::MergeOne(const LocalValueNumbering& other, MergeType merge_type) { @@ -379,6 +381,7 @@ void LocalValueNumbering::MergeOne(const LocalValueNumbering& other, MergeType m non_aliasing_refs_ = other.non_aliasing_refs_; range_checked_ = other.range_checked_; null_checked_ = other.null_checked_; + div_zero_checked_ = other.div_zero_checked_; const BasicBlock* pred_bb = gvn_->GetBasicBlock(other.Id()); if (GlobalValueNumbering::HasNullCheckLastInsn(pred_bb, Id())) { @@ -699,6 +702,28 @@ void LocalValueNumbering::MergeNullChecked() { } } +void LocalValueNumbering::MergeDivZeroChecked() { + DCHECK_GE(gvn_->merge_lvns_.size(), 2u); + + // Find the LVN with the least entries in the set. + const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0]; + for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { + if (lvn->div_zero_checked_.size() < least_entries_lvn->div_zero_checked_.size()) { + least_entries_lvn = lvn; + } + } + + // For each div-zero value name check if it's div-zero checked in all the LVNs. + for (const auto& value_name : least_entries_lvn->div_zero_checked_) { + // Merge null_checked_ for this ref. + merge_names_.clear(); + merge_names_.resize(gvn_->merge_lvns_.size(), value_name); + if (gvn_->DivZeroCheckedInAllPredecessors(merge_names_)) { + div_zero_checked_.insert(div_zero_checked_.end(), value_name); + } + } +} + void LocalValueNumbering::MergeSFieldValues(const SFieldToValueMap::value_type& entry, SFieldToValueMap::iterator hint) { uint16_t field_id = entry.first; @@ -931,6 +956,9 @@ void LocalValueNumbering::Merge(MergeType merge_type) { // Merge null_checked_. We may later insert more, such as merged object field values. MergeNullChecked(); + // Now merge the div_zero_checked_. + MergeDivZeroChecked(); + if (merge_type == kCatchMerge) { // Memory is clobbered. New memory version already created, don't merge aliasing locations. return; @@ -1054,6 +1082,20 @@ void LocalValueNumbering::HandleRangeCheck(MIR* mir, uint16_t array, uint16_t in } } +void LocalValueNumbering::HandleDivZeroCheck(MIR* mir, uint16_t reg) { + auto lb = div_zero_checked_.lower_bound(reg); + if (lb != div_zero_checked_.end() && *lb == reg) { + if (LIKELY(gvn_->CanModify())) { + if (gvn_->GetCompilationUnit()->verbose) { + LOG(INFO) << "Removing div zero check for 0x" << std::hex << mir->offset; + } + mir->optimization_flags |= MIR_IGNORE_DIV_ZERO_CHECK; + } + } else { + div_zero_checked_.insert(lb, reg); + } +} + void LocalValueNumbering::HandlePutObject(MIR* mir) { // If we're storing a non-aliasing reference, stop tracking it as non-aliasing now. uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]); @@ -1139,6 +1181,9 @@ uint16_t LocalValueNumbering::HandlePhi(MIR* mir) { if (!wide && gvn_->NullCheckedInAllPredecessors(merge_names_)) { null_checked_.insert(value_name); } + if (gvn_->DivZeroCheckedInAllPredecessors(merge_names_)) { + div_zero_checked_.insert(value_name); + } } } if (wide) { @@ -1695,6 +1740,13 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } break; + case Instruction::DIV_INT: + case Instruction::DIV_INT_2ADDR: + case Instruction::REM_INT: + case Instruction::REM_INT_2ADDR: + HandleDivZeroCheck(mir, GetOperandValue(mir->ssa_rep->uses[1])); + FALLTHROUGH_INTENDED; + case Instruction::CMPG_FLOAT: case Instruction::CMPL_FLOAT: case Instruction::ADD_INT: @@ -1709,10 +1761,6 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::XOR_INT_2ADDR: case Instruction::SUB_INT: case Instruction::SUB_INT_2ADDR: - case Instruction::DIV_INT: - case Instruction::DIV_INT_2ADDR: - case Instruction::REM_INT: - case Instruction::REM_INT_2ADDR: case Instruction::SHL_INT: case Instruction::SHL_INT_2ADDR: case Instruction::SHR_INT: @@ -1727,19 +1775,22 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } break; + case Instruction::DIV_LONG: + case Instruction::REM_LONG: + case Instruction::DIV_LONG_2ADDR: + case Instruction::REM_LONG_2ADDR: + HandleDivZeroCheck(mir, GetOperandValueWide(mir->ssa_rep->uses[2])); + FALLTHROUGH_INTENDED; + case Instruction::ADD_LONG: case Instruction::SUB_LONG: case Instruction::MUL_LONG: - case Instruction::DIV_LONG: - case Instruction::REM_LONG: case Instruction::AND_LONG: case Instruction::OR_LONG: case Instruction::XOR_LONG: case Instruction::ADD_LONG_2ADDR: case Instruction::SUB_LONG_2ADDR: case Instruction::MUL_LONG_2ADDR: - case Instruction::DIV_LONG_2ADDR: - case Instruction::REM_LONG_2ADDR: case Instruction::AND_LONG_2ADDR: case Instruction::OR_LONG_2ADDR: case Instruction::XOR_LONG_2ADDR: diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h index 979fd5a08f..8613f97e38 100644 --- a/compiler/dex/local_value_numbering.h +++ b/compiler/dex/local_value_numbering.h @@ -47,6 +47,10 @@ class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { return null_checked_.find(value_name) != null_checked_.end(); } + bool IsValueDivZeroChecked(uint16_t value_name) const { + return div_zero_checked_.find(value_name) != div_zero_checked_.end(); + } + bool IsSregValue(uint16_t s_reg, uint16_t value_name) const { auto it = sreg_value_map_.find(s_reg); if (it != sreg_value_map_.end()) { @@ -286,6 +290,7 @@ class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { bool IsNonAliasingArray(uint16_t reg, uint16_t type) const; void HandleNullCheck(MIR* mir, uint16_t reg); void HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index); + void HandleDivZeroCheck(MIR* mir, uint16_t reg); void HandlePutObject(MIR* mir); void HandleEscapingRef(uint16_t base); void HandleInvokeArgs(const MIR* mir, const LocalValueNumbering* mir_lvn); @@ -337,6 +342,7 @@ class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { void MergeNonAliasingIFieldValues(const IFieldLocToValueMap::value_type& entry, IFieldLocToValueMap::iterator hint); void MergeNullChecked(); + void MergeDivZeroChecked(); template <typename Map, Map LocalValueNumbering::*map_ptr, typename Versions> void MergeAliasingValues(const typename Map::value_type& entry, typename Map::iterator hint); @@ -371,6 +377,7 @@ class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { // Range check and null check elimination. RangeCheckSet range_checked_; ValueNameSet null_checked_; + ValueNameSet div_zero_checked_; // Reuse one vector for all merges to avoid leaking too much memory on the ArenaStack. ScopedArenaVector<BasicBlockId> merge_names_; diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc index 51aa9d98c8..f4f13f2c76 100644 --- a/compiler/dex/local_value_numbering_test.cc +++ b/compiler/dex/local_value_numbering_test.cc @@ -87,6 +87,10 @@ class LocalValueNumberingTest : public testing::Test { { opcode, 0u, 0u, 1, { reg }, 0, { } } #define DEF_UNIQUE_REF(opcode, reg) \ { opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ... +#define DEF_DIV_REM(opcode, result, dividend, divisor) \ + { opcode, 0u, 0u, 2, { dividend, divisor }, 1, { result } } +#define DEF_DIV_REM_WIDE(opcode, result, dividend, divisor) \ + { opcode, 0u, 0u, 4, { dividend, dividend + 1, divisor, divisor + 1 }, 2, { result, result + 1 } } void DoPrepareIFields(const IFieldDef* defs, size_t count) { cu_.mir_graph->ifield_lowering_infos_.clear(); @@ -744,4 +748,26 @@ TEST_F(LocalValueNumberingTest, ClInitOnSget) { EXPECT_NE(value_names_[0], value_names_[3]); } +TEST_F(LocalValueNumberingTest, DivZeroCheck) { + static const MIRDef mirs[] = { + DEF_DIV_REM(Instruction::DIV_INT, 1u, 10u, 20u), + DEF_DIV_REM(Instruction::DIV_INT, 2u, 20u, 20u), + DEF_DIV_REM(Instruction::DIV_INT_2ADDR, 3u, 10u, 1u), + DEF_DIV_REM(Instruction::REM_INT, 4u, 30u, 20u), + DEF_DIV_REM_WIDE(Instruction::REM_LONG, 5u, 12u, 14u), + DEF_DIV_REM_WIDE(Instruction::DIV_LONG_2ADDR, 7u, 16u, 14u), + }; + + static const bool expected_ignore_div_zero_check[] = { + false, true, false, true, false, true, + }; + + PrepareMIRs(mirs); + PerformLVN(); + for (size_t i = 0u; i != mir_count_; ++i) { + int expected = expected_ignore_div_zero_check[i] ? MIR_IGNORE_DIV_ZERO_CHECK : 0u; + EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; + } +} + } // namespace art diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 8190f95275..c7ebec3687 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -913,7 +913,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suff bb->first_mir_insn ? " | " : " "); for (mir = bb->first_mir_insn; mir; mir = mir->next) { int opcode = mir->dalvikInsn.opcode; - fprintf(file, " {%04x %s %s %s %s %s %s %s %s\\l}%s\\\n", mir->offset, + fprintf(file, " {%04x %s %s %s %s %s %s %s %s %s\\l}%s\\\n", mir->offset, mir->ssa_rep ? GetDalvikDisassembly(mir) : !MIR::DecodedInstruction::IsPseudoMirOp(opcode) ? Instruction::Name(mir->dalvikInsn.opcode) : @@ -925,6 +925,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suff (mir->optimization_flags & MIR_CALLEE) != 0 ? " inlined" : " ", (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0 ? " cl_inited" : " ", (mir->optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0 ? " cl_in_cache" : " ", + (mir->optimization_flags & MIR_IGNORE_DIV_ZERO_CHECK) != 0 ? " no_div_check" : " ", mir->next ? " | " : " "); } fprintf(file, " }\"];\n\n"); |