Makefile.in (OBJS-common): Add tree-ssa-alias-warnings.o.
gcc/ * Makefile.in (OBJS-common): Add tree-ssa-alias-warnings.o. * c-common.c (strict_aliasing_warning): Modify -Wstrict-aliasing logic. * c-common.h (strict_aliasing_warning): Change return type. * c-opts.c (c_common_handle_option): Add call to set_Wstrict_aliasing. * c-typeck.c (build_indirect_ref): Add call to strict_aliasing_warning. (build_c_cast): Condition call to strict_aliasing_warning. * doc/invoke.texi: Update description of -Wstrict-aliasing[=n]. * flags.h (set_Wstrict_aliasing): Declare. * opts.c (set_Wstrict_alising): Define, add call to. * tree-flow.h (strict_aliasing_warning_backend): Declare. * tree-ssa-alias-warnings.c: New file. * tree-ssa-alias.c (compute_may_aliases): Add call to strict_aliasing_warning_backend. gcc/cp * cp/typeck.c (build_indirect_ref): Add call to strict_aliasing_warning. (build_reinterpret_cast_1): Condition call to strict_aliasing_warning. gcc/testsuite * gcc.dg/Wstrict-aliasing-bogus-const-ptr-nonconst-ptr.c: New test. * gcc.dg/Wstrict-aliasing-bogus-never-dereferenced.c: New test. * gcc.dg/Wstrict-aliasing-bogus-struct-included.c: New test. * gcc.dg/Wstrict-aliasing-converted-assigned.c: New test. * gcc.dg/Wstrict-aliasing-float-ptr-int-obj.c: New test. * gcc.dg/alias-1.c: Update option: -Wstrict-aliasing=2. * gcc.dg/alias-9.c: Update option: -Wstrict-aliasing=2. * g++.dg/warn/Wstrict-aliasing-7.C: Update option: -Wstrict-aliasing=2. * g++.dg/warn/Wstrict-aliasing-bogus-base-derived.C: New test. * g++.dg/warn/Wstrict-aliasing-bogus-char-1.C: New test. * g++.dg/warn/Wstrict-aliasing-bogus-const.C: New test. * g++.dg/warn/Wstrict-aliasing-bogus-nested-arrays.C: New test. * g++.dg/warn/Wstrict-aliasing-bogus-signed-unsigned.C: New test. * g++.dg/warn/Wstrict-aliasing-bogus-struct-included.C: New test. * g++.dg/warn/Wstrict-aliasing-bogus-union.C: New test. * g++.dg/warn/Wstrict-aliasing-float-ref-int-obj.C: New test. From-SVN: r124622
This commit is contained in:
parent
8339896e55
commit
79bedddc8f
@ -1,3 +1,19 @@
|
||||
2007-05-11 Silvius Rus <rus@google.com>
|
||||
|
||||
* Makefile.in (OBJS-common): Add tree-ssa-alias-warnings.o.
|
||||
* c-common.c (strict_aliasing_warning): Modify -Wstrict-aliasing logic.
|
||||
* c-common.h (strict_aliasing_warning): Change return type.
|
||||
* c-opts.c (c_common_handle_option): Add call to set_Wstrict_aliasing.
|
||||
* c-typeck.c (build_indirect_ref): Add call to strict_aliasing_warning.
|
||||
(build_c_cast): Condition call to strict_aliasing_warning.
|
||||
* doc/invoke.texi: Update description of -Wstrict-aliasing[=n].
|
||||
* flags.h (set_Wstrict_aliasing): Declare.
|
||||
* opts.c (set_Wstrict_alising): Define, add call to.
|
||||
* tree-flow.h (strict_aliasing_warning_backend): Declare.
|
||||
* tree-ssa-alias-warnings.c: New file.
|
||||
* tree-ssa-alias.c (compute_may_aliases): Add call to
|
||||
strict_aliasing_warning_backend.
|
||||
|
||||
2007-05-11 Zdenek Dvorak <dvorakz@suse.cz>
|
||||
|
||||
* tree-loop-linear.c (gather_interchange_stats, try_interchange_loops):
|
||||
|
@ -1106,6 +1106,7 @@ OBJS-common = \
|
||||
tree-sra.o \
|
||||
tree-ssa-address.o \
|
||||
tree-ssa-alias.o \
|
||||
tree-ssa-alias-warnings.o \
|
||||
tree-ssa-ccp.o \
|
||||
tree-ssa-coalesce.o \
|
||||
tree-ssa-copy.o \
|
||||
@ -2104,6 +2105,11 @@ tree-ssa-loop-im.o : tree-ssa-loop-im.c $(TREE_FLOW_H) $(CONFIG_H) \
|
||||
tree-ssa-math-opts.o : tree-ssa-math-opts.c $(TREE_FLOW_H) $(CONFIG_H) \
|
||||
$(SYSTEM_H) $(TREE_H) $(TIMEVAR_H) tree-pass.h $(TM_H) $(FLAGS_H) \
|
||||
alloc-pool.h $(BASIC_BLOCK_H) $(TARGET_H)
|
||||
tree-ssa-alias-warnings.o : tree-ssa-alias-warnings.c \
|
||||
$(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(TREE_DUMP_H) \
|
||||
$(TREE_FLOW_H) $(PARAMS_H) $(FUNCTION_H) $(EXPR_H) toplev.h \
|
||||
tree-ssa-structalias.h tree-ssa-propagate.h langhooks.h alloc-pool.h \
|
||||
$(DIAGNOSTIC_H)
|
||||
tree-ssa-alias.o : tree-ssa-alias.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
|
||||
$(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) $(TREE_INLINE_H) $(FLAGS_H) \
|
||||
$(FUNCTION_H) $(TIMEVAR_H) convert.h $(TM_H) coretypes.h langhooks.h \
|
||||
|
@ -1014,35 +1014,67 @@ warn_logical_operator (enum tree_code code, tree arg1, tree
|
||||
strict aliasing mode is in effect. OTYPE is the original
|
||||
TREE_TYPE of EXPR, and TYPE the type we're casting to. */
|
||||
|
||||
void
|
||||
bool
|
||||
strict_aliasing_warning (tree otype, tree type, tree expr)
|
||||
{
|
||||
if (flag_strict_aliasing && warn_strict_aliasing
|
||||
&& POINTER_TYPE_P (type) && POINTER_TYPE_P (otype)
|
||||
&& TREE_CODE (expr) == ADDR_EXPR
|
||||
if (!(flag_strict_aliasing && POINTER_TYPE_P (type)
|
||||
&& POINTER_TYPE_P (otype) && !VOID_TYPE_P (TREE_TYPE (type))))
|
||||
return false;
|
||||
|
||||
if ((warn_strict_aliasing > 1) && TREE_CODE (expr) == ADDR_EXPR
|
||||
&& (DECL_P (TREE_OPERAND (expr, 0))
|
||||
|| handled_component_p (TREE_OPERAND (expr, 0)))
|
||||
&& !VOID_TYPE_P (TREE_TYPE (type)))
|
||||
|| handled_component_p (TREE_OPERAND (expr, 0))))
|
||||
{
|
||||
/* Casting the address of an object to non void pointer. Warn
|
||||
if the cast breaks type based aliasing. */
|
||||
if (!COMPLETE_TYPE_P (TREE_TYPE (type)))
|
||||
warning (OPT_Wstrict_aliasing, "type-punning to incomplete type "
|
||||
"might break strict-aliasing rules");
|
||||
if (!COMPLETE_TYPE_P (TREE_TYPE (type)) && warn_strict_aliasing == 2)
|
||||
{
|
||||
warning (OPT_Wstrict_aliasing, "type-punning to incomplete type "
|
||||
"might break strict-aliasing rules");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
HOST_WIDE_INT set1 = get_alias_set (TREE_TYPE (TREE_OPERAND (expr, 0)));
|
||||
/* warn_strict_aliasing >= 3. This includes the default (3).
|
||||
Only warn if the cast is dereferenced immediately. */
|
||||
HOST_WIDE_INT set1 =
|
||||
get_alias_set (TREE_TYPE (TREE_OPERAND (expr, 0)));
|
||||
HOST_WIDE_INT set2 = get_alias_set (TREE_TYPE (type));
|
||||
|
||||
if (!alias_sets_conflict_p (set1, set2))
|
||||
warning (OPT_Wstrict_aliasing, "dereferencing type-punned "
|
||||
"pointer will break strict-aliasing rules");
|
||||
else if (warn_strict_aliasing > 1
|
||||
&& !alias_sets_might_conflict_p (set1, set2))
|
||||
warning (OPT_Wstrict_aliasing, "dereferencing type-punned "
|
||||
"pointer might break strict-aliasing rules");
|
||||
{
|
||||
warning (OPT_Wstrict_aliasing, "dereferencing type-punned "
|
||||
"pointer will break strict-aliasing rules");
|
||||
return true;
|
||||
}
|
||||
else if (warn_strict_aliasing == 2
|
||||
&& !alias_sets_might_conflict_p (set1, set2))
|
||||
{
|
||||
warning (OPT_Wstrict_aliasing, "dereferencing type-punned "
|
||||
"pointer might break strict-aliasing rules");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if ((warn_strict_aliasing == 1) && !VOID_TYPE_P (TREE_TYPE (otype)))
|
||||
{
|
||||
/* At this level, warn for any conversions, even if an address is
|
||||
not taken in the same statement. This will likely produce many
|
||||
false positives, but could be useful to pinpoint problems that
|
||||
are not revealed at higher levels. */
|
||||
HOST_WIDE_INT set1 = get_alias_set (TREE_TYPE (otype));
|
||||
HOST_WIDE_INT set2 = get_alias_set (TREE_TYPE (type));
|
||||
if (!COMPLETE_TYPE_P(type)
|
||||
|| !alias_sets_might_conflict_p (set1, set2))
|
||||
{
|
||||
warning (OPT_Wstrict_aliasing, "dereferencing type-punned "
|
||||
"pointer might break strict-aliasing rules");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Print a warning about if (); or if () .. else; constructs
|
||||
|
@ -682,7 +682,7 @@ extern void binary_op_error (enum tree_code);
|
||||
extern tree fix_string_type (tree);
|
||||
struct varray_head_tag;
|
||||
extern void constant_expression_warning (tree);
|
||||
extern void strict_aliasing_warning (tree, tree, tree);
|
||||
extern bool strict_aliasing_warning (tree, tree, tree);
|
||||
extern void empty_if_body_warning (tree, tree);
|
||||
extern void warnings_for_convert_and_check (tree, tree, tree);
|
||||
extern tree convert_and_check (tree, tree);
|
||||
|
@ -400,7 +400,7 @@ c_common_handle_option (size_t scode, const char *arg, int value)
|
||||
if (c_dialect_cxx ())
|
||||
warn_sign_compare = value;
|
||||
warn_switch = value;
|
||||
warn_strict_aliasing = value;
|
||||
set_Wstrict_aliasing (value);
|
||||
warn_address = value;
|
||||
warn_strict_overflow = value;
|
||||
warn_array_bounds = value;
|
||||
|
@ -1876,6 +1876,19 @@ build_indirect_ref (tree ptr, const char *errorstring)
|
||||
|
||||
if (TREE_CODE (type) == POINTER_TYPE)
|
||||
{
|
||||
if (TREE_CODE (pointer) == CONVERT_EXPR
|
||||
|| TREE_CODE (pointer) == NOP_EXPR
|
||||
|| TREE_CODE (pointer) == VIEW_CONVERT_EXPR)
|
||||
{
|
||||
/* If a warning is issued, mark it to avoid duplicates from
|
||||
the backend. This only needs to be done at
|
||||
warn_strict_aliasing > 2. */
|
||||
if (warn_strict_aliasing > 2)
|
||||
if (strict_aliasing_warning (TREE_TYPE (TREE_OPERAND (pointer, 0)),
|
||||
type, TREE_OPERAND (pointer, 0)))
|
||||
TREE_NO_WARNING (pointer) = 1;
|
||||
}
|
||||
|
||||
if (TREE_CODE (pointer) == ADDR_EXPR
|
||||
&& (TREE_TYPE (TREE_OPERAND (pointer, 0))
|
||||
== TREE_TYPE (type)))
|
||||
@ -3574,7 +3587,8 @@ build_c_cast (tree type, tree expr)
|
||||
warning (OPT_Wint_to_pointer_cast, "cast to pointer from integer "
|
||||
"of different size");
|
||||
|
||||
strict_aliasing_warning (otype, type, expr);
|
||||
if (warn_strict_aliasing <= 2)
|
||||
strict_aliasing_warning (otype, type, expr);
|
||||
|
||||
/* If pedantic, warn for conversions between function and object
|
||||
pointer types, except for converting a null pointer constant
|
||||
|
@ -1,3 +1,10 @@
|
||||
2007-05-11 Silvius Rus <rus@google.com>
|
||||
|
||||
* cp/typeck.c (build_indirect_ref): Add call to
|
||||
strict_aliasing_warning.
|
||||
(build_reinterpret_cast_1): Condition call to
|
||||
strict_aliasing_warning.
|
||||
|
||||
2007-05-11 Jan Hubicka <jh@suse.cz>
|
||||
|
||||
* semantics.c (expand_or_defer_fn): Do not call c_record_cdtor_fn.
|
||||
|
@ -2333,6 +2333,19 @@ build_indirect_ref (tree ptr, const char *errorstring)
|
||||
types. */
|
||||
tree t = canonical_type_variant (TREE_TYPE (type));
|
||||
|
||||
if (TREE_CODE (ptr) == CONVERT_EXPR
|
||||
|| TREE_CODE (ptr) == NOP_EXPR
|
||||
|| TREE_CODE (ptr) == VIEW_CONVERT_EXPR)
|
||||
{
|
||||
/* If a warning is issued, mark it to avoid duplicates from
|
||||
the backend. This only needs to be done at
|
||||
warn_strict_aliasing > 2. */
|
||||
if (warn_strict_aliasing > 2)
|
||||
if (strict_aliasing_warning (TREE_TYPE (TREE_OPERAND (ptr, 0)),
|
||||
type, TREE_OPERAND (ptr, 0)))
|
||||
TREE_NO_WARNING (ptr) = 1;
|
||||
}
|
||||
|
||||
if (VOID_TYPE_P (t))
|
||||
{
|
||||
/* A pointer to incomplete type (other than cv void) can be
|
||||
@ -5289,7 +5302,8 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
|
||||
/* We need to strip nops here, because the front end likes to
|
||||
create (int *)&a for array-to-pointer decay, instead of &a[0]. */
|
||||
STRIP_NOPS (sexpr);
|
||||
strict_aliasing_warning (intype, type, sexpr);
|
||||
if (warn_strict_aliasing <= 2)
|
||||
strict_aliasing_warning (intype, type, sexpr);
|
||||
|
||||
return fold_if_not_in_template (build_nop (type, expr));
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ Objective-C and Objective-C++ Dialects}.
|
||||
-Wredundant-decls @gol
|
||||
-Wreturn-type -Wsequence-point -Wshadow @gol
|
||||
-Wsign-compare -Wstack-protector @gol
|
||||
-Wstrict-aliasing -Wstrict-aliasing=2 @gol
|
||||
-Wstrict-aliasing -Wstrict-aliasing=n @gol
|
||||
-Wstrict-overflow -Wstrict-overflow=@var{n} @gol
|
||||
-Wswitch -Wswitch-default -Wswitch-enum @gol
|
||||
-Wsystem-headers -Wtrigraphs -Wundef -Wuninitialized @gol
|
||||
@ -3008,14 +3008,40 @@ It warns about code which might break the strict aliasing rules that the
|
||||
compiler is using for optimization. The warning does not catch all
|
||||
cases, but does attempt to catch the more common pitfalls. It is
|
||||
included in @option{-Wall}.
|
||||
It is equivalent to -Wstrict-aliasing=3
|
||||
|
||||
@item -Wstrict-aliasing=2
|
||||
@opindex Wstrict-aliasing=2
|
||||
@item -Wstrict-aliasing=n
|
||||
@opindex Wstrict-aliasing=n
|
||||
This option is only active when @option{-fstrict-aliasing} is active.
|
||||
It warns about code which might break the strict aliasing rules that the
|
||||
compiler is using for optimization. This warning catches more cases than
|
||||
@option{-Wstrict-aliasing}, but it will also give a warning for some ambiguous
|
||||
cases that are safe.
|
||||
compiler is using for optimization.
|
||||
Higher levels correspond to higher accuracy (fewer false positives).
|
||||
Higher levels also correspond to more effort, similar to the way -O works.
|
||||
@option{-Wstrict-aliasing} is equivalent to @option{-Wstrict-aliasing=n},
|
||||
with n=3.
|
||||
|
||||
Level 1: Most aggressive, quick, least accurate.
|
||||
Possibly useful when higher levels
|
||||
do not warn but -fstrict-aliasing still breaks the code, as it has very few
|
||||
false negatives. However, it has many false positives.
|
||||
Warns for all pointer conversions between possibly incompatible types,
|
||||
even if never dereferenced. Runs in the frontend only.
|
||||
|
||||
Level 2: Aggressive, quick, 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).
|
||||
Unlike level 1, it only warns when an address is taken. Warns about
|
||||
incomplete types. Runs in the frontend only.
|
||||
|
||||
Level 3 (default for @option{-Wstrict-aliasing}):
|
||||
Should have very few false positives and few false
|
||||
negatives. Slightly slower than levels 1 or 2 when optimization is enabled.
|
||||
Takes care of the common punn+dereference pattern in the frontend:
|
||||
@code{*(int*)&some_float}.
|
||||
If optimization is enabled, it also runs in the backend, where it deals
|
||||
with multiple statement cases using flow-sensitive points-to information.
|
||||
Only warns when the converted pointer is dereferenced.
|
||||
Does not warn about incomplete types.
|
||||
|
||||
@item -Wstrict-overflow
|
||||
@item -Wstrict-overflow=@var{n}
|
||||
|
@ -122,6 +122,15 @@ extern bool extra_warnings;
|
||||
|
||||
extern void set_Wunused (int setting);
|
||||
|
||||
/* Used to set the level of -Wstrict-aliasing, when no level is specified.
|
||||
The external way to set the default level is to use
|
||||
-Wstrict-aliasing=level.
|
||||
ONOFF is assumed to take value 1 when -Wstrict-aliasing is specified,
|
||||
and 0 otherwise. After calling this function, wstrict_aliasing will be
|
||||
set to the default value of -Wstrict_aliasing=level. */
|
||||
|
||||
extern void set_Wstrict_aliasing (int onoff);
|
||||
|
||||
/* Nonzero means warn about any objects definitions whose size is larger
|
||||
than N bytes. Also want about function definitions whose returned
|
||||
values are larger than N bytes. The value N is in `larger_than_size'. */
|
||||
|
17
gcc/opts.c
17
gcc/opts.c
@ -1352,6 +1352,9 @@ common_handle_option (size_t scode, const char *arg, int value,
|
||||
break;
|
||||
|
||||
case OPT_Wstrict_aliasing:
|
||||
set_Wstrict_aliasing (value);
|
||||
break;
|
||||
|
||||
case OPT_Wstrict_aliasing_:
|
||||
warn_strict_aliasing = value;
|
||||
break;
|
||||
@ -1715,6 +1718,20 @@ set_Wunused (int setting)
|
||||
warn_unused_value = setting;
|
||||
}
|
||||
|
||||
/* Used to set the level of strict aliasing warnings,
|
||||
when no level is specified (i.e., when -Wstrict-aliasing, and not
|
||||
-Wstrict-aliasing=level was given).
|
||||
ONOFF is assumed to take value 1 when -Wstrict-aliasing is specified,
|
||||
and 0 otherwise. After calling this function, wstrict_aliasing will be
|
||||
set to the default value of -Wstrict_aliasing=level, currently 3. */
|
||||
void
|
||||
set_Wstrict_aliasing (int onoff)
|
||||
{
|
||||
gcc_assert (onoff == 0 || onoff == 1);
|
||||
if (onoff != 0)
|
||||
warn_strict_aliasing = 3;
|
||||
}
|
||||
|
||||
/* The following routines are useful in setting all the flags that
|
||||
-ffast-math and -fno-fast-math imply. */
|
||||
void
|
||||
|
@ -1,3 +1,22 @@
|
||||
2007-05-11 Silvius Rus <rus@google.com>
|
||||
|
||||
* gcc.dg/Wstrict-aliasing-bogus-const-ptr-nonconst-ptr.c: New test.
|
||||
* gcc.dg/Wstrict-aliasing-bogus-never-dereferenced.c: New test.
|
||||
* gcc.dg/Wstrict-aliasing-bogus-struct-included.c: New test.
|
||||
* gcc.dg/Wstrict-aliasing-converted-assigned.c: New test.
|
||||
* gcc.dg/Wstrict-aliasing-float-ptr-int-obj.c: New test.
|
||||
* gcc.dg/alias-1.c: Update option: -Wstrict-aliasing=2.
|
||||
* gcc.dg/alias-9.c: Update option: -Wstrict-aliasing=2.
|
||||
* g++.dg/warn/Wstrict-aliasing-7.C: Update option: -Wstrict-aliasing=2.
|
||||
* g++.dg/warn/Wstrict-aliasing-bogus-base-derived.C: New test.
|
||||
* g++.dg/warn/Wstrict-aliasing-bogus-char-1.C: New test.
|
||||
* g++.dg/warn/Wstrict-aliasing-bogus-const.C: New test.
|
||||
* g++.dg/warn/Wstrict-aliasing-bogus-nested-arrays.C: New test.
|
||||
* g++.dg/warn/Wstrict-aliasing-bogus-signed-unsigned.C: New test.
|
||||
* g++.dg/warn/Wstrict-aliasing-bogus-struct-included.C: New test.
|
||||
* g++.dg/warn/Wstrict-aliasing-bogus-union.C: New test.
|
||||
* g++.dg/warn/Wstrict-aliasing-float-ref-int-obj.C: New test.
|
||||
|
||||
2007-05-11 Paolo Carlini <pcarlini@suse.de>
|
||||
|
||||
PR other/31852
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-Wstrict-aliasing -O2" } */
|
||||
/* { dg-options "-Wstrict-aliasing=2 -O2" } */
|
||||
|
||||
int a[2];
|
||||
|
||||
|
@ -0,0 +1,21 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
|
||||
class base {
|
||||
public:
|
||||
int x;
|
||||
};
|
||||
|
||||
class derived: public base {
|
||||
public:
|
||||
int y;
|
||||
};
|
||||
|
||||
base foo () {
|
||||
derived d;
|
||||
base* pb = &d; /* { dg-bogus "base vs. derived" } */
|
||||
pb->x = 1;
|
||||
|
||||
return d;
|
||||
}
|
10
gcc/testsuite/g++.dg/warn/Wstrict-aliasing-bogus-char-1.C
Normal file
10
gcc/testsuite/g++.dg/warn/Wstrict-aliasing-bogus-char-1.C
Normal file
@ -0,0 +1,10 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
|
||||
int foo () {
|
||||
int i;
|
||||
char* c= reinterpret_cast<char*>(&i); /* { dg-bogus "char" } */
|
||||
c[1] = 1;
|
||||
return i;
|
||||
}
|
9
gcc/testsuite/g++.dg/warn/Wstrict-aliasing-bogus-const.C
Normal file
9
gcc/testsuite/g++.dg/warn/Wstrict-aliasing-bogus-const.C
Normal file
@ -0,0 +1,9 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
int foo(int* p) {
|
||||
const int& q = *p; /* { dg-bogus "const vs. non-const" } */
|
||||
*p = 1;
|
||||
return q;
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
int foo () {
|
||||
int buffer[10][10];
|
||||
int* pi = &buffer[0][0]; /* { dg-bogus "same element type" } */
|
||||
*pi = 10;
|
||||
return buffer[0][0];
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
|
||||
int foo () {
|
||||
int i;
|
||||
unsigned int* pu = reinterpret_cast<unsigned int*> (&i); /* { dg-bogus "signed vs. unsigned" } */
|
||||
*pu = 1000000;
|
||||
return i;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
|
||||
struct S {
|
||||
int i;
|
||||
float f;
|
||||
};
|
||||
|
||||
int foo () {
|
||||
struct S s;
|
||||
s.i = 7;
|
||||
float* f = &s.f; /* { dg-bogus "float included in struct S" } */
|
||||
*f = 2.0;
|
||||
return s.i + (int)s.f;
|
||||
}
|
15
gcc/testsuite/g++.dg/warn/Wstrict-aliasing-bogus-union.C
Normal file
15
gcc/testsuite/g++.dg/warn/Wstrict-aliasing-bogus-union.C
Normal file
@ -0,0 +1,15 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
|
||||
union U {
|
||||
int i;
|
||||
float f;
|
||||
};
|
||||
|
||||
float foo () {
|
||||
union U u;
|
||||
float* f = &u.f; /* { dg-bogus "unions are holy in GCC" } */
|
||||
u.i = 2;
|
||||
return *f;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
|
||||
int foo() {
|
||||
int x;
|
||||
float& q = reinterpret_cast<float&> (x); /* { dg-warning "type-punn" } */
|
||||
q = 1.0;
|
||||
return x;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
const int* foo (void)
|
||||
{
|
||||
int *i;
|
||||
const int** cpi = (const int**) &i; /* { dg-bogus "const vs. non-const" } */
|
||||
i = 0;
|
||||
return *cpi;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
|
||||
int foo ()
|
||||
{
|
||||
int x = 10;
|
||||
int *p;
|
||||
float *q;
|
||||
|
||||
q = (float*) &x; /* { dg-bogus "not referenced" } */
|
||||
|
||||
return x;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
|
||||
struct U
|
||||
{
|
||||
float f;
|
||||
int i;
|
||||
};
|
||||
|
||||
|
||||
int foo ()
|
||||
{
|
||||
struct U u;
|
||||
float *pf = (float*)&u; /* { dg-bogus "float included in struct U" } */
|
||||
*pf = 2.0;
|
||||
return u.i;
|
||||
}
|
10
gcc/testsuite/gcc.dg/Wstrict-aliasing-converted-assigned.c
Normal file
10
gcc/testsuite/gcc.dg/Wstrict-aliasing-converted-assigned.c
Normal file
@ -0,0 +1,10 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
|
||||
int foo()
|
||||
{
|
||||
int i;
|
||||
*(long*)&i = 0; /* { dg-warning "type-punn" } */
|
||||
return i;
|
||||
}
|
23
gcc/testsuite/gcc.dg/Wstrict-aliasing-float-ptr-int-obj.c
Normal file
23
gcc/testsuite/gcc.dg/Wstrict-aliasing-float-ptr-int-obj.c
Normal file
@ -0,0 +1,23 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O -Wstrict-aliasing -fstrict-aliasing" } */
|
||||
|
||||
extern int flag;
|
||||
|
||||
int foo() {
|
||||
|
||||
int x;
|
||||
int y = 9;
|
||||
float* q;
|
||||
float* r;
|
||||
|
||||
if (flag) {
|
||||
q = (float*) &x; /* { dg-warning "type-punn" } */
|
||||
} else {
|
||||
q = (float*) &y; /* { dg-warning "type-punn" } */
|
||||
}
|
||||
|
||||
*q = 1.0;
|
||||
|
||||
return x;
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-Wstrict-aliasing -fstrict-aliasing" }
|
||||
// { dg-options "-Wstrict-aliasing=2 -fstrict-aliasing" }
|
||||
|
||||
// Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
// Contributed by Nathan Sidwell 29 Sep 2002 <nathan@codesourcery.com>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-Wstrict-aliasing -O2" } */
|
||||
/* { dg-options "-Wstrict-aliasing=2 -O2" } */
|
||||
|
||||
int a[2];
|
||||
|
||||
|
@ -860,6 +860,8 @@ extern void debug_all_mem_sym_stats (void);
|
||||
definition, a function with this prototype is called. */
|
||||
typedef bool (*walk_use_def_chains_fn) (tree, tree, void *);
|
||||
|
||||
/* In tree-ssa-alias-warnings.c */
|
||||
extern void strict_aliasing_warning_backend (void);
|
||||
|
||||
/* In tree-ssa.c */
|
||||
extern void init_tree_ssa (void);
|
||||
|
991
gcc/tree-ssa-alias-warnings.c
Normal file
991
gcc/tree-ssa-alias-warnings.c
Normal file
@ -0,0 +1,991 @@
|
||||
/* 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 *found;
|
||||
void **slot = NULL;
|
||||
slot = htab_find_slot (ref_map, &key, 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->base.from = t;
|
||||
tp->to = NULL_TREE;
|
||||
slot = htab_find_slot (ht, &t, 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_base_hash, tree_map_eq, NULL);
|
||||
ref_table->ptrs = htab_create (10, tree_map_base_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.base.from = TREE_OPERAND (*tp, 0);
|
||||
maybe_add_match (reference_table (true)->ptrs, &match);
|
||||
}
|
||||
else
|
||||
{
|
||||
match.base.from = *tp;
|
||||
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 (DECL_P (*t) && DECL_ARTIFICIAL (*t))
|
||||
return *t;
|
||||
else
|
||||
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 "*" 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);
|
||||
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 (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 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;
|
||||
|
||||
/* 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 (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 = NULL;
|
||||
|
||||
if (!seen)
|
||||
seen = htab_create (10, tree_map_base_hash, tree_map_eq, NULL);
|
||||
|
||||
slot = htab_find_slot (seen, &func, INSERT);
|
||||
gcc_assert (slot);
|
||||
|
||||
if (*slot)
|
||||
return true;
|
||||
|
||||
gcc_assert (slot);
|
||||
*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))
|
||||
{
|
||||
detect_strict_aliasing_named ();
|
||||
maybe_free_reference_table ();
|
||||
}
|
||||
}
|
@ -97,6 +97,7 @@ static void set_pt_anything (tree);
|
||||
void debug_mp_info (VEC(mem_sym_stats_t,heap) *);
|
||||
|
||||
|
||||
|
||||
/* Return memory reference stats for symbol VAR. Create a new slot in
|
||||
cfun->gimple_df->mem_sym_stats if needed. */
|
||||
|
||||
@ -1605,6 +1606,9 @@ compute_may_aliases (void)
|
||||
dump_referenced_vars (dump_file);
|
||||
}
|
||||
|
||||
/* Report strict aliasing violations. */
|
||||
strict_aliasing_warning_backend ();
|
||||
|
||||
/* Deallocate memory used by aliasing data structures. */
|
||||
delete_alias_info (ai);
|
||||
|
||||
@ -3932,7 +3936,7 @@ create_structure_vars (void)
|
||||
update_stmt (stmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user