This patch makes us apply operand conversions of bitwise binary operations to the operation result instead, which in turn allows us to combine those conversions which helps bitfield related operations. For
struct B { unsigned bit0 : 1; unsigned bit1 : 1; }; void foo (struct B *b) { b->bit0 = b->bit0 | b->bit1; } we with this patch generate D.2686_2 = b_1(D)->bit0; D.2688_4 = b_1(D)->bit1; D.2693_10 = D.2688_4 ^ D.2686_2; b_1(D)->bit0 = D.2693_10; instead of D.2686_2 = b_1(D)->bit0; D.2687_3 = (unsigned char) D.2686_2; D.2688_4 = b_1(D)->bit1; D.2689_5 = (unsigned char) D.2688_4; D.2690_6 = D.2687_3 | D.2689_5; D.2691_7 = (<unnamed-unsigned:1>) D.2690_6; b_1(D)->bit0 = D.2691_7; which is shorter and easier to analyze. The generated code is the same though, and still contains too many masking operations. Bitfield lowering will expose more conversion combining opportunities similar to the above. Bootstrap and regtest pending on x86_64-unknown-linux-gnu. Richard. 2011-05-10 Richard Guenther <rguent...@suse.de> PR tree-optimization/18041 * tree-ssa-forwprop.c (simplify_bitwise_and): Rename to ... (simplify_bitwise_binary): ... this. Handle operand conversions by applying them to the result instead. (tree_ssa_forward_propagate_single_use_vars): Adjust. CSE tree code. * gcc.dg/tree-ssa/forwprop-13.c: New testcase. Index: gcc/tree-ssa-forwprop.c =================================================================== *** gcc/tree-ssa-forwprop.c (revision 173612) --- gcc/tree-ssa-forwprop.c (working copy) *************** simplify_builtin_call (gimple_stmt_itera *** 1612,1655 **** return false; } ! /* Run bitwise and assignments throug the folder. If the first argument is an ! ssa name that is itself a result of a typecast of an ADDR_EXPR to an ! integer, feed the ADDR_EXPR to the folder rather than the ssa name. ! */ ! static void ! simplify_bitwise_and (gimple_stmt_iterator *gsi, gimple stmt) { ! tree res; tree arg1 = gimple_assign_rhs1 (stmt); tree arg2 = gimple_assign_rhs2 (stmt); ! if (TREE_CODE (arg2) != INTEGER_CST) ! return; ! ! if (TREE_CODE (arg1) == SSA_NAME && !SSA_NAME_IS_DEFAULT_DEF (arg1)) { gimple def = SSA_NAME_DEF_STMT (arg1); ! if (gimple_assign_cast_p (def) ! && INTEGRAL_TYPE_P (gimple_expr_type (def))) ! { ! tree op = gimple_assign_rhs1 (def); ! if (TREE_CODE (op) == ADDR_EXPR) ! arg1 = op; } } ! res = fold_binary_loc (gimple_location (stmt), ! BIT_AND_EXPR, TREE_TYPE (gimple_assign_lhs (stmt)), ! arg1, arg2); ! if (res && is_gimple_min_invariant (res)) { ! gimple_assign_set_rhs_from_tree (gsi, res); ! update_stmt (stmt); } ! return; } --- 1612,1691 ---- return false; } ! /* Simplify bitwise binary operations. ! Return true if a transformation applied, otherwise return false. */ ! static bool ! simplify_bitwise_binary (gimple_stmt_iterator *gsi) { ! gimple stmt = gsi_stmt (*gsi); tree arg1 = gimple_assign_rhs1 (stmt); tree arg2 = gimple_assign_rhs2 (stmt); + enum tree_code code = gimple_assign_rhs_code (stmt); + tree res; ! /* If the first argument is an SSA name that is itself a result of a ! typecast of an ADDR_EXPR to an integer, feed the ADDR_EXPR to the ! folder rather than the ssa name. */ ! if (code == BIT_AND_EXPR ! && TREE_CODE (arg2) == INTEGER_CST ! && TREE_CODE (arg1) == SSA_NAME) { gimple def = SSA_NAME_DEF_STMT (arg1); + tree op = arg1; ! /* ??? This looks bogus - the conversion could be truncating. */ ! if (is_gimple_assign (def) ! && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def)) ! && INTEGRAL_TYPE_P (TREE_TYPE (arg1))) ! { ! tree opp = gimple_assign_rhs1 (def); ! if (TREE_CODE (opp) == ADDR_EXPR) ! op = opp; ! } ! res = fold_binary_loc (gimple_location (stmt), ! BIT_AND_EXPR, TREE_TYPE (gimple_assign_lhs (stmt)), ! op, arg2); ! if (res && is_gimple_min_invariant (res)) ! { ! gimple_assign_set_rhs_from_tree (gsi, res); ! update_stmt (stmt); ! return true; } } ! /* For bitwise binary operations apply operand conversions to the ! binary operation result instead of to the operands. This allows ! to combine successive conversions and bitwise binary operations. */ ! if (TREE_CODE (arg1) == SSA_NAME ! && TREE_CODE (arg2) == SSA_NAME) { ! gimple def_stmt1 = SSA_NAME_DEF_STMT (arg1); ! gimple def_stmt2 = SSA_NAME_DEF_STMT (arg2); ! if (is_gimple_assign (def_stmt1) ! && is_gimple_assign (def_stmt2) ! && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt1)) ! && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt2)) ! && types_compatible_p (TREE_TYPE (gimple_assign_rhs1 (def_stmt1)), ! TREE_TYPE (gimple_assign_rhs1 (def_stmt2)))) ! { ! gimple newop; ! tree tem = create_tmp_reg (TREE_TYPE (gimple_assign_rhs1 (def_stmt1)), ! NULL); ! newop = gimple_build_assign_with_ops (code, tem, ! gimple_assign_rhs1 (def_stmt1), ! gimple_assign_rhs1 (def_stmt2)); ! tem = make_ssa_name (tem, newop); ! gimple_assign_set_lhs (newop, tem); ! gsi_insert_before (gsi, newop, GSI_SAME_STMT); ! gimple_assign_set_rhs_with_ops_1 (gsi, NOP_EXPR, ! tem, NULL_TREE, NULL_TREE); ! update_stmt (gsi_stmt (*gsi)); ! return true; ! } } ! return false; } *************** tree_ssa_forward_propagate_single_use_va *** 2123,2128 **** --- 2159,2165 ---- { tree lhs = gimple_assign_lhs (stmt); tree rhs = gimple_assign_rhs1 (stmt); + enum tree_code code = gimple_assign_rhs_code (stmt); if (TREE_CODE (lhs) != SSA_NAME) { *************** tree_ssa_forward_propagate_single_use_va *** 2130,2139 **** continue; } ! if (gimple_assign_rhs_code (stmt) == ADDR_EXPR /* Handle pointer conversions on invariant addresses as well, as this is valid gimple. */ ! || (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt)) && TREE_CODE (rhs) == ADDR_EXPR && POINTER_TYPE_P (TREE_TYPE (lhs)))) { --- 2167,2176 ---- continue; } ! if (code == ADDR_EXPR /* Handle pointer conversions on invariant addresses as well, as this is valid gimple. */ ! || (CONVERT_EXPR_CODE_P (code) && TREE_CODE (rhs) == ADDR_EXPR && POINTER_TYPE_P (TREE_TYPE (lhs)))) { *************** tree_ssa_forward_propagate_single_use_va *** 2151,2157 **** else gsi_next (&gsi); } ! else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR && can_propagate_from (stmt)) { if (TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST --- 2188,2194 ---- else gsi_next (&gsi); } ! else if (code == POINTER_PLUS_EXPR && can_propagate_from (stmt)) { if (TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST *************** tree_ssa_forward_propagate_single_use_va *** 2183,2196 **** else gsi_next (&gsi); } ! else if ((gimple_assign_rhs_code (stmt) == BIT_NOT_EXPR ! || gimple_assign_rhs_code (stmt) == NEGATE_EXPR) && TREE_CODE (rhs) == SSA_NAME) { simplify_not_neg_expr (&gsi); gsi_next (&gsi); } ! else if (gimple_assign_rhs_code (stmt) == COND_EXPR) { /* In this case the entire COND_EXPR is in rhs1. */ int did_something; --- 2220,2233 ---- else gsi_next (&gsi); } ! else if ((code == BIT_NOT_EXPR ! || code == NEGATE_EXPR) && TREE_CODE (rhs) == SSA_NAME) { simplify_not_neg_expr (&gsi); gsi_next (&gsi); } ! else if (code == COND_EXPR) { /* In this case the entire COND_EXPR is in rhs1. */ int did_something; *************** tree_ssa_forward_propagate_single_use_va *** 2203,2229 **** && did_something, stmt, WARN_STRICT_OVERFLOW_CONDITIONAL); gsi_next (&gsi); } ! else if (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt)) ! == tcc_comparison) { if (forward_propagate_comparison (stmt)) cfg_changed = true; gsi_next (&gsi); } ! else if (gimple_assign_rhs_code (stmt) == BIT_AND_EXPR) { ! simplify_bitwise_and (&gsi, stmt); ! gsi_next (&gsi); } ! else if (gimple_assign_rhs_code (stmt) == PLUS_EXPR ! || gimple_assign_rhs_code (stmt) == MINUS_EXPR) { cfg_changed |= associate_plusminus (stmt); gsi_next (&gsi); } ! else if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt)) ! || gimple_assign_rhs_code (stmt) == FLOAT_EXPR ! || gimple_assign_rhs_code (stmt) == FIX_TRUNC_EXPR) { if (!combine_conversions (&gsi)) gsi_next (&gsi); --- 2240,2267 ---- && did_something, stmt, WARN_STRICT_OVERFLOW_CONDITIONAL); gsi_next (&gsi); } ! else if (TREE_CODE_CLASS (code) == tcc_comparison) { if (forward_propagate_comparison (stmt)) cfg_changed = true; gsi_next (&gsi); } ! else if (code == BIT_AND_EXPR ! || code == BIT_IOR_EXPR ! || code == BIT_XOR_EXPR) { ! if (!simplify_bitwise_binary (&gsi)) ! gsi_next (&gsi); } ! else if (code == PLUS_EXPR ! || code == MINUS_EXPR) { cfg_changed |= associate_plusminus (stmt); gsi_next (&gsi); } ! else if (CONVERT_EXPR_CODE_P (code) ! || code == FLOAT_EXPR ! || code == FIX_TRUNC_EXPR) { if (!combine_conversions (&gsi)) gsi_next (&gsi); Index: gcc/testsuite/gcc.dg/tree-ssa/forwprop-13.c =================================================================== *** gcc/testsuite/gcc.dg/tree-ssa/forwprop-13.c (revision 0) --- gcc/testsuite/gcc.dg/tree-ssa/forwprop-13.c (revision 0) *************** *** 0 **** --- 1,16 ---- + /* { dg-do compile } */ + /* { dg-options "-O -fdump-tree-optimized" } */ + + struct B { + unsigned bit0 : 1; + unsigned bit1 : 1; + }; + + void + foo (struct B *b) + { + b->bit0 = b->bit0 | b->bit1; + } + + /* { dg-final { scan-tree-dump-not "\\\(unsigned char\\\)" "optimized" } } */ + /* { dg-final { cleanup-tree-dump "optimized" } } */