Overhaul sequence point warnings (again)

From-SVN: r37706
This commit is contained in:
Bernd Schmidt 2000-11-24 11:49:46 +00:00 committed by Bernd Schmidt
parent 58ecb5e2cd
commit 235cfbc40f
6 changed files with 429 additions and 213 deletions

View File

@ -3,6 +3,20 @@
* combine.c (cant_combine_insn_p): New function. * combine.c (cant_combine_insn_p): New function.
(try_combine): Use it. (try_combine): Use it.
* Makefile.in (c-common.o): Depend on $(OBSTACK_H).
* c-common.c (c-obstack.c): Include "obstack.h".
(struct reverse_tree): Delete.
(reverse_list, reverse_max_depth): Delete.
(build_reverse_tree, common_ancestor, modify_ok): Delete functions.
(struct tlist, struct tlist_cache): New.
(tlist_obstack, tlist_firstobj, warned_ids, save_expr_cache): New.
(add_tlist, merge_tlist, verify_tree, warning_candidate_p,
warn_for_collisions, warn_for_collisions_1, new_tlist): New
static functions.
(verify_sequence_points): Rewritten.
* fold-const.c (fold): Don't lose possibly important sequence
points when removing one arm of TRUTH_ORIF_EXPRs or TRUTH_ANDIF_EXPRs.
2000-11-24 Richard Sandiford <rsandifo@redhat.com> 2000-11-24 Richard Sandiford <rsandifo@redhat.com>
* gcc/cse.c (cse_insn): Removed conversion of REG_EQUIV to REG_EQUAL * gcc/cse.c (cse_insn): Removed conversion of REG_EQUIV to REG_EQUAL

View File

@ -1214,7 +1214,7 @@ s-under: $(GCC_PASSES)
# A file used by all variants of C. # A file used by all variants of C.
c-common.o : c-common.c $(CONFIG_H) system.h $(TREE_H) \ c-common.o : c-common.c $(CONFIG_H) system.h $(TREE_H) $(OBSTACK_H) \
$(C_COMMON_H) flags.h toplev.h output.h c-pragma.h $(RTL_H) $(GGC_H) \ $(C_COMMON_H) flags.h toplev.h output.h c-pragma.h $(RTL_H) $(GGC_H) \
$(EXPR_H) diagnostic.h $(EXPR_H) diagnostic.h

View File

@ -33,6 +33,7 @@ Boston, MA 02111-1307, USA. */
#include "tm_p.h" #include "tm_p.h"
#include "intl.h" #include "intl.h"
#include "diagnostic.h" #include "diagnostic.h"
#include "obstack.h"
#if USE_CPPLIB #if USE_CPPLIB
#include "cpplib.h" #include "cpplib.h"
@ -3414,193 +3415,388 @@ convert_and_check (type, expr)
return t; return t;
} }
/* Describe a reversed version of a normal tree, so that we can get to the /* A node in a list that describes references to variables (EXPR), which are
parent of each node. */ either read accesses if WRITER is zero, or write accesses, in which case
struct reverse_tree WRITER is the parent of EXPR. */
struct tlist
{ {
/* All reverse_tree structures for a given tree are chained through this struct tlist *next;
field. */ tree expr, writer;
struct reverse_tree *next;
/* The parent of this node. */
struct reverse_tree *parent;
/* The actual tree node. */
tree x;
/* The operand number this node corresponds to in the parent. */
int operandno;
/* Describe whether this expression is written to or read. */
char read, write;
}; };
/* A list of all reverse_tree structures for a given expression, built by /* Used to implement a cache the results of a call to verify_tree. We only
build_reverse_tree. */ use this for SAVE_EXPRs. */
static struct reverse_tree *reverse_list; struct tlist_cache
/* The maximum depth of a tree, computed by build_reverse_tree. */ {
static int reverse_max_depth; struct tlist_cache *next;
struct tlist *cache_before_sp;
struct tlist *cache_after_sp;
tree expr;
};
static void build_reverse_tree PARAMS ((tree, struct reverse_tree *, int, int, /* Obstack to use when allocating tlist structures, and corresponding
int, int)); firstobj. */
static struct reverse_tree *common_ancestor PARAMS ((struct reverse_tree *, static struct obstack tlist_obstack;
struct reverse_tree *, static char *tlist_firstobj = 0;
struct reverse_tree **,
struct reverse_tree **)); /* Keep track of the identifiers we've warned about, so we can avoid duplicate
static int modify_ok PARAMS ((struct reverse_tree *, struct reverse_tree *)); warnings. */
static struct tlist *warned_ids;
/* SAVE_EXPRs need special treatment. We process them only once and then
cache the results. */
static struct tlist_cache *save_expr_cache;
static void add_tlist PARAMS ((struct tlist **, struct tlist *, tree, int));
static void merge_tlist PARAMS ((struct tlist **, struct tlist *, int));
static void verify_tree PARAMS ((tree, struct tlist **, struct tlist **, tree));
static int warning_candidate_p PARAMS ((tree));
static void warn_for_collisions PARAMS ((struct tlist *));
static void warn_for_collisions_1 PARAMS ((tree, tree, struct tlist *, int));
static struct tlist *new_tlist PARAMS ((struct tlist *, tree, tree));
static void verify_sequence_points PARAMS ((tree)); static void verify_sequence_points PARAMS ((tree));
/* Recursively process an expression, X, building a reverse tree while /* Create a new struct tlist and fill in its fields. */
descending and recording OPERANDNO, READ, and WRITE in the created static struct tlist *
structures. DEPTH is used to compute reverse_max_depth. new_tlist (next, t, writer)
FIXME: if walk_tree gets moved out of the C++ front end, this should struct tlist *next;
probably use walk_tree. */ tree t;
tree writer;
{
struct tlist *l;
l = (struct tlist *) obstack_alloc (&tlist_obstack, sizeof *l);
l->next = next;
l->expr = t;
l->writer = writer;
return l;
}
/* Add duplicates of the nodes found in ADD to the list *TO. If EXCLUDE_WRITER
is nonnull, we ignore any node we find which has a writer equal to it. */
static void static void
build_reverse_tree (x, parent, operandno, read, write, depth) add_tlist (to, add, exclude_writer, copy)
tree x; struct tlist **to;
struct reverse_tree *parent; struct tlist *add;
int operandno, read, write, depth; tree exclude_writer;
int copy;
{ {
struct reverse_tree *node; while (add)
if (x == 0 || x == error_mark_node)
return;
node = (struct reverse_tree *) xmalloc (sizeof (struct reverse_tree));
node->parent = parent;
node->x = x;
node->read = read;
node->write = write;
node->operandno = operandno;
node->next = reverse_list;
reverse_list = node;
if (depth > reverse_max_depth)
reverse_max_depth = depth;
switch (TREE_CODE (x))
{ {
struct tlist *next = add->next;
if (! copy)
add->next = *to;
if (! exclude_writer || add->writer != exclude_writer)
*to = copy ? new_tlist (*to, add->expr, add->writer) : add;
add = next;
}
}
/* Merge the nodes of ADD into TO. This merging process is done so that for
each variable that already exists in TO, no new node is added; however if
there is a write access recorded in ADD, and an occurrence on TO is only
a read access, then the occurrence in TO will be modified to record the
write. */
static void
merge_tlist (to, add, copy)
struct tlist **to;
struct tlist *add;
int copy;
{
struct tlist **end = to;
while (*end)
end = &(*end)->next;
while (add)
{
int found = 0;
struct tlist *tmp2;
struct tlist *next = add->next;
for (tmp2 = *to; tmp2; tmp2 = tmp2->next)
if (tmp2->expr == add->expr)
{
found = 1;
if (! tmp2->writer)
tmp2->writer = add->writer;
}
if (! found)
{
*end = copy ? add : new_tlist (NULL, add->expr, add->writer);
end = &(*end)->next;
*end = 0;
}
add = next;
}
}
/* WRITTEN is a variable, WRITER is its parent. Warn if any of the variable
references in list LIST conflict with it, excluding reads if ONLY writers
is nonzero. */
static void
warn_for_collisions_1 (written, writer, list, only_writes)
tree written, writer;
struct tlist *list;
int only_writes;
{
struct tlist *tmp;
/* Avoid duplicate warnings. */
for (tmp = warned_ids; tmp; tmp = tmp->next)
if (tmp->expr == written)
return;
while (list)
{
if (list->expr == written
&& list->writer != writer
&& (! only_writes || list->writer))
{
warned_ids = new_tlist (warned_ids, written, NULL_TREE);
warning ("operation on `%s' may be undefined",
IDENTIFIER_POINTER (DECL_NAME (list->expr)));
}
list = list->next;
}
}
/* Given a list LIST of references to variables, find whether any of these
can cause conflicts due to missing sequence points. */
static void
warn_for_collisions (list)
struct tlist *list;
{
struct tlist *tmp;
for (tmp = list; tmp; tmp = tmp->next)
{
if (tmp->writer)
warn_for_collisions_1 (tmp->expr, tmp->writer, list, 0);
}
}
/* Return nonzero if X is a tree that can be verified by the sequence poitn
warnings. */
static int
warning_candidate_p (x)
tree x;
{
return TREE_CODE (x) == VAR_DECL || TREE_CODE (x) == PARM_DECL;
}
/* Walk the tree X, and record accesses to variables. If X is written by the
parent tree, WRITER is the parent.
We store accesses in one of the two lists: PBEFORE_SP, and PNO_SP. If this
expression or its only operand forces a sequence point, then everything up
to the sequence point is stored in PBEFORE_SP. Everything else gets stored
in PNO_SP.
Once we return, we will have emitted warnings if any subexpression before
such a sequence point could be undefined. On a higher level, however, the
sequence point may not be relevant, and we'll merge the two lists.
Example: (b++, a) + b;
The call that processes the COMPOUND_EXPR will store the increment of B
in PBEFORE_SP, and the use of A in PNO_SP. The higher-level call that
processes the PLUS_EXPR will need to merge the two lists so that
eventually, all accesses end up on the same list (and we'll warn about the
unordered subexpressions b++ and b.
A note on merging. If we modify the former example so that our expression
becomes
(b++, b) + a
care must be taken not simply to add all three expressions into the final
PNO_SP list. The function merge_tlist takes care of that by merging the
before-SP list of the COMPOUND_EXPR into its after-SP list in a special
way, so that no more than one access to B is recorded. */
static void
verify_tree (x, pbefore_sp, pno_sp, writer)
tree x;
struct tlist **pbefore_sp, **pno_sp;
tree writer;
{
struct tlist *tmp_before, *tmp_nosp, *tmp_list2, *tmp_list3;
enum tree_code code;
char class;
restart:
code = TREE_CODE (x);
class = TREE_CODE_CLASS (code);
if (warning_candidate_p (x))
{
*pno_sp = new_tlist (*pno_sp, x, writer);
return;
}
switch (code)
{
case COMPOUND_EXPR:
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
tmp_before = tmp_nosp = tmp_list3 = 0;
verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_nosp, NULL_TREE);
warn_for_collisions (tmp_nosp);
merge_tlist (pbefore_sp, tmp_before, 0);
merge_tlist (pbefore_sp, tmp_nosp, 0);
verify_tree (TREE_OPERAND (x, 1), &tmp_list3, pno_sp, NULL_TREE);
merge_tlist (pbefore_sp, tmp_list3, 0);
return;
case COND_EXPR:
tmp_before = tmp_list2 = 0;
verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_list2, NULL_TREE);
warn_for_collisions (tmp_list2);
merge_tlist (pbefore_sp, tmp_before, 0);
merge_tlist (pbefore_sp, tmp_list2, 1);
tmp_list3 = tmp_nosp = 0;
verify_tree (TREE_OPERAND (x, 1), &tmp_list3, &tmp_nosp, NULL_TREE);
warn_for_collisions (tmp_nosp);
merge_tlist (pbefore_sp, tmp_list3, 0);
tmp_list3 = tmp_list2 = 0;
verify_tree (TREE_OPERAND (x, 2), &tmp_list3, &tmp_list2, NULL_TREE);
warn_for_collisions (tmp_list2);
merge_tlist (pbefore_sp, tmp_list3, 0);
/* Rather than add both tmp_nosp and tmp_list2, we have to merge the
two first, to avoid warning for (a ? b++ : b++). */
merge_tlist (&tmp_nosp, tmp_list2, 0);
add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0);
return;
case PREDECREMENT_EXPR: case PREDECREMENT_EXPR:
case PREINCREMENT_EXPR: case PREINCREMENT_EXPR:
case POSTDECREMENT_EXPR: case POSTDECREMENT_EXPR:
case POSTINCREMENT_EXPR: case POSTINCREMENT_EXPR:
build_reverse_tree (TREE_OPERAND (x, 0), node, 0, 1, 1, depth + 1); verify_tree (TREE_OPERAND (x, 0), pno_sp, pno_sp, x);
break; return;
case MODIFY_EXPR:
tmp_before = tmp_nosp = tmp_list3 = 0;
verify_tree (TREE_OPERAND (x, 1), &tmp_before, &tmp_nosp, NULL_TREE);
verify_tree (TREE_OPERAND (x, 0), &tmp_list3, &tmp_list3, x);
/* Expressions inside the LHS are not ordered wrt. the sequence points
in the RHS. Example:
*a = (a++, 2)
Despite the fact that the modification of "a" is in the before_sp
list (tmp_before), it conflicts with the use of "a" in the LHS.
We can handle this by adding the contents of tmp_list3
to those of tmp_before, and redoing the collision warnings for that
list. */
add_tlist (&tmp_before, tmp_list3, x, 1);
warn_for_collisions (tmp_before);
/* Exclude the LHS itself here; we first have to merge it into the
tmp_nosp list. This is done to avoid warning for "a = a"; if we
didn't exclude the LHS, we'd get it twice, once as a read and once
as a write. */
add_tlist (pno_sp, tmp_list3, x, 0);
warn_for_collisions_1 (TREE_OPERAND (x, 0), x, tmp_nosp, 1);
merge_tlist (pbefore_sp, tmp_before, 0);
if (warning_candidate_p (TREE_OPERAND (x, 0)))
merge_tlist (&tmp_nosp, new_tlist (NULL, TREE_OPERAND (x, 0), x), 0);
add_tlist (pno_sp, tmp_nosp, NULL_TREE, 1);
return;
case CALL_EXPR: case CALL_EXPR:
build_reverse_tree (TREE_OPERAND (x, 0), node, 0, 1, 0, depth + 1); /* We need to warn about conflicts among arguments and conflicts between
x = TREE_OPERAND (x, 1); args and the function address. Side effects of the function address,
while (x) however, are not ordered by the sequence point of the call. */
{ tmp_before = tmp_nosp = tmp_list2 = tmp_list3 = 0;
build_reverse_tree (TREE_VALUE (x), node, 1, 1, 0, depth + 1); verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_nosp, NULL_TREE);
x = TREE_CHAIN (x); if (TREE_OPERAND (x, 1))
} verify_tree (TREE_OPERAND (x, 1), &tmp_list2, &tmp_list3, NULL_TREE);
break; merge_tlist (&tmp_list3, tmp_list2, 0);
add_tlist (&tmp_before, tmp_list3, NULL_TREE, 0);
add_tlist (&tmp_before, tmp_nosp, NULL_TREE, 0);
warn_for_collisions (tmp_before);
add_tlist (pbefore_sp, tmp_before, NULL_TREE, 0);
return;
case TREE_LIST: case TREE_LIST:
/* Scan all the list, e.g. indices of multi dimensional array. */ /* Scan all the list, e.g. indices of multi dimensional array. */
while (x) while (x)
{ {
build_reverse_tree (TREE_VALUE (x), node, 0, 1, 0, depth + 1); tmp_before = tmp_nosp = 0;
verify_tree (TREE_VALUE (x), &tmp_before, &tmp_nosp, NULL_TREE);
merge_tlist (&tmp_nosp, tmp_before, 0);
add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0);
x = TREE_CHAIN (x); x = TREE_CHAIN (x);
} }
break; return;
case MODIFY_EXPR: case SAVE_EXPR:
build_reverse_tree (TREE_OPERAND (x, 0), node, 0, 0, 1, depth + 1); {
build_reverse_tree (TREE_OPERAND (x, 1), node, 1, 1, 0, depth + 1); struct tlist_cache *t;
break; for (t = save_expr_cache; t; t = t->next)
if (t->expr == x)
default:
switch (TREE_CODE_CLASS (TREE_CODE (x)))
{
case 'r':
case '<':
case '2':
case 'b':
case '1':
case 'e':
case 's':
case 'x':
{
int lp;
int max = first_rtl_op (TREE_CODE (x));
for (lp = 0; lp < max; lp++)
build_reverse_tree (TREE_OPERAND (x, lp), node, lp, 1, 0,
depth + 1);
break; break;
if (! t)
{
t = (struct tlist_cache *) obstack_alloc (&tlist_obstack,
sizeof *t);
t->next = save_expr_cache;
t->expr = x;
save_expr_cache = t;
tmp_before = tmp_nosp = 0;
verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_nosp, NULL_TREE);
warn_for_collisions (tmp_nosp);
tmp_list3 = 0;
while (tmp_nosp)
{
struct tlist *t = tmp_nosp;
tmp_nosp = t->next;
merge_tlist (&tmp_list3, t, 0);
}
t->cache_before_sp = tmp_before;
t->cache_after_sp = tmp_list3;
} }
default: merge_tlist (pbefore_sp, t->cache_before_sp, 1);
break; add_tlist (pno_sp, t->cache_after_sp, NULL_TREE, 1);
} return;
}
default:
break; break;
} }
}
/* Given nodes P1 and P2 as well as enough scratch space pointed to by TMP1 if (class == '1')
and TMP2, find the common ancestor of P1 and P2. */
static struct reverse_tree *
common_ancestor (p1, p2, tmp1, tmp2)
struct reverse_tree *p1, *p2;
struct reverse_tree **tmp1, **tmp2;
{
struct reverse_tree *t1 = p1;
struct reverse_tree *t2 = p2;
int i, j;
/* First, check if we're actually looking at the same expression twice,
which can happen if it's wrapped in a SAVE_EXPR - in this case there's
no chance of conflict. */
while (t1 && t2 && t1->x == t2->x)
{ {
if (TREE_CODE (t1->x) == SAVE_EXPR) if (first_rtl_op (code) == 0)
return 0; return;
t1 = t1->parent; x = TREE_OPERAND (x, 0);
t2 = t2->parent; writer = 0;
goto restart;
} }
for (i = 0; p1; i++, p1 = p1->parent) switch (class)
tmp1[i] = p1;
for (j = 0; p2; j++, p2 = p2->parent)
tmp2[j] = p2;
while (tmp1[i - 1] == tmp2[j - 1])
i--, j--;
return tmp1[i];
}
/* Subroutine of verify_sequence_points to check whether a node T corresponding
to a MODIFY_EXPR invokes undefined behaviour. OTHER occurs somewhere in the
RHS, and an identical expression is the LHS of T.
For MODIFY_EXPRs, some special cases apply when testing for undefined
behaviour if one of the expressions we found is the LHS of the MODIFY_EXPR.
If the other expression is just a use, then there's no undefined behaviour.
Likewise, if the other expression is wrapped inside another expression that
will force a sequence point, then there's no undefined behaviour either. */
static int
modify_ok (t, other)
struct reverse_tree *t, *other;
{
struct reverse_tree *p;
if (! other->write)
return 1;
/* See if there's an intervening sequence point. */
for (p = other; p->parent != t; p = p->parent)
{ {
if ((TREE_CODE (p->parent->x) == COMPOUND_EXPR case 'r':
|| TREE_CODE (p->parent->x) == TRUTH_ANDIF_EXPR case '<':
|| TREE_CODE (p->parent->x) == TRUTH_ORIF_EXPR case '2':
|| TREE_CODE (p->parent->x) == COND_EXPR) case 'b':
&& p->operandno == 0) case 'e':
return 1; case 's':
if (TREE_CODE (p->parent->x) == SAVE_EXPR) case 'x':
return 1; {
if (TREE_CODE (p->parent->x) == CALL_EXPR int lp;
&& p->operandno != 0) int max = first_rtl_op (TREE_CODE (x));
return 1; for (lp = 0; lp < max; lp++)
{
tmp_before = tmp_nosp = 0;
verify_tree (TREE_OPERAND (x, lp), &tmp_before, &tmp_nosp, NULL_TREE);
merge_tlist (&tmp_nosp, tmp_before, 0);
add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0);
}
break;
}
} }
return 0;
} }
/* Try to warn for undefined behaviour in EXPR due to missing sequence /* Try to warn for undefined behaviour in EXPR due to missing sequence
@ -3610,65 +3806,19 @@ static void
verify_sequence_points (expr) verify_sequence_points (expr)
tree expr; tree expr;
{ {
struct reverse_tree **tmp1, **tmp2; struct tlist *before_sp = 0, *after_sp = 0;
struct reverse_tree *p;
reverse_list = 0; warned_ids = 0;
reverse_max_depth = 0; save_expr_cache = 0;
build_reverse_tree (expr, NULL, 0, 1, 0, 1); if (tlist_firstobj == 0)
tmp1 = (struct reverse_tree **) xmalloc (sizeof (struct reverse_tree *)
* reverse_max_depth);
tmp2 = (struct reverse_tree **) xmalloc (sizeof (struct reverse_tree *)
* reverse_max_depth);
/* Search for multiple occurrences of the same variable, where either both
occurrences are writes, or one is a read and a write. If we can't prove
that these are ordered by a sequence point, warn that the expression is
undefined. */
for (p = reverse_list; p; p = p->next)
{ {
struct reverse_tree *p2; gcc_obstack_init (&tlist_obstack);
if (TREE_CODE (p->x) != VAR_DECL && TREE_CODE (p->x) != PARM_DECL) tlist_firstobj = obstack_alloc (&tlist_obstack, 0);
continue;
for (p2 = p->next; p2; p2 = p2->next)
{
if ((TREE_CODE (p2->x) == VAR_DECL || TREE_CODE (p2->x) == PARM_DECL)
&& DECL_NAME (p->x) == DECL_NAME (p2->x)
&& (p->write || p2->write))
{
struct reverse_tree *t = common_ancestor (p, p2, tmp1, tmp2);
if (t == 0
|| TREE_CODE (t->x) == COMPOUND_EXPR
|| TREE_CODE (t->x) == TRUTH_ANDIF_EXPR
|| TREE_CODE (t->x) == TRUTH_ORIF_EXPR
|| TREE_CODE (t->x) == COND_EXPR)
continue;
if (TREE_CODE (t->x) == MODIFY_EXPR
&& p->parent == t
&& modify_ok (t, p2))
continue;
if (TREE_CODE (t->x) == MODIFY_EXPR
&& p2->parent == t
&& modify_ok (t, p))
continue;
warning ("operation on `%s' may be undefined",
IDENTIFIER_POINTER (DECL_NAME (p->x)));
break;
}
}
} }
while (reverse_list) verify_tree (expr, &before_sp, &after_sp, 0);
{ warn_for_collisions (after_sp);
struct reverse_tree *p = reverse_list; obstack_free (&tlist_obstack, tlist_firstobj);
reverse_list = p->next;
free (p);
}
free (tmp1);
free (tmp2);
} }
void void

