+2012-12-19 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.
+
2012-12-19 Jakub Jelinek <jakub@redhat.com>
PR debug/55730
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
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
+ target_canonicalize_comparison (&compare_code, &op0, &op1, 1);
}
/* Do the rest only if op1 is const0_rtx, which may be the
}
}
-#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
+ target_canonicalize_comparison (&code, &op0, &op1, 0);
*pop0 = op0;
*pop1 = op1;
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
#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
#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. */
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);
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[] =
#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. */
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)
+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;
{
rtx tem;
- if (code == GT || code == LE
- || (!TARGET_ARM && (code == GTU || code == LEU)))
+ 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)
+ switch (*code)
{
case GT:
case LE:
&& arm_const_double_by_immediates (GEN_INT (i + 1)))
{
*op1 = GEN_INT (i + 1);
- return code == GT ? GE : LT;
+ *code = *code == GT ? GE : LT;
+ return;
}
break;
case GTU:
&& arm_const_double_by_immediates (GEN_INT (i + 1)))
{
*op1 = GEN_INT (i + 1);
- return code == GTU ? GEU : LTU;
+ *code = *code == GTU ? GEU : LTU;
+ return;
}
break;
default:
}
/* If that did not work, reverse the condition. */
- tem = *op0;
- *op0 = *op1;
- *op1 = tem;
- return swap_condition (code);
+ if (!op0_preserve_value)
+ {
+ tem = *op0;
+ *op0 = *op1;
+ *op1 = tem;
+ *code = (int)swap_condition ((enum rtx_code)*code);
+ }
}
-
- return 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 (mode == SImode
+ if (!op0_preserve_value
+ && mode == SImode
&& GET_CODE (*op0) == ZERO_EXTEND
&& GET_CODE (XEXP (*op0, 0)) == SUBREG
&& GET_MODE (XEXP (*op0, 0)) == QImode
if (!CONST_INT_P (*op1)
|| const_ok_for_arm (INTVAL (*op1))
|| const_ok_for_arm (- INTVAL (*op1)))
- return code;
+ return;
i = INTVAL (*op1);
- switch (code)
+ switch (*code)
{
case EQ:
case NE:
- return code;
+ return;
case GT:
case LE:
&& (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
{
*op1 = GEN_INT (i + 1);
- return code == GT ? GE : LT;
+ *code = *code == GT ? GE : LT;
+ return;
}
break;
&& (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
{
*op1 = GEN_INT (i - 1);
- return code == GE ? GT : LE;
+ *code = *code == GE ? GT : LE;
+ return;
}
break;
&& (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
{
*op1 = GEN_INT (i + 1);
- return code == GTU ? GEU : LTU;
+ *code = *code == GTU ? GEU : LTU;
+ return;
}
break;
&& (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
{
*op1 = GEN_INT (i - 1);
- return code == GEU ? GTU : LEU;
+ *code = *code == GEU ? GTU : LEU;
+ return;
}
break;
default:
gcc_unreachable ();
}
-
- return code;
}
arm_validize_comparison (rtx *comparison, rtx * op1, rtx * op2)
{
enum rtx_code code = GET_CODE (*comparison);
- enum rtx_code canonical_code;
+ int code_int;
enum machine_mode mode = (GET_MODE (*op1) == VOIDmode)
? GET_MODE (*op2) : GET_MODE (*op1);
if (code == UNEQ || code == LTGT)
return false;
- canonical_code = arm_canonicalize_comparison (code, op1, op2);
- PUT_CODE (*comparison, canonical_code);
+ code_int = (int)code;
+ arm_canonicalize_comparison (&code_int, op1, op2, 0);
+ PUT_CODE (*comparison, (enum rtx_code)code_int);
switch (mode)
{
? 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)
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);
/* 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)
+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
if (MEM_P (*op0) && REG_P (*op1))
{
rtx tem = *op0; *op0 = *op1; *op1 = tem;
- *code = swap_condition (*code);
+ *code = (int)swap_condition ((enum rtx_code)*code);
}
}
#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"
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
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);
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
#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)
}
}
-/* Implement the CANONICALIZE_COMPARISON macro for the combine pass.
- This function is also re-used to canonicalize comparisons in cbranch
- pattern expanders. */
-void
+/* 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)
+ 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. */
}
}
+/* 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)
else
scratch = operands[4];
- sh_canonicalize_comparison (comparison, operands[1], operands[2], mode);
+ 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. */
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) \
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[] =
#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"
#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
in @file{@var{machine}-modes.def}.
@end defmac
-@defmac CANONICALIZE_COMPARISON (@var{code}, @var{op0}, @var{op1})
+@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, 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.
+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 define this macro if it would never change the comparison
-code or operands.
-@end defmac
+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
in @file{@var{machine}-modes.def}.
@end defmac
-@defmac CANONICALIZE_COMPARISON (@var{code}, @var{op0}, @var{op1})
+@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, 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.
+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 define this macro if it would never change the comparison
-code or operands.
-@end defmac
+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
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\
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);