From f155ce84640f25f4fc72cd6c936abe76cc91a65f Mon Sep 17 00:00:00 2001 From: jiwang Date: Wed, 19 Aug 2015 22:55:28 +0000 Subject: [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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@227018 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/expr.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 16 deletions(-) (limited to 'gcc/expr.c') 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. */ -- cgit v1.2.1