public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Bugfix: Additional parameter for canonicalize comparison
@ 2012-12-12 11:23 Andreas Krebbel
  2012-12-19  9:53 ` [PING] " Andreas Krebbel
  0 siblings, 1 reply; 17+ messages in thread
From: Andreas Krebbel @ 2012-12-12 11:23 UTC (permalink / raw)
  To: rearnsha; +Cc: gcc-patches

Hi Richard,

is the attached patch ok for ARM?

Bye,

-Andreas-

2012-12-12  Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>

	* target.def: Define canonicalize_comparison hook.
	* targhooks.h (default_canonicalize_comparison): New prototype.
	* targhooks.c (default_canonicalize_comparison): New function.
	* doc/tm.texi: Add documentation for the new target hook.
	* doc/tm.texi.in: Likewise.
	* combine.c (try_combine): Adjust to use the target hook.
	* config/alpha/alpha.h (CANONICALIZE_COMPARISON): Remove macro
	definition.
	* config/alpha/alpha.c (alpha_canonicalize_comparison): New
	function.
	(TARGET_CANONICALIZE_COMPARISON): New macro definition.
	* config/arm/arm-protos.h (arm_canonicalize_comparison): Remove
	prototype.
	* config/arm/arm.c (arm_canonicalize_comparison): Add new
	parameter.
	(TARGET_CANONICALIZE_COMPARISON): New macro definition.
	* config/arm/arm.h (CANONICALIZE_COMPARISON): Remove macro
	definition.
	* config/s390/s390-protos.h (s390_canonicalize_comparison): Remove
	prototype.
	* config/s390/s390.c (s390_canonicalize_comparison): Add new
	parameter.
	(TARGET_CANONICALIZE_COMPARISON): New macro definition.
	* config/s390/s390.h (CANONICALIZE_COMPARISON): Remove macro
	definition.
	* config/sh/sh-protos.h (sh_canonicalize_comparison): Remove
	prototype.
	* config/sh/sh.c (sh_canonicalize_comparison): Add new prototype.  New
	function overloading the old one.
	(TARGET_CANONICALIZE_COMPARISON): New macro definition.
	* config/sh/sh.h (CANONICALIZE_COMPARISON): Remove macro
	definition.
	* config/spu/spu.c (spu_canonicalize_comparison): New function.
	(TARGET_CANONICALIZE_COMPARISON): New macro definition.
	* config/spu/spu.h (CANONICALIZE_COMPARISON): Remove macro
	definition.

---
 gcc/combine.c                 |   19 ++++++-!!!
 gcc/config/alpha/alpha.c      |   27 ++++++++++++++++
 gcc/config/alpha/alpha.h      |   20 ------------
 gcc/config/arm/arm-protos.h   |    1 
 gcc/config/arm/arm.c          |   68 ++-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 gcc/config/arm/arm.h          |    3 -
 gcc/config/s390/s390-protos.h |    1 
 gcc/config/s390/s390.c        |   13 +++!!!!!
 gcc/config/s390/s390.h        |    4 --
 gcc/config/sh/sh-protos.h     |    2 -
 gcc/config/sh/sh.c            |   34 +++++++++++!!!!!!!!!!
 gcc/config/sh/sh.h            |    4 --
 gcc/config/spu/spu.c          |   17 ++++++++++
 gcc/config/spu/spu.h          |   12 -------
 gcc/doc/tm.texi               |   20 !!!!!!!!!!!!
 gcc/doc/tm.texi.in            |   20 !!!!!!!!!!!!
 gcc/target.def                |    8 ++++
 gcc/targhooks.h               |    2 +
 18 files changed, 94 insertions(+), 50 deletions(-), 131 modifications(!)

Index: gcc/combine.c
===================================================================
*** gcc/combine.c.orig
--- gcc/combine.c
*************** static rtx gen_lowpart_or_truncate (enum
*** 494,499 ****
--- 494,510 ----
  static const struct rtl_hooks combine_rtl_hooks = RTL_HOOKS_INITIALIZER;
  
  \f
+ /* Convenience wrapper for the canonicalize_comparison target hook.
+    Target hooks cannot use enum rtx_code.  */
+ static inline void
+ target_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1,
+ 				bool op0_preserve_value)
+ {
+   int code_int = (int)*code;
+   targetm.canonicalize_comparison (&code_int, op0, op1, op0_preserve_value);
+   *code = (enum rtx_code)code_int;
+ }
+ 
  /* Try to split PATTERN found in INSN.  This returns NULL_RTX if
     PATTERN can not be split.  Otherwise, it returns an insn sequence.
     This is a wrapper around split_insns which ensures that the
*************** try_combine (rtx i3, rtx i2, rtx i1, rtx
*** 2944,2952 ****
  	  compare_code = orig_compare_code = GET_CODE (*cc_use_loc);
  	  compare_code = simplify_compare_const (compare_code,
  						 op0, &op1);
! #ifdef CANONICALIZE_COMPARISON
! 	  CANONICALIZE_COMPARISON (compare_code, op0, op1);
! #endif
  	}
  
        /* Do the rest only if op1 is const0_rtx, which may be the
--- 2955,2961 ----
  	  compare_code = orig_compare_code = GET_CODE (*cc_use_loc);
  	  compare_code = simplify_compare_const (compare_code,
  						 op0, &op1);
! 	  target_canonicalize_comparison (&compare_code, &op0, &op1, 1);
  	}
  
        /* Do the rest only if op1 is const0_rtx, which may be the
*************** simplify_comparison (enum rtx_code code,
*** 11959,11969 ****
  	    }
  	}
  
- #ifdef CANONICALIZE_COMPARISON
    /* If this machine only supports a subset of valid comparisons, see if we
       can convert an unsupported one into a supported one.  */
!   CANONICALIZE_COMPARISON (code, op0, op1);
! #endif
  
    *pop0 = op0;
    *pop1 = op1;
--- 11968,11976 ----
  	    }
  	}
  
    /* If this machine only supports a subset of valid comparisons, see if we
       can convert an unsupported one into a supported one.  */
!   target_canonicalize_comparison (&code, &op0, &op1, 0);
  
    *pop0 = op0;
    *pop1 = op1;
Index: gcc/config/alpha/alpha.c
===================================================================
*** gcc/config/alpha/alpha.c.orig
--- gcc/config/alpha/alpha.c
*************** alpha_conditional_register_usage (void)
*** 9683,9688 ****
--- 9683,9712 ----
      for (i = 32; i < 63; i++)
        fixed_regs[i] = call_used_regs[i] = 1;
  }
+ 
+ /* Canonicalize a comparison from one we don't have to one we do have.  */
+ 
+ static void
+ alpha_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
+ 			       bool op0_preserve_value)
+ {
+   if (!op0_preserve_value
+       && (*code == GE || *code == GT || *code == GEU || *code == GTU)
+       && (REG_P (*op1) || *op1 == const0_rtx))
+     {
+       rtx tem = *op0;
+       *op0 = *op1;
+       *op1 = tem;
+       *code = (int)swap_condition ((enum rtx_code)*code);
+     }
+ 
+   if ((*code == LT || *code == LTU)
+       && CONST_INT_P (*op1) && INTVAL (*op1) == 256)
+     {
+       *code = *code == LT ? LE : LEU;
+       *op1 = GEN_INT (255);
+     }
+ }
  \f
  /* Initialize the GCC target structure.  */
  #if TARGET_ABI_OPEN_VMS
*************** alpha_conditional_register_usage (void)
*** 9850,9855 ****
--- 9874,9882 ----
  #undef TARGET_CONDITIONAL_REGISTER_USAGE
  #define TARGET_CONDITIONAL_REGISTER_USAGE alpha_conditional_register_usage
  
+ #undef TARGET_CANONICALIZE_COMPARISON
+ #define TARGET_CANONICALIZE_COMPARISON alpha_canonicalize_comparison
+ 
  struct gcc_target targetm = TARGET_INITIALIZER;
  
  \f
Index: gcc/config/alpha/alpha.h
===================================================================
*** gcc/config/alpha/alpha.h.orig
--- gcc/config/alpha/alpha.h
*************** do {									     \
*** 922,947 ****
  #define FLOAT_STORE_FLAG_VALUE(MODE) \
    REAL_VALUE_ATOF ((TARGET_FLOAT_VAX ? "0.5" : "2.0"), (MODE))
  
- /* Canonicalize a comparison from one we don't have to one we do have.  */
- 
- #define CANONICALIZE_COMPARISON(CODE,OP0,OP1) \
-   do {									\
-     if (((CODE) == GE || (CODE) == GT || (CODE) == GEU || (CODE) == GTU) \
- 	&& (REG_P (OP1) || (OP1) == const0_rtx))		\
-       {									\
- 	rtx tem = (OP0);						\
- 	(OP0) = (OP1);							\
- 	(OP1) = tem;							\
- 	(CODE) = swap_condition (CODE);					\
-       }									\
-     if (((CODE) == LT || (CODE) == LTU)					\
- 	&& CONST_INT_P (OP1) && INTVAL (OP1) == 256)			\
-       {									\
- 	(CODE) = (CODE) == LT ? LE : LEU;				\
- 	(OP1) = GEN_INT (255);						\
-       }									\
-   } while (0)
- 
  /* Specify the machine mode that pointers have.
     After generation of rtl, the compiler makes no further distinction
     between pointers and any other objects of this machine mode.  */
--- 922,927 ----
Index: gcc/config/arm/arm-protos.h
===================================================================
*** gcc/config/arm/arm-protos.h.orig
--- gcc/config/arm/arm-protos.h
*************** extern int const_ok_for_op (HOST_WIDE_IN
*** 53,59 ****
  extern int const_ok_for_dimode_op (HOST_WIDE_INT, enum rtx_code);
  extern int arm_split_constant (RTX_CODE, enum machine_mode, rtx,
  			       HOST_WIDE_INT, rtx, rtx, int);
- extern RTX_CODE arm_canonicalize_comparison (RTX_CODE, rtx *, rtx *);
  extern int legitimate_pic_operand_p (rtx);
  extern rtx legitimize_pic_address (rtx, enum machine_mode, rtx);
  extern rtx legitimize_tls_address (rtx, rtx);
--- 53,58 ----
Index: gcc/config/arm/arm.c
===================================================================
*** gcc/config/arm/arm.c.orig
--- gcc/config/arm/arm.c
*************** static int arm_cortex_a5_branch_cost (bo
*** 269,275 ****
  
  static bool arm_vectorize_vec_perm_const_ok (enum machine_mode vmode,
  					     const unsigned char *sel);
! 
  \f
  /* Table of machine attributes.  */
  static const struct attribute_spec arm_attribute_table[] =
--- 269,276 ----
  
  static bool arm_vectorize_vec_perm_const_ok (enum machine_mode vmode,
  					     const unsigned char *sel);
! static void arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
! 					 bool op0_preserve_value);
  \f
  /* Table of machine attributes.  */
  static const struct attribute_spec arm_attribute_table[] =
*************** static const struct attribute_spec arm_a
*** 626,631 ****
--- 627,636 ----
  #define TARGET_VECTORIZE_VEC_PERM_CONST_OK \
    arm_vectorize_vec_perm_const_ok
  
+ #undef TARGET_CANONICALIZE_COMPARISON
+ #define TARGET_CANONICALIZE_COMPARISON \
+   arm_canonicalize_comparison
+ 
  struct gcc_target targetm = TARGET_INITIALIZER;
  \f
  /* Obstack for minipool constant handling.  */
*************** arm_gen_constant (enum rtx_code code, en
*** 3543,3550 ****
     This can be done for a few constant compares, where we can make the
     immediate value easier to load.  */
  
! enum rtx_code
! arm_canonicalize_comparison (enum rtx_code code, rtx *op0, rtx *op1)
  {
    enum machine_mode mode;
    unsigned HOST_WIDE_INT i, maxval;
--- 3548,3556 ----
     This can be done for a few constant compares, where we can make the
     immediate value easier to load.  */
  
! static void
! arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
! 			     bool op0_preserve_value)
  {
    enum machine_mode mode;
    unsigned HOST_WIDE_INT i, maxval;
*************** arm_canonicalize_comparison (enum rtx_co
*** 3563,3577 ****
      {
        rtx tem;
  
!       if (code == GT || code == LE
! 	  || (!TARGET_ARM && (code == GTU || code == LEU)))
  	{
  	  /* Missing comparison.  First try to use an available
  	     comparison.  */
  	  if (CONST_INT_P (*op1))
  	    {
  	      i = INTVAL (*op1);
! 	      switch (code)
  		{
  		case GT:
  		case LE:
--- 3569,3583 ----
      {
        rtx tem;
  
!       if (*code == GT || *code == LE
! 	  || (!TARGET_ARM && (*code == GTU || *code == LEU)))
  	{
  	  /* Missing comparison.  First try to use an available
  	     comparison.  */
  	  if (CONST_INT_P (*op1))
  	    {
  	      i = INTVAL (*op1);
! 	      switch (*code)
  		{
  		case GT:
  		case LE:
*************** arm_canonicalize_comparison (enum rtx_co
*** 3579,3585 ****
  		      && arm_const_double_by_immediates (GEN_INT (i + 1)))
  		    {
  		      *op1 = GEN_INT (i + 1);
! 		      return code == GT ? GE : LT;
  		    }
  		  break;
  		case GTU:
--- 3585,3592 ----
  		      && arm_const_double_by_immediates (GEN_INT (i + 1)))
  		    {
  		      *op1 = GEN_INT (i + 1);
! 		      *code = *code == GT ? GE : LT;
! 		      return;
  		    }
  		  break;
  		case GTU:
*************** arm_canonicalize_comparison (enum rtx_co
*** 3588,3594 ****
  		      && arm_const_double_by_immediates (GEN_INT (i + 1)))
  		    {
  		      *op1 = GEN_INT (i + 1);
! 		      return code == GTU ? GEU : LTU;
  		    }
  		  break;
  		default:
--- 3595,3602 ----
  		      && arm_const_double_by_immediates (GEN_INT (i + 1)))
  		    {
  		      *op1 = GEN_INT (i + 1);
! 		      *code = *code == GTU ? GEU : LTU;
! 		      return;
  		    }
  		  break;
  		default:
*************** arm_canonicalize_comparison (enum rtx_co
*** 3597,3615 ****
  	    }
  
  	  /* If that did not work, reverse the condition.  */
! 	  tem = *op0;
! 	  *op0 = *op1;
! 	  *op1 = tem;
! 	  return swap_condition (code);
  	}
! 
!       return code;
      }
  
    /* If *op0 is (zero_extend:SI (subreg:QI (reg:SI) 0)) and comparing
       with const0_rtx, change it to (and:SI (reg:SI) (const_int 255)),
       to facilitate possible combining with a cmp into 'ands'.  */
!   if (mode == SImode
        && GET_CODE (*op0) == ZERO_EXTEND
        && GET_CODE (XEXP (*op0, 0)) == SUBREG
        && GET_MODE (XEXP (*op0, 0)) == QImode
--- 3605,3626 ----
  	    }
  
  	  /* If that did not work, reverse the condition.  */
! 	  if (!op0_preserve_value)
! 	    {
! 	      tem = *op0;
! 	      *op0 = *op1;
! 	      *op1 = tem;
! 	      *code = (int)swap_condition ((enum rtx_code)*code);
! 	    }
  	}
!       return;
      }
  
    /* If *op0 is (zero_extend:SI (subreg:QI (reg:SI) 0)) and comparing
       with const0_rtx, change it to (and:SI (reg:SI) (const_int 255)),
       to facilitate possible combining with a cmp into 'ands'.  */
!   if (!op0_preserve_value
!       && mode == SImode
        && GET_CODE (*op0) == ZERO_EXTEND
        && GET_CODE (XEXP (*op0, 0)) == SUBREG
        && GET_MODE (XEXP (*op0, 0)) == QImode
*************** arm_canonicalize_comparison (enum rtx_co
*** 3624,3638 ****
    if (!CONST_INT_P (*op1)
        || const_ok_for_arm (INTVAL (*op1))
        || const_ok_for_arm (- INTVAL (*op1)))
!     return code;
  
    i = INTVAL (*op1);
  
!   switch (code)
      {
      case EQ:
      case NE:
!       return code;
  
      case GT:
      case LE:
--- 3635,3649 ----
    if (!CONST_INT_P (*op1)
        || const_ok_for_arm (INTVAL (*op1))
        || const_ok_for_arm (- INTVAL (*op1)))
!     return;
  
    i = INTVAL (*op1);
  
!   switch (*code)
      {
      case EQ:
      case NE:
!       return;
  
      case GT:
      case LE:
*************** arm_canonicalize_comparison (enum rtx_co
*** 3640,3646 ****
  	  && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
  	{
  	  *op1 = GEN_INT (i + 1);
! 	  return code == GT ? GE : LT;
  	}
        break;
  
--- 3651,3658 ----
  	  && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
  	{
  	  *op1 = GEN_INT (i + 1);
! 	  *code = *code == GT ? GE : LT;
! 	  return;
  	}
        break;
  
*************** arm_canonicalize_comparison (enum rtx_co
*** 3650,3656 ****
  	  && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
  	{
  	  *op1 = GEN_INT (i - 1);
! 	  return code == GE ? GT : LE;
  	}
        break;
  
--- 3662,3669 ----
  	  && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
  	{
  	  *op1 = GEN_INT (i - 1);
! 	  *code = *code == GE ? GT : LE;
! 	  return;
  	}
        break;
  
*************** arm_canonicalize_comparison (enum rtx_co
*** 3660,3666 ****
  	  && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
  	{
  	  *op1 = GEN_INT (i + 1);
! 	  return code == GTU ? GEU : LTU;
  	}
        break;
  
--- 3673,3680 ----
  	  && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
  	{
  	  *op1 = GEN_INT (i + 1);
! 	  *code = *code == GTU ? GEU : LTU;
! 	  return;
  	}
        break;
  
*************** arm_canonicalize_comparison (enum rtx_co
*** 3670,3684 ****
  	  && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
  	{
  	  *op1 = GEN_INT (i - 1);
! 	  return code == GEU ? GTU : LEU;
  	}
        break;
  
      default:
        gcc_unreachable ();
      }
- 
-   return code;
  }
  
  
--- 3684,3697 ----
  	  && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
  	{
  	  *op1 = GEN_INT (i - 1);
! 	  *code = *code == GEU ? GTU : LEU;
! 	  return;
  	}
        break;
  
      default:
        gcc_unreachable ();
      }
  }
  
  
*************** bool
*** 26979,26985 ****
  arm_validize_comparison (rtx *comparison, rtx * op1, rtx * op2)
  {
    enum rtx_code code = GET_CODE (*comparison);
!   enum rtx_code canonical_code;
    enum machine_mode mode = (GET_MODE (*op1) == VOIDmode) 
      ? GET_MODE (*op2) : GET_MODE (*op1);
  
--- 26992,26998 ----
  arm_validize_comparison (rtx *comparison, rtx * op1, rtx * op2)
  {
    enum rtx_code code = GET_CODE (*comparison);
!   int code_int;
    enum machine_mode mode = (GET_MODE (*op1) == VOIDmode) 
      ? GET_MODE (*op2) : GET_MODE (*op1);
  
*************** arm_validize_comparison (rtx *comparison
*** 26988,26995 ****
    if (code == UNEQ || code == LTGT)
      return false;
  
!   canonical_code = arm_canonicalize_comparison (code, op1, op2);
!   PUT_CODE (*comparison, canonical_code);
  
    switch (mode)
      {
--- 27001,27009 ----
    if (code == UNEQ || code == LTGT)
      return false;
  
!   code_int = (int)code;
!   arm_canonicalize_comparison (&code_int, op1, op2, 0);
!   PUT_CODE (*comparison, (enum rtx_code)code_int);
  
    switch (mode)
      {
Index: gcc/config/arm/arm.h
===================================================================
*** gcc/config/arm/arm.h.orig
--- gcc/config/arm/arm.h
*************** extern int making_const_table;
*** 2078,2086 ****
     ? reverse_condition_maybe_unordered (code) \
     : reverse_condition (code))
  
- #define CANONICALIZE_COMPARISON(CODE, OP0, OP1)				\
-   (CODE) = arm_canonicalize_comparison (CODE, &(OP0), &(OP1))
- 
  /* The arm5 clz instruction returns 32.  */
  #define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE)  ((VALUE) = 32, 1)
  #define CTZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE)  ((VALUE) = 32, 1)
--- 2078,2083 ----
Index: gcc/config/s390/s390-protos.h
===================================================================
*** gcc/config/s390/s390-protos.h.orig
--- gcc/config/s390/s390-protos.h
*************** extern int tls_symbolic_operand (rtx);
*** 58,64 ****
  extern bool s390_match_ccmode (rtx, enum machine_mode);
  extern enum machine_mode s390_tm_ccmode (rtx, rtx, bool);
  extern enum machine_mode s390_select_ccmode (enum rtx_code, rtx, rtx);
- extern void s390_canonicalize_comparison (enum rtx_code *, rtx *, rtx *);
  extern rtx s390_emit_compare (enum rtx_code, rtx, rtx);
  extern void s390_emit_jump (rtx, rtx);
  extern bool symbolic_reference_mentioned_p (rtx);
--- 58,63 ----
Index: gcc/config/s390/s390.c
===================================================================
*** gcc/config/s390/s390.c.orig
--- gcc/config/s390/s390.c
*************** s390_select_ccmode (enum rtx_code code, 
*** 745,753 ****
  /* Replace the comparison OP0 CODE OP1 by a semantically equivalent one
     that we can implement more efficiently.  */
  
! void
! s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
  {
    /* Convert ZERO_EXTRACT back to AND to enable TM patterns.  */
    if ((*code == EQ || *code == NE)
        && *op1 == const0_rtx
--- 745,757 ----
  /* Replace the comparison OP0 CODE OP1 by a semantically equivalent one
     that we can implement more efficiently.  */
  
! static void
! s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
! 			      bool op0_preserve_value)
  {
+   if (op0_preserve_value)
+     return;
+ 
    /* Convert ZERO_EXTRACT back to AND to enable TM patterns.  */
    if ((*code == EQ || *code == NE)
        && *op1 == const0_rtx
*************** s390_canonicalize_comparison (enum rtx_c
*** 894,900 ****
    if (MEM_P (*op0) && REG_P (*op1))
      {
        rtx tem = *op0; *op0 = *op1; *op1 = tem;
!       *code = swap_condition (*code);
      }
  }
  
--- 898,904 ----
    if (MEM_P (*op0) && REG_P (*op1))
      {
        rtx tem = *op0; *op0 = *op1; *op1 = tem;
!       *code = (int)swap_condition ((enum rtx_code)*code);
      }
  }
  
*************** s390_loop_unroll_adjust (unsigned nunrol
*** 11071,11076 ****
--- 11075,11083 ----
  #undef TARGET_UNWIND_WORD_MODE
  #define TARGET_UNWIND_WORD_MODE s390_unwind_word_mode
  
+ #undef TARGET_CANONICALIZE_COMPARISON
+ #define TARGET_CANONICALIZE_COMPARISON s390_canonicalize_comparison
+ 
  struct gcc_target targetm = TARGET_INITIALIZER;
  
  #include "gt-s390.h"
Index: gcc/config/s390/s390.h
===================================================================
*** gcc/config/s390/s390.h.orig
--- gcc/config/s390/s390.h
*************** do {									\
*** 720,729 ****
     return the mode to be used for the comparison.  */
  #define SELECT_CC_MODE(OP, X, Y) s390_select_ccmode ((OP), (X), (Y))
  
- /* Canonicalize a comparison from one we don't have to one we do have.  */
- #define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \
-   s390_canonicalize_comparison (&(CODE), &(OP0), &(OP1))
- 
  /* Relative costs of operations.  */
  
  /* A C expression for the cost of a branch instruction.  A value of 1
--- 720,725 ----
Index: gcc/config/sh/sh-protos.h
===================================================================
*** gcc/config/sh/sh-protos.h.orig
--- gcc/config/sh/sh-protos.h
*************** extern bool sh_expand_t_scc (rtx *);
*** 159,166 ****
  extern rtx sh_gen_truncate (enum machine_mode, rtx, int);
  extern bool sh_vector_mode_supported_p (enum machine_mode);
  extern bool sh_cfun_trap_exit_p (void);
- extern void sh_canonicalize_comparison (enum rtx_code&, rtx&, rtx&,
- 					enum machine_mode mode = VOIDmode);
  extern rtx sh_find_equiv_gbr_addr (rtx cur_insn, rtx mem);
  extern int sh_eval_treg_value (rtx op);
  
--- 159,164 ----
Index: gcc/config/sh/sh.c
===================================================================
*** gcc/config/sh/sh.c.orig
--- gcc/config/sh/sh.c
*************** static int max_mov_insn_displacement (en
*** 314,319 ****
--- 314,322 ----
  static int mov_insn_alignment_mask (enum machine_mode, bool);
  static HOST_WIDE_INT disp_addr_displacement (rtx);
  static bool sequence_insn_p (rtx);
+ static void sh_canonicalize_comparison (int *, rtx *, rtx *, bool);
+ static void sh_canonicalize_comparison (enum rtx_code&, rtx&, rtx&,
+ 					enum machine_mode, bool);
  
  static void sh_init_sync_libfuncs (void) ATTRIBUTE_UNUSED;
  \f
*************** static const struct attribute_spec sh_at
*** 586,591 ****
--- 589,597 ----
  #undef TARGET_LEGITIMATE_CONSTANT_P
  #define TARGET_LEGITIMATE_CONSTANT_P	sh_legitimate_constant_p
  
+ #undef TARGET_CANONICALIZE_COMPARISON
+ #define TARGET_CANONICALIZE_COMPARISON	sh_canonicalize_comparison
+ 
  /* Machine-specific symbol_ref flags.  */
  #define SYMBOL_FLAG_FUNCVEC_FUNCTION    (SYMBOL_FLAG_MACH_DEP << 0)
  
*************** prepare_move_operands (rtx operands[], e
*** 1909,1920 ****
      }
  }
  
! /* Implement the CANONICALIZE_COMPARISON macro for the combine pass.
!    This function is also re-used to canonicalize comparisons in cbranch
!    pattern expanders.  */
! void
  sh_canonicalize_comparison (enum rtx_code& cmp, rtx& op0, rtx& op1,
! 			    enum machine_mode mode)
  {
    /* When invoked from within the combine pass the mode is not specified,
       so try to get it from one of the operands.  */
--- 1915,1928 ----
      }
  }
  
! /* Implement the canonicalize_comparison target hook for the combine
!    pass.  For the target hook this function is invoked via
!    sh_canonicalize_comparison.  This function is also re-used to
!    canonicalize comparisons in cbranch pattern expanders.  */
! static void
  sh_canonicalize_comparison (enum rtx_code& cmp, rtx& op0, rtx& op1,
! 			    enum machine_mode mode,
! 			    bool op0_preserve_value ATTRIBUTE_UNUSED)
  {
    /* When invoked from within the combine pass the mode is not specified,
       so try to get it from one of the operands.  */
*************** sh_canonicalize_comparison (enum rtx_cod
*** 2008,2013 ****
--- 2016,2034 ----
      }
  }
  
+ /* This function implements the canonicalize_comparison target hook.
+    This wrapper around the internally used sh_canonicalize_comparison
+    function is needed to do the enum rtx_code <-> int conversion.
+    Target hooks cannot use enum rtx_code in its definition.  */
+ static void
+ sh_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
+ 			    bool op0_preserve_value)
+ {
+   enum rtx_code tmp_code = (enum rtx_code)*code;
+   sh_canonicalize_comparison (tmp_code, *op0, *op1,
+ 			      VOIDmode, op0_preserve_value);
+   *code = (int)tmp_code;
+ }
  enum rtx_code
  prepare_cbranch_operands (rtx *operands, enum machine_mode mode,
  			  enum rtx_code comparison)
*************** prepare_cbranch_operands (rtx *operands,
*** 2021,2027 ****
    else
      scratch = operands[4];
  
!   sh_canonicalize_comparison (comparison, operands[1], operands[2], mode);
  
    /* Notice that this function is also invoked after reload by
       the cbranchdi4_i pattern, through expand_cbranchdi4.  */
--- 2042,2049 ----
    else
      scratch = operands[4];
  
!   sh_canonicalize_comparison (comparison, operands[1], operands[2],
! 			      mode, false);
  
    /* Notice that this function is also invoked after reload by
       the cbranchdi4_i pattern, through expand_cbranchdi4.  */
Index: gcc/config/sh/sh.h
===================================================================
*** gcc/config/sh/sh.h.orig
--- gcc/config/sh/sh.h
*************** struct sh_args {
*** 1873,1882 ****
     more compact code.  */
  #define SHIFT_COUNT_TRUNCATED (0)
  
- /* CANONICALIZE_COMPARISON macro for the combine pass.  */
- #define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \
-   sh_canonicalize_comparison ((CODE), (OP0), (OP1))
- 
  /* All integers have the same format so truncation is easy.  */
  /* But SHmedia must sign-extend DImode when truncating to SImode.  */
  #define TRULY_NOOP_TRUNCATION(OUTPREC,INPREC) \
--- 1873,1878 ----
Index: gcc/config/spu/spu.c
===================================================================
*** gcc/config/spu/spu.c.orig
--- gcc/config/spu/spu.c
*************** spu_output_mi_thunk (FILE *file, tree th
*** 7095,7100 ****
--- 7095,7114 ----
    final_end_function ();
  }
  
+ /* Canonicalize a comparison from one we don't have to one we do have.  */
+ static void
+ spu_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
+ 			     bool op0_preserve_value)
+ {
+   if (!op0_preserve_value
+       && (*code == LE || *code == LT || *code == LEU || *code == LTU))
+     {
+       rtx tem = *op0;
+       *op0 = *op1;
+       *op1 = tem;
+       *code = (int)swap_condition ((enum rtx_code)*code);
+     }
+ }
  \f
  /*  Table of machine attributes.  */
  static const struct attribute_spec spu_attribute_table[] =
*************** static const struct attribute_spec spu_a
*** 7308,7313 ****
--- 7322,7330 ----
  #undef TARGET_DELAY_VARTRACK
  #define TARGET_DELAY_VARTRACK true
  
+ #undef TARGET_CANONICALIZE_COMPARISON
+ #define TARGET_CANONICALIZE_COMPARISON spu_canonicalize_comparison
+ 
  struct gcc_target targetm = TARGET_INITIALIZER;
  
  #include "gt-spu.h"
Index: gcc/config/spu/spu.h
===================================================================
*** gcc/config/spu/spu.h.orig
--- gcc/config/spu/spu.h
*************** do {									\
*** 520,537 ****
  
  #define NO_IMPLICIT_EXTERN_C 1
  
- /* Canonicalize a comparison from one we don't have to one we do have.  */
- #define CANONICALIZE_COMPARISON(CODE,OP0,OP1) \
-   do {                                                                    \
-     if (((CODE) == LE || (CODE) == LT || (CODE) == LEU || (CODE) == LTU)) \
-       {                                                                   \
-         rtx tem = (OP0);                                                  \
-         (OP0) = (OP1);                                                    \
-         (OP1) = tem;                                                      \
-         (CODE) = swap_condition (CODE);                                   \
-       }                                                                   \
-   } while (0)
- 
  
  /* Address spaces.  */
  #define ADDR_SPACE_EA	1
--- 520,525 ----
Index: gcc/doc/tm.texi.in
===================================================================
*** gcc/doc/tm.texi.in.orig
--- gcc/doc/tm.texi.in
*************** You should define this macro if and only
*** 5928,5952 ****
  in @file{@var{machine}-modes.def}.
  @end defmac
  
! @defmac CANONICALIZE_COMPARISON (@var{code}, @var{op0}, @var{op1})
  On some machines not all possible comparisons are defined, but you can
  convert an invalid comparison into a valid one.  For example, the Alpha
  does not have a @code{GT} comparison, but you can use an @code{LT}
  comparison instead and swap the order of the operands.
  
! On such machines, define this macro to be a C statement to do any
! required conversions.  @var{code} is the initial comparison code
! and @var{op0} and @var{op1} are the left and right operands of the
! comparison, respectively.  You should modify @var{code}, @var{op0}, and
! @var{op1} as required.
  
  GCC will not assume that the comparison resulting from this macro is
  valid but will see if the resulting insn matches a pattern in the
  @file{md} file.
  
! You need not define this macro if it would never change the comparison
! code or operands.
! @end defmac
  
  @defmac REVERSIBLE_CC_MODE (@var{mode})
  A C expression whose value is one if it is always safe to reverse a
--- 5928,5954 ----
  in @file{@var{machine}-modes.def}.
  @end defmac
  
! @hook TARGET_CANONICALIZE_COMPARISON (@var{code}, @var{op0}, @var{op1}, @var{op0_preserve_value})
  On some machines not all possible comparisons are defined, but you can
  convert an invalid comparison into a valid one.  For example, the Alpha
  does not have a @code{GT} comparison, but you can use an @code{LT}
  comparison instead and swap the order of the operands.
  
! On such machines, implement this hook to do any required conversions.
! @var{code} is the initial comparison code and @var{op0} and @var{op1}
! are the left and right operands of the comparison, respectively.  If
! @var{op0_preserve_value} is @code{true} the implementation is not
! allowed to change the value of @var{op0} since the value might be used
! in RTXs which aren't comparisons.  E.g. the implementation is not
! allowed to swap operands in that case.
  
  GCC will not assume that the comparison resulting from this macro is
  valid but will see if the resulting insn matches a pattern in the
  @file{md} file.
  
! You need not to implement this hook if it would never change the
! comparison code or operands.
! @end deftypefn
  
  @defmac REVERSIBLE_CC_MODE (@var{mode})
  A C expression whose value is one if it is always safe to reverse a
Index: gcc/target.def
===================================================================
*** gcc/target.def.orig
--- gcc/target.def
*************** DEFHOOK
*** 2877,2882 ****
--- 2877,2890 ----
   enum unwind_info_type, (void),
   default_debug_unwind_info)
  
+ /* The code parameter should be of type enum rtx_code but this is not
+    defined at this time.  */
+ DEFHOOK
+ (canonicalize_comparison,
+  "",
+  void, (int *code, rtx *op0, rtx *op1, bool op0_preserve_value),
+  default_canonicalize_comparison)
+ 
  DEFHOOKPOD
  (atomic_test_and_set_trueval,
   "This value should be set if the result written by\
Index: gcc/targhooks.h
===================================================================
*** gcc/targhooks.h.orig
--- gcc/targhooks.h
*************** extern unsigned char default_class_max_n
*** 179,184 ****
--- 179,186 ----
  
  extern enum unwind_info_type default_debug_unwind_info (void);
  
+ extern bool default_canonicalize_comparison (int *, rtx *, rtx *, bool);
+ 
  extern int default_label_align_after_barrier_max_skip (rtx);
  extern int default_loop_align_max_skip (rtx);
  extern int default_label_align_max_skip (rtx);
Index: gcc/doc/tm.texi
===================================================================
*** gcc/doc/tm.texi.orig
--- gcc/doc/tm.texi
*************** You should define this macro if and only
*** 6024,6048 ****
  in @file{@var{machine}-modes.def}.
  @end defmac
  
! @defmac CANONICALIZE_COMPARISON (@var{code}, @var{op0}, @var{op1})
  On some machines not all possible comparisons are defined, but you can
  convert an invalid comparison into a valid one.  For example, the Alpha
  does not have a @code{GT} comparison, but you can use an @code{LT}
  comparison instead and swap the order of the operands.
  
! On such machines, define this macro to be a C statement to do any
! required conversions.  @var{code} is the initial comparison code
! and @var{op0} and @var{op1} are the left and right operands of the
! comparison, respectively.  You should modify @var{code}, @var{op0}, and
! @var{op1} as required.
  
  GCC will not assume that the comparison resulting from this macro is
  valid but will see if the resulting insn matches a pattern in the
  @file{md} file.
  
! You need not define this macro if it would never change the comparison
! code or operands.
! @end defmac
  
  @defmac REVERSIBLE_CC_MODE (@var{mode})
  A C expression whose value is one if it is always safe to reverse a
--- 6024,6050 ----
  in @file{@var{machine}-modes.def}.
  @end defmac
  
! @deftypefn {Target Hook} void TARGET_CANONICALIZE_COMPARISON (int *@var{code}, rtx *@var{op0}, rtx *@var{op1}, bool @var{op0_preserve_value}) (@var{code}, @var{op0}, @var{op1}, @var{op0_preserve_value})
  On some machines not all possible comparisons are defined, but you can
  convert an invalid comparison into a valid one.  For example, the Alpha
  does not have a @code{GT} comparison, but you can use an @code{LT}
  comparison instead and swap the order of the operands.
  
! On such machines, implement this hook to do any required conversions.
! @var{code} is the initial comparison code and @var{op0} and @var{op1}
! are the left and right operands of the comparison, respectively.  If
! @var{op0_preserve_value} is @code{true} the implementation is not
! allowed to change the value of @var{op0} since the value might be used
! in RTXs which aren't comparisons.  E.g. the implementation is not
! allowed to swap operands in that case.
  
  GCC will not assume that the comparison resulting from this macro is
  valid but will see if the resulting insn matches a pattern in the
  @file{md} file.
  
! You need not to implement this hook if it would never change the
! comparison code or operands.
! @end deftypefn
  
  @defmac REVERSIBLE_CC_MODE (@var{mode})
  A C expression whose value is one if it is always safe to reverse a

^ permalink raw reply	[flat|nested] 17+ messages in thread
* [PATCH] Bugfix: Additional parameter for canonicalize comparison
@ 2012-12-06  8:25 Andreas Krebbel
  2012-12-07 19:37 ` Richard Henderson
  0 siblings, 1 reply; 17+ messages in thread
