[Patch][expand] Check gimple statement to improve LSHIFT_EXP expand
authorJiong Wang <jiong.wang@arm.com>
Wed, 19 Aug 2015 22:55:28 +0000 (22:55 +0000)
committerJiong Wang <jiwang@gcc.gnu.org>
Wed, 19 Aug 2015 22:55:28 +0000 (22:55 +0000)
This patch improves LSHIFT_EXP expand if the shift operand comes from sign
extension and the shift result across word_mode_size boundary. See code
comments for details.

2015-08-19  Jiong.Wang  <jiong.wang@arm.com>

gcc/
  * expr.c (expand_expr_real_2): Check gimple statement during
  LSHIFT_EXPR expand.

gcc/testsuite
  * gcc.dg/wide_shift_64_1.c: New testcase.
  * gcc.dg/wide_shift_128_1.c: Likewise.
  * gcc.target/aarch64/ashlti3_1.c: Likewise.

From-SVN: r227018

gcc/ChangeLog
gcc/expr.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/wide-shift-128.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/wide-shift-64.c [new file with mode: 0644]
gcc/testsuite/gcc.target/aarch64/ashltidisi.c [new file with mode: 0644]

index 14446bc67955f1e1c82c06f14ef52ec6c0370a53..78a7a7930ba493bfbbe462998a59bd3c3df2e248 100644 (file)
@@ -1,3 +1,8 @@
+2015-08-19  Jiong Wang  <jiong.wang@arm.com>
+
+       * expr.c (expand_expr_real_2): Check gimple statement during
+       LSHIFT_EXPR expand.
+
 2015-08-19  Magnus Granberg  <zorry@gentoo.org>
 
        * common.opt (fstack-protector): Initialize to -1.
index 3202f555838531a444090a6ba05b573ad91876c8..1e820b45a247bcccef29741057f5ebc20a63ecf5 100644 (file)
@@ -8836,23 +8836,112 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
 
     case LSHIFT_EXPR:
     case RSHIFT_EXPR:
-      /* If this is a fixed-point operation, then we cannot use the code
-        below because "expand_shift" doesn't support sat/no-sat fixed-point
-         shifts.   */
-      if (ALL_FIXED_POINT_MODE_P (mode))
-       goto binop;
+      {
+       /* If this is a fixed-point operation, then we cannot use the code
+          below because "expand_shift" doesn't support sat/no-sat fixed-point
+          shifts.  */
+       if (ALL_FIXED_POINT_MODE_P (mode))
+         goto binop;
+
+       if (! safe_from_p (subtarget, treeop1, 1))
+         subtarget = 0;
+       if (modifier == EXPAND_STACK_PARM)
+         target = 0;
+       op0 = expand_expr (treeop0, subtarget,
+                          VOIDmode, EXPAND_NORMAL);
+
+       /* 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:
+
+           Type A = (Type) B  << C
+
+           |<           T          >|
+           | dest_high  |  dest_low |
+
+                        | 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:
+
+          1. Initialize dest_low to B.
+          2. Initialize every bit of dest_high to the sign bit of B.
+          3. Logic left shift dest_low by C bit to finalize dest_low.
+             The value of dest_low before this shift is kept in a temp D.
+          4. Logic left shift dest_high by C.
+          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:
+
+             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.  */
+
+       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)
+           && ! have_insn_for (ASHIFT, mode)
+           && TREE_CONSTANT (treeop1)
+           && TREE_CODE (treeop0) == SSA_NAME)
+         {
+           gimple def = SSA_NAME_DEF_STMT (treeop0);
+           if (is_gimple_assign (def)
+               && gimple_assign_rhs_code (def) == NOP_EXPR)
+             {
+               machine_mode rmode = TYPE_MODE
+                 (TREE_TYPE (gimple_assign_rhs1 (def)));
 
-      if (! safe_from_p (subtarget, treeop1, 1))
-       subtarget = 0;
-      if (modifier == EXPAND_STACK_PARM)
-       target = 0;
-      op0 = expand_expr (treeop0, subtarget,
-                        VOIDmode, EXPAND_NORMAL);
-      temp = expand_variable_shift (code, mode, op0, treeop1, target,
-                                   unsignedp);
-      if (code == LSHIFT_EXPR)
-       temp = REDUCE_BIT_FIELD (temp);
-      return temp;
+               if (GET_MODE_SIZE (rmode) < GET_MODE_SIZE (mode)
+                   && TREE_INT_CST_LOW (treeop1) < GET_MODE_BITSIZE (word_mode)
+                   && ((TREE_INT_CST_LOW (treeop1) + GET_MODE_BITSIZE (rmode))
+                       >= GET_MODE_BITSIZE (word_mode)))
+                 {
+                   unsigned int high_off = subreg_highpart_offset (word_mode,
+                                                                   mode);
+                   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,
+                                                        mode, high_off);
+                   HOST_WIDE_INT ramount = (BITS_PER_WORD
+                                            - TREE_INT_CST_LOW (treeop1));
+                   tree rshift = build_int_cst (TREE_TYPE (treeop1), ramount);
+
+                   /* dest_high = src_low >> (word_size - C).  */
+                   temp = expand_variable_shift (RSHIFT_EXPR, word_mode, low,
+                                                 rshift, dest_high, unsignedp);
+                   if (temp != dest_high)
+                     emit_move_insn (dest_high, temp);
+
+                   /* dest_low = src_low << C.  */
+                   temp = expand_variable_shift (LSHIFT_EXPR, word_mode, low,
+                                                 treeop1, dest_low, unsignedp);
+                   if (temp != dest_low)
+                     emit_move_insn (dest_low, temp);
+
+                   temp = target ;
+                 }
+             }
+         }
+
+       if (temp == NULL_RTX)
+         temp = expand_variable_shift (code, mode, op0, treeop1, target,
+                                       unsignedp);
+       if (code == LSHIFT_EXPR)
+         temp = REDUCE_BIT_FIELD (temp);
+       return temp;
+      }
 
       /* Could determine the answer when only additive constants differ.  Also,
         the addition of one can be handled by changing the condition.  */
