aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-uninit.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-ssa-uninit.c')
-rw-r--r--gcc/tree-ssa-uninit.c1447
1 files changed, 0 insertions, 1447 deletions
diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c
deleted file mode 100644
index 02e88d5..0000000
--- a/gcc/tree-ssa-uninit.c
+++ /dev/null
@@ -1,1447 +0,0 @@
-/* Predicate aware uninitialized variable warning.
- Copyright (C) 2001-2022 Free Software Foundation, Inc.
- Contributed by Xinliang David Li <davidxl@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 3, 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 COPYING3. If not see
-<http://www.gnu.org/licenses/>. */
-
-#define INCLUDE_STRING
-#include "config.h"
-#include "system.h"
-#include "coretypes.h"
-#include "backend.h"
-#include "tree.h"
-#include "gimple.h"
-#include "tree-pass.h"
-#include "ssa.h"
-#include "gimple-pretty-print.h"
-#include "diagnostic-core.h"
-#include "fold-const.h"
-#include "gimple-iterator.h"
-#include "tree-ssa.h"
-#include "tree-cfg.h"
-#include "cfghooks.h"
-#include "attribs.h"
-#include "builtins.h"
-#include "calls.h"
-#include "gimple-range.h"
-
-#include "gimple-predicate-analysis.h"
-
-/* This implements the pass that does predicate aware warning on uses of
- possibly uninitialized variables. The pass first collects the set of
- possibly uninitialized SSA names. For each such name, it walks through
- all its immediate uses. For each immediate use, it rebuilds the condition
- expression (the predicate) that guards the use. The predicate is then
- examined to see if the variable is always defined under that same condition.
- This is done either by pruning the unrealizable paths that lead to the
- default definitions or by checking if the predicate set that guards the
- defining paths is a superset of the use predicate. */
-
-/* Pointer set of potentially undefined ssa names, i.e.,
- ssa names that are defined by phi with operands that
- are not defined or potentially undefined. */
-static hash_set<tree> *possibly_undefined_names = 0;
-
-/* Returns the first bit position (starting from LSB)
- in mask that is non zero. Returns -1 if the mask is empty. */
-static int
-get_mask_first_set_bit (unsigned mask)
-{
- int pos = 0;
- if (mask == 0)
- return -1;
-
- while ((mask & (1 << pos)) == 0)
- pos++;
-
- return pos;
-}
-#define MASK_FIRST_SET_BIT(mask) get_mask_first_set_bit (mask)
-
-/* Return true if T, an SSA_NAME, has an undefined value. */
-static bool
-has_undefined_value_p (tree t)
-{
- return (ssa_undefined_value_p (t)
- || (possibly_undefined_names
- && possibly_undefined_names->contains (t)));
-}
-
-/* Return true if EXPR should suppress either uninitialized warning. */
-
-static inline bool
-get_no_uninit_warning (tree expr)
-{
- return warning_suppressed_p (expr, OPT_Wuninitialized);
-}
-
-/* Suppress both uninitialized warnings for EXPR. */
-
-static inline void
-set_no_uninit_warning (tree expr)
-{
- suppress_warning (expr, OPT_Wuninitialized);
-}
-
-/* Like has_undefined_value_p, but don't return true if the no-warning
- bit is set on SSA_NAME_VAR for either uninit warning. */
-
-static inline bool
-uninit_undefined_value_p (tree t)
-{
- if (!has_undefined_value_p (t))
- return false;
- if (!SSA_NAME_VAR (t))
- return true;
- return !get_no_uninit_warning (SSA_NAME_VAR (t));
-}
-
-/* Emit warnings for uninitialized variables. This is done in two passes.
-
- The first pass notices real uses of SSA names with undefined values.
- Such uses are unconditionally uninitialized, and we can be certain that
- such a use is a mistake. This pass is run before most optimizations,
- so that we catch as many as we can.
-
- The second pass follows PHI nodes to find uses that are potentially
- uninitialized. In this case we can't necessarily prove that the use
- is really uninitialized. This pass is run after most optimizations,
- so that we thread as many jumps and possible, and delete as much dead
- code as possible, in order to reduce false positives. We also look
- again for plain uninitialized variables, since optimization may have
- changed conditionally uninitialized to unconditionally uninitialized. */
-
-/* Emit warning OPT for variable VAR at the point in the program where
- the SSA_NAME T is being used uninitialized. The warning text is in
- MSGID and STMT is the statement that does the uninitialized read.
- PHI_ARG_LOC is the location of the PHI argument if T and VAR are one,
- or UNKNOWN_LOCATION otherwise. */
-
-static void
-warn_uninit (opt_code opt, tree t, tree var, gimple *context,
- location_t phi_arg_loc = UNKNOWN_LOCATION)
-{
- /* Bail if the value isn't provably uninitialized. */
- if (!has_undefined_value_p (t))
- return;
-
- /* Ignore COMPLEX_EXPR as initializing only a part of a complex
- turns in a COMPLEX_EXPR with the not initialized part being
- set to its previous (undefined) value. */
- if (is_gimple_assign (context)
- && gimple_assign_rhs_code (context) == COMPLEX_EXPR)
- return;
-
- /* Ignore REALPART_EXPR or IMAGPART_EXPR if its operand is a call to
- .DEFERRED_INIT. This is for handling the following case correctly:
-
- 1 typedef _Complex float C;
- 2 C foo (int cond)
- 3 {
- 4 C f;
- 5 __imag__ f = 0;
- 6 if (cond)
- 7 {
- 8 __real__ f = 1;
- 9 return f;
- 10 }
- 11 return f;
- 12 }
-
- with -ftrivial-auto-var-init, compiler will insert the following
- artificial initialization at line 4:
- f = .DEFERRED_INIT (f, 2);
- _1 = REALPART_EXPR <f>;
-
- without the following special handling, _1 = REALPART_EXPR <f> will
- be treated as the uninitialized use point, which is incorrect. (the
- real uninitialized use point is at line 11). */
- if (is_gimple_assign (context)
- && (gimple_assign_rhs_code (context) == REALPART_EXPR
- || gimple_assign_rhs_code (context) == IMAGPART_EXPR))
- {
- tree v = gimple_assign_rhs1 (context);
- if (TREE_CODE (TREE_OPERAND (v, 0)) == SSA_NAME
- && gimple_call_internal_p (SSA_NAME_DEF_STMT (TREE_OPERAND (v, 0)),
- IFN_DEFERRED_INIT))
- return;
- }
-
- /* Anonymous SSA_NAMEs shouldn't be uninitialized, but ssa_undefined_value_p
- can return true if the def stmt of an anonymous SSA_NAME is
- 1. A COMPLEX_EXPR created for conversion from scalar to complex. Use the
- underlying var of the COMPLEX_EXPRs real part in that case. See PR71581.
-
- Or
-
- 2. A call to .DEFERRED_INIT internal function. Since the original variable
- has been eliminated by optimziation, we need to get the variable name,
- and variable declaration location from this call. We recorded variable
- name into VAR_NAME_STR, and will get location info and record warning
- suppressed info to VAR_DEF_STMT, which is the .DEFERRED_INIT call. */
-
- const char *var_name_str = NULL;
- gimple *var_def_stmt = NULL;
-
- if (!var && !SSA_NAME_VAR (t))
- {
- var_def_stmt = SSA_NAME_DEF_STMT (t);
-
- if (is_gimple_assign (var_def_stmt)
- && gimple_assign_rhs_code (var_def_stmt) == COMPLEX_EXPR)
- {
- tree v = gimple_assign_rhs1 (var_def_stmt);
- if (TREE_CODE (v) == SSA_NAME
- && has_undefined_value_p (v)
- && zerop (gimple_assign_rhs2 (var_def_stmt)))
- var = SSA_NAME_VAR (v);
- }
-
- if (gimple_call_internal_p (var_def_stmt, IFN_DEFERRED_INIT))
- {
- /* Ignore the call to .DEFERRED_INIT that define the original
- var itself as the following case:
- temp = .DEFERRED_INIT (4, 2, “alt_reloc");
- alt_reloc = temp;
- In order to avoid generating warning for the fake usage
- at alt_reloc = temp.
- */
- tree lhs_var = NULL_TREE;
- tree lhs_var_name = NULL_TREE;
- const char *lhs_var_name_str = NULL;
-
- /* Get the variable name from the 3rd argument of call. */
- tree var_name = gimple_call_arg (var_def_stmt, 2);
- var_name = TREE_OPERAND (TREE_OPERAND (var_name, 0), 0);
- var_name_str = TREE_STRING_POINTER (var_name);
-
- if (is_gimple_assign (context))
- {
- if (TREE_CODE (gimple_assign_lhs (context)) == VAR_DECL)
- lhs_var = gimple_assign_lhs (context);
- else if (TREE_CODE (gimple_assign_lhs (context)) == SSA_NAME)
- lhs_var = SSA_NAME_VAR (gimple_assign_lhs (context));
- }
- if (lhs_var
- && (lhs_var_name = DECL_NAME (lhs_var))
- && (lhs_var_name_str = IDENTIFIER_POINTER (lhs_var_name))
- && (strcmp (lhs_var_name_str, var_name_str) == 0))
- return;
- gcc_assert (var_name_str && var_def_stmt);
- }
- }
-
- if (var == NULL_TREE && var_name_str == NULL)
- return;
-
- /* Avoid warning if we've already done so or if the warning has been
- suppressed. */
- if (((warning_suppressed_p (context, OPT_Wuninitialized)
- || (gimple_assign_single_p (context)
- && get_no_uninit_warning (gimple_assign_rhs1 (context)))))
- || (var && get_no_uninit_warning (var))
- || (var_name_str
- && warning_suppressed_p (var_def_stmt, OPT_Wuninitialized)))
- return;
-
- /* Use either the location of the read statement or that of the PHI
- argument, or that of the uninitialized variable, in that order,
- whichever is valid. */
- location_t location = UNKNOWN_LOCATION;
- if (gimple_has_location (context))
- location = gimple_location (context);
- else if (phi_arg_loc != UNKNOWN_LOCATION)
- location = phi_arg_loc;
- else if (var)
- location = DECL_SOURCE_LOCATION (var);
- else if (var_name_str)
- location = gimple_location (var_def_stmt);
-
- location = linemap_resolve_location (line_table, location,
- LRK_SPELLING_LOCATION, NULL);
-
- auto_diagnostic_group d;
- gcc_assert (opt == OPT_Wuninitialized || opt == OPT_Wmaybe_uninitialized);
- if (var)
- {
- if ((opt == OPT_Wuninitialized
- && !warning_at (location, opt, "%qD is used uninitialized", var))
- || (opt == OPT_Wmaybe_uninitialized
- && !warning_at (location, opt, "%qD may be used uninitialized",
- var)))
- return;
- }
- else if (var_name_str)
- {
- if ((opt == OPT_Wuninitialized
- && !warning_at (location, opt, "%qs is used uninitialized",
- var_name_str))
- || (opt == OPT_Wmaybe_uninitialized
- && !warning_at (location, opt, "%qs may be used uninitialized",
- var_name_str)))
- return;
- }
-
- /* Avoid subsequent warnings for reads of the same variable again. */
- if (var)
- suppress_warning (var, opt);
- else if (var_name_str)
- suppress_warning (var_def_stmt, opt);
-
- /* Issue a note pointing to the read variable unless the warning
- is at the same location. */
- location_t var_loc = var ? DECL_SOURCE_LOCATION (var)
- : gimple_location (var_def_stmt);
- if (location == var_loc)
- return;
-
- if (var)
- inform (var_loc, "%qD was declared here", var);
- else if (var_name_str)
- inform (var_loc, "%qs was declared here", var_name_str);
-}
-
-struct check_defs_data
-{
- /* If we found any may-defs besides must-def clobbers. */
- bool found_may_defs;
-};
-
-/* Return true if STMT is a call to built-in function all of whose
- by-reference arguments are const-qualified (i.e., the function can
- be assumed not to modify them). */
-
-static bool
-builtin_call_nomodifying_p (gimple *stmt)
-{
- if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
- return false;
-
- tree fndecl = gimple_call_fndecl (stmt);
- if (!fndecl)
- return false;
-
- tree fntype = TREE_TYPE (fndecl);
- if (!fntype)
- return false;
-
- /* Check the called function's signature for non-constc pointers.
- If one is found, return false. */
- unsigned argno = 0;
- tree argtype;
- function_args_iterator it;
- FOREACH_FUNCTION_ARGS (fntype, argtype, it)
- {
- if (VOID_TYPE_P (argtype))
- return true;
-
- ++argno;
-
- if (!POINTER_TYPE_P (argtype))
- continue;
-
- if (TYPE_READONLY (TREE_TYPE (argtype)))
- continue;
-
- return false;
- }
-
- /* If the number of actual arguments to the call is less than or
- equal to the number of parameters, return false. */
- unsigned nargs = gimple_call_num_args (stmt);
- if (nargs <= argno)
- return false;
-
- /* Check arguments passed through the ellipsis in calls to variadic
- functions for pointers. If one is found that's a non-constant
- pointer, return false. */
- for (; argno < nargs; ++argno)
- {
- tree arg = gimple_call_arg (stmt, argno);
- argtype = TREE_TYPE (arg);
- if (!POINTER_TYPE_P (argtype))
- continue;
-
- if (TYPE_READONLY (TREE_TYPE (argtype)))
- continue;
-
- return false;
- }
-
- return true;
-}
-
-/* If ARG is a FNDECL parameter declared with attribute access none or
- write_only issue a warning for its read access via PTR. */
-
-static void
-maybe_warn_read_write_only (tree fndecl, gimple *stmt, tree arg, tree ptr)
-{
- if (!fndecl)
- return;
-
- if (get_no_uninit_warning (arg))
- return;
-
- tree fntype = TREE_TYPE (fndecl);
- if (!fntype)
- return;
-
- /* Initialize a map of attribute access specifications for arguments
- to the function function call. */
- rdwr_map rdwr_idx;
- init_attr_rdwr_indices (&rdwr_idx, TYPE_ATTRIBUTES (fntype));
-
- unsigned argno = 0;
- tree parms = DECL_ARGUMENTS (fndecl);
- for (tree parm = parms; parm; parm = TREE_CHAIN (parm), ++argno)
- {
- if (parm != arg)
- continue;
-
- const attr_access* access = rdwr_idx.get (argno);
- if (!access)
- break;
-
- if (access->mode != access_none
- && access->mode != access_write_only)
- continue;
-
- location_t stmtloc
- = linemap_resolve_location (line_table, gimple_location (stmt),
- LRK_SPELLING_LOCATION, NULL);
-
- if (!warning_at (stmtloc, OPT_Wmaybe_uninitialized,
- "%qE may be used uninitialized", ptr))
- break;
-
- suppress_warning (arg, OPT_Wmaybe_uninitialized);
-
- const char* const access_str =
- TREE_STRING_POINTER (access->to_external_string ());
-
- location_t parmloc = DECL_SOURCE_LOCATION (parm);
- inform (parmloc, "accessing argument %u of a function declared with "
- "attribute %<%s%>",
- argno + 1, access_str);
-
- break;
- }
-}
-
-/* Callback for walk_aliased_vdefs. */
-
-static bool
-check_defs (ao_ref *ref, tree vdef, void *data_)
-{
- check_defs_data *data = (check_defs_data *)data_;
- gimple *def_stmt = SSA_NAME_DEF_STMT (vdef);
-
- /* Ignore the vdef if the definition statement is a call
- to .DEFERRED_INIT function. */
- if (gimple_call_internal_p (def_stmt, IFN_DEFERRED_INIT))
- return false;
-
- /* For address taken variable, a temporary variable is added between
- the variable and the call to .DEFERRED_INIT function as:
- _1 = .DEFERRED_INIT (4, 2, &"i1"[0]);
- i1 = _1;
- Ignore this vdef as well. */
- if (is_gimple_assign (def_stmt)
- && gimple_assign_rhs_code (def_stmt) == SSA_NAME)
- {
- tree tmp_var = gimple_assign_rhs1 (def_stmt);
- if (gimple_call_internal_p (SSA_NAME_DEF_STMT (tmp_var),
- IFN_DEFERRED_INIT))
- return false;
- }
-
- /* The ASAN_MARK intrinsic doesn't modify the variable. */
- if (is_gimple_call (def_stmt))
- {
- /* The ASAN_MARK intrinsic doesn't modify the variable. */
- if (gimple_call_internal_p (def_stmt)
- && gimple_call_internal_fn (def_stmt) == IFN_ASAN_MARK)
- return false;
-
- if (tree fndecl = gimple_call_fndecl (def_stmt))
- {
- /* Some sanitizer calls pass integer arguments to built-ins
- that expect pointets. Avoid using gimple_call_builtin_p()
- which fails for such calls. */
- if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
- {
- built_in_function fncode = DECL_FUNCTION_CODE (fndecl);
- if (fncode > BEGIN_SANITIZER_BUILTINS
- && fncode < END_SANITIZER_BUILTINS)
- return false;
- }
- }
- }
-
- /* End of VLA scope is not a kill. */
- if (gimple_call_builtin_p (def_stmt, BUILT_IN_STACK_RESTORE))
- return false;
-
- /* If this is a clobber then if it is not a kill walk past it. */
- if (gimple_clobber_p (def_stmt))
- {
- if (stmt_kills_ref_p (def_stmt, ref))
- return true;
- return false;
- }
-
- if (builtin_call_nomodifying_p (def_stmt))
- return false;
-
- /* Found a may-def on this path. */
- data->found_may_defs = true;
- return true;
-}
-
-/* Counters and limits controlling the the depth of analysis and
- strictness of the warning. */
-struct wlimits
-{
- /* Number of VDEFs encountered. */
- unsigned int vdef_cnt;
- /* Number of statements examined by walk_aliased_vdefs. */
- unsigned int oracle_cnt;
- /* Limit on the number of statements visited by walk_aliased_vdefs. */
- unsigned limit;
- /* Set when basic block with statement is executed unconditionally. */
- bool always_executed;
- /* Set to issue -Wmaybe-uninitialized. */
- bool wmaybe_uninit;
-};
-
-/* Determine if REF references an uninitialized operand and diagnose
- it if so. STMS is the referencing statement. LHS is the result
- of the access and may be null. RHS is the variable referenced by
- the access; it may not be null. */
-
-static tree
-maybe_warn_operand (ao_ref &ref, gimple *stmt, tree lhs, tree rhs,
- wlimits &wlims)
-{
- bool has_bit_insert = false;
- use_operand_p luse_p;
- imm_use_iterator liter;
-
- if (get_no_uninit_warning (rhs))
- return NULL_TREE;
-
- /* Do not warn if the base was marked so or this is a
- hard register var. */
- tree base = ao_ref_base (&ref);
- if ((VAR_P (base)
- && DECL_HARD_REGISTER (base))
- || get_no_uninit_warning (base))
- return NULL_TREE;
-
- /* Do not warn if the access is zero size or if it's fully outside
- the object. */
- poly_int64 decl_size;
- if (known_size_p (ref.size)
- && known_eq (ref.max_size, ref.size)
- && (known_eq (ref.size, 0)
- || known_le (ref.offset + ref.size, 0)))
- return NULL_TREE;
-
- if (DECL_P (base)
- && known_ge (ref.offset, 0)
- && DECL_SIZE (base)
- && poly_int_tree_p (DECL_SIZE (base), &decl_size)
- && known_le (decl_size, ref.offset))
- return NULL_TREE;
-
- /* Do not warn if the result of the access is then used for
- a BIT_INSERT_EXPR. */
- if (lhs && TREE_CODE (lhs) == SSA_NAME)
- FOR_EACH_IMM_USE_FAST (luse_p, liter, lhs)
- {
- gimple *use_stmt = USE_STMT (luse_p);
- /* BIT_INSERT_EXPR first operand should not be considered
- a use for the purpose of uninit warnings. */
- if (gassign *ass = dyn_cast <gassign *> (use_stmt))
- {
- if (gimple_assign_rhs_code (ass) == BIT_INSERT_EXPR
- && luse_p->use == gimple_assign_rhs1_ptr (ass))
- {
- has_bit_insert = true;
- break;
- }
- }
- }
-
- if (has_bit_insert)
- return NULL_TREE;
-
- /* Limit the walking to a constant number of stmts after
- we overcommit quadratic behavior for small functions
- and O(n) behavior. */
- if (wlims.oracle_cnt > 128 * 128
- && wlims.oracle_cnt > wlims.vdef_cnt * 2)
- wlims.limit = 32;
-
- check_defs_data data;
- bool fentry_reached = false;
- data.found_may_defs = false;
- tree use = gimple_vuse (stmt);
- if (!use)
- return NULL_TREE;
- int res = walk_aliased_vdefs (&ref, use,
- check_defs, &data, NULL,
- &fentry_reached, wlims.limit);
- if (res == -1)
- {
- wlims.oracle_cnt += wlims.limit;
- return NULL_TREE;
- }
-
- wlims.oracle_cnt += res;
- if (data.found_may_defs)
- return NULL_TREE;
-
- bool found_alloc = false;
-
- if (fentry_reached)
- {
- if (TREE_CODE (base) == MEM_REF)
- base = TREE_OPERAND (base, 0);
-
- /* Follow the chain of SSA_NAME assignments looking for an alloca
- call (or VLA) or malloc/realloc, or for decls. If any is found
- (and in the latter case, the operand is a local variable) issue
- a warning. */
- while (TREE_CODE (base) == SSA_NAME)
- {
- gimple *def_stmt = SSA_NAME_DEF_STMT (base);
-
- if (is_gimple_call (def_stmt)
- && gimple_call_builtin_p (def_stmt))
- {
- /* Detect uses of uninitialized alloca/VLAs. */
- tree fndecl = gimple_call_fndecl (def_stmt);
- const built_in_function fncode = DECL_FUNCTION_CODE (fndecl);
- if (fncode == BUILT_IN_ALLOCA
- || fncode == BUILT_IN_ALLOCA_WITH_ALIGN
- || fncode == BUILT_IN_MALLOC)
- found_alloc = true;
- break;
- }
-
- if (!is_gimple_assign (def_stmt))
- break;
-
- tree_code code = gimple_assign_rhs_code (def_stmt);
- if (code != ADDR_EXPR && code != POINTER_PLUS_EXPR)
- break;
-
- base = gimple_assign_rhs1 (def_stmt);
- if (TREE_CODE (base) == ADDR_EXPR)
- base = TREE_OPERAND (base, 0);
-
- if (DECL_P (base)
- || TREE_CODE (base) == COMPONENT_REF)
- rhs = base;
-
- if (TREE_CODE (base) == MEM_REF)
- base = TREE_OPERAND (base, 0);
-
- if (tree ba = get_base_address (base))
- base = ba;
- }
-
- /* Replace the RHS expression with BASE so that it
- refers to it in the diagnostic (instead of to
- '<unknown>'). */
- if (DECL_P (base)
- && EXPR_P (rhs)
- && TREE_CODE (rhs) != COMPONENT_REF)
- rhs = base;
- }
-
- /* Do not warn if it can be initialized outside this function.
- If we did not reach function entry then we found killing
- clobbers on all paths to entry. */
- if (!found_alloc && fentry_reached)
- {
- if (TREE_CODE (base) == SSA_NAME)
- {
- tree var = SSA_NAME_VAR (base);
- if (var && TREE_CODE (var) == PARM_DECL)
- {
- maybe_warn_read_write_only (cfun->decl, stmt, var, rhs);
- return NULL_TREE;
- }
- }
-
- if (!VAR_P (base)
- || is_global_var (base))
- /* ??? We'd like to use ref_may_alias_global_p but that
- excludes global readonly memory and thus we get bogus
- warnings from p = cond ? "a" : "b" for example. */
- return NULL_TREE;
- }
-
- /* Strip the address-of expression from arrays passed to functions. */
- if (TREE_CODE (rhs) == ADDR_EXPR)
- rhs = TREE_OPERAND (rhs, 0);
-
- /* Check again since RHS may have changed above. */
- if (get_no_uninit_warning (rhs))
- return NULL_TREE;
-
- /* Avoid warning about empty types such as structs with no members.
- The first_field() test is important for C++ where the predicate
- alone isn't always sufficient. */
- tree rhstype = TREE_TYPE (rhs);
- if (POINTER_TYPE_P (rhstype))
- rhstype = TREE_TYPE (rhstype);
- if (is_empty_type (rhstype))
- return NULL_TREE;
-
- bool warned = false;
- /* We didn't find any may-defs so on all paths either
- reached function entry or a killing clobber. */
- location_t location
- = linemap_resolve_location (line_table, gimple_location (stmt),
- LRK_SPELLING_LOCATION, NULL);
- if (wlims.always_executed)
- {
- if (warning_at (location, OPT_Wuninitialized,
- "%qE is used uninitialized", rhs))
- {
- /* ??? This is only effective for decls as in
- gcc.dg/uninit-B-O0.c. Avoid doing this for maybe-uninit
- uses or accesses by functions as it may hide important
- locations. */
- if (lhs)
- set_no_uninit_warning (rhs);
- warned = true;
- }
- }
- else if (wlims.wmaybe_uninit)
- warned = warning_at (location, OPT_Wmaybe_uninitialized,
- "%qE may be used uninitialized", rhs);
-
- return warned ? base : NULL_TREE;
-}
-
-
-/* Diagnose passing addresses of uninitialized objects to either const
- pointer arguments to functions, or to functions declared with attribute
- access implying read access to those objects. */
-
-static void
-maybe_warn_pass_by_reference (gcall *stmt, wlimits &wlims)
-{
- if (!wlims.wmaybe_uninit)
- return;
-
- unsigned nargs = gimple_call_num_args (stmt);
- if (!nargs)
- return;
-
- tree fndecl = gimple_call_fndecl (stmt);
- tree fntype = gimple_call_fntype (stmt);
- if (!fntype)
- return;
-
- /* Const function do not read their arguments. */
- if (gimple_call_flags (stmt) & ECF_CONST)
- return;
-
- const built_in_function fncode
- = (fndecl && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
- ? DECL_FUNCTION_CODE (fndecl) : (built_in_function)BUILT_IN_LAST);
-
- if (fncode == BUILT_IN_MEMCPY || fncode == BUILT_IN_MEMMOVE)
- /* Avoid diagnosing calls to raw memory functions (this is overly
- permissive; consider tightening it up). */
- return;
-
- /* Save the current warning setting and replace it either a "maybe"
- when passing addresses of uninitialized variables to const-qualified
- pointers or arguments declared with attribute read_write, or with
- a "certain" when passing them to arguments declared with attribute
- read_only. */
- const bool save_always_executed = wlims.always_executed;
-
- /* Initialize a map of attribute access specifications for arguments
- to the function function call. */
- rdwr_map rdwr_idx;
- init_attr_rdwr_indices (&rdwr_idx, TYPE_ATTRIBUTES (fntype));
-
- tree argtype;
- unsigned argno = 0;
- function_args_iterator it;
-
- FOREACH_FUNCTION_ARGS (fntype, argtype, it)
- {
- ++argno;
-
- if (!POINTER_TYPE_P (argtype))
- continue;
-
- tree access_size = NULL_TREE;
- const attr_access* access = rdwr_idx.get (argno - 1);
- if (access)
- {
- if (access->mode == access_none
- || access->mode == access_write_only)
- continue;
-
- if (access->mode == access_deferred
- && !TYPE_READONLY (TREE_TYPE (argtype)))
- continue;
-
- if (save_always_executed && access->mode == access_read_only)
- /* Attribute read_only arguments imply read access. */
- wlims.always_executed = true;
- else
- /* Attribute read_write arguments are documented as requiring
- initialized objects but it's expected that aggregates may
- be only partially initialized regardless. */
- wlims.always_executed = false;
-
- if (access->sizarg < nargs)
- access_size = gimple_call_arg (stmt, access->sizarg);
- }
- else if (!TYPE_READONLY (TREE_TYPE (argtype)))
- continue;
- else if (save_always_executed && fncode != BUILT_IN_LAST)
- /* Const-qualified arguments to built-ins imply read access. */
- wlims.always_executed = true;
- else
- /* Const-qualified arguments to ordinary functions imply a likely
- (but not definitive) read access. */
- wlims.always_executed = false;
-
- /* Ignore args we are not going to read from. */
- if (gimple_call_arg_flags (stmt, argno - 1)
- & (EAF_UNUSED | EAF_NO_DIRECT_READ))
- continue;
-
- tree arg = gimple_call_arg (stmt, argno - 1);
- if (!POINTER_TYPE_P (TREE_TYPE (arg)))
- /* Avoid actual arguments with invalid types. */
- continue;
-
- ao_ref ref;
- ao_ref_init_from_ptr_and_size (&ref, arg, access_size);
- tree argbase = maybe_warn_operand (ref, stmt, NULL_TREE, arg, wlims);
- if (!argbase)
- continue;
-
- if (access && access->mode != access_deferred)
- {
- const char* const access_str =
- TREE_STRING_POINTER (access->to_external_string ());
-
- if (fndecl)
- {
- location_t loc = DECL_SOURCE_LOCATION (fndecl);
- inform (loc, "in a call to %qD declared with "
- "attribute %<%s%> here", fndecl, access_str);
- }
- else
- {
- /* Handle calls through function pointers. */
- location_t loc = gimple_location (stmt);
- inform (loc, "in a call to %qT declared with "
- "attribute %<%s%>", fntype, access_str);
- }
- }
- else
- {
- /* For a declaration with no relevant attribute access create
- a dummy object and use the formatting function to avoid
- having to complicate things here. */
- attr_access ptr_access = { };
- if (!access)
- access = &ptr_access;
- const std::string argtypestr = access->array_as_string (argtype);
- if (fndecl)
- {
- location_t loc (DECL_SOURCE_LOCATION (fndecl));
- inform (loc, "by argument %u of type %s to %qD "
- "declared here",
- argno, argtypestr.c_str (), fndecl);
- }
- else
- {
- /* Handle calls through function pointers. */
- location_t loc (gimple_location (stmt));
- inform (loc, "by argument %u of type %s to %qT",
- argno, argtypestr.c_str (), fntype);
- }
- }
-
- if (DECL_P (argbase))
- {
- location_t loc = DECL_SOURCE_LOCATION (argbase);
- inform (loc, "%qD declared here", argbase);
- }
- }
-
- wlims.always_executed = save_always_executed;
-}
-
-/* Warn about an uninitialized PHI argument on the fallthru path to
- an always executed block BB. */
-
-static void
-warn_uninit_phi_uses (basic_block bb)
-{
- edge_iterator ei;
- edge e, found = NULL, found_back = NULL;
- /* Look for a fallthru and possibly a single backedge. */
- FOR_EACH_EDGE (e, ei, bb->preds)
- {
- /* Ignore backedges. */
- if (dominated_by_p (CDI_DOMINATORS, e->src, bb))
- {
- if (found_back)
- {
- found = NULL;
- break;
- }
- found_back = e;
- continue;
- }
- if (found)
- {
- found = NULL;
- break;
- }
- found = e;
- }
- if (!found)
- return;
-
- basic_block succ = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
- for (gphi_iterator si = gsi_start_phis (bb); !gsi_end_p (si);
- gsi_next (&si))
- {
- gphi *phi = si.phi ();
- tree def = PHI_ARG_DEF_FROM_EDGE (phi, found);
- if (TREE_CODE (def) != SSA_NAME
- || !SSA_NAME_IS_DEFAULT_DEF (def)
- || virtual_operand_p (def))
- continue;
- /* If there's a default def on the fallthru edge PHI
- value and there's a use that post-dominates entry
- then that use is uninitialized and we can warn. */
- imm_use_iterator iter;
- use_operand_p use_p;
- gimple *use_stmt = NULL;
- FOR_EACH_IMM_USE_FAST (use_p, iter, gimple_phi_result (phi))
- {
- use_stmt = USE_STMT (use_p);
- if (gimple_location (use_stmt) != UNKNOWN_LOCATION
- && dominated_by_p (CDI_POST_DOMINATORS, succ,
- gimple_bb (use_stmt))
- /* If we found a non-fallthru edge make sure the
- use is inside the loop, otherwise the backedge
- can serve as initialization. */
- && (!found_back
- || dominated_by_p (CDI_DOMINATORS, found_back->src,
- gimple_bb (use_stmt))))
- break;
- use_stmt = NULL;
- }
- if (use_stmt)
- warn_uninit (OPT_Wuninitialized, def,
- SSA_NAME_VAR (def), use_stmt);
- }
-}
-
-/* Issue warnings about reads of uninitialized variables. WMAYBE_UNINIT
- is true to issue -Wmaybe-uninitialized, otherwise -Wuninitialized. */
-
-static void
-warn_uninitialized_vars (bool wmaybe_uninit)
-{
- /* Counters and limits controlling the the depth of the warning. */
- wlimits wlims = { };
- wlims.wmaybe_uninit = wmaybe_uninit;
-
- gimple_stmt_iterator gsi;
- basic_block bb;
- FOR_EACH_BB_FN (bb, cfun)
- {
- basic_block succ = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
- wlims.always_executed = dominated_by_p (CDI_POST_DOMINATORS, succ, bb);
-
- if (wlims.always_executed)
- warn_uninit_phi_uses (bb);
-
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
- {
- gimple *stmt = gsi_stmt (gsi);
-
- /* The call is an artificial use, will not provide meaningful
- error message. If the result of the call is used somewhere
- else, we warn there instead. */
- if (gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
- continue;
-
- if (is_gimple_debug (stmt))
- continue;
-
- /* We only do data flow with SSA_NAMEs, so that's all we
- can warn about. */
- use_operand_p use_p;
- ssa_op_iter op_iter;
- FOR_EACH_SSA_USE_OPERAND (use_p, stmt, op_iter, SSA_OP_USE)
- {
- /* BIT_INSERT_EXPR first operand should not be considered
- a use for the purpose of uninit warnings. */
- if (gassign *ass = dyn_cast <gassign *> (stmt))
- {
- if (gimple_assign_rhs_code (ass) == BIT_INSERT_EXPR
- && use_p->use == gimple_assign_rhs1_ptr (ass))
- continue;
- }
- tree use = USE_FROM_PTR (use_p);
- if (wlims.always_executed)
- warn_uninit (OPT_Wuninitialized, use,
- SSA_NAME_VAR (use), stmt);
- else if (wmaybe_uninit)
- warn_uninit (OPT_Wmaybe_uninitialized, use,
- SSA_NAME_VAR (use), stmt);
- }
-
- /* For limiting the alias walk below we count all
- vdefs in the function. */
- if (gimple_vdef (stmt))
- wlims.vdef_cnt++;
-
- if (gcall *call = dyn_cast <gcall *> (stmt))
- maybe_warn_pass_by_reference (call, wlims);
- else if (gimple_assign_load_p (stmt)
- && gimple_has_location (stmt))
- {
- tree rhs = gimple_assign_rhs1 (stmt);
- tree lhs = gimple_assign_lhs (stmt);
-
- ao_ref ref;
- ao_ref_init (&ref, rhs);
- tree var = maybe_warn_operand (ref, stmt, lhs, rhs, wlims);
- if (!var)
- continue;
-
- if (DECL_P (var))
- {
- location_t loc = DECL_SOURCE_LOCATION (var);
- inform (loc, "%qD declared here", var);
- }
- }
- }
- }
-}
-
-/* Checks if the operand OPND of PHI is defined by
- another phi with one operand defined by this PHI,
- but the rest operands are all defined. If yes,
- returns true to skip this operand as being
- redundant. Can be enhanced to be more general. */
-
-static bool
-can_skip_redundant_opnd (tree opnd, gimple *phi)
-{
- tree phi_def = gimple_phi_result (phi);
- gimple *op_def = SSA_NAME_DEF_STMT (opnd);
- if (gimple_code (op_def) != GIMPLE_PHI)
- return false;
-
- unsigned n = gimple_phi_num_args (op_def);
- for (unsigned i = 0; i < n; ++i)
- {
- tree op = gimple_phi_arg_def (op_def, i);
- if (TREE_CODE (op) != SSA_NAME)
- continue;
- if (op != phi_def && uninit_undefined_value_p (op))
- return false;
- }
-
- return true;
-}
-
-/* Return a bitset holding the positions of arguments in PHI with empty
- (or possibly empty) definitions. */
-
-static unsigned
-compute_uninit_opnds_pos (gphi *phi)
-{
- unsigned uninit_opnds = 0;
-
- unsigned n = gimple_phi_num_args (phi);
- /* Bail out for phi with too many args. */
- if (n > predicate::func_t::max_phi_args)
- return 0;
-
- for (unsigned i = 0; i < n; ++i)
- {
- tree op = gimple_phi_arg_def (phi, i);
- if (TREE_CODE (op) == SSA_NAME
- && uninit_undefined_value_p (op)
- && !can_skip_redundant_opnd (op, phi))
- {
- if (cfun->has_nonlocal_label || cfun->calls_setjmp)
- {
- /* Ignore SSA_NAMEs that appear on abnormal edges
- somewhere. */
- if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op))
- continue;
- }
- MASK_SET_BIT (uninit_opnds, i);
- }
- }
- return uninit_opnds;
-}
-
-/* Function object type used to determine whether an expression
- is of interest to the predicate analyzer. */
-
-struct uninit_undef_val_t: public predicate::func_t
-{
- virtual bool operator()(tree) override;
- virtual unsigned phi_arg_set (gphi *) override;
-};
-
-/* Return true if the argument is an expression of interest. */
-
-bool
-uninit_undef_val_t::operator()(tree val)
-{
- if (TREE_CODE (val) == SSA_NAME)
- return uninit_undefined_value_p (val);
-
- return false;
-}
-
-/* Return a bitset of PHI arguments of interest. */
-
-unsigned
-uninit_undef_val_t::phi_arg_set (gphi *phi)
-{
- return compute_uninit_opnds_pos (phi);
-}
-
-/* Searches through all uses of a potentially
- uninitialized variable defined by PHI and returns a use
- statement if the use is not properly guarded. It returns
- NULL if all uses are guarded. UNINIT_OPNDS is a bitvector
- holding the position(s) of uninit PHI operands. WORKLIST
- is the vector of candidate phis that may be updated by this
- function. ADDED_TO_WORKLIST is the pointer set tracking
- if the new phi is already in the worklist. */
-
-static gimple *
-find_uninit_use (gphi *phi, unsigned uninit_opnds,
- vec<gphi *> *worklist, hash_set<gphi *> *added_to_worklist)
-{
- /* The Boolean predicate guarding the PHI definition. Initialized
- lazily from PHI in the first call to is_use_guarded() and cached
- for subsequent iterations. */
- uninit_undef_val_t eval;
- predicate def_preds (eval);
-
- use_operand_p use_p;
- imm_use_iterator iter;
- tree phi_result = gimple_phi_result (phi);
- FOR_EACH_IMM_USE_FAST (use_p, iter, phi_result)
- {
- gimple *use_stmt = USE_STMT (use_p);
- if (is_gimple_debug (use_stmt))
- continue;
-
- basic_block use_bb;
- if (gphi *use_phi = dyn_cast<gphi *> (use_stmt))
- use_bb = gimple_phi_arg_edge (use_phi,
- PHI_ARG_INDEX_FROM_USE (use_p))->src;
- else
- use_bb = gimple_bb (use_stmt);
-
- if (def_preds.is_use_guarded (use_stmt, use_bb, phi, uninit_opnds))
- continue;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Found unguarded use in bb %u: ",
- use_bb->index);
- print_gimple_stmt (dump_file, use_stmt, 0);
- }
- /* Found one real use, return. */
- if (gimple_code (use_stmt) != GIMPLE_PHI)
- return use_stmt;
-
- /* Found a phi use that is not guarded,
- add the phi to the worklist. */
- if (!added_to_worklist->add (as_a<gphi *> (use_stmt)))
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "[WORKLIST]: Update worklist with phi: ");
- print_gimple_stmt (dump_file, use_stmt, 0);
- }
-
- worklist->safe_push (as_a<gphi *> (use_stmt));
- possibly_undefined_names->add (phi_result);
- }
- }
-
- return NULL;
-}
-
-/* Look for inputs to PHI that are SSA_NAMEs that have empty definitions
- and gives warning if there exists a runtime path from the entry to a
- use of the PHI def that does not contain a definition. In other words,
- the warning is on the real use. The more dead paths that can be pruned
- by the compiler, the fewer false positives the warning is. WORKLIST
- is a vector of candidate phis to be examined. ADDED_TO_WORKLIST is
- a pointer set tracking if the new phi is added to the worklist or not. */
-
-static void
-warn_uninitialized_phi (gphi *phi, vec<gphi *> *worklist,
- hash_set<gphi *> *added_to_worklist)
-{
- /* Don't look at virtual operands. */
- if (virtual_operand_p (gimple_phi_result (phi)))
- return;
-
- unsigned uninit_opnds = compute_uninit_opnds_pos (phi);
- if (MASK_EMPTY (uninit_opnds))
- return;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Examining phi: ");
- print_gimple_stmt (dump_file, phi, 0);
- }
-
- gimple *uninit_use_stmt = find_uninit_use (phi, uninit_opnds,
- worklist, added_to_worklist);
-
- /* All uses are properly guarded but a new PHI may have been added
- to WORKLIST. */
- if (!uninit_use_stmt)
- return;
-
- unsigned phiarg_index = MASK_FIRST_SET_BIT (uninit_opnds);
- tree uninit_op = gimple_phi_arg_def (phi, phiarg_index);
- if (SSA_NAME_VAR (uninit_op) == NULL_TREE)
- return;
-
- location_t loc = UNKNOWN_LOCATION;
- if (gimple_phi_arg_has_location (phi, phiarg_index))
- loc = gimple_phi_arg_location (phi, phiarg_index);
- else
- {
- tree arg_def = gimple_phi_arg_def (phi, phiarg_index);
- if (TREE_CODE (arg_def) == SSA_NAME)
- {
- gimple *def_stmt = SSA_NAME_DEF_STMT (arg_def);
- if (gphi *arg_phi = dyn_cast<gphi *> (def_stmt))
- {
- unsigned uop = compute_uninit_opnds_pos (arg_phi);
- unsigned idx = MASK_FIRST_SET_BIT (uop);
- if (idx < gimple_phi_num_args (arg_phi)
- && gimple_phi_arg_has_location (arg_phi, idx))
- loc = gimple_phi_arg_location (arg_phi, idx);
- }
- }
- }
-
- warn_uninit (OPT_Wmaybe_uninitialized, uninit_op,
- SSA_NAME_VAR (uninit_op),
- uninit_use_stmt, loc);
-}
-
-static bool
-gate_warn_uninitialized (void)
-{
- return warn_uninitialized || warn_maybe_uninitialized;
-}
-
-namespace {
-
-const pass_data pass_data_late_warn_uninitialized =
-{
- GIMPLE_PASS, /* type */
- "uninit", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- TV_NONE, /* tv_id */
- PROP_ssa, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- 0, /* todo_flags_finish */
-};
-
-class pass_late_warn_uninitialized : public gimple_opt_pass
-{
-public:
- pass_late_warn_uninitialized (gcc::context *ctxt)
- : gimple_opt_pass (pass_data_late_warn_uninitialized, ctxt)
- {}
-
- /* opt_pass methods: */
- opt_pass *clone () { return new pass_late_warn_uninitialized (m_ctxt); }
- virtual bool gate (function *) { return gate_warn_uninitialized (); }
- virtual unsigned int execute (function *);
-
-}; // class pass_late_warn_uninitialized
-
-static void
-execute_late_warn_uninitialized (function *fun)
-{
- basic_block bb;
- gphi_iterator gsi;
- vec<gphi *> worklist = vNULL;
-
- calculate_dominance_info (CDI_DOMINATORS);
- calculate_dominance_info (CDI_POST_DOMINATORS);
- /* Re-do the plain uninitialized variable check, as optimization may have
- straightened control flow. Do this first so that we don't accidentally
- get a "may be" warning when we'd have seen an "is" warning later. */
- warn_uninitialized_vars (/*warn_maybe_uninitialized=*/1);
-
- timevar_push (TV_TREE_UNINIT);
-
- possibly_undefined_names = new hash_set<tree>;
- hash_set<gphi *> added_to_worklist;
-
- /* Initialize worklist */
- FOR_EACH_BB_FN (bb, fun)
- for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
- {
- gphi *phi = gsi.phi ();
-
- /* Don't look at virtual operands. */
- if (virtual_operand_p (gimple_phi_result (phi)))
- continue;
-
- unsigned n = gimple_phi_num_args (phi);
- for (unsigned i = 0; i < n; ++i)
- {
- tree op = gimple_phi_arg_def (phi, i);
- if (TREE_CODE (op) == SSA_NAME && uninit_undefined_value_p (op))
- {
- worklist.safe_push (phi);
- added_to_worklist.add (phi);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "[WORKLIST]: add to initial list "
- "for operand %u of: ", i);
- print_gimple_stmt (dump_file, phi, 0);
- }
- break;
- }
- }
- }
-
- while (worklist.length () != 0)
- {
- gphi *cur_phi = 0;
- cur_phi = worklist.pop ();
- warn_uninitialized_phi (cur_phi, &worklist, &added_to_worklist);
- }
-
- worklist.release ();
- delete possibly_undefined_names;
- possibly_undefined_names = NULL;
- free_dominance_info (CDI_POST_DOMINATORS);
- timevar_pop (TV_TREE_UNINIT);
-}
-
-unsigned int
-pass_late_warn_uninitialized::execute (function *fun)
-{
- execute_late_warn_uninitialized (fun);
- return 0;
-}
-
-} // anon namespace
-
-gimple_opt_pass *
-make_pass_late_warn_uninitialized (gcc::context *ctxt)
-{
- return new pass_late_warn_uninitialized (ctxt);
-}
-
-static unsigned int
-execute_early_warn_uninitialized (void)
-{
- /* Currently, this pass runs always but
- execute_late_warn_uninitialized only runs with optimization. With
- optimization we want to warn about possible uninitialized as late
- as possible, thus don't do it here. However, without
- optimization we need to warn here about "may be uninitialized". */
- calculate_dominance_info (CDI_DOMINATORS);
- calculate_dominance_info (CDI_POST_DOMINATORS);
-
- warn_uninitialized_vars (/*warn_maybe_uninitialized=*/!optimize);
-
- /* Post-dominator information cannot be reliably updated. Free it
- after the use. */
-
- free_dominance_info (CDI_POST_DOMINATORS);
- return 0;
-}
-
-namespace {
-
-const pass_data pass_data_early_warn_uninitialized =
-{
- GIMPLE_PASS, /* type */
- "*early_warn_uninitialized", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- TV_TREE_UNINIT, /* tv_id */
- PROP_ssa, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- 0, /* todo_flags_finish */
-};
-
-class pass_early_warn_uninitialized : public gimple_opt_pass
-{
-public:
- pass_early_warn_uninitialized (gcc::context *ctxt)
- : gimple_opt_pass (pass_data_early_warn_uninitialized, ctxt)
- {}
-
- /* opt_pass methods: */
- virtual bool gate (function *) { return gate_warn_uninitialized (); }
- virtual unsigned int execute (function *)
- {
- return execute_early_warn_uninitialized ();
- }
-
-}; // class pass_early_warn_uninitialized
-
-} // anon namespace
-
-gimple_opt_pass *
-make_pass_early_warn_uninitialized (gcc::context *ctxt)
-{
- return new pass_early_warn_uninitialized (ctxt);
-}