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-fold.h" 21#include "tree-eh.h" 22#include "gimple-expr.h" 23#include "is-a.h" 24#include "gimple.h" 25#include "gimple-iterator.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 33int 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. */ 37bool 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 46static tree 47get_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 100static tree 101get_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 188static void 189compare_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 212static void 213warn_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 255namespace { 256 257const 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 270class pass_warn_self_assign : public gimple_opt_pass 271{ 272public: 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 virtual unsigned int execute (function *); 279 280}; // class pass_warn_self_assign 281 282unsigned int 283pass_warn_self_assign::execute (function *fun) 284{ 285 gimple_stmt_iterator gsi; 286 basic_block bb; 287 288 FOR_EACH_BB_FN (bb, fun) 289 { 290 for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) 291 warn_self_assign (gsi_stmt (gsi)); 292 } 293 294 return 0; 295} 296 297} // anon namespace 298 299static gimple_opt_pass * 300make_pass_warn_self_assign (gcc::context *ctxt) 301{ 302 return new pass_warn_self_assign (ctxt); 303} 304 305/* The initialization routine exposed to and called by GCC. The spec of this 306 function is defined in gcc/gcc-plugin.h. 307 308 PLUGIN_NAME - name of the plugin (useful for error reporting) 309 ARGC - the size of the ARGV array 310 ARGV - an array of key-value argument pair 311 312 Returns 0 if initialization finishes successfully. 313 314 Note that this function needs to be named exactly "plugin_init". */ 315 316int 317plugin_init (struct plugin_name_args *plugin_info, 318 struct plugin_gcc_version *version) 319{ 320 struct register_pass_info pass_info; 321 const char *plugin_name = plugin_info->base_name; 322 int argc = plugin_info->argc; 323 struct plugin_argument *argv = plugin_info->argv; 324 bool enabled = true; 325 int i; 326 327 if (!plugin_default_version_check (version, &gcc_version)) 328 return 1; 329 330 /* Self-assign detection should happen after SSA is constructed. */ 331 pass_info.pass = make_pass_warn_self_assign (g); 332 pass_info.reference_pass_name = "ssa"; 333 pass_info.ref_pass_instance_number = 1; 334 pass_info.pos_op = PASS_POS_INSERT_AFTER; 335 336 /* Process the plugin arguments. This plugin takes the following arguments: 337 check-operator-eq, no-check-operator-eq, enable, and disable. 338 By default, the analysis is enabled with 'operator=' checked. */ 339 for (i = 0; i < argc; ++i) 340 { 341 if (!strcmp (argv[i].key, "check-operator-eq")) 342 { 343 if (argv[i].value) 344 warning (0, G_("option '-fplugin-arg-%s-check-operator-eq=%s'" 345 " ignored (superfluous '=%s')"), 346 plugin_name, argv[i].value, argv[i].value); 347 else 348 check_operator_eq = true; 349 } 350 else if (!strcmp (argv[i].key, "no-check-operator-eq")) 351 { 352 if (argv[i].value) 353 warning (0, G_("option '-fplugin-arg-%s-no-check-operator-eq=%s'" 354 " ignored (superfluous '=%s')"), 355 plugin_name, argv[i].value, argv[i].value); 356 else 357 check_operator_eq = false; 358 } 359 else if (!strcmp (argv[i].key, "enable")) 360 { 361 if (argv[i].value) 362 warning (0, G_("option '-fplugin-arg-%s-enable=%s' ignored" 363 " (superfluous '=%s')"), 364 plugin_name, argv[i].value, argv[i].value); 365 else 366 enabled = true; 367 } 368 else if (!strcmp (argv[i].key, "disable")) 369 { 370 if (argv[i].value) 371 warning (0, G_("option '-fplugin-arg-%s-disable=%s' ignored" 372 " (superfluous '=%s')"), 373 plugin_name, argv[i].value, argv[i].value); 374 else 375 enabled = false; 376 } 377 else 378 warning (0, G_("plugin %qs: unrecognized argument %qs ignored"), 379 plugin_name, argv[i].key); 380 } 381 382 /* Register this new pass with GCC if the analysis is enabled. */ 383 if (enabled) 384 register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, 385 &pass_info); 386 387 return 0; 388} 389