diff mbox series

[10/13] v2 Use new per-location warning APIs in the middle end

Message ID c2505085-b4f8-0366-7365-eb9f3cec934e@gmail.com
State New
Headers show
Series v2 warning control by group and location (PR 74765) | expand

Commit Message

Martin Sebor June 4, 2021, 9:43 p.m. UTC
The attached patch introduces declarations of the new
suppress_warning(), warning_suppressed_p(), and copy_warning() APIs,
and replaces the uses of TREE_NO_WARNING in the middle end with them.

Comments

Martin Sebor June 21, 2021, 9:58 p.m. UTC | #1
Ping: https://gcc.gnu.org/pipermail/gcc-patches/2021-June/571981.html

Looking for a review of the middle end changes to replace the uses
of TREE_NO_WARNING and gimple_{get,set}_no_warning with the new
warning group APIs.  Most of the changes are a mechanical search
and replace kind, just a handful do ever-so-slightly more than
that, but none of those should have any observable effect.

On 6/4/21 3:43 PM, Martin Sebor wrote:
> The attached patch introduces declarations of the new
> suppress_warning(), warning_suppressed_p(), and copy_warning() APIs,
> and replaces the uses of TREE_NO_WARNING in the middle end with them.
Jeff Law June 24, 2021, 5:15 a.m. UTC | #2
On 6/4/2021 3:43 PM, Martin Sebor via Gcc-patches wrote:
> The attached patch introduces declarations of the new
> suppress_warning(), warning_suppressed_p(), and copy_warning() APIs,
> and replaces the uses of TREE_NO_WARNING in the middle end with them.
>
> gcc-no-warning-middle-end.diff
>
> 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
> 	warning_suppressed_p, and suppress_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 (warning_suppressed_p): New function.
> 	(suppress_warning): Same.
> 	(copy_no_warning): Same.
> 	(gimple_set_block): Call gimple_set_location.
> 	(gimple_set_location): Call copy_warning.
> 	* tree.h (no_warning, all_warnings): New constants.
> 	(warning_suppressed_p): New function.
> 	(suppress_warning): Same.
> 	(copy_no_warning): Same.
OK once prereqs are approved.

jeff
Martin Sebor June 25, 2021, 1:40 a.m. UTC | #3
On 6/23/21 11:15 PM, Jeff Law wrote:
> 
> 
> On 6/4/2021 3:43 PM, Martin Sebor via Gcc-patches wrote:
>> The attached patch introduces declarations of the new
>> suppress_warning(), warning_suppressed_p(), and copy_warning() APIs,
>> and replaces the uses of TREE_NO_WARNING in the middle end with them.
>>
>> gcc-no-warning-middle-end.diff
>>
>> 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
>> 	warning_suppressed_p, and suppress_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 (warning_suppressed_p): New function.
>> 	(suppress_warning): Same.
>> 	(copy_no_warning): Same.
>> 	(gimple_set_block): Call gimple_set_location.
>> 	(gimple_set_location): Call copy_warning.
>> 	* tree.h (no_warning, all_warnings): New constants.
>> 	(warning_suppressed_p): New function.
>> 	(suppress_warning): Same.
>> 	(copy_no_warning): Same.
> OK once prereqs are approved.
> 
> jeff
> 

Retested on top patch 1 plus the C and C++ bits and pushed r12-1805.
I'll do the rest of the approved patches tomorrow.

Martin
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
	warning_suppressed_p, and suppress_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 (warning_suppressed_p): New function.
	(suppress_warning): Same.
	(copy_no_warning): Same.
	(gimple_set_block): Call gimple_set_location.
	(gimple_set_location): Call copy_warning.
	* tree.h (no_warning, all_warnings): New constants.
	(warning_suppressed_p): New function.
	(suppress_warning): Same.
	(copy_no_warning): Same.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index af1fe49bb48..740fed69873 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -1095,7 +1095,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 opt_code opt = OPT_Wstringop_overread;
+  if ((expr && warning_suppressed_p (expr, opt))
+      || warning_suppressed_p (arg, opt))
     return;
 
   loc = expansion_point_location_if_in_system_header (loc);
@@ -1123,14 +1125,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")
@@ -1145,7 +1147,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);
     }
@@ -1154,14 +1156,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")
@@ -1176,7 +1178,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);
     }
@@ -1185,9 +1187,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;
+      suppress_warning (arg, opt);
       if (expr)
-	TREE_NO_WARNING (expr) = 1;
+	suppress_warning (expr, opt);
     }
 }
 
@@ -1445,14 +1447,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)
+	  && !warning_suppressed_p (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;
+	  suppress_warning (arg, OPT_Warray_bounds);
 	}
       return NULL_TREE;
     }
