Index: fold-const.c
===================================================================
--- fold-const.c	(revision 188331)
+++ fold-const.c	(working copy)
@@ -2247,35 +2247,46 @@ compcode_to_comparison (enum comparison_
       gcc_unreachable ();
     }
 }
 
 /* Return a tree for the comparison which is the combination of
    doing the AND or OR (depending on CODE) of the two operations LCODE
-   and RCODE on the identical operands LL_ARG and LR_ARG.  Take into account
-   the possibility of trapping if the mode has NaNs, and return NULL_TREE
+   and RCODE on the identical operands LL_ARG and LR_ARG.  The bit 0
+   (resp. 1) of inv indicates, when set, that the result of the
+   operation LCODE needs to be inverted.   Take into account the
+   possibility of trapping if the mode has NaNs, and return NULL_TREE
    if this makes the transformation invalid.  */
 
 tree
 combine_comparisons (location_t loc,
 		     enum tree_code code, enum tree_code lcode,
 		     enum tree_code rcode, tree truth_type,
-		     tree ll_arg, tree lr_arg)
+		     tree ll_arg, tree lr_arg, int inv)
 {
   bool honor_nans = HONOR_NANS (TYPE_MODE (TREE_TYPE (ll_arg)));
   enum comparison_code lcompcode = comparison_to_compcode (lcode);
   enum comparison_code rcompcode = comparison_to_compcode (rcode);
+  int lcompcode2 = lcompcode;
+  int rcompcode2 = rcompcode;
   int compcode;
+  bool exchg = false;
+  tree ret;
+
+  if (inv & 1)
+    lcompcode2 = COMPCODE_TRUE - lcompcode2;
+  if (inv & 2)
+    rcompcode2 = COMPCODE_TRUE - rcompcode2;
 
   switch (code)
     {
     case TRUTH_AND_EXPR: case TRUTH_ANDIF_EXPR:
-      compcode = lcompcode & rcompcode;
+      compcode = lcompcode2 & rcompcode2;
       break;
 
     case TRUTH_OR_EXPR: case TRUTH_ORIF_EXPR:
-      compcode = lcompcode | rcompcode;
+      compcode = lcompcode2 | rcompcode2;
       break;
 
     default:
       return NULL_TREE;
     }
 
@@ -2318,25 +2329,38 @@ combine_comparisons (location_t loc,
 	if (rtrap && !ltrap
 	    && (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR))
 	  return NULL_TREE;
 
 	/* If we changed the conditions that cause a trap, we lose.  */
 	if ((ltrap || rtrap) != trap)
+	  {
+	    /* Try the inverse condition.  */
+	    compcode = COMPCODE_TRUE - compcode;
+	    exchg = true;
+	    trap = (compcode & COMPCODE_UNORD) == 0
+		   && (compcode != COMPCODE_EQ)
+		   && (compcode != COMPCODE_ORD);
+	  }
+	if ((ltrap || rtrap) != trap)
 	  return NULL_TREE;
       }
 
+  /* The inversion can't lead to a constant.  */
   if (compcode == COMPCODE_TRUE)
     return constant_boolean_node (true, truth_type);
   else if (compcode == COMPCODE_FALSE)
     return constant_boolean_node (false, truth_type);
   else
     {
       enum tree_code tcode;
 
       tcode = compcode_to_comparison ((enum comparison_code) compcode);
-      return fold_build2_loc (loc, tcode, truth_type, ll_arg, lr_arg);
+      ret = fold_build2_loc (loc, tcode, truth_type, ll_arg, lr_arg);
+      if (exchg)
+	ret = fold_build1_loc (loc, TRUTH_NOT_EXPR, truth_type, ret);
+      return ret;
     }
 }
 
 /* Return nonzero if two operands (typically of the same tree node)
    are necessarily equal.  If either argument has side-effects this
    function returns zero.  FLAGS modifies behavior as follows:
@@ -5115,22 +5139,22 @@ fold_truth_andor_1 (location_t loc, enum
       && simple_operand_p (lr_arg))
     {
       if (operand_equal_p (ll_arg, rl_arg, 0)
           && operand_equal_p (lr_arg, rr_arg, 0))
 	{
           result = combine_comparisons (loc, code, lcode, rcode,
-					truth_type, ll_arg, lr_arg);
+					truth_type, ll_arg, lr_arg, 0);
 	  if (result)
 	    return result;
 	}
       else if (operand_equal_p (ll_arg, rr_arg, 0)
                && operand_equal_p (lr_arg, rl_arg, 0))
 	{
           result = combine_comparisons (loc, code, lcode,
 					swap_tree_comparison (rcode),
-					truth_type, ll_arg, lr_arg);
+					truth_type, ll_arg, lr_arg, 0);
 	  if (result)
 	    return result;
 	}
     }
 
   code = ((code == TRUTH_AND_EXPR || code == TRUTH_ANDIF_EXPR)
Index: gimple.h
===================================================================
--- gimple.h	(revision 188331)
+++ gimple.h	(working copy)
@@ -5309,12 +5309,12 @@ void gimplify_and_update_call_from_tree
 tree gimple_fold_builtin (gimple);
 bool fold_stmt (gimple_stmt_iterator *);
 bool fold_stmt_inplace (gimple_stmt_iterator *);
 tree get_symbol_constant_value (tree);
 tree canonicalize_constructor_val (tree, tree);
 extern tree maybe_fold_and_comparisons (enum tree_code, tree, tree, 
-					enum tree_code, tree, tree);
+					enum tree_code, tree, tree, int);
 extern tree maybe_fold_or_comparisons (enum tree_code, tree, tree,
-				       enum tree_code, tree, tree);
+				       enum tree_code, tree, tree, int);
 
 bool gimple_val_nonnegative_real_p (tree);
 #endif  /* GCC_GIMPLE_H */
