Commit b61a0d75 by Jiong Wang Committed by Jiong Wang

[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
parent e0f6cba0
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.
......
......@@ -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. */
......
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
......
/* { 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" } } */
/* { 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" } } */
/* { 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" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment