diff mbox series

[v3] fold fold_truth_andor field merging into ifcombine

Message ID oro71mjvtl.fsf_-_@lxoliva.fsfla.org
State New
Headers show
Series [v3] fold fold_truth_andor field merging into ifcombine | expand

Commit Message

Alexandre Oliva Dec. 8, 2024, 10:47 a.m. UTC
This patch introduces various improvements to the logic that merges
field compares, while moving it into ifcombine.

Before the patch, we could merge:

  (a.x1 EQNE b.x1)  ANDOR  (a.y1 EQNE b.y1)

into something like:

  (((type *)&a)[Na] & MASK) EQNE (((type *)&b)[Nb] & MASK)

if both of A's fields live within the same alignment boundaries, and
so do B's, at the same relative positions.  Constants may be used
instead of the object B.

The initial goal of this patch was to enable such combinations when a
field crossed alignment boundaries, e.g. for packed types.  We can't
generally access such fields with a single memory access, so when we
come across such a compare, we will attempt to combine each access
separately.

Some merging opportunities were missed because of right-shifts,
compares expressed as e.g. ((a.x1 ^ b.x1) & MASK) EQNE 0, and
narrowing conversions, especially after earlier merges.  This patch
introduces handlers for several cases involving these.

The merging of multiple field accesses into wider bitfield-like
accesses is undesirable to do too early in compilation, so we move it
from folding to ifcombine, and guard its warnings with
-Wtautological-compare, turned into a common flag.

When the second of a noncontiguous pair of compares is the first that
accesses a word, we may merge the first compare with part of the
second compare that refers to the same word, keeping the compare of
the remaining bits at the spot where the second compare used to be.

Handling compares with non-constant fields was somewhat generalized
from what fold used to do, now handling non-adjacent fields, even if a
field of one object crosses an alignment boundary but the other
doesn't.

Regstrapped on x86_64-linux-gnu.  Ok to install?

Changes from v2:

- fixed ICE when xor appeared only in the right-hand operand of a
  compare

- simplified the interface of decode_field_reference.  pand_mask can now
  be NULL, meaning mask operations are to be rejected, but this ended up
  unused.  It no longer rejects plain SSA names as the expr to be
  combined.

- drop unused variables from fold_truth_andor_for_ifcombine, and ensure
  right-hand masks are applied even on constants

- normalize width of masks and constants represented as wide_int, fixing
  regressions introduced by the transition to wide_int.  Don't bother to
  sign extend them, treat them as unsigned since it's all bitwise
  equality tests.

- more tests; fixed thinko in v2-added test

- guard warnings with -Wtautological-compares; made the option available
  on all languages

- backpedal from match.pd matchers to open-coded matchers

- improve vuse setting to avoid unwarranted assumptions about
  gimplification

- simplify selection of types for combined operands

- drop side effects tests not sensible in gimple

- explain apparent presence of TRUTH_*IF_EXPR in gimple

Changes from v1:

- Noncontiguous ifcombine split out and already installed.

- Do not attempt to place new loads next to the latest of the original
  loads.  Only check that loads have the same vuses, and place loads
  next to either one, even if that's suboptimal.

- Use gimple pattern matching.  It turned out not to be very useful, but
  it enabled the elimination of redundant auxiliary functions.

- Rewrote constants and masks using wide_int.

- Work harder to gather and reuse location_t.

- Rework BIT_XOR handling to avoid having to match patterns again.

- Distinguish the cases of contiguous and noncontiguous conditions.

- Comments, lots of comments.

- More tests.

- Dropped the warnings that were hitting i386 and rs6000 bootstraps.
  The new possibilities with ifcombine, noncontiguous at that, on top of
  more flexible field merging, made room for too many false positives.

Requested but unchanged from v1:

- fold_truth_andor_for_ifcombine (renamed from ...andor_maybe_separate)
still builds and returns generic expressions.  Since other
ifcombine_ifandif build generic exprs and have to go through
regimplification anyway, I failed to see the point.  Implementing that
change would require some more work in tree-ssa-ifcombine.cc to support.
I'd rather do that as a follow up if desired, please let me know.

- it also remains a single bulky function.  There's so much state that
breaking it up doesn't seem to be helpful.  Hopefully the added comments
will help despite making it even bigger.

- TBAA situation is unchanged, same as what's carried over from fold.
I'm not sure what the concerns in your mind are, but if there are actual
problems, they have long been there, and we'd better address them in
both fold and in this bit now moved to ifcombine, ideally in a separate
backportable patch.


for  gcc/ChangeLog

	* fold-const.cc (make_bit_field): Export.
	(unextend, all_ones_mask_p): Drop.
	(decode_field_reference, fold_truth_andor_1): Move
	field compare merging logic...
	* gimple-fold.cc: (fold_truth_andor_for_ifcombine) ... here,
	with -Wtautological-compare warning guards, and...
	(decode_field_reference): ... here.  Rework for gimple.
	(gimple_fold_follow_convert, gimple_fold_binop_cst): New.
	(compute_split_boundary_from_align): New.
	(make_bit_field_load, build_split_load): New.
	(reuse_split_load): New.
	* fold-const.h: (make_bit_field_ref): Declare
	(fold_truth_andor_for_ifcombine): Declare.
	* tree-ssa-ifcombine.cc (ifcombine_ifandif): Try
	fold_truth_andor_for_ifcombine.
	* common.opt (Wtautological-compare): Move here.

for  gcc/c-family/ChangeLog

	* c.opt (Wtautological-compare): Move to ../common.opt.

for  gcc/testsuite/ChangeLog

	* gcc.dg/field-merge-1.c: New.
	* gcc.dg/field-merge-2.c: New.
	* gcc.dg/field-merge-3.c: New.
	* gcc.dg/field-merge-4.c: New.
	* gcc.dg/field-merge-5.c: New.
	* gcc.dg/field-merge-6.c: New.
	* gcc.dg/field-merge-7.c: New.
	* gcc.dg/field-merge-8.c: New.
	* gcc.dg/field-merge-9.c: New.
	* gcc.dg/field-merge-10.c: New.
	* gcc.dg/field-merge-11.c: New.
	* gcc.dg/field-merge-12.c: New.
---
 gcc/c-family/c.opt                    |    4 
 gcc/common.opt                        |    4 
 gcc/fold-const.cc                     |  512 ---------------
 gcc/fold-const.h                      |   10 
 gcc/gimple-fold.cc                    | 1152 +++++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/field-merge-1.c  |   64 ++
 gcc/testsuite/gcc.dg/field-merge-10.c |   36 +
 gcc/testsuite/gcc.dg/field-merge-11.c |   32 +
 gcc/testsuite/gcc.dg/field-merge-12.c |   33 +
 gcc/testsuite/gcc.dg/field-merge-2.c  |   31 +
 gcc/testsuite/gcc.dg/field-merge-3.c  |   36 +
 gcc/testsuite/gcc.dg/field-merge-4.c  |   40 +
 gcc/testsuite/gcc.dg/field-merge-5.c  |   40 +
 gcc/testsuite/gcc.dg/field-merge-6.c  |   26 +
 gcc/testsuite/gcc.dg/field-merge-7.c  |   23 +
 gcc/testsuite/gcc.dg/field-merge-8.c  |   25 +
 gcc/testsuite/gcc.dg/field-merge-9.c  |   38 +
 gcc/tree-ssa-ifcombine.cc             |   14 
 18 files changed, 1607 insertions(+), 513 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-1.c
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-10.c
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-11.c
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-12.c
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-2.c
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-3.c
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-4.c
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-5.c
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-6.c
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-7.c
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-8.c
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-9.c
diff mbox series

Patch

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 0d255561b1bdc..be7916e957d66 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1477,10 +1477,6 @@  Wtemplates
 C++ ObjC++ Var(warn_templates) Warning
 Warn on primary template declaration.
 
-Wtautological-compare
-C ObjC C++ ObjC++ Var(warn_tautological_compare) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
-Warn if a comparison always evaluates to true or false.
-
 Wtemplate-body
 C++ ObjC++ Var(warn_template_body) Warning Init(1)
 Diagnose errors when parsing a template.
diff --git a/gcc/common.opt b/gcc/common.opt
index bb226ac61e6a1..915ce5bffb4e0 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -812,6 +812,10 @@  Wsystem-headers
 Common Var(warn_system_headers) Warning
 Do not suppress warnings from system headers.
 
+Wtautological-compare
+Common Var(warn_tautological_compare) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
+Warn if a comparison always evaluates to true or false.
+
 Wtrampolines
 Common Var(warn_trampolines) Warning
 Warn whenever a trampoline is generated.
diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 1e8ae1ab493ba..6449664598641 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -137,7 +137,6 @@  static tree range_successor (tree);
 static tree fold_range_test (location_t, enum tree_code, tree, tree, tree);
 static tree fold_cond_expr_with_comparison (location_t, tree, enum tree_code,
 					    tree, tree, tree, tree);