View File

@ -5962,7 +5962,9 @@ fold (expr)
/* If either arg is constant true, drop it. */ /* If either arg is constant true, drop it. */
if (TREE_CODE (arg0) == INTEGER_CST && ! integer_zerop (arg0)) if (TREE_CODE (arg0) == INTEGER_CST && ! integer_zerop (arg0))
return non_lvalue (convert (type, arg1)); return non_lvalue (convert (type, arg1));
if (TREE_CODE (arg1) == INTEGER_CST && ! integer_zerop (arg1)) if (TREE_CODE (arg1) == INTEGER_CST && ! integer_zerop (arg1)
/* Preserve sequence points. */
&& (code != TRUTH_ANDIF_EXPR || ! TREE_SIDE_EFFECTS (arg0)))
return non_lvalue (convert (type, arg0)); return non_lvalue (convert (type, arg0));
/* If second arg is constant zero, result is zero, but first arg /* If second arg is constant zero, result is zero, but first arg
must be evaluated. */ must be evaluated. */
@ -6048,7 +6050,9 @@ fold (expr)
/* If either arg is constant zero, drop it. */ /* If either arg is constant zero, drop it. */
if (TREE_CODE (arg0) == INTEGER_CST && integer_zerop (arg0)) if (TREE_CODE (arg0) == INTEGER_CST && integer_zerop (arg0))
return non_lvalue (convert (type, arg1)); return non_lvalue (convert (type, arg1));
if (TREE_CODE (arg1) == INTEGER_CST && integer_zerop (arg1)) if (TREE_CODE (arg1) == INTEGER_CST && integer_zerop (arg1)
/* Preserve sequence points. */
&& (code != TRUTH_ORIF_EXPR || ! TREE_SIDE_EFFECTS (arg0)))
return non_lvalue (convert (type, arg0)); return non_lvalue (convert (type, arg0));
/* If second arg is constant true, result is true, but we must /* If second arg is constant true, result is true, but we must
evaluate first arg. */ evaluate first arg. */

