[gcc(refs/users/aoliva/heads/testme)] improvements for fold_truth_andor_1
Alexandre Oliva
aoliva@gcc.gnu.org
Wed Sep 16 21:38:35 GMT 2020
https://gcc.gnu.org/g:012db34a07d657fa9da81c116736396b2bc96b74
commit 012db34a07d657fa9da81c116736396b2bc96b74
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 | 203 +++++++++++++++++++++++++++++++++++++++------
2 files changed, 182 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..e91e7778999 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,77 @@ 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);
+
+ /* ??? Handle ll_reversep and endianness below? */
+ HOST_WIDE_INT lnlcbitpos = 0;
+ HOST_WIDE_INT lnrcbitpos = lnlbitsize;
+
+ 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
+ {
- 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);
+ /* 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 (! integer_all_onesp (ll_mask))
+ result = build2_loc (loc, BIT_AND_EXPR, lntype, result, ll_mask);
- return build2_loc (loc, wanted_code, truth_type, result,
- const_binop (BIT_IOR_EXPR, l_const, r_const));
+ result = 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 +9327,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;
More information about the Gcc-cvs
mailing list