PR 79121 is a silent wrong code regression where, when generating a
shift from an extended value moving from one to two machine registers,
the type of the right shift is for the most significant word should be
determined by the signedness of the inner type, not the signedness of
the result type.

gcc:
        PR rtl-optimization/79121
        * expr.c (expand_expr_real_2, case LSHIFT_EXPR): Look at the signedness
        of the inner type when shifting an extended value.

testsuite:
        * gcc.c-torture/execute/pr79121.c: New test.

Bootstrapped on x86_64 and cross-tested on ARM.

OK for trunk and GCC-6?

R.
diff --git a/gcc/expr.c b/gcc/expr.c
index 4c54faf..cae13a4 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -9093,7 +9093,6 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode 
tmode,
        if (code == LSHIFT_EXPR
            && target
            && REG_P (target)
-           && ! unsignedp
            && mode == GET_MODE_WIDER_MODE (word_mode)
            && GET_MODE_SIZE (mode) == 2 * GET_MODE_SIZE (word_mode)
            && TREE_CONSTANT (treeop1)
@@ -9114,6 +9113,8 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode 
tmode,
                    rtx_insn *seq, *seq_old;
                    unsigned int high_off = subreg_highpart_offset (word_mode,
                                                                    mode);
+                   bool extend_unsigned
+                     = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (def)));
                    rtx low = lowpart_subreg (word_mode, op0, mode);
                    rtx dest_low = lowpart_subreg (word_mode, target, mode);
                    rtx dest_high = simplify_gen_subreg (word_mode, target,
@@ -9125,7 +9126,8 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode 
tmode,
                    start_sequence ();
                    /* dest_high = src_low >> (word_size - C).  */
                    temp = expand_variable_shift (RSHIFT_EXPR, word_mode, low,
-                                                 rshift, dest_high, unsignedp);
+                                                 rshift, dest_high,
+                                                 extend_unsigned);
                    if (temp != dest_high)
                      emit_move_insn (dest_high, temp);
 
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr79121.c 
b/gcc/testsuite/gcc.c-torture/execute/pr79121.c
new file mode 100644
index 0000000..9fca7fb
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr79121.c
@@ -0,0 +1,34 @@
+extern void abort (void);
+
+__attribute__ ((noinline, noclone)) unsigned long long f1 (int x)
+{
+  return ((unsigned long long) x) << 4;
+}
+
+__attribute__ ((noinline, noclone)) long long f2 (unsigned x)
+{
+  return ((long long) x) << 4;
+}
+
+__attribute__ ((noinline, noclone)) unsigned long long f3 (unsigned x)
+{
+  return ((unsigned long long) x) << 4;
+}
+
+__attribute__ ((noinline, noclone)) long long f4 (int x)
+{
+  return ((long long) x) << 4;
+}
+
+int main ()
+{
+  if (f1 (0xf0000000) != 0xffffffff00000000)
+    abort ();
+  if (f2 (0xf0000000) != 0xf00000000)
+    abort ();
+  if (f3 (0xf0000000) != 0xf00000000)
+    abort ();
+  if (f4 (0xf0000000) != 0xffffffff00000000)
+    abort ();
+  return 0;
+}

Reply via email to