summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2014-11-24 13:42:34 +0000
committer Gerrit Code Review <noreply-gerritcodereview@google.com> 2014-11-24 13:42:35 +0000
commit1c51b2b01f7ac9d64838f46d895a534ea243f625 (patch)
treea60f1c689f6dd41cb56f089ff13343eb38efae64
parentc2155048075b4916536f3aa23a88cd483b0f64a2 (diff)
parente0951144d71a4791a5319ec507d84fce373018e0 (diff)
Merge "ART: Add div-zero check elimination to LVN/GVN"
-rw-r--r--compiler/dex/global_value_numbering.cc16
-rw-r--r--compiler/dex/global_value_numbering.h2
-rw-r--r--compiler/dex/global_value_numbering_test.cc43
-rw-r--r--compiler/dex/local_value_numbering.cc69
-rw-r--r--compiler/dex/local_value_numbering.h7
-rw-r--r--compiler/dex/local_value_numbering_test.cc26
-rw-r--r--compiler/dex/mir_graph.cc3
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");