@@ -3947,10 +3949,10 @@  determine_block_size (tree len, rtx len_rtx,
    accessing an object with SIZE.  */
 
 static bool
-maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
+maybe_warn_for_bound (opt_code 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] || warning_suppressed_p (exp, opt))
     return false;
 
   tree maxobjsize = max_object_size ();
@@ -4042,7 +4044,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;
+	  suppress_warning (exp, opt);
 	}
 
       return warned;
@@ -4089,14 +4091,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")
@@ -4105,14 +4107,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")
@@ -4131,7 +4133,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;
+      suppress_warning (exp, opt);
     }
 
   return warned;
@@ -4357,7 +4359,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;
+	suppress_warning (exp, OPT_Wstringop_overread);
 
       return warned;
     }
@@ -4400,7 +4402,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;
+    suppress_warning (exp, OPT_Wstringop_overread);
 
   return warned;
 }
@@ -4779,8 +4781,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 opt_code opt = OPT_Wstringop_overflow_;
+	  if (warning_suppressed_p (exp, opt)
+	      || (pad && pad->dst.ref
+		  && warning_suppressed_p (pad->dst.ref, opt)))
 	    return false;
 
 	  location_t loc = tree_inlined_location (exp);
@@ -4791,12 +4795,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",
@@ -4817,7 +4821,7 @@  check_access (tree exp, tree dstwrite,
 
 	  if (warned)
 	    {
-	      TREE_NO_WARNING (exp) = true;
+	      suppress_warning (exp, OPT_Wstringop_overflow_);
 	      if (pad)
 		pad->dst.inform_access (pad->mode);
 	    }
@@ -4852,9 +4856,9 @@  check_access (tree exp, tree dstwrite,
 
 	  if (size != maxobjsize && tree_int_cst_lt (size, range[0]))
 	    {
-	      int opt = (dstwrite || mode != access_read_only
-			 ? OPT_Wstringop_overflow_
-			 : OPT_Wstringop_overread);
+	      opt_code opt = (dstwrite || mode != access_read_only
+			      ? OPT_Wstringop_overflow_
+			      : OPT_Wstringop_overread);
 	      maybe_warn_for_bound (opt, loc, exp, func, range, size, pad);
 	      return false;
 	    }
@@ -4890,19 +4894,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 opt_code opt = OPT_Wstringop_overread;
+      if (warning_suppressed_p (exp, opt)
+	  || (srcstr && warning_suppressed_p (srcstr, opt))
+	  || (pad && pad->src.ref
+	      && warning_suppressed_p (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;
+	  suppress_warning (exp, opt);
 	  if (pad)
 	    pad->src.inform_access (access_read_only);
 	}
@@ -7427,8 +7433,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_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);
@@ -13853,10 +13858,11 @@  maybe_emit_free_warning (tree exp)
 	      else
 		{
 		  tree alloc_decl = gimple_call_fndecl (def_stmt);
-		  int opt = (DECL_IS_OPERATOR_NEW_P (alloc_decl)
-			     || DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
-			     ? OPT_Wmismatched_new_delete
-			     : OPT_Wmismatched_dealloc);
+		  const opt_code opt =
+		    (DECL_IS_OPERATOR_NEW_P (alloc_decl)
+		     || DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
+		     ? OPT_Wmismatched_new_delete
+		     : OPT_Wmismatched_dealloc);
 		  warned = warning_at (loc, opt,
 				       "%K%qD called on pointer returned "
 				       "from a mismatched allocation "
@@ -13967,7 +13973,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;
+      suppress_warning (ret);
       return ret;
     }
   return NULL_TREE;
diff --git a/gcc/calls.c b/gcc/calls.c
index a7c78ed9c16..bc6bc03d5d4 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1623,7 +1623,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 || warning_suppressed_p (exp, OPT_Wstringop_overread))
     return false;
 
   /* Avoid clearly invalid calls (more checking done below).  */
@@ -1739,7 +1739,7 @@  maybe_warn_nonstring_arg (tree fndecl, tree exp)
 				 exp, fndecl, bndrng[0], bndrng[1],
 				 maxobjsize);
 	  if (warned)
-	    TREE_NO_WARNING (exp) = true;
+	    suppress_warning (exp, OPT_Wstringop_overread);
 
 	  return warned;
 	}
@@ -1916,7 +1916,7 @@  maybe_warn_nonstring_arg (tree fndecl, tree exp)
     }
 
   if (any_arg_warned)
-    TREE_NO_WARNING (exp) = true;
+    suppress_warning (exp, OPT_Wstringop_overread);
 
   return any_arg_warned;
 }
