Overhaul sequence point warnings (again)
From-SVN: r37706
This commit is contained in:
parent
58ecb5e2cd
commit
235cfbc40f
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
566
gcc/c-common.c
566
gcc/c-common.c
|
@ -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
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue