diff mbox series

[11/11] use xxx_no_warning APIs in the middle end

Message ID 9abe39b2-68cb-8381-68f8-c1a1ec73f108@redhat.com
State New
Headers show
Series warning control by group and location (PR 74765) | expand

Commit Message

Martin Sebor May 24, 2021, 10:16 p.m. UTC
The attached patch replaces TREE_NO_WARNING, gimple_get_no_warning_p
and gimple_set_no_warning with the new APIs, get_no_warning,
set_no_warning, and copy_no_warning.

Comments

David Malcolm May 24, 2021, 11:08 p.m. UTC | #1
On Mon, 2021-05-24 at 16:16 -0600, Martin Sebor via Gcc-patches wrote:
> The attached patch replaces TREE_NO_WARNING, gimple_get_no_warning_p
> and gimple_set_no_warning with the new APIs, get_no_warning,
> set_no_warning, and copy_no_warning.

Might be worth splitting this out into
(a) the middle-end changes versus
(b) the final changes that remove the old mechanisms.

Is this missing a change to remove "nowarning_flag" from the pertinent
tree struct?

Dave
Martin Sebor May 25, 2021, 12:44 a.m. UTC | #2
On 5/24/21 5:08 PM, David Malcolm via Gcc-patches wrote:
> On Mon, 2021-05-24 at 16:16 -0600, Martin Sebor via Gcc-patches wrote:
>> The attached patch replaces TREE_NO_WARNING, gimple_get_no_warning_p
>> and gimple_set_no_warning with the new APIs, get_no_warning,
>> set_no_warning, and copy_no_warning.
> 
> Might be worth splitting this out into
> (a) the middle-end changes versus
> (b) the final changes that remove the old mechanisms.

Sure, I can do that.

> 
> Is this missing a change to remove "nowarning_flag" from the pertinent
> tree struct?

No, the bit is still used and needed when a tree or GIMPLE statement
has no location.

Martin

> 
> Dave
>
diff mbox series

Patch

Add support for per-location warning groups.

