aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/plugin.c1
-rw-r--r--gcc/testsuite/ChangeLog19
-rw-r--r--gcc/testsuite/g++.dg/README1
-rw-r--r--gcc/testsuite/g++.dg/dg.exp1
-rw-r--r--gcc/testsuite/g++.dg/plugin/dumb-plugin-test-1.C53
-rw-r--r--gcc/testsuite/g++.dg/plugin/dumb_plugin.c136
-rw-r--r--gcc/testsuite/g++.dg/plugin/plugin.exp66
-rw-r--r--gcc/testsuite/g++.dg/plugin/self-assign-test-1.C50
-rw-r--r--gcc/testsuite/g++.dg/plugin/self-assign-test-2.C50
-rw-r--r--gcc/testsuite/g++.dg/plugin/self-assign-test-3.C50
-rw-r--r--gcc/testsuite/g++.dg/plugin/selfassign.c365
-rw-r--r--gcc/testsuite/gcc.dg/plugin/plugin.exp65
-rw-r--r--gcc/testsuite/gcc.dg/plugin/self-assign-test-1.c23
-rw-r--r--gcc/testsuite/gcc.dg/plugin/self-assign-test-2.c23
-rw-r--r--gcc/testsuite/gcc.dg/plugin/selfassign.c365
-rw-r--r--gcc/testsuite/lib/plugin-support.exp102
16 files changed, 1369 insertions, 1 deletions
diff --git a/gcc/plugin.c b/gcc/plugin.c
index a8c2eea..dec336d 100644
--- a/gcc/plugin.c
+++ b/gcc/plugin.c
@@ -816,7 +816,6 @@ bool
plugin_default_version_check (struct plugin_gcc_version *gcc_version,
struct plugin_gcc_version *plugin_version)
{
- /* version is NULL if the plugin was not linked with plugin-version.o */
if (!gcc_version || !plugin_version)
return false;
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 7953f67..2a770f2 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,22 @@
+2009-05-06 Le-Chun Wu <lcwu@google.com>
+
+ * lib/plugin-support.exp: New file containing support procs for
+ plugin testcases.
+ * lib/target-supports.exp (check_plugin_available): New proc.
+ * gcc.dg/plugin/plugin.exp: New driver script for gcc testcases.
+ * gcc.dg/plugin/selfassign.c: New plugin source file.
+ * gcc.dg/plugin/self-assign-test-1.c: New test.
+ * gcc.dg/plugin/self-assign-test-2.c: Likewise.
+ * g++.dg/README: Add description for plugin test.
+ * g++.dg/dg.exp: Exclude plugin tests from the general test list.
+ * g++.dg/plugin/plugin.exp: New driver script for g++ testcases.
+ * g++.dg/plugin/selfassign.c: New plugin source file.
+ * g++.dg/plugin/self-assign-test-1.C: New test.
+ * g++.dg/plugin/self-assign-test-2.C: Likewise.
+ * g++.dg/plugin/self-assign-test-3.C: Likewise.
+ * g++.dg/plugin/dumb_plugin.c: New plugin source file.
+ * g++.dg/plugin/dumb-plugin-test-1.C: New test.
+
2009-05-06 Tobias Burnus <burnus@net-b.de>
PR fortran/40041
diff --git a/gcc/testsuite/g++.dg/README b/gcc/testsuite/g++.dg/README
index c63f7cc..cdf6b14 100644
--- a/gcc/testsuite/g++.dg/README
+++ b/gcc/testsuite/g++.dg/README
@@ -18,6 +18,7 @@ opt Tests for fixes of bugs with particular optimizations.
overload Tests for overload resolution and conversions.
parse Tests for parsing.
pch Tests for precompiled headers.
+plugin Tests for plugin support.
rtti Tests for run-time type identification (typeid, dynamic_cast, etc.)
template Tests for templates.
tc1 Tests for Technical Corrigendum 1 conformance.
diff --git a/gcc/testsuite/g++.dg/dg.exp b/gcc/testsuite/g++.dg/dg.exp
index 86565a9..a19bc2c 100644
--- a/gcc/testsuite/g++.dg/dg.exp
+++ b/gcc/testsuite/g++.dg/dg.exp
@@ -37,6 +37,7 @@ set tests [prune $tests $srcdir/$subdir/compat/*]
set tests [prune $tests $srcdir/$subdir/debug/*]
set tests [prune $tests $srcdir/$subdir/gcov/*]
set tests [prune $tests $srcdir/$subdir/pch/*]
+set tests [prune $tests $srcdir/$subdir/plugin/*]
set tests [prune $tests $srcdir/$subdir/special/*]
set tests [prune $tests $srcdir/$subdir/tls/*]
set tests [prune $tests $srcdir/$subdir/vect/*]
diff --git a/gcc/testsuite/g++.dg/plugin/dumb-plugin-test-1.C b/gcc/testsuite/g++.dg/plugin/dumb-plugin-test-1.C
new file mode 100644
index 0000000..70101c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/dumb-plugin-test-1.C
@@ -0,0 +1,53 @@
+// Test case for the dumb plugin.
+// { dg-do compile }
+// { dg-options "-O -fplugin-arg-dumb_plugin-ref-pass-name=ccp -fplugin-arg-dumb_plugin-ref-pass-instance-num=1" }
+
+class Foo {
+ private:
+ int a_;
+
+ public:
+ Foo() : a_(a_) {} // { dg-warning "Before genericizing function" }
+
+ void setA(int a) {
+ a_ = a_;
+ } // { dg-warning "Before genericizing function" }
+
+ void operator=(Foo& rhs) {
+ this->a_ = rhs.a_;
+ } // { dg-warning "Before genericizing function" }
+}; // { dg-warning "Process struct Foo" }
+
+struct Bar {
+ int b_;
+ int c_;
+}; // { dg-warning "Process struct Bar" }
+
+int g = g;
+Foo foo = foo;
+
+int func()
+{
+ Bar *bar1, bar2;
+ Foo local_foo;
+ int x = x;
+ static int y = y;
+ float *f;
+ Bar bar_array[5];
+ char n;
+ int overflow;
+
+ *f = *f;
+ bar1->b_ = bar1->b_;
+ bar2.c_ = bar2.c_;
+ local_foo = local_foo;
+ foo = foo;
+ foo.setA(5);
+ bar_array[3].c_ = bar_array[3].c_;
+ bar_array[x+g].b_ = bar_array[x+g].b_;
+ y = x;
+ x = y;
+} // { dg-warning "Before genericizing function" }
+
+// { dg-warning "Analyze function" "" { target *-*-* } 50 }
+// { dg-warning "End of compilation unit" "" { target *-*-* } 50 }
diff --git a/gcc/testsuite/g++.dg/plugin/dumb_plugin.c b/gcc/testsuite/g++.dg/plugin/dumb_plugin.c
new file mode 100644
index 0000000..0c62f89
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/dumb_plugin.c
@@ -0,0 +1,136 @@
+/* A trivial (dumb) plugin example that shows how to use the GCC plugin
+ mechanism. */
+
+#include <stdlib.h>
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "gcc-plugin.h"
+
+
+/* Callback function to invoke after GCC finishes parsing a struct. */
+
+void
+handle_struct (void *event_data, void *data)
+{
+ tree type = (tree) event_data;
+ warning (0, G_("Process struct %s"),
+ IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))));
+}
+
+/* Callback function to invoke before the program is genericized. */
+
+void
+handle_pre_generic (void *event_data, void *data)
+{
+ tree fndecl = (tree) event_data;
+ warning (0, G_("Before genericizing function %s"),
+ IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+}
+
+/* Callback function to invoke after GCC finishes the compilation unit. */
+
+void
+handle_end_of_compilation_unit (void *event_data, void *data)
+{
+ warning (0, G_("End of compilation unit"));
+}
+
+
+static unsigned int
+execute_dumb_plugin_example (void)
+{
+ warning (0, G_("Analyze function %s"),
+ IDENTIFIER_POINTER (DECL_NAME (current_function_decl)));
+ return 0;
+}
+
+static bool
+gate_dumb_plugin_example (void)
+{
+ return true;
+}
+
+static struct gimple_opt_pass pass_dumb_plugin_example =
+{
+ {
+ GIMPLE_PASS,
+ "dumb_plugin_example", /* name */
+ gate_dumb_plugin_example, /* gate */
+ execute_dumb_plugin_example, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ 0, /* tv_id */
+ PROP_cfg, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func /* todo_flags_finish */
+ }
+};
+
+/* Initialization function that GCC calls. This plugin takes an argument
+ that specifies the name of the reference pass and an instance number,
+ both of which determine where the plugin pass should be inserted. */
+
+int
+plugin_init (const char *plugin_name,
+ struct plugin_gcc_version *version __attribute__((unused)),
+ int argc, struct plugin_argument *argv)
+{
+ struct plugin_pass pass_info;
+ char *ref_pass_name = NULL;
+ int ref_instance_number = 0;
+ int i;
+
+ /* Process the plugin arguments. This plugin takes the following arguments:
+ ref-pass-name=<PASS_NAME> and ref-pass-instance-num=<NUM>. */
+ for (i = 0; i < argc; ++i)
+ {
+ if (!strcmp (argv[i].key, "ref-pass-name"))
+ {
+ if (argv[i].value)
+ ref_pass_name = argv[i].value;
+ else
+ warning (0, G_("option '-fplugin-arg-%s-ref-pass-name'"
+ " requires a pass name"), plugin_name);
+ }
+ else if (!strcmp (argv[i].key, "ref-pass-instance-num"))
+ {
+ if (argv[i].value)
+ ref_instance_number = strtol (argv[i].value, NULL, 0);
+ else
+ warning (0, G_("option '-fplugin-arg-%s-ref-pass-instance-num'"
+ " requires an integer value"), plugin_name);
+ }
+ else
+ warning (0, G_("plugin %qs: unrecognized argument %qs ignored"),
+ plugin_name, argv[i].key);
+ }
+
+ if (!ref_pass_name)
+ {
+ error (G_("plugin %qs requires a reference pass name"), plugin_name);
+ return 1;
+ }
+
+ pass_info.pass = &pass_dumb_plugin_example.pass;
+ pass_info.reference_pass_name = ref_pass_name;
+ pass_info.ref_pass_instance_number = ref_instance_number;
+ pass_info.pos_op = PASS_POS_INSERT_AFTER;
+
+ register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info);
+
+ register_callback (plugin_name, PLUGIN_FINISH_TYPE, handle_struct, NULL);
+
+ register_callback (plugin_name, PLUGIN_CXX_CP_PRE_GENERICIZE,
+ handle_pre_generic, NULL);
+
+ register_callback (plugin_name, PLUGIN_FINISH_UNIT,
+ handle_end_of_compilation_unit, NULL);
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp
new file mode 100644
index 0000000..e1f6d89
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
@@ -0,0 +1,66 @@
+# Copyright (C) 2009 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 3 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 GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Test the functionality of the GCC plugin support
+
+load_lib target-supports.exp
+load_lib g++-dg.exp
+
+global TESTING_IN_BUILD_TREE
+global ENABLE_PLUGIN
+
+# The plugin testcases currently only work when the build tree is available.
+# Also check whether the host supports plugins.
+if { ![info exists TESTING_IN_BUILD_TREE] || ![info exists ENABLE_PLUGIN] } {
+ return
+}
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CXXFLAGS
+if ![info exists DEFAULT_CXXFLAGS] then {
+ set DEFAULT_CXXFLAGS " -ansi -pedantic-errors -Wno-long-long"
+}
+
+# The procedures in plugin-support.exp need these parameters.
+set default_flags $DEFAULT_CXXFLAGS
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+# Load support procs.
+load_lib plugin-support.exp
+
+# Specify the plugin source file and the associated test files in a list.
+# plugin_test_list={ {plugin1 test1 test2 ...} {plugin2 test1 ...} ... }
+set plugin_test_list [list \
+ { selfassign.c self-assign-test-1.C self-assign-test-2.C self-assign-test-3.C } \
+ { dumb_plugin.c dumb-plugin-test-1.C } ]
+
+foreach plugin_test $plugin_test_list {
+ # Replace each source file with its full-path name
+ for {set i 0} {$i < [llength $plugin_test]} {incr i} {
+ set basename [lindex $plugin_test $i]
+ set plugin_test [lreplace $plugin_test $i $i $srcdir/$subdir/$basename]
+ }
+ set plugin_src [lindex $plugin_test 0]
+ # If we're only testing specific files and this isn't one of them, skip it.
+ if ![runtest_file_p $runtests $plugin_src] then {
+ continue
+ }
+ set plugin_input_tests [lreplace $plugin_test 0 0]
+ plugin-test-execute $plugin_src $plugin_input_tests
+}
diff --git a/gcc/testsuite/g++.dg/plugin/self-assign-test-1.C b/gcc/testsuite/g++.dg/plugin/self-assign-test-1.C
new file mode 100644
index 0000000..607381f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/self-assign-test-1.C
@@ -0,0 +1,50 @@
+// Test the self-assignemnt detection plugin.
+// { dg-do compile }
+// { dg-options "-O" }
+
+class Foo {
+ private:
+ int a_;
+
+ public:
+ Foo() : a_(a_) {} // { dg-warning "assigned to itself" }
+
+ void setA(int a) {
+ a_ = a_; // { dg-warning "assigned to itself" }
+ }
+
+ void operator=(Foo& rhs) {
+ this->a_ = rhs.a_;
+ }
+};
+
+struct Bar {
+ int b_;
+ int c_;
+};
+
+int g = g; // { dg-warning "assigned to itself" }
+Foo foo = foo; // { dg-warning "assigned to itself" }
+
+int func()
+{
+ Bar *bar1, bar2;
+ Foo local_foo;
+ int x = x; // { dg-warning "assigned to itself" }
+ static int y = y; // { dg-warning "assigned to itself" }
+ float *f;
+ Bar bar_array[5];
+ char n;
+ int overflow;
+
+ *f = *f; // { dg-warning "assigned to itself" }
+ bar1->b_ = bar1->b_; // { dg-warning "assigned to itself" }
+ bar2.c_ = bar2.c_; // { dg-warning "assigned to itself" }
+ local_foo = local_foo; // { dg-warning "assigned to itself" }
+ foo = foo; // { dg-warning "assigned to itself" }
+ foo.setA(5);
+ bar_array[3].c_ = bar_array[3].c_; // { dg-warning "assigned to itself" }
+ bar_array[x+g].b_ = bar_array[x+g].b_; // { dg-warning "self-assignment detected" }
+ y = x;
+ x = y;
+}
diff --git a/gcc/testsuite/g++.dg/plugin/self-assign-test-2.C b/gcc/testsuite/g++.dg/plugin/self-assign-test-2.C
new file mode 100644
index 0000000..35e1fb8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/self-assign-test-2.C
@@ -0,0 +1,50 @@
+// Test the self-assignemnt detection plugin without checking of operator-eq.
+// { dg-do compile }
+// { dg-options "-O -fplugin-arg-selfassign-no-check-operator-eq" }
+
+class Foo {
+ private:
+ int a_;
+
+ public:
+ Foo() : a_(a_) {} // { dg-warning "assigned to itself" }
+
+ void setA(int a) {
+ a_ = a_; // { dg-warning "assigned to itself" }
+ }
+
+ void operator=(Foo& rhs) {
+ this->a_ = rhs.a_;
+ }
+};
+
+struct Bar {
+ int b_;
+ int c_;
+};
+
+int g = g; // { dg-warning "assigned to itself" }
+Foo foo = foo; // { dg-warning "assigned to itself" }
+
+int func()
+{
+ Bar *bar1, bar2;
+ Foo local_foo;
+ int x = x; // { dg-warning "assigned to itself" }
+ static int y = y; // { dg-warning "assigned to itself" }
+ float *f;
+ Bar bar_array[5];
+ char n;
+ int overflow;
+
+ *f = *f; // { dg-warning "assigned to itself" }
+ bar1->b_ = bar1->b_; // { dg-warning "assigned to itself" }
+ bar2.c_ = bar2.c_; // { dg-warning "assigned to itself" }
+ local_foo = local_foo; // { dg-bogus "assigned to itself" }
+ foo = foo; // { dg-bogus "assigned to itself" }
+ foo.setA(5);
+ bar_array[3].c_ = bar_array[3].c_; // { dg-warning "assigned to itself" }
+ bar_array[x+g].b_ = bar_array[x+g].b_; // { dg-warning "self-assignment detected" }
+ y = x;
+ x = y;
+}
diff --git a/gcc/testsuite/g++.dg/plugin/self-assign-test-3.C b/gcc/testsuite/g++.dg/plugin/self-assign-test-3.C
new file mode 100644
index 0000000..e5b354b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/self-assign-test-3.C
@@ -0,0 +1,50 @@
+// Test the self-assignemnt detection plugin with the 'disable' argument.
+// { dg-do compile }
+// { dg-options "-O -fplugin-arg-selfassign-disable" }
+
+class Foo {
+ private:
+ int a_;
+
+ public:
+ Foo() : a_(a_) {} // { dg-bogus "assigned to itself" }
+
+ void setA(int a) {
+ a_ = a_; // { dg-bogus "assigned to itself" }
+ }
+
+ void operator=(Foo& rhs) {
+ this->a_ = rhs.a_;
+ }
+};
+
+struct Bar {
+ int b_;
+ int c_;
+};
+
+int g = g; // { dg-bogus "assigned to itself" }
+Foo foo = foo; // { dg-bogus "assigned to itself" }
+
+int func()
+{
+ Bar *bar1, bar2;
+ Foo local_foo;
+ int x = x; // { dg-bogus "assigned to itself" }
+ static int y = y; // { dg-bogus "assigned to itself" }
+ float *f;
+ Bar bar_array[5];
+ char n;
+ int overflow;
+
+ *f = *f; // { dg-bogus "assigned to itself" }
+ bar1->b_ = bar1->b_; // { dg-bogus "assigned to itself" }
+ bar2.c_ = bar2.c_; // { dg-bogus "assigned to itself" }
+ local_foo = local_foo; // { dg-bogus "assigned to itself" }
+ foo = foo; // { dg-bogus "assigned to itself" }
+ foo.setA(5);
+ bar_array[3].c_ = bar_array[3].c_; // { dg-bogus "assigned to itself" }
+ bar_array[x+g].b_ = bar_array[x+g].b_; // { dg-bogus "self-assignment detected" }
+ y = x;
+ x = y;
+}
diff --git a/gcc/testsuite/g++.dg/plugin/selfassign.c b/gcc/testsuite/g++.dg/plugin/selfassign.c
new file mode 100644
index 0000000..6fbce83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/selfassign.c
@@ -0,0 +1,365 @@
+/* This plugin contains an analysis pass that detects and warns about
+ self-assignment statements. */
+/* { dg-options "-O" } */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "toplev.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "gcc-plugin.h"
+
+
+/* Indicate whether to check overloaded operator '=', which is performed by
+ default. To disable it, use -fplugin-arg-NAME-no-check-operator-eq. */
+bool check_operator_eq = true;
+
+/* Given a rhs EXPR of a gimple assign statement, if it is
+ - SSA_NAME : returns its var decl, or, if it is a temp variable,
+ returns the rhs of its SSA def statement.
+ - VAR_DECL, PARM_DECL, FIELD_DECL, or a reference expression :
+ returns EXPR itself.
+ - any other expression : returns NULL_TREE. */
+
+static tree
+get_real_ref_rhs (tree expr)
+{
+ switch (TREE_CODE (expr))
+ {
+ case SSA_NAME:
+ {
+ /* Given a self-assign statement, say foo.x = foo.x,
+ the IR (after SSA) looks like:
+
+ D.1797_14 = foo.x;
+ foo.x ={v} D.1797_14;
+
+ So if the rhs EXPR is an SSA_NAME of a temp variable,
+ e.g. D.1797_14, we need to grab the rhs of its SSA def
+ statement (i.e. foo.x). */
+ tree vdecl = SSA_NAME_VAR (expr);
+ if (DECL_ARTIFICIAL (vdecl)
+ && !gimple_nop_p (SSA_NAME_DEF_STMT (expr)))
+ {
+ gimple def_stmt = SSA_NAME_DEF_STMT (expr);
+ /* We are only interested in an assignment with a single
+ rhs operand because if it is not, the original assignment
+ will not possibly be a self-assignment. */
+ if (is_gimple_assign (def_stmt)
+ && (get_gimple_rhs_class (gimple_assign_rhs_code (def_stmt))
+ == GIMPLE_SINGLE_RHS))
+ return get_real_ref_rhs (gimple_assign_rhs1 (def_stmt));
+ else
+ return NULL_TREE;
+ }
+ else
+ return vdecl;
+ }
+ case VAR_DECL:
+ case PARM_DECL:
+ case FIELD_DECL:
+ case COMPONENT_REF:
+ case INDIRECT_REF:
+ case ARRAY_REF:
+ return expr;
+ default:
+ return NULL_TREE;
+ }
+}
+
+/* Given an expression tree, EXPR, that may contains SSA names, returns an
+ equivalent tree with the SSA names converted to var/parm/field decls
+ so that it can be used with '%E' format modifier when emitting warning
+ messages.
+
+ This function currently only supports VAR/PARM/FIELD_DECL, reference
+ expressions (COMPONENT_REF, INDIRECT_REF, ARRAY_REF), integer constant,
+ and SSA_NAME. If EXPR contains any other tree nodes (e.g. an arithmetic
+ expression appears in array index), NULL_TREE is returned. */
+
+static tree
+get_non_ssa_expr (tree expr)
+{
+ switch (TREE_CODE (expr))
+ {
+ case VAR_DECL:
+ case PARM_DECL:
+ case FIELD_DECL:
+ {
+ if (DECL_NAME (expr))
+ return expr;
+ else
+ return NULL_TREE;
+ }
+ case COMPONENT_REF:
+ {
+ tree base, orig_base = TREE_OPERAND (expr, 0);
+ tree component, orig_component = TREE_OPERAND (expr, 1);
+ base = get_non_ssa_expr (orig_base);
+ if (!base)
+ return NULL_TREE;
+ component = get_non_ssa_expr (orig_component);
+ if (!component)
+ return NULL_TREE;
+ /* If either BASE or COMPONENT is converted, build a new
+ component reference tree. */
+ if (base != orig_base || component != orig_component)
+ return build3 (COMPONENT_REF, TREE_TYPE (component),
+ base, component, NULL_TREE);
+ else
+ return expr;
+ }
+ case INDIRECT_REF:
+ {
+ tree orig_base = TREE_OPERAND (expr, 0);
+ tree base = get_non_ssa_expr (orig_base);
+ if (!base)
+ return NULL_TREE;
+ /* If BASE is converted, build a new indirect reference tree. */
+ if (base != orig_base)
+ return build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (base)), base);
+ else
+ return expr;
+ }
+ case ARRAY_REF:
+ {
+ tree array, orig_array = TREE_OPERAND (expr, 0);
+ tree index, orig_index = TREE_OPERAND (expr, 1);
+ array = get_non_ssa_expr (orig_array);
+ if (!array)
+ return NULL_TREE;
+ index = get_non_ssa_expr (orig_index);
+ if (!index)
+ return NULL_TREE;
+ /* If either ARRAY or INDEX is converted, build a new array
+ reference tree. */
+ if (array != orig_array || index != orig_index)
+ return build4 (ARRAY_REF, TREE_TYPE (expr), array, index,
+ TREE_OPERAND (expr, 2), TREE_OPERAND (expr, 3));
+ else
+ return expr;
+ }
+ case SSA_NAME:
+ {
+ tree vdecl = SSA_NAME_VAR (expr);
+ if (DECL_ARTIFICIAL (vdecl)
+ && !gimple_nop_p (SSA_NAME_DEF_STMT (expr)))
+ {
+ gimple def_stmt = SSA_NAME_DEF_STMT (expr);
+ if (is_gimple_assign (def_stmt)
+ && (get_gimple_rhs_class (gimple_assign_rhs_code (def_stmt))
+ == GIMPLE_SINGLE_RHS))
+ vdecl = gimple_assign_rhs1 (def_stmt);
+ }
+ return get_non_ssa_expr (vdecl);
+ }
+ case INTEGER_CST:
+ return expr;
+ default:
+ /* Return NULL_TREE for any other kind of tree nodes. */
+ return NULL_TREE;
+ }
+}
+
+/* Given the LHS and (real) RHS of a gimple assign statement, STMT, check if
+ they are the same. If so, print a warning message about self-assignment. */
+
+static void
+compare_and_warn (gimple stmt, tree lhs, tree rhs)
+{
+ if (operand_equal_p (lhs, rhs, OEP_PURE_SAME))
+ {
+ location_t location;
+ location = (gimple_has_location (stmt)
+ ? gimple_location (stmt)
+ : (DECL_P (lhs)
+ ? DECL_SOURCE_LOCATION (lhs)
+ : input_location));
+ /* If LHS contains any tree node not currently supported by
+ get_non_ssa_expr, simply emit a generic warning without
+ specifying LHS in the message. */
+ lhs = get_non_ssa_expr (lhs);
+ if (lhs)
+ warning (0, G_("%H%qE is assigned to itself"), &location, lhs);
+ else
+ warning (0, G_("%Hself-assignment detected"), &location);
+ }
+}
+
+/* Check and warn if STMT is a self-assign statement. */
+
+static void
+warn_self_assign (gimple stmt)
+{
+ tree rhs, lhs;
+
+ /* Check assigment statement. */
+ if (is_gimple_assign (stmt)
+ && (get_gimple_rhs_class (gimple_assign_rhs_code (stmt))
+ == GIMPLE_SINGLE_RHS))
+ {
+ rhs = get_real_ref_rhs (gimple_assign_rhs1 (stmt));
+ if (!rhs)
+ return;
+
+ lhs = gimple_assign_lhs (stmt);
+ if (TREE_CODE (lhs) == SSA_NAME)
+ {
+ lhs = SSA_NAME_VAR (lhs);
+ if (DECL_ARTIFICIAL (lhs))
+ return;
+ }
+
+ compare_and_warn (stmt, lhs, rhs);
+ }
+ /* Check overloaded operator '=' (if enabled). */
+ else if (check_operator_eq && is_gimple_call (stmt))
+ {
+ tree fdecl = gimple_call_fndecl (stmt);
+ if (fdecl && (DECL_NAME (fdecl) == maybe_get_identifier ("operator=")))
+ {
+ /* If 'operator=' takes reference operands, the arguments will be
+ ADDR_EXPR trees. In this case, just remove the address-taken
+ operator before we compare the lhs and rhs. */
+ lhs = gimple_call_arg (stmt, 0);
+ if (TREE_CODE (lhs) == ADDR_EXPR)
+ lhs = TREE_OPERAND (lhs, 0);
+ rhs = gimple_call_arg (stmt, 1);
+ if (TREE_CODE (rhs) == ADDR_EXPR)
+ rhs = TREE_OPERAND (rhs, 0);
+
+ compare_and_warn (stmt, lhs, rhs);
+ }
+ }
+}
+
+/* Entry point for the self-assignment detection pass. */
+
+static unsigned int
+execute_warn_self_assign (void)
+{
+ gimple_stmt_iterator gsi;
+ basic_block bb;
+
+ FOR_EACH_BB (bb)
+ {
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ warn_self_assign (gsi_stmt (gsi));
+ }
+
+ return 0;
+}
+
+/* Pass gate function. Currently always returns true. */
+
+static bool
+gate_warn_self_assign (void)
+{
+ return true;
+}
+
+static struct gimple_opt_pass pass_warn_self_assign =
+{
+ {
+ GIMPLE_PASS,
+ "warn_self_assign", /* name */
+ gate_warn_self_assign, /* gate */
+ execute_warn_self_assign, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ 0, /* tv_id */
+ PROP_ssa, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func /* todo_flags_finish */
+ }
+};
+
+/* The initialization routine exposed to and called by GCC. The spec of this
+ function is defined in gcc/gcc-plugin.h.
+
+ PLUGIN_NAME - name of the plugin (useful for error reporting)
+ ARGC - the size of the ARGV array
+ ARGV - an array of key-value argument pair
+
+ Returns 0 if initialization finishes successfully.
+
+ Note that this function needs to be named exactly "plugin_init". */
+
+int
+plugin_init (const char *plugin_name, struct plugin_gcc_version *version,
+ int argc, struct plugin_argument *argv)
+{
+ struct plugin_pass pass_info;
+ bool enabled = true;
+ int i;
+
+ if (!plugin_default_version_check (version, version))
+ return 1;
+
+ /* Self-assign detection should happen after SSA is constructed. */
+ pass_info.pass = &pass_warn_self_assign.pass;
+ pass_info.reference_pass_name = "ssa";
+ pass_info.ref_pass_instance_number = 1;
+ pass_info.pos_op = PASS_POS_INSERT_AFTER;
+
+ /* Process the plugin arguments. This plugin takes the following arguments:
+ check-operator-eq, no-check-operator-eq, enable, and disable.
+ By default, the analysis is enabled with 'operator=' checked. */
+ for (i = 0; i < argc; ++i)
+ {
+ if (!strcmp (argv[i].key, "check-operator-eq"))
+ {
+ if (argv[i].value)
+ warning (0, G_("option '-fplugin-arg-%s-check-operator-eq=%s'"
+ " ignored (superfluous '=%s')"),
+ plugin_name, argv[i].value, argv[i].value);
+ else
+ check_operator_eq = true;
+ }
+ else if (!strcmp (argv[i].key, "no-check-operator-eq"))
+ {
+ if (argv[i].value)
+ warning (0, G_("option '-fplugin-arg-%s-no-check-operator-eq=%s'"
+ " ignored (superfluous '=%s')"),
+ plugin_name, argv[i].value, argv[i].value);
+ else
+ check_operator_eq = false;
+ }
+ else if (!strcmp (argv[i].key, "enable"))
+ {
+ if (argv[i].value)
+ warning (0, G_("option '-fplugin-arg-%s-enable=%s' ignored"
+ " (superfluous '=%s')"),
+ plugin_name, argv[i].value, argv[i].value);
+ else
+ enabled = true;
+ }
+ else if (!strcmp (argv[i].key, "disable"))
+ {
+ if (argv[i].value)
+ warning (0, G_("option '-fplugin-arg-%s-disable=%s' ignored"
+ " (superfluous '=%s')"),
+ plugin_name, argv[i].value, argv[i].value);
+ else
+ enabled = false;
+ }
+ else
+ warning (0, G_("plugin %qs: unrecognized argument %qs ignored"),
+ plugin_name, argv[i].key);
+ }
+
+ /* Register this new pass with GCC if the analysis is enabled. */
+ if (enabled)
+ register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+ &pass_info);
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
new file mode 100644
index 0000000..93c0c5c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -0,0 +1,65 @@
+# Copyright (C) 2009 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 3 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 GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Test the functionality of the GCC plugin support
+
+load_lib target-supports.exp
+load_lib gcc-dg.exp
+
+global TESTING_IN_BUILD_TREE
+global ENABLE_PLUGIN
+
+# The plugin testcases currently only work when the build tree is available.
+# Also check whether the host supports plugins.
+if { ![info exists TESTING_IN_BUILD_TREE] || ![info exists ENABLE_PLUGIN] } {
+ return
+}
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+ set DEFAULT_CFLAGS " -ansi -pedantic-errors"
+}
+
+# The procedures in plugin-support.exp need these parameters.
+set default_flags $DEFAULT_CFLAGS
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+# Load support procs.
+load_lib plugin-support.exp
+
+# Specify the plugin source file and the associated test files in a list.
+# plugin_test_list={ {plugin1 test1 test2 ...} {plugin2 test1 ...} ... }
+set plugin_test_list [list \
+ { selfassign.c self-assign-test-1.c self-assign-test-2.c } ]
+
+foreach plugin_test $plugin_test_list {
+ # Replace each source file with its full-path name
+ for {set i 0} {$i < [llength $plugin_test]} {incr i} {
+ set basename [lindex $plugin_test $i]
+ set plugin_test [lreplace $plugin_test $i $i $srcdir/$subdir/$basename]
+ }
+ set plugin_src [lindex $plugin_test 0]
+ # If we're only testing specific files and this isn't one of them, skip it.
+ if ![runtest_file_p $runtests $plugin_src] then {
+ continue
+ }
+ set plugin_input_tests [lreplace $plugin_test 0 0]
+ plugin-test-execute $plugin_src $plugin_input_tests
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/self-assign-test-1.c b/gcc/testsuite/gcc.dg/plugin/self-assign-test-1.c
new file mode 100644
index 0000000..f6dc524
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/self-assign-test-1.c
@@ -0,0 +1,23 @@
+/* Test the self-assignemnt detection plugin. */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+struct Bar {
+ int b_;
+ int c_;
+};
+
+int g;
+
+int main()
+{
+ struct Bar *bar;
+ int x = x; /* { dg-warning "assigned to itself" } */
+ static int y;
+ struct Bar b_array[5];
+
+ b_array[x+g].b_ = b_array[x+g].b_; /* { dg-warning "self-assignment detected" } */
+ g = g; /* { dg-warning "assigned to itself" } */
+ y = y; /* { dg-warning "assigned to itself" } */
+ bar->b_ = bar->b_; /* { dg-warning "assigned to itself" } */
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/self-assign-test-2.c b/gcc/testsuite/gcc.dg/plugin/self-assign-test-2.c
new file mode 100644
index 0000000..2ede371
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/self-assign-test-2.c
@@ -0,0 +1,23 @@
+/* Test the self-assignemnt detection plugin with the 'disable' argument. */
+/* { dg-do compile } */
+/* { dg-options "-O -fplugin-arg-selfassign-disable" } */
+
+struct Bar {
+ int b_;
+ int c_;
+};
+
+int g;
+
+int main()
+{
+ struct Bar *bar;
+ int x = x; /* { dg-bogus "assigned to itself" } */
+ static int y;
+ struct Bar b_array[5];
+
+ b_array[x+g].b_ = b_array[x+g].b_; /* { dg-bogus "self-assignment detected" } */
+ g = g; /* { dg-bogus "assigned to itself" } */
+ y = y; /* { dg-bogus "assigned to itself" } */
+ bar->b_ = bar->b_; /* { dg-bogus "assigned to itself" } */
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/selfassign.c b/gcc/testsuite/gcc.dg/plugin/selfassign.c
new file mode 100644
index 0000000..6fbce83
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/selfassign.c
@@ -0,0 +1,365 @@
+/* This plugin contains an analysis pass that detects and warns about
+ self-assignment statements. */
+/* { dg-options "-O" } */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "toplev.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "gcc-plugin.h"
+
+
+/* Indicate whether to check overloaded operator '=', which is performed by
+ default. To disable it, use -fplugin-arg-NAME-no-check-operator-eq. */
+bool check_operator_eq = true;
+
+/* Given a rhs EXPR of a gimple assign statement, if it is
+ - SSA_NAME : returns its var decl, or, if it is a temp variable,
+ returns the rhs of its SSA def statement.
+ - VAR_DECL, PARM_DECL, FIELD_DECL, or a reference expression :
+ returns EXPR itself.
+ - any other expression : returns NULL_TREE. */
+
+static tree
+get_real_ref_rhs (tree expr)
+{
+ switch (TREE_CODE (expr))
+ {
+ case SSA_NAME:
+ {
+ /* Given a self-assign statement, say foo.x = foo.x,
+ the IR (after SSA) looks like:
+
+ D.1797_14 = foo.x;
+ foo.x ={v} D.1797_14;
+
+ So if the rhs EXPR is an SSA_NAME of a temp variable,
+ e.g. D.1797_14, we need to grab the rhs of its SSA def
+ statement (i.e. foo.x). */
+ tree vdecl = SSA_NAME_VAR (expr);
+ if (DECL_ARTIFICIAL (vdecl)
+ && !gimple_nop_p (SSA_NAME_DEF_STMT (expr)))
+ {
+ gimple def_stmt = SSA_NAME_DEF_STMT (expr);
+ /* We are only interested in an assignment with a single
+ rhs operand because if it is not, the original assignment
+ will not possibly be a self-assignment. */
+ if (is_gimple_assign (def_stmt)
+ && (get_gimple_rhs_class (gimple_assign_rhs_code (def_stmt))
+ == GIMPLE_SINGLE_RHS))
+ return get_real_ref_rhs (gimple_assign_rhs1 (def_stmt));
+ else
+ return NULL_TREE;
+ }
+ else
+ return vdecl;
+ }
+ case VAR_DECL:
+ case PARM_DECL:
+ case FIELD_DECL:
+ case COMPONENT_REF:
+ case INDIRECT_REF:
+ case ARRAY_REF:
+ return expr;
+ default:
+ return NULL_TREE;
+ }
+}
+
+/* Given an expression tree, EXPR, that may contains SSA names, returns an
+ equivalent tree with the SSA names converted to var/parm/field decls
+ so that it can be used with '%E' format modifier when emitting warning
+ messages.
+
+ This function currently only supports VAR/PARM/FIELD_DECL, reference
+ expressions (COMPONENT_REF, INDIRECT_REF, ARRAY_REF), integer constant,
+ and SSA_NAME. If EXPR contains any other tree nodes (e.g. an arithmetic
+ expression appears in array index), NULL_TREE is returned. */
+
+static tree
+get_non_ssa_expr (tree expr)
+{
+ switch (TREE_CODE (expr))
+ {
+ case VAR_DECL:
+ case PARM_DECL:
+ case FIELD_DECL:
+ {
+ if (DECL_NAME (expr))
+ return expr;
+ else
+ return NULL_TREE;
+ }
+ case COMPONENT_REF:
+ {
+ tree base, orig_base = TREE_OPERAND (expr, 0);
+ tree component, orig_component = TREE_OPERAND (expr, 1);
+ base = get_non_ssa_expr (orig_base);
+ if (!base)
+ return NULL_TREE;
+ component = get_non_ssa_expr (orig_component);
+ if (!component)
+ return NULL_TREE;
+ /* If either BASE or COMPONENT is converted, build a new
+ component reference tree. */
+ if (base != orig_base || component != orig_component)
+ return build3 (COMPONENT_REF, TREE_TYPE (component),
+ base, component, NULL_TREE);
+ else
+ return expr;
+ }
+ case INDIRECT_REF:
+ {
+ tree orig_base = TREE_OPERAND (expr, 0);
+ tree base = get_non_ssa_expr (orig_base);
+ if (!base)
+ return NULL_TREE;
+ /* If BASE is converted, build a new indirect reference tree. */
+ if (base != orig_base)
+ return build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (base)), base);
+ else
+ return expr;
+ }
+ case ARRAY_REF:
+ {
+ tree array, orig_array = TREE_OPERAND (expr, 0);
+ tree index, orig_index = TREE_OPERAND (expr, 1);
+ array = get_non_ssa_expr (orig_array);
+ if (!array)
+ return NULL_TREE;
+ index = get_non_ssa_expr (orig_index);
+ if (!index)
+ return NULL_TREE;
+ /* If either ARRAY or INDEX is converted, build a new array
+ reference tree. */
+ if (array != orig_array || index != orig_index)
+ return build4 (ARRAY_REF, TREE_TYPE (expr), array, index,
+ TREE_OPERAND (expr, 2), TREE_OPERAND (expr, 3));
+ else
+ return expr;
+ }
+ case SSA_NAME:
+ {
+ tree vdecl = SSA_NAME_VAR (expr);
+ if (DECL_ARTIFICIAL (vdecl)
+ && !gimple_nop_p (SSA_NAME_DEF_STMT (expr)))
+ {
+ gimple def_stmt = SSA_NAME_DEF_STMT (expr);
+ if (is_gimple_assign (def_stmt)
+ && (get_gimple_rhs_class (gimple_assign_rhs_code (def_stmt))
+ == GIMPLE_SINGLE_RHS))
+ vdecl = gimple_assign_rhs1 (def_stmt);
+ }
+ return get_non_ssa_expr (vdecl);
+ }
+ case INTEGER_CST:
+ return expr;
+ default:
+ /* Return NULL_TREE for any other kind of tree nodes. */
+ return NULL_TREE;
+ }
+}
+
+/* Given the LHS and (real) RHS of a gimple assign statement, STMT, check if
+ they are the same. If so, print a warning message about self-assignment. */
+
+static void
+compare_and_warn (gimple stmt, tree lhs, tree rhs)
+{
+ if (operand_equal_p (lhs, rhs, OEP_PURE_SAME))
+ {
+ location_t location;
+ location = (gimple_has_location (stmt)
+ ? gimple_location (stmt)
+ : (DECL_P (lhs)
+ ? DECL_SOURCE_LOCATION (lhs)
+ : input_location));
+ /* If LHS contains any tree node not currently supported by
+ get_non_ssa_expr, simply emit a generic warning without
+ specifying LHS in the message. */
+ lhs = get_non_ssa_expr (lhs);
+ if (lhs)
+ warning (0, G_("%H%qE is assigned to itself"), &location, lhs);
+ else
+ warning (0, G_("%Hself-assignment detected"), &location);
+ }
+}
+
+/* Check and warn if STMT is a self-assign statement. */
+
+static void
+warn_self_assign (gimple stmt)
+{
+ tree rhs, lhs;
+
+ /* Check assigment statement. */
+ if (is_gimple_assign (stmt)
+ && (get_gimple_rhs_class (gimple_assign_rhs_code (stmt))
+ == GIMPLE_SINGLE_RHS))
+ {
+ rhs = get_real_ref_rhs (gimple_assign_rhs1 (stmt));
+ if (!rhs)
+ return;
+
+ lhs = gimple_assign_lhs (stmt);
+ if (TREE_CODE (lhs) == SSA_NAME)
+ {
+ lhs = SSA_NAME_VAR (lhs);
+ if (DECL_ARTIFICIAL (lhs))
+ return;
+ }
+
+ compare_and_warn (stmt, lhs, rhs);
+ }
+ /* Check overloaded operator '=' (if enabled). */
+ else if (check_operator_eq && is_gimple_call (stmt))
+ {
+ tree fdecl = gimple_call_fndecl (stmt);
+ if (fdecl && (DECL_NAME (fdecl) == maybe_get_identifier ("operator=")))
+ {
+ /* If 'operator=' takes reference operands, the arguments will be
+ ADDR_EXPR trees. In this case, just remove the address-taken
+ operator before we compare the lhs and rhs. */
+ lhs = gimple_call_arg (stmt, 0);
+ if (TREE_CODE (lhs) == ADDR_EXPR)
+ lhs = TREE_OPERAND (lhs, 0);
+ rhs = gimple_call_arg (stmt, 1);
+ if (TREE_CODE (rhs) == ADDR_EXPR)
+ rhs = TREE_OPERAND (rhs, 0);
+
+ compare_and_warn (stmt, lhs, rhs);
+ }
+ }
+}
+
+/* Entry point for the self-assignment detection pass. */
+
+static unsigned int
+execute_warn_self_assign (void)
+{
+ gimple_stmt_iterator gsi;
+ basic_block bb;
+
+ FOR_EACH_BB (bb)
+ {
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ warn_self_assign (gsi_stmt (gsi));
+ }
+
+ return 0;
+}
+
+/* Pass gate function. Currently always returns true. */
+
+static bool
+gate_warn_self_assign (void)
+{
+ return true;
+}
+
+static struct gimple_opt_pass pass_warn_self_assign =
+{
+ {
+ GIMPLE_PASS,
+ "warn_self_assign", /* name */
+ gate_warn_self_assign, /* gate */
+ execute_warn_self_assign, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ 0, /* tv_id */
+ PROP_ssa, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func /* todo_flags_finish */
+ }
+};
+
+/* The initialization routine exposed to and called by GCC. The spec of this
+ function is defined in gcc/gcc-plugin.h.
+
+ PLUGIN_NAME - name of the plugin (useful for error reporting)
+ ARGC - the size of the ARGV array
+ ARGV - an array of key-value argument pair
+
+ Returns 0 if initialization finishes successfully.
+
+ Note that this function needs to be named exactly "plugin_init". */
+
+int
+plugin_init (const char *plugin_name, struct plugin_gcc_version *version,
+ int argc, struct plugin_argument *argv)
+{
+ struct plugin_pass pass_info;
+ bool enabled = true;
+ int i;
+
+ if (!plugin_default_version_check (version, version))
+ return 1;
+
+ /* Self-assign detection should happen after SSA is constructed. */
+ pass_info.pass = &pass_warn_self_assign.pass;
+ pass_info.reference_pass_name = "ssa";
+ pass_info.ref_pass_instance_number = 1;
+ pass_info.pos_op = PASS_POS_INSERT_AFTER;
+
+ /* Process the plugin arguments. This plugin takes the following arguments:
+ check-operator-eq, no-check-operator-eq, enable, and disable.
+ By default, the analysis is enabled with 'operator=' checked. */
+ for (i = 0; i < argc; ++i)
+ {
+ if (!strcmp (argv[i].key, "check-operator-eq"))
+ {
+ if (argv[i].value)
+ warning (0, G_("option '-fplugin-arg-%s-check-operator-eq=%s'"
+ " ignored (superfluous '=%s')"),
+ plugin_name, argv[i].value, argv[i].value);
+ else
+ check_operator_eq = true;
+ }
+ else if (!strcmp (argv[i].key, "no-check-operator-eq"))
+ {
+ if (argv[i].value)
+ warning (0, G_("option '-fplugin-arg-%s-no-check-operator-eq=%s'"
+ " ignored (superfluous '=%s')"),
+ plugin_name, argv[i].value, argv[i].value);
+ else
+ check_operator_eq = false;
+ }
+ else if (!strcmp (argv[i].key, "enable"))
+ {
+ if (argv[i].value)
+ warning (0, G_("option '-fplugin-arg-%s-enable=%s' ignored"
+ " (superfluous '=%s')"),
+ plugin_name, argv[i].value, argv[i].value);
+ else
+ enabled = true;
+ }
+ else if (!strcmp (argv[i].key, "disable"))
+ {
+ if (argv[i].value)
+ warning (0, G_("option '-fplugin-arg-%s-disable=%s' ignored"
+ " (superfluous '=%s')"),
+ plugin_name, argv[i].value, argv[i].value);
+ else
+ enabled = false;
+ }
+ else
+ warning (0, G_("plugin %qs: unrecognized argument %qs ignored"),
+ plugin_name, argv[i].key);
+ }
+
+ /* Register this new pass with GCC if the analysis is enabled. */
+ if (enabled)
+ register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+ &pass_info);
+
+ return 0;
+}
diff --git a/gcc/testsuite/lib/plugin-support.exp b/gcc/testsuite/lib/plugin-support.exp
new file mode 100644
index 0000000..f9017bb
--- /dev/null
+++ b/gcc/testsuite/lib/plugin-support.exp
@@ -0,0 +1,102 @@
+# Copyright (C) 2009 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 3 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 GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+#
+
+# This file contains the support procedures for testing the plugin mechanism.
+
+load_lib dg.exp
+load_lib gcc.exp
+
+#
+# plugin-get-options -- process test directives
+#
+# SRC is the full pathname of the plugin source file.
+#
+proc plugin-get-options { src } {
+ # dg-options sets a variable called dg-extra-tool-flags.
+ set dg-extra-tool-flags ""
+
+ # dg-require-* sets dg-do-what.
+ upvar dg-do-what dg-do-what
+
+ set tmp [dg-get-options $src]
+ foreach op $tmp {
+ set cmd [lindex $op 0]
+ if { ![string compare "dg-options" $cmd] } {
+ set status [catch "$op" errmsg]
+ if { $status != 0 } {
+ perror "src: $errmsg for \"$op\"\n"
+ unresolved "$src: $errmsg for \"$op\""
+ return
+ }
+ } else {
+ # Ignore unrecognized dg- commands, but warn about them.
+ warning "plugin.exp does not support $cmd"
+ }
+ }
+
+ # Return flags to use for compiling the plugin source file
+ return ${dg-extra-tool-flags}
+}
+
+#
+# plugin-test-execute -- build the plugin first and then compile the
+# test files with the plugin.
+#
+# PLUGIN_SRC is the full pathname of the plugin source file.
+# PLUGIN_TESTS is a list of input test source files.
+#
+proc plugin-test-execute { plugin_src plugin_tests } {
+ global srcdir objdir
+ global verbose
+ global GMPINC
+
+ set basename [file tail $plugin_src]
+ set base [file rootname $basename]
+ set plugin_lib $base.so
+
+ verbose "Test the plugin $basename" 1
+
+ # Build the plugin itself
+ set extra_flags [plugin-get-options $plugin_src]
+
+ # Note that the plugin test support currently only works when the GCC
+ # build tree is available. (We make sure that is the case in plugin.exp.)
+ # Once we have figured out how/where to package/install GCC header files
+ # for general plugin support, we should modify the following include paths
+ # accordingly.
+ set gcc_srcdir "$srcdir/../.."
+ set gcc_objdir "$objdir/../../.."
+ set includes "-I. -I${srcdir} -I${gcc_srcdir}/gcc -I${gcc_objdir}/gcc \
+ -I${gcc_srcdir}/include -I${gcc_srcdir}/libcpp/include \
+ -I$GMPINC"
+
+ set optstr "$includes $extra_flags -DIN_GCC -fPIC -shared"
+
+ set status [target_compile "$optstr $plugin_src" "$plugin_lib" executable ""]
+ if { "$status" != "" } {
+ unresolved "$basename compilation, $optstr"
+ return
+ }
+
+ # Compile the input source files with the plugin
+ global default_flags
+ set plugin_enabling_flags "-fplugin=./$plugin_lib"
+ dg-runtest $plugin_tests $plugin_enabling_flags $default_flags
+
+ # Clean up
+ remote_file build delete $plugin_lib
+}