@@ -1979,7 +1979,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;
+  opt_code opt_warned = N_OPTS;
 
   /* A string describing the attributes that the warnings issued by this
      function apply to.  Used to print one informational note per function
@@ -2054,7 +2054,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;
+      opt_code arg_warned = N_OPTS;
       location_t loc = EXPR_LOCATION (exp);
       tree ptr = access.second.ptr;
       if (*sizstr
@@ -2067,24 +2067,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)
+	  if (arg_warned != N_OPTS)
 	    {
 	      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;
 	    }
 	}
@@ -2122,31 +2123,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)
@@ -2154,7 +2157,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;
 	    }
 	}
@@ -2190,17 +2193,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;
+      suppress_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 (warning_suppressed_p (exp, OPT_Wstringop_overflow_))
+	opt_warned = OPT_Wstringop_overflow_;
+      if (opt_warned != N_OPTS)
 	{
-	  any_warned = true;
-
 	  if (access.second.internal_p)
 	    inform (loc, "referencing argument %u of type %qT",
 		    ptridx + 1, ptrtype);
@@ -2222,7 +2225,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 != N_OPTS)
     {
       if (fndecl)
 	inform (DECL_SOURCE_LOCATION (fndecl),
@@ -2233,7 +2236,8 @@  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;
+  if (opt_warned != N_OPTS)
+    suppress_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..92be5e044e9 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_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..55cb0347149 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 (warning_suppressed_p (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 33d64bfbbe8..8acc2c09b19 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -250,7 +250,7 @@  fold_undefer_overflow_warnings (bool issue, const gimple *stmt, int code)
   if (!issue || warnmsg == NULL)
     return;
 
-  if (gimple_no_warning_p (stmt))
+  if (warning_suppressed_p (stmt, OPT_Wstrict_overflow))
     return;
 
   /* Use the smallest code level when deciding to issue the
@@ -4250,8 +4250,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_warning (ret, arg);
       return ret;
     }
 
@@ -9342,7 +9341,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;
+	  suppress_warning (tem /* What warning? */);
 	  TREE_USED (tem) = 1;
 	  return tem;
 	}
@@ -13515,10 +13514,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 (warning_suppressed_p (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..13f08685f8f 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 (warning_suppressed_p (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;
+      suppress_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 (warning_suppressed_p (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;
+      suppress_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;
+	  suppress_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;
+	suppress_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))
+      || warning_suppressed_p (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;
+      suppress_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;
+    suppress_warning (wi->stmt, OPT_Warray_bounds);
 
   return NULL_TREE;
 }
diff --git a/gcc/gimple-expr.c b/gcc/gimple-expr.c
index c3211795d33..a2563a45c37 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_warning (copy, var);
   return copy;
 }
 
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 1c0e930aba5..68031538145 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -2044,7 +2044,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) && !warning_suppressed_p (stmt, OPT_Wrestrict))
 	{
 	  tree func = gimple_call_fndecl (stmt);
 
@@ -2071,9 +2071,9 @@  gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
   if (nonstr)
     {
       /* Avoid folding calls with unterminated arrays.  */
-      if (!gimple_no_warning_p (stmt))
+      if (!warning_suppressed_p (stmt, OPT_Wstringop_overread))
 	warn_string_no_nul (loc, NULL_TREE, "strcpy", src, nonstr);
-      gimple_set_no_warning (stmt, true);
+      suppress_warning (stmt, OPT_Wstringop_overread);
       return false;
     }
 
@@ -2481,7 +2481,7 @@  gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
 
   unsigned HOST_WIDE_INT dstsize;
 
-  bool nowarn = gimple_no_warning_p (stmt);
+  bool nowarn = warning_suppressed_p (stmt, OPT_Wstringop_overflow_);
 
   if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize))
     {
@@ -2504,7 +2504,7 @@  gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
 				    "destination size %wu"),
 			       stmt, fndecl, len, dstsize);
 	  if (nowarn)
-	    gimple_set_no_warning (stmt, true);
+	    suppress_warning (stmt, OPT_Wstringop_overflow_);
 	}
     }
 
@@ -2520,7 +2520,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);
+	suppress_warning (stmt, OPT_Wstringop_overflow_);
     }
 
   tree fn = builtin_decl_implicit (BUILT_IN_STRCAT);
@@ -3105,7 +3105,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)
+	  && !warning_suppressed_p (stmt, OPT_Wrestrict))
 	{
 	  tree func = gimple_call_fndecl (stmt);
 
@@ -3288,10 +3289,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 (!warning_suppressed_p (stmt, OPT_Wstringop_overread))
 	warn_string_no_nul (loc, NULL_TREE, "stpcpy", src, data.decl, size,
 			    exact);
-      gimple_set_no_warning (stmt, true);
+      suppress_warning (stmt, OPT_Wstringop_overread);
       return false;
     }
 