gcc/ChangeLog:

	* builtins.c (warn_string_no_nul): Replace uses of TREE_NO_WARNING,
	gimple_no_warning_p and gimple_set_no_warning with get_no_warning,
	and set_no_warning.
	(c_strlen): Same.
	(maybe_warn_for_bound): Same.
	(warn_for_access): Same.
	(check_access): Same.
	(expand_builtin_strncmp): Same.
	(fold_builtin_varargs): Same.
	* calls.c (maybe_warn_nonstring_arg): Same.
	(maybe_warn_rdwr_sizes): Same.
	* cfgexpand.c (expand_call_stmt): Same.
	* cgraphunit.c (check_global_declaration): Same.
	* fold-const.c (fold_undefer_overflow_warnings): Same.
	(fold_truth_not_expr): Same.
	(fold_unary_loc): Same.
	(fold_checksum_tree): Same.
	* gengtype.c (open_base_files): Same.
	* gimple-array-bounds.cc (array_bounds_checker::check_array_ref): Same.
	(array_bounds_checker::check_mem_ref): Same.
	(array_bounds_checker::check_addr_expr): Same.
	(array_bounds_checker::check_array_bounds): Same.
	* gimple-expr.c (copy_var_decl): Same.
	* gimple-fold.c (gimple_fold_builtin_strcpy): Same.
	(gimple_fold_builtin_strncat): Same.
	(gimple_fold_builtin_stxcpy_chk): Same.
	(gimple_fold_builtin_stpcpy): Same.
	(gimple_fold_builtin_sprintf): Same.
	(fold_stmt_1): Same.
	* gimple-ssa-isolate-paths.c (diag_returned_locals): Same.
	* gimple-ssa-nonnull-compare.c (do_warn_nonnull_compare): Same.
	* gimple-ssa-sprintf.c (handle_printf_call): Same.
	* gimple-ssa-store-merging.c (imm_store_chain_info::output_merged_store): Same.
	* gimple-ssa-warn-restrict.c (maybe_diag_overlap): Same.
	(maybe_diag_access_bounds): Same.
	(check_call): Same.
	(check_bounds_or_overlap): Same.
	* gimple.c (gimple_build_call_from_tree): Same.
	* gimplify.c (gimplify_return_expr): Same.
	(gimplify_cond_expr): Same.
	(gimplify_modify_expr_complex_part): Same.
	(gimplify_modify_expr): Same.
	(gimple_push_cleanup): Same.
	(gimplify_expr): Same.
	* omp-expand.c (expand_omp_for_generic): Same.
	(expand_omp_taskloop_for_outer): Same.
	* omp-low.c (lower_rec_input_clauses): Same.
	(lower_lastprivate_clauses): Same.
	(lower_send_clauses): Same.
	(lower_omp_target): Same.
	* tree-cfg.c (pass_warn_function_return::execute): Same.
	* tree-complex.c (create_one_component_var): Same.
	* tree-inline.c (remap_gimple_op_r): Same.
	(copy_tree_body_r): Same.
	(declare_return_variable): Same.
	(expand_call_inline): Same.
	* tree-nested.c (lookup_field_for_decl): Same.
	* tree-sra.c (create_access_replacement): Same.
	(generate_subtree_copies): Same.
	* tree-ssa-ccp.c (pass_post_ipa_warn::execute): Same.
	* tree-ssa-forwprop.c (combine_cond_expr_cond): Same.
	* tree-ssa-loop-ch.c (ch_base::copy_headers): Same.
	* tree-ssa-loop-im.c (execute_sm): Same.
	* tree-ssa-phiopt.c (cond_store_replacement): Same.
	* tree-ssa-strlen.c (maybe_warn_overflow): Same.
	(handle_builtin_strcpy): Same.
	(maybe_diag_stxncpy_trunc): Same.
	(handle_builtin_stxncpy_strncat): Same.
	(handle_builtin_strcat): Same.
	* tree-ssa-uninit.c (get_no_uninit_warning): Same.
	(set_no_uninit_warning): Same.
	(uninit_undefined_value_p): Same.
	(warn_uninit): Same.
	(maybe_warn_operand): Same.
	* tree-vrp.c (compare_values_warnv): Same.
	* vr-values.c (vr_values::extract_range_for_var_from_comparison_expr): Same.
	(test_for_singularity): Same.

	* gimple.h (get_no_warning): New.
	(set_no_warning): Same.
	(copy_no_warning): Same.
	(gimple_set_block): Call gimple_set_location.
	(gimple_set_location): Call copy_warning.
	(gimple_no_warning_p): Remove.
	(gimple_set_no_warning): Same.
	* tree.h (TREE_NO_WARNING): Remove.
	(get_no_warning): New.
	(set_no_warning): Same.
	(copy_no_warning): Same.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index b0c880dc3b5..105deadd42b 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -1094,7 +1094,9 @@  warn_string_no_nul (location_t loc, tree expr, const char *fname,
 		    bool exact /* = false */,
 		    const wide_int bndrng[2] /* = NULL */)
 {
-  if ((expr && TREE_NO_WARNING (expr)) || TREE_NO_WARNING (arg))
+  const int opt = OPT_Wstringop_overread;
+  if ((expr && get_no_warning (expr, opt))
+      || get_no_warning (arg, opt))
     return;
 
   loc = expansion_point_location_if_in_system_header (loc);
@@ -1122,14 +1124,14 @@  warn_string_no_nul (location_t loc, tree expr, const char *fname,
       if (bndrng)
 	{
 	  if (wi::ltu_p (maxsiz, bndrng[0]))
-	    warned = warning_at (loc, OPT_Wstringop_overread,
+	    warned = warning_at (loc, opt,
 				 "%K%qD specified bound %s exceeds "
 				 "maximum object size %E",
 				 expr, func, bndstr, maxobjsize);
 	  else
 	    {
 	      bool maybe = wi::to_wide (size) == bndrng[0];
-	      warned = warning_at (loc, OPT_Wstringop_overread,
+	      warned = warning_at (loc, opt,
 				   exact
 				   ? G_("%K%qD specified bound %s exceeds "
 					"the size %E of unterminated array")
@@ -1144,7 +1146,7 @@  warn_string_no_nul (location_t loc, tree expr, const char *fname,
 	    }
 	}
       else
-	warned = warning_at (loc, OPT_Wstringop_overread,
+	warned = warning_at (loc, opt,
 			     "%K%qD argument missing terminating nul",
 			     expr, func);
     }
@@ -1153,14 +1155,14 @@  warn_string_no_nul (location_t loc, tree expr, const char *fname,
       if (bndrng)
 	{
 	  if (wi::ltu_p (maxsiz, bndrng[0]))
-	    warned = warning_at (loc, OPT_Wstringop_overread,
+	    warned = warning_at (loc, opt,
 				 "%qs specified bound %s exceeds "
 				 "maximum object size %E",
 				 fname, bndstr, maxobjsize);
 	  else
 	    {
 	      bool maybe = wi::to_wide (size) == bndrng[0];
-	      warned = warning_at (loc, OPT_Wstringop_overread,
+	      warned = warning_at (loc, opt,
 				   exact
 				   ? G_("%qs specified bound %s exceeds "
 					"the size %E of unterminated array")
@@ -1175,7 +1177,7 @@  warn_string_no_nul (location_t loc, tree expr, const char *fname,
 	    }
 	}
       else
-	warned = warning_at (loc, OPT_Wstringop_overread,
+	warned = warning_at (loc, opt,
 			     "%qs argument missing terminating nul",
 			     fname);
     }
@@ -1184,9 +1186,9 @@  warn_string_no_nul (location_t loc, tree expr, const char *fname,
     {
       inform (DECL_SOURCE_LOCATION (decl),
 	      "referenced argument declared here");
-      TREE_NO_WARNING (arg) = 1;
+      set_no_warning (arg, opt);
       if (expr)
-	TREE_NO_WARNING (expr) = 1;
+	set_no_warning (expr, opt);
     }
 }
 
@@ -1443,14 +1445,14 @@  c_strlen (tree arg, int only_value, c_strlen_data *data, unsigned eltsize)
     {
       /* Suppress multiple warnings for propagated constant strings.  */
       if (only_value != 2
-	  && !TREE_NO_WARNING (arg)
+	  && !get_no_warning (arg/*, OPT_Warray_bounds*/)
 	  && warning_at (loc, OPT_Warray_bounds,
 			 "offset %qwi outside bounds of constant string",
 			 eltoff))
 	{
 	  if (decl)
 	    inform (DECL_SOURCE_LOCATION (decl), "%qE declared here", decl);
-	  TREE_NO_WARNING (arg) = 1;
+	  set_no_warning (arg, OPT_Warray_bounds);
 	}
       return NULL_TREE;
     }
@@ -3936,7 +3938,7 @@  static bool
 maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
 		      tree bndrng[2], tree size, const access_data *pad = NULL)
 {
-  if (!bndrng[0] || TREE_NO_WARNING (exp))
+  if (!bndrng[0] || get_no_warning (exp, opt))
     return false;
 
   tree maxobjsize = max_object_size ();
@@ -4028,7 +4030,7 @@  maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
 		inform (EXPR_LOCATION (pad->src.ref),
 			"source object allocated here");
 	    }
-	  TREE_NO_WARNING (exp) = true;
+	  set_no_warning (exp, opt);
 	}
 
       return warned;
@@ -4075,14 +4077,14 @@  maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
     return false;
   else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
     warned = (func
-	      ? warning_at (loc, OPT_Wstringop_overflow_,
+	      ? warning_at (loc, opt,
 			    (maybe
 			     ? G_("%K%qD specified bound %E may exceed "
 				  "destination size %E")
 			     : G_("%K%qD specified bound %E exceeds "
 				  "destination size %E")),
 			    exp, func, bndrng[0], size)
-	      : warning_at (loc, OPT_Wstringop_overflow_,
+	      : warning_at (loc, opt,
 			    (maybe
 			     ? G_("%Kspecified bound %E may exceed "
 				  "destination size %E")
@@ -4091,14 +4093,14 @@  maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
 			    exp, bndrng[0], size));
   else
     warned = (func
-	      ? warning_at (loc, OPT_Wstringop_overflow_,
+	      ? warning_at (loc, opt,
 			    (maybe
 			     ? G_("%K%qD specified bound [%E, %E] may exceed "
 				  "destination size %E")
 			     : G_("%K%qD specified bound [%E, %E] exceeds "
 				  "destination size %E")),
 			    exp, func, bndrng[0], bndrng[1], size)
-	      : warning_at (loc, OPT_Wstringop_overflow_,
+	      : warning_at (loc, opt,
 			    (maybe
 			     ? G_("%Kspecified bound [%E, %E] exceeds "
 				  "destination size %E")
@@ -4117,7 +4119,7 @@  maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
 	    inform (EXPR_LOCATION (pad->dst.ref),
 		    "destination object allocated here");
 	}
-      TREE_NO_WARNING (exp) = true;
+      set_no_warning (exp, opt);
     }
 
   return warned;
@@ -4343,7 +4345,7 @@  warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
 				exp, range[0], range[1], size));
 
       if (warned)
-	TREE_NO_WARNING (exp) = true;
+	set_no_warning (exp, OPT_Wstringop_overread);
 
       return warned;
     }
@@ -4386,7 +4388,7 @@  warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
 			    exp, range[0], range[1], size));
 
   if (warned)
-    TREE_NO_WARNING (exp) = true;
+    set_no_warning (exp, OPT_Wstringop_overread);
 
   return warned;
 }
@@ -4765,8 +4767,10 @@  check_access (tree exp, tree dstwrite,
 		  && tree_fits_uhwi_p (dstwrite)
 		  && tree_int_cst_lt (dstwrite, range[0]))))
 	{
-	  if (TREE_NO_WARNING (exp)
-	      || (pad && pad->dst.ref && TREE_NO_WARNING (pad->dst.ref)))
+	  const int opt = OPT_Wstringop_overflow_;
+	  if (get_no_warning (exp, opt)
+	      || (pad && pad->dst.ref
+		  && get_no_warning (pad->dst.ref, opt)))
 	    return false;
 
 	  location_t loc = tree_inlined_location (exp);
@@ -4777,12 +4781,12 @@  check_access (tree exp, tree dstwrite,
 		 and a source of unknown length.  The call will write
 		 at least one byte past the end of the destination.  */
 	      warned = (func
-			? warning_at (loc, OPT_Wstringop_overflow_,
+			? warning_at (loc, opt,
 				      "%K%qD writing %E or more bytes into "
 				      "a region of size %E overflows "
 				      "the destination",
 				      exp, func, range[0], dstsize)
-			: warning_at (loc, OPT_Wstringop_overflow_,
+			: warning_at (loc, opt,
 				      "%Kwriting %E or more bytes into "
 				      "a region of size %E overflows "
 				      "the destination",
@@ -4803,7 +4807,7 @@  check_access (tree exp, tree dstwrite,
 
 	  if (warned)
 	    {
-	      TREE_NO_WARNING (exp) = true;
+	      set_no_warning (exp, OPT_Wstringop_overflow_);
 	      if (pad)
 		pad->dst.inform_access (pad->mode);
 	    }
@@ -4876,19 +4880,21 @@  check_access (tree exp, tree dstwrite,
 
   if (overread)
     {
-      if (TREE_NO_WARNING (exp)
-	  || (srcstr && TREE_NO_WARNING (srcstr))
-	  || (pad && pad->src.ref && TREE_NO_WARNING (pad->src.ref)))
+      const int opt = OPT_Wstringop_overread;
+      if (get_no_warning (exp, opt)
+	  || (srcstr && get_no_warning (srcstr, opt))
+	  || (pad && pad->src.ref
+	      && get_no_warning (pad->src.ref, opt)))
 	return false;
 
       location_t loc = tree_inlined_location (exp);
       const bool read
 	= mode == access_read_only || mode == access_read_write;
       const bool maybe = pad && pad->dst.parmarray;
-      if (warn_for_access (loc, func, exp, OPT_Wstringop_overread, range,
-			   slen, false, read, maybe))
+      if (warn_for_access (loc, func, exp, opt, range, slen, false, read,
+			   maybe))
 	{
-	  TREE_NO_WARNING (exp) = true;
+	  set_no_warning (exp, opt);
 	  if (pad)
 	    pad->src.inform_access (access_read_only);
 	}
@@ -7413,8 +7419,7 @@  expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
   /* Expand the library call ourselves using a stabilized argument
      list to avoid re-evaluating the function's arguments twice.  */
   tree call = build_call_nofold_loc (loc, fndecl, 3, arg1, arg2, len);
-  if (TREE_NO_WARNING (exp))
-    TREE_NO_WARNING (call) = true;
+  copy_no_warning (call, exp);
   gcc_assert (TREE_CODE (call) == CALL_EXPR);
   CALL_EXPR_TAILCALL (call) = CALL_EXPR_TAILCALL (exp);
   return expand_call (call, target, target == const0_rtx);
@@ -13953,7 +13958,7 @@  fold_builtin_varargs (location_t loc, tree fndecl, tree *args, int nargs)
     {
       ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
       SET_EXPR_LOCATION (ret, loc);
-      TREE_NO_WARNING (ret) = 1;
+      set_no_warning (ret);
       return ret;
     }
   return NULL_TREE;
diff --git a/gcc/calls.c b/gcc/calls.c
index f3da1839dc5..47e2a2b54de 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1622,7 +1622,7 @@  maybe_warn_nonstring_arg (tree fndecl, tree exp)
   if (!fndecl || !fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
     return false;
 
-  if (TREE_NO_WARNING (exp) || !warn_stringop_overread)
+  if (!warn_stringop_overread || get_no_warning (exp, OPT_Wstringop_overread))
     return false;
 
   /* Avoid clearly invalid calls (more checking done below).  */
@@ -1738,7 +1738,7 @@  maybe_warn_nonstring_arg (tree fndecl, tree exp)
 				 exp, fndecl, bndrng[0], bndrng[1],
 				 maxobjsize);
 	  if (warned)
-	    TREE_NO_WARNING (exp) = true;
+	    set_no_warning (exp, OPT_Wstringop_overread);
 
 	  return warned;
 	}
@@ -1915,7 +1915,7 @@  maybe_warn_nonstring_arg (tree fndecl, tree exp)
     }
 
   if (any_arg_warned)
-    TREE_NO_WARNING (exp) = true;
+    set_no_warning (exp, OPT_Wstringop_overread);
 
   return any_arg_warned;
 }
@@ -1978,7 +1978,7 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
 
   /* Set if a warning has been issued for any argument (used to decide
      whether to emit an informational note at the end).  */
-  bool any_warned = false;
+  int opt_warned = false;
 
   /* A string describing the attributes that the warnings issued by this
      function apply to.  Used to print one informational note per function
@@ -2053,7 +2053,7 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
 	*sizstr = '\0';
 
       /* Set if a warning has been issued for the current argument.  */
-      bool arg_warned = false;
+      int arg_warned = 0;
       location_t loc = EXPR_LOCATION (exp);
       tree ptr = access.second.ptr;
       if (*sizstr
@@ -2066,24 +2066,25 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
 	      const std::string argtypestr
 		= access.second.array_as_string (ptrtype);
 
-	      arg_warned = warning_at (loc, OPT_Wstringop_overflow_,
-				       "%Kbound argument %i value %s is "
-				       "negative for a variable length array "
-				       "argument %i of type %s",
-				       exp, sizidx + 1, sizstr,
-				       ptridx + 1, argtypestr.c_str ());
+	      if (warning_at (loc, OPT_Wstringop_overflow_,
+			      "%Kbound argument %i value %s is "
+			      "negative for a variable length array "
+			      "argument %i of type %s",
+			      exp, sizidx + 1, sizstr,
+			      ptridx + 1, argtypestr.c_str ()))
+		arg_warned = OPT_Wstringop_overflow_;
 	    }
-	  else
-	    arg_warned = warning_at (loc, OPT_Wstringop_overflow_,
-				     "%Kargument %i value %s is negative",
-				     exp, sizidx + 1, sizstr);
+	  else if (warning_at (loc, OPT_Wstringop_overflow_,
+			       "%Kargument %i value %s is negative",
+			       exp, sizidx + 1, sizstr))
+	    arg_warned = OPT_Wstringop_overflow_;
 
 	  if (arg_warned)
 	    {
 	      append_attrname (access, attrstr, sizeof attrstr);
 	      /* Remember a warning has been issued and avoid warning
 		 again below for the same attribute.  */
-	      any_warned = true;
+	      opt_warned = arg_warned;
 	      continue;
 	    }
 	}
@@ -2121,31 +2122,33 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
 		  const std::string argtypestr
 		    = access.second.array_as_string (ptrtype);
 
-		  arg_warned = warning_at (loc, OPT_Wnonnull,
-					   "%Kargument %i of variable length "
-					   "array %s is null but "
-					   "the corresponding bound argument "
-					   "%i value is %s",
-					   exp, sizidx + 1, argtypestr.c_str (),
-					   ptridx + 1, sizstr);
+		  if (warning_at (loc, OPT_Wnonnull,
+				  "%Kargument %i of variable length "
+				  "array %s is null but "
+				  "the corresponding bound argument "
+				  "%i value is %s",
+				  exp, sizidx + 1, argtypestr.c_str (),
+				  ptridx + 1, sizstr))
+		    arg_warned = OPT_Wnonnull;
 		}
-	      else
-		arg_warned = warning_at (loc, OPT_Wnonnull,
-					 "%Kargument %i is null but "
-					 "the corresponding size argument "
-					 "%i value is %s",
-					 exp, ptridx + 1, sizidx + 1,
-					 sizstr);
+	      else if (warning_at (loc, OPT_Wnonnull,
+				   "%Kargument %i is null but "
+				   "the corresponding size argument "
+				   "%i value is %s",
+				   exp, ptridx + 1, sizidx + 1,
+				   sizstr))
+		arg_warned = OPT_Wnonnull;
 	    }
 	  else if (access_size && access.second.static_p)
 	    {
 	      /* Warn about null pointers for [static N] array arguments
 		 but do not warn for ordinary (i.e., nonstatic) arrays.  */
-	      arg_warned = warning_at (loc, OPT_Wnonnull,
-				       "%Kargument %i to %<%T[static %E]%> "
-				       "is null where non-null expected",
-				       exp, ptridx + 1, argtype,
-				       access_size);
+	      if (warning_at (loc, OPT_Wnonnull,
+			      "%Kargument %i to %<%T[static %E]%> "
+			      "is null where non-null expected",
+			      exp, ptridx + 1, argtype,
+			      access_size))
+		arg_warned = OPT_Wnonnull;		
 	    }
 
 	  if (arg_warned)
@@ -2153,7 +2156,7 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
 	      append_attrname (access, attrstr, sizeof attrstr);
 	      /* Remember a warning has been issued and avoid warning
 		 again below for the same attribute.  */
-	      any_warned = true;
+	      opt_warned = OPT_Wnonnull;
 	      continue;
 	    }
 	}
@@ -2189,17 +2192,17 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
       /* Clear the no-warning bit in case it was set by check_access
 	 in a prior iteration so that accesses via different arguments
 	 are diagnosed.  */
-      TREE_NO_WARNING (exp) = false;
+      set_no_warning (exp, OPT_Wstringop_overflow_, false);
       access_mode mode = data.mode;
       if (mode == access_deferred)
 	mode = TYPE_READONLY (argtype) ? access_read_only : access_read_write;
       check_access (exp, access_size, /*maxread=*/ NULL_TREE, srcsize,
 		    dstsize, mode, &data);
 
-      if (TREE_NO_WARNING (exp))
+      if (get_no_warning (exp, OPT_Wstringop_overflow_))
+	opt_warned = OPT_Wstringop_overflow_;
+      if (opt_warned)
 	{
-	  any_warned = true;
-
 	  if (access.second.internal_p)
 	    inform (loc, "referencing argument %u of type %qT",
 		    ptridx + 1, ptrtype);
@@ -2221,7 +2224,7 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
 		"in a call with type %qT and attribute %qs",
 		fntype, attrstr);
     }
-  else if (any_warned)
+  else if (opt_warned)
     {
       if (fndecl)
 	inform (DECL_SOURCE_LOCATION (fndecl),
@@ -2232,7 +2235,7 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
     }
 
   /* Set the bit in case if was cleared and not set above.  */
-  TREE_NO_WARNING (exp) = true;
+  set_no_warning (exp, opt_warned);
 }
 
 /* Fill in ARGS_SIZE and ARGS array based on the parameters found in
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 39e5b040427..fcc7cfd3b59 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2807,9 +2807,6 @@  expand_call_stmt (gcall *stmt)
   if (gimple_call_nothrow_p (stmt))
     TREE_NOTHROW (exp) = 1;
 
-  if (gimple_no_warning_p (stmt))
-    TREE_NO_WARNING (exp) = 1;
-
   CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt);
   CALL_EXPR_MUST_TAIL_CALL (exp) = gimple_call_must_tail_p (stmt);
   CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt);
@@ -2823,6 +2820,9 @@  expand_call_stmt (gcall *stmt)
   CALL_EXPR_BY_DESCRIPTOR (exp) = gimple_call_by_descriptor_p (stmt);
   SET_EXPR_LOCATION (exp, gimple_location (stmt));
 
+  /* Must come after copying location.  */
+  copy_no_warning (exp, stmt);
+
   /* Ensure RTL is created for debug args.  */
   if (decl && DECL_HAS_DEBUG_ARGS_P (decl))
     {
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 098eb99dc95..40489aab525 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1074,7 +1074,7 @@  check_global_declaration (symtab_node *snode)
       && ! DECL_ARTIFICIAL (decl)
       && ! TREE_PUBLIC (decl))
     {
-      if (TREE_NO_WARNING (decl))
+      if (get_no_warning (decl, OPT_Wunused))
 	;
       else if (snode->referred_to_p (/*include_self=*/false))
 	pedwarn (input_location, 0, "%q+F used but never defined", decl);
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 3be9c15e6b2..f3bc9ede30c 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -249,7 +249,7 @@  fold_undefer_overflow_warnings (bool issue, const gimple *stmt, int code)
   if (!issue || warnmsg == NULL)
     return;
 
-  if (gimple_no_warning_p (stmt))
+  if (get_no_warning (stmt, OPT_Wstrict_overflow))
     return;
 
   /* Use the smallest code level when deciding to issue the
@@ -4249,8 +4249,7 @@  fold_truth_not_expr (location_t loc, tree arg)
 
       tree ret = build2_loc (loc, code, type, TREE_OPERAND (arg, 0),
 			     TREE_OPERAND (arg, 1));
-      if (TREE_NO_WARNING (arg))
-	TREE_NO_WARNING (ret) = 1;
+      copy_no_warning (ret, arg);
       return ret;
     }
 
@@ -9341,7 +9340,7 @@  fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0)
 	  tem = fold_build1_loc (loc, code, type, TREE_OPERAND (op0, 1));
 	  /* First do the assignment, then return converted constant.  */
 	  tem = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (tem), op0, tem);
-	  TREE_NO_WARNING (tem) = 1;
+	  set_no_warning (tem /* What warning? */);
 	  TREE_USED (tem) = 1;
 	  return tem;
 	}
@@ -13509,10 +13508,10 @@  fold_checksum_tree (const_tree expr, struct md5_ctx *ctx,
 	  TYPE_CACHED_VALUES (tmp) = NULL;
 	}
     }
-  else if (TREE_NO_WARNING (expr) && (DECL_P (expr) || EXPR_P (expr)))
+  else if (get_no_warning (expr) && (DECL_P (expr) || EXPR_P (expr)))
     {
-      /* Allow TREE_NO_WARNING to be set.  Perhaps we shouldn't allow that
-	 and change builtins.c etc. instead - see PR89543.  */
+      /* Allow the no-warning bit to be set.  Perhaps we shouldn't allow
+	 that and change builtins.c etc. instead - see PR89543.  */
       size_t sz = tree_size (expr);
       buf = XALLOCAVAR (union tree_node, sz);
       memcpy ((char *) buf, expr, sz);
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index b94e2f126ec..c1fa6d35c87 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -1727,7 +1727,7 @@  open_base_files (void)
       "target-globals.h", "ipa-ref.h", "cgraph.h", "symbol-summary.h",
       "ipa-prop.h", "ipa-fnsummary.h", "dwarf2out.h", "omp-general.h",
       "omp-offload.h", "ipa-modref-tree.h", "ipa-modref.h", "symtab-thunks.h",
-      "symtab-clones.h",
+      "symtab-clones.h", "diagnostic-spec.h",
       NULL
     };
     const char *const *ifp;
diff --git a/gcc/gimple-array-bounds.cc b/gcc/gimple-array-bounds.cc
index 199d9f5d216..41615002594 100644
--- a/gcc/gimple-array-bounds.cc
+++ b/gcc/gimple-array-bounds.cc
@@ -175,7 +175,7 @@  bool
 array_bounds_checker::check_array_ref (location_t location, tree ref,
 				       bool ignore_off_by_one)
 {
-  if (TREE_NO_WARNING (ref))
+  if (get_no_warning (ref, OPT_Warray_bounds))
     /* Return true to have the caller prevent warnings for enclosing
        refs.  */
     return true;
@@ -346,7 +346,7 @@  array_bounds_checker::check_array_ref (location_t location, tree ref,
       /* Avoid more warnings when checking more significant subscripts
 	 of the same expression.  */
       ref = TREE_OPERAND (ref, 0);
-      TREE_NO_WARNING (ref) = 1;
+      set_no_warning (ref, OPT_Warray_bounds);
 
       if (decl)
 	ref = decl;
@@ -411,7 +411,7 @@  bool
 array_bounds_checker::check_mem_ref (location_t location, tree ref,
 				     bool ignore_off_by_one)
 {
-  if (TREE_NO_WARNING (ref))
+  if (get_no_warning (ref, OPT_Warray_bounds))
     return false;
 
   tree arg = TREE_OPERAND (ref, 0);
@@ -770,7 +770,7 @@  array_bounds_checker::check_mem_ref (location_t location, tree ref,
 	    }
 	}
 
-      TREE_NO_WARNING (ref) = 1;
+      set_no_warning (ref, OPT_Warray_bounds);
       return true;
     }
 
@@ -787,7 +787,7 @@  array_bounds_checker::check_mem_ref (location_t location, tree ref,
 		      "intermediate array offset %wi is outside array bounds "
 		      "of %qT", tmpidx, reftype))
 	{
-	  TREE_NO_WARNING (ref) = 1;
+	  set_no_warning (ref, OPT_Warray_bounds);
 	  return true;
 	}
     }
@@ -818,7 +818,7 @@  array_bounds_checker::check_addr_expr (location_t location, tree t)
 	warned = check_mem_ref (location, t, ignore_off_by_one);
 
       if (warned)
-	TREE_NO_WARNING (t) = true;
+	set_no_warning (t, OPT_Warray_bounds);
 
       t = TREE_OPERAND (t, 0);
     }
@@ -826,7 +826,7 @@  array_bounds_checker::check_addr_expr (location_t location, tree t)
 
   if (TREE_CODE (t) != MEM_REF
       || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR
-      || TREE_NO_WARNING (t))
+      || get_no_warning (t, OPT_Warray_bounds))
     return;
 
   tree tem = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
@@ -886,7 +886,7 @@  array_bounds_checker::check_addr_expr (location_t location, tree t)
       if (DECL_P (t))
 	inform (DECL_SOURCE_LOCATION (t), "while referencing %qD", t);
 
-      TREE_NO_WARNING (t) = 1;
+      set_no_warning (t, OPT_Warray_bounds);
     }
 }
 