From: Andreas Krebbel @ 2012-12-06  8:25 UTC (permalink / raw)
  To: gcc-patches; +Cc: rth, richard.earnshaw, kkojima, uweigand

Hi,

the attached patch fixes a problem I encountered with Richards patch
adding an extvz expander to the s390 backend.

According to its definition the CANONICALIZE_COMPARISON macro is
allowed to modify the two operands as well as the comparison code in
order to make a comparison valid or more efficient. S/390 and other
targets transform the operands while leaving the comparison
semantically equivalent. But this does *not* mean that the operands
after that transformation will resolve to the same value.  try_combine
unfortunately requires that when re-using op0 in other contexts i.e. a
SET rtx. On S/390 this happened when transforming op0 from a
ZERO_EXTRACT to and AND without adding a SHIFT. The comparison stays
equivalent but the value of op0 changes.

Other targets mostly swap the operands.  This would also be incorrect
for combine but so far didn't trigger any problems since the problem
in combine is only triggered when comparing with 0 and swapping the
operands would most likely lead to non-canonical RTL in that case.

The patch adds a boolean parameter to the canonicalize_comparison
invokation which tells the callee that the value of op0 needs to be
preserved.

While touching it I also converted the CANONICALIZE_COMPARISON target
macro into a target hook.

Tested on s390, s390x and x86_64. No regressions.
SPU tests where done by Ulrich Weigand.  No regressions either.
Compile tests on the other affected targets done via cross build.