@@ -3554,8 +3555,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_warning (repl, stmt);
 
       gimple_seq_add_stmt_without_update (&stmts, repl);
       if (tree lhs = gimple_call_lhs (stmt))
@@ -3606,8 +3606,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_warning (repl, stmt);
 
       gimple_seq_add_stmt_without_update (&stmts, repl);
       if (tree lhs = gimple_call_lhs (stmt))
@@ -6065,7 +6064,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 = warning_suppressed_p (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..f2757b66b28 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))
+	  && !warning_suppressed_p (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..41e3be6f9f4 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -330,7 +330,8 @@  get_format_string (tree format, location_t *ploc)
 static bool
 ATTRIBUTE_GCC_DIAG (5, 6)
 fmtwarn (const substring_loc &fmt_loc, location_t param_loc,
-	 const char *corrected_substring, int opt, const char *gmsgid, ...)
+	 const char *corrected_substring, opt_code opt,
+	 const char *gmsgid, ...)
 {
   format_string_diagnostic_t diag (fmt_loc, NULL, param_loc, NULL,
 				   corrected_substring);
@@ -345,7 +346,8 @@  fmtwarn (const substring_loc &fmt_loc, location_t param_loc,
 static bool
 ATTRIBUTE_GCC_DIAG (6, 8) ATTRIBUTE_GCC_DIAG (7, 8)
 fmtwarn_n (const substring_loc &fmt_loc, location_t param_loc,
-	   const char *corrected_substring, int opt, unsigned HOST_WIDE_INT n,
+	   const char *corrected_substring, opt_code opt,
+	   unsigned HOST_WIDE_INT n,
 	   const char *singular_gmsgid, const char *plural_gmsgid, ...)
 {
   format_string_diagnostic_t diag (fmt_loc, NULL, param_loc, NULL,
@@ -921,7 +923,7 @@  struct call_info
   }
 
   /* Return the warning option corresponding to the called function.  */
-  int warnopt () const
+  opt_code warnopt () const
   {
     return bounded ? OPT_Wformat_truncation_ : OPT_Wformat_overflow_;
   }
@@ -4680,7 +4682,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);
+    suppress_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..a9694ddd598 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.  */
+		      suppress_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;
+	      suppress_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 c8c9f9581a8..02771e4cd60 100644
--- a/gcc/gimple-ssa-warn-restrict.c
+++ b/gcc/gimple-ssa-warn-restrict.c
@@ -1431,7 +1431,7 @@  maybe_diag_overlap (location_t loc, gimple *call, builtin_access &acs)
   if (!acs.overlap ())
     return false;
 
-  if (gimple_no_warning_p (call))
+  if (warning_suppressed_p (call, OPT_Wrestrict))
     return true;
 
   /* For convenience.  */
@@ -1680,10 +1680,11 @@  maybe_diag_overlap (location_t loc, gimple *call, builtin_access &acs)
    be issued, false otherwise.
    Both initial values of the offsets and their final value computed
    by the function by incrementing the initial value by the size are
-   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.  */
+   validated.  Return the warning number if the offsets are not valid
+   and a diagnostic has been issued, or would have been issued if
+   DO_WARN had been true, otherwise an invalid warning number.  */
 
-static bool
+static opt_code
 maybe_diag_access_bounds (gimple *call, tree func, int strict,
 			  const builtin_memref &ref, offset_int wroff,
 			  bool do_warn)
@@ -1695,28 +1696,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 opt_code 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 && warning_suppressed_p (ref.ref, OPT_Wstringop_overflow_))
+	return no_warning;
 
+      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 : no_warning;
 	}
     }
 
@@ -1729,18 +1733,19 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
   offset_int ooboff[] = { ref.offrange[0], ref.offrange[1], wroff };
   tree oobref = ref.offset_out_of_bounds (strict, ooboff);
   if (!oobref)
-    return false;
+    return no_warning;
 
+  const opt_code opt = OPT_Warray_bounds;
   /* Return true without issuing a warning.  */
   if (!do_warn)
-    return true;
+    return opt;
 
   if (!warn_array_bounds)
-    return false;
+    return no_warning;
 