@@ -980,9 +980,10 @@  array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
        See pr98266 and pr97595.  */
     *walk_subtree = false;
 
-  /* Propagate the no-warning bit to the outer expression.  */
+  /* Propagate the no-warning bit to the outer statement to avoid also
+     issuing -Wstringop-overflow/-overread for the out-of-bounds accesses.  */
   if (warned)
-    TREE_NO_WARNING (t) = true;
+    set_no_warning (wi->stmt, OPT_Warray_bounds);
 
   return NULL_TREE;
 }
diff --git a/gcc/gimple-expr.c b/gcc/gimple-expr.c
index b8c732b632a..80fba56ead4 100644
--- a/gcc/gimple-expr.c
+++ b/gcc/gimple-expr.c
@@ -377,7 +377,6 @@  copy_var_decl (tree var, tree name, tree type)
   DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (var);
   DECL_IGNORED_P (copy) = DECL_IGNORED_P (var);
   DECL_CONTEXT (copy) = DECL_CONTEXT (var);
-  TREE_NO_WARNING (copy) = TREE_NO_WARNING (var);
   TREE_USED (copy) = 1;
   DECL_SEEN_IN_BIND_EXPR_P (copy) = 1;
   DECL_ATTRIBUTES (copy) = DECL_ATTRIBUTES (var);