-static tree unextend (tree, int, int, tree);
 static tree extract_muldiv (tree, tree, enum tree_code, tree, bool *);
 static tree extract_muldiv_1 (tree, tree, enum tree_code, tree, bool *);
 static tree fold_binary_op_with_conditional_arg (location_t,
@@ -4711,7 +4710,7 @@  invert_truthvalue_loc (location_t loc, tree arg)
    is the original memory reference used to preserve the alias set of
    the access.  */
 
-static tree
+tree
 make_bit_field_ref (location_t loc, tree inner, tree orig_inner, tree type,
 		    HOST_WIDE_INT bitsize, poly_int64 bitpos,
 		    int unsignedp, int reversep)
@@ -4961,136 +4960,6 @@  optimize_bit_field_compare (location_t loc, enum tree_code code,
   return lhs;
 }
 
-/* Subroutine for fold_truth_andor_1: decode a field reference.
-
-   If EXP is a comparison reference, we return the innermost reference.
-
-   *PBITSIZE is set to the number of bits in the reference, *PBITPOS is
-   set to the starting bit number.
-
-   If the innermost field can be completely contained in a mode-sized
-   unit, *PMODE is set to that mode.  Otherwise, it is set to VOIDmode.
-
-   *PVOLATILEP is set to 1 if the any expression encountered is volatile;
-   otherwise it is not changed.
-
-   *PUNSIGNEDP is set to the signedness of the field.
-
-   *PREVERSEP is set to the storage order of the field.
-
-   *PMASK is set to the mask used.  This is either contained in a
-   BIT_AND_EXPR or derived from the width of the field.
-
-   *PAND_MASK is set to the mask found in a BIT_AND_EXPR, if any.
-
-   Return 0 if this is not a component reference or is one that we can't
-   do anything with.  */
-
-static tree
-decode_field_reference (location_t loc, tree *exp_, HOST_WIDE_INT *pbitsize,
-			HOST_WIDE_INT *pbitpos, machine_mode *pmode,
-			int *punsignedp, int *preversep, int *pvolatilep,
-			tree *pmask, tree *pand_mask)
-{
-  tree exp = *exp_;
-  tree outer_type = 0;
-  tree and_mask = 0;
-  tree mask, inner, offset;
-  tree unsigned_type;
-  unsigned int precision;
-
-  /* All the optimizations using this function assume integer fields.
-     There are problems with FP fields since the type_for_size call
-     below can fail for, e.g., XFmode.  */
-  if (! INTEGRAL_TYPE_P (TREE_TYPE (exp)))
-    return NULL_TREE;
-
-  /* We are interested in the bare arrangement of bits, so strip everything
-     that doesn't affect the machine mode.  However, record the type of the
-     outermost expression if it may matter below.  */
-  if (CONVERT_EXPR_P (exp)
-      || TREE_CODE (exp) == NON_LVALUE_EXPR)
-    outer_type = TREE_TYPE (exp);
-  STRIP_NOPS (exp);
-
-  if (TREE_CODE (exp) == BIT_AND_EXPR)
-    {
-      and_mask = TREE_OPERAND (exp, 1);
-      exp = TREE_OPERAND (exp, 0);
-      STRIP_NOPS (exp); STRIP_NOPS (and_mask);
-      if (TREE_CODE (and_mask) != INTEGER_CST)
-	return NULL_TREE;
-    }
-
-  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
-      || offset != 0
-      || TREE_CODE (inner) == PLACEHOLDER_EXPR
-      /* We eventually want to build a larger reference and need to take
-	 the address of this.  */
-      || (!REFERENCE_CLASS_P (inner) && !DECL_P (inner))
-      /* Reject out-of-bound accesses (PR79731).  */
-      || (! AGGREGATE_TYPE_P (TREE_TYPE (inner))
-	  && compare_tree_int (TYPE_SIZE (TREE_TYPE (inner)),
-			       *pbitpos + *pbitsize) < 0))
-    return NULL_TREE;
-
-  unsigned_type = lang_hooks.types.type_for_size (*pbitsize, 1);
-  if (unsigned_type == NULL_TREE)
-    return NULL_TREE;
-
-  *exp_ = exp;
-
-  /* If the number of bits in the reference is the same as the bitsize of
-     the outer type, then the outer type gives the signedness. Otherwise
-     (in case of a small bitfield) the signedness is unchanged.  */
-  if (outer_type && *pbitsize == TYPE_PRECISION (outer_type))
-    *punsignedp = TYPE_UNSIGNED (outer_type);
-
-  /* Compute the mask to access the bitfield.  */
-  precision = TYPE_PRECISION (unsigned_type);
-
-  mask = build_int_cst_type (unsigned_type, -1);
-
-  mask = const_binop (LSHIFT_EXPR, mask, size_int (precision - *pbitsize));
-  mask = const_binop (RSHIFT_EXPR, mask, size_int (precision - *pbitsize));
-
-  /* Merge it with the mask we found in the BIT_AND_EXPR, if any.  */
-  if (and_mask != 0)
-    mask = fold_build2_loc (loc, BIT_AND_EXPR, unsigned_type,
-			fold_convert_loc (loc, unsigned_type, and_mask), mask);
-
-  *pmask = mask;
-  *pand_mask = and_mask;
-  return inner;
-}
-
-/* Return nonzero if MASK represents a mask of SIZE ones in the low-order
-   bit positions and MASK is SIGNED.  */
-
-static bool
-all_ones_mask_p (const_tree mask, unsigned int size)
-{
-  tree type = TREE_TYPE (mask);
-  unsigned int precision = TYPE_PRECISION (type);
-
-  /* If this function returns true when the type of the mask is
-     UNSIGNED, then there will be errors.  In particular see
-     gcc.c-torture/execute/990326-1.c.  There does not appear to be
-     any documentation paper trail as to why this is so.  But the pre
-     wide-int worked with that restriction and it has been preserved
-     here.  */
-  if (size > precision || TYPE_SIGN (type) == UNSIGNED)
-    return false;
-
-  return wi::mask (size, false, precision) == wi::to_wide (mask);
-}
-
 /* Subroutine for fold: determine if VAL is the INTEGER_CONST that
    represents the sign bit of EXP's type.  If EXP represents a sign
    or zero extension, also test VAL against the unextended type.
@@ -6400,48 +6269,6 @@  fold_range_test (location_t loc, enum tree_code code, tree type,
   return 0;
 }
 
-/* Subroutine for fold_truth_andor_1: C is an INTEGER_CST interpreted as a P
-   bit value.  Arrange things so the extra bits will be set to zero if and
-   only if C is signed-extended to its full width.  If MASK is nonzero,
-   it is an INTEGER_CST that should be AND'ed with the extra bits.  */
-
-static tree
-unextend (tree c, int p, int unsignedp, tree mask)
-{
-  tree type = TREE_TYPE (c);
-  int modesize = GET_MODE_BITSIZE (SCALAR_INT_TYPE_MODE (type));
-  tree temp;
-
-  if (p == modesize || unsignedp)
-    return c;
-
-  /* We work by getting just the sign bit into the low-order bit, then
-     into the high-order bit, then sign-extend.  We then XOR that value
-     with C.  */
-  temp = build_int_cst (TREE_TYPE (c),
-			wi::extract_uhwi (wi::to_wide (c), p - 1, 1));
-
-  /* We must use a signed type in order to get an arithmetic right shift.
-     However, we must also avoid introducing accidental overflows, so that
-     a subsequent call to integer_zerop will work.  Hence we must
-     do the type conversion here.  At this point, the constant is either
-     zero or one, and the conversion to a signed type can never overflow.
-     We could get an overflow if this conversion is done anywhere else.  */
-  if (TYPE_UNSIGNED (type))
-    temp = fold_convert (signed_type_for (type), temp);
-
-  temp = const_binop (LSHIFT_EXPR, temp, size_int (modesize - 1));
-  temp = const_binop (RSHIFT_EXPR, temp, size_int (modesize - p - 1));
-  if (mask != 0)
-    temp = const_binop (BIT_AND_EXPR, temp,
-			fold_convert (TREE_TYPE (c), mask));
-  /* If necessary, convert the type back to match the type of C.  */
-  if (TYPE_UNSIGNED (type))
-    temp = fold_convert (type, temp);
-
-  return fold_convert (type, const_binop (BIT_XOR_EXPR, c, temp));
-}
-
 /* For an expression that has the form
      (A && B) || ~B
    or
@@ -6512,20 +6339,13 @@  merge_truthop_with_opposite_arm (location_t loc, tree op, tree cmpop,
 			    lhs, rhs);
   return NULL_TREE;
 }
-
+
 /* Find ways of folding logical expressions of LHS and RHS:
    Try to merge two comparisons to the same innermost item.
    Look for range tests like "ch >= '0' && ch <= '9'".
    Look for combinations of simple terms on machines with expensive branches
    and evaluate the RHS unconditionally.
 
-   For example, if we have p->a == 2 && p->b == 4 and we can make an
-   object large enough to span both A and B, we can do this with a comparison
-   against the object ANDed with the a mask.
-
-   If we have p->a == q->a && p->b == q->b, we may be able to use bit masking
-   operations to do this with one comparison.
-
    We check for both normal comparisons and the BIT_AND_EXPRs made this by
    function and the one above.
 
@@ -6550,24 +6370,9 @@  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 wanted_code;
   enum tree_code lcode, rcode;
   tree ll_arg, lr_arg, rl_arg, rr_arg;
-  tree ll_inner, lr_inner, rl_inner, rr_inner;
-  HOST_WIDE_INT ll_bitsize, ll_bitpos, lr_bitsize, lr_bitpos;
-  HOST_WIDE_INT rl_bitsize, rl_bitpos, rr_bitsize, rr_bitpos;
-  HOST_WIDE_INT xll_bitpos, xlr_bitpos, xrl_bitpos, xrr_bitpos;
-  HOST_WIDE_INT lnbitsize, lnbitpos, rnbitsize, rnbitpos;
-  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;
-  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;
+  tree result;
 
   /* 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
@@ -6662,316 +6467,7 @@  fold_truth_andor_1 (location_t loc, enum tree_code code, tree truth_type,
 			   build_int_cst (TREE_TYPE (ll_arg), 0));
     }
 
-  /* See if the comparisons can be merged.  Then get all the parameters for
-     each side.  */
-
-  if ((lcode != EQ_EXPR && lcode != NE_EXPR)
-      || (rcode != EQ_EXPR && rcode != NE_EXPR))
-    return 0;
-
-  ll_reversep = lr_reversep = rl_reversep = rr_reversep = 0;
-  volatilep = 0;
-  ll_inner = decode_field_reference (loc, &ll_arg,
-				     &ll_bitsize, &ll_bitpos, &ll_mode,
-				     &ll_unsignedp, &ll_reversep, &volatilep,
-				     &ll_mask, &ll_and_mask);
-  lr_inner = decode_field_reference (loc, &lr_arg,
-				     &lr_bitsize, &lr_bitpos, &lr_mode,
-				     &lr_unsignedp, &lr_reversep, &volatilep,
-				     &lr_mask, &lr_and_mask);
-  rl_inner = decode_field_reference (loc, &rl_arg,
-				     &rl_bitsize, &rl_bitpos, &rl_mode,
-				     &rl_unsignedp, &rl_reversep, &volatilep,
-				     &rl_mask, &rl_and_mask);
-  rr_inner = decode_field_reference (loc, &rr_arg,
-				     &rr_bitsize, &rr_bitpos, &rr_mode,
-				     &rr_unsignedp, &rr_reversep, &volatilep,
-				     &rr_mask, &rr_and_mask);
-
-  /* It must be true that the inner operation on the lhs of each
-     comparison must be the same if we are to be able to do anything.
-     Then see if we have constants.  If not, the same must be true for
-     the rhs's.  */
-  if (volatilep
-      || ll_reversep != rl_reversep
-      || ll_inner == 0 || rl_inner == 0
-      || ! operand_equal_p (ll_inner, rl_inner, 0))
-    return 0;
-
-  if (TREE_CODE (lr_arg) == INTEGER_CST
-      && TREE_CODE (rr_arg) == INTEGER_CST)
-    {
-      l_const = lr_arg, r_const = rr_arg;
-      lr_reversep = ll_reversep;
-    }
-  else if (lr_reversep != rr_reversep
-	   || lr_inner == 0 || rr_inner == 0
-	   || ! operand_equal_p (lr_inner, rr_inner, 0))
-    return 0;
-  else
-    l_const = r_const = 0;
-
-  /* If either comparison code is not correct for our logical operation,
-     fail.  However, we can convert a one-bit comparison against zero into
-     the opposite comparison against that bit being set in the field.  */
-
-  wanted_code = (code == TRUTH_AND_EXPR ? EQ_EXPR : NE_EXPR);
-  if (lcode != wanted_code)
-    {
-      if (l_const && integer_zerop (l_const) && integer_pow2p (ll_mask))
-	{
-	  /* Make the left operand unsigned, since we are only interested
-	     in the value of one bit.  Otherwise we are doing the wrong
-	     thing below.  */
-	  ll_unsignedp = 1;
-	  l_const = ll_mask;
-	}
-      else
-	return 0;
-    }
-
-  /* This is analogous to the code for l_const above.  */
-  if (rcode != wanted_code)
-    {
-      if (r_const && integer_zerop (r_const) && integer_pow2p (rl_mask))
-	{
-	  rl_unsignedp = 1;
-	  r_const = rl_mask;
-	}
-      else
-	return 0;
-    }
-
-  /* See if we can find a mode that contains both fields being compared on
-     the left.  If we can't, fail.  Otherwise, update all constants and masks
-     to be relative to a field of that size.  */
-  first_bit = MIN (ll_bitpos, rl_bitpos);
-  end_bit = MAX (ll_bitpos + ll_bitsize, rl_bitpos + rl_bitsize);
-  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;
-
-  lnbitsize = GET_MODE_BITSIZE (lnmode);
-  lnbitpos = first_bit & ~ (lnbitsize - 1);
-  lntype = lang_hooks.types.type_for_size (lnbitsize, 1);
-  xll_bitpos = ll_bitpos - lnbitpos, xrl_bitpos = rl_bitpos - lnbitpos;
-
-  if (ll_reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
-    {
-      xll_bitpos = lnbitsize - xll_bitpos - ll_bitsize;
-      xrl_bitpos = lnbitsize - xrl_bitpos - rl_bitsize;
-    }
-
-  ll_mask = const_binop (LSHIFT_EXPR, fold_convert_loc (loc, lntype, ll_mask),
-			 size_int (xll_bitpos));
-  rl_mask = const_binop (LSHIFT_EXPR, fold_convert_loc (loc, lntype, rl_mask),
-			 size_int (xrl_bitpos));
-  if (ll_mask == NULL_TREE || rl_mask == NULL_TREE)
-    return 0;
-
-  if (l_const)
-    {
-      l_const = fold_convert_loc (loc, lntype, l_const);
-      l_const = unextend (l_const, ll_bitsize, ll_unsignedp, ll_and_mask);
-      l_const = const_binop (LSHIFT_EXPR, l_const, size_int (xll_bitpos));
-      if (l_const == NULL_TREE)
-	return 0;
-      if (! integer_zerop (const_binop (BIT_AND_EXPR, l_const,
-					fold_build1_loc (loc, BIT_NOT_EXPR,
-							 lntype, ll_mask))))
-	{
-	  warning (0, "comparison is always %d", wanted_code == NE_EXPR);
-
-	  return constant_boolean_node (wanted_code == NE_EXPR, truth_type);
-	}
-    }
-  if (r_const)
-    {
-      r_const = fold_convert_loc (loc, lntype, r_const);
-      r_const = unextend (r_const, rl_bitsize, rl_unsignedp, rl_and_mask);
-      r_const = const_binop (LSHIFT_EXPR, r_const, size_int (xrl_bitpos));
-      if (r_const == NULL_TREE)
-	return 0;
-      if (! integer_zerop (const_binop (BIT_AND_EXPR, r_const,
-					fold_build1_loc (loc, BIT_NOT_EXPR,
-							 lntype, rl_mask))))
-	{
-	  warning (0, "comparison is always %d", wanted_code == NE_EXPR);
-
-	  return constant_boolean_node (wanted_code == NE_EXPR, truth_type);
-	}
-    }
-
-  /* If the right sides are not constant, do the same for it.  Also,
-     disallow this optimization if a size, signedness or storage order
-     mismatch occurs between the left and right sides.  */
-  if (l_const == 0)
-    {
-      if (ll_bitsize != lr_bitsize || rl_bitsize != rr_bitsize
-	  || ll_unsignedp != lr_unsignedp || rl_unsignedp != rr_unsignedp
-	  || 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)
-	return 0;
-
-      first_bit = MIN (lr_bitpos, rr_bitpos);
-      end_bit = MAX (lr_bitpos + lr_bitsize, rr_bitpos + rr_bitsize);
-      if (!get_best_mode (end_bit - first_bit, first_bit, 0, 0,
-			  TYPE_ALIGN (TREE_TYPE (lr_inner)), BITS_PER_WORD,
-			  volatilep, &rnmode))
-	return 0;
-
-      rnbitsize = GET_MODE_BITSIZE (rnmode);
-      rnbitpos = first_bit & ~ (rnbitsize - 1);
-      rntype = lang_hooks.types.type_for_size (rnbitsize, 1);
-      xlr_bitpos = lr_bitpos - rnbitpos, xrr_bitpos = rr_bitpos - rnbitpos;
-
-      if (lr_reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
-	{
-	  xlr_bitpos = rnbitsize - xlr_bitpos - lr_bitsize;
-	  xrr_bitpos = rnbitsize - xrr_bitpos - rr_bitsize;
-	}
-
-      lr_mask = const_binop (LSHIFT_EXPR, fold_convert_loc (loc,
-							    rntype, lr_mask),
-			     size_int (xlr_bitpos));
-      rr_mask = const_binop (LSHIFT_EXPR, fold_convert_loc (loc,
-							    rntype, rr_mask),
-			     size_int (xrr_bitpos));
-      if (lr_mask == NULL_TREE || rr_mask == NULL_TREE)
-	return 0;
-
-      /* Make a mask that corresponds to both fields being compared.
-	 Do this for both items being compared.  If the operands are the
-	 same size and the bits being compared are in the same position
-	 then we can do this by masking both and comparing the masked
-	 results.  */
-      ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask);
-      lr_mask = const_binop (BIT_IOR_EXPR, lr_mask, rr_mask);
-      if (lnbitsize == rnbitsize
-	  && xll_bitpos == xlr_bitpos
-	  && lnbitpos >= 0
-	  && rnbitpos >= 0)
-	{
-	  lhs = make_bit_field_ref (loc, ll_inner, ll_arg,
-				    lntype, lnbitsize, lnbitpos,
-				    ll_unsignedp || rl_unsignedp, ll_reversep);
-	  if (! all_ones_mask_p (ll_mask, lnbitsize))
-	    lhs = build2 (BIT_AND_EXPR, lntype, lhs, ll_mask);
-
-	  rhs = make_bit_field_ref (loc, lr_inner, lr_arg,
-				    rntype, rnbitsize, rnbitpos,
-				    lr_unsignedp || rr_unsignedp, lr_reversep);
-	  if (! all_ones_mask_p (lr_mask, rnbitsize))
-	    rhs = build2 (BIT_AND_EXPR, rntype, rhs, lr_mask);
-
-	  return build2_loc (loc, wanted_code, truth_type, lhs, rhs);
-	}
-
-      /* There is still another way we can do something:  If both pairs of
-	 fields being compared are adjacent, we may be able to make a wider
-	 field containing them both.
-
-	 Note that we still must mask the lhs/rhs expressions.  Furthermore,
-	 the mask must be shifted to account for the shift done by
-	 make_bit_field_ref.  */
-      if (((ll_bitsize + ll_bitpos == rl_bitpos
-	    && lr_bitsize + lr_bitpos == rr_bitpos)
-	   || (ll_bitpos == rl_bitpos + rl_bitsize
-	       && lr_bitpos == rr_bitpos + rr_bitsize))
-	  && ll_bitpos >= 0
-	  && rl_bitpos >= 0
-	  && lr_bitpos >= 0
-	  && rr_bitpos >= 0)
-	{
-	  tree type;
-
-	  lhs = make_bit_field_ref (loc, ll_inner, ll_arg, lntype,
-				    ll_bitsize + rl_bitsize,
-				    MIN (ll_bitpos, rl_bitpos),
-				    ll_unsignedp, ll_reversep);
-	  rhs = make_bit_field_ref (loc, lr_inner, lr_arg, rntype,
-				    lr_bitsize + rr_bitsize,
-				    MIN (lr_bitpos, rr_bitpos),
-				    lr_unsignedp, lr_reversep);
-
-	  ll_mask = const_binop (RSHIFT_EXPR, ll_mask,
-				 size_int (MIN (xll_bitpos, xrl_bitpos)));
-	  lr_mask = const_binop (RSHIFT_EXPR, lr_mask,
-				 size_int (MIN (xlr_bitpos, xrr_bitpos)));
-	  if (ll_mask == NULL_TREE || lr_mask == NULL_TREE)
-	    return 0;
-
-	  /* Convert to the smaller type before masking out unwanted bits.  */
-	  type = lntype;
-	  if (lntype != rntype)
-	    {
-	      if (lnbitsize > rnbitsize)
-		{
-		  lhs = fold_convert_loc (loc, rntype, lhs);
-		  ll_mask = fold_convert_loc (loc, rntype, ll_mask);
-		  type = rntype;
-		}
-	      else if (lnbitsize < rnbitsize)
-		{
-		  rhs = fold_convert_loc (loc, lntype, rhs);
-		  lr_mask = fold_convert_loc (loc, lntype, lr_mask);
-		  type = lntype;
-		}
-	    }
-
-	  if (! all_ones_mask_p (ll_mask, ll_bitsize + rl_bitsize))
-	    lhs = build2 (BIT_AND_EXPR, type, lhs, ll_mask);
-
-	  if (! all_ones_mask_p (lr_mask, lr_bitsize + rr_bitsize))
-	    rhs = build2 (BIT_AND_EXPR, type, rhs, lr_mask);
-
-	  return build2_loc (loc, wanted_code, truth_type, lhs, rhs);
-	}
-
-      return 0;
-    }
-
-  /* Handle the case of comparisons with constants.  If there is something in
-     common between the masks, those bits of the constants must be the same.
-     If not, the condition is always false.  Test for this to avoid generating
-     incorrect code below.  */
-  result = const_binop (BIT_AND_EXPR, ll_mask, rl_mask);
-  if (! integer_zerop (result)
-      && simple_cst_equal (const_binop (BIT_AND_EXPR, result, l_const),
-			   const_binop (BIT_AND_EXPR, result, r_const)) != 1)
-    {
-      if (wanted_code == NE_EXPR)
-	{
-	  warning (0, "%<or%> of unmatched not-equal tests is always 1");
-	  return constant_boolean_node (true, truth_type);
-	}
-      else
-	{
-	  warning (0, "%<and%> of mutually exclusive equal-tests is always 0");
-	  return constant_boolean_node (false, 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);
-
-  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);
-
-  return build2_loc (loc, wanted_code, truth_type, result,
-		     const_binop (BIT_IOR_EXPR, l_const, r_const));
+  return 0;
 }
 
 /* T is an integer expression that is being multiplied, divided, or taken a
diff --git a/gcc/fold-const.h b/gcc/fold-const.h
index 3e3998b57b042..77a5c916cbd88 100644
--- a/gcc/fold-const.h
+++ b/gcc/fold-const.h
@@ -253,11 +253,21 @@  extern tree fold_build_pointer_plus_hwi_loc (location_t loc, tree ptr, HOST_WIDE
 extern tree_code minmax_from_comparison (tree_code, tree, tree,
 					 tree, tree);
 
+extern tree make_bit_field_ref (location_t, tree, tree, tree,
+				HOST_WIDE_INT, poly_int64, int, int);
+
 /* In gimple-fold.cc.  */
 extern void clear_type_padding_in_mask (tree, unsigned char *);
 extern bool clear_padding_type_may_have_padding_p (tree);
 extern bool arith_overflowed_p (enum tree_code, const_tree, const_tree,
 				const_tree);
+extern tree fold_truth_andor_for_ifcombine (enum tree_code, tree,
+					    location_t, enum tree_code,
+					    tree, tree,
+					    location_t, enum tree_code,
+					    tree, tree,
+					    tree *);
+
 
 /* Class used to compare gimple operands.  */
 
diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 3c72cd6c79ae6..126d6c5b849e9 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -7438,7 +7438,1159 @@  maybe_fold_comparisons_from_match_pd (tree type, enum tree_code code,
 
   return NULL_TREE;
 }
+
+/* Return TRUE and set op[0] if T, following all SSA edges, is a type
+   conversion.  */
 
+static bool
+gimple_fold_follow_convert (tree t, tree op[1])
+{
+  if (TREE_CODE (t) == SSA_NAME
+      && !SSA_NAME_IS_DEFAULT_DEF (t))
+    if (gassign *def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (t)))
+      switch (gimple_assign_rhs_code (def))
+	{
+	CASE_CONVERT:
+	  op[0] = gimple_assign_rhs1 (def);
+	  return true;
+	case VIEW_CONVERT_EXPR:
+	  op[0] = TREE_OPERAND (gimple_assign_rhs1 (def), 0);
+	  return true;
+	default:
+	  break;
+	}
+  return false;
+}
+
+/* Return TRUE and set op[*] if T, following all SSA edges, resolves to a
+   binary expression with code CODE.  */
+
+static bool
+gimple_fold_binop_cst (enum tree_code code, tree t, tree op[2])
+{
+  if (TREE_CODE (t) == SSA_NAME
+      && !SSA_NAME_IS_DEFAULT_DEF (t))
+    if (gimple *def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (t)))
+      if (gimple_assign_rhs_code (def) == code)
+	{
+	  tree op0 = gimple_assign_rhs1 (def);
+	  tree op1 = gimple_assign_rhs2 (def);
+	  if (tree_swap_operands_p (op0, op1))
+	    std::swap (op0, op1);
+	  if (uniform_integer_cst_p (op1))
+	    {
+	      op[0] = op0;
+	      op[1] = op1;
+	      return true;
+	    }
+	}
+  return false;
+}
+/* Subroutine for fold_truth_andor_1: decode a field reference.
+
+   If *PEXP is a comparison reference, we return the innermost reference.
+
+   *PBITSIZE is set to the number of bits in the reference, *PBITPOS is
+   set to the starting bit number.
+
+   *PVOLATILEP is set to 1 if the any expression encountered is volatile;
+   otherwise it is not changed.
+
+   *PUNSIGNEDP is set to the signedness of the field.
+
+   *PREVERSEP is set to the storage order of the field.
+
+   *PAND_MASK is set to the mask found in a BIT_AND_EXPR, if any.
+   If PAND_MASK *is NULL, BIT_AND_EXPR is not recognized.
+
+   *XOR_P is to be FALSE if EXP might be a XOR used in a compare, in which
+   case, if XOR_CMP_OP is a zero constant, it will be overridden with *PEXP,
+   *XOR_P will be set to TRUE, and the left-hand operand of the XOR will be
+   decoded.  If *XOR_P is TRUE, XOR_CMP_OP is supposed to be NULL, and then the
+   right-hand operand of the XOR will be decoded.
+
+   *LOAD is set to the load stmt of the innermost reference, if any,
+   *and NULL otherwise.
+
+   LOC[0..3] are filled in as conversion, masking, shifting and loading
+   operations are located.
+
+   Return 0 if this is not a component reference or is one that we can't
+   do anything with.  */
+
+static tree
+decode_field_reference (tree *pexp, HOST_WIDE_INT *pbitsize,
+			HOST_WIDE_INT *pbitpos,
+			bool *punsignedp, bool *preversep, bool *pvolatilep,
+			wide_int *pand_mask, bool *xor_p, tree *xor_cmp_op,
+			gimple **load, location_t loc[4])
+{
+  tree exp = *pexp;
+  tree outer_type = 0;
+  wide_int and_mask;
+  tree inner, offset;
+  int shiftrt = 0;
+  tree res_ops[2];
+  machine_mode mode;
+
+  *load = NULL;
+
+  /* All the optimizations using this function assume integer fields.
+     There are problems with FP fields since the type_for_size call
+     below can fail for, e.g., XFmode.  */
+  if (! INTEGRAL_TYPE_P (TREE_TYPE (exp)))
+    return NULL_TREE;
+
+  /* Drop casts, only save the outermost type.  We need not worry about
+     narrowing then widening casts, or vice-versa, for those that are not
+     essential for the compare have already been optimized out at this
+     point.  */
+  if (gimple_fold_follow_convert (exp, res_ops))
+    {
+      if (!outer_type)
+	{
+	  outer_type = TREE_TYPE (exp);
+	  loc[0] = gimple_location (SSA_NAME_DEF_STMT (exp));
+	}
+      exp = res_ops[0];
+    }
+
+  /* Recognize and save a masking operation.  */
+  if (pand_mask && gimple_fold_binop_cst (BIT_AND_EXPR, exp, res_ops))
+    {
+      loc[1] = gimple_location (SSA_NAME_DEF_STMT (exp));
+      exp = res_ops[0];
+      and_mask = wi::to_wide (res_ops[1]);
+    }
+
+  /* Turn (a ^ b) [!]= 0 into a [!]= b.  */
+  if (xor_p && gimple_fold_binop_cst (BIT_XOR_EXPR, exp, res_ops))
+    {
+      /* No location recorded for this one, it's entirely subsumed by the
+	 compare.  */
+      if (*xor_p)
+	{
+	  exp = res_ops[1];
+	  gcc_checking_assert (!xor_cmp_op);
+	}
+      else if (!xor_cmp_op)
+	/* Not much we can do when xor appears in the right-hand compare
+	   operand.  */
+	return NULL_TREE;
+      else
+	{
+	  *xor_p = true;
+	  exp = res_ops[0];
+	  *xor_cmp_op = *pexp;
+	}
+    }
+
+  /* Another chance to drop conversions.  */
+  if (gimple_fold_follow_convert (exp, res_ops))
+    {
+      if (!outer_type)
+	{
+	  outer_type = TREE_TYPE (exp);
+	  loc[0] = gimple_location (SSA_NAME_DEF_STMT (exp));
+	}
+      exp = res_ops[0];
+    }
+
+  /* Take note of shifts.  */
+  if (gimple_fold_binop_cst (RSHIFT_EXPR, exp, res_ops))
+    {
+      loc[2] = gimple_location (SSA_NAME_DEF_STMT (exp));
+      exp = res_ops[0];
+      if (!tree_fits_shwi_p (res_ops[1]))
+	return NULL_TREE;
+      shiftrt = tree_to_shwi (res_ops[1]);
+      if (shiftrt <= 0)
+	return NULL_TREE;
+    }
+
+  /* Yet another chance to drop conversions.  */
+  if (gimple_fold_follow_convert (exp, res_ops))
+    {
+      if (!outer_type)
+	{
+	  outer_type = TREE_TYPE (exp);
+	  loc[0] = gimple_location (SSA_NAME_DEF_STMT (exp));
+	}
+      exp = res_ops[0];
+    }
+
+  /* Identify the load, if there is one.  */
+  if (TREE_CODE (exp) == SSA_NAME
+      && !SSA_NAME_IS_DEFAULT_DEF (exp))
+    {
+      gimple *def = SSA_NAME_DEF_STMT (exp);
+      if (gimple_assign_load_p (def))
+	{
+	  loc[3] = gimple_location (def);
+	  *load = def;
+	  exp = gimple_assign_rhs1 (def);
+	}
+    }
+
+  /* Identify the relevant bits.  */
+  poly_int64 poly_bitsize, poly_bitpos;
+  int unsignedp, reversep = *preversep, volatilep = *pvolatilep;
+  inner = get_inner_reference (exp, &poly_bitsize, &poly_bitpos, &offset,
+			       &mode, &unsignedp, &reversep, &volatilep);
+
+  HOST_WIDE_INT bs, bp;
+  if (!poly_bitsize.is_constant (&bs)
+      || !poly_bitpos.is_constant (&bp)
+      || bs <= shiftrt
+      || offset != 0
+      || TREE_CODE (inner) == PLACEHOLDER_EXPR
+      /* Reject out-of-bound accesses (PR79731).  */
+      || (! AGGREGATE_TYPE_P (TREE_TYPE (inner))
+	  && compare_tree_int (TYPE_SIZE (TREE_TYPE (inner)),
+			       bp + bs) < 0))
+    return NULL_TREE;
+
+  *pbitsize = bs;
+  *pbitpos = bp;
+  *punsignedp = unsignedp;
+  *preversep = reversep;
+  *pvolatilep = volatilep;
+
+  /* Adjust shifts...  */
+  if (shiftrt)
+    {
+      if (!*preversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+	*pbitpos += shiftrt;
+      *pbitsize -= shiftrt;
+    }
+
+  /* ... and bit position.  */
+  if (outer_type && *pbitsize > TYPE_PRECISION (outer_type))
+    {
+      HOST_WIDE_INT excess = *pbitsize - TYPE_PRECISION (outer_type);
+      if (*preversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+	*pbitpos += excess;
+      *pbitsize -= excess;
+    }
+
+  *pexp = exp;
+
+  /* If the number of bits in the reference is the same as the bitsize of
+     the outer type, then the outer type gives the signedness. Otherwise
+     (in case of a small bitfield) the signedness is unchanged.  */
+  if (outer_type && *pbitsize == TYPE_PRECISION (outer_type))
+    *punsignedp = TYPE_UNSIGNED (outer_type);
+
+  /* Make the mask the expected width.  */
+  if (and_mask.get_precision () != 0)
+    and_mask = wide_int::from (and_mask, *pbitsize, UNSIGNED);
+
+  if (pand_mask)
+    *pand_mask = and_mask;
+
+  return inner;
+}
+
+/* Return the one bitpos within bit extents L or R that is at an
+   ALIGN-bit alignment boundary, or -1 if there is more than one such
+   boundary, if there isn't any, or if there is any such boundary
+   between the extents.  L and R are given by bitpos and bitsize.  If
+   it doesn't return -1, there are two consecutive ALIGN-bit words
+   that contain both extents, and at least one of the extents
+   straddles across the returned alignment boundary.  */
+
+static inline HOST_WIDE_INT
+compute_split_boundary_from_align (HOST_WIDE_INT align,
+				   HOST_WIDE_INT l_bitpos,
+				   HOST_WIDE_INT l_bitsize,
+				   HOST_WIDE_INT r_bitpos,
+				   HOST_WIDE_INT r_bitsize)
+{
+  HOST_WIDE_INT amask = ~(align - 1);
+
+  HOST_WIDE_INT first_bit = MIN (l_bitpos, r_bitpos);
+  HOST_WIDE_INT end_bit = MAX (l_bitpos + l_bitsize, r_bitpos + r_bitsize);
+
+  HOST_WIDE_INT boundary = (end_bit - 1) & amask;
+
+  /* Make sure we're crossing no more than one alignment boundary.
+
+     ??? We don't have logic to recombine loads of two adjacent
+     fields that each crosses a different alignment boundary, so
+     as to load the middle word only once, if other words can't be
+     otherwise recombined.  */
+  if (boundary - first_bit > align)
+    return -1;
+
+  HOST_WIDE_INT l_start_word = l_bitpos & amask;
+  HOST_WIDE_INT l_end_word = (l_bitpos + l_bitsize - 1) & amask;
+
+  HOST_WIDE_INT r_start_word = r_bitpos & amask;
+  HOST_WIDE_INT r_end_word = (r_bitpos + r_bitsize - 1) & amask;
+
+  /* If neither field straddles across an alignment boundary, it's no
+     use to even try to merge them.  */
+  if (l_start_word == l_end_word && r_start_word == r_end_word)
+    return -1;
+
+  return boundary;
+}
+
+/* Make a bit_field_ref.  If POINT is NULL, return the BIT_FIELD_REF.
+   Otherwise, build and insert a load stmt before POINT, and return
+   the SSA_NAME.  ???  Rewrite LOAD in terms of the bitfield?  */
+
+static tree
+make_bit_field_load (location_t loc, tree inner, tree orig_inner, tree type,
+		     HOST_WIDE_INT bitsize, poly_int64 bitpos,
+		     bool unsignedp, bool reversep, gimple *point)
+{
+  if (point && loc == UNKNOWN_LOCATION)
+    loc = gimple_location (point);
+
+  tree ref = make_bit_field_ref (loc, unshare_expr (inner),
+				 unshare_expr (orig_inner),
+				 type, bitsize, bitpos,
+				 unsignedp, reversep);
+  if (!point)
+    return ref;
+
+  gimple_seq stmts = NULL;
+  tree ret = force_gimple_operand (ref, &stmts, true, NULL_TREE);
+
+  /* We know the vuse is supposed to end up being the same as that at the
+     original load at the insertion point, but if we don't set it, it will be a
+     generic placeholder that only the global SSA update at the end of the pass
+     would make equal, too late for us to use in further combinations.  So go
+     ahead and copy the vuse.  */
+
+  tree reaching_vuse = gimple_vuse (point);
+  for (gimple_stmt_iterator i = gsi_start (stmts);
+       !gsi_end_p (i); gsi_next (&i))
+    {
+      gimple *new_stmt = gsi_stmt (i);
+      if (gimple_has_mem_ops (new_stmt))
+	gimple_set_vuse (new_stmt, reaching_vuse);
+    }
+
+  gimple_stmt_iterator gsi = gsi_for_stmt (point);
+  gsi_insert_seq_before (&gsi, stmts, GSI_SAME_STMT);
+  return ret;
+}
+
+/* Initialize ln_arg[0] and ln_arg[1] to a pair of newly-created (at
+   LOC) loads from INNER (from ORIG_INNER), of modes MODE and MODE2,
+   respectively, starting at BIT_POS, using reversed endianness if
+   REVERSEP.  Also initialize BITPOS (the starting position of each
+   part into INNER), BITSIZ (the bit count starting at BITPOS),
+   TOSHIFT[1] (the amount by which the part and its mask are to be
+   shifted right to bring its least-significant bit to bit zero) and
+   SHIFTED (the amount by which the part, by separate loading, has
+   already been shifted right, but that the mask needs shifting to
+   match).  */
+
+static inline void
+build_split_load (tree /* out */ ln_arg[2],
+		  HOST_WIDE_INT /* out */ bitpos[2],
+		  HOST_WIDE_INT /* out */ bitsiz[2],
+		  HOST_WIDE_INT /* in[0] out[0..1] */ toshift[2],
+		  HOST_WIDE_INT /* out */ shifted[2],
+		  location_t loc, tree inner, tree orig_inner,
+		  scalar_int_mode mode, scalar_int_mode mode2,
+		  HOST_WIDE_INT bit_pos, bool reversep,
+		  gimple *point[2])
+{
+  scalar_int_mode modes[2] = { mode, mode2 };
+  bitsiz[0] = GET_MODE_BITSIZE (mode);
+  bitsiz[1] = GET_MODE_BITSIZE (mode2);
+
+  for (int i = 0; i < 2; i++)
+    {
+      tree type = lang_hooks.types.type_for_mode (modes[i], 1);
+      if (!type)
+	{
+	  type = build_nonstandard_integer_type (bitsiz[0], 1);
+	  gcc_assert (type);
+	}
+      bitpos[i] = bit_pos;
+      ln_arg[i] = make_bit_field_load (loc, inner, orig_inner,
+				       type, bitsiz[i],
+				       bit_pos, 1, reversep, point[i]);
+      bit_pos += bitsiz[i];
+    }
+
+  toshift[1] = toshift[0];
+  if (reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+    {
+      shifted[0] = bitsiz[1];
+      shifted[1] = 0;
+      toshift[0] = 0;
+    }
+  else
+    {
+      shifted[1] = bitsiz[0];
+      shifted[0] = 0;
+      toshift[1] = 0;
+    }
+}
+
+/* Make arrangements to split at bit BOUNDARY a single loaded word
+   (with REVERSEP bit order) LN_ARG[0], to be shifted right by
+   TOSHIFT[0] to bring the field of interest to the least-significant
+   bit.  The expectation is that the same loaded word will be
+   propagated from part 0 to part 1, with just different shifting and
+   masking to extract both parts.  MASK is not expected to do more
+   than masking out the bits that belong to the other part.  See
+   build_split_load for more information on the other fields.  */
+
+static inline void
+reuse_split_load (tree /* in[0] out[1] */ ln_arg[2],
+		  HOST_WIDE_INT /* in[0] out[1] */ bitpos[2],
+		  HOST_WIDE_INT /* in[0] out[1] */ bitsiz[2],
+		  HOST_WIDE_INT /* in[0] out[0..1] */ toshift[2],
+		  HOST_WIDE_INT /* out */ shifted[2],
+		  wide_int /* out */ mask[2],
+		  HOST_WIDE_INT boundary, bool reversep)
+{
+  unsigned prec = TYPE_PRECISION (TREE_TYPE (ln_arg[0]));
+
+  ln_arg[1] = ln_arg[0];
+  bitpos[1] = bitpos[0];
+  bitsiz[1] = bitsiz[0];
+  shifted[1] = shifted[0] = 0;
+
+  if (reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+    {
+      toshift[1] = toshift[0];
+      toshift[0] = bitpos[0] + bitsiz[0] - boundary;
+      mask[0] = wi::mask (toshift[0], true, prec);
+      mask[1] = wi::mask (toshift[0], false, prec);
+    }
+  else
+    {
+      toshift[1] = boundary - bitpos[1];
+      mask[1] = wi::mask (toshift[1], true, prec);
+      mask[0] = wi::mask (toshift[1], false, prec);
+    }
+}
+
+/* Find ways of folding logical expressions of LHS and RHS:
+
+   Try to merge two comparisons to nearby fields.
+
+   For example, if we have p->a == 2 && p->b == 4 and we can load both A and B
+   at once, we can do this with a comparison against the object ANDed with the
+   a mask.
+
+   If we have p->a == q->a && p->b == q->b, we may be able to use bit masking
+   operations to do this with one comparison, loading both fields from P at
+   once, and likewise from Q.
+
+   Herein, loading at once means loading from within the same alignment
+   boundary for the enclosing object.  If (packed) fields cross such alignment
+   boundaries, we may still recombine the compares, so that loads do not cross
+   the boundaries.
+
+   CODE is the logical operation being done.  It can be TRUTH_ANDIF_EXPR,
+   TRUTH_AND_EXPR, TRUTH_ORIF_EXPR, or TRUTH_OR_EXPR.
+
+   TRUTH_TYPE is the type of the logical operand.
+
+   LHS is denoted as LL_ARG LCODE LR_ARG.
+
+   RHS is denoted as RL_ARG RCODE RR_ARG.
+
+   LHS is assumed to dominate RHS.
+
+   Combined loads are inserted next to preexisting loads, once we determine
+   that the combination is viable, and the combined condition references new
+   SSA_NAMEs that hold the loaded values.  Since the original loads are
+   verified to have the same gimple_vuse, the insertion point doesn't matter
+   for correctness.  ??? The loads may be a lot earlier than the compares, and
+   it's conceivable that one or two loads for RHS appear before those for LHS.
+   It could be advantageous to try to place the loads optimally, taking
+   advantage of knowing whether RHS is accessed before LHS, or that both are
+   accessed before both compares, but we don't do that (yet?).
+
+   SEPARATEP should be NULL if the combined condition must be returned as a
+   single expression, even if it is a compound condition.  This must only be
+   done if LHS and RHS are adjacent, without intervening conditions, and the
+   combined condition is to replace RHS, while LHS is dropped altogether.
+
+   Otherwise, SEPARATEP must be a non-NULL pointer to a NULL_TREE, that may be
+   replaced by a part of the compound condition that could replace RHS, while
+   the returned expression replaces LHS.  This works whether or not LHS and RHS
+   are adjacent, as long as there aren't VDEFs or other side effects between
+   them.
+
+   If the "words" accessed by RHS are already accessed by LHS, this won't
+   matter, but if RHS accesses "words" that LHS doesn't, then *SEPARATEP will
+   be set to the compares that should take RHS's place.  By "words" we mean
+   contiguous bits that do not cross a an TYPE_ALIGN boundary of the accessed
+   object's type.
+
+   We return the simplified tree or 0 if no optimization is possible.  */
+
+tree
+fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type,
+				location_t lloc, enum tree_code lcode,
+				tree ll_arg, tree lr_arg,
+				location_t rloc, enum tree_code rcode,
+				tree rl_arg, tree rr_arg,
+				tree *separatep)
+{
+  /* If this is the "or" of two comparisons, we can do something if
+     the comparisons are NE_EXPR.  If this is the "and", we can do something
+     if the comparisons are EQ_EXPR.  I.e.,
+	(a->b == 2 && a->c == 4) can become (a->new == NEW).
+
+     WANTED_CODE is this operation code.  For single bit fields, we can
+     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;
+  tree ll_inner, lr_inner, rl_inner, rr_inner;
+  gimple *ll_load, *lr_load, *rl_load, *rr_load;
+  HOST_WIDE_INT ll_bitsize, ll_bitpos, lr_bitsize, lr_bitpos;
+  HOST_WIDE_INT rl_bitsize, rl_bitpos, rr_bitsize, rr_bitpos;
+  HOST_WIDE_INT xll_bitpos, xlr_bitpos, xrl_bitpos, xrr_bitpos;
+  HOST_WIDE_INT lnbitsize, lnbitpos, lnprec;
+  HOST_WIDE_INT rnbitsize, rnbitpos, rnprec;
+  bool ll_unsignedp, lr_unsignedp, rl_unsignedp, rr_unsignedp;
+  bool ll_reversep, lr_reversep, rl_reversep, rr_reversep;
+  scalar_int_mode lnmode, lnmode2, rnmode;
+  wide_int ll_and_mask, lr_and_mask, rl_and_mask, rr_and_mask;
+  wide_int l_const, r_const;
+  tree lntype, rntype, result;
+  HOST_WIDE_INT first_bit, end_bit;
+  bool volatilep;
+  bool l_split_load;
+
+  /* These are indexed by: conv, mask, shft, load.  */
+  location_t ll_loc[4] = { lloc, lloc, lloc, UNKNOWN_LOCATION };
+  location_t lr_loc[4] = { lloc, lloc, lloc, UNKNOWN_LOCATION };
+  location_t rl_loc[4] = { rloc, rloc, rloc, UNKNOWN_LOCATION };
+  location_t rr_loc[4] = { rloc, rloc, rloc, UNKNOWN_LOCATION };
+
+  gcc_checking_assert (!separatep || !*separatep);
+
+  /* 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
+     it were surrounded with a NE_EXPR.  */
+
+  if (TREE_CODE_CLASS (lcode) != tcc_comparison
+      || TREE_CODE_CLASS (rcode) != tcc_comparison)
+    return 0;
+
+  /* We don't normally find TRUTH_*IF_EXPR in gimple, but these codes may be
+     given by our caller to denote conditions from different blocks.  */
+  switch (code)
+    {
+    case TRUTH_AND_EXPR:
+    case TRUTH_ANDIF_EXPR:
+      code = TRUTH_AND_EXPR;
+      break;
+
+    case TRUTH_OR_EXPR:
+    case TRUTH_ORIF_EXPR:
+      code = TRUTH_OR_EXPR;
+      break;
+
+    default:
+      return 0;
+    }
+
+  bool lsignbit = false, rsignbit = false;
+  if ((lcode == LT_EXPR || lcode == GE_EXPR)
+      && integer_zerop (lr_arg)
+      && INTEGRAL_TYPE_P (TREE_TYPE (ll_arg))
+      && !TYPE_UNSIGNED (TREE_TYPE (ll_arg)))
+    {
+      lsignbit = true;
+      lcode = (lcode == LT_EXPR ? NE_EXPR : EQ_EXPR);
+    }
+  if ((rcode == LT_EXPR || rcode == GE_EXPR)
+      && integer_zerop (rr_arg)
+      && INTEGRAL_TYPE_P (TREE_TYPE (rl_arg))
+      && !TYPE_UNSIGNED (TREE_TYPE (rl_arg)))
+    {
+      rsignbit = true;
+      rcode = (rcode == LT_EXPR ? NE_EXPR : EQ_EXPR);
+    }
+
+  /* See if the comparisons can be merged.  Then get all the parameters for
+     each side.  */
+
+  if ((lcode != EQ_EXPR && lcode != NE_EXPR)
+      || (rcode != EQ_EXPR && rcode != NE_EXPR))
+    return 0;
+
+  ll_reversep = lr_reversep = rl_reversep = rr_reversep = 0;
+  volatilep = 0;
+  bool l_xor = false, r_xor = false;
+  ll_inner = decode_field_reference (&ll_arg, &ll_bitsize, &ll_bitpos,
+				     &ll_unsignedp, &ll_reversep, &volatilep,
+				     &ll_and_mask, &l_xor, &lr_arg,
+				     &ll_load, ll_loc);
+  lr_inner = decode_field_reference (&lr_arg, &lr_bitsize, &lr_bitpos,
+				     &lr_unsignedp, &lr_reversep, &volatilep,
+				     &lr_and_mask, &l_xor, 0,
+				     &lr_load, lr_loc);
+  rl_inner = decode_field_reference (&rl_arg, &rl_bitsize, &rl_bitpos,
+				     &rl_unsignedp, &rl_reversep, &volatilep,
+				     &rl_and_mask, &r_xor, &rr_arg,
+				     &rl_load, rl_loc);
+  rr_inner = decode_field_reference (&rr_arg, &rr_bitsize, &rr_bitpos,
+				     &rr_unsignedp, &rr_reversep, &volatilep,
+				     &rr_and_mask, &r_xor, 0,
+				     &rr_load, rr_loc);
+
+  /* It must be true that the inner operation on the lhs of each
+     comparison must be the same if we are to be able to do anything.
+     Then see if we have constants.  If not, the same must be true for
+     the rhs's.  */
+  if (volatilep
+      || ll_reversep != rl_reversep
+      || ll_inner == 0 || rl_inner == 0
+      || ! operand_equal_p (ll_inner, rl_inner, 0)
+      || (ll_load && rl_load
+	  && gimple_vuse (ll_load) != gimple_vuse (rl_load)))
+    return 0;
+
+  if (TREE_CODE (lr_arg) == INTEGER_CST
+      && TREE_CODE (rr_arg) == INTEGER_CST)
+    {
+      l_const = wi::to_wide (lr_arg);
+      /* We don't expect masks on constants, but if there are any, apply
+	 them now.  */
+      if (lr_and_mask.get_precision ())
+	l_const &= wide_int::from (lr_and_mask,
+				   l_const.get_precision (), UNSIGNED);
+      r_const = wi::to_wide (rr_arg);
+      if (rr_and_mask.get_precision ())
+	r_const &= wide_int::from (rr_and_mask,
+				   r_const.get_precision (), UNSIGNED);
+      lr_reversep = ll_reversep;
+    }
+  else if (lr_reversep != rr_reversep
+	   || lr_inner == 0 || rr_inner == 0
+	   || ! operand_equal_p (lr_inner, rr_inner, 0)
+	   || (lr_load && rr_load
+	       && gimple_vuse (lr_load) != gimple_vuse (rr_load)))
+    return 0;
+
+  /* If we found sign tests, finish turning them into bit tests.  */
+
+  if (lsignbit)
+    {
+      wide_int sign = wi::mask (ll_bitsize - 1, true, ll_bitsize);
+      if (!ll_and_mask.get_precision ())
+	ll_and_mask = sign;
+      else
+	ll_and_mask &= sign;
+    }
+
+  if (rsignbit)
+    {
+      wide_int sign = wi::mask (rl_bitsize - 1, true, rl_bitsize);
+      if (!rl_and_mask.get_precision ())
+	rl_and_mask = sign;
+      else
+	rl_and_mask &= sign;
+    }
+
+  /* If either comparison code is not correct for our logical operation,
+     fail.  However, we can convert a one-bit comparison against zero into
+     the opposite comparison against that bit being set in the field.  */
+
+  wanted_code = (code == TRUTH_AND_EXPR ? EQ_EXPR : NE_EXPR);
+  if (lcode != wanted_code)
+    {
+      if (l_const.get_precision ()
+	  && l_const == 0
+	  && ll_and_mask.get_precision ()
+	  && wi::popcount (ll_and_mask) == 1)
+	{
+	  /* Make the left operand unsigned, since we are only interested
+	     in the value of one bit.  Otherwise we are doing the wrong
+	     thing below.  */
+	  ll_unsignedp = 1;
+	  l_const = ll_and_mask;
+	}
+      else
+	return 0;
+    }
+
+  /* This is analogous to the code for l_const above.  */
+  if (rcode != wanted_code)
+    {
+      if (r_const.get_precision ()
+	  && r_const == 0
+	  && rl_and_mask.get_precision ()
+	  && wi::popcount (rl_and_mask) == 1)
+	{
+	  rl_unsignedp = 1;
+	  r_const = rl_and_mask;
+	}
+      else
+	return 0;
+    }
+
+  /* This will be bumped to 2 if any of the field pairs crosses an
+     alignment boundary, so the merged compare has to be done in two
+     parts.  */
+  int parts = 1;
+  /* Set to true if the second combined compare should come first,
+     e.g., because the second original compare accesses a word that
+     the first one doesn't, and the combined compares access those in
+     cmp[0].  */
+  bool first1 = false;
+  /* Set to true if the first original compare is not the one being
+     split.  */
+  bool maybe_separate = false;
+
+  /* The following 2-dimensional arrays use the first index to
+     identify left(0)- vs right(1)-hand compare operands, and the
+     second one to identify merged compare parts.  */
+  /* The memory loads or constants to be compared.  */
+  tree ld_arg[2][2];
+  /* The first bit of the corresponding inner object that the
+     corresponding LD_ARG covers.  */
+  HOST_WIDE_INT bitpos[2][2];
+  /* The bit count starting at BITPOS that the corresponding LD_ARG
+     covers.  */
+  HOST_WIDE_INT bitsiz[2][2];
+  /* The number of bits by which LD_ARG has already been shifted
+     right, WRT mask.  */
+  HOST_WIDE_INT shifted[2][2];
+  /* The number of bits by which both LD_ARG and MASK need shifting to
+     bring its least-significant bit to bit zero.  */
+  HOST_WIDE_INT toshift[2][2];
+  /* An additional mask to be applied to LD_ARG, to remove any bits
+     that may have been loaded for use in another compare, but that
+     don't belong in the corresponding compare.  */
+  wide_int xmask[2][2] = {};
+
+  /* The combined compare or compares.  */
+  tree cmp[2];
+
+  /* Consider we're comparing two non-contiguous fields of packed
+     structs, both aligned at 32-bit boundaries:
+
+     ll_arg: an 8-bit field at offset 0
+     lr_arg: a 16-bit field at offset 2
+
+     rl_arg: an 8-bit field at offset 1
+     rr_arg: a 16-bit field at offset 3
+
+     We'll have r_split_load, because rr_arg straddles across an
+     alignment boundary.
+
+     We'll want to have:
+
+     bitpos  = { {  0,  0 }, {  0, 32 } }
+     bitsiz  = { { 32, 32 }, { 32,  8 } }
+
+     And, for little-endian:
+
+     shifted = { {  0,  0 }, {  0, 32 } }
+     toshift = { {  0, 24 }, {  0,  0 } }
+
+     Or, for big-endian:
+
+     shifted = { {  0,  0 }, {  8,  0 } }
+     toshift = { {  8,  0 }, {  0,  0 } }
+  */
+
+  /* See if we can find a mode that contains both fields being compared on
+     the left.  If we can't, fail.  Otherwise, update all constants and masks
+     to be relative to a field of that size.  */
+  first_bit = MIN (ll_bitpos, rl_bitpos);
+  end_bit = MAX (ll_bitpos + ll_bitsize, rl_bitpos + rl_bitsize);
+  if (get_best_mode (end_bit - first_bit, first_bit, 0, 0,
+		     TYPE_ALIGN (TREE_TYPE (ll_inner)), BITS_PER_WORD,
+		     volatilep, &lnmode))
+    l_split_load = false;
+  else
+    {
+      /* 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 boundary = compute_split_boundary_from_align
+	(align, ll_bitpos, ll_bitsize, rl_bitpos, rl_bitsize);
+
+      if (boundary < 0
+	  || !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;
+
+      /* If we can't have a single load, but can with two, figure out whether
+	 the two compares can be separated, i.e., whether the entirety of the
+	 first original compare is encompassed by the entirety of the first
+	 combined compare.  If the first original compare is past the alignment
+	 boundary, arrange to compare that range first, by setting first1
+	 (meaning make cmp[1] first, instead of cmp[0]).  */
+      l_split_load = true;
+      parts = 2;
+      if (ll_bitpos >= boundary)
+	maybe_separate = first1 = true;
+      else if (ll_bitpos + ll_bitsize <= boundary)
+	maybe_separate = true;
+    }
+
+  lnbitsize = GET_MODE_BITSIZE (lnmode);
+  lnbitpos = first_bit & ~ (lnbitsize - 1);
+  /* Avoid situations that the code below can't handle.  */
+  if (lnbitpos < 0)
+    return 0;
+
+  /* Choose the type for the combined compare.  Even if we're splitting loads,
+     make it wide enough to hold both.  */
+  if (l_split_load)
+    lnbitsize += GET_MODE_BITSIZE (lnmode2);
+  lntype = build_nonstandard_integer_type (lnbitsize, 1);
+  if (!lntype)
+    return NULL_TREE;
+  lnprec = TYPE_PRECISION (lntype);
+  xll_bitpos = ll_bitpos - lnbitpos, xrl_bitpos = rl_bitpos - lnbitpos;
+
+  /* Adjust bit ranges for reverse endianness.  */
+  if (ll_reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+    {
+      xll_bitpos = lnbitsize - xll_bitpos - ll_bitsize;
+      xrl_bitpos = lnbitsize - xrl_bitpos - rl_bitsize;
+    }
+
+  /* Adjust masks to match the positions in the combined lntype.  */
+  wide_int ll_mask, rl_mask, r_mask;
+  if (ll_and_mask.get_precision ())
+    ll_mask = wi::lshift (wide_int::from (ll_and_mask, lnprec, UNSIGNED),
+			  xll_bitpos);
+  else
+    ll_mask = wi::shifted_mask (xll_bitpos, ll_bitsize, false, lnprec);
+  if (rl_and_mask.get_precision ())
+    rl_mask = wi::lshift (wide_int::from (rl_and_mask, lnprec, UNSIGNED),
+			  xrl_bitpos);
+  else
+    rl_mask = wi::shifted_mask (xrl_bitpos, rl_bitsize, false, lnprec);
+
+  /* Adjust right-hand constants in both original comparisons to match width
+     and bit position.  */
+  if (l_const.get_precision ())
+    {
+      /* We're doing bitwise equality tests, so don't bother with sign
+	 extensions.  */
+      l_const = wide_int::from (l_const, lnprec, UNSIGNED);
+      if (ll_and_mask.get_precision ())
+	l_const &= wide_int::from (ll_and_mask, lnprec, UNSIGNED);
+      l_const <<= xll_bitpos;
+      if ((l_const & ~ll_mask) != 0)
+	{
+	  warning_at (lloc, OPT_Wtautological_compare,
+		      "comparison is always %d", wanted_code == NE_EXPR);
+
+	  return constant_boolean_node (wanted_code == NE_EXPR, truth_type);
+	}
+
+      /* When we set l_const, we also set r_const, so we need not test it
+	 again.  */
+      gcc_checking_assert (r_const.get_precision ());
+
+      r_const = wide_int::from (r_const, lnprec, UNSIGNED);
+      if (rl_and_mask.get_precision ())
+	r_const &= wide_int::from (rl_and_mask, lnprec, UNSIGNED);
+      r_const <<= xrl_bitpos;
+      if ((r_const & ~rl_mask) != 0)
+	{
+	  warning_at (rloc, OPT_Wtautological_compare,
+		      "comparison is always %d", wanted_code == NE_EXPR);
+
+	  return constant_boolean_node (wanted_code == NE_EXPR, truth_type);
+	}
+
+      /* If there is something in common between the masks, those bits of the
+	 constants must be the same.  If not, the combined condition cannot be
+	 met, and the result is known.  Test for this to avoid generating
+	 incorrect code below.  */
+      wide_int mask = ll_mask & rl_mask;
+      if (mask != 0
+	  && (l_const & mask) != (r_const & mask))
+	{
+	  if (wanted_code == NE_EXPR)
+	    return constant_boolean_node (true, truth_type);
+	  else
+	    return constant_boolean_node (false, truth_type);
+	}
+
+      /* The constants are combined so as to line up with the loaded field, so
+	 tentatively use the same parameters for the second combined
+	 compare.  */
+      ld_arg[1][0] = wide_int_to_tree (lntype, l_const | r_const);
+      toshift[1][0] = MIN (xll_bitpos, xrl_bitpos);
+      shifted[1][0] = 0;
+      bitpos[1][0] = lnbitpos;
+      bitsiz[1][0] = lnbitsize;
+
+      if (parts > 1)
+	reuse_split_load (ld_arg[1], bitpos[1], bitsiz[1], toshift[1],
+			  shifted[1], xmask[1],
+			  lnbitpos + GET_MODE_BITSIZE (lnmode),
+			  lr_reversep);
+
+      /* No masking needed, we know the full constants.  */
+      r_mask = wi::mask (0, true, lnprec);
+
+      /* If the compiler thinks this is used uninitialized below, it's
+	 because it can't realize that parts can only be 2 when
+	 comparing with constants if l_split_load is also true.  This
+	 just silences the warning.  */
+      rnbitpos = 0;
+    }
+
+  /* Likewise, if the right sides are not constant, align them for the combined
+     compare.  Also, disallow this optimization if a size, signedness or
+     storage order mismatch occurs between the left and right sides.  */
+  else
+    {
+      if (ll_bitsize != lr_bitsize || rl_bitsize != rr_bitsize
+	  || ll_unsignedp != lr_unsignedp || rl_unsignedp != rr_unsignedp
+	  || 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)
+	return 0;
+
+      bool r_split_load;
+      scalar_int_mode rnmode2;
+
+      /* Figure out how to load the bits for the right-hand size of the
+	 combined compare.  As in the left-hand size, we may have to split it,
+	 and then we use two separate compares.  */
+      first_bit = MIN (lr_bitpos, rr_bitpos);
+      end_bit = MAX (lr_bitpos + lr_bitsize, rr_bitpos + rr_bitsize);
+      if (!get_best_mode (end_bit - first_bit, first_bit, 0, 0,
+			  TYPE_ALIGN (TREE_TYPE (lr_inner)), BITS_PER_WORD,
+			  volatilep, &rnmode))
+	{
+	  /* 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 (lr_inner));
+	  HOST_WIDE_INT boundary = compute_split_boundary_from_align
+	    (align, lr_bitpos, lr_bitsize, rr_bitpos, rr_bitsize);
+
+	  if (boundary < 0
+	      /* If we're to split both, make sure the split point is
+		 the same.  */
+	      || (l_split_load
+		  && (boundary - lr_bitpos
+		      != (lnbitpos + GET_MODE_BITSIZE (lnmode)) - ll_bitpos))
+	      || !get_best_mode (boundary - first_bit, first_bit, 0, 0,
+				 align, BITS_PER_WORD, volatilep, &rnmode)
+	      || !get_best_mode (end_bit - boundary, boundary, 0, 0,
+				 align, BITS_PER_WORD, volatilep, &rnmode2))
+	    return 0;
+
+	  r_split_load = true;
+	  parts = 2;
+	  if (lr_bitpos >= boundary)
+	    maybe_separate = first1 = true;
+	  else if (lr_bitpos + lr_bitsize <= boundary)
+	    maybe_separate = true;
+	}
+      else
+	r_split_load = false;
+
+      /* Find a type that can hold the entire right-hand operand.  */
+      rnbitsize = GET_MODE_BITSIZE (rnmode);
+      rnbitpos = first_bit & ~ (rnbitsize - 1);
+      if (r_split_load)
+	rnbitsize += GET_MODE_BITSIZE (rnmode2);
+      rntype = build_nonstandard_integer_type (rnbitsize, 1);
+      if (!rntype)
+	return  0;
+      rnprec = TYPE_PRECISION (rntype);
+      xlr_bitpos = lr_bitpos - rnbitpos, xrr_bitpos = rr_bitpos - rnbitpos;
+
+      /* Adjust for reversed endianness.  */
+      if (lr_reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+	{
+	  xlr_bitpos = rnbitsize - xlr_bitpos - lr_bitsize;
+	  xrr_bitpos = rnbitsize - xrr_bitpos - rr_bitsize;
+	}
+
+      /* Adjust the masks to match the combined type, and combine them.  */
+      wide_int lr_mask, rr_mask;
+      if (lr_and_mask.get_precision ())
+	lr_mask = wi::lshift (wide_int::from (lr_and_mask, rnprec, UNSIGNED),
+			  xlr_bitpos);
+      else
+	lr_mask = wi::shifted_mask (xlr_bitpos, lr_bitsize, false, rnprec);
+      if (rl_and_mask.get_precision ())
+	rr_mask = wi::lshift (wide_int::from (rr_and_mask, rnprec, UNSIGNED),
+			      xrr_bitpos);
+      else
+	rr_mask = wi::shifted_mask (xrr_bitpos, rr_bitsize, false, rnprec);
+      r_mask = lr_mask | rr_mask;
+
+      /* Load the right-hand operand of the combined compare.  */
+      toshift[1][0] = MIN (xlr_bitpos, xrr_bitpos);
+      shifted[1][0] = 0;
+
+      if (!r_split_load)
+	{
+	  bitpos[1][0] = rnbitpos;
+	  bitsiz[1][0] = rnbitsize;
+	  ld_arg[1][0] = make_bit_field_load (ll_loc[3], lr_inner, lr_arg,
+					      rntype, rnbitsize, rnbitpos,
+					      lr_unsignedp || rr_unsignedp,
+					      lr_reversep, lr_load);
+	}
+
+      /* ... and the second part of the right-hand operand if needed.  */
+      if (parts > 1)
+	{
+	  if (r_split_load)
+	    {
+	      gimple *point[2];
+	      point[0] = lr_load;
+	      point[1] = rr_load;
+	      build_split_load (ld_arg[1], bitpos[1], bitsiz[1], toshift[1],
+				shifted[1], rl_loc[3], lr_inner, lr_arg,
+				rnmode, rnmode2, rnbitpos, lr_reversep, point);
+	    }
+	  else
+	    reuse_split_load (ld_arg[1], bitpos[1], bitsiz[1], toshift[1],
+			      shifted[1], xmask[1],
+			      lnbitpos + GET_MODE_BITSIZE (lnmode)
+			      - ll_bitpos + lr_bitpos, lr_reversep);
+	}
+    }
+
+  /* Now issue the loads for the left-hand combined operand/s.  */
+  wide_int l_mask = ll_mask | rl_mask;
+  toshift[0][0] = MIN (xll_bitpos, xrl_bitpos);
+  shifted[0][0] = 0;
+
+  if (!l_split_load)
+    {
+      bitpos[0][0] = lnbitpos;
+      bitsiz[0][0] = lnbitsize;
+      ld_arg[0][0] = make_bit_field_load (ll_loc[3], ll_inner, ll_arg,
+					  lntype, lnbitsize, lnbitpos,
+					  ll_unsignedp || rl_unsignedp,
+					  ll_reversep, ll_load);
+    }
+
+  if (parts > 1)
+    {
+      if (l_split_load)
+	    {
+	      gimple *point[2];
+	      point[0] = ll_load;
+	      point[1] = rl_load;
+	      build_split_load (ld_arg[0], bitpos[0], bitsiz[0], toshift[0],
+				shifted[0], rl_loc[3], ll_inner, ll_arg,
+				lnmode, lnmode2, lnbitpos, ll_reversep, point);
+	    }
+      else
+	reuse_split_load (ld_arg[0], bitpos[0], bitsiz[0], toshift[0],
+			  shifted[0], xmask[0],
+			  rnbitpos + GET_MODE_BITSIZE (rnmode)
+			  - lr_bitpos + ll_bitpos, ll_reversep);
+    }
+
+  /* Compute the compares.  */
+  for (int i = 0; i < parts; i++)
+    {
+      tree op[2] = { ld_arg[0][i], ld_arg[1][i] };
+      wide_int mask[2] = { l_mask, r_mask };
+      location_t *locs[2] = { i ? rl_loc : ll_loc, i ? rr_loc : lr_loc };
+
+      /* Figure out the masks, and unshare the original operands.  */
+      for (int j = 0; j < 2; j++)
+	{
+	  unsigned prec = TYPE_PRECISION (TREE_TYPE (op[j]));
+	  op[j] = unshare_expr (op[j]);
+
+	  /* Mask out the bits belonging to the other part.  */
+	  if (xmask[j][i].get_precision ())
+	    mask[j] &= xmask[j][i];
+
+	  if (shifted[j][i])
+	    {
+	      wide_int shift = wide_int::from (shifted[j][i], prec, UNSIGNED);
+	      mask[j] = wi::lrshift (mask[j], shift);
+	    }
+	  mask[j] = wide_int::from (mask[j], prec, UNSIGNED);
+	}
+
+      /* Line up the operands for a compare.  */
+      HOST_WIDE_INT shift = (toshift[0][i] - toshift[1][i]);
+
+      if (shift)
+	{
+	  int j;
+	  if (shift > 0)
+	    j = 0;
+	  else
+	    {
+	      j = 1;
+	      shift = -shift;
+	    }
+
+	  tree shiftsz = bitsize_int (shift);
+	  op[j] = fold_build2_loc (locs[j][1], RSHIFT_EXPR, TREE_TYPE (op[j]),
+				   op[j], shiftsz);
+	  mask[j] = wi::lrshift (mask[j], shift);
+	}
+
+      /* Convert to the smaller type before masking out unwanted
+	 bits.  */
+      tree type = TREE_TYPE (op[0]);
+      if (type != TREE_TYPE (op[1]))
+	{
+	  int j = (TYPE_PRECISION (type)
+		   < TYPE_PRECISION (TREE_TYPE (op[1])));
+	  if (!j)
+	    type = TREE_TYPE (op[1]);
+	  op[j] = fold_convert_loc (locs[j][0], type, op[j]);
+	  mask[j] = wide_int::from (mask[j], TYPE_PRECISION (type), UNSIGNED);
+	}
+
+      /* Apply masks.  */
+      for (int j = 0; j < 2; j++)
+	if (mask[j] != wi::mask (0, true, mask[j].get_precision ()))
+	  op[j] = build2_loc (locs[j][2], BIT_AND_EXPR, type,
+			      op[j], wide_int_to_tree (type, mask[j]));
+
+      cmp[i] = build2_loc (i ? rloc : lloc, wanted_code, truth_type,
+			   op[0], op[1]);
+    }
+
+  /* Reorder the compares if needed.  */
+  if (first1)
+    std::swap (cmp[0], cmp[1]);
+
+  /* Prepare to return the resulting compares.  Combine two parts if
+     needed.  */
+  if (parts == 1)
+    result = cmp[0];
+  else if (!separatep || !maybe_separate)
+    result = build2_loc (rloc, orig_code, truth_type, cmp[0], cmp[1]);
+  else
+    {
+      result = cmp[0];
+      *separatep = cmp[1];
+    }
+
+  return result;
+}
+
 /* Try to simplify the AND of two comparisons, specified by
    (OP1A CODE1 OP1B) and (OP2B CODE2 OP2B), respectively.
    If this can be simplified to a single expression (without requiring
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 0000000000000..1818e104437e1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-1.c
@@ -0,0 +1,64 @@ 
+/* { dg-do run } */
+/* { dg-options "-O -save-temps -fdump-tree-optimized" } */
+
+/* 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) {
+  /* Which words of    | vL | vB | */
+  /* are accessed by   |0123|0123| */
+  if (0 /* the tests?  |    |    | */
+      || vL.p != vc /* |*   |    | */
+      || vB.p != vc /* |    |*   | */
+      || vL.s != vc /* |   *|    | */
+      || vB.q != vc /* |    | *  | */
+      || vL.a != vi /* |^*  |    | */
+      || vB.b != vi /* |    | ^* | */
+      || vL.c != vi /* |  *^|    | */
+      || vB.c != vi /* |    |  ^*| */
+      || vL.b != vi /* | ^^ |    | */
+      || vL.q != vc /* | ^  |    | */
+      || vB.a != vi /* |    |^^  | */
+      || vB.r != vc /* |    |  ^ | */
+      || vB.s != vc /* |    |   ^| */
+      || vL.r != vc /* |  ^ |    | */
+      )
+    __builtin_abort ();
+}
+
+int main () {
+  f ();
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "BIT_FIELD_REF" 8 "optimized" } } */
+/* { dg-final { scan-assembler-not "cmpb" { target { i*86-*-* || x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "cmpl" 8 { target { i*86-*-* || x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "cmpw" 8 { target { powerpc*-*-* || rs6000-*-* } } } } */
diff --git a/gcc/testsuite/gcc.dg/field-merge-10.c b/gcc/testsuite/gcc.dg/field-merge-10.c
new file mode 100644
index 0000000000000..dca6febb75859
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-10.c
@@ -0,0 +1,36 @@ 
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+/* Check that we don't move loads across stores.  */
+
+struct s {
+  short a;
+  short b;
+} __attribute__ ((aligned (4)));
+
+struct s p[2] = {
+  { 42, 0xfe01 },
+  { 42, 0xfe10 }
+};
+
+void f (void) {
+  short a0 = p[0].a;
+  short b0 = p[0].b;
+  short a1 = p[1].a;
+  short b1 = p[1].b;
+  __builtin_memset (p, 0, sizeof (p));
+  asm ("" : "+m" (p));
+  if (0
+      || p[0].a != p[1].a
+      || p[0].b != p[1].b
+      )
+    __builtin_abort ();
+  if (a0 == a1)
+    if (b0 == b1)
+      __builtin_abort ();
+}
+
+int main () {
+  f ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/field-merge-11.c b/gcc/testsuite/gcc.dg/field-merge-11.c
new file mode 100644
index 0000000000000..fe627cddd7fdf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-11.c
@@ -0,0 +1,32 @@ 
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+/* Check that narrowing casts aren't ignored, and that same-field tests at
+   different widths aren't misoptimized.  */
+
+struct s {
+  short a;
+  unsigned short b;
+  int c;
+} __attribute__ ((aligned (4)));
+
+struct s p = { 42, (short)(0xef1 - 0x1000), 0x12345678 };
+
+void f (void) {
+  if (0
+      || (p.a & 0xcc) != 8
+      || p.a != 42
+      || (int)(signed char)p.b != (int)(signed char)(0xef1 - 0x1000)
+      || (unsigned)(unsigned char)p.b != (unsigned)(unsigned char)(0xef1 - 0x1000)
+      || (unsigned)p.b != (unsigned short)(0xef1 - 0x1000)
+      || (int)(short)p.b != (int)(0xef1 - 0x1000)
+      || (long)(unsigned char)(p.c >> 8) != (long)(unsigned char)0x123456
+      || p.c != 0x12345678
+      )
+    __builtin_abort ();
+}
+
+int main () {
+  f ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/field-merge-12.c b/gcc/testsuite/gcc.dg/field-merge-12.c
new file mode 100644
index 0000000000000..7056eb607e904
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-12.c
@@ -0,0 +1,33 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+/* Check that we don't crash when trying to handle masks that don't match the
+   width of the original type.  */
+
+struct s {
+  long long q;
+};
+
+struct s x1 = { 1 };
+struct s xm1 = { -1 };
+struct s x8 = { 8 };
+struct s x0 = { 0 };
+
+bool f(struct s *p)
+{
+  int q = (int)p->q;
+  return (q < 0) || (q & 7);
+}
+
+int main ()
+{
+  if (!f (&x1))
+    __builtin_abort ();
+  if (!f (&xm1))
+    __builtin_abort ();
+  if (f (&x8))
+    __builtin_abort ();
+  if (f (&x0))
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/field-merge-2.c b/gcc/testsuite/gcc.dg/field-merge-2.c
new file mode 100644
index 0000000000000..80c573b31ddc7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-2.c
@@ -0,0 +1,31 @@ 
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+struct TL {
+  unsigned short a;
+  unsigned short b;
+} __attribute__ ((packed, aligned (8)));
+
+struct TB {
+  unsigned char p;
+  unsigned short a;
+  unsigned short b;
+} __attribute__ ((packed, aligned (8)));
+
+#define vc 0xaa
+
+struct TL vL = { vc, vc };
+struct TB vB = { vc, vc, vc };
+
+void f (void) {
+  if (0
+      || vL.b != vB.b
+      || vL.a != vB.a
+      )
+    __builtin_abort ();
+}
+
+int main () {
+  f ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/field-merge-3.c b/gcc/testsuite/gcc.dg/field-merge-3.c
new file mode 100644
index 0000000000000..f26e8a96ea04f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-3.c
@@ -0,0 +1,36 @@ 
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+const int BIG_ENDIAN_P = (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__);
+
+struct T1 {
+  unsigned char p[2];
+  unsigned short a;
+  unsigned int z;
+} __attribute__((__aligned__(8)));
+
+struct T2 {
+  unsigned short p;
+  unsigned short a;
+  unsigned int z;
+} __attribute__((__aligned__(8)));
+
+#define vc 0xaa
+#define vi 0x12345678
+
+struct T1 v1 = { { vc + !BIG_ENDIAN_P, vc + BIG_ENDIAN_P }, vc, vi };
+struct T2 v2 = { (vc << 8) | (vc - 1), vc, vi };
+
+void f (void) {
+  if (0
+      || v1.p[!BIG_ENDIAN_P] != v2.p >> 8
+      || v1.a != v2.a
+      || ((v1.z ^ v2.z) & 0xff00ff00) != 0
+      || ((v1.z ^ v2.z) & 0x00ff00ff) != 0)
+    __builtin_abort ();
+}
+
+int main () {
+  f ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/field-merge-4.c b/gcc/testsuite/gcc.dg/field-merge-4.c
new file mode 100644
index 0000000000000..c629069e52b2c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-4.c
@@ -0,0 +1,40 @@ 
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+struct T1 {
+  unsigned int zn;
+  unsigned char p;
+  unsigned char qn;
+  unsigned short a;
+  unsigned int z;
+} __attribute__((__packed__, __aligned__(4)));
+
+struct T2 {
+  unsigned int zn;
+  unsigned char rn;
+  unsigned char p;
+  unsigned char qn;
+  unsigned short a;
+  unsigned int z;
+} __attribute__((__packed__, __aligned__(4)));
+
+#define vc 0xaa
+#define vs 0xccdd
+#define vi 0x12345678
+
+struct T1 v1 = { -1, vc, 1, vs, vi };
+struct T2 v2 = { -1, 0, vc, 1, vs, vi };
+
+void f (void) {
+  if (0
+      || v1.p != v2.p
+      || v1.a != v2.a
+      || v1.z != v2.z
+      )
+    __builtin_abort ();
+}
+
+int main () {
+  f ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/field-merge-5.c b/gcc/testsuite/gcc.dg/field-merge-5.c
new file mode 100644
index 0000000000000..1580b14bcc935
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-5.c
@@ -0,0 +1,40 @@ 
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+struct T1 {
+  unsigned int zn;
+  unsigned char p;
+  unsigned char qn;
+  unsigned short a;
+  unsigned int z;
+} __attribute__((__packed__, __aligned__(8)));
+
+struct T2 {
+  unsigned int zn;
+  unsigned char rn;
+  unsigned char p;
+  unsigned char qn;
+  unsigned short a;
+  unsigned int z;
+} __attribute__((__packed__, __aligned__(8)));
+
+#define vc 0xaa
+#define vs 0xccdd
+#define vi 0x12345678
+
+struct T1 v1 = { -1, vc, 1, vs, vi };
+struct T2 v2 = { -1, 0, vc, 1, vs, vi };
+
+void f (void) {
+  if (0
+      || v1.p != v2.p
+      || v1.a != v2.a
+      || v1.z != v2.z
+      )
+    __builtin_abort ();
+}
+
+int main () {
+  f ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/field-merge-6.c b/gcc/testsuite/gcc.dg/field-merge-6.c
new file mode 100644
index 0000000000000..7fd48a138d14a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-6.c
@@ -0,0 +1,26 @@ 
+/* { dg-do run } */
+/* { dg-options "-O" } */
+/* { dg-shouldfail } */
+
+/* Check that the third compare won't be pulled ahead of the second one and
+   prevent, which would prevent the NULL pointer dereference that should cause
+   the execution to fail.  */
+
+struct s {
+  char a, b;
+  int *p;
+};
+
+struct s a = { 0, 1, 0 };
+struct s b = { 0, 0, 0 };
+
+int f () {
+  return (a.a != b.a
+	  || *b.p != *a.p
+	  || a.b != b.b);
+}
+
+int main() {
+  f ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/field-merge-7.c b/gcc/testsuite/gcc.dg/field-merge-7.c
new file mode 100644
index 0000000000000..728a29b6fafa9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-7.c
@@ -0,0 +1,23 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O -fdump-tree-ifcombine-details" } */
+
+/* Check that the third compare won't be combined with the first one.  */
+
+struct s {
+  char a, b;
+  int p;
+};
+
+struct s a = { 0, 0, 0 };
+struct s b = { 0, 0, 0 };
+
+int f () {
+  return (a.a != b.a || (a.p != b.p && a.b != b.b));
+}
+
+int g () {
+  return (a.a == b.a && (a.p == b.p || a.b == b.b));
+}
+
+/* { dg-final { scan-tree-dump-not "optimizing" "ifcombine" } } */
+/* { dg-final { scan-tree-dump-not "BIT_FIELD_REF" "ifcombine" } } */
diff --git a/gcc/testsuite/gcc.dg/field-merge-8.c b/gcc/testsuite/gcc.dg/field-merge-8.c
new file mode 100644
index 0000000000000..ae270e10070e4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-8.c
@@ -0,0 +1,25 @@ 
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+/* Check that conversions are not thrown away.  */
+
+struct s {
+  unsigned char a;
+  unsigned short b;
+} __attribute__ ((aligned (4)));
+
+struct s p = { 42, 0xfe };
+struct s q = { 42, 0xfe | (2 << (__CHAR_BIT__ - 1)) };
+
+void f (void) {
+  if (0
+      || p.a != q.a
+      || (unsigned char)p.b != (unsigned char)q.b
+      )
+    __builtin_abort ();
+}
+
+int main () {
+  f ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/field-merge-9.c b/gcc/testsuite/gcc.dg/field-merge-9.c
new file mode 100644
index 0000000000000..b9e08d8fa37d2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-9.c
@@ -0,0 +1,38 @@ 
+/* { dg-do run } */
+/* { dg-options "-O -fdump-tree-ifcombine-details" } */
+
+/* Check that conversions and selections of similar bit ranges across different
+   types don't prevent combination.  */
+
+struct s1 {
+  unsigned char b[2];
+  unsigned char a;
+} __attribute__ ((aligned (4)));
+
+struct s2 {
+  unsigned short b;
+  unsigned char a;
+} __attribute__ ((aligned (4)));
+
+static const char le = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ? 1 : 0;
+
+struct s1 p = { { -!le , -le }, 42 };
+struct s2 q = { (le
+		 ? -2 << (__CHAR_BIT__ - 1)
+		 : -1 & ((1 << (__CHAR_BIT__ - 1) << 1) - 1)), 42 };
+
+void f (void) {
+  if (0
+      || p.a != q.a
+      || p.b[!le] != (unsigned char)q.b
+      || p.b[le] != (unsigned char)((q.b >> (__CHAR_BIT__ - 1)) >> 1)
+      )
+    __builtin_abort ();
+}
+
+int main () {
+  f ();
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "optimizing two comparisons" 2 "ifcombine" } } */
diff --git a/gcc/tree-ssa-ifcombine.cc b/gcc/tree-ssa-ifcombine.cc
index a87bf1210776f..de8db2be5572a 100644
--- a/gcc/tree-ssa-ifcombine.cc
+++ b/gcc/tree-ssa-ifcombine.cc
@@ -971,7 +971,19 @@  ifcombine_ifandif (basic_block inner_cond_bb, bool inner_inv,
 					    outer_cond_code,
 					    gimple_cond_lhs (outer_cond),
 					    gimple_cond_rhs (outer_cond),
-					    gimple_bb (outer_cond))))
+					    gimple_bb (outer_cond)))
+	  && !(t = (fold_truth_andor_for_ifcombine
+		    (TRUTH_ANDIF_EXPR, boolean_type_node,
+		     gimple_location (outer_cond),
+		     outer_cond_code,
+		     gimple_cond_lhs (outer_cond),
+		     gimple_cond_rhs (outer_cond),
+		     gimple_location (inner_cond),
+		     inner_cond_code,
+		     gimple_cond_lhs (inner_cond),
+		     gimple_cond_rhs (inner_cond),
+		     single_pred (inner_cond_bb) != outer_cond_bb
+		     ? &ts : 0))))
 	{
 	  /* Only combine conditions in this fallback case if the blocks are
 	     neighbors.  */