I'll cc the target maintainers for their approval.
Ok for mainline?

Bye,

-Andreas-

2012-12-06  Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>

	* target.def: Define canonicalize_comparison hook.
	* targhooks.h (default_canonicalize_comparison): New prototype.
	* targhooks.c (default_canonicalize_comparison): New function.
	* doc/tm.texi: Add documentation for the new target hook.
	* doc/tm.texi.in: Likewise.
	* combine.c (try_combine): Adjust to use the target hook.
	* config/alpha/alpha.h (CANONICALIZE_COMPARISON): Remove macro
	definition.
	* config/alpha/alpha.c (alpha_canonicalize_comparison): New
	function.
	(TARGET_CANONICALIZE_COMPARISON): New macro definition.
	* config/arm/arm-protos.h (arm_canonicalize_comparison): Remove
	prototype.
	* config/arm/arm.c (arm_canonicalize_comparison): Add new
	parameter and return value.
	(TARGET_CANONICALIZE_COMPARISON): New macro definition.
	* config/arm/arm.h (CANONICALIZE_COMPARISON): Remove macro
	definition.
	* config/s390/s390-protos.h (s390_canonicalize_comparison): Remove
	prototype.
	* config/s390/s390.c (s390_canonicalize_comparison): Add new
	parameter and return value.
	(TARGET_CANONICALIZE_COMPARISON): New macro definition.
	* config/s390/s390.h (CANONICALIZE_COMPARISON): Remove macro
	definition.
	* config/sh/sh-protos.h (sh_canonicalize_comparison): Remove
	prototype.
	* config/sh/sh.c (sh_canonicalize_comparison): Add prototype.  New
	function. Rename old function with that name to
	sh_canonicalize_comparison_1 and add new parameter and return
	value.
	(TARGET_CANONICALIZE_COMPARISON): New macro definition.
	* config/sh/sh.h (CANONICALIZE_COMPARISON): Remove macro
	definition.
	* config/spu/spu.c (spu_canonicalize_comparison): New function.
	(TARGET_CANONICALIZE_COMPARISON): New macro definition.
	* config/spu/spu.h (CANONICALIZE_COMPARISON): Remove macro
	definition.


