This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[tree-ssa] PATCH to volatile support
- From: Jason Merrill <jason at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Wed, 25 Jun 2003 03:29:39 -0400
- Subject: [tree-ssa] PATCH to volatile support
Most of this patch fixes volatile semantics on the tree-ssa branch:
Evaluating "i++ + 5" will only load from i once.
A statement consisting just of "i" will load from i.
A volatile variable is not a valid GIMPLE value; we need to copy it into a
temporary before using it in an expression. So a volatile reference can
only appear as the full LHS or RHS of a MODIFY_EXPR, not as a
subexpression.
This patch also changes the interaction of gimplify_return_expr and
gimplify_modify_expr: instead of suppressing use of the postqueue in
gimplify_modify_expr, gimplify_return_expr will look through the gimple
form to find the actual modification of the RESULT_DECL. It's still not
especially elegant, but this should avoid a lot of gratuitous conflicts
caused by emitting an increment sooner than necessary.
Tested athlon-pc-linux-gnu, applied to tree-ssa.
2003-06-24 Jason Merrill <jason@redhat.com>
* gimplify.c (gimplify_self_mod_expr): Add want_value parm.
For postfix ops, make sure it returns an rvalue.
(gimplify_expr): Copy a volatile reference into a temp.
(create_tmp_var): Require a complete type.
(create_tmp_alias_var): Use TYPE_VOLATILE on types.
* tree-simple.c (is_gimple_stmt): Flesh out a bit.
(is_gimple_val): Don't allow volatiles.
* c-simplify.c (gimplify_expr_stmt): Don't insert a null pointer.
* gimplify.c (gimplify_return_expr): Search through the gimple
form for the interesting MODIFY_EXPR.
(gimplify_modify_expr): Don't suppress posteffects if want_value.
*** c-simplify.c.~1~ Tue Jun 17 13:19:19 2003
--- c-simplify.c Wed Jun 25 00:34:19 2003
*************** gimplify_expr_stmt (stmt_p)
*** 484,491 ****
}
}
! if (stmts_are_full_exprs_p ())
stmt = build1 (CLEANUP_POINT_EXPR, void_type_node, stmt);
*stmt_p = stmt;
}
--- 484,494 ----
}
}
! if (stmt == NULL_TREE)
! stmt = build_empty_stmt ();
! else if (stmts_are_full_exprs_p ())
stmt = build1 (CLEANUP_POINT_EXPR, void_type_node, stmt);
+
*stmt_p = stmt;
}
*** gimplify.c.~1~ Mon Jun 23 15:55:30 2003
--- gimplify.c Tue Jun 24 18:37:24 2003
*************** static void gimplify_modify_expr PAR
*** 54,60 ****
static void gimplify_compound_expr PARAMS ((tree *, tree *));
static void gimplify_save_expr PARAMS ((tree *, tree *, tree *));
static void gimplify_addr_expr PARAMS ((tree *, tree *, tree *));
! static void gimplify_self_mod_expr PARAMS ((tree *, tree *, tree *));
static void gimplify_cond_expr PARAMS ((tree *, tree *, tree));
static void gimplify_boolean_expr PARAMS ((tree *, tree *));
static void gimplify_return_expr PARAMS ((tree, tree *));
--- 54,60 ----
static void gimplify_compound_expr PARAMS ((tree *, tree *));
static void gimplify_save_expr PARAMS ((tree *, tree *, tree *));
static void gimplify_addr_expr PARAMS ((tree *, tree *, tree *));
! static void gimplify_self_mod_expr PARAMS ((tree *, tree *, tree *, int));
static void gimplify_cond_expr PARAMS ((tree *, tree *, tree));
static void gimplify_boolean_expr PARAMS ((tree *, tree *));
static void gimplify_return_expr PARAMS ((tree, tree *));
*************** gimplify_expr (expr_p, pre_p, post_p, gi
*** 354,360 ****
true, regardless of the structure of the underlying tree, so
if that is our predicate, then we bypass this test and
force gimplification of the expression. FIXME, someone
! should fix is_gimple_stmt. */
if (gimple_test_f != is_gimple_stmt && (*gimple_test_f) (*expr_p))
return 1;
--- 354,364 ----
true, regardless of the structure of the underlying tree, so
if that is our predicate, then we bypass this test and
force gimplification of the expression. FIXME, someone
! should fix is_gimple_stmt.
!
! Actually, my (jason's) theory has been for gimplification to be
! idempotent, and for the predicates to only test for valid forms, not
! whether they are fully simplified. But we aren't there yet. */
if (gimple_test_f != is_gimple_stmt && (*gimple_test_f) (*expr_p))
return 1;
*************** gimplify_expr (expr_p, pre_p, post_p, gi
*** 408,414 ****
case POSTDECREMENT_EXPR:
case PREINCREMENT_EXPR:
case PREDECREMENT_EXPR:
! gimplify_self_mod_expr (expr_p, pre_p, post_p);
break;
case ARRAY_REF:
--- 412,418 ----
case POSTDECREMENT_EXPR:
case PREINCREMENT_EXPR:
case PREDECREMENT_EXPR:
! gimplify_self_mod_expr (expr_p, pre_p, post_p, fallback != fb_none);
break;
case ARRAY_REF:
*************** gimplify_expr (expr_p, pre_p, post_p, gi
*** 690,695 ****
--- 694,722 ----
/* If we replaced *expr_p, gimplify again. */
while (*expr_p && *expr_p != save_expr);
+ if (fallback == fb_none && !is_gimple_stmt (*expr_p))
+ {
+ /* We aren't looking for a value, and we don't have a valid
+ statement. If it doesn't have side-effects, throw it away. */
+ if (!TREE_SIDE_EFFECTS (*expr_p))
+ *expr_p = build_empty_stmt ();
+ else if (!TREE_THIS_VOLATILE (*expr_p))
+ /* We only handle volatiles here; anything else with side-effects
+ must be converted to a valid statement before we get here. */
+ abort ();
+ else if (COMPLETE_TYPE_P (TREE_TYPE (*expr_p)))
+ {
+ /* Historically, the compiler has treated a bare
+ reference to a volatile lvalue as forcing a load. */
+ tree tmp = create_tmp_var (TREE_TYPE (*expr_p), "vol");
+ *expr_p = build (MODIFY_EXPR, TREE_TYPE (tmp), tmp, *expr_p);
+ }
+ else
+ /* We can't do anything useful with a volatile reference to
+ incomplete type, so just throw it away. */
+ *expr_p = build_empty_stmt ();
+ }
+
/* If we are gimplifying at the statement level, we're done. Tack
everything together and replace the original statement with the
gimplified form. */
*************** gimplify_bind_expr (expr_p, pre_p)
*** 898,954 ****
STMT should be stored. */
static void
! gimplify_return_expr (stmt, pre_p)
! tree stmt;
! tree *pre_p;
{
tree ret_expr = TREE_OPERAND (stmt, 0);
! if (ret_expr)
{
! /* We need to pass the full MODIFY_EXPR down so that special handling
! can replace it with something else. FIXME this code is way too
! complicated. */
if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
{
! /* We are trying to return an expression in a void function; move
! the expression to before the return. Note that RET_EXPR
! might be a POSTINCREMENT_EXPR or similar expression which
! requires even more special handling. Ugh. */
! gimplify_expr (&ret_expr, pre_p, NULL, is_gimple_stmt, fb_either);
! add_tree (ret_expr, pre_p);
! TREE_OPERAND (stmt, 0) = NULL_TREE;
! return;
}
! gimplify_expr (&ret_expr, pre_p, NULL, is_gimple_stmt, fb_none);
! /* When compiling C++ code, RET_EXPR can be a RESULT_DECL, which
! is a legitimate return value. */
! if (TREE_CODE (ret_expr) == RESULT_DECL)
! TREE_OPERAND (stmt, 0) = ret_expr;
! else if (TREE_CODE (ret_expr) != MODIFY_EXPR)
{
! /* We're returning a value that is not necessarily a bitwise
! copy. As in the previous case, move the expression to before
! the return. */
! add_tree (ret_expr, pre_p);
! TREE_OPERAND (stmt, 0) = NULL_TREE;
}
! else
! {
! /* We want the RHS to be a value as that makes conversion
! of TRY_FINALLY_EXPRs into TRY_CATCH_EXPRs gimple. The
! only RHS which needs special handling is CALL_EXPRs.
!
! Therefore, if the RHS is a CALL_EXPR, then gimplify the
! RHS to a gimple_val. Otherwise allow any gimple_rhs. */
! gimplify_expr (&TREE_OPERAND (ret_expr, 1), pre_p, NULL,
! (TREE_CODE (TREE_OPERAND (ret_expr, 1)) == CALL_EXPR
! ? is_gimple_val : is_gimple_rhs),
! fb_rvalue);
! TREE_OPERAND (stmt, 0) = ret_expr;
! }
}
}
--- 925,996 ----
STMT should be stored. */
static void
! gimplify_return_expr (tree stmt, tree *pre_p)
{
tree ret_expr = TREE_OPERAND (stmt, 0);
+ tree_stmt_iterator si;
! if (ret_expr && TREE_CODE (ret_expr) != RESULT_DECL)
{
! tree result;
!
if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
+ result = NULL_TREE;
+ else
{
! result = TREE_OPERAND (ret_expr, 0);
! #ifdef ENABLE_CHECKING
! if ((TREE_CODE (ret_expr) != MODIFY_EXPR
! && TREE_CODE (ret_expr) != INIT_EXPR)
! || TREE_CODE (result) != RESULT_DECL)
! abort ();
! #endif
}
! /* We need to pass the full MODIFY_EXPR down so that special handling
! can replace it with something else. */
! gimplify_stmt (&ret_expr);
! /* If there's still a MODIFY_EXPR of the RESULT_DECL after
! gimplification, find it so we can put it in the RETURN_EXPR. */
! TREE_OPERAND (stmt, 0) = NULL_TREE;
! if (result)
{
! tree ret = NULL_TREE;
! tree last = NULL_TREE;
!
! for (si = tsi_start (&ret_expr); !tsi_end_p (si); tsi_next (&si))
! {
! last = tsi_stmt (si);
! if (TREE_CODE (last) == MODIFY_EXPR
! && TREE_OPERAND (last, 0) == result)
! ret = last;
! else if (ret)
! break;
! }
!
! /* If there were posteffects after the MODIFY_EXPR, we need a
! temporary. We also copy the result of a CALL_EXPR into a
! temporary; apparently this simplifies dealing with
! TRY_FINALLY_EXPR somehow. */
! if (ret
! && (ret != last
! || TREE_CODE (TREE_OPERAND (ret, 1)) == CALL_EXPR))
! {
! tree tmp = create_tmp_var (TREE_TYPE (result), "retval");
! TREE_OPERAND (ret, 0) = tmp;
! ret = build (MODIFY_EXPR, TREE_TYPE (result), result, tmp);
! }
!
! if (ret)
! TREE_OPERAND (stmt, 0) = ret;
! else
! /* The return value must be set up some other way. Just tell
! expand_return that we're returning the RESULT_DECL. */
! TREE_OPERAND (stmt, 0) = result;
}
!
! add_tree (ret_expr, pre_p);
}
}
*************** gimplify_compound_lval (expr_p, pre_p, p
*** 1294,1309 ****
*EXPR_P should be stored.
POST_P points to the list where side effects that must happen after
! *EXPR_P should be stored. */
static void
! gimplify_self_mod_expr (expr_p, pre_p, post_p)
! tree *expr_p;
! tree *pre_p;
! tree *post_p;
{
enum tree_code code;
tree lhs, lvalue, rhs, t1;
code = TREE_CODE (*expr_p);
--- 1336,1354 ----
*EXPR_P should be stored.
POST_P points to the list where side effects that must happen after
! *EXPR_P should be stored.
!
! WANT_VALUE is nonzero iff we want to use the value of this expression
! in another expression. */
static void
! gimplify_self_mod_expr (tree *expr_p, tree *pre_p, tree *post_p,
! int want_value)
{
enum tree_code code;
tree lhs, lvalue, rhs, t1;
+ bool postfix;
+ enum tree_code arith_code;
code = TREE_CODE (*expr_p);
*************** gimplify_self_mod_expr (expr_p, pre_p, p
*** 1315,1362 ****
abort ();
#endif
/* Gimplify the LHS into a GIMPLE lvalue. */
lvalue = TREE_OPERAND (*expr_p, 0);
gimplify_expr (&lvalue, pre_p, post_p, is_gimple_modify_expr_lhs,
fb_lvalue);
! /* Extract the operands to the arithmetic operation, including an rvalue
! version of our LHS. */
lhs = lvalue;
- /* And reduce it to an ID. */
- gimplify_expr (&lhs, pre_p, post_p, is_gimple_id, fb_rvalue);
rhs = TREE_OPERAND (*expr_p, 1);
- gimplify_expr (&rhs, pre_p, post_p, is_gimple_val, fb_rvalue);
! /* Determine whether we need to create a PLUS or a MINUS operation. */
! if (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR)
! t1 = build (PLUS_EXPR, TREE_TYPE (*expr_p), lhs, rhs);
! else
! t1 = build (MINUS_EXPR, TREE_TYPE (*expr_p), lhs, rhs);
!
! #if defined ENABLE_CHECKING
! if (!is_gimple_binary_expr (t1))
! abort ();
! #endif
t1 = build (MODIFY_EXPR, TREE_TYPE (lvalue), lvalue, t1);
- if (TREE_LOCUS (*expr_p))
- TREE_LOCUS (t1) = TREE_LOCUS (*expr_p);
- else
- annotate_with_file_line (t1, input_filename, input_line);
! /* Determine whether the new assignment should go before or after
! the gimplified expression. */
! if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
! add_tree (t1, pre_p);
else
! add_tree (t1, post_p);
!
! /* Replace the original expression with the LHS of the assignment. */
! *expr_p = lvalue;
}
-
/* Gimplify the COMPONENT_REF node pointed by EXPR_P.
PRE_P points to the list where side effects that must happen before
--- 1360,1407 ----
abort ();
#endif
+ /* Prefix or postfix? */
+ if (code == POSTINCREMENT_EXPR || code == POSTDECREMENT_EXPR)
+ /* Faster to treat as prefix if result is not used. */
+ postfix = want_value;
+ else
+ postfix = false;
+
+ /* Add or subtract? */
+ if (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR)
+ arith_code = PLUS_EXPR;
+ else
+ arith_code = MINUS_EXPR;
+
/* Gimplify the LHS into a GIMPLE lvalue. */
lvalue = TREE_OPERAND (*expr_p, 0);
gimplify_expr (&lvalue, pre_p, post_p, is_gimple_modify_expr_lhs,
fb_lvalue);
! /* Extract the operands to the arithmetic operation. */
lhs = lvalue;
rhs = TREE_OPERAND (*expr_p, 1);
! if (postfix)
! /* We want to return the original value. */
! gimplify_expr (&lhs, pre_p, post_p, is_gimple_val, fb_rvalue);
+ t1 = build (arith_code, TREE_TYPE (*expr_p), lhs, rhs);
t1 = build (MODIFY_EXPR, TREE_TYPE (lvalue), lvalue, t1);
! if (postfix)
! /* If this is a postfix operator, put the assignment in the postqueue
! and replace the original expression with the (rvalue) LHS. */
! {
! gimplify_stmt (&t1);
! add_tree (t1, post_p);
! *expr_p = lhs;
! }
else
! /* Otherwise, just plug in the assignment and continue. */
! *expr_p = t1;
}
/* Gimplify the COMPONENT_REF node pointed by EXPR_P.
PRE_P points to the list where side effects that must happen before
*************** gimplify_cond_expr (expr_p, pre_p, targe
*** 1785,1802 ****
*EXPR_P should be stored.
POST_P points to the list where side effects that must happen after
! *EXPR_P should be stored. */
static void
! gimplify_modify_expr (expr_p, pre_p, post_p, want_value)
! tree *expr_p;
! tree *pre_p;
! tree *post_p;
! int want_value;
{
tree *from_p = &TREE_OPERAND (*expr_p, 1);
tree *to_p = &TREE_OPERAND (*expr_p, 0);
tree type = TREE_TYPE (*to_p);
#if defined ENABLE_CHECKING
if (TREE_CODE (*expr_p) != MODIFY_EXPR
--- 1830,1847 ----
*EXPR_P should be stored.
POST_P points to the list where side effects that must happen after
! *EXPR_P should be stored.
!
! WANT_VALUE is nonzero iff we want to use the value of this expression
! in another expression. */
static void
! gimplify_modify_expr (tree *expr_p, tree *pre_p, tree *post_p, int want_value)
{
tree *from_p = &TREE_OPERAND (*expr_p, 1);
tree *to_p = &TREE_OPERAND (*expr_p, 0);
tree type = TREE_TYPE (*to_p);
+ int (*pred) (tree);
#if defined ENABLE_CHECKING
if (TREE_CODE (*expr_p) != MODIFY_EXPR
*************** gimplify_modify_expr (expr_p, pre_p, pos
*** 1868,1886 ****
}
init = build (MODIFY_EXPR, TREE_TYPE (purpose), cref, value);
! gimplify_expr (&init, pre_p, NULL, is_gimple_stmt, fb_none);
add_tree (init, pre_p);
}
! *expr_p = TREE_OPERAND (*expr_p, 0);
return;
}
- /* If this is for a RETURN_EXPR, we can't have any posteffects. */
- if (!want_value)
- post_p = NULL;
-
- gimplify_expr (from_p, pre_p, post_p, is_gimple_rhs, fb_rvalue);
-
/* If the RHS of the MODIFY_EXPR may throw and the LHS is a user
variable, then we need to introduce a temporary.
ie temp = RHS; LHS = temp.
--- 1913,1928 ----
}
init = build (MODIFY_EXPR, TREE_TYPE (purpose), cref, value);
! gimplify_stmt (&init);
add_tree (init, pre_p);
}
! if (want_value)
! *expr_p = *to_p;
! else
! *expr_p = build_empty_stmt ();
return;
}
/* If the RHS of the MODIFY_EXPR may throw and the LHS is a user
variable, then we need to introduce a temporary.
ie temp = RHS; LHS = temp.
*************** gimplify_modify_expr (expr_p, pre_p, pos
*** 1888,1908 ****
This way the optimizers can determine that the user variable is
only modified if evaluation of the RHS does not throw.
! FIXME. What to do about cases where the LHS can throw? */
if (flag_exceptions
&& ! (DECL_P (*to_p) && DECL_ARTIFICIAL (*to_p))
&& ((TREE_CODE (*from_p) == CALL_EXPR
&& ! (call_expr_flags (*from_p) & ECF_NOTHROW))
|| (flag_non_call_exceptions && could_trap_p (*from_p))))
! {
! tree tmp = get_initialized_tmp_var (*from_p, pre_p);
! TREE_OPERAND (*expr_p, 1) = tmp;
! }
if (want_value)
{
add_tree (*expr_p, pre_p);
! *expr_p = TREE_OPERAND (*expr_p, 0);
}
}
--- 1930,1951 ----
This way the optimizers can determine that the user variable is
only modified if evaluation of the RHS does not throw.
! FIXME this should be handled by the is_gimple_rhs predicate. */
if (flag_exceptions
&& ! (DECL_P (*to_p) && DECL_ARTIFICIAL (*to_p))
&& ((TREE_CODE (*from_p) == CALL_EXPR
&& ! (call_expr_flags (*from_p) & ECF_NOTHROW))
|| (flag_non_call_exceptions && could_trap_p (*from_p))))
! pred = is_gimple_val;
! else
! pred = is_gimple_rhs;
!
! gimplify_expr (from_p, pre_p, post_p, pred, fb_rvalue);
if (want_value)
{
add_tree (*expr_p, pre_p);
! *expr_p = *to_p;
}
}
*************** create_tmp_var (type, prefix)
*** 2228,2233 ****
--- 2271,2278 ----
frontend, something is wrong. */
if (TREE_CODE (type) == ARRAY_TYPE || TREE_ADDRESSABLE (type))
abort ();
+ if (!COMPLETE_TYPE_P (type))
+ abort ();
#endif
/* Make the type of the variable writable. */
*************** create_tmp_alias_var (type, prefix)
*** 2293,2299 ****
DECL_CONTEXT (tmp_var) = current_function_decl;
TREE_STATIC (tmp_var) = 0;
TREE_USED (tmp_var) = 1;
! TREE_THIS_VOLATILE (tmp_var) = TREE_THIS_VOLATILE (type);
return tmp_var;
--- 2338,2344 ----
DECL_CONTEXT (tmp_var) = current_function_decl;
TREE_STATIC (tmp_var) = 0;
TREE_USED (tmp_var) = 1;
! TREE_THIS_VOLATILE (tmp_var) = TYPE_VOLATILE (type);
return tmp_var;
*** tree-simple.c.~1~ Mon Jun 23 15:55:30 2003
--- tree-simple.c Tue Jun 24 18:37:24 2003
*************** is_gimple_const (t)
*** 604,613 ****
}
int
! is_gimple_stmt (t)
! tree t ATTRIBUTE_UNUSED;
{
! return 1;
}
/* Return nonzero if T is a GIMPLE identifier. */
--- 604,648 ----
}
int
! is_gimple_stmt (tree t)
{
! enum tree_code code = TREE_CODE (t);
! char class = TREE_CODE_CLASS (code);
!
! if (IS_EMPTY_STMT (t))
! return 1;
!
! switch (class)
! {
! case 'r':
! case '1':
! case '2':
! case '<':
! case 'd':
! case 'c':
! /* These should never appear at statement level. */
! return 0;
!
! case 'e':
! case 's':
! /* Might be OK. */
! break;
!
! default:
! /* Not an expression?!? */
! abort ();
! }
!
! switch (code)
! {
! case CALL_EXPR:
! case MODIFY_EXPR:
! return 1;
!
! default:
! /* FIXME enumerate the acceptable codes and change this to 0. */
! return 1;
! }
}
/* Return nonzero if T is a GIMPLE identifier. */
*************** is_gimple_id (t)
*** 643,654 ****
/* Return nonzero if T is an identifier or a constant. */
int
! is_gimple_val (t)
! tree t;
{
if (t == NULL_TREE)
return 1;
return (is_gimple_id (t) || is_gimple_const (t));
}
--- 678,693 ----
/* Return nonzero if T is an identifier or a constant. */
int
! is_gimple_val (tree t)
{
if (t == NULL_TREE)
return 1;
+ /* A volatile decl or _REF is not a valid operand, because we can't reuse
+ it as needed. We need to copy it into a temp first. */
+ if (TREE_THIS_VOLATILE (t))
+ return 0;
+
return (is_gimple_id (t) || is_gimple_const (t));
}