summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/dead_code_elimination.cc64
-rw-r--r--test/672-checker-throw-method/src/Main.java72
2 files changed, 136 insertions, 0 deletions
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index cca1055ac8..9fa0f72e80 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -146,6 +146,65 @@ static HConstant* Evaluate(HCondition* condition, HInstruction* left, HInstructi
}
}
+static bool RemoveNonNullControlDependences(HBasicBlock* block, HBasicBlock* throws) {
+ // Test for an if as last statement.
+ if (!block->EndsWithIf()) {
+ return false;
+ }
+ HIf* ifs = block->GetLastInstruction()->AsIf();
+ // Find either:
+ // if obj == null
+ // throws
+ // else
+ // not_throws
+ // or:
+ // if obj != null
+ // not_throws
+ // else
+ // throws
+ HInstruction* cond = ifs->InputAt(0);
+ HBasicBlock* not_throws = nullptr;
+ if (throws == ifs->IfTrueSuccessor() && cond->IsEqual()) {
+ not_throws = ifs->IfFalseSuccessor();
+ } else if (throws == ifs->IfFalseSuccessor() && cond->IsNotEqual()) {
+ not_throws = ifs->IfTrueSuccessor();
+ } else {
+ return false;
+ }
+ DCHECK(cond->IsEqual() || cond->IsNotEqual());
+ HInstruction* obj = cond->InputAt(1);
+ if (obj->IsNullConstant()) {
+ obj = cond->InputAt(0);
+ } else if (!cond->InputAt(0)->IsNullConstant()) {
+ return false;
+ }
+ // Scan all uses of obj and find null check under control dependence.
+ HBoundType* bound = nullptr;
+ const HUseList<HInstruction*>& uses = obj->GetUses();
+ for (auto it = uses.begin(), end = uses.end(); it != end;) {
+ HInstruction* user = it->GetUser();
+ ++it; // increment before possibly replacing
+ if (user->IsNullCheck()) {
+ HBasicBlock* user_block = user->GetBlock();
+ if (user_block != block &&
+ user_block != throws &&
+ block->Dominates(user_block)) {
+ if (bound == nullptr) {
+ ReferenceTypeInfo ti = obj->GetReferenceTypeInfo();
+ bound = new (obj->GetBlock()->GetGraph()->GetAllocator()) HBoundType(obj);
+ bound->SetUpperBound(ti, /*can_be_null*/ false);
+ bound->SetReferenceTypeInfo(ti);
+ bound->SetCanBeNull(false);
+ not_throws->InsertInstructionBefore(bound, not_throws->GetFirstInstruction());
+ }
+ user->ReplaceWith(bound);
+ user_block->RemoveInstruction(user);
+ }
+ }
+ }
+ return bound != nullptr;
+}
+
// Simplify the pattern:
//
// B1
@@ -203,6 +262,11 @@ bool HDeadCodeElimination::SimplifyAlwaysThrows() {
block->ReplaceSuccessor(succ, exit);
rerun_dominance_and_loop_analysis = true;
MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyThrowingInvoke);
+ // Perform a quick follow up optimization on object != null control dependences
+ // that is much cheaper to perform now than in a later phase.
+ if (RemoveNonNullControlDependences(pred, block)) {
+ MaybeRecordStat(stats_, MethodCompilationStat::kRemovedNullCheck);
+ }
}
}
}
diff --git a/test/672-checker-throw-method/src/Main.java b/test/672-checker-throw-method/src/Main.java
index ceb5eb784c..a507133b91 100644
--- a/test/672-checker-throw-method/src/Main.java
+++ b/test/672-checker-throw-method/src/Main.java
@@ -37,6 +37,12 @@ public class Main {
doThrow(par);
}
+ static private void checkNotNullSplitAlt(Object obj, String par) {
+ if (obj != null)
+ return;
+ doThrow(par);
+ }
+
//
// Various ways of enforcing non-null parameter.
// In all cases, par should be subject to code sinking.
@@ -174,6 +180,60 @@ public class Main {
}
//
+ // Various ways of exploiting non-null parameter.
+ // In all cases, implicit null checks are redundant.
+ //
+
+ /// CHECK-START: int Main.deleteNullCheck(int[]) dead_code_elimination$after_inlining (before)
+ /// CHECK: <<Par:l\d+>> ParameterValue
+ /// CHECK: <<Zero:i\d+>> IntConstant 0
+ /// CHECK: <<Null:l\d+>> NullCheck [<<Par>>]
+ /// CHECK: <<Len:i\d+>> ArrayLength [<<Null>>]
+ /// CHECK: <<Check:i\d+>> BoundsCheck [<<Zero>>,<<Len>>]
+ /// CHECK: <<Get:i\d+>> ArrayGet [<<Null>>,<<Check>>]
+ /// CHECK: Return [<<Get>>]
+ //
+ /// CHECK-START: int Main.deleteNullCheck(int[]) dead_code_elimination$after_inlining (after)
+ /// CHECK: <<Par:l\d+>> ParameterValue
+ /// CHECK: <<Zero:i\d+>> IntConstant 0
+ /// CHECK: <<BT:l\d+>> BoundType [<<Par>>]
+ /// CHECK: <<Len:i\d+>> ArrayLength [<<BT>>]
+ /// CHECK: <<Check:i\d+>> BoundsCheck [<<Zero>>,<<Len>>]
+ /// CHECK: <<Get:i\d+>> ArrayGet [<<BT>>,<<Check>>]
+ /// CHECK: Return [<<Get>>]
+ //
+ /// CHECK-START: int Main.deleteNullCheck(int[]) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: NullCheck
+ static public int deleteNullCheck(int[] a) {
+ checkNotNullSplit(a, "a");
+ return a[0];
+ }
+
+ /// CHECK-START: int Main.deleteNullCheckAlt(int[]) dead_code_elimination$after_inlining (before)
+ /// CHECK: NullCheck
+ //
+ /// CHECK-START: int Main.deleteNullCheckAlt(int[]) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: NullCheck
+ static public int deleteNullCheckAlt(int[] a) {
+ checkNotNullSplitAlt(a, "a");
+ return a[0];
+ }
+
+ /// CHECK-START: int Main.deleteNullChecks3(int[], int[], int[]) dead_code_elimination$after_inlining (before)
+ /// CHECK: NullCheck
+ /// CHECK: NullCheck
+ /// CHECK: NullCheck
+ //
+ /// CHECK-START: int Main.deleteNullChecks3(int[], int[], int[]) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: NullCheck
+ static public int deleteNullChecks3(int[] a, int[] b, int[] c) {
+ checkNotNullSplit(a, "a");
+ checkNotNullSplit(b, "b");
+ checkNotNullSplit(c, "c");
+ return a[0] + b[0] + c[0];
+ }
+
+ //
// Test driver.
//
@@ -233,6 +293,18 @@ public class Main {
expectEquals(5, a[i]);
}
+ int[] x = { 11 } ;
+ expectEquals(11, deleteNullCheck(x));
+ int[] y = { 55 } ;
+ int[] z = { 22 } ;
+ expectEquals(88, deleteNullChecks3(x, y, z));
+
+ try {
+ deleteNullCheck(null);
+ System.out.println("should not reach this!");
+ } catch (Error e) {
+ }
+
System.out.println("passed");
}