From b61a0d75fe479befe4bb92eb1447ed52b095599b Mon Sep 17 00:00:00 2001 From: Jiong Wang Date: Wed, 19 Aug 2015 22:55:28 +0000 Subject: [PATCH] [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 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 | 5 + gcc/expr.c | 121 +++++++++++++++--- gcc/testsuite/ChangeLog | 6 + gcc/testsuite/gcc.dg/wide-shift-128.c | 11 ++ gcc/testsuite/gcc.dg/wide-shift-64.c | 10 ++ gcc/testsuite/gcc.target/aarch64/ashltidisi.c | 49 +++++++ 6 files changed, 186 insertions(+), 16 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/wide-shift-128.c create mode 100644 gcc/testsuite/gcc.dg/wide-shift-64.c create mode 100644 gcc/testsuite/gcc.target/aarch64/ashltidisi.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 14446bc6795..78a7a7930ba 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2015-08-19 Jiong Wang + + * expr.c (expand_expr_real_2): Check gimple statement during + LSHIFT_EXPR expand. + 2015-08-19 Magnus Granberg * common.opt (fstack-protector): Initialize to -1. diff --git a/gcc/expr.c b/gcc/expr.c index 3202f555838..1e820b45a24 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 9654cc34089..076de9ca606 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2015-08-19 Jiong Wang + + * 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 * 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 00000000000..d769833af04 --- /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 00000000000..c1624c5cdbf --- /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 00000000000..293a0f2563b --- /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" } } */ -- 2.30.2