This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
-ftree-check for review
- From: "Sebastian Pop" <sebastian dot pop at inria dot fr>
- To: "GCC Patches" <gcc-patches at gcc dot gnu dot org>, "Nic Volanschi" <nic dot volanschi at free dot fr>
- Date: Fri, 13 Apr 2007 15:32:00 -0400
- Subject: -ftree-check for review
Hi,
Here is the patch that implements the tree-checker proposed by Nic
for performing user-defined checks on the tree/CFG form.
This patch just moves the code from the graphite-branch to trunk.
Bootstrapped and tested on i686-linux. Okay for trunk?
For extra coverage of the tree-checker code, we can also activate,
when the compiler is bootstrapping with checking, custom checks
for va_end, as Zack suggested in
http://gcc.gnu.org/ml/gcc/2006-04/msg00062.html
Sebastian
2007-04-13 Nic Volanschi <nic.volanschi@free.fr>
Sebastian Pop <sebastian.pop@inria.fr>
* doc/invoke.texi (-ftree-check, -ftree-checks,
-ftree-check-verbose): Documented.
* flags.h (tree_check_file, tree_check_string): New.
* tree-pretty-print.c (maybe_init_pretty_print): Define extern.
(do_niy, dump_generic_node): Special case for pp_lazy_mode.
(dump_generic_node_1, lazy_dump_generic_node,
lazy_print_generic_expr): New.
* tree.c (tree_name): New.
* tree.h (tree_name): Declared.
* tree-pass.h (pass_check): Declared.
* diagnostic.h (maybe_init_pretty_print, lazy_dump_generic_node,
lazy_print_generic_expr): Declared.
* toplev.c (tree_check_file, tree_check_string): Defined.
* pretty-print.c: Include vec.h and diagnostic.h.
(pp_clear_state, pp_append_r, pp_base_format, pp_base_flush,
pp_base_formatted_text, pp_base_last_position_in_text,
pp_base_newline, pp_base_character): Special case for pp_lazy_mode.
(pp_write_list_to_stream, pp_lazy_mode, new_tree_chunk, pp_add_tree,
pp_add_string, pp_add_char, pp_write_list, pp_free_list): New.
(pp_construct): Initialize pretty printers buffer chunks.
* pretty-print.h: (tree_chunk_s, tree_chunk): New and define VECs of
tree_chunk pointers.
(chunks): New.
(new_tree_chunk, pp_lazy_mode, pp_add_tree, pp_add_string, pp_add_char,
pp_free_list, pp_write_list): Declared.
* opts.c (common_handle_option): Get arguments of -ftree-check and
-ftree-checks.
* timevar.def (TV_TREE_CHECK): New.
* common.opt (ftree-check, ftree-checks, ftree-checks-verbose): New.
* Makefile.in (condate.tab.o, tree-check.o, tree-match.o): Added.
(pretty-print.o): Depends on vec.h and DIAGNOSTIC_H.
(GTFILES): Add tree-check.c and tree-match.c.
* passes.c (pass_check): Scheduled.
* condate.y: New.
* tree-check.c: New.
* tree-match.h: New.
* tree-match.c: New.
* testsuite/gcc.dg/tree-checker/tree-checks-1.c: New.
* testsuite/gcc.dg/tree-checker/tree-checks-2.c: New.
* testsuite/gcc.dg/tree-checker/tree-checks-3.c: New.
* testsuite/gcc.dg/tree-checker/tree-checks-4.c: New.
* testsuite/gcc.dg/tree-checker/tree-checks.exp: New.
* testsuite/gcc.dg/tree-checker/condates.crp: New.
Index: doc/invoke.texi
===================================================================
--- doc/invoke.texi (revision 123789)
+++ doc/invoke.texi (working copy)
@@ -130,6 +130,7 @@ only one of these two forms, whichever o
* Language Independent Options:: Controlling how diagnostics should be
formatted.
* Warning Options:: How picky should the compiler be?
+* User-Defined Checking Options:: Customizing the compiler checks
* Debugging Options:: Symbol tables, measurements, and debugging dumps.
* Optimize Options:: How much optimization?
* Preprocessor Options:: Controlling header files and macro definitions.
@@ -352,6 +353,7 @@ Objective-C and Objective-C++ Dialects}.
-ftree-pre -ftree-ccp -ftree-dce -ftree-loop-optimize @gol
-ftree-loop-linear -ftree-loop-im -ftree-loop-ivcanon -fivopts @gol
-fcheck-data-deps @gol
+-ftree-check=@var{pattern} -ftree-checks=@var{file} -ftree-check-verbose @gol
-ftree-dominator-opts -ftree-dse -ftree-copyrename -ftree-sink @gol
-ftree-ch -ftree-sra -ftree-ter -ftree-fre -ftree-vectorize @gol
-ftree-vect-loop-version -ftree-salias -fipa-pta -fweb @gol
@@ -3828,6 +3830,194 @@ This option is implied by @option{-pedan
@option{-Wno-overlength-strings}.
@end table
+@node User-Defined Checking Options
+@section User-Defined Checks
+@cindex condates
+
+GCC can be customized very easily to perform user-defined checks for
+detecting for example memory leaks, unreleased locks, or null pointer
+dereferences. User-defined checks are performed in addition to normal
+compilation, and may result in additional warning messages.
+
+Currently, only the C language takes into account user-defined
+checking options.
+
+The following options control user-defined checks:
+
+@table @gcctabopt
+@cindex syntax checking
+@item -ftree-check=@var{pattern}
+@opindex ftree-check
+Raise a warning for every statement matching an atomic syntactic
+@var{pattern} in the source files being compiled.
+
+Atomic syntactic patterns are plain C code fragments but possibly
+containing pattern variables. Pattern variables are sometimes called
+meta-variables, to distinguish them from variables of the C language.
+Pattern variables are represented by the @code{%} character followed
+by a variable name consisting of one letter. For example, @code{%X =
+malloc (%Y)}, @code{return %X}, @code{%X = %X + 1}, @code{%X++},
+@code{%X * 3}, @code{sizeof (%X)}, and @code{%X == 0} are atomic
+patterns representing C statements or expressions. Note that there is
+no special difference between atomic statement patterns and atomic
+expression patterns.
+
+A C fragment matches an atomic pattern if there is a substitution
+mapping the pattern variables to C sub-fragments that makes the
+pattern equal to the C fragment. This means that a same variable
+occurring twice in a pattern stands for the same sub-fragment. By
+exception, there is an anonymous variable, noted @code{%_} whose
+occurrences may stand for different sub-fragments. For instance, the
+code fragment @code{a[i] = a[i] + 1} matches the pattern @code{%X = %X
++ 1}. The fragment @code{a[i] = a[i - 1] + 1} does not match the
+pattern @code{%X = %X + 1}, but it matches the pattern @code{%X = %Y +
+1} and also the pattern @code{%_ = %_ + 1}.
+
+Using @option{-ftree-check=@var{pattern}}, one can search for
+dangerous or unrecommended statements. For instance, the standard
+library call @code{gets ()} is unrecommended because it can be used in
+a malicious way by an external attack that overruns the corresponding
+buffer in the program. All these cases may be warned by supplying the
+option @code{-ftree-check="gets (%_)"}.
+
+Note that patterns are tried only on the top level of a statement.
+Thus, pattern @code{gets (%_)} would not match statement @code{i = gets (buff)}.
+This latter form can be matched by the pattern @code{%_ = gets (%_)}.
+If you want to match both forms of calling @code{gets ()}, you should use
+a disjunctive pattern, as described later on.
+
+@item -ftree-checks=@var{file}
+@opindex ftree-checks
+Perform the series of user-defined checks defined in @var{file} over
+the source files being compiled.
+
+More complex user-defined checks take into account not only syntax,
+but also control flow and data flow information. These control flow
+and dataflow patterns are called ``condates''. A condate encodes an
+example of programming error that should never occur.
+
+GCC traverses the CFG and prints a warning for each example of condate
+it finds. Condates express reachability properties on the
+control-flow graph of each function, having the general form:
+
+@smallexample
+from @var{s1} to @var{s2} avoid @var{e};
+@end smallexample
+
+interpreted as ``find some path in the CFG starting with a statement
+matching @var{s1}, finishing with a statement matching @var{s2} while
+avoiding all edges matching @var{e}'', where @var{s1}, @var{s2} are
+statement patterns, and @var{e} is an edge pattern.
+
+A statement pattern is either an atomic statement pattern, as
+described above, or a disjunction of atomic statement patterns
+separated by the @code{or} operator. For example the statement
+pattern @code{"gets (%_)" or "%_ = gets (%_)"} matches any call
+to @code{gets ()}.
+
+An edge pattern may have one of the following forms:
+
+@itemize @bullet
+@item @code{@var{s}}, where @var{s} is an atomic statement pattern:
+ matches any edge leaving a statement matching @var{s},
+
+@item @code{+@var{exp}}, where @var{exp} is an atomic expression pattern:
+ matches the @code{then} edge of any conditional statement whose
+ test expression matches @var{exp},
+
+@item @code{-@var{exp}}, where @var{exp} is an atomic expression pattern:
+ matches the @code{else} edge of any conditional statement whose
+ test expression matches @var{exp},
+
+@item @code{@var{p1} or @var{p2}}, where @var{p1} and @var{p2} are edge
+ patterns: matches any edge matching one of the two patterns.
+@end itemize
+
+All parts of a condate except the first part may be omitted. The
+default pattern for the @code{to} pattern @var{s2} is the @code{%_}
+pattern matching anything. The default pattern for the @code{avoid}
+pattern @var{p3} is the null pattern, matching nothing. In
+particular, if only the @code{from} part is specified, meaning that
+there is a path from @var{p1} to anywhere avoiding nothing, the
+compiler simply issues a warning for all statements matching pattern
+@var{p1}.
+
+For example, the following check detects cases when a function exits
+with interrupts disabled:
+
+@smallexample
+from "disable_interrupts (%X)" to "return" or "return %_"
+avoid "enable_interrupts (%X)";
+@end smallexample
+
+Here is a slightly more complex example involving all the three
+patterns, and looking for pointers obtained through @code{malloc ()}
+being dereferenced without being checked as non-null:
+
+@smallexample
+from "%X = malloc(%_)"
+to "%_ = %X->%_" or "%X->%_ = %_"
+avoid "%X = %_" or +"%X != 0B" or -"%X == 0B";
+@end smallexample
+
+Note that @code{NULL} values are represented as @code{0B} above
+because condates are checked on the Gimple form, where @code{NULL}
+pointers are represented as such. To see the Gimple form, use
+@option{-fdump-tree-gimple}, which generates it in file
+@file{@var{name}.c.004t.gimple}.
+
+Pattern variables occuring in checks are of two kinds:
+
+@itemize @bullet
+@item global variables, named by upper case letters (%A, %X, ...), shared
+ between the patterns of a same check
+
+@item local variables, named by lower case letters (%b, %i, ...), that are
+ local to each pattern. Recall however, that even a local variable
+ occuring twice in the same pattern must stand for the same value.
+@end itemize
+
+Using @option{-ftree-checks=@var{file}}, one can check for a series of
+condates defined in the given @var{file} as described above. Comments
+may be added at any place within the file, preceded by a @code{#}
+character.
+
+All the condates described so far are anonymous condates. When raising
+a warning on an anonymous condate, GCC refers to it by its indexing
+number in @var{file}, and prints a generic warning message. There is
+an alternate syntax for condates allowing to give them a name and to
+customize the corresponding warning message:
+
+@smallexample
+condate @var{name} @{ @var{condate} @} warning(``@var{message}'');
+@end smallexample
+
+For example (a slightly extended version of) the malloc check above
+can be defined as a named condate as follows:
+
+@smallexample
+@group
+condate malloc_deref @{
+ from "%X = malloc (%_)" # any malloc
+ to "%_ = %X->%_" or "%X->%_ = %_"
+ or "*%X = %_" or "%_ = *%X"
+ avoid "%X = %_" or +"%X != 0B" or -"%X == 0B"
+@} warning("unsafe dereference of X after malloc");
+@end group
+@end smallexample
+
+Named condates should be preferred over anonymous condates as
+they generate more explicit warnings.
+
+@item -ftree-check-verbose
+@opindex ftree-check-verbose
+
+Outputs to @code{stderr} more precise messages on the matched patterns
+for each warning generated by the tree-checker. This is turned off by
+default.
+
+@end table
+
@node Debugging Options
@section Options for Debugging Your Program or GCC
@cindex options, debugging
Index: flags.h
===================================================================
--- flags.h (revision 123789)
+++ flags.h (working copy)
@@ -240,6 +240,11 @@ extern enum graph_dump_types graph_dump_
and to print them when we are done. */
extern int flag_detailed_statistics;
+/* File containing check specifications */
+extern const char *tree_check_file;
+/* File containing check specifications */
+extern const char *tree_check_string;
+
/* Nonzero means that we defer emitting functions until they are actually
used. */
extern int flag_remove_unreachable_functions;
Index: tree-pretty-print.c
===================================================================
--- tree-pretty-print.c (revision 123789)
+++ tree-pretty-print.c (working copy)
@@ -42,7 +42,6 @@ static const char *op_symbol (tree);
static void pretty_print_string (pretty_printer *, const char*);
static void print_call_name (pretty_printer *, tree);
static void newline_and_indent (pretty_printer *, int);
-static void maybe_init_pretty_print (FILE *);
static void print_declaration (pretty_printer *, tree, int, int);
static void print_struct_decl (pretty_printer *, tree, int, int);
static void do_niy (pretty_printer *, tree);
@@ -72,7 +71,7 @@ do_niy (pretty_printer *buffer, tree nod
pp_string (buffer, "<<< Unknown tree: ");
pp_string (buffer, tree_code_name[(int) TREE_CODE (node)]);
- if (EXPR_P (node))
+ if (!pp_lazy_mode (buffer) && EXPR_P (node))
{
len = TREE_OPERAND_LENGTH (node);
for (i = 0; i < len; ++i)
@@ -429,15 +428,11 @@ dump_symbols (pretty_printer *buffer, bi
}
}
+/* Internal worker function for dump_generic_node. */
-/* Dump the node NODE on the pretty_printer BUFFER, SPC spaces of indent.
- FLAGS specifies details to show in the dump (see TDF_* in tree.h). If
- IS_STMT is true, the object printed is considered to be a statement
- and it is terminated by ';' if appropriate. */
-
-int
-dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
- bool is_stmt)
+static int
+dump_generic_node_1 (pretty_printer *buffer, tree node, int spc, int flags,
+ bool is_stmt)
{
tree type;
tree op0, op1;
@@ -2060,6 +2055,24 @@ dump_generic_node (pretty_printer *buffe
return spc;
}
+/* Dump the node NODE on the pretty_printer BUFFER, SPC spaces of indent.
+ FLAGS specifies details to show in the dump (see TDF_* in tree.h). If
+ IS_STMT is true, the object printed is considered to be a statement
+ and it is terminated by ';' if appropriate. */
+
+int
+dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
+ bool is_stmt)
+{
+ if (!pp_lazy_mode (buffer))
+ return dump_generic_node_1 (buffer, node, spc, flags, is_stmt);
+ else
+ {
+ pp_add_tree (buffer, node);
+ return 0;
+ }
+}
+
/* Print the declaration of a variable. */
static void
@@ -2689,7 +2702,7 @@ pretty_print_string (pretty_printer *buf
}
}
-static void
+void
maybe_init_pretty_print (FILE *file)
{
if (!initialized)
@@ -3030,3 +3043,35 @@ dump_generic_bb_buff (pretty_printer *bu
if (flags & TDF_BLOCKS)
dump_bb_end (buffer, bb, indent, flags);
}
+
+/* Unparse the top level of a tree, returning the list of tree chunks
+ that constitute its printed form. Tree chunks may be: characters,
+ strings, and sub-trees. */
+
+VEC (tree_chunk, heap) *
+lazy_dump_generic_node (tree node, int flags, bool is_stmt)
+{
+ pretty_printer *pp = &buffer;
+ VEC (tree_chunk, heap) *res;
+
+ pp->buffer->chunks = VEC_alloc (tree_chunk, heap, 10);
+ dump_generic_node_1 (pp, node, 0, flags, is_stmt);
+ res = pp->buffer->chunks;
+ pp->buffer->chunks = NULL;
+
+ return res;
+}
+
+/* Unparse the top level of a tree and dump the resulting list of
+ tree chunks to a file. */
+
+void
+lazy_print_generic_expr (FILE *file, tree t, int flags)
+{
+ VEC (tree_chunk, heap) *chunks;
+
+ maybe_init_pretty_print (file);
+ fprintf (file, "<%s>=", tree_name(t));
+ chunks = lazy_dump_generic_node (t, flags, false);
+ pp_write_list (chunks, file);
+}
Index: tree.c
===================================================================
--- tree.c (revision 123789)
+++ tree.c (working copy)
@@ -8267,6 +8267,14 @@ empty_body_p (tree stmt)
return true;
}
+/* Return the type name of a tree (e.g. "var_decl"). */
+
+const char *
+tree_name (tree t)
+{
+ return tree_code_name[(int) TREE_CODE (t)];
+}
+
tree *
tree_block (tree t)
{
Index: tree.h
===================================================================
--- tree.h (revision 123789)
+++ tree.h (working copy)
@@ -4888,6 +4888,8 @@ extern unsigned HOST_WIDE_INT compute_bu
/* In expr.c. */
extern unsigned HOST_WIDE_INT highest_pow2_factor (tree);
+extern const char *tree_name (tree t);
+
/* In tree-inline.c. */
void init_inline_once (void);
Index: tree-pass.h
===================================================================
--- tree-pass.h (revision 123789)
+++ tree-pass.h (working copy)
@@ -306,6 +306,7 @@ extern struct tree_opt_pass pass_reassoc
extern struct tree_opt_pass pass_rebuild_cgraph_edges;
extern struct tree_opt_pass pass_build_cgraph_edges;
extern struct tree_opt_pass pass_reset_cc_flags;
+extern struct tree_opt_pass pass_check;
/* IPA Passes */
extern struct tree_opt_pass pass_ipa_cp;
Index: diagnostic.h
===================================================================
--- diagnostic.h (revision 123789)
+++ diagnostic.h (working copy)
@@ -208,12 +208,16 @@ extern char *diagnostic_build_prefix (di
extern char *file_name_as_prefix (const char *);
/* In tree-pretty-print.c */
+extern void maybe_init_pretty_print (FILE *file);
extern int dump_generic_node (pretty_printer *, tree, int, int, bool);
extern void print_generic_stmt (FILE *, tree, int);
extern void print_generic_stmt_indented (FILE *, tree, int, int);
extern void print_generic_expr (FILE *, tree, int);
extern void print_generic_decl (FILE *, tree, int);
+extern VEC (tree_chunk, heap) *lazy_dump_generic_node (tree, int, bool);
+extern void lazy_print_generic_expr (FILE *file, tree t, int flags);
+
extern void debug_generic_expr (tree);
extern void debug_generic_stmt (tree);
extern void debug_tree_chain (tree);
Index: toplev.c
===================================================================
--- toplev.c (revision 123789)
+++ toplev.c (working copy)
@@ -338,6 +338,11 @@ rtx stack_limit_rtx;
to optimize, debug_info_level and debug_hooks in process_options (). */
int flag_var_tracking = AUTODETECT_VALUE;
+/* File containing check specifications */
+const char *tree_check_file = NULL;
+/* String containing inline check specifications */
+const char *tree_check_string = NULL;
+
/* True if the user has tagged the function with the 'section'
attribute. */
Index: pretty-print.c
===================================================================
--- pretty-print.c (revision 123789)
+++ pretty-print.c (working copy)
@@ -25,8 +25,10 @@ Software Foundation, 51 Franklin Street,
#include "system.h"
#include "coretypes.h"
#include "intl.h"
-#include "pretty-print.h"
#include "tree.h"
+#include "vec.h"
+#include "pretty-print.h"
+#include "diagnostic.h"
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
@@ -90,6 +92,8 @@ pp_clear_state (pretty_printer *pp)
{
pp->emitted_prefix = false;
pp_indentation (pp) = 0;
+ if (pp_lazy_mode (pp))
+ pp_free_list (pp->buffer->chunks);
}
/* Flush the formatted text of PRETTY-PRINTER onto the attached stream. */
@@ -101,6 +105,15 @@ pp_write_text_to_stream (pretty_printer
pp_clear_output_area (pp);
}
+/* Flush the formatted list of PRETTY-PRINTER onto the attached stream. */
+static void
+pp_write_list_to_stream (pretty_printer *pp)
+{
+ FILE *f = pp->buffer->stream;
+ VEC (tree_chunk, heap) *chunks = pp->buffer->chunks;
+ pp_write_list (chunks, f);
+}
+
/* Wrap a text delimited by START and END into PRETTY-PRINTER. */
static void
pp_wrap_text (pretty_printer *pp, const char *start, const char *end)
@@ -149,7 +162,11 @@ pp_maybe_wrap_text (pretty_printer *pp,
static inline void
pp_append_r (pretty_printer *pp, const char *start, int length)
{
- obstack_grow (pp->buffer->obstack, start, length);
+ if (!pp_lazy_mode (pp))
+ obstack_grow (pp->buffer->obstack, start, length);
+ else
+ pp_add_string (pp, start, length);
+
pp->buffer->line_length += length;
}
@@ -216,6 +233,7 @@ pp_base_format (pretty_printer *pp, text
pp_wrapping_mode_t old_wrapping_mode;
bool any_unnumbered = false, any_numbered = false;
const char **formatters[PP_NL_ARGMAX];
+ VEC (tree_chunk, heap) *saved_chunks;
/* Allocate a new chunk structure. */
new_chunk_array = XOBNEW (&buffer->chunk_obstack, struct chunk_info);
@@ -368,6 +386,10 @@ pp_base_format (pretty_printer *pp, text
prefixing off. */
buffer->obstack = &buffer->chunk_obstack;
old_wrapping_mode = pp_set_verbatim_wrapping (pp);
+ /* Also disable lazy printing while using the chunk_obstack. */
+ saved_chunks = buffer->chunks;
+ buffer->chunks = NULL;
+
/* Second phase. Replace each formatter with the formatted text it
corresponds to. */
@@ -548,6 +570,8 @@ pp_base_format (pretty_printer *pp, text
buffer->line_length = 0;
pp_wrapping_mode (pp) = old_wrapping_mode;
pp_clear_state (pp);
+ /* And also revert lazy mode to its previous state. */
+ buffer->chunks = saved_chunks;
}
/* Format of a message pointed to by TEXT. */
@@ -593,10 +617,15 @@ pp_base_format_verbatim (pretty_printer
void
pp_base_flush (pretty_printer *pp)
{
- pp_write_text_to_stream (pp);
+ if (!pp_lazy_mode (pp))
+ {
+ pp_write_text_to_stream (pp);
+ fputc ('\n', pp->buffer->stream);
+ fflush (pp->buffer->stream);
+ }
+ else
+ pp_write_list_to_stream (pp);
pp_clear_state (pp);
- fputc ('\n', pp->buffer->stream);
- fflush (pp->buffer->stream);
pp_needs_newline (pp) = false;
}
@@ -682,6 +711,7 @@ pp_construct (pretty_printer *pp, const
obstack_init (&pp->buffer->formatted_obstack);
pp->buffer->obstack = &pp->buffer->formatted_obstack;
pp->buffer->stream = stderr;
+ pp->buffer->chunks = NULL;
pp_line_cutoff (pp) = maximum_length;
pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_ONCE;
pp_set_prefix (pp, prefix);
@@ -711,8 +741,13 @@ pp_base_append_text (pretty_printer *pp,
const char *
pp_base_formatted_text (pretty_printer *pp)
{
- obstack_1grow (pp->buffer->obstack, '\0');
- return pp_formatted_text_data (pp);
+ if (!pp_lazy_mode (pp))
+ {
+ obstack_1grow (pp->buffer->obstack, '\0');
+ return pp_formatted_text_data (pp);
+ }
+ else
+ return "";
}
/* Return a pointer to the last character emitted in PRETTY-PRINTER's
@@ -723,8 +758,12 @@ pp_base_last_position_in_text (const pre
const char *p = NULL;
struct obstack *text = pp->buffer->obstack;
- if (obstack_base (text) != obstack_next_free (text))
- p = ((const char *) obstack_next_free (text)) - 1;
+ if (!pp_lazy_mode (pp))
+ {
+ /* Normal print mode. */
+ if (obstack_base (text) != obstack_next_free (text))
+ p = ((const char *) obstack_next_free (text)) - 1;
+ }
return p;
}
@@ -777,7 +816,11 @@ pp_verbatim (pretty_printer *pp, const c
void
pp_base_newline (pretty_printer *pp)
{
- obstack_1grow (pp->buffer->obstack, '\n');
+ if (!pp_lazy_mode (pp))
+ /* Normal print mode. */
+ obstack_1grow (pp->buffer->obstack, '\n');
+ else
+ pp_add_char (pp, '\n');
pp->buffer->line_length = 0;
}
@@ -792,7 +835,11 @@ pp_base_character (pretty_printer *pp, i
if (ISSPACE (c))
return;
}
- obstack_1grow (pp->buffer->obstack, c);
+ if (!pp_lazy_mode (pp))
+ /* Normal print mode. */
+ obstack_1grow (pp->buffer->obstack, c);
+ else
+ pp_add_char (pp, c);
++pp->buffer->line_length;
}
@@ -804,7 +851,109 @@ pp_base_string (pretty_printer *pp, cons
pp_maybe_wrap_text (pp, str, str + (str ? strlen (str) : 0));
}
-/* Maybe print out a whitespace if needed. */
+/* Functions dealing with the lazy mode of pretty-print. */
+
+bool
+pp_lazy_mode (const pretty_printer *pp)
+{
+ return (pp->buffer->chunks != NULL);
+}
+
+/* Tree chunk constructor. */
+
+tree_chunk
+new_tree_chunk (void)
+{
+ tree_chunk chunk = xmalloc (sizeof (struct tree_chunk_s));
+ chunk->t = NULL;
+ chunk->s = NULL;
+ chunk->c = '\0';
+ return chunk;
+}
+
+/* Add a tree chunk to the lazy list. */
+
+void
+pp_add_tree (pretty_printer *pp, tree t)
+{
+ tree_chunk chunk = new_tree_chunk ();
+
+ chunk->t = t;
+ VEC_safe_push (tree_chunk, heap, pp->buffer->chunks, chunk);
+}
+
+/* Add a string chunk to the lazy list. */
+
+void
+pp_add_string (pretty_printer *pp, const char *start, int len)
+{
+ tree_chunk chunk = new_tree_chunk ();
+ char *str = xmalloc (len + 1);
+
+ strncpy (str, start, len);
+ str[len] = '\0';
+ chunk->s = str;
+ VEC_safe_push (tree_chunk, heap, pp->buffer->chunks, chunk);
+}
+
+/* Add a character chunk to the lazy list. */
+
+void
+pp_add_char (pretty_printer *pp, char c)
+{
+ tree_chunk chunk = new_tree_chunk ();
+
+ chunk->c = c;
+ VEC_safe_push (tree_chunk, heap, pp->buffer->chunks, chunk);
+}
+
+/* Write the lazy list to a file. */
+
+void
+pp_write_list (VEC (tree_chunk, heap) *chunks, FILE *f)
+{
+ unsigned int i;
+ tree_chunk chunk;
+
+ fprintf (f, "[ ");
+
+ for (i = 0; VEC_iterate (tree_chunk, chunks, i, chunk); i++)
+ {
+ if (chunk->t)
+ fprintf (f, "<%s>", tree_name (chunk->t));
+
+ else if (chunk->s)
+ fprintf (f, "\"%s\"", chunk->s);
+
+ else
+ fprintf (f, "'%c'", chunk->c);
+
+ fprintf (f, " ");
+ }
+
+ fprintf (f, "]");
+}
+
+/* Lazy list destructor. */
+
+void
+pp_free_list (VEC (tree_chunk, heap) *chunks)
+{
+ unsigned int i;
+ tree_chunk chunk;
+
+ for (i = 0; VEC_iterate (tree_chunk, chunks, i, chunk); i++)
+ {
+ if (chunk->s)
+ free (chunk->s);
+
+ free (chunk);
+ }
+
+ VEC_free (tree_chunk, heap, chunks);
+}
+
+/* Print out a whitespace if needed. */
void
pp_base_maybe_space (pretty_printer *pp)
Index: pretty-print.h
===================================================================
--- pretty-print.h (revision 123789)
+++ pretty-print.h (working copy)
@@ -69,6 +69,19 @@ struct chunk_info
const char *args[PP_NL_ARGMAX * 2];
};
+/* Structure containing a piece of a tree's textual representation. */
+typedef struct tree_chunk_s {
+ /* Reference to a subtree. */
+ tree t;
+ /* A token. */
+ char *s;
+ /* A token of only one char is not stored in a malloc-ed string. */
+ char c;
+} *tree_chunk;
+
+DEF_VEC_P (tree_chunk);
+DEF_VEC_ALLOC_P (tree_chunk, heap);
+
/* The output buffer datatype. This is best seen as an abstract datatype
whose fields should not be accessed directly by clients. */
typedef struct
@@ -87,6 +100,14 @@ typedef struct
/* Stack of chunk arrays. These come from the chunk_obstack. */
struct chunk_info *cur_chunk_array;
+ /* A buffer where the text is built up. If null, the textual
+ representation of a tree is built as a plain string in the
+ obstack above. If CHUNKS is not null, we're in "lazy mode", i.e.
+ instead of recursively building the textual representation of a
+ tree as a plain string, we build a list of token strings and of
+ "lazy" references to subtrees. */
+ VEC (tree_chunk, heap) *chunks;
+
/* Where to output formatted text. */
FILE *stream;
@@ -323,6 +344,14 @@ extern void pp_base_string (pretty_print
extern void pp_write_text_to_stream (pretty_printer *pp);
extern void pp_base_maybe_space (pretty_printer *);
+extern tree_chunk new_tree_chunk (void);
+extern bool pp_lazy_mode (const pretty_printer *);
+extern void pp_add_tree (pretty_printer *, tree);
+extern void pp_add_string (pretty_printer *, const char *, int);
+extern void pp_add_char (pretty_printer *, char);
+extern void pp_free_list (VEC (tree_chunk, heap) *);
+extern void pp_write_list (VEC (tree_chunk, heap) *, FILE *);
+
/* Switch into verbatim mode and return the old mode. */
static inline pp_wrapping_mode_t
pp_set_verbatim_wrapping_ (pretty_printer *pp)
Index: testsuite/gcc.dg/tree-checker/tree-checks-4.c
===================================================================
--- testsuite/gcc.dg/tree-checker/tree-checks-4.c (revision 0)
+++ testsuite/gcc.dg/tree-checker/tree-checks-4.c (revision 0)
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+main()
+{
+ int i, N, *p;
+ char buf[3];
+
+ printf("Input a number less than 100: ");
+ flockfile(stdin);
+ fgets(buf, 3, stdin);
+ N = atoi(buf);
+ printf("\nExecuting %d iterations\n", N);
+ for(i = 0; i<N; i++)
+ {
+ p = malloc(sizeof(int));
+ if(!p)
+ {
+ printf("failed to malloc p\n");
+ free(p);
+ return -1;
+ }
+ if(!*p)
+ {
+ fprintf(stdout, "%d\n", i);
+ }
+ free(p);
+ }
+
+ funlockfile(stdin);
+ return 0;
+} /* { dg-warning "missing_unlock" } */
Index: testsuite/gcc.dg/tree-checker/condates.crp
===================================================================
--- testsuite/gcc.dg/tree-checker/condates.crp (revision 0)
+++ testsuite/gcc.dg/tree-checker/condates.crp (revision 0)
@@ -0,0 +1,32 @@
+#
+# Examples of condates
+#
+
+condate malloc_deref {
+ from "%X = malloc (%_)" # any malloc
+ to "%_ = %X->%_" or "%X->%_ = %_"
+ or "*%X = %_" or "%_ = *%X"
+ avoid "%X = %_" or +"%X != 0B" or -"%X == 0B"
+} warning("Unsafe dereference of X after malloc");
+
+condate unfreed {
+ from "%X = malloc (%_)" to "return" or "return %_"
+ avoid "free (%X)" or +"%X == 0B" or -"%X != 0B"
+} warning("Un-freed memory for variable X");
+
+condate missing_unlock {
+ from "flockfile (%X)"
+ to ("return" or "return %_")
+ avoid "funlockfile (%X)"
+} warning("Unreleased lock on file X");
+
+condate missing_tryunlock {
+ from "%L = ftrylockfile (%X)"
+ to ("return" or "return %_")
+ avoid "funlockfile (%X)" or +"%L != 0" or -"%L == 0"
+} warning("Unreleased (successful) trylock on file X");
+
+condate gets { # "degenerate" crq, of only one pattern
+ # 'from' keyword must be omitted:
+ "%X = gets(%Y)" or "gets(%Y)"
+} warning("Never use gets(), see man gets");
Index: testsuite/gcc.dg/tree-checker/tree-checks.exp
===================================================================
--- testsuite/gcc.dg/tree-checker/tree-checks.exp (revision 0)
+++ testsuite/gcc.dg/tree-checker/tree-checks.exp (revision 0)
@@ -0,0 +1,30 @@
+# Copyright (C) 2007 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/tree-checks-*.\[cS\]]] \
+ "" "-ansi -pedantic-errors -O2 -ftree-checks=$srcdir/$subdir/condates.crp"
+
+# All done.
+dg-finish
Index: testsuite/gcc.dg/tree-checker/tree-checks-1.c
===================================================================
--- testsuite/gcc.dg/tree-checker/tree-checks-1.c (revision 0)
+++ testsuite/gcc.dg/tree-checker/tree-checks-1.c (revision 0)
@@ -0,0 +1,6 @@
+main()
+{
+ char buf[3];
+ gets(buf); /* { dg-warning "gets" } */
+ return 0;
+}
Index: testsuite/gcc.dg/tree-checker/tree-checks-2.c
===================================================================
--- testsuite/gcc.dg/tree-checker/tree-checks-2.c (revision 0)
+++ testsuite/gcc.dg/tree-checker/tree-checks-2.c (revision 0)
@@ -0,0 +1,31 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+main()
+{
+ int i, N, *p;
+ char buf[3];
+
+ printf("Input a number less than 100: ");
+ flockfile(stdin);
+ fgets(buf, 3, stdin);
+ N = atoi(buf);
+ printf("\nExecuting %d iterations\n", N);
+ for(i = 0; i<N; i++)
+ {
+ p = malloc(sizeof(int));
+ if(!p)
+ {
+ printf("failed to malloc p\n");
+ }
+ *p = ftrylockfile(stdout); /* { dg-warning "malloc_deref" } */
+ if(!*p)
+ {
+ fprintf(stdout, "%d\n", i);
+ funlockfile(stdout);
+ }
+ free(p);
+ }
+ funlockfile(stdin);
+ return 0;
+}
Index: testsuite/gcc.dg/tree-checker/tree-checks-3.c
===================================================================
--- testsuite/gcc.dg/tree-checker/tree-checks-3.c (revision 0)
+++ testsuite/gcc.dg/tree-checker/tree-checks-3.c (revision 0)
@@ -0,0 +1,31 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+main()
+{
+ int i, N, *p;
+ char buf[3];
+
+ printf("Input a number less than 100: ");
+ flockfile(stdin);
+ fgets(buf, 3, stdin);
+ N = atoi(buf);
+ printf("\nExecuting %d iterations\n", N);
+ for(i = 0; i<N; i++)
+ {
+ p = malloc(sizeof(int));
+ if(!p)
+ {
+ printf("failed to malloc p\n");
+ funlockfile(stdin);
+ return -1;
+ }
+ if(!*p)
+ {
+ fprintf(stdout, "%d\n", i);
+ }
+ }
+
+ funlockfile(stdin);
+ return 0;
+} /* { dg-warning "unfreed" } */
Index: opts.c
===================================================================
--- opts.c (revision 123789)
+++ opts.c (working copy)
@@ -1345,6 +1345,14 @@ common_handle_option (size_t scode, cons
flag_tracer_set = true;
break;
+ case OPT_ftree_check_:
+ tree_check_string = arg;
+ break;
+
+ case OPT_ftree_checks_:
+ tree_check_file = arg;
+ break;
+
case OPT_funroll_loops:
flag_unroll_loops_set = true;
break;
Index: tree-match.c
===================================================================
--- tree-match.c (revision 0)
+++ tree-match.c (revision 0)
@@ -0,0 +1,700 @@
+/* Library for tree matching.
+ Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+ Contributed by Nic Volanschi <nic.volanschi@free.fr>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "output.h"
+#include "errors.h"
+#include "flags.h"
+#include "function.h"
+#include "expr.h"
+#include "diagnostic.h"
+#include "tree-flow.h"
+#include "timevar.h"
+#include "tree-dump.h"
+#include "tree-pass.h"
+#include "toplev.h"
+#include "tree-match.h"
+
+/* The tree matching user interface is described in tree-pattern.h. */
+
+hole global_holes[GLOBAL_MAX];
+hole local_holes[LOCAL_MAX];
+
+/* Check whether a variable is a temporary introduced by the compiler. */
+
+static bool
+is_tmp_var (tree var)
+{
+ const char *name;
+ if (TREE_CODE (var) != VAR_DECL)
+ return false;
+
+ /* Artificial decls like return values have no name, so don't get hung. */
+ if (!DECL_NAME (var))
+ return true;
+
+ name = IDENTIFIER_POINTER (DECL_NAME (var));
+ return !strncmp (name, "D.", 2) || strchr (name, '.');
+}
+
+/* If t is a cast expression, return the value without the cast. */
+
+static tree
+substitute_cast_expr (tree t)
+{
+ if (TREE_CODE (t) == CONVERT_EXPR || TREE_CODE (t) == NOP_EXPR)
+ return TREE_OPERAND (t, 0);
+
+ return NULL;
+}
+
+/* If t is a temporary and you can find its def from current node
+ upwards in the same block, (including the current node or not),
+ return its value; otherwise return NULL. INCLUDING_CRT is set to
+ true on recursive calls. */
+
+static tree
+substitute_tmp_var (tree var, cfg_node ctx_node, bool including_crt)
+{
+ tree val;
+ if (!is_tmp_var (var))
+ return NULL;
+
+ /* We are at the beginning of the block, or the context is unknown. */
+ if (!ctx_node)
+ return NULL;
+
+ if ((val = NULL, tree_scanf (cfg_node_stmt (ctx_node), "%t = %t",
+ NULL, &var, &val))
+ || (val = NULL,
+ tree_scanf (cfg_node_stmt (ctx_node), "%t = (%_)%t",
+ NULL, &var, &val)))
+ {
+ if (including_crt)
+ {
+ PP_TRACE (TRACE_MATCH_STEPS, {
+ fprintf (stderr, "substitute_tmp_var(");
+ print_generic_expr (stderr, var, 0);
+ fprintf (stderr, ")=");
+ lazy_print_generic_expr (stderr, val, 0);
+ fprintf (stderr, "\n");
+ });
+ return val;
+ }
+ else
+ /* We are on a def, but exclude it => don't go up. */
+ return NULL;
+ }
+ else
+ /* We are not on a def => go up and try again. */
+ return substitute_tmp_var (var, ctx_node->prev, true);
+}
+
+static bool tree_equal_mod_tmps (tree, tree, cfg_node, cfg_node);
+
+/* Worker function for tree_equal_mod_tmps. */
+
+static bool
+tree_equal (tree t1, tree t2, cfg_node ctx_node1, cfg_node ctx_node2)
+{
+ VEC (tree_chunk, heap) *chunks1;
+ VEC (tree_chunk, heap) *chunks2;
+ tree_chunk chunk1, chunk2;
+ int len1, len2, i;
+ bool res = false;
+
+ if ((!t1 || !t2))
+ return (t1 == t2);
+
+ if (t1 == t2)
+ return true;
+
+ chunks1 = lazy_dump_generic_node (t1, 0, false);
+ chunks2 = lazy_dump_generic_node (t2, 0, false);
+
+ PP_TRACE (TRACE_MATCH_STEPS, {
+ fprintf (stderr, "tree cmp:\n");
+ lazy_print_generic_expr (stderr, t1, 0);
+ fprintf (stderr, "vs:\n");
+ lazy_print_generic_expr (stderr, t2, 0);
+ fprintf (stderr, "---\n");
+ });
+
+ len1 = VEC_length (tree_chunk, chunks1);
+ len2 = VEC_length (tree_chunk, chunks2);
+
+ if (len1 != len2)
+ goto mismatch;
+
+ for (i = 0; VEC_iterate (tree_chunk, chunks1, i, chunk1); i++)
+ {
+ chunk2 = VEC_index (tree_chunk, chunks2, i);
+
+ if (((chunk1->t || chunk2->t)
+ && (!(chunk1->t && chunk2->t)
+ || !tree_equal_mod_tmps (chunk1->t, chunk2->t,
+ ctx_node1, ctx_node2)))
+ || ((chunk1->s || chunk2->s)
+ && (!(chunk1->s && chunk2->s)
+ || strcmp (chunk1->s, chunk2->s)))
+ || (chunk1->c != chunk2->c))
+ goto mismatch;
+ }
+
+ res = true;
+ mismatch:
+ pp_free_list (chunks1);
+ pp_free_list (chunks2);
+ return res;
+}
+
+/* Check if two trees are equal, modulo casts and substitutions of
+ tmp vars with their values. */
+
+static bool
+tree_equal_mod_tmps (tree t1, tree t2, cfg_node ctx_node1, cfg_node ctx_node2)
+{
+ tree val;
+
+ if ((!t1 || !t2))
+ return (t1 == t2);
+
+ return (tree_equal (t1, t2, ctx_node1, ctx_node2)
+ || ((val = substitute_tmp_var (t1, ctx_node1, false)) != NULL
+ && tree_equal_mod_tmps (val, t2, ctx_node1, ctx_node2))
+ || ((val = substitute_tmp_var (t2, ctx_node2, false)) != NULL
+ && tree_equal_mod_tmps (t1, val, ctx_node1, ctx_node2))
+ || ((val = substitute_cast_expr (t1)) != NULL
+ && tree_equal_mod_tmps (val, t2, ctx_node1, ctx_node2))
+ || ((val = substitute_cast_expr (t2)) != NULL
+ && tree_equal_mod_tmps (t1, val, ctx_node1, ctx_node2)));
+}
+
+static char tree_1st_char (tree);
+
+/* Get the first character of (the printed form of) a tree chunk. */
+
+static char
+chunk_1st_char (tree_chunk chunk)
+{
+ if (chunk->t)
+ return tree_1st_char (chunk->t);
+
+ else if (chunk->s)
+ return *chunk->s;
+
+ else
+ return chunk->c;
+}
+
+/* Search the first chunk of a lazy list not consisting of whitespace. */
+
+static tree_chunk
+chunks_lookahead (VEC (tree_chunk, heap) *chunks, unsigned int i)
+{
+ tree_chunk chunk;
+
+ for (; VEC_iterate (tree_chunk, chunks, i, chunk); i++)
+ if (chunk->c == 0 || chunk->c != ' ')
+ break;
+
+ return chunk;
+}
+
+/* Get the first character of (the printed form of) a tree. */
+
+static char
+tree_1st_char (tree t)
+{
+ VEC (tree_chunk, heap) *chunks;
+ tree_chunk chunk;
+
+ /* Don't hung on unnamed vars, etc. Cannot dump these nodes. */
+ if (TREE_CODE (t) == VAR_DECL || TREE_CODE_CLASS (TREE_CODE (t)) == 'x')
+ return '\0';
+
+ chunks = lazy_dump_generic_node (t, 0, false);
+ chunk = chunks_lookahead (chunks, 0);
+ pp_free_list (chunks);
+
+ return chunk_1st_char (chunk);
+}
+
+/* Get the first non-space character in a pattern. */
+
+static char
+pattern_lookahead (patt_info *patt, int n)
+{
+ const char *s = patt->format_spec + n;
+ int skip = 0;
+
+ do {
+ if (s[0] == '\\' && s[1] == ')')
+ skip = 2;
+
+ else if (s[0] == ' ')
+ skip = 1;
+
+ else
+ skip = 0;
+
+ s += skip;
+ } while (skip);
+
+ return s[0];
+}
+
+static bool match_tree_pattinfo (tree, patt_info *, const char *, cfg_node);
+
+/* Worker function for match_tree_pattinfo. Matches a lazy list with
+ a pattern. */
+
+static bool
+match_chunks_pattinfo (VEC (tree_chunk, heap) *chunks, patt_info *patt,
+ const char *delim, cfg_node ctx_node)
+{
+ unsigned int i;
+ tree_chunk chunk;
+
+ for (i = 0; VEC_iterate (tree_chunk, chunks, i, chunk); i++)
+ {
+ if (chunk->t)
+ {
+ /* Compute delimiter for t. */
+ char next_char = (i + 1 == VEC_length (tree_chunk, chunks) ?
+ *delim :
+ chunk_1st_char (chunks_lookahead (chunks, i + 1)));
+
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "tree delimited by %c", next_char));
+ PP_TRACE (TRACE_MATCH_STEPS, {
+ fprintf (stderr, "{match_tree_pattinfo(");
+ print_generic_expr (stderr, chunk->t, 0);
+ fprintf (stderr, ", \"%s\") ", patt->format_spec);
+ });
+
+ if (!match_tree_pattinfo (chunk->t, patt, &next_char, ctx_node))
+ {
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "=> fail tree chunk} "));
+ return 0;
+ }
+
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "=> succeed tree chunk} "));
+ }
+ else if (chunk->s)
+ {
+ if (*patt->format_spec == '%')
+ {
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "fail str chunk '%s' vs hole",
+ chunk->s));
+ return 0;
+ }
+ else
+ {
+ if (memcmp (patt->format_spec, chunk->s, strlen (chunk->s)))
+ {
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "fail str chunk '%s' vs patt '%s'",
+ chunk->s, patt->format_spec));
+ return 0;
+ }
+
+ patt->format_spec += strlen (chunk->s);
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "succeed str chunk '%s' vs patt",
+ chunk->s));
+ }
+ }
+ else
+ {
+ /* one-character chunk */
+ if (chunk->c == ' ')
+ {
+ /* whitespace */
+ while (*patt->format_spec == ' ')
+ patt->format_spec++;
+
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr,
+ "succeed space chunk vs patt whitespace"));
+ }
+ else
+ {
+ /* not whitespace */
+ if (*patt->format_spec == '%')
+ {
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "fail char chunk '%c' vs hole",
+ chunk->c));
+ return 0;
+ }
+ else
+ {
+ if (*patt->format_spec != chunk->c)
+ {
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr,
+ "fail char chunk '%c' vs patt '%c'",
+ chunk->c, *patt->format_spec));
+ return 0;
+ }
+
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "succeed char chunk '%c' vs patt",
+ chunk->c));
+ patt->format_spec++;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+/* Match a tree t, consuming the pattern. If the args in patt_info is
+ not null, the pattern has anonymous holes, indicated by the
+ va_args, so consume the args as you go. Otherwise (args is null),
+ you have a pattern with named holes. The ctx_node (if non-null)
+ indicates the cfg_node where the tree is supposed to occur, in case
+ some tmp vars are to be searched for from this point backwards. */
+
+static bool
+match_tree_pattinfo (tree t, patt_info *patt, const char *delim,
+ cfg_node ctx_node)
+{
+ tree *pt;
+ hole *ph = NULL;
+ bool res;
+ int parskip = 0;
+ tree val;
+
+ if (patt->format_spec[0] == '%'
+ && TREE_CODE (t) != TREE_LIST /* don't match entire lists */
+ && *delim == pattern_lookahead (patt, 2))
+ {
+ /* lookahead(1) ok */
+ if (patt->format_spec[1] != '_')
+ {
+ /* not "any" hole */
+ if (patt->args_ptr) /* anonymous holes */
+ pt = va_arg (*patt->args_ptr, tree *);
+ else
+ {
+ /* named holes */
+ ph = get_hole_named (patt->format_spec[1]);
+
+ if (!ph)
+ fatal_error ("Invalid pattern variable: %%%c\n",
+ patt->format_spec[1]);
+
+ pt = &ph->tree;
+ }
+
+ if (!*pt)
+ {
+ /* var hole */
+ /* refuse to catch a tmpvar def */
+ if (is_tmp_var (t) && ctx_node &&
+ (tree_scanf (cfg_node_stmt (ctx_node), "%t = %_", NULL, &t)
+ || tree_scanf (cfg_node_stmt (ctx_node), "%t = (%_)%_",
+ NULL, &t)))
+ {
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr,
+ "refusing to assign tmpvar def to global hole"));
+ return 0;
+ }
+
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "assign tree chunk to hole"));
+ *pt = t;
+ if (ph)
+ ph->ctx = ctx_node;
+ }
+ else
+ {
+ /* instantiated hole */
+ if (!tree_equal_mod_tmps (*pt, t, ph? ph->ctx : NULL, ctx_node))
+ {
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "fail eq tree chunk vs hole"));
+ return 0;
+ }
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "succeed eq tree chunk vs hole"));
+ }
+ } /* else (%_) just go on */
+
+ patt->format_spec += 2; /* consume %h */
+ return 1;
+ }
+ else
+ {
+ /* Can't swallow a whole tree, must recurse on it. */
+ VEC (tree_chunk, heap) *chunks;
+
+ PP_TRACE (TRACE_MATCH_STEPS, fprintf (stderr, "check chunks vs patt"));
+
+ /* Check an eventual pattern-only '(' to be skipped. */
+ if (patt->format_spec[0] == '\\' && patt->format_spec[1] == '(')
+ {
+ PP_TRACE (TRACE_MATCH_STEPS, fprintf (stderr, "[skip lpar]"));
+ patt->format_spec+=2; parskip = 1;
+ }
+
+ /* On a tmpvar or a cast, there is no point to recurse directly (they
+ cannot be in the pattern), so substitute it before. */
+ while ((val = substitute_tmp_var (t, ctx_node, false)) != NULL
+ || (val = substitute_cast_expr (t)) != NULL)
+ {
+ PP_TRACE (TRACE_MATCH_STEPS, fprintf (stderr, "succeed subst tmp"));
+ t = val;
+ }
+
+ maybe_init_pretty_print (stdout);
+ chunks = lazy_dump_generic_node (t, 0, false);
+ res = match_chunks_pattinfo (chunks, patt, delim, ctx_node);
+ pp_free_list (chunks);
+ PP_TRACE (TRACE_MATCH_STEPS, fprintf (stderr, "%s chunks vs patt",
+ (res? "succeed": "fail")));
+
+ /* if needed, look for corresponding pattern-only ')' */
+ if (res && parskip)
+ {
+ if (patt->format_spec[0] == '\\' && patt->format_spec[1] == ')')
+ {
+ PP_TRACE (TRACE_MATCH_STEPS, fprintf (stderr, "[skip rpar]"));
+ patt->format_spec+=2;
+ }
+ else
+ {
+ PP_TRACE (TRACE_MATCH_STEPS, fprintf (stderr, "[no rpar!]"));
+ res = 0;
+ }
+ }
+
+ return res;
+ }
+}
+
+/* Check whether a hole name represents a global variable. */
+
+inline bool
+is_global_hole (char c)
+{
+ return 'A' <= c && c <= 'Z';
+}
+
+/* Get the hole named by a (local or global) variable name. */
+
+hole *
+get_hole_named (char c)
+{
+ if ('a' <= c && c <= 'z')
+ return &local_holes[c - 'a'];
+
+ if ('A' <= c && c <= 'Z')
+ return &global_holes[c - 'A'];
+
+ return NULL;
+}
+
+/* Unbind all global variables. */
+
+void
+reset_global_holes (void)
+{
+ int i;
+ for (i=0; i<GLOBAL_MAX; i++)
+ {
+ global_holes[i].tree = NULL;
+ global_holes[i].ctx = NULL;
+ }
+}
+
+/* Unbind all local variables. */
+
+void
+reset_local_holes (void)
+{
+ int i;
+ for (i=0; i<LOCAL_MAX; i++)
+ {
+ local_holes[i].tree = NULL;
+ local_holes[i].ctx = NULL;
+ }
+}
+
+/* Save the values of all global variables in a buffer. */
+
+hole *
+save_global_holes (void)
+{
+ hole *buf = xmalloc (sizeof (global_holes));
+ memcpy (buf, global_holes, sizeof (global_holes));
+ return buf;
+}
+
+/* Restore the values of all global variables from a buffer. */
+
+void
+restore_global_holes (hole_p saved)
+{
+ memcpy (global_holes, saved, sizeof (global_holes));
+ free (saved);
+}
+
+/* Compare two sets of global variables. */
+
+bool
+eq_global_holes (hole *holes1, hole *holes2)
+{
+ int i;
+
+ for (i=0; i<GLOBAL_MAX; i++)
+ if (!tree_equal_mod_tmps (holes1[i].tree, holes2[i].tree,
+ holes1[i].ctx, holes2[i].ctx))
+ return 0;
+
+ return 1;
+}
+
+/* Print the list of bounded local variables. */
+
+void
+print_local_holes (void)
+{
+ int i;
+
+ for (i=0; i<LOCAL_MAX; i++)
+ {
+ if (local_holes[i].tree)
+ {
+ fprintf (stderr, "local_holes[%d] == ", i);
+ print_generic_expr (stderr, local_holes[i].tree, 0);
+ fprintf (stderr, "\n");
+ }
+ }
+}
+
+/* Print the list of bounded global variables. */
+
+void
+print_global_holes (void)
+{
+ int i, state = 0;
+
+ fprintf (stderr, "{");
+
+ for (i=0; i<GLOBAL_MAX; i++)
+ {
+ if (global_holes[i].tree)
+ {
+ if (state)
+ fprintf (stderr, ", ");
+ fprintf (stderr, "%c <- ", 'A' + i);
+ print_generic_expr (stderr, global_holes[i].tree, 0);
+ state = 1;
+ }
+ }
+
+ fprintf (stderr, "}");
+}
+
+/* Match a tree with a pattern with anonymous holes, and bind the
+ corresponding subtrees to the list of extra arguments. Returns
+ true if the tree completely matched the pattern, false otherwise.
+ Even on unsuccessful match, some holes might be filled in. */
+
+bool
+tree_scanf (tree t, const char *fmt, cfg_node ctx_node, ...)
+{
+ patt_info patt;
+ va_list ap;
+ bool res;
+
+ va_start (ap, ctx_node);
+ patt.args_ptr = ≈
+ patt.format_spec = fmt;
+ PP_TRACE (TRACE_MATCH_STEPS, {
+ fprintf (stderr, "{tree_scanf(");
+ print_generic_expr (stderr, t, 0);
+ fprintf (stderr, ", \"%s\") ", fmt);
+ });
+ res = match_tree_pattinfo (t, &patt, "", ctx_node);
+ PP_TRACE (TRACE_MATCH_STEPS, fprintf (stderr, "} "));
+ va_end (ap);
+
+ /* nothing left in the pattern ? */
+ if (res && (*patt.format_spec == '\0'))
+ return true;
+ else
+ return false;
+}
+
+/* Match a tree against an atomic pattern with named holes. */
+
+bool
+tree_match (tree t, const char *fmt, cfg_node ctx_node)
+{
+ patt_info patt;
+ bool res;
+ hole *old_global_holes = save_global_holes ();
+
+ patt.args_ptr = NULL;
+ patt.format_spec = fmt;
+ reset_local_holes ();
+ res = match_tree_pattinfo (t, &patt, "", ctx_node);
+ PP_TRACE (TRACE_MATCH_STEPS,
+ fprintf (stderr, "=>match returned %d, and fmt='%s'\n",
+ res, patt.format_spec));
+
+ /* nothing left in the pattern ? */
+ if (res && (*patt.format_spec == '\0'))
+ return true;
+ else
+ {
+ restore_global_holes (old_global_holes); /* unbind global holes */
+ return false;
+ }
+}
+
+/* Match a tree against a disjunctive pattern with named holes. */
+
+bool
+tree_match_disj (tree t, pattern patt, cfg_node ctx_node)
+{
+ if (!patt)
+ return false;
+
+ return (tree_match (t, patt->format_spec, ctx_node)
+ || (patt->next && tree_match_disj (t, patt->next, ctx_node)));
+}
Index: tree-match.h
===================================================================
--- tree-match.h (revision 0)
+++ tree-match.h (revision 0)
@@ -0,0 +1,230 @@
+/* Tree pattern matching and checking using concrete syntax.
+ Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+ Contributed by Nic Volanschi <nic.volanschi@free.fr>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* At the statement level, a node in the CFG has the following type. */
+typedef struct tree_statement_list_node *cfg_node;
+
+DEF_VEC_P (cfg_node);
+DEF_VEC_ALLOC_P (cfg_node, heap);
+
+/* Accessor for a CFG node. */
+static inline tree
+cfg_node_stmt (cfg_node node)
+{
+ return node->stmt;
+}
+
+/* Map a statement iterator to a CFG node. */
+static inline cfg_node
+bsi_cfg_node (block_stmt_iterator bsi)
+{
+ return bsi.tsi.ptr;
+}
+
+/* Get 1st CFG node in a basic block. */
+static inline cfg_node
+bb_1st_cfg_node (basic_block bb)
+{
+ return STATEMENT_LIST_HEAD (bb->stmt_list);
+}
+
+/* Patterns are of 2 kinds:
+ 1. with named holes, as in: "lock(%X, %Y)"
+ 2. with anonymous holes, as in: tree_scanf(t, "lock(%t, %t)", &t1, &t2)
+ */
+typedef struct patt_info_s {
+ const char *format_spec;
+ int sign;
+ va_list *args_ptr; /* only used for anomymous holes */
+ struct patt_info_s *next; /* next disjunct in the pattern */
+} patt_info;
+
+typedef patt_info *pattern;
+
+/* Atomic pattern constructor. */
+static inline pattern
+mkpat (const char *str)
+{
+ pattern res = xmalloc (sizeof (patt_info));
+ res->format_spec = xstrdup (str);
+ res->next = NULL;
+ res->args_ptr = NULL;
+ return res;
+}
+
+/* Disjunctive pattern constructor. */
+static inline pattern
+pat_or (pattern p1, pattern p2)
+{
+ p1->next = p2;
+ return p1;
+}
+
+/* Pattern destructor. */
+static inline void
+rmpat (pattern p)
+{
+ if (p->next)
+ rmpat (p->next);
+ free ((char *) p->format_spec);
+ free (p);
+}
+
+/* Display a pattern to stderr. */
+static inline void
+pat_print (pattern p)
+{
+ const char *sign;
+ if (!p)
+ return;
+
+ sign = (p->sign > 0)? "+" : (p->sign < 0)? "-" : "";
+ if (!p->next)
+ fprintf (stderr, "%s\"%s\"", sign, p->format_spec);
+ else
+ {
+ fprintf (stderr, "%s\"%s\" or ", sign, p->format_spec);
+ pat_print (p->next);
+ }
+}
+
+/* A "hole" is a pattern variable (aka meta-variable). It contains a
+ tree (which is NULL when the var is unistantiated), and a CFG
+ "context" node which points to the stmt containing the tree. The
+ context is useful to interpret temporary variables, or other
+ dataflow data. */
+typedef struct hole_s {
+ tree tree;
+ cfg_node ctx;
+} hole;
+
+typedef hole *hole_p;
+
+DEF_VEC_P (hole_p);
+DEF_VEC_ALLOC_P (hole_p, heap);
+
+/* Named local holes are noted by a single lowercase letter. */
+#define LOCAL_MAX 26
+/* Named global holes are noted by a single capital letter. */
+#define GLOBAL_MAX 26
+
+/* Named local holes are used locally within each pattern. Thus, a
+ variable %x may have different values in different patterns. */
+extern hole local_holes[LOCAL_MAX];
+/* Named global holes are shared between all the patterns in a same
+ property. Thus, a variable %X has the same value in different
+ patterns. */
+extern hole global_holes[GLOBAL_MAX];
+
+extern bool is_global_hole (char c);
+extern hole *get_hole_named (char c);
+extern void reset_local_holes (void);
+extern void reset_global_holes (void);
+extern hole *save_global_holes (void);
+extern void restore_global_holes (hole *saved);
+extern bool eq_global_holes (hole *holes1, hole *holes2);
+extern void print_local_holes (void);
+extern void print_global_holes (void);
+
+/* User interface to AST pattern matching. */
+extern bool tree_scanf (tree t, const char *fmt, cfg_node ctx_node, ...);
+extern bool tree_match (tree t, const char *fmt, cfg_node ctx_node);
+extern bool tree_match_disj (tree t, pattern fmt, cfg_node ctx_node);
+
+/* A "condate" is a program property involving CONtrol, DATa, and
+ other aspects such as syntactic and semantic information. For
+ example, '[there is a path] from "lock(%X)" to "unlock(%X)"
+ avoiding "return" or "return %_"' is a condate specifying
+ lock-unlock bugs. Thus, condates are built upon patterns, CFG and
+ dataflow information. */
+typedef struct condate_s {
+ char *name; /* Used to identify the condate in warning messages. */
+ pattern from; /* Paths start at nodes matching this pattern. */
+ pattern to; /* Paths end at nodes matching this pattern. */
+ pattern avoid; /* Paths must avoid nodes matching this pattern. */
+ pattern avoid_then; /* ... successful conditions matching this pattern. */
+ pattern avoid_else; /* ... and unsuccessful conditions mathing this one. */
+ const char *msg; /* Message to print if the condate is matched. */
+} *condate;
+
+/* Condate constructor. */
+static inline condate
+mkcond (const char *name, pattern from, pattern to, pattern avoid,
+ pattern avoid_then, pattern avoid_else)
+{
+ condate cond = xmalloc (sizeof (struct condate_s));
+ if (name)
+ cond->name = xstrdup (name);
+ else
+ cond->name = NULL;
+ cond->from = from;
+ cond->to = to;
+ cond->avoid = avoid;
+ cond->avoid_then = avoid_then;
+ cond->avoid_else = avoid_else;
+ cond->msg = NULL;
+ return cond;
+}
+
+/* Condate destructor. */
+static inline void
+rmcond (condate cond)
+{
+ if (cond->name)
+ free (cond->name);
+ rmpat (cond->from);
+ rmpat (cond->to);
+ rmpat (cond->avoid);
+ rmpat (cond->avoid_then);
+ rmpat (cond->avoid_else);
+ free (cond);
+}
+
+extern void print_cond (condate cond);
+
+#define CONDMAX 100
+extern condate conds[CONDMAX]; /* List of condated to check. */
+extern int n_conds; /* Number of condates to check. */
+
+extern void normalize_condate(condate cond);
+extern void name_condate(condate cond);
+extern void add_condate(condate cond);
+
+extern FILE *checkfile;
+extern int condate_parse (void);
+
+/* Tracing levels & macros. */
+enum trace_level {
+ TRACE_ALWAYS = 0,
+ TRACE_CHECK,
+ TRACE_CHECK_STEPS,
+ TRACE_MATCH,
+ TRACE_MATCH_STEPS
+};
+
+/* Current tracing level. */
+#define pp_trace_level TRACE_ALWAYS
+/* Use this macro to conditionally execute a tracing action. */
+#define PP_TRACE(lev, stmt) if (lev <= pp_trace_level) stmt;
Index: timevar.def
===================================================================
--- timevar.def (revision 123789)
+++ timevar.def (working copy)
@@ -122,6 +122,7 @@ DEFTIMEVAR (TV_TREE_COPY_RENAME , "
DEFTIMEVAR (TV_TREE_SSA_VERIFY , "tree SSA verifier")
DEFTIMEVAR (TV_TREE_STMT_VERIFY , "tree STMT verifier")
DEFTIMEVAR (TV_CGRAPH_VERIFY , "callgraph verifier")
+DEFTIMEVAR (TV_TREE_CHECK , "tree/cfg checker")
DEFTIMEVAR (TV_DOM_FRONTIERS , "dominance frontiers")
DEFTIMEVAR (TV_DOMINANCE , "dominance computation")
DEFTIMEVAR (TV_CONTROL_DEPENDENCES , "control dependences")
Index: common.opt
===================================================================
--- common.opt (revision 123789)
+++ common.opt (working copy)
@@ -986,6 +986,18 @@ ftree-ch
Common Report Var(flag_tree_ch) Optimization
Enable loop header copying on trees
+ftree-check=
+Common Joined RejectNegative
+-ftree-check=pattern: Warn for statements matching the pattern
+
+ftree-checks=
+Common Joined RejectNegative
+-ftree-checks=file: Take checking rules from file
+
+ftree-checks-verbose
+Common Report Var(flag_tree_check_verbose)
+Outputs more information on stderr for each tree-check warning
+
ftree-copyrename
Common Report Var(flag_tree_copyrename) Optimization
Replace SSA temporaries with better names in copies
Index: Makefile.in
===================================================================
--- Makefile.in (revision 123789)
+++ Makefile.in (working copy)
@@ -971,6 +971,7 @@ OBJS-common = \
cfgrtl.o \
combine.o \
combine-stack-adj.o \
+ condate.tab.o \
convert.o \
coverage.o \
cse.o \
@@ -1082,6 +1083,7 @@ OBJS-common = \
tree-affine.o \
tree-cfg.o \
tree-cfgcleanup.o \
+ tree-check.o \
tree-chrec.o \
tree-complex.o \
tree-data-ref.o \
@@ -1093,6 +1095,7 @@ OBJS-common = \
tree-into-ssa.o \
tree-iterator.o \
tree-loop-linear.o \
+ tree-match.o \
tree-nested.o \
tree-nrv.o \
tree-object-size.o \
@@ -2003,6 +2006,26 @@ tree-cfg.o : tree-cfg.c $(TREE_FLOW_H) $
$(TREE_DUMP_H) except.h langhooks.h $(CFGLOOP_H) tree-pass.h \
$(CFGLAYOUT_H) $(BASIC_BLOCK_H) hard-reg-set.h toplev.h \
tree-ssa-propagate.h
+tree-check.o : tree-check.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
+ $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) $(FLAGS_H) output.h \
+ $(DIAGNOSTIC_H) $(FUNCTION_H) $(TIMEVAR_H) $(TM_H) coretypes.h \
+ $(TREE_DUMP_H) except.h langhooks.h $(CFGLOOP_H) tree-pass.h \
+ $(CFGLAYOUT_H) $(BASIC_BLOCK_H) hard-reg-set.h $(HASHTAB_H) toplev.h \
+ tree-ssa-propagate.h tree-match.h
+tree-match.o : tree-match.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
+ $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) $(FLAGS_H) output.h \
+ $(DIAGNOSTIC_H) $(FUNCTION_H) $(TIMEVAR_H) $(TM_H) coretypes.h \
+ $(TREE_DUMP_H) except.h langhooks.h $(CFGLOOP_H) tree-pass.h \
+ $(CFGLAYOUT_H) $(BASIC_BLOCK_H) hard-reg-set.h $(HASHTAB_H) toplev.h \
+ tree-ssa-propagate.h tree-match.h
+condate.tab.o : condate.tab.c
+condate.tab.c : condate.y $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
+ $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) $(FLAGS_H) output.h \
+ $(DIAGNOSTIC_H) $(FUNCTION_H) $(TIMEVAR_H) $(TM_H) coretypes.h \
+ $(TREE_DUMP_H) except.h langhooks.h $(CFGLOOP_H) tree-pass.h \
+ $(CFGLAYOUT_H) $(BASIC_BLOCK_H) hard-reg-set.h $(HASHTAB_H) toplev.h \
+ tree-ssa-propagate.h tree-match.h
+ $(BISON) $(BISONFLAGS) $<
tree-cfgcleanup.o : tree-cfgcleanup.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
$(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) $(FLAGS_H) output.h \
$(DIAGNOSTIC_H) toplev.h $(FUNCTION_H) $(TIMEVAR_H) $(TM_H) coretypes.h \
@@ -2723,7 +2746,7 @@ params.o : params.c $(CONFIG_H) $(SYSTEM
pointer-set.o: pointer-set.c pointer-set.h $(CONFIG_H) $(SYSTEM_H)
hooks.o: hooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(HOOKS_H)
pretty-print.o: $(CONFIG_H) $(SYSTEM_H) coretypes.h intl.h $(PRETTY_PRINT_H) \
- $(TREE_H)
+ $(TREE_H) vec.h $(DIAGNOSTIC_H)
errors.o : errors.c $(CONFIG_H) $(SYSTEM_H) errors.h $(BCONFIG_H)
lower-subreg.o : lower-subreg.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
$(MACHMODE_H) $(TM_H) $(RTL_H) $(TM_P_H) $(TIMEVAR_H) $(FLAGS_H) \
@@ -2956,6 +2979,7 @@ GTFILES = $(srcdir)/input.h $(srcdir)/co
$(srcdir)/tree-mudflap.c $(srcdir)/tree-flow.h \
$(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c $(srcdir)/tree-ssa-address.c \
$(srcdir)/tree-phinodes.c $(srcdir)/tree-cfg.c \
+ $(srcdir)/tree-check.c $(srcdir)/tree-match.c \
$(srcdir)/tree-dfa.c $(srcdir)/tree-ssa-propagate.c \
$(srcdir)/tree-iterator.c $(srcdir)/gimplify.c \
$(srcdir)/tree-chrec.h $(srcdir)/tree-vect-generic.c \
Index: passes.c
===================================================================
--- passes.c (revision 123789)
+++ passes.c (working copy)
@@ -451,6 +451,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_lower_cf);
NEXT_PASS (pass_lower_eh);
NEXT_PASS (pass_build_cfg);
+ NEXT_PASS (pass_check);
NEXT_PASS (pass_lower_complex_O0);
NEXT_PASS (pass_lower_vector);
NEXT_PASS (pass_warn_function_return);
Index: condate.y
===================================================================
--- condate.y (revision 0)
+++ condate.y (revision 0)
@@ -0,0 +1,307 @@
+/* Condate language for tree/CFG checks.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Contributed by Nic Volanschi <nic.volanschi@free.fr>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
+
+/* The Condate language for expressing user-defined properties
+ of a program. These properties, called "condates"
+ blend control-flow, data-flow, syntactic and semantic information.
+*/
+
+%{
+ #define YYSTYPE void *
+ #include "config.h"
+ #include "system.h"
+ #include "coretypes.h"
+ #include "tm.h"
+ #include "tree.h"
+ #include "rtl.h"
+ #include "tm_p.h"
+ #include "hard-reg-set.h"
+ #include "basic-block.h"
+ #include "output.h"
+ #include "errors.h"
+ #include "flags.h"
+ #include "function.h"
+ #include "expr.h"
+ #include "diagnostic.h"
+ #include "tree-flow.h"
+ #include "timevar.h"
+ #include "tree-dump.h"
+ #include "tree-pass.h"
+ #include "toplev.h"
+ #include "tree-match.h"
+ int yylex (void);
+ void yyerror (char const *);
+
+%}
+
+/* Bison declarations. */
+%name-prefix="condate_"
+%token CONDATE
+%token FROM
+%token TO
+%token AVOID
+%token IDENT
+%right OR
+%token STR
+%token CCODE
+%token WARNING
+
+%% /* The grammar follows. */
+start: /* empty */
+| start condate {/* print_cond($2); */
+ normalize_condate($2);
+ name_condate($2);
+ add_condate($2);};
+
+condate: CONDATE IDENT '{' crq '}'
+ WARNING '(' STR ')' ';' {$$ = $4;
+ ((condate)$$)->name = $2; ((condate)$$)->msg = $8;}
+| crq ';' {$$ = $1;};
+
+/* Constraint Reachability Queries */
+crq: patexp {$$ = mkcond(NULL, $1, NULL, NULL, NULL, NULL);}
+| FROM patexp TO patexp {$$ = mkcond(NULL, $2, $4, NULL, NULL, NULL);}
+| FROM patexp TO patexp AVOID patexp {$$ = mkcond(NULL, $2, $4, $6, NULL, NULL);};
+
+patexp: edgepat {$$ = $1;}
+| patexp OR patexp {$$ = pat_or($1, $3);}
+| '(' patexp ')' {$$ = $2;};
+
+edgepat: pat {$$ = $1; ((pattern)$$)->sign = 0;}
+| '+' pat {$$ = $2; ((pattern)$$)->sign = +1;}
+| '-' pat {$$ = $2; ((pattern)$$)->sign = -1;};
+
+pat: STR sempat {$$ = mkpat($1); free($1);};
+
+sempat: /* empty */
+| '|' CCODE {fprintf(stderr, "%s: warning: semantic patterns NYI: {%s}\n",
+ tree_check_file, (char *)$2);};
+
+%%
+
+/* The lexical analyzer returns a double floating point
+ number on the stack and the token NUM, or the numeric code
+ of the character read if not a number. It skips all blanks
+ and tabs, and returns 0 for end-of-input. */
+
+#include <ctype.h>
+
+/* The folowing should be bigger than all of the folowing:
+ - the maximal keyword length
+ - the maximal pattern length
+ - the maximal length of a CCODE block.
+ Note: this ugly limit should be eliminated by writing the lexer in Flex.
+*/
+#define MAX_KEYWORD_LEN 1024
+
+int
+yylex (void)
+{
+ int c;
+ static char buf[MAX_KEYWORD_LEN + 1];
+ int len;
+ static int afterbar = 0;
+
+ c = getc (checkfile);
+ /* Skip white space and comments. */
+ do
+ {
+ while (c == ' ' || c == '\t' || c == '\n')
+ c = getc (checkfile);
+ if(c == '#')
+ {
+ while ((c = getc (checkfile)) != '\n' && c != EOF)
+ ;
+ if (c == '\n')
+ c = getc (checkfile);
+ }
+ } while(c == ' ' || c == '\t' || c == '#' || c == '\n');
+
+ /* Return end-of-input. */
+ if (c == EOF)
+ return 0;
+
+ /* recognize one-character keywords */
+ if (c == '+' || c == '-' || c == ';'
+ || c == '}' || c == '(' || c == ')')
+ return c;
+ if (c == '|')
+ {
+ afterbar = 1;
+ return c;
+ }
+
+ /* Process strings. */
+ if (c == '"')
+ {
+ len = 0;
+ while ((c = getc (checkfile)) != '"' && c != EOF && len < MAX_KEYWORD_LEN)
+ {
+ buf[len++] = c;
+ }
+ buf[len] = 0;
+ if (c == EOF || len == MAX_KEYWORD_LEN)
+ return 0;
+ yylval = xstrdup(buf);
+ return STR;
+ }
+
+ /* Meaning of '{' is context-dependent: */
+ if (c == '{')
+ {
+ if (!afterbar)
+ return c;
+ else
+ { /* Process C code. */
+ len = 0;
+ while ((c = getc (checkfile)) != '}' && c != EOF && len < MAX_KEYWORD_LEN)
+ {
+ buf[len++] = c;
+ }
+ if (c == EOF || len == MAX_KEYWORD_LEN)
+ return 0;
+ buf[len] = 0;
+ afterbar = 0;
+ yylval = xstrdup(buf);
+ return CCODE;
+ }
+ }
+ /* Recognize keywords & identifiers */
+ if (isalpha(c))
+ {
+ len = 0;
+ buf[len++] = c;
+ while ((isalnum((c = getc (checkfile))) || c == '_') && len < MAX_KEYWORD_LEN)
+ {
+ buf[len++] = c;
+ }
+ if (c == EOF || len == MAX_KEYWORD_LEN)
+ return 0;
+ buf[len] = 0;
+ ungetc (c, checkfile);
+
+ /* try keywords */
+ if (!strcmp (buf, "condate"))
+ return CONDATE;
+ else if (!strcmp (buf, "from"))
+ return FROM;
+ else if (!strcmp (buf, "to"))
+ return TO;
+ else if (!strcmp (buf, "avoid"))
+ return AVOID;
+ else if (!strcmp (buf, "or"))
+ return OR;
+ else if (!strcmp (buf, "warning"))
+ return WARNING;
+ /* identifier */
+ yylval = xstrdup (buf);
+ return IDENT;
+ }
+
+ /* Return a single char. */
+ fprintf (stderr, "Illegal character: '%c'\n", c);
+ return 0;
+}
+
+/* Called by yyparse on error. */
+void
+yyerror (char const *s)
+{
+ char buf[32];
+ fprintf (stderr, "%s: %s\n", tree_check_file, s);
+ fgets (buf, 32, checkfile);
+ fprintf (stderr, "%s: before or near: \"%s\"\n",
+ tree_check_file, buf);
+}
+
+struct split_pattern_s split_pattern(pattern p);
+
+/* Structure to return a pattern splitted in: unsigned, positive, and negative
+ edge patterns. */
+struct split_pattern_s {pattern p1, p2, p3;};
+
+struct split_pattern_s
+split_pattern (pattern p)
+{
+ struct split_pattern_s sp;
+ if (!p)
+ sp.p1 = sp.p2 = sp.p3 = NULL;
+ else
+ {
+ sp = split_pattern(p->next);
+ if (p->sign == 0)
+ {
+ p->next = sp.p1;
+ sp.p1 = p;
+ }
+ else if (p->sign > 0)
+ {
+ p->next = sp.p2;
+ sp.p2 = p;
+ }
+ else
+ {
+ p->next = sp.p3;
+ sp.p3 = p;
+ }
+ }
+ return sp;
+}
+
+/* Normalize a condate by separating the avoid patterns into:
+ - avoid (unsigned edge patterns),
+ - avoid_then (positive edge patterns), and
+ - avoid_else (negative edge patterns).
+ Normalization conserves the meaning of a condate, but optimizes its matching.
+ Note: we assume that the initial condate contains only 'avoid' patterns.
+*/
+void
+normalize_condate (condate cond)
+{
+ struct split_pattern_s sp = split_pattern (cond->avoid);
+ cond->avoid = sp.p1;
+ cond->avoid_then = sp.p2;
+ cond->avoid_else = sp.p3;
+}
+
+void
+name_condate (condate cond)
+{
+ if(!cond->name)
+ {
+ cond->name = xmalloc (strlen (tree_check_file) + 6);
+ strcpy (cond->name, tree_check_file);
+ sprintf (cond->name + strlen (tree_check_file), "[%d]", n_conds + 1);
+ }
+}
+
+void
+add_condate (condate cond)
+{
+ static int warned = 0;
+ if (n_conds == CONDMAX && !warned)
+ {
+ fprintf (stderr, "Warning: ignoring checks beyond %d", CONDMAX);
+ warned = 1;
+ return;
+ }
+ conds[n_conds++] = cond;
+}
Index: tree-check.c
===================================================================
--- tree-check.c (revision 0)
+++ tree-check.c (revision 0)
@@ -0,0 +1,517 @@
+/* Tree/CFG checking pass.
+ Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+ Contributed by Nic Volanschi <nic.volanschi@free.fr>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "output.h"
+#include "errors.h"
+#include "flags.h"
+#include "function.h"
+#include "expr.h"
+#include "diagnostic.h"
+#include "tree-flow.h"
+#include "timevar.h"
+#include "tree-dump.h"
+#include "tree-pass.h"
+#include "toplev.h"
+#include "tree-match.h"
+
+
+/* Raise a warning upon detecting a satisfied condate. The concept of
+ condate (control & data property to be checked) is described in
+ tree-match.h. */
+
+static void
+tree_check_warning (condate cond, tree stmt, int check_option)
+{
+ location_t saved_location = input_location;
+
+ if (EXPR_HAS_LOCATION (stmt))
+ input_location = EXPR_LOCATION (stmt);
+
+ warning (check_option, "user-defined warning %s: %s.",
+ cond->name, cond->msg ? cond->msg : "");
+
+ if (flag_tree_check_verbose)
+ {
+ fprintf (stderr, "%s:%d: instance = ", input_location.file, input_location.line);
+ /* print_local_holes (); */
+ print_global_holes ();
+ fprintf (stderr, ",\n");
+ fprintf (stderr, "%s:%d: reached: ", input_location.file, input_location.line);
+ print_generic_expr (stderr, stmt, 0);
+ fprintf (stderr, ".\n");
+ }
+
+ input_location = saved_location;
+}
+
+/* Initialization function for the tree-check pass. */
+
+static void
+tree_check_init (void)
+{
+ basic_block bb;
+ block_stmt_iterator bsi;
+
+ reset_global_holes ();
+
+ FOR_EACH_BB (bb)
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ TREE_VISITED (bsi_stmt (bsi)) = 0;
+}
+
+/* Visit a CFG node. Used in tree_check_instance. */
+
+static bool
+check_node (cfg_node node, condate cond)
+{
+ tree stmt = cfg_node_stmt (node);
+
+ if (!stmt || TREE_VISITED (stmt))
+ return 0;
+
+ TREE_VISITED (stmt) = 1;
+
+ PP_TRACE (TRACE_CHECK_STEPS, {
+ fprintf (stderr, "checking stmt:");
+ print_generic_expr (stderr, stmt, 0);
+ fprintf (stderr, "\n");
+ });
+
+ if (tree_match_disj (stmt, cond->to, node))
+ {
+ tree_check_warning (cond, stmt, OPT_ftree_checks_);
+ return 0; /* follow_none */
+ }
+
+ /* Inspect successors? */
+ if (cond->avoid && tree_match_disj (stmt, cond->avoid, node))
+ {
+ PP_TRACE (TRACE_CHECK, fprintf (stderr, "via node, backtracking\n"));
+ return 0; /* follow_none */;
+ }
+ else
+ return 1; /* follow_all */
+}
+
+/* Check a condate instance over the CFG of the current function. */
+
+static void
+tree_check_instance (condate cond)
+{
+ VEC (cfg_node, heap) *stack = VEC_alloc (cfg_node, heap, 100);
+ basic_block bb;
+ cfg_node node;
+ tree stmt;
+
+ PP_TRACE (TRACE_CHECK, {
+ fprintf (stderr, "checking condate instance:\n");
+ print_global_holes ();
+ });
+
+ /* Push from nodes on the stack. */
+ PP_TRACE (TRACE_CHECK, fprintf (stderr, "searching src pat %s\n",
+ cond->from->format_spec));
+
+ FOR_EACH_BB (bb)
+ {
+ block_stmt_iterator bsi;
+ tree stmt;
+
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ pattern patt;
+ stmt = bsi_stmt (bsi);
+ patt = cond->from;
+
+ PP_TRACE (TRACE_MATCH, {
+ lazy_print_generic_expr (stderr, stmt, 0);
+ fprintf (stderr, "= ");
+ print_generic_expr (stderr, stmt, 0);
+ fprintf (stderr, "\n");
+ });
+
+ if (!patt || tree_match_disj (stmt, patt, bsi_cfg_node (bsi)))
+ {
+ node = bsi_cfg_node (bsi);
+ stmt = cfg_node_stmt (node);
+
+ VEC_safe_push (cfg_node, heap, stack, node);
+
+ if (stmt)
+ TREE_VISITED (stmt) = 1;
+
+ PP_TRACE (TRACE_CHECK_STEPS, {
+ fprintf (stderr, "found src stmt:");
+ print_generic_expr (stderr, stmt, 0);
+ fprintf (stderr, "\n");
+ });
+ }
+ }
+ }
+
+ PP_TRACE (TRACE_CHECK, fprintf (stderr, "%d src stmts found\n",
+ (unsigned) VEC_length (cfg_node, stack)));
+
+ /* Perform depth-first search. */
+ while (VEC_length (cfg_node, stack) != 0)
+ {
+ cfg_node succ_node;
+ bool push_it;
+
+ node = VEC_pop (cfg_node, stack);
+ stmt = cfg_node_stmt (node);
+
+ if (node->next == NULL)
+ {
+ edge e;
+ edge_iterator ei;
+
+ bb = bb_for_stmt (stmt);
+
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ if (e->dest == EXIT_BLOCK_PTR)
+ continue;
+
+ if (TREE_CODE (stmt) == COND_EXPR
+ && (e->flags & EDGE_TRUE_VALUE && cond->avoid_then
+ && tree_match_disj (COND_EXPR_COND (stmt), cond->avoid_then,
+ node)))
+ {
+ PP_TRACE (TRACE_CHECK,
+ fprintf (stderr, "via-then edge, skipping\n"));
+ continue;
+ }
+
+ if (TREE_CODE (stmt) == COND_EXPR
+ && (e->flags & EDGE_FALSE_VALUE && cond->avoid_else
+ && tree_match_disj (COND_EXPR_COND (stmt), cond->avoid_else,
+ node)))
+ {
+ PP_TRACE (TRACE_CHECK,
+ fprintf (stderr, "via-else edge, skipping\n"));
+ continue;
+ }
+
+ succ_node = bb_1st_cfg_node (e->dest);
+ push_it = check_node (succ_node, cond);
+
+ if (push_it)
+ VEC_safe_push (cfg_node, heap, stack, succ_node);
+ }
+ }
+ else
+ {
+ succ_node = node->next;
+ push_it = check_node (succ_node, cond);
+
+ if (push_it)
+ VEC_safe_push (cfg_node, heap, stack, succ_node);
+ }
+ }
+
+ VEC_free (cfg_node, heap, stack);
+}
+
+/* Collect new condate instances. An instance is new if the
+ combination of global hole values has not been seen yet. */
+
+static void
+push_global_holes_if_new (VEC (hole_p, heap) *stack)
+{
+ unsigned int i;
+ hole_p h;
+
+ /* Check if these global holes were already seen. */
+ for (i = 0; VEC_iterate (hole_p, stack, i, h); i++)
+ if (eq_global_holes (global_holes, h))
+ {
+ reset_global_holes ();
+ return;
+ }
+
+ VEC_safe_push (hole_p, heap, stack, save_global_holes ());
+ reset_global_holes ();
+}
+
+/* Check a trivial condate consisting only in a (FROM) pattern.
+ This comes to reporting every match of the pattern in a function. */
+
+static void
+tree_scan (condate cond)
+{
+ basic_block bb;
+
+ FOR_EACH_BB (bb)
+ {
+ block_stmt_iterator bsi;
+ tree stmt;
+
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ stmt = bsi_stmt (bsi);
+
+ PP_TRACE (TRACE_MATCH, {
+ lazy_print_generic_expr (stderr, stmt, 0);
+ fprintf (stderr, "= ");
+ print_generic_expr (stderr, stmt, 0);
+ fprintf (stderr, "\n");
+ });
+
+ if (!cond->from || tree_match_disj (stmt, cond->from, bsi_cfg_node (bsi)))
+ {
+ tree_check_warning (cond,
+ cfg_node_stmt (bsi_cfg_node (bsi)),
+ OPT_ftree_check_);
+ reset_global_holes ();
+ }
+ }
+ }
+}
+
+/* Check a condate on a function. */
+
+static void
+tree_check (condate cond)
+{
+ VEC (hole_p, heap) *stack;
+ pattern patt = cond->from;
+ basic_block bb;
+
+ /* Check for trivial condates. */
+ if (!cond->to)
+ {
+ tree_scan (cond);
+ return;
+ }
+
+ /* Allocate stack for collecting condate instances. */
+ stack = VEC_alloc (hole_p, heap, 10);
+ patt = cond->from;
+
+ PP_TRACE (TRACE_CHECK,
+ fprintf (stderr, "searching src pat %s\n",
+ patt->format_spec));
+
+ FOR_EACH_BB (bb)
+ {
+ block_stmt_iterator bsi;
+ tree stmt;
+
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ stmt = bsi_stmt (bsi);
+
+ PP_TRACE (TRACE_MATCH, {
+ lazy_print_generic_expr (stderr, stmt, 0);
+ fprintf (stderr, "= ");
+ print_generic_expr (stderr, stmt, 0);
+ fprintf (stderr, "\n");
+ });
+
+ if (!patt || tree_match_disj (stmt, patt, bsi_cfg_node (bsi)))
+ push_global_holes_if_new (stack);
+ }
+ }
+
+ PP_TRACE (TRACE_CHECK, fprintf (stderr, "%d condate instances found\n",
+ VEC_length (hole_p, stack)));
+
+ while (VEC_length (hole_p, stack))
+ {
+ hole_p h = VEC_pop (hole_p, stack);
+
+ restore_global_holes (h);
+ tree_check_instance (cond);
+ PP_TRACE (TRACE_CHECK, fprintf (stderr, "recounting stmts\n"));
+ tree_check_init (); /* clear visited flag */
+ }
+
+ VEC_free (hole_p, heap, stack);
+}
+
+
+/* Print a condate. */
+
+void
+print_cond (condate cond)
+{
+ fprintf (stderr, "condate %s {", cond->name);
+ pat_print (cond->from);
+ fprintf (stderr, ", ");
+ pat_print (cond->to);
+ fprintf (stderr, ", ");
+ pat_print (cond->avoid);
+ fprintf (stderr, ", ");
+ pat_print (cond->avoid_then);
+ fprintf (stderr, ", ");
+ pat_print (cond->avoid_else);
+ fprintf (stderr, "} warning(%s);\n", cond->msg? cond->msg : "<null>");
+}
+
+/* Check a list of condates on the current function. */
+
+static void
+execute_conds (condate conds[], int n)
+{
+ int i;
+ condate cond;
+
+ for (i = 0; i < n; i++)
+ {
+ cond = conds[i];
+ PP_TRACE (TRACE_CHECK, {
+ print_cond (cond);
+ });
+
+ tree_check (cond);
+ }
+}
+
+condate conds[CONDMAX]; /* list of condated to check */
+int n_conds = 0; /* number of condates to check */
+
+/* Flush the list of condates. */
+
+static void
+delete_conds (condate conds[], int n)
+{
+ int i;
+ condate cond;
+
+ for (i = 0; i < n; i++)
+ {
+ cond = conds[i];
+ rmcond (cond);
+ }
+ n_conds = 0;
+}
+
+/* Open file containing the checks. Used by the parser condate.y. */
+FILE *checkfile;
+
+/* Parse the file containing condates definitions, and cache the result. */
+
+static int
+parse_tree_check_file_once (void)
+{
+ static const char *current_check_file = NULL;
+
+ if (current_check_file)
+ {
+ /* Not called for the first time. */
+ if (!strcmp (current_check_file, tree_check_file))
+ /* file hasn't changed */
+ return 0;
+ else
+ delete_conds (conds, n_conds);
+ }
+
+ current_check_file = tree_check_file;
+ checkfile = fopen (tree_check_file, "r");
+
+ if (!checkfile)
+ {
+ fprintf (stderr, "tree-check-file %s not found\n", tree_check_file);
+ return -1;
+ }
+
+ if (condate_parse () != 0)
+ {
+ fclose (checkfile);
+ return -2;
+ }
+
+ fclose (checkfile);
+ return 0;
+}
+
+/* Main function of the tree-check pass. Triggered either by
+ -ftree-check or -ftree-checks. */
+
+static unsigned int
+execute_tree_check (void)
+{
+ const char *fn_name = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
+
+ PP_TRACE (TRACE_CHECK, fprintf (stderr, "function %s() {\n", fn_name));
+ PP_TRACE (TRACE_CHECK,
+ fprintf (stderr,
+ "Executing tree reachability checks: file=%s, string=%s\n",
+ tree_check_file, tree_check_string));
+ PP_TRACE (TRACE_CHECK, fprintf (stderr, "counting stmts\n"));
+ tree_check_init ();
+
+ if (tree_check_file)
+ {
+ if (parse_tree_check_file_once () < 0)
+ return 0;
+ }
+ else
+ {
+ /* tree_check_string != NULL */
+ static const char *current_check_string = NULL;
+ if (!current_check_string)
+ {
+ condate cond = mkcond (tree_check_string, mkpat (tree_check_string),
+ NULL, NULL, NULL, NULL);
+ add_condate (cond);
+ current_check_string = tree_check_string;
+ }
+ }
+ execute_conds (conds, n_conds);
+
+ PP_TRACE (TRACE_CHECK, fprintf (stderr, "}\n"));
+ return 0;
+}
+
+static bool
+gate_tree_check (void)
+{
+ return ((tree_check_file != 0 || tree_check_string != 0)
+ && basic_block_info != 0);
+}
+
+struct tree_opt_pass pass_check =
+{
+ "check", /* name */
+ gate_tree_check, /* gate */
+ execute_tree_check, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_TREE_CHECK, /* tv_id */
+ PROP_cfg, /* | PROP_ssa, */ /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+ 0 /* letter */
+};