---
 gcc/combine.c                 |    8 !
 gcc/config/alpha/alpha.c      |   32 +++++++
 gcc/config/alpha/alpha.h      |   20 ----
 gcc/config/arm/arm-protos.h   |    1 
 gcc/config/arm/arm.c          |   76 +!!!!!!!!!!!!!!!!
 gcc/config/arm/arm.h          |    3 
 gcc/config/s390/s390-protos.h |    1 
 gcc/config/s390/s390.c        |   24 +++!!
 gcc/config/s390/s390.h        |    4 
 gcc/config/sh/sh-protos.h     |    2 
 gcc/config/sh/sh.c            |  175 ++++++!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 gcc/config/sh/sh.h            |    4 
 gcc/config/spu/spu.c          |   20 ++++
 gcc/config/spu/spu.h          |   12 --
 gcc/doc/tm.texi               |   22 !!!!!
 gcc/doc/tm.texi.in            |   22 !!!!!
 gcc/target.def                |    8 +
 gcc/targhooks.c               |   11 ++
 gcc/targhooks.h               |    2 
 19 files changed, 119 insertions(+), 49 deletions(-), 279 modifications(!)

Index: gcc/combine.c
===================================================================
*** gcc/combine.c.orig
--- gcc/combine.c
*************** try_combine (rtx i3, rtx i2, rtx i1, rtx
*** 2944,2952 ****
  	  compare_code = orig_compare_code = GET_CODE (*cc_use_loc);
  	  compare_code = simplify_compare_const (compare_code,
  						 op0, &op1);
! #ifdef CANONICALIZE_COMPARISON
! 	  CANONICALIZE_COMPARISON (compare_code, op0, op1);
! #endif
  	}
  
        /* Do the rest only if op1 is const0_rtx, which may be the
--- 2944,2950 ----
  	  compare_code = orig_compare_code = GET_CODE (*cc_use_loc);
  	  compare_code = simplify_compare_const (compare_code,
  						 op0, &op1);
! 	  targetm.canonicalize_comparison ((int*)&compare_code, &op0, &op1, 1);
  	}
  
        /* Do the rest only if op1 is const0_rtx, which may be the
*************** simplify_comparison (enum rtx_code code,
*** 11959,11969 ****
  	    }
  	}
  
- #ifdef CANONICALIZE_COMPARISON
    /* If this machine only supports a subset of valid comparisons, see if we
       can convert an unsupported one into a supported one.  */
!   CANONICALIZE_COMPARISON (code, op0, op1);
! #endif
  
    *pop0 = op0;
    *pop1 = op1;