@@ -387,6 +386,7 @@  copy_var_decl (tree var, tree name, tree type)
       DECL_USER_ALIGN (copy) = 1;
     }
 
+  copy_no_warning (copy, var);
   return copy;
 }
 
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 68717cf1542..29dd361370a 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -2039,7 +2039,7 @@  gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
 	 not point to objects and so do not indicate an overlap;
 	 such calls could be the result of sanitization and jump
 	 threading).  */
-      if (!integer_zerop (dest) && !gimple_no_warning_p (stmt))
+      if (!integer_zerop (dest) && !get_no_warning (stmt, OPT_Wrestrict))
 	{
 	  tree func = gimple_call_fndecl (stmt);
 
@@ -2066,9 +2066,9 @@  gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
   if (nonstr)
     {
       /* Avoid folding calls with unterminated arrays.  */
-      if (!gimple_no_warning_p (stmt))
+      if (!get_no_warning (stmt, OPT_Wstringop_overread))
 	warn_string_no_nul (loc, NULL_TREE, "strcpy", src, nonstr);
-      gimple_set_no_warning (stmt, true);
+      set_no_warning (stmt, OPT_Wstringop_overread);
       return false;
     }
 
@@ -2476,7 +2476,7 @@  gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
 
   unsigned HOST_WIDE_INT dstsize;
 
-  bool nowarn = gimple_no_warning_p (stmt);
+  bool nowarn = get_no_warning (stmt, OPT_Wstringop_overflow_);
 
   if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize))
     {
@@ -2499,7 +2499,7 @@  gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
 				    "destination size %wu"),
 			       stmt, fndecl, len, dstsize);
 	  if (nowarn)
-	    gimple_set_no_warning (stmt, true);
+	    set_no_warning (stmt, OPT_Wstringop_overflow_);
 	}
     }
 
@@ -2515,7 +2515,7 @@  gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
       if (warning_at (loc, OPT_Wstringop_overflow_,
 		      "%G%qD specified bound %E equals source length",
 		      stmt, fndecl, len))
-	gimple_set_no_warning (stmt, true);
+	set_no_warning (stmt, OPT_Wstringop_overflow_);
     }
 
   tree fn = builtin_decl_implicit (BUILT_IN_STRCAT);
@@ -3100,7 +3100,8 @@  gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
 	 not point to objects and so do not indicate an overlap;
 	 such calls could be the result of sanitization and jump
 	 threading).  */
-      if (!integer_zerop (dest) && !gimple_no_warning_p (stmt))
+      if (!integer_zerop (dest)
+	  && !get_no_warning (stmt, OPT_Wrestrict))
 	{
 	  tree func = gimple_call_fndecl (stmt);
 
@@ -3283,10 +3284,10 @@  gimple_fold_builtin_stpcpy (gimple_stmt_iterator *gsi)
   if (data.decl)
     {
       /* Avoid folding calls with unterminated arrays.  */
-      if (!gimple_no_warning_p (stmt))
+      if (!get_no_warning (stmt, OPT_Wstringop_overread))
 	warn_string_no_nul (loc, NULL_TREE, "stpcpy", src, data.decl, size,
 			    exact);
-      gimple_set_no_warning (stmt, true);
+      set_no_warning (stmt, OPT_Wstringop_overread);
       return false;
     }
 
@@ -3550,8 +3551,7 @@  gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
 
       /* Propagate the NO_WARNING bit to avoid issuing the same
 	 warning more than once.  */
-      if (gimple_no_warning_p (stmt))
-	gimple_set_no_warning (repl, true);
+      copy_no_warning (repl, stmt);
 
       gimple_seq_add_stmt_without_update (&stmts, repl);
       if (tree lhs = gimple_call_lhs (stmt))
@@ -3603,8 +3603,7 @@  gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
 
       /* Propagate the NO_WARNING bit to avoid issuing the same
 	 warning more than once.  */
-      if (gimple_no_warning_p (stmt))
-	gimple_set_no_warning (repl, true);
+      copy_no_warning (repl, stmt);
 
       gimple_seq_add_stmt_without_update (&stmts, repl);
       if (tree lhs = gimple_call_lhs (stmt))
@@ -6062,7 +6061,7 @@  fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
 {
   bool changed = false;
   gimple *stmt = gsi_stmt (*gsi);
-  bool nowarning = gimple_no_warning_p (stmt);
+  bool nowarning = get_no_warning (stmt, OPT_Wstrict_overflow);
   unsigned i;
   fold_defer_overflow_warnings ();
 
diff --git a/gcc/gimple-ssa-isolate-paths.c b/gcc/gimple-ssa-isolate-paths.c
index eb23cd41f4b..2dafe849ad3 100644
--- a/gcc/gimple-ssa-isolate-paths.c
+++ b/gcc/gimple-ssa-isolate-paths.c
@@ -400,6 +400,11 @@  diag_returned_locals (bool maybe, const locmap_t &locmap)
       gimple *stmt = (*it).first;
       const args_loc_t &argsloc = (*it).second;
       location_t stmtloc = gimple_location (stmt);
+      if (stmtloc == UNKNOWN_LOCATION)
+	/* When multiple return statements are merged into one it
+	   may not have an associated location.  Use the location
+	   of the closing brace instead.  */
+	stmtloc = cfun->function_end_locus;
 
       auto_diagnostic_group d;
       unsigned nargs = argsloc.locvec.length ();
diff --git a/gcc/gimple-ssa-nonnull-compare.c b/gcc/gimple-ssa-nonnull-compare.c
index 9d7894633dc..517e791204f 100644
--- a/gcc/gimple-ssa-nonnull-compare.c
+++ b/gcc/gimple-ssa-nonnull-compare.c
@@ -97,7 +97,7 @@  do_warn_nonnull_compare (function *fun, tree arg)
       if (op
 	  && (POINTER_TYPE_P (TREE_TYPE (arg))
 	      ? integer_zerop (op) : integer_minus_onep (op))
-	  && !gimple_no_warning_p (stmt))
+	  && !get_no_warning (stmt, OPT_Wnonnull_compare))
 	warning_at (loc, OPT_Wnonnull_compare,
 		    "%<nonnull%> argument %qD compared to NULL", arg);
     }
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index fc744669e4b..ed69a73c183 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -4680,7 +4680,7 @@  handle_printf_call (gimple_stmt_iterator *gsi, pointer_query &ptr_qry)
 
   bool success = compute_format_length (info, &res, ptr_qry.rvals);
   if (res.warned)
-    gimple_set_no_warning (info.callstmt, true);
+    set_no_warning (info.callstmt, info.warnopt ());
 
   /* When optimizing and the printf return value optimization is enabled,
      attempt to substitute the computed result for the return value of
diff --git a/gcc/gimple-ssa-store-merging.c b/gcc/gimple-ssa-store-merging.c
index 123c92d9b44..51128f4482b 100644
--- a/gcc/gimple-ssa-store-merging.c
+++ b/gcc/gimple-ssa-store-merging.c
@@ -4348,10 +4348,12 @@  imm_store_chain_info::output_merged_store (merged_store_group *group)
 		      MR_DEPENDENCE_BASE (ops[j]) = base;
 		    }
 		  if (!integer_zerop (mask))
-		    /* The load might load some bits (that will be masked off
-		       later on) uninitialized, avoid -W*uninitialized
-		       warnings in that case.  */
-		    TREE_NO_WARNING (ops[j]) = 1;
+		    {
+		      /* The load might load some bits (that will be masked
+			 off later on) uninitialized, avoid -W*uninitialized
+			 warnings in that case.  */
+		      set_no_warning (ops[j], OPT_Wuninitialized);
+		    }
 
 		  stmt = gimple_build_assign (make_ssa_name (dest_type), ops[j]);
 		  gimple_set_location (stmt, load_loc);
@@ -4533,7 +4535,7 @@  imm_store_chain_info::output_merged_store (merged_store_group *group)
 		 provably uninitialized (no stores at all yet or previous
 		 store a CLOBBER) we'd optimize away the load and replace
 		 it e.g. with 0.  */
-	      TREE_NO_WARNING (load_src) = 1;
+	      set_no_warning (load_src, OPT_Wuninitialized);
 	      stmt = gimple_build_assign (tem, load_src);
 	      gimple_set_location (stmt, loc);
 	      gimple_set_vuse (stmt, new_vuse);
diff --git a/gcc/gimple-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c
index ad37f20afaa..c4a773a45ff 100644
--- a/gcc/gimple-ssa-warn-restrict.c
+++ b/gcc/gimple-ssa-warn-restrict.c
@@ -1425,7 +1425,7 @@  maybe_diag_overlap (location_t loc, gimple *call, builtin_access &acs)
   if (!acs.overlap ())
     return false;
 
-  if (gimple_no_warning_p (call))
+  if (get_no_warning (call, OPT_Wrestrict))
     return true;
 
   /* For convenience.  */
@@ -1677,7 +1677,7 @@  maybe_diag_overlap (location_t loc, gimple *call, builtin_access &acs)
    validated.  Return true if the offsets are not valid and a diagnostic
    has been issued, or would have been issued if DO_WARN had been true.  */
 
-static bool
+static int
 maybe_diag_access_bounds (gimple *call, tree func, int strict,
 			  const builtin_memref &ref, offset_int wroff,
 			  bool do_warn)
@@ -1689,28 +1689,31 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
      since the result is used to make codegen decisions.  */
   if (ref.sizrange[0] > maxobjsize)
     {
+      const int opt = OPT_Wstringop_overflow_;
       /* Return true without issuing a warning.  */
       if (!do_warn)
-	return true;
+	return opt;
 
-      if (ref.ref && TREE_NO_WARNING (ref.ref))
-	return false;
+      if (ref.ref && get_no_warning (ref.ref, OPT_Wstringop_overflow_))
+	return 0;
 
+      bool warned = false;
       if (warn_stringop_overflow)
 	{
 	  if (ref.sizrange[0] == ref.sizrange[1])
-	    return warning_at (loc, OPT_Wstringop_overflow_,
-			       "%G%qD specified bound %wu "
-			       "exceeds maximum object size %wu",
-			       call, func, ref.sizrange[0].to_uhwi (),
-			       maxobjsize.to_uhwi ());
-
-	  return warning_at (loc, OPT_Wstringop_overflow_,
-			     "%G%qD specified bound between %wu and %wu "
-			     "exceeds maximum object size %wu",
-			     call, func, ref.sizrange[0].to_uhwi (),
-			     ref.sizrange[1].to_uhwi (),
-			     maxobjsize.to_uhwi ());
+	    warned = warning_at (loc, opt,
+				 "%G%qD specified bound %wu "
+				 "exceeds maximum object size %wu",
+				 call, func, ref.sizrange[0].to_uhwi (),
+				 maxobjsize.to_uhwi ());
+	  else
+	    warned = warning_at (loc, opt,
+				 "%G%qD specified bound between %wu and %wu "
+				 "exceeds maximum object size %wu",
+				 call, func, ref.sizrange[0].to_uhwi (),
+				 ref.sizrange[1].to_uhwi (),
+				 maxobjsize.to_uhwi ());
+	  return warned ? opt : 0;
 	}
     }
 