View File

@ -1,3 +1,7 @@
2000-11-24 Bernd Schmidt <bernds@redhat.co.uk>
* gcc.dg/sequence-point-1.c: Add some new tests.
2000-11-24 Nathan Sidwell <nathan@codesourcery.com> 2000-11-24 Nathan Sidwell <nathan@codesourcery.com>
* g++.old-deja/g++.other/vaarg4.C: New test. * g++.old-deja/g++.other/vaarg4.C: New test.

View File

@ -19,7 +19,7 @@ typedef __SIZE_TYPE__ size_t;
void void
foo (int a, int b, int n, int p, int *ptr, struct s *sptr, foo (int a, int b, int n, int p, int *ptr, struct s *sptr,
int *ap, int *bp, int **cp, char *ans) int *ap, int *bp, int **cp, char *ans, int (*fnp[8])(int))
{ {
int len; int len;
@ -44,6 +44,9 @@ foo (int a, int b, int n, int p, int *ptr, struct s *sptr,
a = (bp[a++] = b) + 1; /* { dg-warning "undefined" "sequence point warning" } */ a = (bp[a++] = b) + 1; /* { dg-warning "undefined" "sequence point warning" } */
a = b++ * b++; /* { dg-warning "undefined" "sequence point warning" } */ a = b++ * b++; /* { dg-warning "undefined" "sequence point warning" } */
a = fnb (b++, b++); /* { dg-warning "undefined" "sequence point warning" } */ a = fnb (b++, b++); /* { dg-warning "undefined" "sequence point warning" } */
a = (*fnp[b++]) (b++); /* { dg-warning "undefined" "sequence point warning" } */
a = (*fnp[b]) (b++); /* { dg-warning "undefined" "sequence point warning" } */
a = (*fnp[b++]) (b); /* { dg-warning "undefined" "sequence point warning" } */
*ap = fnc (ap++); /* { dg-warning "undefined" "sequence point warning" } */ *ap = fnc (ap++); /* { dg-warning "undefined" "sequence point warning" } */
(a += b) + (a += n); /* { dg-warning "undefined" "sequence point warning" } */ (a += b) + (a += n); /* { dg-warning "undefined" "sequence point warning" } */
a = (b, b++) + (b++, b); /* { dg-warning "undefined" "sequence point warning" } */ a = (b, b++) + (b++, b); /* { dg-warning "undefined" "sequence point warning" } */
@ -51,10 +54,25 @@ foo (int a, int b, int n, int p, int *ptr, struct s *sptr,
ap[a+=1] += a; /* { dg-warning "undefined" "sequence point warning" } */ ap[a+=1] += a; /* { dg-warning "undefined" "sequence point warning" } */
ap[a++] += a++; /* { dg-warning "undefined" "sequence point warning" } */ ap[a++] += a++; /* { dg-warning "undefined" "sequence point warning" } */
ap[a+=1] += a++; /* { dg-warning "undefined" "sequence point warning" } */ ap[a+=1] += a++; /* { dg-warning "undefined" "sequence point warning" } */
a = a++, b = a; /* { dg-warning "undefined" "sequence point warning" } */
b = a, a = a++; /* { dg-warning "undefined" "sequence point warning" } */
a = (b++ ? n : a) + b; /* { dg-warning "undefined" "sequence point warning" } */
b ? a = a++ : a; /* { dg-warning "undefined" "sequence point warning" } */
b ? a : a = a++; /* { dg-warning "undefined" "sequence point warning" } */
b && (a = a++); /* { dg-warning "undefined" "sequence point warning" } */
(a = a++) && b; /* { dg-warning "undefined" "sequence point warning" } */
b, (a = a++); /* { dg-warning "undefined" "sequence point warning" } */
(a = a++), b; /* { dg-warning "undefined" "sequence point warning" } */
a ^= b ^= a ^= b; /* { dg-warning "undefined" "sequence point warning" } */
a = a; /* { dg-bogus "undefined" "bogus sequence point warning" } */
a = (a++ && 4); /* { dg-bogus "undefined" "bogus sequence point warning" } */ a = (a++ && 4); /* { dg-bogus "undefined" "bogus sequence point warning" } */
a = ! (a++ && 4); /* { dg-bogus "undefined" "bogus sequence point warning" } */
a = - (a++ && 4); /* { dg-bogus "undefined" "bogus sequence point warning" } */
a = (double) (a++ && 4); /* { dg-bogus "undefined" "bogus sequence point warning" } */
len = sprintf (ans, "%d", len++); /* { dg-bogus "undefined" "bogus sequence point warning" } */ len = sprintf (ans, "%d", len++); /* { dg-bogus "undefined" "bogus sequence point warning" } */
a = fn (a++); /* { dg-bogus "undefined" "sequence point warning" } */ a = fn (a++); /* { dg-bogus "undefined" "bogus sequence point warning" } */
b++, (b + b); /* { dg-bogus "undefined" "bogus sequence point warning" } */
(a = b++), (a = b++); /* { dg-bogus "undefined" "bogus sequence point warning" } */ (a = b++), (a = b++); /* { dg-bogus "undefined" "bogus sequence point warning" } */
a = (b++, b++); /* { dg-bogus "undefined" "bogus sequence point warning" } */ a = (b++, b++); /* { dg-bogus "undefined" "bogus sequence point warning" } */
a = b++ && b++; /* { dg-bogus "undefined" "bogus sequence point warning" } */ a = b++ && b++; /* { dg-bogus "undefined" "bogus sequence point warning" } */
@ -63,4 +81,30 @@ foo (int a, int b, int n, int p, int *ptr, struct s *sptr,
a = (b++ ? a : b++); /* { dg-bogus "undefined" "bogus sequence point warning" } */ a = (b++ ? a : b++); /* { dg-bogus "undefined" "bogus sequence point warning" } */
ap[a++] += bp[b]; /* { dg-bogus "undefined" "bogus sequence point warning" } */ ap[a++] += bp[b]; /* { dg-bogus "undefined" "bogus sequence point warning" } */
ap[a += 1] += 1; /* { dg-bogus "undefined" "bogus sequence point warning" } */ ap[a += 1] += 1; /* { dg-bogus "undefined" "bogus sequence point warning" } */
*ptr < 128 ? *ptr++ : *(ptr += 2); /* { dg-bogus "undefined" "bogus sequence point warning" } */
/* The following will be represented internally with a tree consisting of
many duplicated SAVE_EXPRs. This caused the previous version of the
sequence point warning code to fail by running out of virtual memory. */
a = ((b & 1 ? 21 : 0)
| (b & 2 ? 22 : 0)
| (b & 3 ? 23 : 0)
| (b & 4 ? 24 : 0)
| (b & 5 ? 25 : 0)
| (b & 6 ? 26 : 0)
| (b & 7 ? 27 : 0)
| (b & 8 ? 28 : 0)
| (b & 9 ? 29 : 0)
| (b & 10 ? 30 : 0)
| (b & 11 ? 31 : 0)
| (b & 12 ? 32 : 0)
| (b & 13 ? 1 : 0)
| (b & 14 ? 2 : 0)
| (b & 15 ? 3 : 0)
| (b & 16 ? 4 : 0)
| (b & 17 ? 5 : 0)
| (b & 18 ? 6 : 0)
| (b & 19 ? 7 : 0)
| (b & 20 ? 8 : 0)
| (b & 21 ? 9 : 0));
} }