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:
parent
b050b2dec6
commit
8ba50c2c9e
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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/*]
|
||||
|
53
gcc/testsuite/g++.dg/plugin/dumb-plugin-test-1.C
Normal file
53
gcc/testsuite/g++.dg/plugin/dumb-plugin-test-1.C
Normal 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 }
|
136
gcc/testsuite/g++.dg/plugin/dumb_plugin.c
Normal file
136
gcc/testsuite/g++.dg/plugin/dumb_plugin.c
Normal 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;
|
||||
}
|
66
gcc/testsuite/g++.dg/plugin/plugin.exp
Normal file
66
gcc/testsuite/g++.dg/plugin/plugin.exp
Normal 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
|
||||
}
|
50
gcc/testsuite/g++.dg/plugin/self-assign-test-1.C
Normal file
50
gcc/testsuite/g++.dg/plugin/self-assign-test-1.C
Normal 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;
|
||||
}
|
50
gcc/testsuite/g++.dg/plugin/self-assign-test-2.C
Normal file
50
gcc/testsuite/g++.dg/plugin/self-assign-test-2.C
Normal 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;
|
||||
}
|
50
gcc/testsuite/g++.dg/plugin/self-assign-test-3.C
Normal file
50
gcc/testsuite/g++.dg/plugin/self-assign-test-3.C
Normal 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;
|
||||
}
|
365
gcc/testsuite/g++.dg/plugin/selfassign.c
Normal file
365
gcc/testsuite/g++.dg/plugin/selfassign.c
Normal 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;
|
||||
}
|
65
gcc/testsuite/gcc.dg/plugin/plugin.exp
Normal file
65
gcc/testsuite/gcc.dg/plugin/plugin.exp
Normal 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
|
||||
}
|
23
gcc/testsuite/gcc.dg/plugin/self-assign-test-1.c
Normal file
23
gcc/testsuite/gcc.dg/plugin/self-assign-test-1.c
Normal 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" } */
|
||||
}
|
23
gcc/testsuite/gcc.dg/plugin/self-assign-test-2.c
Normal file
23
gcc/testsuite/gcc.dg/plugin/self-assign-test-2.c
Normal 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" } */
|
||||
}
|
365
gcc/testsuite/gcc.dg/plugin/selfassign.c
Normal file
365
gcc/testsuite/gcc.dg/plugin/selfassign.c
Normal 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;
|
||||
}
|
102
gcc/testsuite/lib/plugin-support.exp
Normal file
102
gcc/testsuite/lib/plugin-support.exp
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user