@@ -1732,8 +1735,8 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
   if (!warn_array_bounds)
     return false;
 
-  if (TREE_NO_WARNING (ref.ptr)
-      || (ref.ref && TREE_NO_WARNING (ref.ref)))
+  if (get_no_warning (ref.ptr, OPT_Warray_bounds )
+      || (ref.ref && get_no_warning (ref.ref, OPT_Warray_bounds)))
     return false;
 
   char rangestr[2][64];
@@ -1877,7 +1880,7 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
 	}
     }
 
-  return warned;
+  return warned ? OPT_Warray_bounds : 0;
 }
 
 /* Check a CALL statement for restrict-violations and issue warnings
@@ -1888,7 +1891,7 @@  check_call (range_query *query, gimple *call)
 {
   /* Avoid checking the call if it has already been diagnosed for
      some reason.  */
-  if (gimple_no_warning_p (call))
+  if (get_no_warning (call, OPT_Wrestrict))
     return;
 
   tree func = gimple_call_fndecl (call);
@@ -1974,11 +1977,12 @@  check_call (range_query *query, gimple *call)
       || (dstwr && !INTEGRAL_TYPE_P (TREE_TYPE (dstwr))))
     return;
 
-  if (!check_bounds_or_overlap (query, call, dst, src, dstwr, NULL_TREE))
+  int opt = check_bounds_or_overlap (query, call, dst, src, dstwr, NULL_TREE);
+  if (!opt)
     return;
 
   /* Avoid diagnosing the call again.  */
-  gimple_set_no_warning (call, true);
+  set_no_warning (call, OPT_Wrestrict);
 }
 
 } /* anonymous namespace */
@@ -2026,12 +2030,16 @@  check_bounds_or_overlap (range_query *query,
   /* Validate offsets to each reference before the access first to make
      sure they are within the bounds of the destination object if its
      size is known, or PTRDIFF_MAX otherwise.  */
-  if (maybe_diag_access_bounds (call, func, strict, dstref, wroff, do_warn)
-      || maybe_diag_access_bounds (call, func, strict, srcref, 0, do_warn))
+  int opt
+    = maybe_diag_access_bounds (call, func, strict, dstref, wroff, do_warn);
+  if (!opt)
+    opt = maybe_diag_access_bounds (call, func, strict, srcref, 0, do_warn);
+
+  if (opt)
     {
       if (do_warn)
-	gimple_set_no_warning (call, true);
-      return OPT_Warray_bounds;
+	set_no_warning (call, OPT_Wrestrict);
+      return opt;
     }
 
   if (!warn_restrict || bounds_only || !src)
@@ -2058,12 +2066,12 @@  check_bounds_or_overlap (range_query *query,
 	 not point to objects and so do not indicate an overlap;
 	 such calls could be the result of sanitization and jump
 	 threading).  */
-      if (!integer_zerop (dst) && !gimple_no_warning_p (call))
+      if (!integer_zerop (dst) && !get_no_warning (call, OPT_Wrestrict))
 	{
 	  warning_at (loc, OPT_Wrestrict,
 		      "%G%qD source argument is the same as destination",
 		      call, func);
-	  gimple_set_no_warning (call, true);
+	  set_no_warning (call, OPT_Wrestrict);
 	  return OPT_Wrestrict;
 	}
 
@@ -2073,7 +2081,7 @@  check_bounds_or_overlap (range_query *query,
   /* Return false when overlap has been detected.  */
   if (maybe_diag_overlap (loc, call, acs))
     {
-      gimple_set_no_warning (call, true);
+      set_no_warning (call, OPT_Wrestrict);
       return OPT_Wrestrict;
     }
 
diff --git a/gcc/gimple.c b/gcc/gimple.c
index f1044e9c630..0ff1e9ab304 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -399,7 +399,7 @@  gimple_build_call_from_tree (tree t, tree fnptrtype)
   gimple_call_set_va_arg_pack (call, CALL_EXPR_VA_ARG_PACK (t));
   gimple_call_set_nothrow (call, TREE_NOTHROW (t));
   gimple_call_set_by_descriptor (call, CALL_EXPR_BY_DESCRIPTOR (t));
-  gimple_set_no_warning (call, TREE_NO_WARNING (t));
+  copy_no_warning (call, t);
 
   if (fnptrtype)
     {
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 91b92b4a4d1..f95ce872cb5 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -1634,6 +1634,21 @@  extern bool gimple_inexpensive_call_p (gcall *);
 extern bool stmt_can_terminate_bb_p (gimple *);
 extern location_t gimple_or_expr_nonartificial_location (gimple *, tree);
 
+/* Return true if a warning is enabled for the statement.  */
+extern bool get_no_warning (const gimple *, int = -1)
+  ATTRIBUTE_NONNULL (1);
+/* Enable, or by default disable, a warning for the statement.  */
+extern void set_no_warning (gimple *, int = -1, bool = true)
+  ATTRIBUTE_NONNULL (1);
+/* Copy the warning disposition mapping from one statement to another.  */
+extern void copy_no_warning (gimple *, const gimple *)
+  ATTRIBUTE_NONNULL (1) ATTRIBUTE_NONNULL (2);
+/* Copy the warning disposition mapping from an expression to a statement.  */
+extern void copy_no_warning (gimple *, const_tree)
+  ATTRIBUTE_NONNULL (1) ATTRIBUTE_NONNULL (2);
+/* Copy the warning disposition mapping from a statement to an expression.  */
+extern void copy_no_warning (tree, const gimple *)
+  ATTRIBUTE_NONNULL (1) ATTRIBUTE_NONNULL (2);
 
 /* Formal (expression) temporary table handling: multiple occurrences of
    the same scalar expression are evaluated into the same temporary.  */
@@ -1854,16 +1869,17 @@  gimple_block (const gimple *g)
   return LOCATION_BLOCK (g->location);
 }
 
+/* Forward declare.  */
+static inline void gimple_set_location (gimple *, location_t);
 
 /* Set BLOCK to be the lexical scope block holding statement G.  */
 
 static inline void
 gimple_set_block (gimple *g, tree block)
 {
-  g->location = set_block (g->location, block);
+  gimple_set_location (g, set_block (g->location, block));
 }
 
-
 /* Return location information for statement G.  */
 
 static inline location_t
@@ -1886,6 +1902,8 @@  gimple_location_safe (const gimple *g)
 static inline void
 gimple_set_location (gimple *g, location_t location)
 {
+  /* Copy the no-warning data to the statement location.  */
+  copy_no_warning (location, g->location);
   g->location = location;
 }
 
@@ -1948,22 +1966,6 @@  gimple_seq_singleton_p (gimple_seq seq)
 	  && (gimple_seq_first (seq) == gimple_seq_last (seq)));
 }
 
-/* Return true if no warnings should be emitted for statement STMT.  */
-
-static inline bool
-gimple_no_warning_p (const gimple *stmt)
-{
-  return stmt->no_warning;
-}
-
-/* Set the no_warning flag of STMT to NO_WARNING.  */
-
-static inline void
-gimple_set_no_warning (gimple *stmt, bool no_warning)
-{
-  stmt->no_warning = (unsigned) no_warning;
-}
-
 /* Set the visited status on statement STMT to VISITED_P.
 
    Please note that this 'visited' property of the gimple statement is
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 719a4e16391..20445b89e27 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1596,7 +1596,7 @@  gimplify_return_expr (tree stmt, gimple_seq *pre_p)
     {
       maybe_add_early_return_predict_stmt (pre_p);
       greturn *ret = gimple_build_return (ret_expr);
-      gimple_set_no_warning (ret, TREE_NO_WARNING (stmt));
+      copy_no_warning (ret, stmt);
       gimplify_seq_add_stmt (pre_p, ret);
       return GS_ALL_DONE;
     }
@@ -1658,7 +1658,7 @@  gimplify_return_expr (tree stmt, gimple_seq *pre_p)
 	 we can wind up warning about an uninitialized value for this.  Due
 	 to how this variable is constructed and initialized, this is never
 	 true.  Give up and never warn.  */
-      TREE_NO_WARNING (result) = 1;
+      set_no_warning (result, OPT_Wuninitialized);
 
       gimplify_ctxp->return_temp = result;
     }
@@ -1672,7 +1672,7 @@  gimplify_return_expr (tree stmt, gimple_seq *pre_p)
 
   maybe_add_early_return_predict_stmt (pre_p);
   ret = gimple_build_return (result);
-  gimple_set_no_warning (ret, TREE_NO_WARNING (stmt));
+  copy_no_warning (ret, stmt);
   gimplify_seq_add_stmt (pre_p, ret);
 
   return GS_ALL_DONE;
@@ -4244,7 +4244,8 @@  gimplify_cond_expr (tree *expr_p, gimple_seq *pre_p, fallback_t fallback)
 				 &arm2);
   cond_stmt = gimple_build_cond (pred_code, arm1, arm2, label_true,
 				 label_false);
-  gimple_set_no_warning (cond_stmt, TREE_NO_WARNING (COND_EXPR_COND (expr)));
+  gimple_set_location (cond_stmt, EXPR_LOCATION (expr));
+  copy_no_warning (cond_stmt, COND_EXPR_COND (expr));
   gimplify_seq_add_stmt (&seq, cond_stmt);
   gimple_stmt_iterator gsi = gsi_last (seq);
   maybe_fold_stmt (&gsi);
@@ -5694,7 +5695,7 @@  gimplify_modify_expr_complex_part (tree *expr_p, gimple_seq *pre_p,
 
   ocode = code == REALPART_EXPR ? IMAGPART_EXPR : REALPART_EXPR;
   other = build1 (ocode, TREE_TYPE (rhs), lhs);
-  TREE_NO_WARNING (other) = 1;
+  set_no_warning (other);
   other = get_formal_tmp_var (other, pre_p);
 
   realpart = code == REALPART_EXPR ? rhs : other;
@@ -5979,7 +5980,7 @@  gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
       assign = gimple_build_assign (*to_p, *from_p);
       gimple_set_location (assign, EXPR_LOCATION (*expr_p));
       if (COMPARISON_CLASS_P (*from_p))
-	gimple_set_no_warning (assign, TREE_NO_WARNING (*from_p));
+	copy_no_warning (assign, *from_p);
     }
 
   if (gimplify_ctxp->into_ssa && is_gimple_reg (*to_p))