-  if (TREE_NO_WARNING (ref.ptr)
-      || (ref.ref && TREE_NO_WARNING (ref.ref)))
-    return false;
+  if (warning_suppressed_p (ref.ptr, opt)
+      || (ref.ref && warning_suppressed_p (ref.ref, opt)))
+    return no_warning;
 
   char rangestr[2][64];
   if (ooboff[0] == ooboff[1]
@@ -1770,7 +1775,7 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
 	  && TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
 	{
 	  auto_diagnostic_group d;
-	  if (warning_at (loc, OPT_Warray_bounds,
+	  if (warning_at (loc, opt,
 			  "%G%qD pointer overflow between offset %s "
 			  "and size %s accessing array %qD with type %qT",
 			  call, func, rangestr[0], rangestr[1], ref.base, type))
@@ -1780,13 +1785,13 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
 	      warned = true;
 	    }
 	  else
-	    warned = warning_at (loc, OPT_Warray_bounds,
+	    warned = warning_at (loc, opt,
 				 "%G%qD pointer overflow between offset %s "
 				 "and size %s",
 				 call, func, rangestr[0], rangestr[1]);
 	}
       else
-	warned = warning_at (loc, OPT_Warray_bounds,
+	warned = warning_at (loc, opt,
 			     "%G%qD pointer overflow between offset %s "
 			     "and size %s",
 			     call, func, rangestr[0], rangestr[1]);
@@ -1802,7 +1807,7 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
 	{
 	  auto_diagnostic_group d;
 	  if ((ref.basesize < maxobjsize
-	       && warning_at (loc, OPT_Warray_bounds,
+	       && warning_at (loc, opt,
 			      form
 			      ? G_("%G%qD forming offset %s is out of "
 				   "the bounds [0, %wu] of object %qD with "
@@ -1811,7 +1816,7 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
 				   "[0, %wu] of object %qD with type %qT"),
 			      call, func, rangestr[0], ref.basesize.to_uhwi (),
 			      ref.base, TREE_TYPE (ref.base)))
-	      || warning_at (loc, OPT_Warray_bounds,
+	      || warning_at (loc, opt,
 			     form
 			     ? G_("%G%qD forming offset %s is out of "
 				  "the bounds of object %qD with type %qT")
@@ -1826,7 +1831,7 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
 	    }
 	}
       else if (ref.basesize < maxobjsize)
-	warned = warning_at (loc, OPT_Warray_bounds,
+	warned = warning_at (loc, opt,
 			     form
 			     ? G_("%G%qD forming offset %s is out "
 				  "of the bounds [0, %wu]")
@@ -1834,7 +1839,7 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
 				  "of the bounds [0, %wu]"),
 			     call, func, rangestr[0], ref.basesize.to_uhwi ());
       else
-	warned = warning_at (loc, OPT_Warray_bounds,
+	warned = warning_at (loc, opt,
 			     form
 			     ? G_("%G%qD forming offset %s is out of bounds")
 			     : G_("%G%qD offset %s is out of bounds"),
@@ -1848,7 +1853,7 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
 	type = TREE_TYPE (type);
       type = TYPE_MAIN_VARIANT (type);
 
-      if (warning_at (loc, OPT_Warray_bounds,
+      if (warning_at (loc, opt,
 		      "%G%qD offset %s from the object at %qE is out "
 		      "of the bounds of %qT",
 		      call, func, rangestr[0], ref.base, type))
@@ -1866,7 +1871,7 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
       tree refop = TREE_OPERAND (ref.ref, 0);
       tree type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
 
-      if (warning_at (loc, OPT_Warray_bounds,
+      if (warning_at (loc, opt,
 		      "%G%qD offset %s from the object at %qE is out "
 		      "of the bounds of referenced subobject %qD with "
 		      "type %qT at offset %wi",
@@ -1883,7 +1888,7 @@  maybe_diag_access_bounds (gimple *call, tree func, int strict,
 	}
     }
 
-  return warned;
+  return warned ? opt : no_warning;
 }
 
 /* Check a CALL statement for restrict-violations and issue warnings
@@ -1894,7 +1899,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 (warning_suppressed_p (call, OPT_Wrestrict))
     return;
 
   tree func = gimple_call_fndecl (call);
@@ -1980,11 +1985,10 @@  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))
-    return;
-
+  opt_code opt = check_bounds_or_overlap (query, call, dst, src, dstwr,
+					  NULL_TREE);
   /* Avoid diagnosing the call again.  */
-  gimple_set_no_warning (call, true);
+  suppress_warning (call, opt);
 }
 
 } /* anonymous namespace */
@@ -1996,7 +2000,7 @@  check_call (range_query *query, gimple *call)
    without issue a warning.  Return the OPT_Wxxx constant corresponding
    to the warning if one has been detected and zero otherwise.  */
 
-int
+opt_code
 check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize,
 			 tree srcsize, bool bounds_only /* = false */,
 			 bool do_warn /* = true */)
@@ -2006,7 +2010,7 @@  check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize,
 				  bounds_only, do_warn);
 }
 
-int
+opt_code
 check_bounds_or_overlap (range_query *query,
 			 gimple *call, tree dst, tree src, tree dstsize,
 			 tree srcsize, bool bounds_only /* = false */,
@@ -2032,16 +2036,20 @@  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))
+  opt_code opt
+    = maybe_diag_access_bounds (call, func, strict, dstref, wroff, do_warn);
+  if (opt == no_warning)
+    opt = maybe_diag_access_bounds (call, func, strict, srcref, 0, do_warn);
+
+  if (opt != no_warning)
     {
       if (do_warn)
-	gimple_set_no_warning (call, true);
-      return OPT_Warray_bounds;
+	suppress_warning (call, opt);
+      return opt;
     }
 
   if (!warn_restrict || bounds_only || !src)
-    return 0;
+    return no_warning;
 
   if (!bounds_only)
     {
@@ -2051,7 +2059,7 @@  check_bounds_or_overlap (range_query *query,
 	case BUILT_IN_MEMMOVE_CHK:
 	case BUILT_IN_MEMSET:
 	case BUILT_IN_MEMSET_CHK:
-	  return 0;
+	  return no_warning;
 	default:
 	  break;
 	}
@@ -2064,26 +2072,26 @@  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) && !warning_suppressed_p (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);
+	  suppress_warning (call, OPT_Wrestrict);
 	  return OPT_Wrestrict;
 	}
 
-      return 0;
+      return no_warning;
     }
 
   /* Return false when overlap has been detected.  */
   if (maybe_diag_overlap (loc, call, acs))
     {
-      gimple_set_no_warning (call, true);
+      suppress_warning (call, OPT_Wrestrict);
       return OPT_Wrestrict;
     }
 
-  return 0;
+  return no_warning;
 }
 
 gimple_opt_pass *
diff --git a/gcc/gimple.c b/gcc/gimple.c
index f1044e9c630..60a90667e4b 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_warning (call, t);
 
   if (fnptrtype)
     {
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 91b92b4a4d1..ca5d4acfc71 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -1634,6 +1634,24 @@  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 the disposition for a warning (or all warnings by default)
+   for a statement.  */
+extern bool warning_suppressed_p (const gimple *, opt_code = all_warnings)
+  ATTRIBUTE_NONNULL (1);
+/* Set the disposition for a warning (or all warnings by default)
+   at a location to enabled by default.  */
+extern void suppress_warning (gimple *, opt_code = all_warnings,
+			      bool = true) ATTRIBUTE_NONNULL (1);
+
+/* Copy the warning disposition mapping from one statement to another.  */
+extern void copy_warning (gimple *, const gimple *)
+  ATTRIBUTE_NONNULL (1) ATTRIBUTE_NONNULL (2);
+/* Copy the warning disposition mapping from an expression to a statement.  */
+extern void copy_warning (gimple *, const_tree)
+  ATTRIBUTE_NONNULL (1) ATTRIBUTE_NONNULL (2);
+/* Copy the warning disposition mapping from a statement to an expression.  */
+extern void copy_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 +1872,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 +1905,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_warning (location, g->location);
   g->location = location;
 }
 
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 39f5b973d18..e26c8106cfa 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1599,7 +1599,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_warning (ret, stmt);
       gimplify_seq_add_stmt (pre_p, ret);
       return GS_ALL_DONE;
     }
@@ -1661,7 +1661,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;
+      suppress_warning (result, OPT_Wuninitialized);
 
       gimplify_ctxp->return_temp = result;
     }
@@ -1675,7 +1675,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_warning (ret, stmt);
   gimplify_seq_add_stmt (pre_p, ret);
 
   return GS_ALL_DONE;
@@ -4250,7 +4250,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_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);
@@ -5680,7 +5681,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;
+  suppress_warning (other);
   other = get_formal_tmp_var (other, pre_p);
 
   realpart = code == REALPART_EXPR ? rhs : other;
@@ -5965,7 +5966,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_warning (assign, *from_p);
     }
 
   if (gimplify_ctxp->into_ssa && is_gimple_reg (*to_p))