index 9654cc340892b9d565948d2b95fd55185c1e5e2b..076de9ca6063787020ff536b847155d55b45f61e 100644 (file)
@@ -1,3 +1,9 @@
+2015-08-19  Jiong Wang  <jiong.wang@arm.com>
+
+       * gcc.dg/wide_shift_64_1.c: New testcase.
+       * gcc.dg/wide_shift_128_1.c: Likewise.
+       * gcc.target/aarch64/ashlti3_1.c: Likewise.
+
 2015-08-19  Magnus Granberg  <zorry@gentoo.org>
 
        * lib/target-supports.exp
diff --git a/gcc/testsuite/gcc.dg/wide-shift-128.c b/gcc/testsuite/gcc.dg/wide-shift-128.c
new file mode 100644 (file)
index 0000000..d769833
--- /dev/null
@@ -0,0 +1,11 @@
+/* { dg-do compile { target aarch64*-*-* mips64*-*-* sparc64*-*-* } } */
+/* { dg-require-effective-target int128 } */
+/* { dg-options "-O2 -fdump-rtl-combine" } */
+
+__int128_t
+load2 (int data)
+{
+    return (__int128_t) data << 50;
+}
+
+/* { dg-final { scan-rtl-dump-not "ior" "combine" } } */
diff --git a/gcc/testsuite/gcc.dg/wide-shift-64.c b/gcc/testsuite/gcc.dg/wide-shift-64.c
new file mode 100644 (file)
index 0000000..c1624c5
--- /dev/null
@@ -0,0 +1,10 @@
+/* { dg-do compile { target mips*-*-* sparc*-*-* } } */
+/* { dg-options "-O2 -fdump-rtl-combine" } */
+
+long long
+load1 (int data)
+{
+    return (long long) data << 12;
+}
+
+/* { dg-final { scan-rtl-dump-not "ior" "combine" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/ashltidisi.c b/gcc/testsuite/gcc.target/aarch64/ashltidisi.c
new file mode 100644 (file)
index 0000000..293a0f2
--- /dev/null
@@ -0,0 +1,49 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -save-temps" } */
+
+extern void abort (void);
+
+#define GEN_TEST_CASE(x, y, z)\
+__uint128_t __attribute__ ((noinline))\
+ushift_##x##_##z (unsigned y data)\
+{\
+  return (__uint128_t) data << x;\
+}\
+__int128_t __attribute__ ((noinline)) \
+shift_##x##_##z (y data) \
+{\
+  return (__int128_t) data << x;\
+}
+
+GEN_TEST_CASE (53, int, i)
+GEN_TEST_CASE (3, long long, ll)
+GEN_TEST_CASE (13, long long, ll)
+GEN_TEST_CASE (53, long long, ll)
+
+int
+main (int argc, char **argv)
+{
+
+#define SHIFT_CHECK(x, y, z, p) \
+       if (ushift_##y##_##p (x)\
+           != ((__uint128_t) (unsigned z) x << y)) \
+         abort ();\
+       if (shift_##y##_##p (x)\
+           != ((__uint128_t) (signed z) x << y)) \
+         abort ();
+
+  SHIFT_CHECK (0x12345678, 53, int, i)
+  SHIFT_CHECK (0xcafecafe, 53, int, i)
+
+  SHIFT_CHECK (0x1234567890abcdefLL, 3, long long, ll)
+  SHIFT_CHECK (0x1234567890abcdefLL, 13, long long, ll)
+  SHIFT_CHECK (0x1234567890abcdefLL, 53, long long, ll)
+  SHIFT_CHECK (0xcafecafedeaddeadLL, 3, long long, ll)
+  SHIFT_CHECK (0xcafecafedeaddeadLL, 13, long long, ll)
+  SHIFT_CHECK (0xcafecafedeaddeadLL, 53, long long, ll)
+
+  return 0;
+}
+
+/* { dg-final { scan-assembler-times "asr" 4 } } */
+/* { dg-final { scan-assembler-not "extr\t" } } */