Index: tree.h
===================================================================
--- tree.h	(revision 188331)
+++ tree.h	(working copy)
@@ -5366,13 +5366,13 @@ extern bool tree_invalid_nonnegative_war
 extern bool tree_call_nonnegative_warnv_p (tree, tree, tree, tree, bool *);
 
 extern bool tree_expr_nonzero_warnv_p (tree, bool *);
 
 extern bool fold_real_zero_addition_p (const_tree, const_tree, int);
 extern tree combine_comparisons (location_t, enum tree_code, enum tree_code,
-				 enum tree_code, tree, tree, tree);
+				 enum tree_code, tree, tree, tree, int);
 extern void debug_fold_checksum (const_tree);
 
 /* Return nonzero if CODE is a tree code that represents a truth value.  */
 static inline bool
 truth_value_p (enum tree_code code)
 {
Index: tree-ssa-ifcombine.c
===================================================================
--- tree-ssa-ifcombine.c	(revision 188331)
+++ tree-ssa-ifcombine.c	(working copy)
@@ -372,13 +372,13 @@ ifcombine_ifandif (basic_block inner_con
 
       if (!(t = maybe_fold_and_comparisons (gimple_cond_code (inner_cond),
 					    gimple_cond_lhs (inner_cond),
 					    gimple_cond_rhs (inner_cond),
 					    gimple_cond_code (outer_cond),
 					    gimple_cond_lhs (outer_cond),
-					    gimple_cond_rhs (outer_cond))))
+					    gimple_cond_rhs (outer_cond), 0)))
 	return false;
       t = canonicalize_cond_expr_cond (t);
       if (!t)
 	return false;
       gimple_cond_set_condition_from_tree (inner_cond, t);
       update_stmt (inner_cond);
@@ -520,13 +520,13 @@ ifcombine_iforif (basic_block inner_cond
 
       if (!(t = maybe_fold_or_comparisons (gimple_cond_code (inner_cond),
 					   gimple_cond_lhs (inner_cond),
 					   gimple_cond_rhs (inner_cond),
 					   gimple_cond_code (outer_cond),
 					   gimple_cond_lhs (outer_cond),
-					   gimple_cond_rhs (outer_cond))))
+					   gimple_cond_rhs (outer_cond), 0)))
 	return false;
       t = canonicalize_cond_expr_cond (t);
       if (!t)
 	return false;
       gimple_cond_set_condition_from_tree (inner_cond, t);
       update_stmt (inner_cond);
@@ -545,12 +545,156 @@ ifcombine_iforif (basic_block inner_cond
       return true;
     }
 
   return false;
 }
 
