From: Kyrylo Tkachov <[email protected]>
A comparison mask m = cmp ? CST1 : CST2 feeding several tests
if (m == C) is kept as a constant-pair PHI, so each test is a separate
diamond and the backward jump threader tail-duplicates the chain; the
selects then come out as branches instead of conditional moves
(PR125672).
Let phiopt_early_allow accept the CMP, (convert)(compare) and
-(convert)(compare) forms: sequences consisting only of conversions,
negations and comparisons. An equality test of the materialized mask
folds to the comparison itself, exposing the shared condition for the
pass_merge_diamonds change that follows.
match_simplify_replacement gains two checks for the new forms:
* the user predictor check now also covers the multi-statement mask
sequences;
* a mask is not materialized for a diamond that other paths enter,
through its join or through a shared arm. That would leave a PHI
mixing the mask with the other paths' values, which nothing
downstream recovers, and lose the ccmp chains tree-ifcombine forms
from the original branches.
Testsuite: pr21090.c, pr68198.c, predict-15.c and pr109071_6.c keep
testing their original shapes with phiopt1 disabled, and new
pr21090-2.c covers the improved folding; vrp102.c scans evrp, where
the tested property is now visible; auto-init-uninit-15.c now warns at
the real use instead of the inlined location, so its previously
xfailed alternative becomes the expectation.
Run-time neutral on 731.astcenc_r by itself; it enables the 20%
improvement delivered by the following patch.
Bootstrapped and regression tested on aarch64-unknown-linux-gnu.
gcc/ChangeLog:
PR tree-optimization/125672
PR tree-optimization/126043
* tree-ssa-phiopt.cc (phiopt_early_allow): Accept CMP,
(convert)(compare) and -(convert)(compare) sequences consisting
only of conversions, negations and comparisons.
(match_simplify_replacement): Extend the predictor keeping check
to the new mask forms. Reject materializing a mask for a diamond
whose join has additional predecessors or whose arm is shared
with another path.
gcc/testsuite/ChangeLog:
PR tree-optimization/125672
PR tree-optimization/126043
* gcc.dg/tree-ssa/pr125672-1.c: New test.
* gcc.dg/tree-ssa/pr21090-2.c: New test.
* gcc.dg/tree-ssa/pr21090.c: Disable phiopt1.
* gcc.dg/tree-ssa/pr68198.c: Likewise.
* gcc.dg/predict-15.c: Likewise.
* gcc.dg/pr109071_6.c: Likewise.
* gcc.dg/tree-ssa/vrp102.c: Scan evrp instead of vrp1.
* gcc.dg/auto-init-uninit-15.c: Expect the warning at the use in
bar instead of the inlined location.
Signed-off-by: Kyrylo Tkachov <[email protected]>
---
gcc/testsuite/gcc.dg/auto-init-uninit-15.c | 12 ++--
gcc/testsuite/gcc.dg/pr109071_6.c | 5 +-
gcc/testsuite/gcc.dg/predict-15.c | 4 +-
gcc/testsuite/gcc.dg/tree-ssa/pr125672-1.c | 42 ++++++++++++
gcc/testsuite/gcc.dg/tree-ssa/pr21090-2.c | 27 ++++++++
gcc/testsuite/gcc.dg/tree-ssa/pr21090.c | 5 +-
gcc/testsuite/gcc.dg/tree-ssa/pr68198.c | 4 +-
gcc/testsuite/gcc.dg/tree-ssa/vrp102.c | 4 +-
gcc/tree-ssa-phiopt.cc | 80 ++++++++++++++++++----
9 files changed, 158 insertions(+), 25 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr125672-1.c
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr21090-2.c
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-15.c
b/gcc/testsuite/gcc.dg/auto-init-uninit-15.c
index 121f0cff274..8d5fdc233b3 100644
--- a/gcc/testsuite/gcc.dg/auto-init-uninit-15.c
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-15.c
@@ -1,16 +1,14 @@
/* PR tree-optimization/17506
- We issue an uninitialized variable warning at a wrong location at
- line 11, which is very confusing. Make sure we print out a note to
- make it less confusing. (not xfailed alternative)
- But it is of course ok if we warn in bar about uninitialized use
- of j. (not xfailed alternative) */
+ We used to issue an uninitialized variable warning at a wrong (inlined)
+ location, which was very confusing. Since early phiopt materializes
+ foo's comparison the warning is issued at the real use in bar, with the
note pointing at the declaration. */
/* { dg-do compile } */
/* { dg-options "-O1 -Wuninitialized -ftrivial-auto-var-init=zero" } */
inline int
foo (int i)
{
- if (i) /* { dg-warning "used uninitialized" } */
+ if (i)
return 1;
return 0;
}
@@ -21,6 +19,6 @@ void
bar (void)
{
int j; /* { dg-message "note: 'j' was declared here" "" } */
- for (; foo (j); ++j) /* { dg-warning "'j' is used uninitialized" "" { xfail
*-*-* } } */
+ for (; foo (j); ++j) /* { dg-warning "'j' is used uninitialized" } */
baz ();
}
diff --git a/gcc/testsuite/gcc.dg/pr109071_6.c
b/gcc/testsuite/gcc.dg/pr109071_6.c
index eddf15b350c..6062882583f 100644
--- a/gcc/testsuite/gcc.dg/pr109071_6.c
+++ b/gcc/testsuite/gcc.dg/pr109071_6.c
@@ -1,7 +1,10 @@
/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
due to code duplication from jump threading.
test case is from PR117179, which is a duplication of PR109071. */
-/* { dg-options "-O2 -Warray-bounds -fdiagnostics-show-context=1" } */
+/* { dg-options "-O2 -Warray-bounds -fdiagnostics-show-context=1
-fdisable-tree-phiopt1" } */
+/* phiopt1 is disabled: early phiopt now materializes setval_internal's
+ comparison, so no threaded path with the out-of-bounds range reaches
+ the array access and no warning is issued at all. */
/* { dg-additional-options "-fdiagnostics-show-line-numbers
-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
/* { dg-enable-nn-line-numbers "" } */
const char* commands[] = {"a", "b"};
diff --git a/gcc/testsuite/gcc.dg/predict-15.c
b/gcc/testsuite/gcc.dg/predict-15.c
index 2a8c3ea8597..9a25537c9b6 100644
--- a/gcc/testsuite/gcc.dg/predict-15.c
+++ b/gcc/testsuite/gcc.dg/predict-15.c
@@ -1,5 +1,7 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-profile_estimate" } */
+/* { dg-options "-O2 -fdump-tree-profile_estimate -fdisable-tree-phiopt1" } */
+/* phiopt1 is disabled: early phiopt now materializes the comparison
+ and no goto edge remains to predict. */
int main(int argc, char **argv)
{
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125672-1.c
b/gcc/testsuite/gcc.dg/tree-ssa/pr125672-1.c
new file mode 100644
index 00000000000..6cc90f656c5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125672-1.c
@@ -0,0 +1,42 @@
+/* Early phiopt materializes a comparison mask "cmp ? CST1 : CST2" as
+ -(int)cmp / (int)cmp, exposing its comparison. An equality test of the
+ mask ("m == C") then folds to the comparison itself, so the mask
+ disappears and diamonds controlled by the same mask share an identical
+ condition (a prerequisite for pass_merge_diamonds). */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-phiopt1" } */
+
+/* -(int)cmp all-ones mask tested against a constant: the test folds to the
+ comparison. */
+int
+neg_masktest (int a, int b, int x, int y)
+{
+ int m;
+ if (a > b) m = -1; else m = 0;
+ return (m == -1) ? x : y;
+}
+
+/* (int)cmp boolean mask tested against a constant (the convert case). */
+int
+cvt_masktest (int a, int b, int x, int y)
+{
+ int m;
+ if (a > b) m = 1; else m = 0;
+ return (m == 1) ? x : y;
+}
+
+/* A mask consumed by other operators is materialized too. */
+int
+mask_algebra (int a, int b)
+{
+ int m;
+ if (a > b) m = -1; else m = 0;
+ return m & 7;
+}
+
+/* All three masks are materialized: no constant-pair mask PHI survives and
+ the equality tests fold away. */
+/* { dg-final { scan-tree-dump-not "= PHI <-1\\(" "phiopt1" } } */
+/* { dg-final { scan-tree-dump-not "== -1" "phiopt1" } } */
+/* { dg-final { scan-tree-dump-not "== 1" "phiopt1" } } */
+/* { dg-final { scan-tree-dump "& 7" "phiopt1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21090-2.c
b/gcc/testsuite/gcc.dg/tree-ssa/pr21090-2.c
new file mode 100644
index 00000000000..d3792d4314f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21090-2.c
@@ -0,0 +1,27 @@
+/* With early phiopt lowering "p != 0 ? 1 : 0" to
+ a boolean mask, the non-null test of a PHI of two object addresses folds
+ away entirely, without needing VRP's predicate folding (compare
+ pr21090.c). */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-thread-jumps -fdelete-null-pointer-checks
-fdump-tree-optimized" } */
+
+int g, h;
+
+int
+foo (int a)
+{
+ int *p;
+
+ if (a)
+ p = &g;
+ else
+ p = &h;
+
+ if (p != 0)
+ return 1;
+ else
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump "return 1;" "optimized" { target { !
keeps_null_pointer_checks } } } } */
+/* { dg-final { scan-tree-dump-not "PHI" "optimized" { target { !
keeps_null_pointer_checks } } } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c
b/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c
index 92a87688601..ef755e85270 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c
@@ -1,5 +1,8 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp -fdump-tree-vrp1
-fdelete-null-pointer-checks" } */
+/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp -fdump-tree-vrp1
-fdelete-null-pointer-checks -fdisable-tree-phiopt1" } */
+/* phiopt1 is disabled so the non-null test reaches VRP as a branch; early
+ phiopt now lowers it to a boolean mask that is folded even earlier,
+ which pr21090-2.c covers. */
int g, h;
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr68198.c
b/gcc/testsuite/gcc.dg/tree-ssa/pr68198.c
index 125072941da..3b33961268f 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr68198.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr68198.c
@@ -1,5 +1,7 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-threadfull1-details -fdisable-tree-ethread" }
*/
+/* { dg-options "-O2 -fdump-tree-threadfull1-details -fdisable-tree-ethread
-fdisable-tree-phiopt1" } */
+/* phiopt1 is disabled: early phiopt now lowers the remove test to a boolean
+ mask and these threading opportunities change shape. */
extern void abort (void);
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp102.c
b/gcc/testsuite/gcc.dg/tree-ssa/vrp102.c
index fb62e570bed..83f6d98796f 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/vrp102.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp102.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-tree-dominator-opts -fdump-tree-vrp1" } */
+/* { dg-options "-O2 -fno-tree-dominator-opts -fdump-tree-evrp" } */
int f(int x, int y)
{
@@ -15,4 +15,4 @@ int f(int x, int y)
/* We should have computed x ^ y as zero and propagated the result into the
PHI feeding the result. */
-/* { dg-final { scan-tree-dump "ret_\[0-9\]+ = PHI <\[01\]\\\(\[0-9\]+\\\),
\[01\]\\\(\[0-9\]+\\\)>" "vrp1" } } */
+/* { dg-final { scan-tree-dump "ret_\[0-9\]+ = PHI <\[01\]\\\(\[0-9\]+\\\),
\[01\]\\\(\[0-9\]+\\\)>" "evrp" } } */
diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc
index e12dc7a8b0c..df658b04cfb 100644
--- a/gcc/tree-ssa-phiopt.cc
+++ b/gcc/tree-ssa-phiopt.cc
@@ -666,6 +666,29 @@ phiopt_early_allow (gimple_seq &seq, gimple_match_op &op)
code = gimple_assign_rhs_code (stmt);
return code == MIN_EXPR || code == MAX_EXPR;
}
+ /* Accept CMP, (convert)(compare) and -(convert)(compare) when the
+ generated stmts are only conversions and comparisons, so a mask such as
+ "cmp ? -1 : 0" / "cmp ? 1 : 0" can be materialized as -(int)cmp /
+ (int)cmp, exposing its comparison. This is only the structural test;
+ match_simplify_replacement additionally keeps user predictors alive
+ and leaves diamonds that other paths enter alone. */
+ if (code == NEGATE_EXPR
+ || CONVERT_EXPR_CODE_P (code)
+ || TREE_CODE_CLASS (code) == tcc_comparison)
+ {
+ for (gimple_stmt_iterator gsi = gsi_start (seq); !gsi_end_p (gsi);
+ gsi_next (&gsi))
+ {
+ gimple *s = gsi_stmt (gsi);
+ if (!is_gimple_assign (s))
+ return false;
+ tree_code scode = gimple_assign_rhs_code (s);
+ if (!CONVERT_EXPR_CODE_P (scode)
+ && TREE_CODE_CLASS (scode) != tcc_comparison)
+ return false;
+ }
+ return true;
+ }
return false;
}
@@ -684,7 +707,8 @@ phiopt_early_allow (gimple_seq &seq, gimple_match_op &op)
case FIXED_CST:
return true;
default:
- return false;
+ /* A bare comparison (CMP) is also allowed. */
+ return TREE_CODE_CLASS (code) == tcc_comparison;
}
}
@@ -1101,9 +1125,9 @@ match_simplify_replacement (basic_block cond_bb,
basic_block middle_bb,
}
/* For early phiopt, we don't want to lose user generated predictors
- if the phiopt is converting `if (a)` into `a` as that might
- be jump threaded later on so we want to keep around the
- predictors. */
+ if the phiopt is converting `if (a)` into `a` (or into the new
+ (convert)cmp / -(convert)cmp mask forms) as that might be jump
+ threaded later on so we want to keep around the predictors. */
if (early_p && result && TREE_CODE (result) == SSA_NAME)
{
bool check_it = false;
@@ -1111,15 +1135,33 @@ match_simplify_replacement (basic_block cond_bb,
basic_block middle_bb,
tree cmp1 = gimple_cond_rhs (stmt);
if (result == cmp0 || result == cmp1)
check_it = true;
- else if (gimple_seq_singleton_p (seq))
- {
- gimple *stmt = gimple_seq_first_stmt (seq);
- if (is_gimple_assign (stmt)
- && result == gimple_assign_lhs (stmt)
- && TREE_CODE_CLASS (gimple_assign_rhs_code (stmt))
- == tcc_comparison)
+ else if (!gimple_seq_empty_p (seq))
+ {
+ /* A comparison mask: the generated statements are only
+ conversions, negations and comparisons, with at least one
+ comparison (the forms phiopt_early_allow accepts). */
+ bool has_cmp = false, only_mask_ops = true;
+ for (gimple_stmt_iterator gi = gsi_start (seq); !gsi_end_p (gi);
+ gsi_next (&gi))
+ {
+ gimple *gs = gsi_stmt (gi);
+ if (!is_gimple_assign (gs))
+ {
+ only_mask_ops = false;
+ break;
+ }
+ tree_code c = gimple_assign_rhs_code (gs);
+ if (TREE_CODE_CLASS (c) == tcc_comparison)
+ has_cmp = true;
+ else if (!CONVERT_EXPR_CODE_P (c) && c != NEGATE_EXPR)
+ {
+ only_mask_ops = false;
+ break;
+ }
+ }
+ if (only_mask_ops && has_cmp)
check_it = true;
- }
+ }
if (!check_it)
;
else if (contains_hot_cold_predict (middle_bb))
@@ -1128,6 +1170,20 @@ match_simplify_replacement (basic_block cond_bb,
basic_block middle_bb,
&& middle_bb != middle_bb_alt
&& contains_hot_cold_predict (middle_bb_alt))
return false;
+ /* Materializing a comparison mask for a diamond that other paths
+ enter (through its join or through a shared arm) would leave
+ a PHI mixing the mask with the other paths' values: a residual
+ half-diamond that neither ifcombine nor RTL if-conversion
+ recovers, whereas they do handle the original nested branches
+ (e.g. into ccmp chains). Leave such embedded diamonds alone. */
+ else if (result != cmp0 && result != cmp1
+ && (EDGE_COUNT (gimple_bb (phi)->preds) != 2
+ || (middle_bb != gimple_bb (phi)
+ && !single_pred_p (middle_bb))
+ || (threeway_p
+ && middle_bb_alt != gimple_bb (phi)
+ && !single_pred_p (middle_bb_alt))))
+ return false;
}
if (!result)
--
2.50.1 (Apple Git-155)