@@ -6738,7 +6739,7 @@  gimple_push_cleanup (tree var, tree cleanup, bool eh_only, gimple_seq *pre_p,
 	  /* Because of this manipulation, and the EH edges that jump
 	     threading cannot redirect, the temporary (VAR) will appear
 	     to be used uninitialized.  Don't warn.  */
-	  TREE_NO_WARNING (var) = 1;
+	  set_no_warning (var, OPT_Wuninitialized);
 	}
     }
   else
@@ -14470,7 +14471,7 @@  gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 
 	    gimplify_and_add (EH_FILTER_FAILURE (*expr_p), &failure);
 	    ehf = gimple_build_eh_filter (EH_FILTER_TYPES (*expr_p), failure);
-	    gimple_set_no_warning (ehf, TREE_NO_WARNING (*expr_p));
+	    copy_no_warning (ehf, *expr_p);
 	    gimplify_seq_add_stmt (pre_p, ehf);
 	    ret = GS_ALL_DONE;
 	    break;
diff --git a/gcc/omp-expand.c b/gcc/omp-expand.c
index 0f843bad79a..dbccbfdeefa 100644
--- a/gcc/omp-expand.c
+++ b/gcc/omp-expand.c
@@ -3845,7 +3845,7 @@  expand_omp_for_generic (struct omp_region *region,
 	  for (i = first_zero_iter1;
 	       i < (fd->ordered ? fd->ordered : fd->collapse); i++)
 	    if (SSA_VAR_P (counts[i]))
-	      TREE_NO_WARNING (counts[i]) = 1;
+	      set_no_warning (counts[i], OPT_Wuninitialized);
 	  gsi_prev (&gsi);
 	  e = split_block (entry_bb, gsi_stmt (gsi));
 	  entry_bb = e->dest;
@@ -3862,7 +3862,7 @@  expand_omp_for_generic (struct omp_region *region,
 	     be executed in that case, so just avoid uninit warnings.  */
 	  for (i = first_zero_iter2; i < fd->ordered; i++)
 	    if (SSA_VAR_P (counts[i]))
-	      TREE_NO_WARNING (counts[i]) = 1;
+	      set_no_warning (counts[i], OPT_Wuninitialized);
 	  if (zero_iter1_bb)
 	    make_edge (zero_iter2_bb, entry_bb, EDGE_FALLTHRU);
 	  else
@@ -7051,7 +7051,7 @@  expand_omp_taskloop_for_outer (struct omp_region *region,
 	     be executed in that case, so just avoid uninit warnings.  */
 	  for (i = first_zero_iter; i < fd->collapse; i++)
 	    if (SSA_VAR_P (counts[i]))
-	      TREE_NO_WARNING (counts[i]) = 1;
+	      set_no_warning (counts[i], OPT_Wuninitialized);
 	  gsi_prev (&gsi);
 	  edge e = split_block (entry_bb, gsi_stmt (gsi));
 	  entry_bb = e->dest;
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index d1136d181b3..cb20fdf7609 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -5614,7 +5614,7 @@  lower_rec_input_clauses (tree clauses, gimple_seq *ilist, gimple_seq *dlist,
 		 able to notice this and not store anything at all, but
 		 we're generating code too early.  Suppress the warning.  */
 	      if (!by_ref)
-		TREE_NO_WARNING (var) = 1;
+		set_no_warning (var, OPT_Wuninitialized);
 	      break;
 
 	    case OMP_CLAUSE__CONDTEMP_:
@@ -6575,7 +6575,7 @@  lower_rec_input_clauses (tree clauses, gimple_seq *ilist, gimple_seq *dlist,
       uid = create_tmp_var (ptr_type_node, "simduid");
       /* Don't want uninit warnings on simduid, it is always uninitialized,
 	 but we use it not for the value, but for the DECL_UID only.  */
-      TREE_NO_WARNING (uid) = 1;
+      set_no_warning (uid, OPT_Wuninitialized);
       c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__SIMDUID_);
       OMP_CLAUSE__SIMDUID__DECL (c) = uid;
       OMP_CLAUSE_CHAIN (c) = gimple_omp_for_clauses (ctx->stmt);
@@ -7003,7 +7003,7 @@  lower_lastprivate_clauses (tree clauses, tree predicate, gimple_seq *body_p,
 	      if (predicate
 		  && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE
 		      || OMP_CLAUSE_LINEAR_NO_COPYIN (c)))
-		TREE_NO_WARNING (new_var) = 1;
+		set_no_warning (new_var, OPT_Wuninitialized);
 	    }
 
 	  if (!maybe_simt && simduid && DECL_HAS_VALUE_EXPR_P (new_var))
@@ -7834,7 +7834,7 @@  lower_send_clauses (tree clauses, gimple_seq *ilist, gimple_seq *olist,
 	  if (OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c)
 	      && !by_ref
 	      && is_task_ctx (ctx))
-	    TREE_NO_WARNING (var) = 1;
+	    set_no_warning (var);
 	  do_in = true;
 	  break;
 
@@ -12388,7 +12388,7 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		      {
 			if (is_gimple_reg (var)
 			    && OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c))
-			  TREE_NO_WARNING (var) = 1;
+			  set_no_warning (var);
 			var = build_fold_addr_expr (var);
 		      }
 		    else
@@ -12412,7 +12412,7 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 			   we'll get a warning for the store to avar.
 			   Don't warn in that case, the mapping might
 			   be implicit.  */
-			TREE_NO_WARNING (var) = 1;
+			set_no_warning (var, OPT_Wuninitialized);
 			gimplify_assign (avar, var, &ilist);
 		      }
 		    avar = build_fold_addr_expr (avar);
@@ -12566,7 +12566,7 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		if (omp_is_reference (var))
 		  t = build_simple_mem_ref (var);
 		else if (OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c))
-		  TREE_NO_WARNING (var) = 1;
+		  set_no_warning (var);
 		if (TREE_CODE (type) != POINTER_TYPE)
 		  t = fold_convert (pointer_sized_int_node, t);
 		t = fold_convert (TREE_TYPE (x), t);
@@ -12579,7 +12579,7 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		tree avar = create_tmp_var (TREE_TYPE (var));
 		mark_addressable (avar);
 		if (OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c))
-		  TREE_NO_WARNING (var) = 1;
+		  set_no_warning (var);
 		gimplify_assign (avar, var, &ilist);
 		avar = build_fold_addr_expr (avar);
 		gimplify_assign (x, avar, &ilist);
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index 02256580c98..c85e9146508 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -9428,7 +9428,7 @@  pass_warn_function_return::execute (function *fun)
   /* If we see "return;" in some basic block, then we do reach the end
      without returning a value.  */
   else if (warn_return_type > 0
-	   && !TREE_NO_WARNING (fun->decl)
+	   && !get_no_warning (fun->decl, OPT_Wreturn_type)
 	   && !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fun->decl))))
     {
       FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (fun)->preds)
@@ -9437,14 +9437,14 @@  pass_warn_function_return::execute (function *fun)
 	  greturn *return_stmt = dyn_cast <greturn *> (last);
 	  if (return_stmt
 	      && gimple_return_retval (return_stmt) == NULL
-	      && !gimple_no_warning_p (last))
+	      && !get_no_warning (last, OPT_Wreturn_type))
 	    {
 	      location = gimple_location (last);
 	      if (LOCATION_LOCUS (location) == UNKNOWN_LOCATION)
 		location = fun->function_end_locus;
 	      if (warning_at (location, OPT_Wreturn_type,
 			      "control reaches end of non-void function"))
-		TREE_NO_WARNING (fun->decl) = 1;
+		set_no_warning (fun->decl, OPT_Wreturn_type);
 	      break;
 	    }
 	}
@@ -9452,7 +9452,7 @@  pass_warn_function_return::execute (function *fun)
 	 into __builtin_unreachable () call with BUILTINS_LOCATION.
 	 Recognize those too.  */
       basic_block bb;
-      if (!TREE_NO_WARNING (fun->decl))
+      if (!get_no_warning (fun->decl, OPT_Wreturn_type))
 	FOR_EACH_BB_FN (bb, fun)
 	  if (EDGE_COUNT (bb->succs) == 0)
 	    {
@@ -9476,7 +9476,7 @@  pass_warn_function_return::execute (function *fun)
 		    location = fun->function_end_locus;
 		  if (warning_at (location, OPT_Wreturn_type,
 				  "control reaches end of non-void function"))
-		    TREE_NO_WARNING (fun->decl) = 1;
+		    set_no_warning (fun->decl, OPT_Wreturn_type);
 		  break;
 		}
 	    }
diff --git a/gcc/tree-complex.c b/gcc/tree-complex.c
index d7d991714de..c5d57346023 100644
--- a/gcc/tree-complex.c
+++ b/gcc/tree-complex.c
@@ -456,12 +456,12 @@  create_one_component_var (tree type, tree orig, const char *prefix,
       SET_DECL_DEBUG_EXPR (r, build1 (code, type, orig));
       DECL_HAS_DEBUG_EXPR_P (r) = 1;
       DECL_IGNORED_P (r) = 0;
-      TREE_NO_WARNING (r) = TREE_NO_WARNING (orig);
+      copy_no_warning (r, orig);
     }
   else
     {
       DECL_IGNORED_P (r) = 1;
-      TREE_NO_WARNING (r) = 1;
+      set_no_warning (r);
     }
 
   return r;
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 8f945b88c12..2fea85c34bf 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -1116,7 +1116,7 @@  remap_gimple_op_r (tree *tp, int *walk_subtrees, void *data)
 	  *tp = fold_build2 (MEM_REF, type, ptr, TREE_OPERAND (*tp, 1));
 	  TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old);
 	  TREE_SIDE_EFFECTS (*tp) = TREE_SIDE_EFFECTS (old);
-	  TREE_NO_WARNING (*tp) = TREE_NO_WARNING (old);
+	  copy_no_warning (*tp, old);
 	  if (MR_DEPENDENCE_CLIQUE (old) != 0)
 	    {
 	      MR_DEPENDENCE_CLIQUE (*tp)
@@ -1375,7 +1375,7 @@  copy_tree_body_r (tree *tp, int *walk_subtrees, void *data)
 	  *tp = fold_build2 (MEM_REF, type, ptr, TREE_OPERAND (*tp, 1));
 	  TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old);
 	  TREE_SIDE_EFFECTS (*tp) = TREE_SIDE_EFFECTS (old);