@@ -6724,7 +6725,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;
+	  suppress_warning (var, OPT_Wuninitialized);
 	}
     }
   else
@@ -14491,7 +14492,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_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..6a0e0966ead 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;
+	      suppress_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;
+	      suppress_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;
+	      suppress_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 2d5cdf671eb..c5abab6a281 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -5627,7 +5627,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;
+		suppress_warning (var, OPT_Wuninitialized);
 	      break;
 
 	    case OMP_CLAUSE__CONDTEMP_:
@@ -6588,7 +6588,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;
+      suppress_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);
@@ -7016,7 +7016,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;
+		suppress_warning (new_var, OPT_Wuninitialized);
 	    }
 
 	  if (!maybe_simt && simduid && DECL_HAS_VALUE_EXPR_P (new_var))
@@ -7850,7 +7850,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;
+	    suppress_warning (var);
 	  do_in = true;
 	  break;
 
@@ -12634,7 +12634,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;
+			  suppress_warning (var);
 			var = build_fold_addr_expr (var);
 		      }
 		    else
@@ -12658,7 +12658,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;
+			suppress_warning (var, OPT_Wuninitialized);
 			gimplify_assign (avar, var, &ilist);
 		      }
 		    avar = build_fold_addr_expr (avar);
@@ -12812,7 +12812,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;
+		  suppress_warning (var);
 		if (TREE_CODE (type) != POINTER_TYPE)
 		  t = fold_convert (pointer_sized_int_node, t);
 		t = fold_convert (TREE_TYPE (x), t);
