[tree-ssa] Kill more useless type conversions, expose more expressions
law@redhat.com
law@redhat.com
Fri Aug 15 15:37:00 GMT 2003
This patch has three key purposes.
1. Remove (during gimplification) more useless type casts. The code to
determine if a type cast is useless is shared between the dominator
optimizer and the gimplifier.
2. Whenever possible make the type of a COMPONENT_REF expression match
its associated FIELD_DECL type. This is possible when the COMPONENT_REF
is wrapped with a NOP_EXPR -- and we will add a suitable NOP_EXPR in
some cases to make this possible. While this can increase NOP_EXPRs,
it canonicalizes types for memory references which tends to expose
more redundant memory operations.
3. If the RHS of a MODIFY_EXPR is a CALL_EXPR, then the result must
always be stored in a gimple temporary. This tends to expose more
redundant expressions.
Whee. This fixes a few more of the tree-ssa tests.
* gimplify.c (gimplify_expr, cases NOP_EXPR, CONVERT_EXPR): If a
COMPONENT_REF is wrapped with a NOP_EXPR, then force the type of
the COMPONENT_REF to match the accessed field. Strip away
unnecessary type conversions and handle the case where all type
conversions were removed.
(case ARRAY_REF, COMPONENT_REF): Indicate to gimplify_array_ref
and gimplify_component_ref if we want an lvalue or not.
(gimplify_array_ref, gimplify_component_ref): Pass new argument
WANT_LVALUE through to gimplify_compound_lval.
(gimplify_compound_lval): If we do not want an lvalue and the
toplevel COMPONENT_REF's type does not match its field's type,
then wrap the COMPONENT_REF in a NOP_EXPR and force the
COMPONENT_REF's type to match its field's type.
(gimplify_modify_expr): If the RHS is a CALL_EXPR and the LHS
is not a gimple temporary, then force the RHS through a gimple
temporary, even if the call can not throw.
(create_tmp_var): Make sure not to lose the type's attributes
for the new variable.
* tree-ssa.c (tree_ssa_useless_type_conversion): New function.
* tree-flow.h (tree_ssa_useless_type_conversion): Prototype.
* tree-ssa-dom.c (optimize_stmt): Use tree_ssa_useless_type_conversion.
Index: gimplify.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/gimplify.c,v
retrieving revision 1.1.2.69
diff -c -3 -p -r1.1.2.69 gimplify.c
*** gimplify.c 11 Aug 2003 17:17:15 -0000 1.1.2.69
--- gimplify.c 15 Aug 2003 13:15:07 -0000
*************** Software Foundation, 59 Temple Place - S
*** 43,52 ****
#include "real.h"
static void gimplify_constructor (tree, tree *, tree *);
! static void gimplify_array_ref (tree *, tree *, tree *);
static void gimplify_array_ref_to_plus (tree *, tree *, tree *);
! static void gimplify_compound_lval (tree *, tree *, tree *);
! static void gimplify_component_ref (tree *, tree *, tree *);
static void gimplify_call_expr (tree *, tree *, tree *, int (*) (tree));
static void gimplify_tree_list (tree *, tree *, tree *);
static void gimplify_modify_expr (tree *, tree *, tree *, int);
--- 43,52 ----
#include "real.h"
static void gimplify_constructor (tree, tree *, tree *);
! static void gimplify_array_ref (tree *, tree *, tree *, int);
static void gimplify_array_ref_to_plus (tree *, tree *, tree *);
! static void gimplify_compound_lval (tree *, tree *, tree *, int);
! static void gimplify_component_ref (tree *, tree *, tree *, int);
static void gimplify_call_expr (tree *, tree *, tree *, int (*) (tree));
static void gimplify_tree_list (tree *, tree *, tree *);
static void gimplify_modify_expr (tree *, tree *, tree *, int);
*************** gimplify_expr (tree *expr_p, tree *pre_p
*** 409,419 ****
break;
case ARRAY_REF:
! gimplify_array_ref (expr_p, pre_p, post_p);
break;
case COMPONENT_REF:
! gimplify_component_ref (expr_p, pre_p, post_p);
break;
case COND_EXPR:
--- 409,419 ----
break;
case ARRAY_REF:
! gimplify_array_ref (expr_p, pre_p, post_p, fallback & fb_lvalue);
break;
case COMPONENT_REF:
! gimplify_component_ref (expr_p, pre_p, post_p, fallback & fb_lvalue);
break;
case COND_EXPR:
*************** gimplify_expr (tree *expr_p, tree *pre_p
*** 477,491 ****
*expr_p = TREE_OPERAND (*expr_p, 0);
break;
}
! /* Only keep the outermost NOP/CONVERT. */
! STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
! /* And not even that if it's useless. */
! if (TYPE_MAIN_VARIANT (TREE_TYPE (*expr_p))
! == TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (*expr_p, 0))))
{
! *expr_p = TREE_OPERAND (*expr_p, 0);
! break;
}
case FIX_TRUNC_EXPR:
case FIX_CEIL_EXPR:
--- 477,518 ----
*expr_p = TREE_OPERAND (*expr_p, 0);
break;
}
!
! /* If a NOP conversion is changing the type of a COMPONENT_REF
! expression, then it is safe to force the type of the
! COMPONENT_REF to be the same as the type of the field the
! COMPONENT_REF is accessing.
!
! This is a profitable thing to do as canonicalization of
! types on COMPONENT_REFs exposes more redundant COMPONENT_REFs. */
! if (TREE_CODE (TREE_OPERAND (*expr_p, 0)) == COMPONENT_REF)
{
! TREE_TYPE (TREE_OPERAND (*expr_p, 0))
! = TREE_TYPE (TREE_OPERAND (TREE_OPERAND (*expr_p, 0), 1));
! }
!
! /* Strip away as many useless type conversions as possible
! at the toplevel. */
! while (tree_ssa_useless_type_conversion (*expr_p))
! *expr_p = TREE_OPERAND (*expr_p, 0);
!
! /* If we still have a conversion at the toplevel, then strip
! away all but the outermost conversion. */
! if (TREE_CODE (*expr_p) == NOP_EXPR
! || TREE_CODE (*expr_p) == CONVERT_EXPR)
! {
! STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
!
! /* And remove the outermost conversion if it's useless. */
! if (TYPE_MAIN_VARIANT (TREE_TYPE (*expr_p))
! == TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (*expr_p, 0))))
! {
! *expr_p = TREE_OPERAND (*expr_p, 0);
! break;
! }
}
+ else
+ break;
case FIX_TRUNC_EXPR:
case FIX_CEIL_EXPR:
*************** build_addr_expr (tree t)
*** 1287,1293 ****
ARRAY_REF should be extended. */
static void
! gimplify_array_ref (tree *expr_p, tree *pre_p, tree *post_p)
{
#if 1
tree elttype = TREE_TYPE (TREE_TYPE (TREE_OPERAND (*expr_p, 0)));
--- 1314,1321 ----
ARRAY_REF should be extended. */
static void
! gimplify_array_ref (tree *expr_p, tree *pre_p,
! tree *post_p, int want_lvalue)
{
#if 1
tree elttype = TREE_TYPE (TREE_TYPE (TREE_OPERAND (*expr_p, 0)));
*************** gimplify_array_ref (tree *expr_p, tree *
*** 1298,1304 ****
else
/* Handle array and member refs together for now. When alias analysis
improves, we may want to go back to handling them separately. */
! gimplify_compound_lval (expr_p, pre_p, post_p);
#else
tree *p;
varray_type dim_stack;
--- 1326,1332 ----
else
/* Handle array and member refs together for now. When alias analysis
improves, we may want to go back to handling them separately. */
! gimplify_compound_lval (expr_p, pre_p, post_p, want_lvalue);
#else
tree *p;
varray_type dim_stack;
*************** gimplify_array_ref_to_plus (tree *expr_p
*** 1362,1368 ****
*EXPR_P should be stored. */
static void
! gimplify_compound_lval (tree *expr_p, tree *pre_p, tree *post_p)
{
tree *p;
enum tree_code code;
--- 1390,1397 ----
*EXPR_P should be stored. */
static void
! gimplify_compound_lval (tree *expr_p, tree *pre_p,
! tree *post_p, int want_lvalue)
{
tree *p;
enum tree_code code;
*************** gimplify_compound_lval (tree *expr_p, tr
*** 1415,1420 ****
--- 1444,1469 ----
/* Update TREE_SIDE_EFFECTS. */
recalculate_side_effects (t);
}
+
+ /* Now look at the toplevel expression. If it is a COMPONENT_REF and
+ the type of the COMPONENT_REF is different than the field being
+ referenced, then wrap the whole thing inside a NOP_EXPR and force
+ the type of the COMPONENT_REF to be the same as the field being
+ referenced. */
+ if (! want_lvalue
+ && TREE_CODE (*expr_p) == COMPONENT_REF
+ && TREE_TYPE (*expr_p) != TREE_TYPE (TREE_OPERAND (*expr_p, 1)))
+ {
+ tree type_for_nop_expr = TREE_TYPE (*expr_p);
+
+ /* Set the type of the COMPONENT_REF to the type of the field
+ being referenced. */
+ TREE_TYPE (*expr_p) = TREE_TYPE (TREE_OPERAND (*expr_p, 1));
+
+ /* And wrap the whole thing inside a NOP_EXPR. */
+ *expr_p = build1 (NOP_EXPR, type_for_nop_expr, *expr_p);
+ recalculate_side_effects (*expr_p);
+ }
}
/* Gimplify the self modifying expression pointed by EXPR_P (++, --, +=,
-=).
*************** gimplify_self_mod_expr (tree *expr_p, tr
*** 1498,1509 ****
*EXPR_P should be stored. */
static void
! gimplify_component_ref (tree *expr_p, tree *pre_p, tree *post_p)
{
#if 1
/* Handle array and member refs together for now. When alias analysis
improves, we may want to go back to handling them separately. */
! gimplify_compound_lval (expr_p, pre_p, post_p);
#else
tree *p;
--- 1547,1559 ----
*EXPR_P should be stored. */
static void
! gimplify_component_ref (tree *expr_p, tree *pre_p,
! tree *post_p, int want_lvalue)
{
#if 1
/* Handle array and member refs together for now. When alias analysis
improves, we may want to go back to handling them separately. */
! gimplify_compound_lval (expr_p, pre_p, post_p, want_lvalue);
#else
tree *p;
*************** gimplify_modify_expr (tree *expr_p, tree
*** 1999,2007 ****
FIXME this should be handled by the is_gimple_rhs predicate. */
if (! is_gimple_tmp_var (*to_p)
! && ((TREE_CODE (*from_p) == CALL_EXPR
! && ((flag_exceptions && ! (call_expr_flags (*from_p) & ECF_NOTHROW))
! || FUNCTION_RECEIVES_NONLOCAL_GOTO (current_function_decl)))
|| (flag_non_call_exceptions && could_trap_p (*from_p))))
gimplify_expr (from_p, pre_p, post_p, is_gimple_val, fb_rvalue);
--- 2049,2055 ----
FIXME this should be handled by the is_gimple_rhs predicate. */
if (! is_gimple_tmp_var (*to_p)
! && (TREE_CODE (*from_p) == CALL_EXPR
|| (flag_non_call_exceptions && could_trap_p (*from_p))))
gimplify_expr (from_p, pre_p, post_p, is_gimple_val, fb_rvalue);
*************** create_tmp_var (tree type, const char *p
*** 2323,2328 ****
--- 2371,2377 ----
char *tmp_name;
char *preftmp = NULL;
tree tmp_var;
+ tree new_type;
if (prefix)
{
*************** create_tmp_var (tree type, const char *p
*** 2343,2349 ****
#endif
/* Make the type of the variable writable. */
! type = build_type_variant (type, 0, 0);
tmp_var = build_decl (VAR_DECL, get_identifier (tmp_name), type);
--- 2392,2399 ----
#endif
/* Make the type of the variable writable. */
! new_type = build_type_variant (type, 0, 0);
! TYPE_ATTRIBUTES (new_type) = TYPE_ATTRIBUTES (type);
tmp_var = build_decl (VAR_DECL, get_identifier (tmp_name), type);
Index: tree-ssa.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-ssa.c,v
retrieving revision 1.1.4.115
diff -c -3 -p -r1.1.4.115 tree-ssa.c
*** tree-ssa.c 8 Aug 2003 00:27:10 -0000 1.1.4.115
--- tree-ssa.c 15 Aug 2003 13:15:10 -0000
*************** get_def_blocks_for (tree var)
*** 2393,2396 ****
--- 2393,2440 ----
return (struct def_blocks_d *) htab_find (def_blocks, &dm);
}
+ /* Return true if EXPR is a useless type conversion, otherwise return
+ false. */
+
+ bool
+ tree_ssa_useless_type_conversion (tree expr)
+ {
+ /* If we have an assignment that merely uses a NOP_EXPR to change
+ the top of the RHS to the type of the LHS and the type conversion
+ is "safe", then strip away the type conversion so that we can
+ enter LHS = RHS into the const_and_copies table. */
+ if (TREE_CODE (expr) == NOP_EXPR || TREE_CODE (expr) == CONVERT_EXPR)
+ {
+ tree outer_type = TREE_TYPE (expr);
+ tree inner_type = TREE_TYPE (TREE_OPERAND (expr, 0));
+
+ /* If the inner and outer types are effectively the same, then
+ strip the type conversion and enter the equivalence into
+ the table. */
+ if (inner_type == outer_type
+ || TYPE_MAIN_VARIANT (inner_type) == TYPE_MAIN_VARIANT (outer_type))
+ return true;
+
+ /* If the outer type is a (void *), then we can enter the
+ equivalence into the table. The opposite is not true since
+ that conversion would result in a loss of information if
+ the equivalence was used. Consider an indirect function call
+ where we need to know the exact type of the function to
+ correctly implement the ABI. */
+ else if (POINTER_TYPE_P (inner_type) && POINTER_TYPE_P (outer_type)
+ && TREE_CODE (TREE_TYPE (outer_type)) == VOID_TYPE)
+ return true;
+
+ /* If both the inner and outer types are integral types, then
+ we can enter the equivalence if they have the same mode
+ and signedness. */
+ else if (INTEGRAL_TYPE_P (inner_type) && INTEGRAL_TYPE_P (outer_type)
+ && TYPE_MODE (inner_type) == TYPE_MODE (outer_type)
+ && TREE_UNSIGNED (inner_type) == TREE_UNSIGNED (outer_type))
+ return true;
+ }
+
+ return false;
+ }
+
#include "gt-tree-ssa.h"
Index: tree-flow.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-flow.h,v
retrieving revision 1.1.4.101
diff -c -3 -p -r1.1.4.101 tree-flow.h
*** tree-flow.h 8 Aug 2003 00:27:10 -0000 1.1.4.101
--- tree-flow.h 15 Aug 2003 13:15:12 -0000
*************** extern void dump_tree_ssa_stats (FILE *)
*** 470,475 ****
--- 470,476 ----
extern void debug_tree_ssa_stats (void);
extern void ssa_remove_edge (edge);
extern void set_is_used (tree);
+ extern bool tree_ssa_useless_type_conversion (tree);
/* In tree-ssa-pre.c */
extern void tree_perform_ssapre (tree);
Index: tree-ssa-dom.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-ssa-dom.c,v
retrieving revision 1.1.2.23
diff -c -3 -p -r1.1.2.23 tree-ssa-dom.c
*** tree-ssa-dom.c 15 Aug 2003 07:23:42 -0000 1.1.2.23
--- tree-ssa-dom.c 15 Aug 2003 13:15:13 -0000
*************** optimize_stmt (block_stmt_iterator si, v
*** 732,776 ****
if (TREE_CODE (stmt) == MODIFY_EXPR
&& TREE_CODE (TREE_OPERAND (stmt, 0)) == SSA_NAME)
{
! tree rhs;
! rhs = TREE_OPERAND (stmt, 1);
!
! /* Some NOP_EXPRs are totally uninteresting and get in the way
! to optimizing as they prevent equivalences from being recognized.
!
! If we have an assignment that merely uses a NOP_EXPR to change
! the top of the RHS to the type of the LHS and the type conversion
! is "safe", then strip away the type conversion so that we can
! enter LHS = RHS into the const_and_copies table. */
! if (TREE_CODE (rhs) == NOP_EXPR)
! {
! tree outer_type = TREE_TYPE (rhs);
! tree inner_type = TREE_TYPE (TREE_OPERAND (rhs, 0));
!
! /* If the inner and outer types are effectively the same, then
! strip the type conversion and enter the equivalence into
! the table. */
! if (TYPE_MAIN_VARIANT (inner_type) == TYPE_MAIN_VARIANT (outer_type)
! || outer_type == inner_type)
! STRIP_MAIN_TYPE_NOPS (rhs);
! /* If the outer type is a (void *), then we can enter the
! equivalence into the table. The opposite is not true since
! that conversion would result in a loss of information if
! the equivalence was used. Consider an indirect function call
! where we need to know the exact type of the function to
! correctly implement the ABI. */
! else if (POINTER_TYPE_P (outer_type) && POINTER_TYPE_P (inner_type))
! {
! if (TREE_CODE (TREE_TYPE (outer_type)) == VOID_TYPE)
! rhs = TREE_OPERAND (rhs, 0);
! }
! /* If both the inner and outer types are integral types, then
! we can enter the equivalence if they have the same mode
! and signedness. */
! else if (INTEGRAL_TYPE_P (outer_type) && INTEGRAL_TYPE_P (inner_type))
! STRIP_SIGN_NOPS (rhs);
! }
if (may_optimize_p)
{
--- 732,742 ----
if (TREE_CODE (stmt) == MODIFY_EXPR
&& TREE_CODE (TREE_OPERAND (stmt, 0)) == SSA_NAME)
{
! tree rhs = TREE_OPERAND (stmt, 1);
! /* Strip away any useless type conversions. */
! while (tree_ssa_useless_type_conversion (rhs))
! rhs = TREE_OPERAND (rhs, 0);
if (may_optimize_p)
{
More information about the Gcc-patches
mailing list