+/* If-convert on a !a||b pattern.  The inner if is specified by its
+   INNER_COND_BB, the outer by OUTER_COND_BB.  Returns true if the
+   edges to the common outer else basic-block / inner then basic-block
+   were merged.  */
+
+static bool
+ifcombine_ifnotorif (basic_block inner_cond_bb, basic_block outer_cond_bb)
+{
+  gimple inner_cond, outer_cond;
+
+  inner_cond = last_stmt (inner_cond_bb);
+  if (!inner_cond
+      || gimple_code (inner_cond) != GIMPLE_COND)
+    return false;
+
+  outer_cond = last_stmt (outer_cond_bb);
+  if (!outer_cond
+      || gimple_code (outer_cond) != GIMPLE_COND)
+    return false;
+
+  /* See if we have two comparisons that we can merge into one.  */
+  if (TREE_CODE_CLASS (gimple_cond_code (inner_cond)) == tcc_comparison
+	   && TREE_CODE_CLASS (gimple_cond_code (outer_cond)) == tcc_comparison)
+    {
+      tree t;
+      bool inv = false;
+
+      if (!(t = maybe_fold_or_comparisons (gimple_cond_code (inner_cond),
+					   gimple_cond_lhs (inner_cond),
+					   gimple_cond_rhs (inner_cond),
+					   gimple_cond_code (outer_cond),
+					   gimple_cond_lhs (outer_cond),
+					   gimple_cond_rhs (outer_cond), 2)))
+	return false;
+
+      if (TREE_CODE (t) == TRUTH_NOT_EXPR)
+	{
+	  inv = true;
+	  t = TREE_OPERAND (t, 0);
+	}
+
+      t = canonicalize_cond_expr_cond (t);
+      if (!t)
+	return false;
+
+      if (inv)
+	{
+	  edge t = EDGE_SUCC (inner_cond_bb, 0);
+	  edge e = EDGE_SUCC (inner_cond_bb, 1);
+	  t->flags ^= (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
+	  e->flags ^= (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
+	}
+      gimple_cond_set_condition_from_tree (inner_cond, t);
+      update_stmt (inner_cond);
+
+      /* Leave CFG optimization to cfg_cleanup.  */
+      gimple_cond_set_condition_from_tree (outer_cond, boolean_true_node);
+      update_stmt (outer_cond);
+
+      if (dump_file)
+	{
+	  fprintf (dump_file, "optimizing two comparisons to ");
+	  print_generic_expr (dump_file, t, 0);
+	  fprintf (dump_file, "\n");
+	}
+
+      return true;
+    }
+
+  return false;
+}
+
+/* If-convert on a !a&&b pattern.  The inner if is specified by its
+   INNER_COND_BB, the outer by OUTER_COND_BB.  Returns true if the
+   edges to the common outer then basic-block / inner else basic-block
+   were merged.  */
+
+static bool
+ifcombine_ifnotandif (basic_block inner_cond_bb, basic_block outer_cond_bb)
+{
+  gimple inner_cond, outer_cond;
+
+  inner_cond = last_stmt (inner_cond_bb);
+  if (!inner_cond
+      || gimple_code (inner_cond) != GIMPLE_COND)
+    return false;
+
+  outer_cond = last_stmt (outer_cond_bb);
+  if (!outer_cond
+      || gimple_code (outer_cond) != GIMPLE_COND)
+    return false;
+
+  /* See if we have two comparisons that we can merge into one.  */
+  if (TREE_CODE_CLASS (gimple_cond_code (inner_cond)) == tcc_comparison
+	   && TREE_CODE_CLASS (gimple_cond_code (outer_cond)) == tcc_comparison)
+    {
+      tree t;
+      bool inv = false;
+
+      if (!(t = maybe_fold_and_comparisons (gimple_cond_code (inner_cond),
+					    gimple_cond_lhs (inner_cond),
+					    gimple_cond_rhs (inner_cond),
+					    gimple_cond_code (outer_cond),
+					    gimple_cond_lhs (outer_cond),
+					    gimple_cond_rhs (outer_cond), 2)))
+	return false;
+
+      if (TREE_CODE (t) == TRUTH_NOT_EXPR)
+	{
+	  inv = true;
+	  t = TREE_OPERAND (t, 0);
+	}
+
+      t = canonicalize_cond_expr_cond (t);
+      if (!t)
+	return false;
+
+      if (inv)
+	{
+	  edge t = EDGE_SUCC (inner_cond_bb, 0);
+	  edge e = EDGE_SUCC (inner_cond_bb, 1);
+	  t->flags ^= (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
+	  e->flags ^= (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
+	}
+      gimple_cond_set_condition_from_tree (inner_cond, t);
+      update_stmt (inner_cond);
+
+      /* Leave CFG optimization to cfg_cleanup.  */
+      gimple_cond_set_condition_from_tree (outer_cond, boolean_false_node);
+      update_stmt (outer_cond);
+
+      if (dump_file)
+	{
+	  fprintf (dump_file, "optimizing two comparisons to ");
+	  print_generic_expr (dump_file, t, 0);
+	  fprintf (dump_file, "\n");
+	}
+
+      return true;
+    }
+
+  return false;
+}
+
 /* Recognize a CFG pattern and dispatch to the appropriate
    if-conversion helper.  We start with BB as the innermost
    worker basic-block.  Returns true if a transformation was done.  */
 
 static bool
 tree_ssa_ifcombine_bb (basic_block inner_cond_bb)
@@ -607,12 +751,52 @@ tree_ssa_ifcombine_bb (basic_block inner
 		 if (q) goto then_bb; else goto ...;
 	       <then_bb>
 		 ...
 	   */
 	  return ifcombine_iforif (inner_cond_bb, outer_cond_bb);
 	}
+
+      /* The !|| form is characterized by an outer else_bb that
+	 matches the inner else_bb, with the two edges leading to it
+	 mergable.  The latter is guaranteed by matching PHI arguments
+	 in the else_bb and the inner cond_bb having no side-effects.  */
+      if (recognize_if_then_else (outer_cond_bb, &inner_cond_bb, &then_bb)
+	  && same_phi_args_p (outer_cond_bb, inner_cond_bb, then_bb)
+	  && bb_no_side_effects_p (inner_cond_bb))
+	{
+	  /* We have
+	       <outer_cond_bb>
+		 if (q) goto inner_cond_bb; else goto then_bb;
+	       <inner_cond_bb>
+		 if (p) goto then_bb; else goto ...;
+		 ...
+	       <then_bb>
+		 ...
+	   */
+	  return ifcombine_ifnotorif (inner_cond_bb, outer_cond_bb);
+	}
+
+      /* The !&& form is characterized by an outer then_bb that
+	 matches the inner else_bb, with the two edges leading to it
+	 mergable.  The latter is guaranteed by matching PHI arguments
+	 in the else_bb and the inner cond_bb having no side-effects.  */
+      if (recognize_if_then_else (outer_cond_bb, &else_bb, &inner_cond_bb)
+	  && same_phi_args_p (outer_cond_bb, inner_cond_bb, else_bb)
+	  && bb_no_side_effects_p (inner_cond_bb))
+	{
+	  /* We have
+	       <outer_cond_bb>
+		 if (q) goto else_bb; else goto inner_cond_bb;
+	       <inner_cond_bb>
+		 if (p) goto ...; else goto else_bb;
+		 ...
+	       <else_bb>
+		 ...
+	   */
+	  return ifcombine_ifnotandif (inner_cond_bb, outer_cond_bb);
+	}
     }
 
   return false;
 }
 
 /* Main entry for the tree if-conversion pass.  */
Index: tree-ssa-reassoc.c
===================================================================
--- tree-ssa-reassoc.c	(revision 188331)
+++ tree-ssa-reassoc.c	(working copy)
@@ -1539,17 +1539,17 @@ eliminate_redundant_comparison (enum tre
 
       /* If we got here, we have a match.  See if we can combine the
 	 two comparisons.  */
       if (opcode == BIT_IOR_EXPR)
 	t = maybe_fold_or_comparisons (lcode, op1, op2,
 				       rcode, gimple_assign_rhs1 (def2),
-				       gimple_assign_rhs2 (def2));
+				       gimple_assign_rhs2 (def2), 0);
       else
 	t = maybe_fold_and_comparisons (lcode, op1, op2,
 					rcode, gimple_assign_rhs1 (def2),
-					gimple_assign_rhs2 (def2));
+					gimple_assign_rhs2 (def2), 0);
       if (!t)
 	continue;
 
       /* maybe_fold_and_comparisons and maybe_fold_or_comparisons
 	 always give us a boolean_type_node value back.  If the original
 	 BIT_AND_EXPR or BIT_IOR_EXPR was of a wider integer type,
Index: gimple-fold.c
===================================================================
--- gimple-fold.c	(revision 188331)
+++ gimple-fold.c	(working copy)
@@ -1470,29 +1470,34 @@ same_bool_result_p (const_tree op1, cons
 }
 
 /* Forward declarations for some mutually recursive functions.  */
 
 static tree
 and_comparisons_1 (enum tree_code code1, tree op1a, tree op1b,
-		   enum tree_code code2, tree op2a, tree op2b);
+		   enum tree_code code2, tree op2a, tree op2b, int inv);
 static tree
 and_var_with_comparison (tree var, bool invert,
 			 enum tree_code code2, tree op2a, tree op2b);
 static tree
 and_var_with_comparison_1 (gimple stmt, 
 			   enum tree_code code2, tree op2a, tree op2b);
 static tree
 or_comparisons_1 (enum tree_code code1, tree op1a, tree op1b,
-		  enum tree_code code2, tree op2a, tree op2b);
+		  enum tree_code code2, tree op2a, tree op2b, int inv);
 static tree
 or_var_with_comparison (tree var, bool invert,
 			enum tree_code code2, tree op2a, tree op2b);
 static tree
 or_var_with_comparison_1 (gimple stmt, 
 			  enum tree_code code2, tree op2a, tree op2b);
 
