[gcc(refs/users/aoliva/heads/testme)] improvements for fold_truth_andor_1
Alexandre Oliva
aoliva@gcc.gnu.org
Thu Sep 17 02:52:15 GMT 2020
https://gcc.gnu.org/g:8e6137d4c9d9cba7dc7ea5ec2cfada53f40f6b6b
commit 8e6137d4c9d9cba7dc7ea5ec2cfada53f40f6b6b
Author: Alexandre Oliva <oliva@adacore.com>
Date: Tue Sep 15 13:32:07 2020 -0300
improvements for fold_truth_andor_1
Enable combining with the rightmost non-and/or, however deeply-nested.
Handle shift-and-mask.
Handle fields that cross alignment boundaries, when either part can be
combined.
Diff:
---
gcc/config/rs6000/t-rs6000 | 4 +
gcc/fold-const.c | 214 +++++++++++++++++++++++++++++++----
gcc/testsuite/gcc.dg/field-merge-1.c | 48 ++++++++
3 files changed, 241 insertions(+), 25 deletions(-)
diff --git a/gcc/config/rs6000/t-rs6000 b/gcc/config/rs6000/t-rs6000
index 1ddb5729cb2..516486df9a4 100644
--- a/gcc/config/rs6000/t-rs6000
+++ b/gcc/config/rs6000/t-rs6000
@@ -52,6 +52,10 @@ $(srcdir)/config/rs6000/rs6000-tables.opt: $(srcdir)/config/rs6000/genopt.sh \
$(SHELL) $(srcdir)/config/rs6000/genopt.sh $(srcdir)/config/rs6000 > \
$(srcdir)/config/rs6000/rs6000-tables.opt
+# FRAME_GROWS_DOWNWARD tests flag_sanitize in a way that rules out a
+# test in toplev.c.
+toplev.o-warn = -Wno-error
+
# The rs6000 backend doesn't cause warnings in these files.
insn-conditions.o-warn =
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 0cc80adf632..b163950f2c3 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -4640,6 +4640,7 @@ decode_field_reference (location_t loc, tree *exp_, HOST_WIDE_INT *pbitsize,
tree mask, inner, offset;
tree unsigned_type;
unsigned int precision;
+ HOST_WIDE_INT shiftrt = 0;
/* All the optimizations using this function assume integer fields.
There are problems with FP fields since the type_for_size call
@@ -4664,13 +4665,28 @@ decode_field_reference (location_t loc, tree *exp_, HOST_WIDE_INT *pbitsize,
return NULL_TREE;
}
+ if (TREE_CODE (exp) == RSHIFT_EXPR
+ && TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
+ && tree_fits_shwi_p (TREE_OPERAND (exp, 1)))
+ {
+ shiftrt = tree_to_shwi (TREE_OPERAND (exp, 1));
+ if (shiftrt > 0)
+ exp = TREE_OPERAND (exp, 0);
+ else
+ shiftrt = 0;
+ }
+
+ if (TREE_CODE (exp) == NOP_EXPR)
+ exp = TREE_OPERAND (exp, 0);
+
poly_int64 poly_bitsize, poly_bitpos;
inner = get_inner_reference (exp, &poly_bitsize, &poly_bitpos, &offset,
pmode, punsignedp, preversep, pvolatilep);
+
if ((inner == exp && and_mask == 0)
|| !poly_bitsize.is_constant (pbitsize)
|| !poly_bitpos.is_constant (pbitpos)
- || *pbitsize < 0
+ || *pbitsize < shiftrt
|| offset != 0
|| TREE_CODE (inner) == PLACEHOLDER_EXPR
/* Reject out-of-bound accesses (PR79731). */
@@ -4679,6 +4695,12 @@ decode_field_reference (location_t loc, tree *exp_, HOST_WIDE_INT *pbitsize,
*pbitpos + *pbitsize) < 0))
return NULL_TREE;
+ if (shiftrt)
+ {
+ *pbitpos += shiftrt;
+ *pbitsize -= shiftrt;
+ }
+
unsigned_type = lang_hooks.types.type_for_size (*pbitsize, 1);
if (unsigned_type == NULL_TREE)
return NULL_TREE;
@@ -6157,6 +6179,7 @@ fold_truth_andor_1 (location_t loc, enum tree_code code, tree truth_type,
convert EQ_EXPR to NE_EXPR so we need not reject the "wrong"
comparison for one-bit fields. */
+ enum tree_code orig_code = code;
enum tree_code wanted_code;
enum tree_code lcode, rcode;
tree ll_arg, lr_arg, rl_arg, rr_arg;
@@ -6168,13 +6191,14 @@ fold_truth_andor_1 (location_t loc, enum tree_code code, tree truth_type,
int ll_unsignedp, lr_unsignedp, rl_unsignedp, rr_unsignedp;
int ll_reversep, lr_reversep, rl_reversep, rr_reversep;
machine_mode ll_mode, lr_mode, rl_mode, rr_mode;
- scalar_int_mode lnmode, rnmode;
+ scalar_int_mode lnmode, lnmode2, rnmode;
tree ll_mask, lr_mask, rl_mask, rr_mask;
tree ll_and_mask, lr_and_mask, rl_and_mask, rr_and_mask;
tree l_const, r_const;
tree lntype, rntype, result;
HOST_WIDE_INT first_bit, end_bit;
int volatilep;
+ bool resplit_load;
/* Start by getting the comparison codes. Fail if anything is volatile.
If one operand is a BIT_AND_EXPR with the constant one, treat it as if
@@ -6202,7 +6226,48 @@ fold_truth_andor_1 (location_t loc, enum tree_code code, tree truth_type,
if (TREE_CODE_CLASS (lcode) != tcc_comparison
|| TREE_CODE_CLASS (rcode) != tcc_comparison)
- return 0;
+ {
+ /* Check for the possibility of merging component references.
+ If any of our operands is another similar operation, recurse
+ to try to merge individual operands, but avoiding double
+ recursion: recurse to each leaf of lhs, and from there to
+ each leaf of rhs, but don't bother recursing into lhs if rhs
+ is neither a comparison nor a compound expr, nor into rhs if
+ the lhs leaf isn't a comparison. In case of no successful
+ merging, recursion depth is limited to the sum of the depths
+ of lhs and rhs, and the non-recursing code below will run no
+ more times than the product of the leaf counts of lhs and
+ rhs. If there is a successful merge, we (recursively)
+ further attempt to fold the result, so recursion depth and
+ merge attempts are harder to compute. */
+ if (TREE_CODE (lhs) == code && TREE_TYPE (lhs) == truth_type
+ && (TREE_CODE_CLASS (rcode) == tcc_comparison
+ || (TREE_CODE (rhs) == code && TREE_TYPE (rhs) == truth_type)))
+ {
+ if ((result = fold_truth_andor_1 (loc, code, truth_type,
+ TREE_OPERAND (lhs, 1), rhs)) != 0)
+ return fold_build2_loc (loc, code, truth_type,
+ TREE_OPERAND (lhs, 0), result);
+ if ((result = fold_truth_andor_1 (loc, code, truth_type,
+ TREE_OPERAND (lhs, 0), rhs)) != 0)
+ return fold_build2_loc (loc, code, truth_type,
+ result, TREE_OPERAND (lhs, 1));
+ }
+ else if (TREE_CODE_CLASS (lcode) == tcc_comparison
+ && TREE_CODE (rhs) == code && TREE_TYPE (rhs) == truth_type)
+ {
+ if ((result = fold_truth_andor_1 (loc, code, truth_type, lhs,
+ TREE_OPERAND (rhs, 0))) != 0)
+ return fold_build2_loc (loc, code, truth_type,
+ result, TREE_OPERAND (rhs, 1));
+ if ((result = fold_truth_andor_1 (loc, code, truth_type, lhs,
+ TREE_OPERAND (rhs, 1))) != 0)
+ return fold_build2_loc (loc, code, truth_type,
+ result, TREE_OPERAND (rhs, 0));
+ }
+
+ return 0;
+ }
ll_arg = TREE_OPERAND (lhs, 0);
lr_arg = TREE_OPERAND (lhs, 1);
@@ -6357,10 +6422,48 @@ fold_truth_andor_1 (location_t loc, enum tree_code code, tree truth_type,
if (!get_best_mode (end_bit - first_bit, first_bit, 0, 0,
TYPE_ALIGN (TREE_TYPE (ll_inner)), BITS_PER_WORD,
volatilep, &lnmode))
- return 0;
+ {
+ /* Consider the possibility of recombining loads if any of the
+ fields straddles across an alignment boundary, so that either
+ part can be loaded along with the other field. */
+ HOST_WIDE_INT align = TYPE_ALIGN (TREE_TYPE (ll_inner));
+ HOST_WIDE_INT amask = ~(align - 1);
+
+ HOST_WIDE_INT boundary = (end_bit - 1) & amask;
+ /* Make sure we're only crossing one alignment boundary.
+
+ ??? This won't recombine loads of two adjacent fields that
+ each crosses a different alignment boundary, so as to load
+ the middle word only once. */
+ if (boundary - first_bit > align)
+ return 0;
+
+ HOST_WIDE_INT ll_start_word = ll_bitpos & amask;
+ HOST_WIDE_INT ll_end_word = (ll_bitpos + ll_bitsize - 1) & amask;
+
+ HOST_WIDE_INT rl_start_word = rl_bitpos & amask;
+ HOST_WIDE_INT rl_end_word = (rl_bitpos + rl_bitsize - 1) & amask;
+
+ /* If neither field straddles across an alignment boundary,
+ we've nothing further ado. */
+ if (ll_start_word == ll_end_word && rl_start_word == rl_end_word)
+ return 0;
+
+ if (!get_best_mode (boundary - first_bit, first_bit, 0, 0,
+ align, BITS_PER_WORD, volatilep, &lnmode)
+ || !get_best_mode (end_bit - boundary, boundary, 0, 0,
+ align, BITS_PER_WORD, volatilep, &lnmode2))
+ return 0;
+
+ resplit_load = true;
+ }
+ else
+ resplit_load = false;
lnbitsize = GET_MODE_BITSIZE (lnmode);
lnbitpos = first_bit & ~ (lnbitsize - 1);
+ if (resplit_load)
+ lnbitsize += GET_MODE_BITSIZE (lnmode2);
lntype = lang_hooks.types.type_for_size (lnbitsize, 1);
xll_bitpos = ll_bitpos - lnbitpos, xrl_bitpos = rl_bitpos - lnbitpos;
@@ -6414,7 +6517,8 @@ fold_truth_andor_1 (location_t loc, enum tree_code code, tree truth_type,
|| ll_reversep != lr_reversep
/* Make sure the two fields on the right
correspond to the left without being swapped. */
- || ll_bitpos - rl_bitpos != lr_bitpos - rr_bitpos)
+ || ll_bitpos - rl_bitpos != lr_bitpos - rr_bitpos
+ || resplit_load)
return 0;
first_bit = MIN (lr_bitpos, rr_bitpos);
@@ -6555,20 +6659,88 @@ fold_truth_andor_1 (location_t loc, enum tree_code code, tree truth_type,
if (lnbitpos < 0)
return 0;
- /* Construct the expression we will return. First get the component
- reference we will make. Unless the mask is all ones the width of
- that field, perform the mask operation. Then compare with the
- merged constant. */
- result = make_bit_field_ref (loc, ll_inner, ll_arg,
- lntype, lnbitsize, lnbitpos,
- ll_unsignedp || rl_unsignedp, ll_reversep);
+ if (resplit_load)
+ {
+ HOST_WIDE_INT align = TYPE_ALIGN (TREE_TYPE (ll_inner));
+ HOST_WIDE_INT lnlbitsize = GET_MODE_BITSIZE (lnmode);
+ HOST_WIDE_INT lnrbitsize = GET_MODE_BITSIZE (lnmode2);
+ HOST_WIDE_INT midpoint = lnlbitsize;
+ HOST_WIDE_INT lnrbitpos = lnbitpos + midpoint;
+ HOST_WIDE_INT lnlbitpos = lnrbitpos - lnlbitsize;
+ gcc_checking_assert ((lnrbitpos & (align - 1)) == 0);
+ tree lnltype = lang_hooks.types.type_for_size (lnlbitsize, 1);
+ tree lnrtype = lang_hooks.types.type_for_size (lnrbitsize, 1);
+ tree lll_arg = make_bit_field_ref (loc, ll_inner, ll_arg,
+ lnltype, lnlbitsize, lnlbitpos,
+ ll_unsignedp || rl_unsignedp,
+ ll_reversep);
+ tree llr_arg = make_bit_field_ref (loc, ll_inner, ll_arg,
+ lnrtype, lnrbitsize, lnrbitpos,
+ ll_unsignedp || rl_unsignedp,
+ ll_reversep);
+
+ HOST_WIDE_INT lnlcbitpos;
+ HOST_WIDE_INT lnrcbitpos;
+ /* The bit field ref constant folding below already takes care
+ of default target endianness. */
+ if (!ll_reversep)
+ {
+ lnlcbitpos = 0;
+ lnrcbitpos = lnlbitsize;
+ }
+ else
+ {
+ lnlcbitpos = lnrbitsize;
+ lnrcbitpos = 0;
+ }
+
+ tree mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask);
+ tree lll_mask = fold_build3 (BIT_FIELD_REF, lnltype, mask,
+ bitsize_int (lnlbitsize),
+ bitsize_int (lnlcbitpos));
+ if (! integer_all_onesp (lll_mask))
+ lll_arg = fold_build2_loc (loc, BIT_AND_EXPR, lnltype,
+ lll_arg, lll_mask);
+ tree llr_mask = fold_build3 (BIT_FIELD_REF, lnrtype, mask,
+ bitsize_int (lnrbitsize),
+ bitsize_int (lnrcbitpos));
+ if (! integer_all_onesp (llr_mask))
+ llr_arg = fold_build2_loc (loc, BIT_AND_EXPR, lnrtype,
+ llr_arg, llr_mask);
+ tree lnr_const = const_binop (BIT_IOR_EXPR, l_const, r_const);
+ tree lll_const = fold_build3 (BIT_FIELD_REF, lnltype, lnr_const,
+ bitsize_int (lnlbitsize),
+ bitsize_int (lnlcbitpos));
+ tree lll_result = fold_build2_loc (loc, wanted_code, truth_type,
+ lll_arg, lll_const);
+ tree llr_const = fold_build3 (BIT_FIELD_REF, lnrtype, lnr_const,
+ bitsize_int (lnrbitsize),
+ bitsize_int (lnrcbitpos));
+ tree llr_result = fold_build2_loc (loc, wanted_code, truth_type,
+ llr_arg, llr_const);
+ result = build2_loc (loc, orig_code, truth_type,
+ lll_result, llr_result);
+ }
+ else
+ {
+
+ /* Construct the expression we will return. First get the component
+ reference we will make. Unless the mask is all ones the width of
+ that field, perform the mask operation. Then compare with the
+ merged constant. */
+ result = make_bit_field_ref (loc, ll_inner, ll_arg,
+ lntype, lnbitsize, lnbitpos,
+ ll_unsignedp || rl_unsignedp, ll_reversep);
- ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask);
- if (! all_ones_mask_p (ll_mask, lnbitsize))
- result = build2_loc (loc, BIT_AND_EXPR, lntype, result, ll_mask);
+ ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask);
+ if (! integer_all_onesp (ll_mask))
+ result = build2_loc (loc, BIT_AND_EXPR, lntype, result, ll_mask);
+
+ result = build2_loc (loc, wanted_code, truth_type, result,
+ const_binop (BIT_IOR_EXPR, l_const, r_const));
+ }
- return build2_loc (loc, wanted_code, truth_type, result,
- const_binop (BIT_IOR_EXPR, l_const, r_const));
+ return result;
}
/* T is an integer expression that is being multiplied, divided, or taken a
@@ -9166,14 +9338,6 @@ fold_truth_andor (location_t loc, enum tree_code code, tree type,
return fold_build2_loc (loc, code, type, arg0, tem);
}
- /* Check for the possibility of merging component references. If our
- lhs is another similar operation, try to merge its rhs with our
- rhs. Then try to merge our lhs and rhs. */
- if (TREE_CODE (arg0) == code
- && (tem = fold_truth_andor_1 (loc, code, type,
- TREE_OPERAND (arg0, 1), arg1)) != 0)
- return fold_build2_loc (loc, code, type, TREE_OPERAND (arg0, 0), tem);
-
if ((tem = fold_truth_andor_1 (loc, code, type, arg0, arg1)) != 0)
return tem;
diff --git a/gcc/testsuite/gcc.dg/field-merge-1.c b/gcc/testsuite/gcc.dg/field-merge-1.c
new file mode 100644
index 00000000000..491ec2936f1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-1.c
@@ -0,0 +1,48 @@
+/* { dg-do run } */
+/* { dg-options "-O -save-temps" } */
+
+/* Check that field loads compared with constants are merged, even if
+ tested out of order, and when fields straddle across alignment
+ boundaries. */
+
+struct TL {
+ unsigned char p;
+ unsigned int a;
+ unsigned char q;
+ unsigned int b;
+ unsigned char r;
+ unsigned int c;
+ unsigned char s;
+} __attribute__ ((packed, aligned (4), scalar_storage_order ("little-endian")));
+
+struct TB {
+ unsigned char p;
+ unsigned int a;
+ unsigned char q;
+ unsigned int b;
+ unsigned char r;
+ unsigned int c;
+ unsigned char s;
+} __attribute__ ((packed, aligned (4), scalar_storage_order ("big-endian")));
+
+#define vc 0xaa
+#define vi 0x12345678
+
+struct TL vL = { vc, vi, vc, vi, vc, vi, vc };
+struct TB vB = { vc, vi, vc, vi, vc, vi, vc };
+
+void f (void) {
+ if (vL.a != vi || vL.b != vi || vL.c != vi
+ || vB.a != vi || vB.b != vi || vB.c != vi
+ || vL.p != vc || vL.q != vc || vL.r != vc || vL.s != vc
+ || vB.p != vc || vB.q != vc || vB.r != vc || vB.s != vc)
+ __builtin_abort ();
+}
+
+int main () {
+ f ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler-not "cmpb" { target { i*86-*-* || x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "cmpl" 8 { target { i*86-*-* || x86_64-*-* } } } } */
More information about the Gcc-cvs
mailing list