1037 lines
29 KiB
C
1037 lines
29 KiB
C
|
/* 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 ();
|
|||
|
}
|