+static int swap12 (int flags)
+{
+  return ((flags & 1) << 1) | (flags >> 1);
+}
+
 /* Helper function for and_comparisons_1:  try to simplify the AND of the
    ssa variable VAR with the comparison specified by (OP2A CODE2 OP2B).
    If INVERT is true, invert the value of the VAR before doing the AND.
    Return NULL_EXPR if we can't simplify this to a single expression.  */
 
 static tree
@@ -1556,13 +1561,14 @@ and_var_with_comparison_1 (gimple stmt,
     {
       tree t = and_comparisons_1 (innercode,
 				  gimple_assign_rhs1 (stmt),
 				  gimple_assign_rhs2 (stmt),
 				  code2,
 				  op2a,
-				  op2b);
+				  op2b,
+				  0);
       if (t)
 	return t;
     }
 
   /* If the definition is an AND or OR expression, we may be able to
      simplify by reassociating.  */
@@ -1601,13 +1607,13 @@ and_var_with_comparison_1 (gimple stmt,
       if (TREE_CODE (inner1) == SSA_NAME
 	  && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner1))
 	  && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison
 	  && (t = maybe_fold_and_comparisons (gimple_assign_rhs_code (s),
 					      gimple_assign_rhs1 (s),
 					      gimple_assign_rhs2 (s),
-					      code2, op2a, op2b)))
+					      code2, op2a, op2b, 0)))
 	{
 	  /* Handle the AND case, where we are reassociating:
 	     (inner1 AND inner2) AND (op2a code2 op2b)
 	     => (t AND inner2)
 	     If the partial result t is a constant, we win.  Otherwise
 	     continue on to try reassociating with the other inner test.  */
@@ -1633,13 +1639,13 @@ and_var_with_comparison_1 (gimple stmt,
       if (TREE_CODE (inner2) == SSA_NAME
 	  && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner2))
 	  && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison
 	  && (t = maybe_fold_and_comparisons (gimple_assign_rhs_code (s),
 					      gimple_assign_rhs1 (s),
 					      gimple_assign_rhs2 (s),
-					      code2, op2a, op2b)))
+					      code2, op2a, op2b, 0)))
 	{
 	  /* Handle the AND case, where we are reassociating:
 	     (inner1 AND inner2) AND (op2a code2 op2b)
 	     => (inner1 AND t)  */
 	  if (is_and)
 	    {
@@ -1683,43 +1689,50 @@ and_var_with_comparison_1 (gimple stmt,
 
 /* Try to simplify the AND of two comparisons defined by
    (OP1A CODE1 OP1B) and (OP2A CODE2 OP2B), respectively.
    If this can be done without constructing an intermediate value,
    return the resulting tree; otherwise NULL_TREE is returned.
    This function is deliberately asymmetric as it recurses on SSA_DEFs
-   in the first comparison but not the second.  */
+   in the first comparison but not the second.
+   If inv & 1, the first test is actually !(OP1A CODE1 OP1B).
+   If inv & 2, the second test is actually !(OP2A CODE2 OP2B).  */
 
 static tree
 and_comparisons_1 (enum tree_code code1, tree op1a, tree op1b,
-		   enum tree_code code2, tree op2a, tree op2b)
+		   enum tree_code code2, tree op2a, tree op2b, int inv)
 {
   /* First check for ((x CODE1 y) AND (x CODE2 y)).  */
   if (operand_equal_p (op1a, op2a, 0)
       && operand_equal_p (op1b, op2b, 0))
     {
       /* Result will be either NULL_TREE, or a combined comparison.  */
       tree t = combine_comparisons (UNKNOWN_LOCATION,
 				    TRUTH_ANDIF_EXPR, code1, code2,
-				    boolean_type_node, op1a, op1b);
+				    boolean_type_node, op1a, op1b, inv);
       if (t)
 	return t;
     }
 
   /* Likewise the swapped case of the above.  */
   if (operand_equal_p (op1a, op2b, 0)
       && operand_equal_p (op1b, op2a, 0))
     {
       /* Result will be either NULL_TREE, or a combined comparison.  */
       tree t = combine_comparisons (UNKNOWN_LOCATION,
 				    TRUTH_ANDIF_EXPR, code1,
 				    swap_tree_comparison (code2),
-				    boolean_type_node, op1a, op1b);
+				    boolean_type_node, op1a, op1b, inv);
       if (t)
 	return t;
     }
 
+  if (inv & 1) code1 = invert_tree_comparison (code1,
+			 HONOR_NANS (TYPE_MODE (TREE_TYPE (op1a))));
+  if (inv & 2) code2 = invert_tree_comparison (code2,
+			 HONOR_NANS (TYPE_MODE (TREE_TYPE (op2a))));
+
   /* If both comparisons are of the same value against constants, we might
      be able to merge them.  */
   if (operand_equal_p (op1a, op2a, 0)
       && TREE_CODE (op1b) == INTEGER_CST
       && TREE_CODE (op2b) == INTEGER_CST)
     {
@@ -1935,23 +1948,26 @@ and_comparisons_1 (enum tree_code code1,
 
 /* 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
    introducing more SSA variables to hold intermediate values),
    return the resulting tree.  Otherwise return NULL_TREE.
-   If the result expression is non-null, it has boolean type.  */
+   If the result expression is non-null, it has boolean type.
+   If inv & 1, the first test is actually !(OP1A CODE1 OP1B).
+   If inv & 2, the second test is actually !(OP2A CODE2 OP2B).  */
 
 tree
 maybe_fold_and_comparisons (enum tree_code code1, tree op1a, tree op1b,
-			    enum tree_code code2, tree op2a, tree op2b)
+			    enum tree_code code2, tree op2a, tree op2b, int inv)
 {
-  tree t = and_comparisons_1 (code1, op1a, op1b, code2, op2a, op2b);
+  tree t = and_comparisons_1 (code1, op1a, op1b, code2, op2a, op2b, inv);
   if (t)
     return t;
   else
-    return and_comparisons_1 (code2, op2a, op2b, code1, op1a, op1b);
+    return and_comparisons_1 (code2, op2a, op2b, code1, op1a, op1b,
+			      swap12 (inv));
 }
 
 /* Helper function for or_comparisons_1:  try to simplify the OR of the
    ssa variable VAR with the comparison specified by (OP2A CODE2 OP2B).
    If INVERT is true, invert the value of VAR before doing the OR.
    Return NULL_EXPR if we can't simplify this to a single expression.  */
@@ -2017,13 +2033,14 @@ or_var_with_comparison_1 (gimple stmt,
     {
       tree t = or_comparisons_1 (innercode,
 				 gimple_assign_rhs1 (stmt),
 				 gimple_assign_rhs2 (stmt),
 				 code2,
 				 op2a,
-				 op2b);
+				 op2b,
+				 0);
       if (t)
 	return t;
     }
   
   /* If the definition is an AND or OR expression, we may be able to
      simplify by reassociating.  */
@@ -2062,13 +2079,13 @@ or_var_with_comparison_1 (gimple stmt,
       if (TREE_CODE (inner1) == SSA_NAME
 	  && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner1))
 	  && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison
 	  && (t = maybe_fold_or_comparisons (gimple_assign_rhs_code (s),
 					     gimple_assign_rhs1 (s),
 					     gimple_assign_rhs2 (s),
-					     code2, op2a, op2b)))
+					     code2, op2a, op2b, 0)))
 	{
 	  /* Handle the OR case, where we are reassociating:
 	     (inner1 OR inner2) OR (op2a code2 op2b)
 	     => (t OR inner2)
 	     If the partial result t is a constant, we win.  Otherwise
 	     continue on to try reassociating with the other inner test.  */
@@ -2094,13 +2111,13 @@ or_var_with_comparison_1 (gimple stmt,
       if (TREE_CODE (inner2) == SSA_NAME
 	  && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner2))
 	  && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison
 	  && (t = maybe_fold_or_comparisons (gimple_assign_rhs_code (s),
 					     gimple_assign_rhs1 (s),
 					     gimple_assign_rhs2 (s),
-					     code2, op2a, op2b)))
+					     code2, op2a, op2b, 0)))
 	{
 	  /* Handle the OR case, where we are reassociating:
 	     (inner1 OR inner2) OR (op2a code2 op2b)
 	     => (inner1 OR t)
 	     => (t OR partial)  */
 	  if (is_or)
@@ -2145,43 +2162,50 @@ or_var_with_comparison_1 (gimple stmt,
 
 /* Try to simplify the OR of two comparisons defined by
    (OP1A CODE1 OP1B) and (OP2A CODE2 OP2B), respectively.
    If this can be done without constructing an intermediate value,
    return the resulting tree; otherwise NULL_TREE is returned.
    This function is deliberately asymmetric as it recurses on SSA_DEFs
-   in the first comparison but not the second.  */
+   in the first comparison but not the second.
+   If inv & 1, the first test is actually !(OP1A CODE1 OP1B).
+   If inv & 2, the second test is actually !(OP2A CODE2 OP2B).  */
 
 static tree
 or_comparisons_1 (enum tree_code code1, tree op1a, tree op1b,
-		  enum tree_code code2, tree op2a, tree op2b)
+		  enum tree_code code2, tree op2a, tree op2b, int inv)
 {
   /* First check for ((x CODE1 y) OR (x CODE2 y)).  */
   if (operand_equal_p (op1a, op2a, 0)
       && operand_equal_p (op1b, op2b, 0))
     {
       /* Result will be either NULL_TREE, or a combined comparison.  */
       tree t = combine_comparisons (UNKNOWN_LOCATION,
 				    TRUTH_ORIF_EXPR, code1, code2,
-				    boolean_type_node, op1a, op1b);
+				    boolean_type_node, op1a, op1b, inv);
       if (t)
 	return t;
     }
 
   /* Likewise the swapped case of the above.  */
   if (operand_equal_p (op1a, op2b, 0)
       && operand_equal_p (op1b, op2a, 0))
     {
       /* Result will be either NULL_TREE, or a combined comparison.  */
       tree t = combine_comparisons (UNKNOWN_LOCATION,
 				    TRUTH_ORIF_EXPR, code1,
 				    swap_tree_comparison (code2),
-				    boolean_type_node, op1a, op1b);
+				    boolean_type_node, op1a, op1b, inv);
       if (t)
 	return t;
     }
 
