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