(root)/
gcc-13.2.0/
gcc/
testsuite/
g++.dg/
plugin/
selfassign.c
       1  /* This plugin contains an analysis pass that detects and warns about
       2     self-assignment statements.  */
       3  /* { dg-options "-O" } */
       4  
       5  #include "gcc-plugin.h"
       6  #include "config.h"
       7  #include "system.h"
       8  #include "coretypes.h"
       9  #include "tm.h"
      10  #include "tree.h"
      11  #include "stringpool.h"
      12  #include "toplev.h"
      13  #include "basic-block.h"
      14  #include "hash-table.h"
      15  #include "vec.h"
      16  #include "ggc.h"
      17  #include "basic-block.h"
      18  #include "tree-ssa-alias.h"
      19  #include "internal-fn.h"
      20  #include "gimple.h"
      21  #include "gimple-iterator.h"
      22  #include "gimple-fold.h"
      23  #include "tree-eh.h"
      24  #include "gimple-expr.h"
      25  #include "is-a.h"
      26  #include "tree.h"
      27  #include "tree-pass.h"
      28  #include "intl.h"
      29  #include "plugin-version.h"
      30  #include "diagnostic.h"
      31  #include "context.h"
      32  
      33  int plugin_is_GPL_compatible;
      34  
      35  /* Indicate whether to check overloaded operator '=', which is performed by
      36     default. To disable it, use -fplugin-arg-NAME-no-check-operator-eq.  */
      37  bool check_operator_eq = true;
      38  
      39  /* Given a rhs EXPR of a gimple assign statement, if it is
      40     - SSA_NAME : returns its var decl, or, if it is a temp variable,
      41                  returns the rhs of its SSA def statement.
      42     - VAR_DECL, PARM_DECL, FIELD_DECL, or a reference expression :
      43                  returns EXPR itself.
      44     - any other expression : returns NULL_TREE.  */
      45  
      46  static tree
      47  get_real_ref_rhs (tree expr)
      48  {
      49    switch (TREE_CODE (expr))
      50      {
      51        case SSA_NAME:
      52          {
      53            /* Given a self-assign statement, say foo.x = foo.x,
      54               the IR (after SSA) looks like:
      55  
      56               D.1797_14 = foo.x;
      57               foo.x ={v} D.1797_14;
      58  
      59               So if the rhs EXPR is an SSA_NAME of a temp variable,
      60               e.g. D.1797_14, we need to grab the rhs of its SSA def
      61               statement (i.e. foo.x).  */
      62            tree vdecl = SSA_NAME_VAR (expr);
      63            if ((!vdecl || DECL_ARTIFICIAL (vdecl))
      64                && !gimple_nop_p (SSA_NAME_DEF_STMT (expr)))
      65              {
      66  	      gimple *def_stmt = SSA_NAME_DEF_STMT (expr);
      67                /* We are only interested in an assignment with a single
      68                   rhs operand because if it is not, the original assignment
      69                   will not possibly be a self-assignment.  */
      70                if (gimple_assign_single_p (def_stmt))
      71                  return get_real_ref_rhs (gimple_assign_rhs1 (def_stmt));
      72                else
      73                  return NULL_TREE;
      74              }
      75            else
      76              return vdecl;
      77          }
      78        case VAR_DECL:
      79        case PARM_DECL:
      80        case FIELD_DECL:
      81        case COMPONENT_REF:
      82        case MEM_REF:
      83        case ARRAY_REF:
      84          return expr;
      85        default:
      86          return NULL_TREE;
      87      }
      88  }
      89  
      90  /* Given an expression tree, EXPR, that may contains SSA names, returns an
      91     equivalent tree with the SSA names converted to var/parm/field decls
      92     so that it can be used with '%E' format modifier when emitting warning
      93     messages.
      94  
      95     This function currently only supports VAR/PARM/FIELD_DECL, reference
      96     expressions (COMPONENT_REF, INDIRECT_REF, ARRAY_REF), integer constant,
      97     and SSA_NAME. If EXPR contains any other tree nodes (e.g. an arithmetic
      98     expression appears in array index), NULL_TREE is returned.  */
      99  
     100  static tree
     101  get_non_ssa_expr (tree expr)
     102  {
     103    if (!expr)
     104      return NULL_TREE;
     105    switch (TREE_CODE (expr))
     106      {
     107        case VAR_DECL:
     108        case PARM_DECL:
     109        case FIELD_DECL:
     110          {
     111            if (DECL_NAME (expr))
     112              return expr;
     113            else
     114              return NULL_TREE;
     115          }
     116        case COMPONENT_REF:
     117          {
     118            tree base, orig_base = TREE_OPERAND (expr, 0);
     119            tree component, orig_component = TREE_OPERAND (expr, 1);
     120            base = get_non_ssa_expr (orig_base);
     121            if (!base)
     122              return NULL_TREE;
     123            component = get_non_ssa_expr (orig_component);
     124            if (!component)
     125              return NULL_TREE;
     126            /* If either BASE or COMPONENT is converted, build a new
     127               component reference tree.  */
     128            if (base != orig_base || component != orig_component)
     129              return build3 (COMPONENT_REF, TREE_TYPE (component),
     130                             base, component, NULL_TREE);
     131            else
     132              return expr;
     133          }
     134        case MEM_REF:
     135          {
     136            tree orig_base = TREE_OPERAND (expr, 0);
     137  	  if (TREE_CODE (orig_base) == SSA_NAME)
     138  	    {
     139  	      tree base = get_non_ssa_expr (orig_base);
     140  	      if (!base)
     141  		return NULL_TREE;
     142  	      return fold_build2 (MEM_REF, TREE_TYPE (expr),
     143  				  base, TREE_OPERAND (expr, 1));
     144  	    }
     145  	  return expr;
     146          }
     147        case ARRAY_REF:
     148          {
     149            tree array, orig_array = TREE_OPERAND (expr, 0);
     150            tree index, orig_index = TREE_OPERAND (expr, 1);
     151            array = get_non_ssa_expr (orig_array);
     152            if (!array)
     153              return NULL_TREE;
     154            index = get_non_ssa_expr (orig_index);
     155            if (!index)
     156              return NULL_TREE;
     157            /* If either ARRAY or INDEX is converted, build a new array
     158               reference tree.  */
     159            if (array != orig_array || index != orig_index)
     160              return build4 (ARRAY_REF, TREE_TYPE (expr), array, index,
     161                             TREE_OPERAND (expr, 2), TREE_OPERAND (expr, 3));
     162            else
     163              return expr;
     164          }
     165        case SSA_NAME:
     166          {
     167            tree vdecl = SSA_NAME_VAR (expr);
     168            if ((!vdecl || DECL_ARTIFICIAL (vdecl))
     169                && !gimple_nop_p (SSA_NAME_DEF_STMT (expr)))
     170              {
     171  	      gimple *def_stmt = SSA_NAME_DEF_STMT (expr);
     172                if (gimple_assign_single_p (def_stmt))
     173                  vdecl = gimple_assign_rhs1 (def_stmt);
     174              }
     175            return get_non_ssa_expr (vdecl);
     176          }
     177        case INTEGER_CST:
     178          return expr;
     179        default:
     180          /* Return NULL_TREE for any other kind of tree nodes.  */
     181          return NULL_TREE;
     182      }
     183  }
     184  
     185  /* Given the LHS and (real) RHS of a gimple assign statement, STMT, check if
     186     they are the same. If so, print a warning message about self-assignment.  */
     187  
     188  static void
     189  compare_and_warn (gimple *stmt, tree lhs, tree rhs)
     190  {
     191    if (operand_equal_p (lhs, rhs, OEP_PURE_SAME))
     192      {
     193        location_t location;
     194        location = (gimple_has_location (stmt)
     195                    ? gimple_location (stmt)
     196                    : (DECL_P (lhs)
     197                       ? DECL_SOURCE_LOCATION (lhs)
     198                       : input_location));
     199        /* If LHS contains any tree node not currently supported by
     200           get_non_ssa_expr, simply emit a generic warning without
     201           specifying LHS in the message.  */
     202        lhs = get_non_ssa_expr (lhs);
     203        if (lhs)
     204          warning_at (location, 0, G_("%qE is assigned to itself"), lhs);
     205        else
     206          warning_at (location, 0, G_("self-assignment detected"));
     207      }
     208  }
     209  
     210  /* Check and warn if STMT is a self-assign statement.  */
     211  
     212  static void
     213  warn_self_assign (gimple *stmt)
     214  {
     215    tree rhs, lhs;
     216  
     217    /* Check assigment statement.  */
     218    if (gimple_assign_single_p (stmt))
     219      {
     220        rhs = get_real_ref_rhs (gimple_assign_rhs1 (stmt));
     221        if (!rhs)
     222          return;
     223  
     224        lhs = gimple_assign_lhs (stmt);
     225        if (TREE_CODE (lhs) == SSA_NAME)
     226          {
     227            lhs = SSA_NAME_VAR (lhs);
     228            if (!lhs || DECL_ARTIFICIAL (lhs))
     229              return;
     230          }
     231  
     232        compare_and_warn (stmt, lhs, rhs);
     233      }
     234    /* Check overloaded operator '=' (if enabled).  */
     235    else if (check_operator_eq && is_gimple_call (stmt))
     236      {
     237        tree fdecl = gimple_call_fndecl (stmt);
     238        if (fdecl && (DECL_NAME (fdecl) == maybe_get_identifier ("operator=")))
     239          {
     240            /* If 'operator=' takes reference operands, the arguments will be 
     241               ADDR_EXPR trees. In this case, just remove the address-taken
     242               operator before we compare the lhs and rhs.  */
     243            lhs = gimple_call_arg (stmt, 0);
     244            if (TREE_CODE (lhs) == ADDR_EXPR)
     245              lhs = TREE_OPERAND (lhs, 0);
     246            rhs = gimple_call_arg (stmt, 1);
     247            if (TREE_CODE (rhs) == ADDR_EXPR)
     248              rhs = TREE_OPERAND (rhs, 0);
     249  
     250            compare_and_warn (stmt, lhs, rhs);
     251          }
     252      }
     253  }
     254  
     255  namespace {
     256  
     257  const pass_data pass_data_warn_self_assign =
     258  {
     259    GIMPLE_PASS, /* type */
     260    "warn_self_assign", /* name */
     261    OPTGROUP_NONE, /* optinfo_flags */
     262    TV_NONE, /* tv_id */
     263    PROP_ssa, /* properties_required */
     264    0, /* properties_provided */
     265    0, /* properties_destroyed */
     266    0, /* todo_flags_start */
     267    0, /* todo_flags_finish */
     268  };
     269  
     270  class pass_warn_self_assign : public gimple_opt_pass
     271  {
     272  public:
     273    pass_warn_self_assign(gcc::context *ctxt)
     274      : gimple_opt_pass(pass_data_warn_self_assign, ctxt)
     275    {}
     276  
     277    /* opt_pass methods: */
     278    bool gate (function *) { return true; }
     279    virtual unsigned int execute (function *);
     280  
     281  }; // class pass_warn_self_assign
     282  
     283  unsigned int
     284  pass_warn_self_assign::execute (function *fun)
     285  {
     286    gimple_stmt_iterator gsi;
     287    basic_block bb;
     288  
     289    FOR_EACH_BB_FN (bb, fun)
     290      {
     291        for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
     292          warn_self_assign (gsi_stmt (gsi));
     293      }
     294  
     295    return 0;
     296  }
     297  
     298  } // anon namespace
     299  
     300  static gimple_opt_pass *
     301  make_pass_warn_self_assign (gcc::context *ctxt)
     302  {
     303    return new pass_warn_self_assign (ctxt);
     304  }
     305  
     306  /* The initialization routine exposed to and called by GCC. The spec of this
     307     function is defined in gcc/gcc-plugin.h.
     308  
     309     PLUGIN_NAME - name of the plugin (useful for error reporting)
     310     ARGC        - the size of the ARGV array
     311     ARGV        - an array of key-value argument pair
     312  
     313     Returns 0 if initialization finishes successfully.
     314  
     315     Note that this function needs to be named exactly "plugin_init".  */
     316  
     317  int
     318  plugin_init (struct plugin_name_args *plugin_info,
     319               struct plugin_gcc_version *version)
     320  {
     321    struct register_pass_info pass_info;
     322    const char *plugin_name = plugin_info->base_name;
     323    int argc = plugin_info->argc;
     324    struct plugin_argument *argv = plugin_info->argv;
     325    bool enabled = true;
     326    int i;
     327  
     328    if (!plugin_default_version_check (version, &gcc_version))
     329      return 1;
     330  
     331    /* Self-assign detection should happen after SSA is constructed.  */
     332    pass_info.pass = make_pass_warn_self_assign (g);
     333    pass_info.reference_pass_name = "ssa";
     334    pass_info.ref_pass_instance_number = 1;
     335    pass_info.pos_op = PASS_POS_INSERT_AFTER;
     336  
     337    /* Process the plugin arguments. This plugin takes the following arguments:
     338       check-operator-eq, no-check-operator-eq, enable, and disable.
     339       By default, the analysis is enabled with 'operator=' checked.  */
     340    for (i = 0; i < argc; ++i)
     341      {
     342        if (!strcmp (argv[i].key, "check-operator-eq"))
     343          {
     344            if (argv[i].value)
     345              warning (0, G_("option '-fplugin-arg-%s-check-operator-eq=%s'"
     346                             " ignored (superfluous '=%s')"),
     347                       plugin_name, argv[i].value, argv[i].value);
     348            else
     349              check_operator_eq = true;
     350          }
     351        else if (!strcmp (argv[i].key, "no-check-operator-eq"))
     352          {
     353            if (argv[i].value)
     354              warning (0, G_("option '-fplugin-arg-%s-no-check-operator-eq=%s'"
     355                             " ignored (superfluous '=%s')"),
     356                       plugin_name, argv[i].value, argv[i].value);
     357            else
     358              check_operator_eq = false;
     359          }
     360        else if (!strcmp (argv[i].key, "enable"))
     361          {
     362            if (argv[i].value)
     363              warning (0, G_("option '-fplugin-arg-%s-enable=%s' ignored"
     364                             " (superfluous '=%s')"),
     365                       plugin_name, argv[i].value, argv[i].value);
     366            else
     367              enabled = true;
     368          }
     369        else if (!strcmp (argv[i].key, "disable"))
     370          {
     371            if (argv[i].value)
     372              warning (0, G_("option '-fplugin-arg-%s-disable=%s' ignored"
     373                             " (superfluous '=%s')"),
     374                       plugin_name, argv[i].value, argv[i].value);
     375            else
     376              enabled = false;
     377          }
     378        else
     379          warning (0, G_("plugin %qs: unrecognized argument %qs ignored"),
     380                   plugin_name, argv[i].key);
     381      }
     382  
     383    /* Register this new pass with GCC if the analysis is enabled.  */
     384    if (enabled)
     385      register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
     386                         &pass_info);
     387  
     388    return 0;
     389  }