Ping~ Thanks, Kugan
+2013-09-25 Kugan Vivekanandarajah <kug...@linaro.org> + + * dojump.c (do_compare_and_jump): Generate rtl without + zero/sign extension if redundant. + * cfgexpand.c (expand_gimple_stmt_1): Likewise. + * gimple.c (gimple_assign_is_zero_sign_ext_redundant) : New + function. + * gimple.h (gimple_assign_is_zero_sign_ext_redundant) : Declare. + On 26/09/13 18:04, Kugan Vivekanandarajah wrote: > Hi, > > This is the updated patch for expanding gimple stmts without zer/sign > extensions when it is safe to do that. This is based on the > latest changes to propagating value range information to SSA_NAMEs > and addresses review comments from Eric. > > Bootstrapped and regtested on x86_64-unknown-linux-gnu and arm-none > linux-gnueabi. Is this OK ? > > Thanks, > Kugan >
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 88e48c2..6a22f8b 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -2311,6 +2311,20 @@ expand_gimple_stmt_1 (gimple stmt) if (temp == target) ; + /* If the value in SUBREG of temp fits that SUBREG (does not + overflow) and is assigned to target SUBREG of the same mode + without sign convertion, we can skip the SUBREG + and extension. */ + else if (promoted + && gimple_assign_is_zero_sign_ext_redundant (stmt) + && (GET_CODE (temp) == SUBREG) + && (GET_MODE_PRECISION (GET_MODE (SUBREG_REG (temp))) + >= GET_MODE_PRECISION (GET_MODE (target))) + && (GET_MODE (SUBREG_REG (target)) + == GET_MODE (SUBREG_REG (temp)))) + { + emit_move_insn (SUBREG_REG (target), SUBREG_REG (temp)); + } else if (promoted) { int unsignedp = SUBREG_PROMOTED_UNSIGNED_P (target); diff --git a/gcc/dojump.c b/gcc/dojump.c index 3f04eac..9ea5995 100644 --- a/gcc/dojump.c +++ b/gcc/dojump.c @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see #include "ggc.h" #include "basic-block.h" #include "tm_p.h" +#include "gimple.h" static bool prefer_and_bit_test (enum machine_mode, int); static void do_jump_by_parts_greater (tree, tree, int, rtx, rtx, int); @@ -1108,6 +1109,64 @@ do_compare_and_jump (tree treeop0, tree treeop1, enum rtx_code signed_code, type = TREE_TYPE (treeop0); mode = TYPE_MODE (type); + + /* Is zero/sign extension redundant. */ + bool op0_ext_redundant = false; + bool op1_ext_redundant = false; + + /* If promoted and the value in SUBREG of op0 fits (does not overflow), + it is a candidate for extension elimination. */ + if (GET_CODE (op0) == SUBREG && SUBREG_PROMOTED_VAR_P (op0)) + op0_ext_redundant = + gimple_assign_is_zero_sign_ext_redundant (SSA_NAME_DEF_STMT (treeop0)); + + /* If promoted and the value in SUBREG of op1 fits (does not overflow), + it is a candidate for extension elimination. */ + if (GET_CODE (op1) == SUBREG && SUBREG_PROMOTED_VAR_P (op1)) + op1_ext_redundant = + gimple_assign_is_zero_sign_ext_redundant (SSA_NAME_DEF_STMT (treeop1)); + + /* If zero/sign extension is redundant, generate RTL + for operands without zero/sign extension. */ + if ((op0_ext_redundant || TREE_CODE (treeop0) == INTEGER_CST) + && (op1_ext_redundant || TREE_CODE (treeop1) == INTEGER_CST)) + { + if ((TREE_CODE (treeop1) == INTEGER_CST) + && (!mode_signbit_p (GET_MODE (op1), op1))) + { + /* First operand is constant and signbit is not set (not + represented in RTL as a negative constant). */ + rtx new_op0 = gen_reg_rtx (GET_MODE (SUBREG_REG (op0))); + emit_move_insn (new_op0, SUBREG_REG (op0)); + op0 = new_op0; + } + else if ((TREE_CODE (treeop0) == INTEGER_CST) + && (!mode_signbit_p (GET_MODE (op0), op0))) + { + /* Other operand is constant and signbit is not set (not + represented in RTL as a negative constant). */ + rtx new_op1 = gen_reg_rtx (GET_MODE (SUBREG_REG (op1))); + + emit_move_insn (new_op1, SUBREG_REG (op1)); + op1 = new_op1; + } + else if ((TREE_CODE (treeop0) != INTEGER_CST) + && (TREE_CODE (treeop1) != INTEGER_CST) + && (GET_MODE (op0) == GET_MODE (op1)) + && (GET_MODE (SUBREG_REG (op0)) == GET_MODE (SUBREG_REG (op1)))) + { + /* Compare registers fits SUBREG and of the + same mode. */ + rtx new_op0 = gen_reg_rtx (GET_MODE (SUBREG_REG (op0))); + rtx new_op1 = gen_reg_rtx (GET_MODE (SUBREG_REG (op1))); + + emit_move_insn (new_op0, SUBREG_REG (op0)); + emit_move_insn (new_op1, SUBREG_REG (op1)); + op0 = new_op0; + op1 = new_op1; + } + } + if (TREE_CODE (treeop0) == INTEGER_CST && (TREE_CODE (treeop1) != INTEGER_CST || (GET_MODE_BITSIZE (mode) diff --git a/gcc/gimple.c b/gcc/gimple.c index 59fcf43..7bb93a6 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -200,6 +200,102 @@ gimple_call_reset_alias_info (gimple s) pt_solution_reset (gimple_call_clobber_set (s)); } +/* Check gimple assign stmt and see if zero/sign extension is + redundant. i.e. if an assignment gimple statement has RHS expression + value that can fit in LHS type, subreg and extension to fit can be + redundant. Zero/sign extensions in this case can be removed. + + If the assignment is + 1) NOP_EXPR or CONVERT_EXPR: + Check value range of RHS to see if it fits LHS type. If value fits type, + extension is redundant. + + 2) For all other types: + If the value range of LHS is less than the LHS TYPE_MAX and greater than + LHS TYPE_MIN, RHS expresion (from which LHS value range is derived) + will also have the same value range. Therefore extension is redundant. */ + +bool +gimple_assign_is_zero_sign_ext_redundant (gimple stmt) +{ + double_int type_min, type_max; + double_int min, max; + enum value_range_type range_type; + tree int_val = NULL_TREE; + enum tree_code stmt_code; + tree lhs, rhs1; + + if (!is_gimple_assign (stmt)) + return false; + + stmt_code = gimple_assign_rhs_code (stmt); + lhs = gimple_assign_lhs (stmt); + rhs1 = gimple_assign_rhs1 (stmt); + + /* We remove extension for non-pointer and integral stmts. */ + if (!INTEGRAL_TYPE_P (TREE_TYPE (lhs)) + || POINTER_TYPE_P (TREE_TYPE (lhs))) + return false; + + type_max = tree_to_double_int (TYPE_MAX_VALUE (TREE_TYPE (lhs))); + type_min = tree_to_double_int (TYPE_MIN_VALUE (TREE_TYPE (lhs))); + + if ((stmt_code == NOP_EXPR || stmt_code == CONVERT_EXPR)) + { + if ((TREE_CODE (rhs1) != SSA_NAME) + || (TYPE_UNSIGNED (TREE_TYPE (lhs)) + != TYPE_UNSIGNED (TREE_TYPE (rhs1)))) + return false; + + range_type = get_range_info (rhs1, &min, &max); + + /* For NOP_EXPR and CONVERT_EXPR, if rhs value range fits lhs + type, zero/sign extension is redundant. */ + if (range_type == VR_RANGE + && max.cmp (type_max, TYPE_UNSIGNED (TREE_TYPE (lhs))) != 1 + && (type_min.cmp (min, TYPE_UNSIGNED (TREE_TYPE (lhs))) != 1)) + return true; + } + + /* For binary expressions, if one of the argument is constant and is + larger than signed maximum, it can be interpreted as negative + number and sign extended in RTL, so return false in this case. */ + if (TREE_CODE_CLASS (stmt_code) == tcc_binary) + { + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + + if (TREE_CODE (rhs1) == INTEGER_CST) + int_val = rhs1; + else if (TREE_CODE (rhs2) == INTEGER_CST) + int_val = rhs2; + + if (int_val != NULL_TREE) + { + tree max = TYPE_MIN_VALUE (TREE_TYPE (lhs)); + + /* If type is unsigned, get the max for signed equivalent. */ + if (!INT_CST_LT (TYPE_MIN_VALUE (TREE_TYPE (lhs)), integer_zero_node)) + max = int_const_binop (RSHIFT_EXPR, + max, build_int_cst (TREE_TYPE (max), 1)); + if (!INT_CST_LT (int_val, max)) + return false; + } + } + + /* Get the value range. */ + range_type = get_range_info (lhs, &min, &max); + + /* Value range fits type. */ + if (range_type == VR_RANGE + && (max.cmp (type_max, TYPE_UNSIGNED (TREE_TYPE (lhs))) == -1) + && ((type_min.cmp (min, TYPE_UNSIGNED (TREE_TYPE (lhs))) == -1) + || min.is_zero ())) + return true; + return false; +} + + /* Helper for gimple_build_call, gimple_build_call_valist, gimple_build_call_vec and gimple_build_call_from_tree. Build the basic components of a GIMPLE_CALL statement to function FN with NARGS diff --git a/gcc/gimple.h b/gcc/gimple.h index 5f12805..791f606 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -832,6 +832,7 @@ int gimple_call_flags (const_gimple); int gimple_call_return_flags (const_gimple); int gimple_call_arg_flags (const_gimple, unsigned); void gimple_call_reset_alias_info (gimple); +bool gimple_assign_is_zero_sign_ext_redundant (gimple); bool gimple_assign_copy_p (gimple); bool gimple_assign_ssa_name_copy_p (gimple); bool gimple_assign_unary_nop_p (gimple);