@@ -12825,7 +12825,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;
+		  suppress_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..9bb436aad77 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)
+	   && !warning_suppressed_p (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))
+	      && !warning_suppressed_p (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;
+		suppress_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 (!warning_suppressed_p (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;
+		    suppress_warning (fun->decl, OPT_Wreturn_type);
 		  break;
 		}
 	    }
diff --git a/gcc/tree-complex.c b/gcc/tree-complex.c
index d7d991714de..a528cdc7fee 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_warning (r, orig);
     }
   else
     {
       DECL_IGNORED_P (r) = 1;
-      TREE_NO_WARNING (r) = 1;
+      suppress_warning (r);
     }
 
   return r;
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 05d1a253d5b..9ec2013b56d 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_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_warning (*tp, old);
 	  if (MR_DEPENDENCE_CLIQUE (old) != 0)
 	    {
 	      MR_DEPENDENCE_CLIQUE (*tp)
@@ -3775,7 +3775,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;
+  suppress_warning (var /* OPT_Wuninitialized? */);
 
   declare_inline_vars (id->block, var);
 
@@ -5033,7 +5033,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;
+	suppress_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..5ca6db248d5 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_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..385b228dbbc 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;
+	suppress_warning (repl /* Be more selective! */);
       else
-	TREE_NO_WARNING (repl) = TREE_NO_WARNING (access->base);
+	copy_warning (repl, access->base);
     }
   else
-    TREE_NO_WARNING (repl) = 1;
+    suppress_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;
+	      suppress_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..42585412325 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) || warning_suppressed_p (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 beb2702f3b6..db3b18b275c 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 = warning_suppressed_p (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..dfa5dc87c34 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);
+			suppress_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);
+			suppress_warning (stmt, OPT_Wstrict_overflow_);
 		    }
 		}
 	    }
diff --git a/gcc/tree-ssa-loop-im.c b/gcc/tree-ssa-loop-im.c
index 8034cf68d27..c3fecb26b2b 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;
+      suppress_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 969b868397e..1763ad4ce7b 100644
--- a/gcc/tree-ssa-phiopt.c
+++ b/gcc/tree-ssa-phiopt.c
@@ -3010,9 +3010,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);
+    suppress_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 423075b2bd1..656dfa465f4 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -1935,7 +1935,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 || warning_suppressed_p (stmt, OPT_Wstringop_overflow_))
     return;
 
   /* The DECL of the function performing the write if it is done
@@ -1954,7 +1954,7 @@  maybe_warn_overflow (gimple *stmt, tree len, pointer_query &ptr_qry,
   else
     return;
 
-  if (TREE_NO_WARNING (dest))
+  if (warning_suppressed_p (dest, OPT_Wstringop_overflow_))
     return;
 
   const int ostype = rawmem ? 0 : 1;
@@ -2098,7 +2098,7 @@  maybe_warn_overflow (gimple *stmt, tree len, pointer_query &ptr_qry,
   if (!warned)
     return;
 
-  gimple_set_no_warning (stmt, true);
+  suppress_warning (stmt, OPT_Wstringop_overflow_);
 
   aref.inform_access (access_write_only);
 }
@@ -2621,16 +2621,16 @@  handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
   len = fold_convert_loc (loc, type, unshare_expr (srclen));
   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;
+  /* Disable warning for the transformed statement?  */
+  opt_code no_warning_opt = no_warning;
 
-  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)
+	suppress_warning (stmt, no_warning_opt);
+    }
 
   if (fn == NULL_TREE)
     return;
@@ -2664,8 +2664,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)
+    suppress_warning (stmt, no_warning_opt);
 }
 
 /* Check the size argument to the built-in forms of stpncpy and strncpy
@@ -2793,7 +2793,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 (warning_suppressed_p (stmt, OPT_Wstringop_truncation))
     return false;
 
   wide_int cntrange[2];
@@ -3153,9 +3153,10 @@  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))
+  opt_code opt = check_bounds_or_overlap (stmt, dst, src, dstlenp1, srclenp1);
+  if (opt != no_warning)
     {
-      gimple_set_no_warning (stmt, true);
+      suppress_warning (stmt, opt);
       return;
     }
 
@@ -3166,7 +3167,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);
+	suppress_warning (stmt, OPT_Wstringop_truncation);
 
       return;
     }
@@ -3203,8 +3204,8 @@  handle_builtin_stxncpy_strncat (bool append_p, gimple_stmt_iterator *gsi)
       /* Issue -Wstringop-overflow when appending or when writing into
 	 a destination of a known size.  Otherwise, when copying into
 	 a destination of an unknown size, it's truncation.  */