+  if (inv & 1) code1 = invert_tree_comparison (code1,
+			 HONOR_NANS (TYPE_MODE (TREE_TYPE (op1a))));
+  if (inv & 2) code2 = invert_tree_comparison (code2,
+			 HONOR_NANS (TYPE_MODE (TREE_TYPE (op2a))));
+
   /* If both comparisons are of the same value against constants, we might
      be able to merge them.  */
   if (operand_equal_p (op1a, op2a, 0)
       && TREE_CODE (op1b) == INTEGER_CST
       && TREE_CODE (op2b) == INTEGER_CST)
     {
@@ -2397,23 +2421,26 @@ or_comparisons_1 (enum tree_code code1,
 
 /* Try to simplify the OR of two comparisons, specified by
    (OP1A CODE1 OP1B) and (OP2B CODE2 OP2B), respectively.
    If this can be simplified to a single expression (without requiring
    introducing more SSA variables to hold intermediate values),
    return the resulting tree.  Otherwise return NULL_TREE.
-   If the result expression is non-null, it has boolean type.  */
+   If the result expression is non-null, it has boolean type.
+   If inv & 1, the first test is actually !(OP1A CODE1 OP1B).
+   If inv & 2, the second test is actually !(OP2A CODE2 OP2B).  */
 
 tree
 maybe_fold_or_comparisons (enum tree_code code1, tree op1a, tree op1b,
-			   enum tree_code code2, tree op2a, tree op2b)
+			   enum tree_code code2, tree op2a, tree op2b, int inv)
 {
-  tree t = or_comparisons_1 (code1, op1a, op1b, code2, op2a, op2b);
+  tree t = or_comparisons_1 (code1, op1a, op1b, code2, op2a, op2b, inv);
   if (t)
     return t;
   else
-    return or_comparisons_1 (code2, op2a, op2b, code1, op1a, op1b);
+    return or_comparisons_1 (code2, op2a, op2b, code1, op1a, op1b,
+			     swap12 (inv));
 }
 
 
 /* Fold STMT to a constant using VALUEIZE to valueize SSA names.
 
    Either NULL_TREE, a simplified but non-constant or a constant
Index: tree-if-conv.c
===================================================================
--- tree-if-conv.c	(revision 188331)
+++ tree-if-conv.c	(working copy)
@@ -315,13 +315,13 @@ fold_or_predicates (location_t loc, tree
   enum tree_code code1 = parse_predicate (c1, &op1a, &op1b);
   enum tree_code code2 = parse_predicate (c2, &op2a, &op2b);
 
   if (code1 != ERROR_MARK && code2 != ERROR_MARK)
     {
       tree t = maybe_fold_or_comparisons (code1, op1a, op1b,
-					  code2, op2a, op2b);
+					  code2, op2a, op2b, 0);
       if (t)
 	return t;
     }
 
   return fold_build2_loc (loc, TRUTH_OR_EXPR, boolean_type_node, c1, c2);
 }
Index: testsuite/gcc.dg/tree-ssa/ssa-ifcombine-8.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/ssa-ifcombine-8.c	(revision 0)
+++ testsuite/gcc.dg/tree-ssa/ssa-ifcombine-8.c	(revision 0)
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O -ftrapping-math -fdump-tree-ifcombine" } */
+
+double test1 (double i, double j)
+{
+  if (i >= j)
+    if (i <= j)
+      goto plif;
+    else
+      goto plouf;
+  else
+    goto plif;
+
+plif:
+  return 0;
+plouf:
+  return -1;
+}
+
+/* The above should be optimized to a i > j test by ifcombine,
+   which also switches plif and plouf.  */
+
+/* { dg-final { scan-tree-dump " > " "ifcombine" } } */
+/* { dg-final { cleanup-tree-dump "ifcombine" } } */

Property changes on: testsuite/gcc.dg/tree-ssa/ssa-ifcombine-8.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision URL
Added: svn:eol-style
   + native

Index: testsuite/gcc.dg/tree-ssa/ssa-ifcombine-9.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/ssa-ifcombine-9.c	(revision 0)
+++ testsuite/gcc.dg/tree-ssa/ssa-ifcombine-9.c	(revision 0)
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-ifcombine" } */
+
+void f ();
+enum Sign { NEG=-1, ZERO, POS };
+
+static inline enum Sign sign (double x)
+{
+  if (x > 0) return POS;
+  if (x < 0) return NEG;
+  return ZERO;
+}
+void g (double x)
+{
+  if (sign (x) == NEG) f();
+}
+
+/* The above should be optimized to x < 0 by ifcombine.  */
+
+/* { dg-final { scan-tree-dump "optimizing.* < " "ifcombine" } } */
+/* { dg-final { cleanup-tree-dump "ifcombine" } } */