--- 11957,11965 ----
  	    }
  	}
  
    /* If this machine only supports a subset of valid comparisons, see if we
       can convert an unsupported one into a supported one.  */
!   targetm.canonicalize_comparison ((int*)&code, &op0, &op1, 0);
  
    *pop0 = op0;
    *pop1 = op1;
Index: gcc/config/s390/s390.c
===================================================================
*** gcc/config/s390/s390.c.orig
--- gcc/config/s390/s390.c
*************** s390_select_ccmode (enum rtx_code code, 
*** 745,753 ****
  /* Replace the comparison OP0 CODE OP1 by a semantically equivalent one
     that we can implement more efficiently.  */
  
! void
! s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
  {
    /* Convert ZERO_EXTRACT back to AND to enable TM patterns.  */
    if ((*code == EQ || *code == NE)
        && *op1 == const0_rtx
--- 745,759 ----
  /* Replace the comparison OP0 CODE OP1 by a semantically equivalent one
     that we can implement more efficiently.  */
  
! static bool
! s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
! 			      bool op0_preserve_value)
  {
+   bool modified = false;
+ 
+   if (op0_preserve_value)
+     return false;
+ 
    /* Convert ZERO_EXTRACT back to AND to enable TM patterns.  */
    if ((*code == EQ || *code == NE)
        && *op1 == const0_rtx
*************** s390_canonicalize_comparison (enum rtx_c
*** 771,776 ****
--- 777,783 ----
  
  	  *op0 = gen_rtx_AND (GET_MODE (inner), inner,
  			      gen_int_mode (block, GET_MODE (inner)));
+ 	  modified = true;
  	}
      }
  
*************** s390_canonicalize_comparison (enum rtx_c
*** 805,810 ****
--- 812,818 ----
  	      mask = gen_int_mode (s390_extract_part (mask, QImode, 0), QImode);
  	      inner = adjust_address_nv (inner, QImode, part);
  	      *op0 = gen_rtx_AND (QImode, inner, mask);
+ 	      modified = true;
  	    }
  	}
      }
*************** s390_canonicalize_comparison (enum rtx_c
*** 819,824 ****
--- 827,833 ----
      {
        *op0 = gen_lowpart (HImode, *op0);
        *op1 = constm1_rtx;
+       modified = true;
      }
  
    /* Remove redundant UNSPEC_CCU_TO_INT conversions if possible.  */
*************** s390_canonicalize_comparison (enum rtx_c
*** 846,851 ****
--- 855,861 ----
  	{
  	  *op0 = XVECEXP (*op0, 0, 0);
  	  *code = new_code;
+ 	  modified = true;
  	}
      }
  
*************** s390_canonicalize_comparison (enum rtx_c
*** 870,875 ****
--- 880,886 ----
  	{
  	  *op0 = XVECEXP (*op0, 0, 0);
  	  *code = new_code;
+ 	  modified = true;
  	}
      }
  
*************** s390_canonicalize_comparison (enum rtx_c
*** 888,901 ****
        else
  	*code = NE;
        *op0 = XEXP (*op0, 0);
      }
  
    /* Prefer register over memory as first operand.  */
    if (MEM_P (*op0) && REG_P (*op1))
      {
        rtx tem = *op0; *op0 = *op1; *op1 = tem;
!       *code = swap_condition (*code);
      }
  }
  
  /* Emit a compare instruction suitable to implement the comparison
--- 899,916 ----
        else
  	*code = NE;
        *op0 = XEXP (*op0, 0);
+       modified = true;
      }
  
    /* Prefer register over memory as first operand.  */
    if (MEM_P (*op0) && REG_P (*op1))
      {
        rtx tem = *op0; *op0 = *op1; *op1 = tem;
!       *code = (int)swap_condition ((enum rtx_code)*code);
!       modified = true;
      }
+ 
+   return modified;
  }
  
  /* Emit a compare instruction suitable to implement the comparison
*************** s390_loop_unroll_adjust (unsigned nunrol
*** 11071,11076 ****
--- 11086,11094 ----
  #undef TARGET_UNWIND_WORD_MODE
  #define TARGET_UNWIND_WORD_MODE s390_unwind_word_mode
  
+ #undef TARGET_CANONICALIZE_COMPARISON
+ #define TARGET_CANONICALIZE_COMPARISON s390_canonicalize_comparison
+ 
  struct gcc_target targetm = TARGET_INITIALIZER;
  
  #include "gt-s390.h"
Index: gcc/target.def
===================================================================
*** gcc/target.def.orig
--- gcc/target.def
*************** DEFHOOK
*** 2877,2882 ****
--- 2877,2890 ----
   enum unwind_info_type, (void),
   default_debug_unwind_info)
  
+ /* The code parameter should be of type enum rtx_code but this is not
+    defined at this time.  */
+ DEFHOOK
+ (canonicalize_comparison,
+  "",
+  bool, (int *code, rtx *op0, rtx *op1, bool op0_preserve_value),
+  default_canonicalize_comparison)
+ 
  DEFHOOKPOD
  (atomic_test_and_set_trueval,
   "This value should be set if the result written by\
Index: gcc/targhooks.c
===================================================================
*** gcc/targhooks.c.orig
--- gcc/targhooks.c
*************** default_debug_unwind_info (void)
*** 1414,1419 ****
--- 1414,1430 ----
    return UI_NONE;
  }
  
+ /* For targets where comparisons might need some additional
+    transformations in order to become valid or more efficient.  */
+ bool
+ default_canonicalize_comparison (int *code ATTRIBUTE_UNUSED,
+ 				 rtx *op0 ATTRIBUTE_UNUSED,
+ 				 rtx *op1 ATTRIBUTE_UNUSED,
+ 				 bool op0_preserve_value ATTRIBUTE_UNUSED)
+ {
+   return false;
+ }
+ 
  /* To be used by targets where reg_raw_mode doesn't return the right
     mode for registers used in apply_builtin_return and apply_builtin_arg.  */
  
Index: gcc/targhooks.h
===================================================================
*** gcc/targhooks.h.orig
--- gcc/targhooks.h
*************** extern unsigned char default_class_max_n
*** 179,184 ****
--- 179,186 ----
  
  extern enum unwind_info_type default_debug_unwind_info (void);
  
+ extern bool default_canonicalize_comparison (int *, rtx *, rtx *, bool);
+ 
  extern int default_label_align_after_barrier_max_skip (rtx);
  extern int default_loop_align_max_skip (rtx);
  extern int default_label_align_max_skip (rtx);
Index: gcc/doc/tm.texi.in
===================================================================
*** gcc/doc/tm.texi.in.orig
--- gcc/doc/tm.texi.in
*************** You should define this macro if and only
*** 5930,5954 ****
  in @file{@var{machine}-modes.def}.
  @end defmac
  
! @defmac CANONICALIZE_COMPARISON (@var{code}, @var{op0}, @var{op1})
  On some machines not all possible comparisons are defined, but you can
  convert an invalid comparison into a valid one.  For example, the Alpha
  does not have a @code{GT} comparison, but you can use an @code{LT}
  comparison instead and swap the order of the operands.
  
! On such machines, define this macro to be a C statement to do any
! required conversions.  @var{code} is the initial comparison code
! and @var{op0} and @var{op1} are the left and right operands of the
! comparison, respectively.  You should modify @var{code}, @var{op0}, and
! @var{op1} as required.
  
  GCC will not assume that the comparison resulting from this macro is
  valid but will see if the resulting insn matches a pattern in the
  @file{md} file.
  
! You need not define this macro if it would never change the comparison
! code or operands.
! @end defmac
  
  @defmac REVERSIBLE_CC_MODE (@var{mode})
  A C expression whose value is one if it is always safe to reverse a
--- 5930,5958 ----
  in @file{@var{machine}-modes.def}.
  @end defmac
  
! @hook TARGET_CANONICALIZE_COMPARISON (@var{code}, @var{op0}, @var{op1}, @var{op0_preserve_value})
  On some machines not all possible comparisons are defined, but you can
  convert an invalid comparison into a valid one.  For example, the Alpha
  does not have a @code{GT} comparison, but you can use an @code{LT}
  comparison instead and swap the order of the operands.
  
! On such machines, implement this hook to do any required conversions.
! @var{code} is the initial comparison code and @var{op0} and @var{op1}
! are the left and right operands of the comparison, respectively.  If
! @var{op0_preserve_value} is @code{true} the implementation is not
! allowed to change the value of @var{op0} since the value might be used
! in RTXs which aren't comparisons.  E.g. the implementation is not
! allowed to swap operands in that case.
! 
! The hook should return @code{true} if any modification was done.
  
  GCC will not assume that the comparison resulting from this macro is
  valid but will see if the resulting insn matches a pattern in the
  @file{md} file.
  
! You need not to implement this hook if it would never change the
! comparison code or operands.
! @end deftypefn
  
  @defmac REVERSIBLE_CC_MODE (@var{mode})
  A C expression whose value is one if it is always safe to reverse a
Index: gcc/config/arm/arm.c
===================================================================
*** gcc/config/arm/arm.c.orig
--- gcc/config/arm/arm.c
*************** static int arm_cortex_a5_branch_cost (bo
*** 269,275 ****
  
  static bool arm_vectorize_vec_perm_const_ok (enum machine_mode vmode,
  					     const unsigned char *sel);
! 
  \f
  /* Table of machine attributes.  */
  static const struct attribute_spec arm_attribute_table[] =
--- 269,276 ----
  
  static bool arm_vectorize_vec_perm_const_ok (enum machine_mode vmode,
  					     const unsigned char *sel);
! static bool arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
! 					 bool op0_preserve_value);
  \f
  /* Table of machine attributes.  */
  static const struct attribute_spec arm_attribute_table[] =
*************** static const struct attribute_spec arm_a
*** 626,631 ****
--- 627,636 ----
  #define TARGET_VECTORIZE_VEC_PERM_CONST_OK \
    arm_vectorize_vec_perm_const_ok
  
+ #undef TARGET_CANONICALIZE_COMPARISON
+ #define TARGET_CANONICALIZE_COMPARISON \
+   arm_canonicalize_comparison
+ 
  struct gcc_target targetm = TARGET_INITIALIZER;
  \f
  /* Obstack for minipool constant handling.  */
