On 18/01/17 21:07, Jeff Law wrote:
> On 01/18/2017 11:08 AM, Richard Earnshaw (lists) wrote:
>> 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.
> I had to refamiliarize myself with this code and nearly got the analysis
> wrong (again).
> 
> Due to the copying of the low word into the high word we have to select
> the type of shift based on the type of the object that was the source of
> the NOP conversion.  The code currently makes that determination based
> on the signedness of the shift, which is wrong.
> 
> 
> OK for the trunk.
> 
> jeff
> 
> 

Thanks, Jeff.  I made some minor tweaks to the comments (adding a bit
more about signed vs unsigned) and committed the following.

What about gcc-6?

R.
diff --git a/gcc/expr.c b/gcc/expr.c
index 4c54faf..2d8868e 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -9056,9 +9056,9 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode 
tmode,
 
        /* Left shift optimization when shifting across word_size boundary.
 
-          If mode == GET_MODE_WIDER_MODE (word_mode), then normally there isn't
-          native instruction to support this wide mode left shift.  Given below
-          scenario:
+          If mode == GET_MODE_WIDER_MODE (word_mode), then normally
+          there isn't native instruction to support this wide mode
+          left shift.  Given below scenario:
 
            Type A = (Type) B  << C
 
@@ -9067,10 +9067,11 @@ expand_expr_real_2 (sepops ops, rtx target, 
machine_mode tmode,
 
                         | word_size |
 
-          If the shift amount C caused we shift B to across the word size
-          boundary, i.e part of B shifted into high half of destination
-          register, and part of B remains in the low half, then GCC will use
-          the following left shift expand logic:
+          If the shift amount C caused we shift B to across the word
+          size boundary, i.e part of B shifted into high half of
+          destination register, and part of B remains in the low
+          half, then GCC will use the following left shift expand
+          logic:
 
           1. Initialize dest_low to B.
           2. Initialize every bit of dest_high to the sign bit of B.
@@ -9080,20 +9081,30 @@ expand_expr_real_2 (sepops ops, rtx target, 
machine_mode tmode,
           5. Logic right shift D by (word_size - C).
           6. Or the result of 4 and 5 to finalize dest_high.
 
-          While, by checking gimple statements, if operand B is coming from
-          signed extension, then we can simplify above expand logic into:
+          While, by checking gimple statements, if operand B is
+          coming from signed extension, then we can simplify above
+          expand logic into:
 
              1. dest_high = src_low >> (word_size - C).
              2. dest_low = src_low << C.
 
-          We can use one arithmetic right shift to finish all the purpose of
-          steps 2, 4, 5, 6, thus we reduce the steps needed from 6 into 2.  */
+          We can use one arithmetic right shift to finish all the
+          purpose of steps 2, 4, 5, 6, thus we reduce the steps
+          needed from 6 into 2.
+
+          The case is similar for zero extension, except that we
+          initialize dest_high to zero rather than copies of the sign
+          bit from B.  Furthermore, we need to use a logical right shift
+          in this case.
+
+          The choice of sign-extension versus zero-extension is
+          determined entirely by whether or not B is signed and is
+          independent of the current setting of unsignedp.  */
 
        temp = NULL_RTX;
        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 +9125,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 +9138,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