102 lines
3.4 KiB
Diff
102 lines
3.4 KiB
Diff
|
The cprop_hardreg pass is built around the assumption that accessing a
|
||
|
register in a narrower mode is the same as accessing the lowpart of
|
||
|
the register. This unfortunately is not true for vector registers on
|
||
|
IBM Z. This caused a miscompile of LLVM with GCC 8.5. The problem
|
||
|
could not be reproduced with upstream GCC unfortunately but we have to
|
||
|
assume that it is latent there. The right fix would require
|
||
|
substantial changes to the cprop pass and is certainly something we
|
||
|
would want for our platform. But since this would not be acceptable
|
||
|
for older GCCs I'll go with what Vladimir proposed in the RedHat BZ
|
||
|
and introduce a hopefully temporary and undocumented target hook to
|
||
|
disable that specific transformation in regcprop.c.
|
||
|
|
||
|
--- a/gcc/config/s390/s390.c
|
||
|
+++ b/gcc/config/s390/s390.c
|
||
|
@@ -10488,6 +10488,18 @@ s390_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
+/* Implement TARGET_NARROW_MODE_REFERS_LOW_PART_P. */
|
||
|
+
|
||
|
+static bool
|
||
|
+s390_narrow_mode_refers_low_part_p (unsigned int regno)
|
||
|
+{
|
||
|
+ if (reg_classes_intersect_p (VEC_REGS, REGNO_REG_CLASS (regno)))
|
||
|
+ return false;
|
||
|
+
|
||
|
+ return true;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
/* Implement TARGET_MODES_TIEABLE_P. */
|
||
|
|
||
|
static bool
|
||
|
@@ -16956,6 +16968,9 @@ s390_case_values_threshold (void)
|
||
|
#undef TARGET_CASE_VALUES_THRESHOLD
|
||
|
#define TARGET_CASE_VALUES_THRESHOLD s390_case_values_threshold
|
||
|
|
||
|
+#undef TARGET_NARROW_MODE_REFERS_LOW_PART_P
|
||
|
+#define TARGET_NARROW_MODE_REFERS_LOW_PART_P s390_narrow_mode_refers_low_part_p
|
||
|
+
|
||
|
struct gcc_target targetm = TARGET_INITIALIZER;
|
||
|
|
||
|
#include "gt-s390.h"
|
||
|
--- a/gcc/regcprop.c
|
||
|
+++ b/gcc/regcprop.c
|
||
|
@@ -426,7 +426,8 @@ maybe_mode_change (machine_mode orig_mode, machine_mode copy_mode,
|
||
|
|
||
|
if (orig_mode == new_mode)
|
||
|
return gen_raw_REG (new_mode, regno);
|
||
|
- else if (mode_change_ok (orig_mode, new_mode, regno))
|
||
|
+ else if (mode_change_ok (orig_mode, new_mode, regno)
|
||
|
+ && targetm.narrow_mode_refers_low_part_p (copy_regno))
|
||
|
{
|
||
|
int copy_nregs = hard_regno_nregs (copy_regno, copy_mode);
|
||
|
int use_nregs = hard_regno_nregs (copy_regno, new_mode);
|
||
|
--- a/gcc/target.def
|
||
|
+++ b/gcc/target.def
|
||
|
@@ -5446,6 +5446,16 @@ value that the middle-end intended.",
|
||
|
bool, (machine_mode from, machine_mode to, reg_class_t rclass),
|
||
|
hook_bool_mode_mode_reg_class_t_true)
|
||
|
|
||
|
+/* This hook is used to work around a problem in regcprop. Hardcoded
|
||
|
+assumptions currently prevent it from working correctly for targets
|
||
|
+where the low part of a multi-word register doesn't align to accessing
|
||
|
+the register with a narrower mode. */
|
||
|
+DEFHOOK_UNDOC
|
||
|
+(narrow_mode_refers_low_part_p,
|
||
|
+"",
|
||
|
+bool, (unsigned int regno),
|
||
|
+hook_bool_uint_true)
|
||
|
+
|
||
|
/* Change pseudo allocno class calculated by IRA. */
|
||
|
DEFHOOK
|
||
|
(ira_change_pseudo_allocno_class,
|
||
|
--- a/gcc/hooks.h
|
||
|
+++ b/gcc/hooks.h
|
||
|
@@ -86,6 +86,7 @@ extern void hook_void_tree (tree);
|
||
|
extern void hook_void_tree_treeptr (tree, tree *);
|
||
|
extern void hook_void_int_int (int, int);
|
||
|
extern void hook_void_gcc_optionsp (struct gcc_options *);
|
||
|
+extern bool hook_bool_uint_true (unsigned int);
|
||
|
extern bool hook_bool_uint_uintp_false (unsigned int, unsigned int *);
|
||
|
|
||
|
extern int hook_int_uint_mode_1 (unsigned int, machine_mode);
|
||
|
--- a/gcc/hooks.c
|
||
|
+++ b/gcc/hooks.c
|
||
|
@@ -498,6 +498,14 @@ hook_void_gcc_optionsp (struct gcc_optio
|
||
|
{
|
||
|
}
|
||
|
|
||
|
+/* Generic hook that takes an unsigned int and returns true. */
|
||
|
+
|
||
|
+bool
|
||
|
+hook_bool_uint_true (unsigned int)
|
||
|
+{
|
||
|
+ return true;
|
||
|
+}
|
||
|
+
|
||
|
/* Generic hook that takes an unsigned int, an unsigned int pointer and
|
||
|
returns false. */
|
||
|
|