summaryrefslogtreecommitdiffstats
path: root/contrib/gcc/tree-ssa-alias-warnings.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/gcc/tree-ssa-alias-warnings.c')
-rw-r--r--contrib/gcc/tree-ssa-alias-warnings.c1036
1 files changed, 1036 insertions, 0 deletions
diff --git a/contrib/gcc/tree-ssa-alias-warnings.c b/contrib/gcc/tree-ssa-alias-warnings.c
new file mode 100644
index 0000000..0c78752
--- /dev/null
+++ b/contrib/gcc/tree-ssa-alias-warnings.c
@@ -0,0 +1,1036 @@
+/* Strict aliasing checks.
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Contributed by Silvius Rus <rus@google.com>.
+
+ This file is part of GCC.
+
+ GCC 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 2, or (at your option)
+ any later version.
+
+ GCC 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 COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "alloc-pool.h"
+#include "tree.h"
+#include "tree-dump.h"
+#include "tree-flow.h"
+#include "params.h"
+#include "function.h"
+#include "expr.h"
+#include "toplev.h"
+#include "diagnostic.h"
+#include "tree-ssa-structalias.h"
+#include "tree-ssa-propagate.h"
+#include "langhooks.h"
+
+/* Module to issue a warning when a program uses data through a type
+ different from the type through which the data were defined.
+ Implements -Wstrict-aliasing and -Wstrict-aliasing=n.
+ These checks only happen when -fstrict-aliasing is present.
+
+ The idea is to use the compiler to identify occurrences of nonstandard
+ aliasing, and report them to programmers. Programs free of such aliasing
+ are more portable, maintainable, and can usually be optimized better.
+
+ The current, as of April 2007, C and C++ language standards forbid
+ accessing data of type A through an lvalue of another type B,
+ with certain exceptions. See the C Standard ISO/IEC 9899:1999,
+ section 6.5, paragraph 7, and the C++ Standard ISO/IEC 14882:1998,
+ section 3.10, paragraph 15.
+
+ Example 1:*a is used as int but was defined as a float, *b.
+ int* a = ...;
+ float* b = reinterpret_cast<float*> (a);
+ *b = 2.0;
+ return *a
+
+ Unfortunately, the problem is in general undecidable if we take into
+ account arithmetic expressions such as array indices or pointer arithmetic.
+ (It is at least as hard as Peano arithmetic decidability.)
+ Even ignoring arithmetic, the problem is still NP-hard, because it is
+ at least as hard as flow-insensitive may-alias analysis, which was proved
+ NP-hard by Horwitz et al, TOPLAS 1997.
+
+ It is clear that we need to choose some heuristics.
+ Unfortunately, various users have different goals which correspond to
+ different time budgets so a common approach will not suit all.
+ We present the user with three effort/accuracy levels. By accuracy, we mean
+ a common-sense mix of low count of false positives with a
+ reasonably low number of false negatives. We are heavily biased
+ towards a low count of false positives.
+ The effort (compilation time) is likely to increase with the level.
+
+ -Wstrict-aliasing=1
+ ===================
+ Most aggressive, least accurate. Possibly useful when higher levels
+ do not warn but -fstrict-aliasing still breaks the code, as
+ it has very few false negatives.
+ Warn for all bad pointer conversions, even if never dereferenced.
+ Implemented in the front end (c-common.c).
+ Uses alias_sets_might_conflict to compare types.
+
+ -Wstrict-aliasing=2
+ ===================
+ Aggressive, not too precise.
+ May still have many false positives (not as many as level 1 though),
+ and few false negatives (but possibly more than level 1).
+ Runs only in the front end. Uses alias_sets_might_conflict to
+ compare types. Does not check for pointer dereferences.
+ Only warns when an address is taken. Warns about incomplete type punning.
+
+ -Wstrict-aliasing=3 (default)
+ ===================
+ Should have very few false positives and few false negatives.
+ Takes care of the common punn+dereference pattern in the front end:
+ *(int*)&some_float.
+ Takes care of multiple statement cases in the back end,
+ using flow-sensitive points-to information (-O required).
+ Uses alias_sets_conflict_p to compare types and only warns
+ when the converted pointer is dereferenced.
+ Does not warn about incomplete type punning.
+
+ Future improvements can be included by adding higher levels.
+
+ In summary, expression level analysis is performed in the front-end,
+ and multiple-statement analysis is performed in the backend.
+ The remainder of this discussion is only about the backend analysis.
+
+ This implementation uses flow-sensitive points-to information.
+ Flow-sensitivity refers to accesses to the pointer, and not the object
+ pointed. For instance, we do not warn about the following case.
+
+ Example 2.
+ int* a = (int*)malloc (...);
+ float* b = reinterpret_cast<float*> (a);
+ *b = 2.0;
+ a = (int*)malloc (...);
+ return *a;
+
+ In SSA, it becomes clear that the INT value *A_2 referenced in the
+ return statement is not aliased to the FLOAT defined through *B_1.
+ int* a_1 = (int*)malloc (...);
+ float* b_1 = reinterpret_cast<float*> (a_1);
+ *b_1 = 2.0;
+ a_2 = (int*)malloc (...);
+ return *a_2;
+
+
+ Algorithm Outline
+ =================
+
+ ForEach (ptr, object) in the points-to table
+ If (incompatible_types (*ptr, object))
+ If (referenced (ptr, current function)
+ and referenced (object, current function))
+ Issue warning (ptr, object, reference locations)
+
+ The complexity is:
+ O (sizeof (points-to table)
+ + sizeof (function body) * lookup_time (points-to table))
+
+ Pointer dereference locations are looked up on demand. The search is
+ a single scan of the function body, in which all references to pointers
+ and objects in the points-to table are recorded. However, this dominant
+ time factor occurs rarely, only when cross-type aliasing was detected.
+
+
+ Limitations of the Proposed Implementation
+ ==========================================
+
+ 1. We do not catch the following case, because -fstrict-aliasing will
+ associate different tags with MEM while building points-to information,
+ thus before we get to analyze it.
+ XXX: this could be solved by either running with -fno-strict-aliasing
+ or by recording the points-to information before splitting the orignal
+ tag based on type.
+
+ Example 3.
+ void* mem = malloc (...);
+ int* pi = reinterpret_cast<int*> (mem);
+ float* b = reinterpret_cast<float*> (mem);
+ *b = 2.0;
+ return *pi+1;
+
+ 2. We do not check whether the two conflicting (de)references can
+ reach each other in the control flow sense. If we fixed limitation
+ 1, we would wrongly issue a warning in the following case.
+
+ Example 4.
+ void* raw = malloc (...);
+ if (...) {
+ float* b = reinterpret_cast<float*> (raw);
+ *b = 2.0;
+ return (int)*b;
+ } else {
+ int* a = reinterpret_cast<int*> (raw);
+ *a = 1;
+ return *a;
+
+ 3. Only simple types are compared, thus no structures, unions or classes
+ are analyzed. A first attempt to deal with structures introduced much
+ complication and has not showed much improvement in preliminary tests,
+ so it was left out.
+
+ 4. All analysis is intraprocedural. */
+
+
+/* Local declarations. */
+static void find_references_in_function (void);
+
+
+
+/* Get main type of tree TYPE, stripping array dimensions and qualifiers. */
+
+static tree
+get_main_type (tree type)
+{
+ while (TREE_CODE (type) == ARRAY_TYPE)
+ type = TREE_TYPE (type);
+ return TYPE_MAIN_VARIANT (type);
+}
+
+
+/* Get the type of the given object. If IS_PTR is true, get the type of the
+ object pointed to or referenced by OBJECT instead.
+ For arrays, return the element type. Ignore all qualifiers. */
+
+static tree
+get_otype (tree object, bool is_ptr)
+{
+ tree otype = TREE_TYPE (object);
+
+ if (is_ptr)
+ {
+ gcc_assert (POINTER_TYPE_P (otype));
+ otype = TREE_TYPE (otype);
+ }
+ return get_main_type (otype);
+}
+
+
+/* Return true if tree TYPE is struct, class or union. */
+
+static bool
+struct_class_union_p (tree type)
+{
+ return (TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == UNION_TYPE
+ || TREE_CODE (type) == QUAL_UNION_TYPE);
+}
+
+
+
+/* Keep data during a search for an aliasing site.
+ RHS = object or pointer aliased. No LHS is specified because we are only
+ looking in the UseDef paths of a given variable, so LHS will always be
+ an SSA name of the same variable.
+ When IS_RHS_POINTER = true, we are looking for ... = RHS. Otherwise,
+ we are looking for ... = &RHS.
+ SITE is the output of a search, non-NULL if the search succeeded. */
+
+struct alias_match
+{
+ tree rhs;
+ bool is_rhs_pointer;
+ tree site;
+};
+
+
+/* Callback for find_alias_site. Return true if the right hand site
+ of STMT matches DATA. */
+
+static bool
+find_alias_site_helper (tree var ATTRIBUTE_UNUSED, tree stmt, void *data)
+{
+ struct alias_match *match = (struct alias_match *) data;
+ tree rhs_pointer = get_rhs (stmt);
+ tree to_match = NULL_TREE;
+
+ while (TREE_CODE (rhs_pointer) == NOP_EXPR
+ || TREE_CODE (rhs_pointer) == CONVERT_EXPR
+ || TREE_CODE (rhs_pointer) == VIEW_CONVERT_EXPR)
+ rhs_pointer = TREE_OPERAND (rhs_pointer, 0);
+
+ if (!rhs_pointer)
+ /* Not a type conversion. */
+ return false;
+
+ if (TREE_CODE (rhs_pointer) == ADDR_EXPR && !match->is_rhs_pointer)
+ to_match = TREE_OPERAND (rhs_pointer, 0);
+ else if (POINTER_TYPE_P (rhs_pointer) && match->is_rhs_pointer)
+ to_match = rhs_pointer;
+
+ if (to_match != match->rhs)
+ /* Type conversion, but not a name match. */
+ return false;
+
+ /* Found it. */
+ match->site = stmt;
+ return true;
+}
+
+
+/* Find the statement where OBJECT1 gets aliased to OBJECT2.
+ If IS_PTR2 is true, consider OBJECT2 to be the name of a pointer or
+ reference rather than the actual aliased object.
+ For now, just implement the case where OBJECT1 is an SSA name defined
+ by a PHI statement. */
+
+static tree
+find_alias_site (tree object1, bool is_ptr1 ATTRIBUTE_UNUSED,
+ tree object2, bool is_ptr2)
+{
+ struct alias_match match;
+
+ match.rhs = object2;
+ match.is_rhs_pointer = is_ptr2;
+ match.site = NULL_TREE;
+
+ if (TREE_CODE (object1) != SSA_NAME)
+ return NULL_TREE;
+
+ walk_use_def_chains (object1, find_alias_site_helper, &match, false);
+ return match.site;
+}
+
+
+/* Structure to store temporary results when trying to figure out whether
+ an object is referenced. Just its presence in the text is not enough,
+ as we may just be taking its address. */
+
+struct match_info
+{
+ tree object;
+ bool is_ptr;
+ /* The difference between the number of references to OBJECT
+ and the number of occurences of &OBJECT. */
+ int found;
+};
+
+
+/* Return the base if EXPR is an SSA name. Return EXPR otherwise. */
+
+static tree
+get_ssa_base (tree expr)
+{
+ if (TREE_CODE (expr) == SSA_NAME)
+ return SSA_NAME_VAR (expr);
+ else
+ return expr;
+}
+
+
+/* Record references to objects and pointer dereferences across some piece of
+ code. The number of references is recorded for each item.
+ References to an object just to take its address are not counted.
+ For instance, if PTR is a pointer and OBJ is an object:
+ 1. Expression &obj + *ptr will have the following reference match structure:
+ ptrs: <ptr, 1>
+ objs: <ptr, 1>
+ OBJ does not appear as referenced because we just take its address.
+ 2. Expression ptr + *ptr will have the following reference match structure:
+ ptrs: <ptr, 1>
+ objs: <ptr, 2>
+ PTR shows up twice as an object, but is dereferenced only once.
+
+ The elements of the hash tables are tree_map objects. */
+struct reference_matches
+{
+ htab_t ptrs;
+ htab_t objs;
+};
+
+
+/* Return the match, if any. Otherwise, return NULL_TREE. It will
+ return NULL_TREE even when a match was found, if the value associated
+ to KEY is NULL_TREE. */
+
+static inline tree
+match (htab_t ref_map, tree key)
+{
+ struct tree_map to_find;
+ struct tree_map *found;
+ void **slot = NULL;
+
+ to_find.from = key;
+ to_find.hash = htab_hash_pointer (key);
+ slot = htab_find_slot (ref_map, &to_find, NO_INSERT);
+
+ if (!slot)
+ return NULL_TREE;
+
+ found = (struct tree_map *) *slot;
+ return found->to;
+}
+
+
+/* Set the entry corresponding to KEY, but only if the entry
+ already exists and its value is NULL_TREE. Otherwise, do nothing. */
+
+static inline void
+maybe_add_match (htab_t ref_map, struct tree_map *key)
+{
+ struct tree_map *found = htab_find (ref_map, key);
+
+ if (found && !found->to)
+ found->to = key->to;
+}
+
+
+/* Add an entry to HT, with key T and value NULL_TREE. */
+
+static void
+add_key (htab_t ht, tree t, alloc_pool references_pool)
+{
+ void **slot;
+ struct tree_map *tp = pool_alloc (references_pool);
+
+ tp->from = t;
+ tp->to = NULL_TREE;
+ tp->hash = htab_hash_pointer(tp->from);
+
+ slot = htab_find_slot (ht, tp, INSERT);
+ *slot = (void *) tp;
+}
+
+
+/* Some memory to keep the objects in the reference table. */
+
+static alloc_pool ref_table_alloc_pool = NULL;
+
+
+/* Get some memory to keep the objects in the reference table. */
+
+static inline alloc_pool
+reference_table_alloc_pool (bool build)
+{
+ if (ref_table_alloc_pool || !build)
+ return ref_table_alloc_pool;
+
+ ref_table_alloc_pool =
+ create_alloc_pool ("ref_table_alloc_pool", sizeof (struct tree_map), 20);
+
+ return ref_table_alloc_pool;
+}
+
+
+/* Initialize the reference table by adding all pointers in the points-to
+ table as keys, and NULL_TREE as associated values. */
+
+static struct reference_matches *
+build_reference_table (void)
+{
+ unsigned int i;
+ struct reference_matches *ref_table = NULL;
+ alloc_pool references_pool = reference_table_alloc_pool (true);
+
+ ref_table = XNEW (struct reference_matches);
+ ref_table->objs = htab_create (10, tree_map_hash, tree_map_eq, NULL);
+ ref_table->ptrs = htab_create (10, tree_map_hash, tree_map_eq, NULL);
+
+ for (i = 1; i < num_ssa_names; i++)
+ {
+ tree ptr = ssa_name (i);
+ struct ptr_info_def *pi;
+
+ if (ptr == NULL_TREE)
+ continue;
+
+ pi = SSA_NAME_PTR_INFO (ptr);
+
+ if (!SSA_NAME_IN_FREE_LIST (ptr) && pi && pi->name_mem_tag)
+ {
+ /* Add pointer to the interesting dereference list. */
+ add_key (ref_table->ptrs, ptr, references_pool);
+
+ /* Add all aliased names to the interesting reference list. */
+ if (pi->pt_vars)
+ {
+ unsigned ix;
+ bitmap_iterator bi;
+
+ EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, ix, bi)
+ {
+ tree alias = referenced_var (ix);
+ add_key (ref_table->objs, alias, references_pool);
+ }
+ }
+ }
+ }
+
+ return ref_table;
+}
+
+
+/* Reference table. */
+
+static struct reference_matches *ref_table = NULL;
+
+
+/* Clean up the reference table if allocated. */
+
+static void
+maybe_free_reference_table (void)
+{
+ if (ref_table)
+ {
+ htab_delete (ref_table->ptrs);
+ htab_delete (ref_table->objs);
+ free (ref_table);
+ ref_table = NULL;
+ }
+
+ if (ref_table_alloc_pool)
+ {
+ free_alloc_pool (ref_table_alloc_pool);
+ ref_table_alloc_pool = NULL;
+ }
+}
+
+
+/* Get the reference table. Initialize it if needed. */
+
+static inline struct reference_matches *
+reference_table (bool build)
+{
+ if (ref_table || !build)
+ return ref_table;
+
+ ref_table = build_reference_table ();
+ find_references_in_function ();
+ return ref_table;
+}
+
+
+/* Callback for find_references_in_function.
+ Check whether *TP is an object reference or pointer dereference for the
+ variables given in ((struct match_info*)DATA)->OBJS or
+ ((struct match_info*)DATA)->PTRS. The total number of references
+ is stored in the same structures. */
+
+static tree
+find_references_in_tree_helper (tree *tp,
+ int *walk_subtrees ATTRIBUTE_UNUSED,
+ void *data)
+{
+ struct tree_map match;
+ static int parent_tree_code = ERROR_MARK;
+
+ /* Do not report references just for the purpose of taking an address.
+ XXX: we rely on the fact that the tree walk is in preorder
+ and that ADDR_EXPR is not a leaf, thus cannot be carried over across
+ walks. */
+ if (parent_tree_code == ADDR_EXPR)
+ goto finish;
+
+ match.to = (tree) data;
+
+ if (TREE_CODE (*tp) == INDIRECT_REF)
+ {
+ match.from = TREE_OPERAND (*tp, 0);
+ match.hash = htab_hash_pointer (match.from);
+ maybe_add_match (reference_table (true)->ptrs, &match);
+ }
+ else
+ {
+ match.from = *tp;
+ match.hash = htab_hash_pointer (match.from);
+ maybe_add_match (reference_table (true)->objs, &match);
+ }
+
+finish:
+ parent_tree_code = TREE_CODE (*tp);
+ return NULL_TREE;
+}
+
+
+/* Find all the references to aliased variables in the current function. */
+
+static void
+find_references_in_function (void)
+{
+ basic_block bb;
+ block_stmt_iterator i;
+
+ FOR_EACH_BB (bb)
+ for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
+ walk_tree (bsi_stmt_ptr (i), find_references_in_tree_helper,
+ (void *) *bsi_stmt_ptr (i), NULL);
+}
+
+
+/* Find the reference site for OBJECT.
+ If IS_PTR is true, look for derferences of OBJECT instead.
+ XXX: only the first site is returned in the current
+ implementation. If there are no matching sites, return NULL_TREE. */
+
+static tree
+reference_site (tree object, bool is_ptr)
+{
+ if (is_ptr)
+ return match (reference_table (true)->ptrs, object);
+ else
+ return match (reference_table (true)->objs, object);
+}
+
+
+/* Try to get more location info when something is missing.
+ OBJECT1 and OBJECT2 are aliased names. If IS_PTR1 or IS_PTR2, the alias
+ is on the memory referenced or pointed to by OBJECT1 and OBJECT2.
+ ALIAS_SITE, DEREF_SITE1 and DEREF_SITE2 are the statements where the
+ alias takes place (some pointer assignment usually) and where the
+ alias is referenced through OBJECT1 and OBJECT2 respectively.
+ REF_TYPE1 and REF_TYPE2 will return the type of the reference at the
+ respective sites. Only the first matching reference is returned for
+ each name. If no statement is found, the function header is returned. */
+
+static void
+maybe_find_missing_stmts (tree object1, bool is_ptr1,
+ tree object2, bool is_ptr2,
+ tree *alias_site,
+ tree *deref_site1,
+ tree *deref_site2)
+{
+ if (object1 && object2)
+ {
+ if (!*alias_site || !EXPR_HAS_LOCATION (*alias_site))
+ *alias_site = find_alias_site (object1, is_ptr1, object2, is_ptr2);
+
+ if (!*deref_site1 || !EXPR_HAS_LOCATION (*deref_site1))
+ *deref_site1 = reference_site (object1, is_ptr1);
+
+ if (!*deref_site2 || !EXPR_HAS_LOCATION (*deref_site2))
+ *deref_site2 = reference_site (object2, is_ptr2);
+ }
+
+ /* If we could not find the alias site, set it to one of the dereference
+ sites, if available. */
+ if (!*alias_site)
+ {
+ if (*deref_site1)
+ *alias_site = *deref_site1;
+ else if (*deref_site2)
+ *alias_site = *deref_site2;
+ }
+
+ /* If we could not find the dereference sites, set them to the alias site,
+ if known. */
+ if (!*deref_site1 && *alias_site)
+ *deref_site1 = *alias_site;
+ if (!*deref_site2 && *alias_site)
+ *deref_site2 = *alias_site;
+}
+
+
+/* Callback for find_first_artificial_name.
+ Find out if there are no artificial names at tree node *T. */
+
+static tree
+ffan_walker (tree *t,
+ int *go_below ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
+{
+ if (TREE_CODE (*t) == VAR_DECL || TREE_CODE (*t) == PARM_DECL)
+ if (DECL_ARTIFICIAL (*t))
+ return *t;
+
+ return NULL_TREE;
+}
+
+/* Return the first artificial name within EXPR, or NULL_TREE if
+ none exists. */
+
+static tree
+find_first_artificial_name (tree expr)
+{
+ return walk_tree_without_duplicates (&expr, ffan_walker, NULL);
+}
+
+
+/* Get a name from the original program for VAR. */
+
+static const char *
+get_var_name (tree var)
+{
+ if (TREE_CODE (var) == SSA_NAME)
+ return get_var_name (get_ssa_base (var));
+
+ if (find_first_artificial_name (var))
+ return "{unknown}";
+
+ if (TREE_CODE (var) == VAR_DECL || TREE_CODE (var) == PARM_DECL)
+ if (DECL_NAME (var))
+ return IDENTIFIER_POINTER (DECL_NAME (var));
+
+ return "{unknown}";
+}
+
+
+/* Return true if VAR contains an artificial name. */
+
+static bool
+contains_artificial_name_p (tree var)
+{
+ if (TREE_CODE (var) == SSA_NAME)
+ return contains_artificial_name_p (get_ssa_base (var));
+
+ return find_first_artificial_name (var) != NULL_TREE;
+}
+
+
+/* Return "*" if OBJECT is not the actual alias but a pointer to it, or
+ "" otherwise.
+ IS_PTR is true when OBJECT is not the actual alias.
+ In addition to checking IS_PTR, we also make sure that OBJECT is a pointer
+ since IS_PTR would also be true for C++ references, but we should only
+ print a * before a pointer and not before a reference. */
+
+static const char *
+get_maybe_star_prefix (tree object, bool is_ptr)
+{
+ gcc_assert (object);
+ return (is_ptr
+ && TREE_CODE (TREE_TYPE (object)) == POINTER_TYPE) ? "*" : "";
+}
+
+
+/* Callback for contains_node_type_p.
+ Returns true if *T has tree code *(int*)DATA. */
+
+static tree
+contains_node_type_p_callback (tree *t,
+ int *go_below ATTRIBUTE_UNUSED,
+ void *data)
+{
+ return ((int) TREE_CODE (*t) == *((int *) data)) ? *t : NULL_TREE;
+}
+
+
+/* Return true if T contains a node with tree code TYPE. */
+
+static bool
+contains_node_type_p (tree t, int type)
+{
+ return (walk_tree_without_duplicates (&t, contains_node_type_p_callback,
+ (void *) &type)
+ != NULL_TREE);
+}
+
+
+/* Return true if a warning was issued in the front end at STMT. */
+
+static bool
+already_warned_in_frontend_p (tree stmt)
+{
+ tree rhs_pointer;
+
+ if (stmt == NULL_TREE)
+ return false;
+
+ rhs_pointer = get_rhs (stmt);
+
+ if ((TREE_CODE (rhs_pointer) == NOP_EXPR
+ || TREE_CODE (rhs_pointer) == CONVERT_EXPR
+ || TREE_CODE (rhs_pointer) == VIEW_CONVERT_EXPR)
+ && TREE_NO_WARNING (rhs_pointer))
+ return true;
+ else
+ return false;
+}
+
+
+/* Return true if and only if TYPE is a function or method pointer type,
+ or pointer to a pointer to ... to a function or method. */
+
+static bool
+is_method_pointer (tree type)
+{
+ while (TREE_CODE (type) == POINTER_TYPE)
+ type = TREE_TYPE (type);
+ return TREE_CODE (type) == METHOD_TYPE || TREE_CODE (type) == FUNCTION_TYPE;
+}
+
+
+/* Issue a -Wstrict-aliasing warning.
+ OBJECT1 and OBJECT2 are aliased names.
+ If IS_PTR1 and/or IS_PTR2 is true, then the corresponding name
+ OBJECT1/OBJECT2 is a pointer or reference to the aliased memory,
+ rather than actual storage.
+ ALIAS_SITE is a statement where the alias took place. In the most common
+ case, that is where a pointer was assigned to the address of an object. */
+
+static bool
+strict_aliasing_warn (tree alias_site,
+ tree object1, bool is_ptr1,
+ tree object2, bool is_ptr2,
+ bool filter_artificials)
+{
+ tree ref_site1 = NULL_TREE;
+ tree ref_site2 = NULL_TREE;
+ const char *name1;
+ const char *name2;
+ location_t alias_loc;
+ location_t ref1_loc;
+ location_t ref2_loc;
+ gcc_assert (object1);
+ gcc_assert (object2);
+
+ if (contains_artificial_name_p (object1)
+ || contains_artificial_name_p (object2))
+ return false;
+
+ name1 = get_var_name (object1);
+ name2 = get_var_name (object2);
+
+ if (is_method_pointer (get_main_type (TREE_TYPE (object2))))
+ return false;
+
+ maybe_find_missing_stmts (object1, is_ptr1, object2, is_ptr2, &alias_site,
+ &ref_site1, &ref_site2);
+
+ if (!alias_site)
+ return false;
+
+ if (EXPR_HAS_LOCATION (alias_site))
+ alias_loc = EXPR_LOCATION (alias_site);
+ else
+ return false;
+
+ if (EXPR_HAS_LOCATION (ref_site1))
+ ref1_loc = EXPR_LOCATION (ref_site1);
+ else
+ ref1_loc = alias_loc;
+
+ if (EXPR_HAS_LOCATION (ref_site2))
+ ref2_loc = EXPR_LOCATION (ref_site2);
+ else
+ ref2_loc = alias_loc;
+
+ if (already_warned_in_frontend_p (alias_site))
+ return false;
+
+ /* If they are not SSA names, but contain SSA names, drop the warning
+ because it cannot be displayed well.
+ Also drop it if they both contain artificials.
+ XXX: this is a hack, must figure out a better way to display them. */
+ if (filter_artificials)
+ if ((find_first_artificial_name (get_ssa_base (object1))
+ && find_first_artificial_name (get_ssa_base (object2)))
+ || (TREE_CODE (object1) != SSA_NAME
+ && contains_node_type_p (object1, SSA_NAME))
+ || (TREE_CODE (object2) != SSA_NAME
+ && contains_node_type_p (object2, SSA_NAME)))
+ return false;
+
+ /* XXX: In the following format string, %s:%d should be replaced by %H.
+ However, in my tests only the first %H printed ok, while the
+ second and third were printed as blanks. */
+ warning (OPT_Wstrict_aliasing,
+ "%Hlikely type-punning may break strict-aliasing rules: "
+ "object %<%s%s%> of main type %qT is referenced at or around "
+ "%s:%d and may be "
+ "aliased to object %<%s%s%> of main type %qT which is referenced "
+ "at or around %s:%d.",
+ &alias_loc,
+ get_maybe_star_prefix (object1, is_ptr1),
+ name1, get_otype (object1, is_ptr1),
+ LOCATION_FILE (ref1_loc), LOCATION_LINE (ref1_loc),
+ get_maybe_star_prefix (object2, is_ptr2),
+ name2, get_otype (object2, is_ptr2),
+ LOCATION_FILE (ref2_loc), LOCATION_LINE (ref2_loc));
+
+ return true;
+}
+
+
+
+/* Return true when any objects of TYPE1 and TYPE2 respectively
+ may not be aliased according to the language standard. */
+
+static bool
+nonstandard_alias_types_p (tree type1, tree type2)
+{
+ HOST_WIDE_INT set1;
+ HOST_WIDE_INT set2;
+
+ if (VOID_TYPE_P (type1) || VOID_TYPE_P (type2))
+ return false;
+
+ set1 = get_alias_set (type1);
+ set2 = get_alias_set (type2);
+ return !alias_sets_conflict_p (set1, set2);
+}
+
+
+
+/* Returns true if the given name is a struct field tag (SFT). */
+
+static bool
+struct_field_tag_p (tree var)
+{
+ return TREE_CODE (var) == STRUCT_FIELD_TAG;
+}
+
+
+/* Returns true when *PTR may not be aliased to ALIAS.
+ See C standard 6.5p7 and C++ standard 3.10p15.
+ If PTR_PTR is true, ALIAS represents a pointer or reference to the
+ aliased storage rather than its actual name. */
+
+static bool
+nonstandard_alias_p (tree ptr, tree alias, bool ptr_ptr)
+{
+ /* Find the types to compare. */
+ tree ptr_type = get_otype (ptr, true);
+ tree alias_type = get_otype (alias, ptr_ptr);
+
+ /* XXX: for now, say it's OK if the alias escapes.
+ Not sure this is needed in general, but otherwise GCC will not
+ bootstrap. */
+ if (var_ann (get_ssa_base (alias))->escape_mask != NO_ESCAPE)
+ return false;
+
+ /* XXX: don't get into structures for now. It brings much complication
+ and little benefit. */
+ if (struct_class_union_p (ptr_type) || struct_class_union_p (alias_type))
+ return false;
+
+ /* XXX: In 4.2.1, field resolution in alias is not as good as in pre-4.3
+ This fixes problems found during the backport, where a pointer to the
+ first field of a struct appears to be aliased to the whole struct. */
+ if (struct_field_tag_p (alias))
+ return false;
+
+ /* If they are both SSA names of artificials, let it go, the warning
+ is too confusing. */
+ if (find_first_artificial_name (ptr) && find_first_artificial_name (alias))
+ return false;
+
+ /* Compare the types. */
+ return nonstandard_alias_types_p (ptr_type, alias_type);
+}
+
+
+/* Return true when we should skip analysis for pointer PTR based on the
+ fact that their alias information *PI is not considered relevant. */
+
+static bool
+skip_this_pointer (tree ptr ATTRIBUTE_UNUSED, struct ptr_info_def *pi)
+{
+ /* If it is not dereferenced, it is not a problem (locally). */
+ if (!pi->is_dereferenced)
+ return true;
+
+ /* This would probably cause too many false positives. */
+ if (pi->value_escapes_p || pi->pt_anything)
+ return true;
+
+ return false;
+}
+
+
+/* Find aliasing to named objects for pointer PTR. */
+
+static void
+dsa_named_for (tree ptr)
+{
+ struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
+
+ if (pi)
+ {
+ if (skip_this_pointer (ptr, pi))
+ return;
+
+ /* For all the variables it could be aliased to. */
+ if (pi->pt_vars)
+ {
+ unsigned ix;
+ bitmap_iterator bi;
+
+ EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, ix, bi)
+ {
+ tree alias = referenced_var (ix);
+
+ if (is_global_var (alias))
+ continue;
+
+ if (nonstandard_alias_p (ptr, alias, false))
+ strict_aliasing_warn (SSA_NAME_DEF_STMT (ptr),
+ ptr, true, alias, false, true);
+ }
+ }
+ }
+}
+
+
+/* Detect and report strict aliasing violation of named objects. */
+
+static void
+detect_strict_aliasing_named (void)
+{
+ unsigned int i;
+
+ for (i = 1; i < num_ssa_names; i++)
+ {
+ tree ptr = ssa_name (i);
+ struct ptr_info_def *pi;
+
+ if (ptr == NULL_TREE)
+ continue;
+
+ pi = SSA_NAME_PTR_INFO (ptr);
+
+ if (!SSA_NAME_IN_FREE_LIST (ptr) && pi && pi->name_mem_tag)
+ dsa_named_for (ptr);
+ }
+}
+
+
+/* Return false only the first time I see each instance of FUNC. */
+
+static bool
+processed_func_p (tree func)
+{
+ static htab_t seen = NULL;
+ void **slot;
+
+ if (!seen)
+ seen = htab_create (100, htab_hash_pointer, htab_eq_pointer, NULL);
+
+ slot = htab_find_slot (seen, func, INSERT);
+ gcc_assert (slot);
+
+ if (*slot)
+ return true;
+
+ *slot = func;
+ return false;
+}
+
+
+/* Detect and warn about type-punning using points-to information. */
+
+void
+strict_aliasing_warning_backend (void)
+{
+ if (!(flag_strict_aliasing
+ && warn_strict_aliasing == 3
+ && !processed_func_p (current_function_decl)))
+ return;
+
+ detect_strict_aliasing_named ();
+ maybe_free_reference_table ();
+}
OpenPOWER on IntegriCloud