-	  TREE_NO_WARNING (*tp) = TREE_NO_WARNING (old);
+	  copy_no_warning (*tp, old);
 	  if (MR_DEPENDENCE_CLIQUE (old) != 0)
 	    {
 	      MR_DEPENDENCE_CLIQUE (*tp)
@@ -3753,7 +3753,7 @@  declare_return_variable (copy_body_data *id, tree return_slot, tree modify_dest,
 
   /* Do not have the rest of GCC warn about this variable as it should
      not be visible to the user.  */
-  TREE_NO_WARNING (var) = 1;
+  set_no_warning (var /* OPT_Wuninitialized? */);
 
   declare_inline_vars (id->block, var);
 
@@ -5018,7 +5018,7 @@  expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id,
 	 initialized.  We do not want to issue a warning about that
 	 uninitialized variable.  */
       if (DECL_P (modify_dest))
-	TREE_NO_WARNING (modify_dest) = 1;
+	set_no_warning (modify_dest, OPT_Wuninitialized);
 
       if (gimple_call_return_slot_opt_p (call_stmt))
 	{
diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
index cea917a4d58..5d9e59134d2 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -411,8 +411,8 @@  lookup_field_for_decl (struct nesting_info *info, tree decl,
 	  DECL_USER_ALIGN (field) = DECL_USER_ALIGN (decl);
 	  DECL_IGNORED_P (field) = DECL_IGNORED_P (decl);
 	  DECL_NONADDRESSABLE_P (field) = !TREE_ADDRESSABLE (decl);
-	  TREE_NO_WARNING (field) = TREE_NO_WARNING (decl);
 	  TREE_THIS_VOLATILE (field) = TREE_THIS_VOLATILE (decl);
+	  copy_no_warning (field, decl);
 
 	  /* Declare the transformation and adjust the original DECL.  For a
 	     variable or for a parameter when not optimizing, we make it point
diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
index 8dfc923ed7e..2e3efc31666 100644
--- a/gcc/tree-sra.c
+++ b/gcc/tree-sra.c
@@ -2240,12 +2240,12 @@  create_access_replacement (struct access *access, tree reg_type = NULL_TREE)
 	  DECL_HAS_DEBUG_EXPR_P (repl) = 1;
 	}
       if (access->grp_no_warning)
-	TREE_NO_WARNING (repl) = 1;
+	set_no_warning (repl /* Be more selective! */);
       else
-	TREE_NO_WARNING (repl) = TREE_NO_WARNING (access->base);
+	copy_no_warning (repl, access->base);
     }
   else
-    TREE_NO_WARNING (repl) = 1;
+    set_no_warning (repl /* Be more selective! */);
 
   if (dump_file)
     {
@@ -3556,7 +3556,7 @@  generate_subtree_copies (struct access *access, tree agg,
 	    }
 	  else
 	    {
-	      TREE_NO_WARNING (repl) = 1;
+	      set_no_warning (repl /* Be more selective! */);
 	      if (access->grp_partial_lhs)
 		repl = force_gimple_operand_gsi (gsi, repl, true, NULL_TREE,
 						 !insert_after,
diff --git a/gcc/tree-ssa-ccp.c b/gcc/tree-ssa-ccp.c
index 3834212b867..f0303492f8e 100644
--- a/gcc/tree-ssa-ccp.c
+++ b/gcc/tree-ssa-ccp.c
@@ -3527,7 +3527,7 @@  pass_post_ipa_warn::execute (function *fun)
       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
 	{
 	  gimple *stmt = gsi_stmt (gsi);
-	  if (!is_gimple_call (stmt) || gimple_no_warning_p (stmt))
+	  if (!is_gimple_call (stmt) || get_no_warning (stmt, OPT_Wnonnull))
 	    continue;
 
 	  tree fntype = gimple_call_fntype (stmt);
diff --git a/gcc/tree-ssa-forwprop.c b/gcc/tree-ssa-forwprop.c
index 0706fd862de..fa72ff007a2 100644
--- a/gcc/tree-ssa-forwprop.c
+++ b/gcc/tree-ssa-forwprop.c
@@ -403,7 +403,8 @@  combine_cond_expr_cond (gimple *stmt, enum tree_code code, tree type,
       return NULL_TREE;
     }
 
-  fold_undefer_overflow_warnings (!gimple_no_warning_p (stmt), stmt, 0);
+  bool nowarn = get_no_warning (stmt, OPT_Wstrict_overflow);
+  fold_undefer_overflow_warnings (!nowarn, stmt, 0);
 
   return t;
 }
diff --git a/gcc/tree-ssa-loop-ch.c b/gcc/tree-ssa-loop-ch.c
index 08caa83b4b4..9051820c8a7 100644
--- a/gcc/tree-ssa-loop-ch.c
+++ b/gcc/tree-ssa-loop-ch.c
@@ -458,7 +458,7 @@  ch_base::copy_headers (function *fun)
 			  && gimple_cond_code (stmt) != NE_EXPR
 			  && INTEGRAL_TYPE_P (TREE_TYPE (lhs))
 			  && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (lhs)))
-			gimple_set_no_warning (stmt, true);
+			set_no_warning (stmt, OPT_Wstrict_overflow_);
 		    }
 		  else if (is_gimple_assign (stmt))
 		    {
@@ -469,7 +469,7 @@  ch_base::copy_headers (function *fun)
 			  && rhs_code != NE_EXPR
 			  && INTEGRAL_TYPE_P (TREE_TYPE (rhs1))
 			  && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (rhs1)))
-			gimple_set_no_warning (stmt, true);
+			set_no_warning (stmt, OPT_Wstrict_overflow_);
 		    }
 		}
 	    }
diff --git a/gcc/tree-ssa-loop-im.c b/gcc/tree-ssa-loop-im.c
index 8034cf68d27..64c63c6ee8b 100644
--- a/gcc/tree-ssa-loop-im.c
+++ b/gcc/tree-ssa-loop-im.c
@@ -2143,7 +2143,7 @@  execute_sm (class loop *loop, im_mem_ref *ref,
       /* If not emitting a load mark the uninitialized state on the
 	 loop entry as not to be warned for.  */
       tree uninit = create_tmp_reg (TREE_TYPE (aux->tmp_var));
-      TREE_NO_WARNING (uninit) = 1;
+      set_no_warning (uninit, OPT_Wuninitialized);
       load = gimple_build_assign (aux->tmp_var, uninit);
     }
   lim_data = init_lim_data (load);
diff --git a/gcc/tree-ssa-phiopt.c b/gcc/tree-ssa-phiopt.c
index 8e8a08bc679..58b764991ff 100644
--- a/gcc/tree-ssa-phiopt.c
+++ b/gcc/tree-ssa-phiopt.c
@@ -3014,9 +3014,12 @@  cond_store_replacement (basic_block middle_bb, basic_block join_bb,
   new_stmt = gimple_build_assign (name, lhs);
   gimple_set_location (new_stmt, locus);
   lhs = unshare_expr (lhs);
-  /* Set TREE_NO_WARNING on the rhs of the load to avoid uninit
-     warnings.  */
-  TREE_NO_WARNING (gimple_assign_rhs1 (new_stmt)) = 1;
+  {
+    /* Set the no-warning bit on the rhs of the load to avoid uninit
+       warnings.  */
+    tree rhs1 = gimple_assign_rhs1 (new_stmt);
+    set_no_warning (rhs1, OPT_Wuninitialized);
+  }
   gsi_insert_on_edge (e1, new_stmt);
 
   /* 3) Create a PHI node at the join block, with one argument
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index c7b5e2c6e6b..88728a2c7f2 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -1918,7 +1918,7 @@  maybe_warn_overflow (gimple *stmt, tree len, pointer_query &ptr_qry,
 		     strinfo *si = NULL, bool plus_one = false,
 		     bool rawmem = false)
 {
-  if (!len || gimple_no_warning_p (stmt))
+  if (!len || get_no_warning (stmt, OPT_Wstringop_overflow_))
     return;
 
   /* The DECL of the function performing the write if it is done
@@ -1937,7 +1937,7 @@  maybe_warn_overflow (gimple *stmt, tree len, pointer_query &ptr_qry,
   else
     return;
 
-  if (TREE_NO_WARNING (dest))
+  if (get_no_warning (dest, OPT_Wstringop_overflow_))
     return;
 
   const int ostype = rawmem ? 0 : 1;
@@ -2081,7 +2081,7 @@  maybe_warn_overflow (gimple *stmt, tree len, pointer_query &ptr_qry,
   if (!warned)
     return;
 
-  gimple_set_no_warning (stmt, true);
+  set_no_warning (stmt, OPT_Wstringop_overflow_);
 
   aref.inform_access (access_write_only);
 }
@@ -2605,15 +2605,15 @@  handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
   len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
 
   /* Set the no-warning bit on the transformed statement?  */
-  bool set_no_warning = false;
+  int no_warning_opt = 0;
 
-  if (const strinfo *chksi = olddsi ? olddsi : dsi)
-    if (si
-	&& check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
-      {
-	gimple_set_no_warning (stmt, true);
-	set_no_warning = true;
-      }
+  if (const strinfo *chksi = si ? olddsi ? olddsi : dsi : NULL)
+    {
+      no_warning_opt = check_bounds_or_overlap (stmt, chksi->ptr, si->ptr,
+						NULL_TREE, len);
+      if (no_warning_opt)
+	set_no_warning (stmt, no_warning_opt);
+    }
 
   if (fn == NULL_TREE)
     return;
@@ -2647,8 +2647,8 @@  handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
   else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     fprintf (dump_file, "not possible.\n");
 
-  if (set_no_warning)
-    gimple_set_no_warning (stmt, true);
+  if (no_warning_opt)
+    set_no_warning (stmt, no_warning_opt);
 }
 
 /* Check the size argument to the built-in forms of stpncpy and strncpy
@@ -2776,7 +2776,7 @@  maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
 			  pointer_query *ptr_qry /* = NULL */)
 {
   gimple *stmt = gsi_stmt (gsi);
-  if (gimple_no_warning_p (stmt))
+  if (get_no_warning (stmt, OPT_Wstringop_truncation))
     return false;
 
   wide_int cntrange[2];
@@ -3135,9 +3135,9 @@  handle_builtin_stxncpy_strncat (bool append_p, gimple_stmt_iterator *gsi)
   else
     srclenp1 = NULL_TREE;
 
-  if (check_bounds_or_overlap (stmt, dst, src, dstlenp1, srclenp1))
+  if (int opt = check_bounds_or_overlap (stmt, dst, src, dstlenp1, srclenp1))
     {
-      gimple_set_no_warning (stmt, true);
+      set_no_warning (stmt, opt);
       return;
     }
 
@@ -3148,7 +3148,7 @@  handle_builtin_stxncpy_strncat (bool append_p, gimple_stmt_iterator *gsi)
   if (!pss || pss->first <= 0)
     {
       if (maybe_diag_stxncpy_trunc (*gsi, src, len))
-	gimple_set_no_warning (stmt, true);
+	set_no_warning (stmt, OPT_Wstringop_truncation);
 
       return;
     }
@@ -3428,7 +3428,7 @@  handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi,
     }
 
   /* Set the no-warning bit on the transformed statement?  */