-      int opt = (append_p || dstsize
-		 ? OPT_Wstringop_overflow_ : OPT_Wstringop_truncation);
+      opt_code opt = (append_p || dstsize
+		      ? OPT_Wstringop_overflow_ : OPT_Wstringop_truncation);
       warned = warning_at (callloc, opt,
 			   "%G%qD specified bound depends on the length "
 			   "of the source argument",
@@ -3445,8 +3446,8 @@  handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi,
 	srclen = get_string_length (si);
     }
 
-  /* Set the no-warning bit on the transformed statement?  */
-  bool set_no_warning = false;
+  /* Disable warning for the transformed statement?  */
+  opt_code no_warning_opt = no_warning;
 
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
@@ -3463,12 +3464,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)
+	  suppress_warning (stmt, no_warning_opt);
       }
 
       /* strcat (p, q) can be transformed into
@@ -3575,11 +3574,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)
+	suppress_warning (stmt, no_warning_opt);
     }
 
   tree len = NULL_TREE;
@@ -3645,8 +3643,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)
+    suppress_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 7c002f8ed87..99442d7f975 100644
--- a/gcc/tree-ssa-uninit.c
+++ b/gcc/tree-ssa-uninit.c
@@ -87,17 +87,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 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) && 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.
@@ -165,10 +181,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)
+       && (warning_suppressed_p (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))
@@ -185,7 +201,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;
+      suppress_warning (expr, wc);
 
       if (location == DECL_SOURCE_LOCATION (var))
 	return;
@@ -260,7 +276,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
@@ -268,7 +284,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.  */
@@ -407,7 +423,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.
@@ -435,7 +451,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 b9c0e65bd98..0565c9b5073 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 || !warning_suppressed_p (val1, OPT_Woverflow))
+	  && (!inv2 || !warning_suppressed_p (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 || !warning_suppressed_p (val1, OPT_Woverflow))
+	  && (!sym2 || !warning_suppressed_p (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 62b2de46479..3c92c58980e 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -21,6 +21,7 @@  along with GCC; see the file COPYING3.  If not see
 #define GCC_TREE_H
 
 #include "tree-core.h"
+#include "options.h"
 
 /* Convert a target-independent built-in function code to a combined_fn.  */
 
@@ -6440,4 +6434,30 @@  public:
   operator location_t () const { return m_combined_loc; }
 };
 
+/* Code that doesn't refer to any warning.  Has no effect on suppression
+   functions.  */
+constexpr opt_code no_warning = opt_code ();
+/* Wildcard code that refers to all warnings.  */
+constexpr opt_code all_warnings = N_OPTS;
+
+/* Return the disposition for a warning (or all warnings by default)
+   at a location.  */
+extern bool warning_suppressed_at (location_t, opt_code = all_warnings);
+/* Set the disposition for a warning (or all warnings by default)
+   at a location to disabled by default.  */
+extern bool suppress_warning_at (location_t, opt_code = all_warnings,
+				 bool = true);
+/* Copy warning disposition from one location to another.  */
+extern void copy_warning (location_t, location_t);
+
+/* Return the disposition for a warning (or all warnings by default)
+   for an expression.  */
+extern bool warning_suppressed_p (const_tree, opt_code = all_warnings);
+/* Set the disposition for a warning (or all warnings by default)
+   at a location to disabled by default.  */
+extern void suppress_warning (tree, opt_code = all_warnings, bool = true)
+  ATTRIBUTE_NONNULL (1);
+/* Copy warning disposition from one expression to another.  */
+extern void copy_warning (tree, const_tree);
+
 #endif  /* GCC_TREE_H  */
diff --git a/gcc/vr-values.c b/gcc/vr-values.c
index 509c8b093c5..3ae2c68499d 100644
--- a/gcc/vr-values.c
+++ b/gcc/vr-values.c
@@ -699,7 +699,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;
+		suppress_warning (max, OPT_Woverflow);
 	    }
 
 	  vr_p->update (min, max);
@@ -739,7 +739,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;
+		suppress_warning (min, OPT_Woverflow);
 	    }
 
 	  vr_p->update (min, max);
@@ -3355,7 +3355,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;
+	    suppress_warning (max, OPT_Woverflow);
 	}
     }
   else if (cond_code == GE_EXPR || cond_code == GT_EXPR)
@@ -3369,7 +3369,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;
+	    suppress_warning (min, OPT_Woverflow);
 	}
     }