aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJiong Wang <jiong.wang@arm.com>2015-08-19 22:55:28 +0000
committerJiong Wang <jiwang@gcc.gnu.org>2015-08-19 22:55:28 +0000
commitb61a0d75fe479befe4bb92eb1447ed52b095599b (patch)
tree5aa74ee7132df7a44f46653bee3dce7ee3bdf5b7 /gcc
parente0f6cba004eb94ef9a06c68d09160d8601503085 (diff)
downloadgcc-b61a0d75fe479befe4bb92eb1447ed52b095599b.zip
gcc-b61a0d75fe479befe4bb92eb1447ed52b095599b.tar.gz
gcc-b61a0d75fe479befe4bb92eb1447ed52b095599b.tar.bz2
[Patch][expand] Check gimple statement to improve LSHIFT_EXP expand
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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog5
-rw-r--r--gcc/expr.c121
-rw-r--r--gcc/testsuite/ChangeLog6
-rw-r--r--gcc/testsuite/gcc.dg/wide-shift-128.c11
-rw-r--r--gcc/testsuite/gcc.dg/wide-shift-64.c10
-rw-r--r--gcc/testsuite/gcc.target/aarch64/ashltidisi.c49
6 files changed, 186 insertions, 16 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 14446bc..78a7a79 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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.
diff --git a/gcc/expr.c b/gcc/expr.c
index 3202f55..1e820b4 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -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. */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 9654cc3..076de9c 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
index 0000000..d769833
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/wide-shift-128.c
@@ -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
index 0000000..c1624c5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/wide-shift-64.c
@@ -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
index 0000000..293a0f2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ashltidisi.c
@@ -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" } } */