-  bool set_no_warning = false;
+  int no_warning_opt = 0;
 
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
@@ -3445,12 +3445,10 @@  handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi,
 	  }
 
 	tree sptr = si && si->ptr ? si->ptr : src;
-
-	if (check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
-	  {
-	    gimple_set_no_warning (stmt, true);
-	    set_no_warning = true;
-	  }
+	no_warning_opt = check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE,
+						  slen);
+	if (no_warning_opt)
+	  set_no_warning (stmt, no_warning_opt);
       }
 
       /* strcat (p, q) can be transformed into
@@ -3557,11 +3555,10 @@  handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi,
       tree dstsize = fold_build2 (PLUS_EXPR, type, dstlen, one);
       tree sptr = si && si->ptr ? si->ptr : src;
 
-      if (check_bounds_or_overlap (stmt, dst, sptr, dstsize, srcsize))
-	{
-	  gimple_set_no_warning (stmt, true);
-	  set_no_warning = true;
-	}
+      no_warning_opt = check_bounds_or_overlap (stmt, dst, sptr, dstsize,
+						srcsize);
+      if (no_warning_opt)
+	set_no_warning (stmt, no_warning_opt);
     }
 
   tree len = NULL_TREE;
@@ -3627,8 +3624,8 @@  handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi,
   else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     fprintf (dump_file, "not possible.\n");
 
-  if (set_no_warning)
-    gimple_set_no_warning (stmt, true);
+  if (no_warning_opt)
+    set_no_warning (stmt, no_warning_opt);
 }
 
 /* Handle a call to an allocation function like alloca, malloc or calloc,
diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c
index f55ce1939ac..e1d50ab92a7 100644
--- a/gcc/tree-ssa-uninit.c
+++ b/gcc/tree-ssa-uninit.c
@@ -86,17 +86,33 @@  has_undefined_value_p (tree t)
 	      && possibly_undefined_names->contains (t)));
 }
 
-/* Like has_undefined_value_p, but don't return true if TREE_NO_WARNING
-   is set on SSA_NAME_VAR.  */
+/* Return true if EXPR should suppress either uninitialized warning.  */
+
+static inline bool
+get_no_uninit_warning (tree expr)
+{
+  return get_no_warning (expr, OPT_Wuninitialized);
+}
+
+/* Suppress both uninitialized warnings for EXPR.  */
+
+static inline void
+set_no_uninit_warning (tree expr)
+{
+  set_no_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) && TREE_NO_WARNING (SSA_NAME_VAR (t)))
-    return false;
-  return true;
+  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.
@@ -164,10 +180,10 @@  warn_uninit (enum opt_code wc, tree t, tree expr, tree var,
   /* TREE_NO_WARNING either means we already warned, or the front end
      wishes to suppress the warning.  */
   if ((context
-       && (gimple_no_warning_p (context)
+       && (get_no_warning (context, OPT_Wuninitialized)
 	   || (gimple_assign_single_p (context)
-	       && TREE_NO_WARNING (gimple_assign_rhs1 (context)))))
-      || TREE_NO_WARNING (expr))
+	       && get_no_uninit_warning (gimple_assign_rhs1 (context)))))
+      || get_no_uninit_warning (expr))
     return;
 
   if (context != NULL && gimple_has_location (context))
@@ -184,7 +200,7 @@  warn_uninit (enum opt_code wc, tree t, tree expr, tree var,
   auto_diagnostic_group d;
   if (warning_at (location, wc, gmsgid, expr))
     {
-      TREE_NO_WARNING (expr) = 1;
+      set_no_warning (expr, wc);
 
       if (location == DECL_SOURCE_LOCATION (var))
 	return;
@@ -259,7 +275,7 @@  maybe_warn_operand (ao_ref &ref, gimple *stmt, tree lhs, tree rhs,
   use_operand_p luse_p;
   imm_use_iterator liter;
 
-  if (TREE_NO_WARNING (rhs))
+  if (get_no_uninit_warning (rhs))
     return NULL_TREE;
 
   /* Do not warn if the base was marked so or this is a
@@ -267,7 +283,7 @@  maybe_warn_operand (ao_ref &ref, gimple *stmt, tree lhs, tree rhs,
   tree base = ao_ref_base (&ref);
   if ((VAR_P (base)
        && DECL_HARD_REGISTER (base))
-      || TREE_NO_WARNING (base))
+      || get_no_uninit_warning (base))
     return NULL_TREE;
 
   /* Do not warn if the access is fully outside of the variable.  */
@@ -406,7 +422,7 @@  maybe_warn_operand (ao_ref &ref, gimple *stmt, tree lhs, tree rhs,
     rhs = TREE_OPERAND (rhs, 0);
 
   /* Check again since RHS may have changed above.  */
-  if (TREE_NO_WARNING (rhs))
+  if (get_no_uninit_warning (rhs))
     return NULL_TREE;
 
   /* Avoid warning about empty types such as structs with no members.
@@ -434,7 +450,7 @@  maybe_warn_operand (ao_ref &ref, gimple *stmt, tree lhs, tree rhs,
 	     uses or accesses by functions as it may hide important
 	     locations.  */
 	  if (lhs)
-	    TREE_NO_WARNING (rhs) = 1;
+	    set_no_uninit_warning (rhs);
 	  warned = true;
 	}
     }
diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
index 12e6e6f3e22..d9aede22d6e 100644
--- a/gcc/tree-vrp.c
+++ b/gcc/tree-vrp.c
@@ -406,10 +406,10 @@  compare_values_warnv (tree val1, tree val2, bool *strict_overflow_p)
 	return -2;
 
       if (strict_overflow_p != NULL
-	  /* Symbolic range building sets TREE_NO_WARNING to declare
+	  /* Symbolic range building sets the no-warning bit to declare
 	     that overflow doesn't happen.  */
-	  && (!inv1 || !TREE_NO_WARNING (val1))
-	  && (!inv2 || !TREE_NO_WARNING (val2)))
+	  && (!inv1 || !get_no_warning (val1, OPT_Woverflow))
+	  && (!inv2 || !get_no_warning (val2, OPT_Woverflow)))
 	*strict_overflow_p = true;
 
       if (!inv1)
@@ -432,10 +432,10 @@  compare_values_warnv (tree val1, tree val2, bool *strict_overflow_p)
 	return -2;
 
       if (strict_overflow_p != NULL
-	  /* Symbolic range building sets TREE_NO_WARNING to declare
+	  /* Symbolic range building sets the no-warning bit to declare
 	     that overflow doesn't happen.  */
-	  && (!sym1 || !TREE_NO_WARNING (val1))
-	  && (!sym2 || !TREE_NO_WARNING (val2)))
+	  && (!sym1 || !get_no_warning (val1, OPT_Woverflow))
+	  && (!sym2 || !get_no_warning (val2, OPT_Woverflow)))
 	*strict_overflow_p = true;
 
       const signop sgn = TYPE_SIGN (TREE_TYPE (val1));
diff --git a/gcc/tree.h b/gcc/tree.h
index 64612cfa368..37593d0a3d8 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -699,13 +699,6 @@  extern void omp_clause_range_check_failed (const_tree, const char *, int,
 /* Determines whether an ENUMERAL_TYPE has defined the list of constants. */
 #define ENUM_IS_OPAQUE(NODE) (ENUMERAL_TYPE_CHECK (NODE)->base.private_flag)
 
-/* In an expr node (usually a conversion) this means the node was made
-   implicitly and should not lead to any sort of warning.  In a decl node,
-   warnings concerning the decl should be suppressed.  This is used at
-   least for used-before-set warnings, and it set after one warning is
-   emitted.  */
-#define TREE_NO_WARNING(NODE) ((NODE)->base.nowarning_flag)
-
 /* Nonzero if we should warn about the change in empty class parameter
    passing ABI in this TU.  */
 #define TRANSLATION_UNIT_WARN_EMPTY_P(NODE) \
@@ -6430,4 +6423,22 @@  public:
   operator location_t () const { return m_combined_loc; }
 };
 
+/* Return true if a warning is enabled for the decl/expression.  */
+extern bool get_no_warning (const_tree, int = -1) ATTRIBUTE_NONNULL (1);
+/* Enable, or by default disable, a warning for the expression.  */
+extern void set_no_warning (tree, int = -1, bool = true)
+  ATTRIBUTE_NONNULL (1);
+/* Copy the warning disposition mapping from one expression to another.  */
+extern void copy_no_warning (tree, const_tree)
+  ATTRIBUTE_NONNULL (1) ATTRIBUTE_NONNULL (2);
+
+/* Return the disposition for a warning (or all warnings by default)
+   at a location.  */
+extern bool get_no_warning (location_t, int = -1);
+/* Set the disposition for a warning (or all warnings by default)
+   at a location to disabled by default.  */
+extern bool set_no_warning (location_t, int = -1, bool = true);
+/* Copy warning disposition from one location to another.  */
+extern void copy_no_warning (location_t, location_t);
+
 #endif  /* GCC_TREE_H  */
diff --git a/gcc/vr-values.c b/gcc/vr-values.c
index 02bc0db9088..67c0e35033a 100644
--- a/gcc/vr-values.c
+++ b/gcc/vr-values.c
@@ -697,7 +697,7 @@  vr_values::extract_range_for_var_from_comparison_expr (tree var,
 				   build_int_cst (TREE_TYPE (max), 1));
 	      /* Signal to compare_values_warnv this expr doesn't overflow.  */
 	      if (EXPR_P (max))
-		TREE_NO_WARNING (max) = 1;
+		set_no_warning (max, OPT_Woverflow);
 	    }
 
 	  vr_p->update (min, max);
@@ -737,7 +737,7 @@  vr_values::extract_range_for_var_from_comparison_expr (tree var,
 				   build_int_cst (TREE_TYPE (min), 1));
 	      /* Signal to compare_values_warnv this expr doesn't overflow.  */
 	      if (EXPR_P (min))
-		TREE_NO_WARNING (min) = 1;
+		set_no_warning (min, OPT_Woverflow);
 	    }
 
 	  vr_p->update (min, max);
@@ -3353,7 +3353,7 @@  test_for_singularity (enum tree_code cond_code, tree op0,
 	  max = fold_build2 (MINUS_EXPR, TREE_TYPE (op0), max, one);
 	  /* Signal to compare_values_warnv this expr doesn't overflow.  */
 	  if (EXPR_P (max))
-	    TREE_NO_WARNING (max) = 1;
+	    set_no_warning (max, OPT_Woverflow);
 	}
     }
   else if (cond_code == GE_EXPR || cond_code == GT_EXPR)
@@ -3367,7 +3367,7 @@  test_for_singularity (enum tree_code cond_code, tree op0,
 	  min = fold_build2 (PLUS_EXPR, TREE_TYPE (op0), min, one);
 	  /* Signal to compare_values_warnv this expr doesn't overflow.  */
 	  if (EXPR_P (min))
-	    TREE_NO_WARNING (min) = 1;
+	    set_no_warning (min, OPT_Woverflow);
 	}
     }