[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