plugin-support.exp: New file containing support procs for plugin testcases.

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.

From-SVN: r147185
This commit is contained in:
Le-Chun Wu 2009-05-06 16:22:19 +00:00 committed by Diego Novillo
parent b050b2dec6
commit 8ba50c2c9e
16 changed files with 1369 additions and 1 deletions

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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/*]

View File

@ -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 }

View File

@ -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;
}

View File

@ -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
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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
}

View File

@ -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" } */
}

View File

@ -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" } */
}

View File

@ -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;
}

View File

@ -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
}