Extend De Morgan factorisation to `HBooleanNot`.
Change-Id: I81aa92277fa136d675e7ef01be8e4acdbd3d3b7c
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index c1e3863..fa2f6bb 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -176,8 +176,8 @@
// We can apply De Morgan's laws if both inputs are Not's and are only used
// by `op`.
- if (left->IsNot() &&
- right->IsNot() &&
+ if (((left->IsNot() && right->IsNot()) ||
+ (left->IsBooleanNot() && right->IsBooleanNot())) &&
left->HasOnlyOneNonEnvironmentUse() &&
right->HasOnlyOneNonEnvironmentUse()) {
// Replace code looking like
@@ -187,8 +187,8 @@
// with
// OR or, a, b (respectively AND)
// NOT dest, or
- HInstruction* src_left = left->AsNot()->GetInput();
- HInstruction* src_right = right->AsNot()->GetInput();
+ HInstruction* src_left = left->InputAt(0);
+ HInstruction* src_right = right->InputAt(0);
uint32_t dex_pc = op->GetDexPc();
// Remove the negations on the inputs.
@@ -204,7 +204,12 @@
} else {
hbin = new (GetGraph()->GetArena()) HAnd(type, src_left, src_right, dex_pc);
}
- HNot* hnot = new (GetGraph()->GetArena()) HNot(type, hbin, dex_pc);
+ HInstruction* hnot;
+ if (left->IsBooleanNot()) {
+ hnot = new (GetGraph()->GetArena()) HBooleanNot(hbin, dex_pc);
+ } else {
+ hnot = new (GetGraph()->GetArena()) HNot(type, hbin, dex_pc);
+ }
op->GetBlock()->InsertInstructionBefore(hbin, op);
op->GetBlock()->ReplaceAndRemoveInstructionWith(op, hnot);
@@ -1308,8 +1313,8 @@
HInstruction* left = instruction->GetLeft();
HInstruction* right = instruction->GetRight();
- if (left->IsNot() &&
- right->IsNot() &&
+ if (((left->IsNot() && right->IsNot()) ||
+ (left->IsBooleanNot() && right->IsBooleanNot())) &&
left->HasOnlyOneNonEnvironmentUse() &&
right->HasOnlyOneNonEnvironmentUse()) {
// Replace code looking like
@@ -1318,8 +1323,8 @@
// XOR dst, nota, notb
// with
// XOR dst, a, b
- instruction->ReplaceInput(left->AsNot()->GetInput(), 0);
- instruction->ReplaceInput(right->AsNot()->GetInput(), 1);
+ instruction->ReplaceInput(left->InputAt(0), 0);
+ instruction->ReplaceInput(right->InputAt(0), 1);
left->GetBlock()->RemoveInstruction(left);
right->GetBlock()->RemoveInstruction(right);
RecordSimplification();
diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java
index c51eda8..41af97b 100644
--- a/test/565-checker-doublenegbitwise/src/Main.java
+++ b/test/565-checker-doublenegbitwise/src/Main.java
@@ -56,6 +56,8 @@
/// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
/// CHECK: Not
/// CHECK-NOT: Not
+
+ /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
/// CHECK-NOT: And
public static int $opt$noinline$andToOr(int a, int b) {
@@ -64,6 +66,43 @@
}
/**
+ * Test transformation of Not/Not/And into Or/Not for boolean negations.
+ * Note that the graph before this instruction simplification pass does not
+ * contain `HBooleanNot` instructions. This is because this transformation
+ * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+ * same pass.
+ */
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier_after_bce (before)
+ /// CHECK: <<P1:z\d+>> ParameterValue
+ /// CHECK: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK: <<And:i\d+>> And [<<Select2>>,<<Select1>>]
+ /// CHECK: Return [<<And>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<Cond1:z\d+>> ParameterValue
+ /// CHECK: <<Cond2:z\d+>> ParameterValue
+ /// CHECK: <<Or:i\d+>> Or [<<Cond2>>,<<Cond1>>]
+ /// CHECK: <<BooleanNot:z\d+>> BooleanNot [<<Or>>]
+ /// CHECK: Return [<<BooleanNot>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: BooleanNot
+ /// CHECK-NOT: BooleanNot
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-NOT: And
+
+ public static boolean $opt$noinline$booleanAndToOr(boolean a, boolean b) {
+ if (doThrow) throw new Error();
+ return !a & !b;
+ }
+
+ /**
* Test transformation of Not/Not/Or into And/Not.
*/
@@ -88,6 +127,8 @@
/// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
/// CHECK: Not
/// CHECK-NOT: Not
+
+ /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
/// CHECK-NOT: Or
public static long $opt$noinline$orToAnd(long a, long b) {
@@ -96,6 +137,43 @@
}
/**
+ * Test transformation of Not/Not/Or into Or/And for boolean negations.
+ * Note that the graph before this instruction simplification pass does not
+ * contain `HBooleanNot` instructions. This is because this transformation
+ * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+ * same pass.
+ */
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier_after_bce (before)
+ /// CHECK: <<P1:z\d+>> ParameterValue
+ /// CHECK: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK: <<Or:i\d+>> Or [<<Select2>>,<<Select1>>]
+ /// CHECK: Return [<<Or>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<Cond1:z\d+>> ParameterValue
+ /// CHECK: <<Cond2:z\d+>> ParameterValue
+ /// CHECK: <<And:i\d+>> And [<<Cond2>>,<<Cond1>>]
+ /// CHECK: <<BooleanNot:z\d+>> BooleanNot [<<And>>]
+ /// CHECK: Return [<<BooleanNot>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: BooleanNot
+ /// CHECK-NOT: BooleanNot
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-NOT: Or
+
+ public static boolean $opt$noinline$booleanOrToAnd(boolean a, boolean b) {
+ if (doThrow) throw new Error();
+ return !a | !b;
+ }
+
+ /**
* Test that the transformation copes with inputs being separated from the
* bitwise operations.
* This is a regression test. The initial logic was inserting the new bitwise
@@ -127,6 +205,8 @@
/// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
/// CHECK: Not
/// CHECK-NOT: Not
+
+ /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
/// CHECK-NOT: Or
public static int $opt$noinline$regressInputsAway(int a, int b) {
@@ -167,6 +247,38 @@
}
/**
+ * Test transformation of Not/Not/Xor into Xor for boolean negations.
+ * Note that the graph before this instruction simplification pass does not
+ * contain `HBooleanNot` instructions. This is because this transformation
+ * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+ * same pass.
+ */
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier_after_bce (before)
+ /// CHECK: <<P1:z\d+>> ParameterValue
+ /// CHECK: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK: <<Xor:i\d+>> Xor [<<Select2>>,<<Select1>>]
+ /// CHECK: Return [<<Xor>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<Cond1:z\d+>> ParameterValue
+ /// CHECK: <<Cond2:z\d+>> ParameterValue
+ /// CHECK: <<Xor:i\d+>> Xor [<<Cond2>>,<<Cond1>>]
+ /// CHECK: Return [<<Xor>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-NOT: BooleanNot
+
+ public static boolean $opt$noinline$booleanNotXorToXor(boolean a, boolean b) {
+ if (doThrow) throw new Error();
+ return !a ^ !b;
+ }
+
+ /**
* Check that no transformation is done when one Not has multiple uses.
*/