This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

-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 = &ap;
+  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 */
+};

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]