*************** arm_gen_constant (enum rtx_code code, en
*** 3543,3553 ****
     This can be done for a few constant compares, where we can make the
     immediate value easier to load.  */
  
! enum rtx_code
! arm_canonicalize_comparison (enum rtx_code code, rtx *op0, rtx *op1)
  {
    enum machine_mode mode;
    unsigned HOST_WIDE_INT i, maxval;
  
    mode = GET_MODE (*op0);
    if (mode == VOIDmode)
--- 3548,3560 ----
     This can be done for a few constant compares, where we can make the
     immediate value easier to load.  */
  
! static bool
! arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
! 			     bool op0_preserve_value)
  {
    enum machine_mode mode;
    unsigned HOST_WIDE_INT i, maxval;
+   bool modified = false;
  
    mode = GET_MODE (*op0);
    if (mode == VOIDmode)
*************** arm_canonicalize_comparison (enum rtx_co
*** 3563,3577 ****
      {
        rtx tem;
  
!       if (code == GT || code == LE
! 	  || (!TARGET_ARM && (code == GTU || code == LEU)))
  	{
  	  /* Missing comparison.  First try to use an available
  	     comparison.  */
  	  if (CONST_INT_P (*op1))
  	    {
  	      i = INTVAL (*op1);
! 	      switch (code)
  		{
  		case GT:
  		case LE:
--- 3570,3584 ----
      {
        rtx tem;
  
!       if (*code == GT || *code == LE
! 	  || (!TARGET_ARM && (*code == GTU || *code == LEU)))
  	{
  	  /* Missing comparison.  First try to use an available
  	     comparison.  */
  	  if (CONST_INT_P (*op1))
  	    {
  	      i = INTVAL (*op1);
! 	      switch (*code)
  		{
  		case GT:
  		case LE:
*************** arm_canonicalize_comparison (enum rtx_co
*** 3579,3585 ****
  		      && arm_const_double_by_immediates (GEN_INT (i + 1)))
  		    {
  		      *op1 = GEN_INT (i + 1);
! 		      return code == GT ? GE : LT;
  		    }
  		  break;
  		case GTU:
--- 3586,3593 ----
  		      && arm_const_double_by_immediates (GEN_INT (i + 1)))
  		    {
  		      *op1 = GEN_INT (i + 1);
! 		      *code = *code == GT ? GE : LT;
! 		      modified = true;
  		    }
  		  break;
  		case GTU:
*************** arm_canonicalize_comparison (enum rtx_co
*** 3588,3594 ****
  		      && arm_const_double_by_immediates (GEN_INT (i + 1)))
  		    {
  		      *op1 = GEN_INT (i + 1);
! 		      return code == GTU ? GEU : LTU;
  		    }
  		  break;
  		default:
--- 3596,3603 ----
  		      && arm_const_double_by_immediates (GEN_INT (i + 1)))
  		    {
  		      *op1 = GEN_INT (i + 1);
! 		      *code = *code == GTU ? GEU : LTU;
! 		      modified = true;
  		    }
  		  break;
  		default:
*************** arm_canonicalize_comparison (enum rtx_co
*** 3597,3646 ****
  	    }
  
  	  /* If that did not work, reverse the condition.  */
! 	  tem = *op0;
! 	  *op0 = *op1;
! 	  *op1 = tem;
! 	  return swap_condition (code);
  	}
! 
!       return code;
      }
  
    /* If *op0 is (zero_extend:SI (subreg:QI (reg:SI) 0)) and comparing
       with const0_rtx, change it to (and:SI (reg:SI) (const_int 255)),
       to facilitate possible combining with a cmp into 'ands'.  */
!   if (mode == SImode
        && GET_CODE (*op0) == ZERO_EXTEND
        && GET_CODE (XEXP (*op0, 0)) == SUBREG
        && GET_MODE (XEXP (*op0, 0)) == QImode
        && GET_MODE (SUBREG_REG (XEXP (*op0, 0))) == SImode
        && subreg_lowpart_p (XEXP (*op0, 0))
        && *op1 == const0_rtx)
!     *op0 = gen_rtx_AND (SImode, SUBREG_REG (XEXP (*op0, 0)),
! 			GEN_INT (255));
  
    /* Comparisons smaller than DImode.  Only adjust comparisons against
       an out-of-range constant.  */
    if (!CONST_INT_P (*op1)
        || const_ok_for_arm (INTVAL (*op1))
        || const_ok_for_arm (- INTVAL (*op1)))
!     return code;
  
    i = INTVAL (*op1);
  
!   switch (code)
      {
      case EQ:
      case NE:
!       return code;
! 
      case GT:
      case LE:
        if (i != maxval
  	  && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
  	{
  	  *op1 = GEN_INT (i + 1);
! 	  return code == GT ? GE : LT;
  	}
        break;
  
--- 3606,3662 ----
  	    }
  
  	  /* If that did not work, reverse the condition.  */
! 	  if (!modified && !op0_preserve_value)
! 	    {
! 	      tem = *op0;
! 	      *op0 = *op1;
! 	      *op1 = tem;
! 	      *code = (int)swap_condition ((enum rtx_code)*code);
! 	      modified = true;
! 	    }
  	}
!       return modified;
      }
  
    /* If *op0 is (zero_extend:SI (subreg:QI (reg:SI) 0)) and comparing
       with const0_rtx, change it to (and:SI (reg:SI) (const_int 255)),
       to facilitate possible combining with a cmp into 'ands'.  */
!   if (!op0_preserve_value
!       && mode == SImode
        && GET_CODE (*op0) == ZERO_EXTEND
        && GET_CODE (XEXP (*op0, 0)) == SUBREG
        && GET_MODE (XEXP (*op0, 0)) == QImode
        && GET_MODE (SUBREG_REG (XEXP (*op0, 0))) == SImode
        && subreg_lowpart_p (XEXP (*op0, 0))
        && *op1 == const0_rtx)
!     {
!       *op0 = gen_rtx_AND (SImode, SUBREG_REG (XEXP (*op0, 0)),
! 			  GEN_INT (255));
!       modified = true;
!     }
  
    /* Comparisons smaller than DImode.  Only adjust comparisons against
       an out-of-range constant.  */
    if (!CONST_INT_P (*op1)
        || const_ok_for_arm (INTVAL (*op1))
        || const_ok_for_arm (- INTVAL (*op1)))
!     return modified;
  
    i = INTVAL (*op1);
  
!   switch (*code)
      {
      case EQ:
      case NE:
!       break;
      case GT:
      case LE:
        if (i != maxval
  	  && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
  	{
  	  *op1 = GEN_INT (i + 1);
! 	  *code = *code == GT ? GE : LT;
! 	  modified = true;
  	}
        break;
  
*************** arm_canonicalize_comparison (enum rtx_co
*** 3650,3656 ****
  	  && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
  	{
  	  *op1 = GEN_INT (i - 1);
! 	  return code == GE ? GT : LE;
  	}
        break;
  
--- 3666,3673 ----
  	  && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
  	{
  	  *op1 = GEN_INT (i - 1);
! 	  *code = *code == GE ? GT : LE;
! 	  modified = true;
  	}
        break;
  
*************** arm_canonicalize_comparison (enum rtx_co
*** 3660,3666 ****
  	  && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
  	{
  	  *op1 = GEN_INT (i + 1);
! 	  return code == GTU ? GEU : LTU;
  	}
        break;
  
--- 3677,3684 ----
  	  && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
  	{
  	  *op1 = GEN_INT (i + 1);
! 	  *code = *code == GTU ? GEU : LTU;
! 	  modified = true;
  	}
        break;
  
*************** arm_canonicalize_comparison (enum rtx_co
*** 3670,3676 ****
  	  && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
  	{
  	  *op1 = GEN_INT (i - 1);
! 	  return code == GEU ? GTU : LEU;
  	}
        break;
  
--- 3688,3695 ----
  	  && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
  	{
  	  *op1 = GEN_INT (i - 1);
! 	  *code = *code == GEU ? GTU : LEU;
! 	  modified = true;
  	}
        break;
  
*************** arm_canonicalize_comparison (enum rtx_co
*** 3678,3684 ****
        gcc_unreachable ();
      }
  
!   return code;
  }
  
  
--- 3697,3703 ----
        gcc_unreachable ();
      }
  
!   return modified;
  }
  
  
*************** bool
*** 26970,26976 ****
  arm_validize_comparison (rtx *comparison, rtx * op1, rtx * op2)
  {
    enum rtx_code code = GET_CODE (*comparison);
-   enum rtx_code canonical_code;
    enum machine_mode mode = (GET_MODE (*op1) == VOIDmode) 
      ? GET_MODE (*op2) : GET_MODE (*op1);
  
--- 26989,26994 ----
*************** arm_validize_comparison (rtx *comparison
*** 26979,26986 ****
    if (code == UNEQ || code == LTGT)
      return false;
  
!   canonical_code = arm_canonicalize_comparison (code, op1, op2);
!   PUT_CODE (*comparison, canonical_code);
  
    switch (mode)
      {
--- 26997,27004 ----
    if (code == UNEQ || code == LTGT)
      return false;
  
!   arm_canonicalize_comparison ((int*)&code, op1, op2, 0);
!   PUT_CODE (*comparison, code);
  
    switch (mode)
      {
Index: gcc/config/arm/arm.h
===================================================================
*** gcc/config/arm/arm.h.orig
--- gcc/config/arm/arm.h
*************** extern int making_const_table;
*** 2077,2085 ****
     ? reverse_condition_maybe_unordered (code) \
     : reverse_condition (code))
  
- #define CANONICALIZE_COMPARISON(CODE, OP0, OP1)				\
-   (CODE) = arm_canonicalize_comparison (CODE, &(OP0), &(OP1))
- 
  /* The arm5 clz instruction returns 32.  */
  #define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE)  ((VALUE) = 32, 1)
  #define CTZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE)  ((VALUE) = 32, 1)
--- 2077,2082 ----
Index: gcc/config/s390/s390.h
===================================================================
*** gcc/config/s390/s390.h.orig
--- gcc/config/s390/s390.h
*************** do {									\
*** 720,729 ****
     return the mode to be used for the comparison.  */
  #define SELECT_CC_MODE(OP, X, Y) s390_select_ccmode ((OP), (X), (Y))
  
- /* Canonicalize a comparison from one we don't have to one we do have.  */
- #define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \
-   s390_canonicalize_comparison (&(CODE), &(OP0), &(OP1))
- 
  /* Relative costs of operations.  */
  
  /* A C expression for the cost of a branch instruction.  A value of 1
--- 720,725 ----
Index: gcc/config/alpha/alpha.c
===================================================================
*** gcc/config/alpha/alpha.c.orig
--- gcc/config/alpha/alpha.c
*************** alpha_conditional_register_usage (void)
*** 9683,9688 ****
--- 9683,9717 ----
      for (i = 32; i < 63; i++)
        fixed_regs[i] = call_used_regs[i] = 1;
  }
+ 
+ /* Canonicalize a comparison from one we don't have to one we do have.  */
+ 
+ static bool
+ alpha_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
+ 			       bool op0_preserve_value)
+ {
+   bool modified = false;
+ 
+   if (!op0_preserve_value
+       && (*code == GE || *code == GT || *code == GEU || *code == GTU)
+       && (REG_P (*op1) || *op1 == const0_rtx))
+     {
+       rtx tem = *op0;
+       *op0 = *op1;
+       *op1 = tem;
+       *code = (int)swap_condition ((enum rtx_code)*code);
+       modified = true;
+     }
+ 
+   if ((*code == LT || *code == LTU)
+       && CONST_INT_P (*op1) && INTVAL (*op1) == 256)
+     {
+       *code = *code == LT ? LE : LEU;
+       *op1 = GEN_INT (255);
+       modified = true;
+     }
+   return modified;
+ }
  \f
  /* Initialize the GCC target structure.  */
  #if TARGET_ABI_OPEN_VMS
*************** alpha_conditional_register_usage (void)
*** 9850,9855 ****
--- 9879,9887 ----
  #undef TARGET_CONDITIONAL_REGISTER_USAGE
  #define TARGET_CONDITIONAL_REGISTER_USAGE alpha_conditional_register_usage
  
+ #undef TARGET_CANONICALIZE_COMPARISON
+ #define TARGET_CANONICALIZE_COMPARISON alpha_canonicalize_comparison
+ 
  struct gcc_target targetm = TARGET_INITIALIZER;
  
  \f
Index: gcc/config/alpha/alpha.h
===================================================================
*** gcc/config/alpha/alpha.h.orig
--- gcc/config/alpha/alpha.h
*************** do {									     \
*** 921,946 ****
  #define FLOAT_STORE_FLAG_VALUE(MODE) \
    REAL_VALUE_ATOF ((TARGET_FLOAT_VAX ? "0.5" : "2.0"), (MODE))
  
- /* Canonicalize a comparison from one we don't have to one we do have.  */
- 
- #define CANONICALIZE_COMPARISON(CODE,OP0,OP1) \
-   do {									\
-     if (((CODE) == GE || (CODE) == GT || (CODE) == GEU || (CODE) == GTU) \
- 	&& (REG_P (OP1) || (OP1) == const0_rtx))		\
-       {									\
- 	rtx tem = (OP0);						\
- 	(OP0) = (OP1);							\
- 	(OP1) = tem;							\
- 	(CODE) = swap_condition (CODE);					\
-       }									\
-     if (((CODE) == LT || (CODE) == LTU)					\
- 	&& CONST_INT_P (OP1) && INTVAL (OP1) == 256)			\
-       {									\
- 	(CODE) = (CODE) == LT ? LE : LEU;				\
- 	(OP1) = GEN_INT (255);						\
-       }									\
-   } while (0)
- 
  /* Specify the machine mode that pointers have.
     After generation of rtl, the compiler makes no further distinction
     between pointers and any other objects of this machine mode.  */
--- 921,926 ----
Index: gcc/doc/tm.texi
===================================================================
*** gcc/doc/tm.texi.orig
--- gcc/doc/tm.texi
*************** You should define this macro if and only
*** 6026,6050 ****
  in @file{@var{machine}-modes.def}.
  @end defmac
  
! @defmac CANONICALIZE_COMPARISON (@var{code}, @var{op0}, @var{op1})
  On some machines not all possible comparisons are defined, but you can
  convert an invalid comparison into a valid one.  For example, the Alpha
  does not have a @code{GT} comparison, but you can use an @code{LT}
  comparison instead and swap the order of the operands.
  
! On such machines, define this macro to be a C statement to do any
! required conversions.  @var{code} is the initial comparison code
! and @var{op0} and @var{op1} are the left and right operands of the
! comparison, respectively.  You should modify @var{code}, @var{op0}, and
! @var{op1} as required.
  
  GCC will not assume that the comparison resulting from this macro is
  valid but will see if the resulting insn matches a pattern in the
  @file{md} file.
  
! You need not define this macro if it would never change the comparison
! code or operands.
! @end defmac
  
  @defmac REVERSIBLE_CC_MODE (@var{mode})
  A C expression whose value is one if it is always safe to reverse a
--- 6026,6054 ----
  in @file{@var{machine}-modes.def}.
  @end defmac
  
! @deftypefn {Target Hook} bool TARGET_CANONICALIZE_COMPARISON (int *@var{code}, rtx *@var{op0}, rtx *@var{op1}, bool @var{op0_preserve_value}) (@var{code}, @var{op0}, @var{op1}, @var{op0_preserve_value})
  On some machines not all possible comparisons are defined, but you can
  convert an invalid comparison into a valid one.  For example, the Alpha
  does not have a @code{GT} comparison, but you can use an @code{LT}
  comparison instead and swap the order of the operands.
  
! On such machines, implement this hook to do any required conversions.
! @var{code} is the initial comparison code and @var{op0} and @var{op1}
! are the left and right operands of the comparison, respectively.  If
! @var{op0_preserve_value} is @code{true} the implementation is not
! allowed to change the value of @var{op0} since the value might be used
! in RTXs which aren't comparisons.  E.g. the implementation is not
! allowed to swap operands in that case.
! 
! The hook should return @code{true} if any modification was done.
  
  GCC will not assume that the comparison resulting from this macro is
  valid but will see if the resulting insn matches a pattern in the
  @file{md} file.
  
! You need not to implement this hook if it would never change the
! comparison code or operands.
! @end deftypefn
  
  @defmac REVERSIBLE_CC_MODE (@var{mode})
  A C expression whose value is one if it is always safe to reverse a
Index: gcc/config/arm/arm-protos.h
===================================================================
*** gcc/config/arm/arm-protos.h.orig
--- gcc/config/arm/arm-protos.h
*************** extern int const_ok_for_op (HOST_WIDE_IN
*** 53,59 ****
  extern int const_ok_for_dimode_op (HOST_WIDE_INT, enum rtx_code);
  extern int arm_split_constant (RTX_CODE, enum machine_mode, rtx,
  			       HOST_WIDE_INT, rtx, rtx, int);
- extern RTX_CODE arm_canonicalize_comparison (RTX_CODE, rtx *, rtx *);
  extern int legitimate_pic_operand_p (rtx);
  extern rtx legitimize_pic_address (rtx, enum machine_mode, rtx);
  extern rtx legitimize_tls_address (rtx, rtx);
--- 53,58 ----
Index: gcc/config/s390/s390-protos.h
===================================================================
*** gcc/config/s390/s390-protos.h.orig
--- gcc/config/s390/s390-protos.h
*************** extern int tls_symbolic_operand (rtx);
*** 58,64 ****
  extern bool s390_match_ccmode (rtx, enum machine_mode);
  extern enum machine_mode s390_tm_ccmode (rtx, rtx, bool);
  extern enum machine_mode s390_select_ccmode (enum rtx_code, rtx, rtx);
- extern void s390_canonicalize_comparison (enum rtx_code *, rtx *, rtx *);
  extern rtx s390_emit_compare (enum rtx_code, rtx, rtx);
  extern void s390_emit_jump (rtx, rtx);
  extern bool symbolic_reference_mentioned_p (rtx);
--- 58,63 ----
Index: gcc/config/sh/sh-protos.h
===================================================================
*** gcc/config/sh/sh-protos.h.orig
--- gcc/config/sh/sh-protos.h
*************** extern bool sh_expand_t_scc (rtx *);
*** 159,166 ****
  extern rtx sh_gen_truncate (enum machine_mode, rtx, int);
  extern bool sh_vector_mode_supported_p (enum machine_mode);
  extern bool sh_cfun_trap_exit_p (void);
- extern void sh_canonicalize_comparison (enum rtx_code&, rtx&, rtx&,
- 					enum machine_mode mode = VOIDmode);
  extern rtx sh_find_equiv_gbr_addr (rtx cur_insn, rtx mem);
  extern int sh_eval_treg_value (rtx op);
  
--- 159,164 ----
Index: gcc/config/sh/sh.c
===================================================================
*** gcc/config/sh/sh.c.orig
--- gcc/config/sh/sh.c
*************** static int max_mov_insn_displacement (en
*** 314,319 ****
--- 314,320 ----
  static int mov_insn_alignment_mask (enum machine_mode, bool);
  static HOST_WIDE_INT disp_addr_displacement (rtx);
  static bool sequence_insn_p (rtx);
+ static bool sh_canonicalize_comparison (int *, rtx *, rtx *, bool);
  
  static void sh_init_sync_libfuncs (void) ATTRIBUTE_UNUSED;
  \f
*************** static const struct attribute_spec sh_at
*** 586,591 ****
--- 587,595 ----
  #undef TARGET_LEGITIMATE_CONSTANT_P
  #define TARGET_LEGITIMATE_CONSTANT_P	sh_legitimate_constant_p
  
+ #undef TARGET_CANONICALIZE_COMPARISON
+ #define TARGET_CANONICALIZE_COMPARISON	sh_canonicalize_comparison
+ 
  /* Machine-specific symbol_ref flags.  */
  #define SYMBOL_FLAG_FUNCVEC_FUNCTION    (SYMBOL_FLAG_MACH_DEP << 0)
  
*************** prepare_move_operands (rtx operands[], e
*** 1909,1921 ****
      }
  }
  
! /* Implement the CANONICALIZE_COMPARISON macro for the combine pass.
!    This function is also re-used to canonicalize comparisons in cbranch
!    pattern expanders.  */
! void
! sh_canonicalize_comparison (enum rtx_code& cmp, rtx& op0, rtx& op1,
! 			    enum machine_mode mode)
  {
    /* When invoked from within the combine pass the mode is not specified,
       so try to get it from one of the operands.  */
    if (mode == VOIDmode)
--- 1913,1930 ----
      }
  }
  
! /* Implement the canonicalize_comparison target hook for the combine
!    pass.  For the target hook this function is invoked via
!    sh_canonicalize_comparison.  This function is also re-used to
!    canonicalize comparisons in cbranch pattern expanders.  */
! static bool
! sh_canonicalize_comparison_1 (enum rtx_code& cmp, rtx& op0, rtx& op1,
! 			      enum machine_mode mode,
! 			      bool op0_preserve_value ATTRIBUTE_UNUSED)
  {
+   bool modified = false;
+   HOST_WIDE_INT val;
+ 
    /* When invoked from within the combine pass the mode is not specified,
       so try to get it from one of the operands.  */
    if (mode == VOIDmode)
*************** sh_canonicalize_comparison (enum rtx_cod
*** 1925,2011 ****
  
    // We need to have a mode to do something useful here.
    if (mode == VOIDmode)
!     return;
  
    // Currently, we don't deal with floats here.
    if (GET_MODE_CLASS (mode) == MODE_FLOAT)
!     return;
  
    // Make sure that the constant operand is the second operand.
    if (CONST_INT_P (op0) && !CONST_INT_P (op1))
      {
        std::swap (op0, op1);
        cmp = swap_condition (cmp);
      }
  
!   if (CONST_INT_P (op1))
      {
!       /* Try to adjust the constant operand in such a way that available
!          comparison insns can be utilized better and the constant can be
!          loaded with a 'mov #imm,Rm' insn.  This avoids a load from the
!          constant pool.  */
!       const HOST_WIDE_INT val = INTVAL (op1);
! 
!       /* x > -1		  --> x >= 0
! 	 x > 0xFFFFFF7F	  --> x >= 0xFFFFFF80
! 	 x <= -1	  --> x < 0
! 	 x <= 0xFFFFFF7F  --> x < 0xFFFFFF80  */
!       if ((val == -1 || val == -0x81) && (cmp == GT || cmp == LE))
! 	{
! 	  cmp = cmp == GT ? GE : LT;
! 	  op1 = gen_int_mode (val + 1, mode);
!         }
  
!       /* x >= 1     --> x > 0
! 	 x >= 0x80  --> x > 0x7F
! 	 x < 1      --> x <= 0
! 	 x < 0x80   --> x <= 0x7F  */
!       else if ((val == 1 || val == 0x80) && (cmp == GE || cmp == LT))
! 	{
! 	  cmp = cmp == GE ? GT : LE;
! 	  op1 = gen_int_mode (val - 1, mode);
! 	}
  
!       /* unsigned x >= 1  --> x != 0
! 	 unsigned x < 1   --> x == 0  */
!       else if (val == 1 && (cmp == GEU || cmp == LTU))
! 	{
! 	  cmp = cmp == GEU ? NE : EQ;
! 	  op1 = CONST0_RTX (mode);
! 	}
  
!       /* unsigned x >= 0x80  --> unsigned x > 0x7F
! 	 unsigned x < 0x80   --> unsigned x < 0x7F  */
!       else if (val == 0x80 && (cmp == GEU || cmp == LTU))
! 	{
! 	  cmp = cmp == GEU ? GTU : LEU;
! 	  op1 = gen_int_mode (val - 1, mode);
! 	}
  
!       /* unsigned x > 0   --> x != 0
! 	 unsigned x <= 0  --> x == 0  */
!       else if (val == 0 && (cmp == GTU || cmp == LEU))
! 	cmp = cmp == GTU ? NE : EQ;
  
!       /* unsigned x > 0x7FFFFFFF   --> signed x < 0
! 	 unsigned x <= 0x7FFFFFFF  --> signed x >= 0  */
!       else if (mode == SImode && (cmp == GTU || cmp == LEU)
! 	       && val == 0x7FFFFFFF)
! 	{
! 	  cmp = cmp == GTU ? LT : GE;
! 	  op1 = const0_rtx;
! 	}
  
!       /* unsigned x >= 0x80000000  --> signed x < 0
! 	 unsigned x < 0x80000000   --> signed x >= 0  */
!       else if (mode == SImode && (cmp == GEU || cmp == LTU)
! 	       && (unsigned HOST_WIDE_INT)val
! 		   == ((unsigned HOST_WIDE_INT)0x7FFFFFFF + 1))
! 	{
! 	  cmp = cmp == GEU ? LT : GE;
! 	  op1 = const0_rtx;
! 	}
      }
  }
  
  enum rtx_code
--- 1934,2047 ----
  
    // We need to have a mode to do something useful here.
    if (mode == VOIDmode)
!     return false;
  
    // Currently, we don't deal with floats here.
    if (GET_MODE_CLASS (mode) == MODE_FLOAT)
!     return false;
  
    // Make sure that the constant operand is the second operand.
    if (CONST_INT_P (op0) && !CONST_INT_P (op1))
      {
        std::swap (op0, op1);
        cmp = swap_condition (cmp);
+       modified = true;
      }
  
!   if (!CONST_INT_P (op1))
!     return modified;
! 
!   /* Try to adjust the constant operand in such a way that available
!      comparison insns can be utilized better and the constant can be
!      loaded with a 'mov #imm,Rm' insn.  This avoids a load from the
!      constant pool.  */
!   val = INTVAL (op1);
! 
!   /* x > -1		  --> x >= 0
!      x > 0xFFFFFF7F	  --> x >= 0xFFFFFF80
!      x <= -1	  --> x < 0
!      x <= 0xFFFFFF7F  --> x < 0xFFFFFF80  */
!   if ((val == -1 || val == -0x81) && (cmp == GT || cmp == LE))
      {
!       cmp = cmp == GT ? GE : LT;
!       op1 = gen_int_mode (val + 1, mode);
!       return true;
!     }
  
!   /* x >= 1     --> x > 0
!      x >= 0x80  --> x > 0x7F
!      x < 1      --> x <= 0
!      x < 0x80   --> x <= 0x7F  */
!   else if ((val == 1 || val == 0x80) && (cmp == GE || cmp == LT))
!     {
!       cmp = cmp == GE ? GT : LE;
!       op1 = gen_int_mode (val - 1, mode);
!       return true;
!     }
  
!   /* unsigned x >= 1  --> x != 0
!      unsigned x < 1   --> x == 0  */
!   else if (val == 1 && (cmp == GEU || cmp == LTU))
!     {
!       cmp = cmp == GEU ? NE : EQ;
!       op1 = CONST0_RTX (mode);
!       return true;
!     }
  
!   /* unsigned x >= 0x80  --> unsigned x > 0x7F
!      unsigned x < 0x80   --> unsigned x < 0x7F  */
!   else if (val == 0x80 && (cmp == GEU || cmp == LTU))
!     {
!       cmp = cmp == GEU ? GTU : LEU;
!       op1 = gen_int_mode (val - 1, mode);
!       return true;
!     }
  
!   /* unsigned x > 0   --> x != 0
!      unsigned x <= 0  --> x == 0  */
!   else if (val == 0 && (cmp == GTU || cmp == LEU))
!     {
!       cmp = cmp == GTU ? NE : EQ;
!       return true;
!     }
  
!   /* unsigned x > 0x7FFFFFFF   --> signed x < 0
!      unsigned x <= 0x7FFFFFFF  --> signed x >= 0  */
!   else if (mode == SImode && (cmp == GTU || cmp == LEU)
! 	   && val == 0x7FFFFFFF)
!     {
!       cmp = cmp == GTU ? LT : GE;
!       op1 = const0_rtx;
!       return true;
!     }
  
!   /* unsigned x >= 0x80000000  --> signed x < 0
!      unsigned x < 0x80000000   --> signed x >= 0  */
!   else if (mode == SImode && (cmp == GEU || cmp == LTU)
! 	   && (unsigned HOST_WIDE_INT)val
! 	   == ((unsigned HOST_WIDE_INT)0x7FFFFFFF + 1))
!     {
!       cmp = cmp == GEU ? LT : GE;
!       op1 = const0_rtx;
!       return true;
      }
+   return modified;
+ }
+ 
+ /* This function implements the canonicalize_comparison target hook.
+    This wrapper around the internally used
+    sh_canonicalize_comparison_1 function is needed to do the enum
+    rtx_code <-> int conversion.  Target hooks cannot use enum rtx_code
+    in its definition.  */
+ static bool
+ sh_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
+ 			    bool op0_preserve_value)
+ {
+   enum rtx_code tmp_code = (enum rtx_code)*code;
+   bool modified = sh_canonicalize_comparison_1 (tmp_code, *op0, *op1,
+ 						VOIDmode, op0_preserve_value);
+   *code = (int)tmp_code;
+   return modified;
  }
  
  enum rtx_code
*************** prepare_cbranch_operands (rtx *operands,
*** 2021,2027 ****
    else
      scratch = operands[4];
  
!   sh_canonicalize_comparison (comparison, operands[1], operands[2], mode);
  
    /* Notice that this function is also invoked after reload by
       the cbranchdi4_i pattern, through expand_cbranchdi4.  */
--- 2057,2064 ----
    else
      scratch = operands[4];
  
!   sh_canonicalize_comparison_1 (comparison, operands[1], operands[2],
! 				mode, false);
  
    /* Notice that this function is also invoked after reload by
       the cbranchdi4_i pattern, through expand_cbranchdi4.  */
Index: gcc/config/sh/sh.h
===================================================================
*** gcc/config/sh/sh.h.orig
--- gcc/config/sh/sh.h
*************** struct sh_args {
*** 1872,1881 ****
     more compact code.  */
  #define SHIFT_COUNT_TRUNCATED (0)
  
- /* CANONICALIZE_COMPARISON macro for the combine pass.  */
- #define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \
-   sh_canonicalize_comparison ((CODE), (OP0), (OP1))
- 
  /* All integers have the same format so truncation is easy.  */
  /* But SHmedia must sign-extend DImode when truncating to SImode.  */
  #define TRULY_NOOP_TRUNCATION(OUTPREC,INPREC) \
--- 1872,1877 ----
Index: gcc/config/spu/spu.c
===================================================================
*** gcc/config/spu/spu.c.orig
--- gcc/config/spu/spu.c
*************** spu_output_mi_thunk (FILE *file, tree th
*** 7095,7100 ****
--- 7095,7117 ----
    final_end_function ();
  }
  
+ /* Canonicalize a comparison from one we don't have to one we do have.  */
+ static bool
+ spu_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
+ 			     bool op0_preserve_value)
+ {
+   if (!op0_preserve_value
+       && (*code == LE || *code == LT || *code == LEU || *code == LTU))
+     {
+       rtx tem = *op0;
+       *op0 = *op1;
+       *op1 = tem;
+       *code = (int)swap_condition ((enum rtx_code)*code);
+       return true;
+     }
+   return false;
+ }
+ 
  \f
  /*  Table of machine attributes.  */
  static const struct attribute_spec spu_attribute_table[] =
*************** static const struct attribute_spec spu_a
*** 7308,7313 ****
--- 7325,7333 ----
  #undef TARGET_DELAY_VARTRACK
  #define TARGET_DELAY_VARTRACK true
  
+ #undef TARGET_CANONICALIZE_COMPARISON
+ #define TARGET_CANONICALIZE_COMPARISON spu_canonicalize_comparison
+ 
  struct gcc_target targetm = TARGET_INITIALIZER;
  
  #include "gt-spu.h"
Index: gcc/config/spu/spu.h
===================================================================
*** gcc/config/spu/spu.h.orig
--- gcc/config/spu/spu.h
*************** do {									\
*** 520,537 ****
  
  #define NO_IMPLICIT_EXTERN_C 1
  
- /* Canonicalize a comparison from one we don't have to one we do have.  */
- #define CANONICALIZE_COMPARISON(CODE,OP0,OP1) \
-   do {                                                                    \
-     if (((CODE) == LE || (CODE) == LT || (CODE) == LEU || (CODE) == LTU)) \
-       {                                                                   \
-         rtx tem = (OP0);                                                  \
-         (OP0) = (OP1);                                                    \
-         (OP1) = tem;                                                      \
-         (CODE) = swap_condition (CODE);                                   \
-       }                                                                   \
-   } while (0)
- 
  
  /* Address spaces.  */
  #define ADDR_SPACE_EA	1
--- 520,525 ----

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2012-12-19 18:57 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-12-12 11:23 [PATCH] Bugfix: Additional parameter for canonicalize comparison Andreas Krebbel
2012-12-19  9:53 ` [PING] " Andreas Krebbel
2012-12-19 11:34   ` Richard Earnshaw
2012-12-19 14:04     ` Richard Biener
2012-12-19 14:05       ` Richard Biener
2012-12-19 18:57       ` Jan-Benedict Glaw
  -- strict thread matches above, loose matches on Subject: below --
2012-12-06  8:25 [PATCH] " Andreas Krebbel
2012-12-07 19:37 ` Richard Henderson
2012-12-08  7:27   ` Oleg Endo
2012-12-10  9:51     ` Andreas Krebbel
2012-12-10 19:23       ` Richard Henderson
2012-12-10 19:54         ` Oleg Endo
2012-12-10 23:13       ` Kaz Kojima
2012-12-11  8:55         ` Andreas Krebbel
2012-12-11  9:16           ` Kaz Kojima
2012-12-11 10:15             ` Andreas Krebbel
2012-12-11 10:35               ` Oleg Endo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).