ubsan: Separate -f{,no-}delete-null-pointer-checks from -fsanitize={null,{,returns-}nonnull-attribute} [PR104426]

Message ID 20220209084713.GK2646553@tucnak
State New
Headers
Series ubsan: Separate -f{,no-}delete-null-pointer-checks from -fsanitize={null,{,returns-}nonnull-attribute} [PR104426] |

Commit Message

Jakub Jelinek Feb. 9, 2022, 8:47 a.m. UTC
  Hi!

The 3 ubsan -fsanitize={null,{,returns-}nonnull-attribute} sanitizers were
setting implicitly -fno-delete-null-pointer-exceptions, so that
optimizations don't optimize away its checks whether some pointers are NULL.
Unfortunately by doing that there is no way to find out if
flag_delete_null_pointer_checks is 0 or 1 because the target usually allows
variables or functions at the start of address space, or user asked for
that option or whether it was because of sanitization or a combination of
those.

Unfortunately Variable in *.opt files can't be PerFunction or Optimization,
so there is no easy way to have some global_options.x_* be combined from
other Optimization flags.

So, the following patch instead introduces an inline function that should be
used in most places, and for the folding_initializer case ignores
sanitization and honors just target's or user
-fno-delete-null-pointer-checks.

Another possibility would be to invert the meaning of the variable,
change flag_delete_null_pointer_checks into
flag_dont_delete_null_pointer_checks and use separate values for the var
originating from command line or target's decision (e.g. 1) and from
sanitization (e.g. 2), but it would be a small nightmare to encode that
into *.opt.

A separate issue not solved in this patch is whether addresses of automatic
variables can be assumed to be non-NULL even in the
-fno-delete-null-pointer-checks case (whether no target actually places its
stack at the very start of the address space (especially growing up)).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2022-02-08  Jakub Jelinek  <jakub@redhat.com>

	PR c++/67762
	PR c++/71962
	PR c++/104426
gcc/
	* asan.h (delete_null_pointer_checks): New function.
	* opts.cc (finish_options): Don't clear
	opts->x_flag_delete_null_pointer_checks for -fsanitize=null,
	-fsanitize=nonnull-attribute and/or
	-fsanitize=returns-nonnull-attribute.
	* tree.cc: Include asan.h.
	(nonnull_arg_p): Use delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* range-op.cc: Include attribs.h and asan.h.
	(pointer_plus_operator::wi_fold): Use delete_null_pointer_checks ()
	instead of flag_delete_null_pointer_checks.
	* gimple-range-fold.cc: Include attribs.h and asan.h.
	(fold_using_range::range_of_address): Use
	delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* tree-ssa-structalias.cc: Include asan.h.
	(get_constraint_for_1, find_func_aliases_for_builtin_call): Use
	delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* symtab.cc: Include asan.h.
	(symtab_node::nonzero_address): If !folding_initializer, use
	delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* ipa-fnsummary.cc: Include asan.h.
	(points_to_local_or_readonly_memory_p): Use
	delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* tree-ssa-alias.cc: Include attribs.h and asan.h.
	(modref_may_conflict): Use delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* ubsan.cc (instrument_nonnull_arg, instrument_nonnull_return):
	Instead of temporarily setting flag_delete_null_pointer_checks
	temporarily clear SANITIZE_NULL, SANITIZE_NONNULL_ATTRIBUTE and
	SANITIZE_RETURNS_NONNULL_ATTRIBUTE bits from flag_sanitize.
	* tree-vrp.cc: Include attribs.h and asan.h.
	(extract_range_from_pointer_plus_expr): Use
	delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* vr-values.cc: Include asan.h.
	(vr_values::vrp_stmt_computes_nonzero): Use
	delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* tree-ssa-loop-niter.cc: Include attribs.h and asan.h.
	(infer_loop_bounds_from_pointer_arith): Use
	delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* ipa-pure-const.cc: Include attribs.h and asan.h.
	(malloc_candidate_p): Use delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* gimple.cc (gimple_call_nonnull_result_p,
	infer_nonnull_range_by_dereference, infer_nonnull_range_by_attribute):
	Use delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* fold-const.cc (tree_expr_nonzero_warnv_p): Use
	delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
	* rtlanal.cc: Include stringpool.h, attribs.h and asan.h.
	(nonzero_address_p): Use delete_null_pointer_checks () instead of
	flag_delete_null_pointer_checks.
gcc/c-family/
	* c-ubsan.cc (ubsan_maybe_instrument_reference_or_call): Instead of
	temporarily setting flag_delete_null_pointer_checks temporarily clear
	SANITIZE_NULL, SANITIZE_NONNULL_ATTRIBUTE and
	SANITIZE_RETURNS_NONNULL_ATTRIBUTE bits from flag_sanitize.
gcc/cp/
	* typeck.cc (build_static_cast_1): Use delete_null_pointer_checks ()
	instead of flag_delete_null_pointer_checks.
gcc/testsuite/
	* g++.dg/ubsan/pr71962.C: New test.
	* g++.dg/ubsan/pr104426.C: New test.


	Jakub
  

Comments

Richard Biener Feb. 9, 2022, 9:03 a.m. UTC | #1
On Wed, 9 Feb 2022, Jakub Jelinek wrote:

> Hi!
> 
> The 3 ubsan -fsanitize={null,{,returns-}nonnull-attribute} sanitizers were
> setting implicitly -fno-delete-null-pointer-exceptions, so that
> optimizations don't optimize away its checks whether some pointers are NULL.
> Unfortunately by doing that there is no way to find out if
> flag_delete_null_pointer_checks is 0 or 1 because the target usually allows
> variables or functions at the start of address space, or user asked for
> that option or whether it was because of sanitization or a combination of
> those.
> 
> Unfortunately Variable in *.opt files can't be PerFunction or Optimization,
> so there is no easy way to have some global_options.x_* be combined from
> other Optimization flags.
> 
> So, the following patch instead introduces an inline function that should be
> used in most places, and for the folding_initializer case ignores
> sanitization and honors just target's or user
> -fno-delete-null-pointer-checks.
> 
> Another possibility would be to invert the meaning of the variable,
> change flag_delete_null_pointer_checks into
> flag_dont_delete_null_pointer_checks and use separate values for the var
> originating from command line or target's decision (e.g. 1) and from

I see there are still targets doing sth like

static void
msp430_option_override (void)
{
  /* The MSP430 architecture can safely dereference a NULL pointer.  In 
fact,
     there are memory mapped registers there.  */
  flag_delete_null_pointer_checks = 0;

it would be very nice to instead have those override the 
zero_address_valid target hook (only i386.cc does that at the moment).
It probably doesn't remove much of the complication.

Then at the same time you add the delete_null_pointer_checks ()
abstraction we should add a zero_address_valid (type/as) abstraction
in case we want -fno-delete-null-pointer-checks be a user accessible
way to override the target here.

> sanitization (e.g. 2), but it would be a small nightmare to encode that
> into *.opt.
> 
> A separate issue not solved in this patch is whether addresses of automatic
> variables can be assumed to be non-NULL even in the
> -fno-delete-null-pointer-checks case (whether no target actually places its
> stack at the very start of the address space (especially growing up)).

True - but not sure if worth optimizing ... (the zero_address_valid
could get a decl overload as well).

Did you replace all flag_delete_null_pointer_checks uses?  Can you
rename the flag just to be sure?

Thanks,
Richard.

> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2022-02-08  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/67762
> 	PR c++/71962
> 	PR c++/104426
> gcc/
> 	* asan.h (delete_null_pointer_checks): New function.
> 	* opts.cc (finish_options): Don't clear
> 	opts->x_flag_delete_null_pointer_checks for -fsanitize=null,
> 	-fsanitize=nonnull-attribute and/or
> 	-fsanitize=returns-nonnull-attribute.
> 	* tree.cc: Include asan.h.
> 	(nonnull_arg_p): Use delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* range-op.cc: Include attribs.h and asan.h.
> 	(pointer_plus_operator::wi_fold): Use delete_null_pointer_checks ()
> 	instead of flag_delete_null_pointer_checks.
> 	* gimple-range-fold.cc: Include attribs.h and asan.h.
> 	(fold_using_range::range_of_address): Use
> 	delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* tree-ssa-structalias.cc: Include asan.h.
> 	(get_constraint_for_1, find_func_aliases_for_builtin_call): Use
> 	delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* symtab.cc: Include asan.h.
> 	(symtab_node::nonzero_address): If !folding_initializer, use
> 	delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* ipa-fnsummary.cc: Include asan.h.
> 	(points_to_local_or_readonly_memory_p): Use
> 	delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* tree-ssa-alias.cc: Include attribs.h and asan.h.
> 	(modref_may_conflict): Use delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* ubsan.cc (instrument_nonnull_arg, instrument_nonnull_return):
> 	Instead of temporarily setting flag_delete_null_pointer_checks
> 	temporarily clear SANITIZE_NULL, SANITIZE_NONNULL_ATTRIBUTE and
> 	SANITIZE_RETURNS_NONNULL_ATTRIBUTE bits from flag_sanitize.
> 	* tree-vrp.cc: Include attribs.h and asan.h.
> 	(extract_range_from_pointer_plus_expr): Use
> 	delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* vr-values.cc: Include asan.h.
> 	(vr_values::vrp_stmt_computes_nonzero): Use
> 	delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* tree-ssa-loop-niter.cc: Include attribs.h and asan.h.
> 	(infer_loop_bounds_from_pointer_arith): Use
> 	delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* ipa-pure-const.cc: Include attribs.h and asan.h.
> 	(malloc_candidate_p): Use delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* gimple.cc (gimple_call_nonnull_result_p,
> 	infer_nonnull_range_by_dereference, infer_nonnull_range_by_attribute):
> 	Use delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* fold-const.cc (tree_expr_nonzero_warnv_p): Use
> 	delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> 	* rtlanal.cc: Include stringpool.h, attribs.h and asan.h.
> 	(nonzero_address_p): Use delete_null_pointer_checks () instead of
> 	flag_delete_null_pointer_checks.
> gcc/c-family/
> 	* c-ubsan.cc (ubsan_maybe_instrument_reference_or_call): Instead of
> 	temporarily setting flag_delete_null_pointer_checks temporarily clear
> 	SANITIZE_NULL, SANITIZE_NONNULL_ATTRIBUTE and
> 	SANITIZE_RETURNS_NONNULL_ATTRIBUTE bits from flag_sanitize.
> gcc/cp/
> 	* typeck.cc (build_static_cast_1): Use delete_null_pointer_checks ()
> 	instead of flag_delete_null_pointer_checks.
> gcc/testsuite/
> 	* g++.dg/ubsan/pr71962.C: New test.
> 	* g++.dg/ubsan/pr104426.C: New test.
> 
> --- gcc/asan.h.jj	2022-01-11 23:11:21.542302070 +0100
> +++ gcc/asan.h	2022-02-08 10:26:18.695464165 +0100
> @@ -260,4 +260,12 @@ sanitize_coverage_p (const_tree fn = cur
>  				   DECL_ATTRIBUTES (fn)) == NULL_TREE));
>  }
>  
> +static inline bool
> +delete_null_pointer_checks (const_tree fn = current_function_decl)
> +{
> +  return (flag_delete_null_pointer_checks
> +	  && !sanitize_flags_p (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
> +				| SANITIZE_RETURNS_NONNULL_ATTRIBUTE, fn));
> +}
> +
>  #endif /* TREE_ASAN */
> --- gcc/opts.cc.jj	2022-02-04 14:36:55.440599224 +0100
> +++ gcc/opts.cc	2022-02-08 10:33:23.128566415 +0100
> @@ -1224,12 +1224,6 @@ finish_options (struct gcc_options *opts
>        error_at (loc, "%<-fsanitize-recover=%s%> is not supported",
>  		sanitizer_opts[i].name);
>  
> -  /* When instrumenting the pointers, we don't want to remove
> -     the null pointer checks.  */
> -  if (opts->x_flag_sanitize & (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
> -				| SANITIZE_RETURNS_NONNULL_ATTRIBUTE))
> -    opts->x_flag_delete_null_pointer_checks = 0;
> -
>    /* Aggressive compiler optimizations may cause false negatives.  */
>    if (opts->x_flag_sanitize & ~(SANITIZE_LEAK | SANITIZE_UNREACHABLE))
>      opts->x_flag_aggressive_loop_optimizations = 0;
> --- gcc/tree.cc.jj	2022-02-04 14:36:56.380586095 +0100
> +++ gcc/tree.cc	2022-02-08 11:04:17.937686466 +0100
> @@ -69,6 +69,7 @@ along with GCC; see the file COPYING3.
>  #include "gimple-fold.h"
>  #include "escaped_string.h"
>  #include "gimple-range.h"
> +#include "asan.h"
>  
>  /* Tree code classes.  */
>  
> @@ -13937,12 +13938,12 @@ nonnull_arg_p (const_tree arg)
>    /* THIS argument of method is always non-NULL.  */
>    if (TREE_CODE (TREE_TYPE (cfun->decl)) == METHOD_TYPE
>        && arg == DECL_ARGUMENTS (cfun->decl)
> -      && flag_delete_null_pointer_checks)
> +      && delete_null_pointer_checks ())
>      return true;
>  
>    /* Values passed by reference are always non-NULL.  */
>    if (TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE
> -      && flag_delete_null_pointer_checks)
> +      && delete_null_pointer_checks ())
>      return true;
>  
>    fntype = TREE_TYPE (cfun->decl);
> --- gcc/range-op.cc.jj	2022-02-04 14:36:55.500598385 +0100
> +++ gcc/range-op.cc	2022-02-08 11:03:26.136410211 +0100
> @@ -46,6 +46,8 @@ along with GCC; see the file COPYING3.
>  #include "wide-int.h"
>  #include "value-relation.h"
>  #include "range-op.h"
> +#include "attribs.h"
> +#include "asan.h"
>  
>  // Return the upper limit for a type.
>  
> @@ -3870,7 +3872,7 @@ pointer_plus_operator::wi_fold (irange &
>    if ((!wi_includes_zero_p (type, lh_lb, lh_ub)
>         || !wi_includes_zero_p (type, rh_lb, rh_ub))
>        && !TYPE_OVERFLOW_WRAPS (type)
> -      && (flag_delete_null_pointer_checks
> +      && (delete_null_pointer_checks ()
>  	  || !wi::sign_mask (rh_ub)))
>      r = range_nonzero (type);
>    else if (lh_lb == lh_ub && lh_lb == 0
> --- gcc/gimple-range-fold.cc.jj	2022-02-04 14:36:55.198602603 +0100
> +++ gcc/gimple-range-fold.cc	2022-02-08 10:57:57.461002325 +0100
> @@ -43,6 +43,9 @@ along with GCC; see the file COPYING3.
>  #include "value-query.h"
>  #include "range-op.h"
>  #include "gimple-range.h"
> +#include "attribs.h"
> +#include "asan.h"
> +
>  // Construct a fur_source, and set the m_query field.
>  
>  fur_source::fur_source (range_query *q)
> @@ -721,7 +724,7 @@ fold_using_range::range_of_address (iran
>        /* If &X->a is equal to X, the range of X is the result.  */
>        if (off_cst && known_eq (off, 0))
>  	return true;
> -      else if (flag_delete_null_pointer_checks
> +      else if (delete_null_pointer_checks ()
>  	       && !TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr)))
>  	{
>  	  /* For -fdelete-null-pointer-checks -fno-wrapv-pointer we don't
> @@ -741,7 +744,7 @@ fold_using_range::range_of_address (iran
>        if (!TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))
>  	  && off_cst
>  	  && known_ne (off, 0)
> -	  && (flag_delete_null_pointer_checks || known_gt (off, 0)))
> +	  && (delete_null_pointer_checks () || known_gt (off, 0)))
>  	{
>  	  r = range_nonzero (TREE_TYPE (gimple_assign_rhs1 (stmt)));
>  	  return true;
> --- gcc/tree-ssa-structalias.cc.jj	2022-01-18 11:59:00.093974757 +0100
> +++ gcc/tree-ssa-structalias.cc	2022-02-08 11:02:47.128955208 +0100
> @@ -47,6 +47,7 @@
>  #include "ipa-modref-tree.h"
>  #include "ipa-modref.h"
>  #include "attr-fnspec.h"
> +#include "asan.h"
>  
>  /* The idea behind this analyzer is to generate set constraints from the
>     program, then solve the resulting constraints in order to generate the
> @@ -3531,7 +3532,7 @@ get_constraint_for_1 (tree t, vec<ce_s>
>        || (TREE_CODE (t) == CONSTRUCTOR
>  	  && CONSTRUCTOR_NELTS (t) == 0))
>      {
> -      if (flag_delete_null_pointer_checks)
> +      if (delete_null_pointer_checks ())
>  	temp.var = nothing_id;
>        else
>  	temp.var = nonlocal_id;
> @@ -4591,7 +4592,7 @@ find_func_aliases_for_builtin_call (stru
>  	    }
>  	  get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc);
>  	  do_deref (&lhsc);
> -	  if (flag_delete_null_pointer_checks
> +	  if (delete_null_pointer_checks ()
>  	      && integer_zerop (gimple_call_arg (t, 1)))
>  	    {
>  	      ac.type = ADDRESSOF;
> --- gcc/symtab.cc.jj	2022-01-18 11:58:59.892977628 +0100
> +++ gcc/symtab.cc	2022-02-08 10:57:00.150803038 +0100
> @@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.
>  #include "attribs.h"
>  #include "builtins.h"
>  #include "fold-const.h"
> +#include "asan.h"
>  
>  static const char *ipa_ref_use_name[] = {"read","write","addr","alias"};
>  
> @@ -2117,6 +2118,13 @@ symtab_node::get_partitioning_class (voi
>  bool
>  symtab_node::nonzero_address ()
>  {
> +  /* When folding initializers, only look at -fdelete-null-pointer-checks,
> +     otherwise also consider UBSan.  */
> +  bool do_delete_null_pointer_checks
> +    = (folding_initializer
> +       ? flag_delete_null_pointer_checks
> +       : delete_null_pointer_checks ());
> +
>    /* Weakrefs may be NULL when their target is not defined.  */
>    if (alias && weakref)
>      {
> @@ -2135,11 +2143,11 @@ symtab_node::nonzero_address ()
>  	     could be useful to eliminate the NULL pointer checks in LTO
>  	     programs.  */
>  	  if (target->definition && !DECL_EXTERNAL (target->decl))
> -	      return true;
> +	    return true;
>  	  if (target->resolution != LDPR_UNKNOWN
>  	      && target->resolution != LDPR_UNDEF
>  	      && !target->can_be_discarded_p ()
> -	      && flag_delete_null_pointer_checks)
> +	      && do_delete_null_pointer_checks)
>  	    return true;
>  	  return false;
>  	}
> @@ -2156,7 +2164,7 @@ symtab_node::nonzero_address ()
>  
>       When parsing, beware the cases when WEAK attribute is added later.  */
>    if ((!DECL_WEAK (decl) || DECL_COMDAT (decl))
> -      && flag_delete_null_pointer_checks)
> +      && do_delete_null_pointer_checks)
>      {
>        refuse_visibility_changes = true;
>        return true;
> @@ -2167,7 +2175,7 @@ symtab_node::nonzero_address ()
>       Play safe for flag_delete_null_pointer_checks where weak definition may
>       be re-defined by NULL.  */
>    if (definition && !DECL_EXTERNAL (decl)
> -      && (flag_delete_null_pointer_checks || !DECL_WEAK (decl)))
> +      && (do_delete_null_pointer_checks || !DECL_WEAK (decl)))
>      {
>        if (!DECL_WEAK (decl))
>          refuse_visibility_changes = true;
> @@ -2178,7 +2186,7 @@ symtab_node::nonzero_address ()
>    if (resolution != LDPR_UNKNOWN
>        && resolution != LDPR_UNDEF
>        && !can_be_discarded_p ()
> -      && flag_delete_null_pointer_checks)
> +      && do_delete_null_pointer_checks)
>      return true;
>    return false;
>  }
> --- gcc/ipa-fnsummary.cc.jj	2022-02-04 14:36:55.259601752 +0100
> +++ gcc/ipa-fnsummary.cc	2022-02-08 10:58:17.435723245 +0100
> @@ -88,6 +88,7 @@ along with GCC; see the file COPYING3.
>  #include "symtab-clones.h"
>  #include "gimple-range.h"
>  #include "tree-dfa.h"
> +#include "asan.h"
>  
>  /* Summaries.  */
>  fast_function_summary <ipa_fn_summary *, va_gc> *ipa_fn_summaries;
> @@ -2573,7 +2574,7 @@ points_to_local_or_readonly_memory_p (tr
>  {
>    /* See if memory location is clearly invalid.  */
>    if (integer_zerop (t))
> -    return flag_delete_null_pointer_checks;
> +    return delete_null_pointer_checks ();
>    if (TREE_CODE (t) == SSA_NAME)
>      {
>        /* For IPA passes we can consinder accesses to return slot local
> --- gcc/tree-ssa-alias.cc.jj	2022-01-18 11:58:59.992976199 +0100
> +++ gcc/tree-ssa-alias.cc	2022-02-08 11:03:40.523209202 +0100
> @@ -47,6 +47,8 @@ along with GCC; see the file COPYING3.
>  #include "print-tree.h"
>  #include "tree-ssa-alias-compare.h"
>  #include "builtins.h"
> +#include "attribs.h"
> +#include "asan.h"
>  
>  /* Broad overview of how alias analysis on gimple works:
>  
> @@ -2644,7 +2646,7 @@ modref_may_conflict (const gcall *stmt,
>  
>  	      alias_stats.modref_baseptr_tests++;
>  
> -	      if (integer_zerop (arg) && flag_delete_null_pointer_checks)
> +	      if (integer_zerop (arg) && delete_null_pointer_checks ())
>  		continue;
>  
>  	      /* PTA oracle will be unhapy of arg is not an pointer.  */
> --- gcc/ubsan.cc.jj	2022-01-18 11:59:00.252972485 +0100
> +++ gcc/ubsan.cc	2022-02-08 10:45:35.882359636 +0100
> @@ -1946,10 +1946,11 @@ instrument_nonnull_arg (gimple_stmt_iter
>  {
>    gimple *stmt = gsi_stmt (*gsi);
>    location_t loc[2];
> -  /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
> +  /* infer_nonnull_range needs delete_null_pointer_checks () true,
>       while for nonnull sanitization it is clear.  */
> -  int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
> -  flag_delete_null_pointer_checks = 1;
> +  int save_flag_sanitize = flag_sanitize;
> +  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
> +		     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
>    loc[0] = gimple_location (stmt);
>    loc[1] = UNKNOWN_LOCATION;
>    for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++)
> @@ -2001,7 +2002,7 @@ instrument_nonnull_arg (gimple_stmt_iter
>  	}
>        *gsi = gsi_for_stmt (stmt);
>      }
> -  flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
> +  flag_sanitize = save_flag_sanitize;
>  }
>  
>  /* Instrument returns in functions with returns_nonnull attribute.  */
> @@ -2012,10 +2013,11 @@ instrument_nonnull_return (gimple_stmt_i
>    greturn *stmt = as_a <greturn *> (gsi_stmt (*gsi));
>    location_t loc[2];
>    tree arg = gimple_return_retval (stmt);
> -  /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
> -     while for nonnull return sanitization it is clear.  */
> -  int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
> -  flag_delete_null_pointer_checks = 1;
> +  /* infer_nonnull_range needs delete_null_pointer_checks () true,
> +     while for nonnull sanitization it is clear.  */
> +  int save_flag_sanitize = flag_sanitize;
> +  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
> +		     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
>    loc[0] = gimple_location (stmt);
>    loc[1] = UNKNOWN_LOCATION;
>    if (arg
> @@ -2056,7 +2058,7 @@ instrument_nonnull_return (gimple_stmt_i
>        ubsan_create_edge (g);
>        *gsi = gsi_for_stmt (stmt);
>      }
> -  flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
> +  flag_sanitize = save_flag_sanitize;
>  }
>  
>  /* Instrument memory references.  Here we check whether the pointer
> --- gcc/tree-vrp.cc.jj	2022-01-18 11:59:00.249972528 +0100
> +++ gcc/tree-vrp.cc	2022-02-08 11:04:47.829268939 +0100
> @@ -50,6 +50,8 @@ along with GCC; see the file COPYING3.
>  #include "gimple-range-path.h"
>  #include "value-pointer-equiv.h"
>  #include "gimple-fold.h"
> +#include "attribs.h"
> +#include "asan.h"
>  
>  /* Set of SSA names found live during the RPO traversal of the function
>     for still active basic-blocks.  */
> @@ -732,7 +734,7 @@ extract_range_from_pointer_plus_expr (va
>    if ((!range_includes_zero_p (vr0)
>         || !range_includes_zero_p (vr1))
>        && !TYPE_OVERFLOW_WRAPS (expr_type)
> -      && (flag_delete_null_pointer_checks
> +      && (delete_null_pointer_checks ()
>  	  || (range_int_cst_p (vr1)
>  	      && !tree_int_cst_sign_bit (vr1->max ()))))
>      vr->set_nonzero (expr_type);
> --- gcc/vr-values.cc.jj	2022-01-18 11:59:00.255972442 +0100
> +++ gcc/vr-values.cc	2022-02-08 11:04:05.241863846 +0100
> @@ -51,6 +51,7 @@ along with GCC; see the file COPYING3.
>  #include "cfghooks.h"
>  #include "range-op.h"
>  #include "gimple-range.h"
> +#include "asan.h"
>  
>  /* Set value range VR to a non-negative range of type TYPE.  */
>  
> @@ -426,7 +427,7 @@ vr_values::vrp_stmt_computes_nonzero (gi
>  	     For -fdelete-null-pointer-checks -fno-wrapv-pointer we don't
>  	     allow going from non-NULL pointer to NULL.  */
>  	  if ((off_cst && known_eq (off, 0))
> -	      || (flag_delete_null_pointer_checks
> +	      || (delete_null_pointer_checks ()
>  		  && !TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))))
>  	    {
>  	      const value_range_equiv *vr
> @@ -440,7 +441,7 @@ vr_values::vrp_stmt_computes_nonzero (gi
>  	  if (!TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))
>  	      && off_cst
>  	      && known_ne (off, 0)
> -	      && (flag_delete_null_pointer_checks || known_gt (off, 0)))
> +	      && (delete_null_pointer_checks () || known_gt (off, 0)))
>  	    return true;
>  	}
>      }
> --- gcc/tree-ssa-loop-niter.cc.jj	2022-02-04 18:30:34.697003947 +0100
> +++ gcc/tree-ssa-loop-niter.cc	2022-02-08 11:03:14.416573955 +0100
> @@ -43,6 +43,8 @@ along with GCC; see the file COPYING3.
>  #include "tree-scalar-evolution.h"
>  #include "tree-dfa.h"
>  #include "gimple-range.h"
> +#include "attribs.h"
> +#include "asan.h"
>  
>  
>  /* The maximum number of dominator BBs we search for conditions
> @@ -3899,7 +3901,7 @@ infer_loop_bounds_from_pointer_arith (cl
>       NULL pointer since that would mean wrapping, which we assume here not to
>       happen.  So, we can exclude NULL from the valid range of pointer
>       arithmetic.  */
> -  if (flag_delete_null_pointer_checks && int_cst_value (low) == 0)
> +  if (delete_null_pointer_checks () && int_cst_value (low) == 0)
>      low = build_int_cstu (TREE_TYPE (low), TYPE_ALIGN_UNIT (TREE_TYPE (type)));
>  
>    record_nonwrapping_iv (loop, base, step, stmt, low, high, false, true);
> --- gcc/ipa-pure-const.cc.jj	2022-01-18 11:58:59.655981014 +0100
> +++ gcc/ipa-pure-const.cc	2022-02-08 10:59:22.325816627 +0100
> @@ -63,6 +63,8 @@ along with GCC; see the file COPYING3.
>  #include "ipa-fnsummary.h"
>  #include "symtab-thunks.h"
>  #include "dbgcnt.h"
> +#include "attribs.h"
> +#include "asan.h"
>  
>  /* Lattice values for const and pure functions.  Everything starts out
>     being const, then may drop to pure and then neither depending on
> @@ -972,7 +974,7 @@ malloc_candidate_p (function *fun, bool
>    cgraph_node *node = cgraph_node::get_create (fun->decl);
>  
>    if (EDGE_COUNT (exit_block->preds) == 0
> -      || !flag_delete_null_pointer_checks)
> +      || !delete_null_pointer_checks ())
>      return false;
>  
>    auto_bitmap visited;
> --- gcc/gimple.cc.jj	2022-02-04 14:36:55.217602338 +0100
> +++ gcc/gimple.cc	2022-02-08 10:29:43.887620018 +0100
> @@ -1673,17 +1673,18 @@ gimple_call_nonnull_result_p (gcall *cal
>    tree fndecl = gimple_call_fndecl (call);
>    if (!fndecl)
>      return false;
> -  if (flag_delete_null_pointer_checks && !flag_check_new
> +  if (delete_null_pointer_checks ()
> +      && !flag_check_new
>        && DECL_IS_OPERATOR_NEW_P (fndecl)
>        && !TREE_NOTHROW (fndecl))
>      return true;
>  
>    /* References are always non-NULL.  */
> -  if (flag_delete_null_pointer_checks
> +  if (delete_null_pointer_checks ()
>        && TREE_CODE (TREE_TYPE (fndecl)) == REFERENCE_TYPE)
>      return true;
>  
> -  if (flag_delete_null_pointer_checks
> +  if (delete_null_pointer_checks ()
>        && lookup_attribute ("returns_nonnull",
>  			   TYPE_ATTRIBUTES (gimple_call_fntype (call))))
>      return true;
> @@ -3033,7 +3034,7 @@ infer_nonnull_range_by_dereference (gimp
>  {
>    /* We can only assume that a pointer dereference will yield
>       non-NULL if -fdelete-null-pointer-checks is enabled.  */
> -  if (!flag_delete_null_pointer_checks
> +  if (!delete_null_pointer_checks ()
>        || !POINTER_TYPE_P (TREE_TYPE (op))
>        || gimple_code (stmt) == GIMPLE_ASM
>        || gimple_clobber_p (stmt))
> @@ -3053,7 +3054,7 @@ infer_nonnull_range_by_attribute (gimple
>  {
>    /* We can only assume that a pointer dereference will yield
>       non-NULL if -fdelete-null-pointer-checks is enabled.  */
> -  if (!flag_delete_null_pointer_checks
> +  if (!delete_null_pointer_checks ()
>        || !POINTER_TYPE_P (TREE_TYPE (op))
>        || gimple_code (stmt) == GIMPLE_ASM)
>      return false;
> --- gcc/fold-const.cc.jj	2022-02-06 11:16:22.352814445 +0100
> +++ gcc/fold-const.cc	2022-02-08 10:30:08.237281118 +0100
> @@ -10713,11 +10713,12 @@ tree_expr_nonzero_warnv_p (tree t, bool
>        {
>  	tree fndecl = get_callee_fndecl (t);
>  	if (!fndecl) return false;
> -	if (flag_delete_null_pointer_checks && !flag_check_new
> +	if (delete_null_pointer_checks ()
> +	    && !flag_check_new
>  	    && DECL_IS_OPERATOR_NEW_P (fndecl)
>  	    && !TREE_NOTHROW (fndecl))
>  	  return true;
> -	if (flag_delete_null_pointer_checks
> +	if (delete_null_pointer_checks ()
>  	    && lookup_attribute ("returns_nonnull",
>  		 TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
>  	  return true;
> --- gcc/rtlanal.cc.jj	2022-01-18 11:58:59.886977714 +0100
> +++ gcc/rtlanal.cc	2022-02-08 11:00:01.568268352 +0100
> @@ -38,6 +38,9 @@ along with GCC; see the file COPYING3.
>  #include "rtl-iter.h"
>  #include "hard-reg-set.h"
>  #include "function-abi.h"
> +#include "stringpool.h"
> +#include "attribs.h"
> +#include "asan.h"
>  
>  /* Forward declarations */
>  static void set_of_1 (rtx, const_rtx, void *);
> @@ -721,7 +724,7 @@ nonzero_address_p (const_rtx x)
>    switch (code)
>      {
>      case SYMBOL_REF:
> -      return flag_delete_null_pointer_checks && !SYMBOL_REF_WEAK (x);
> +      return delete_null_pointer_checks () && !SYMBOL_REF_WEAK (x);
>  
>      case LABEL_REF:
>        return true;
> --- gcc/c-family/c-ubsan.cc.jj	2022-01-18 11:58:58.885992014 +0100
> +++ gcc/c-family/c-ubsan.cc	2022-02-08 10:50:50.620965960 +0100
> @@ -464,17 +464,16 @@ ubsan_maybe_instrument_reference_or_call
>  	{
>  	  bool strict_overflow_p = false;
>  	  /* tree_single_nonzero_warnv_p will not return true for non-weak
> -	     non-automatic decls with -fno-delete-null-pointer-checks,
> +	     non-automatic decls with !delete-null-pointer-checks (),
>  	     which is disabled during -fsanitize=null.  We don't want to
>  	     instrument those, just weak vars though.  */
> -	  int save_flag_delete_null_pointer_checks
> -	    = flag_delete_null_pointer_checks;
> -	  flag_delete_null_pointer_checks = 1;
> +	  int save_flag_sanitize = flag_sanitize;
> +	  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
> +			     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
>  	  if (!tree_single_nonzero_warnv_p (op, &strict_overflow_p)
>  	      || strict_overflow_p)
>  	    instrument = true;
> -	  flag_delete_null_pointer_checks
> -	    = save_flag_delete_null_pointer_checks;
> +	  flag_sanitize = save_flag_sanitize;
>  	}
>        else if (sanitize_flags_p (SANITIZE_NULL))
>  	instrument = true;
> --- gcc/cp/typeck.cc.jj	2022-02-04 14:36:54.818607910 +0100
> +++ gcc/cp/typeck.cc	2022-02-08 10:49:17.747263557 +0100
> @@ -7917,7 +7917,7 @@ build_static_cast_1 (location_t loc, tre
>  	 that expr is NULL, if the static_cast is to a reference type,
>  	 it is UB if it would be NULL, so omit the non-NULL check.  */
>        expr = build_base_path (MINUS_EXPR, expr, base,
> -			      /*nonnull=*/flag_delete_null_pointer_checks,
> +			      /*nonnull=*/delete_null_pointer_checks (),
>  			      complain);
>  
>        /* Convert the pointer to a reference -- but then remember that
> --- gcc/testsuite/g++.dg/ubsan/pr71962.C.jj	2022-02-08 11:19:18.985101982 +0100
> +++ gcc/testsuite/g++.dg/ubsan/pr71962.C	2022-02-08 11:12:59.582400892 +0100
> @@ -0,0 +1,17 @@
> +// PR c++/71962
> +// { dg-do compile }
> +// { dg-options "-std=c++14 -fsanitize=undefined" }
> +
> +struct P {
> +  constexpr P (const int *p) : p(p) { }
> +  const int *p;
> +  explicit constexpr operator bool() const { return (bool) p; }
> +};
> +
> +int
> +main ()
> +{
> +  static constexpr int x{1};
> +  constexpr P p{&x};
> +  static_assert ((bool)p, "");
> +}
> --- gcc/testsuite/g++.dg/ubsan/pr104426.C.jj	2022-02-08 11:11:28.323675454 +0100
> +++ gcc/testsuite/g++.dg/ubsan/pr104426.C	2022-02-08 11:11:15.280857618 +0100
> @@ -0,0 +1,29 @@
> +// PR c++/104426
> +// { dg-do compile }
> +// { dg-options "-std=c++17 -fsanitize=undefined" }
> +
> +struct category
> +{
> +  constexpr bool failed () const noexcept { return true; }
> +};
> +
> +inline constexpr category s_cat;
> +
> +struct condition
> +{
> +  category const *cat_;
> +  constexpr bool failed () const noexcept
> +  {
> +    if (cat_)
> +      return cat_->failed ();
> +    else
> +      return false;
> +  }
> +};
> +
> +int
> +main ()
> +{
> +  constexpr condition cond { &s_cat };
> +  static_assert (cond.failed ());
> +}
> 
> 	Jakub
> 
>
  
Jakub Jelinek Feb. 9, 2022, 9:18 a.m. UTC | #2
On Wed, Feb 09, 2022 at 10:03:02AM +0100, Richard Biener wrote:
> I see there are still targets doing sth like
> 
> static void
> msp430_option_override (void)
> {
>   /* The MSP430 architecture can safely dereference a NULL pointer.  In 
> fact,
>      there are memory mapped registers there.  */
>   flag_delete_null_pointer_checks = 0;

Sure, that is the typical embedded target case.
And the user option is in case users are using a typically non-embedded
target case in an embedded way, even on x86_64-linux one can mmap something
at address 0 if one tweaks some kernel config parameters.

I guess I could change
default_addr_space_zero_address_valid from return false; to
  if (!flag_delete_null_pointer_checks)
    return true;
  if (folding_initializer)
    return false;
  if (current_function_decl
      && sanitize_flags_p (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
			   | SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
			   current_function_decl))
    return true;
  return false;
and change the i386 one to also call the default version.

Replacing all the flag_delete_null_pointer_checks uses
with some wrappers around the target hook will be less fun, not sure if the
pointer type will be always visible there...

> Did you replace all flag_delete_null_pointer_checks uses?

I did.

>  Can you rename the flag just to be sure?

Sure.

	Jakub
  
Richard Biener Feb. 9, 2022, 10:19 a.m. UTC | #3
On Wed, 9 Feb 2022, Jakub Jelinek wrote:

> On Wed, Feb 09, 2022 at 10:03:02AM +0100, Richard Biener wrote:
> > I see there are still targets doing sth like
> > 
> > static void
> > msp430_option_override (void)
> > {
> >   /* The MSP430 architecture can safely dereference a NULL pointer.  In 
> > fact,
> >      there are memory mapped registers there.  */
> >   flag_delete_null_pointer_checks = 0;
> 
> Sure, that is the typical embedded target case.

Yes.

> And the user option is in case users are using a typically non-embedded
> target case in an embedded way, even on x86_64-linux one can mmap something
> at address 0 if one tweaks some kernel config parameters.
> 
> I guess I could change
> default_addr_space_zero_address_valid from return false; to
>   if (!flag_delete_null_pointer_checks)
>     return true;
>   if (folding_initializer)
>     return false;
>   if (current_function_decl
>       && sanitize_flags_p (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
> 			   | SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
> 			   current_function_decl))
>     return true;
>   return false;
> and change the i386 one to also call the default version.

That does look like bogus abstraction though - I'd rather have
the target be specific w/o option checks and replace 
targetm.zero_addres_valid uses with a wrapper (like you do for
flag_delete_null_pointer_checks), if we think that the specific
query should be adjusted by sanitize flags (why?) or
folding_initializer (why?).

> Replacing all the flag_delete_null_pointer_checks uses
> with some wrappers around the target hook will be less fun, not sure if the
> pointer type will be always visible there...

I'd only replace target hook invocations.  Checking whether
NULL pointers are validly pointing to objects should be different
from checking whether optimization should remove checks for NULL.
That we overload -fno-delete-null-pointer-checks is a mistake
difficult to undo.  But at least making targets not set
that flag as we now have a proper target hook would be a good
start.

> > Did you replace all flag_delete_null_pointer_checks uses?
> 
> I did.

OK, just wanted to check.

> >  Can you rename the flag just to be sure?
> 
> Sure.

flag_internal_dnpc or so ;)

> 	Jakub
  
Jakub Jelinek Feb. 9, 2022, 2:05 p.m. UTC | #4
On Wed, Feb 09, 2022 at 11:19:25AM +0100, Richard Biener wrote:
> That does look like bogus abstraction though - I'd rather have
> the target be specific w/o option checks and replace 
> targetm.zero_addres_valid uses with a wrapper (like you do for
> flag_delete_null_pointer_checks), if we think that the specific
> query should be adjusted by sanitize flags (why?) or
> folding_initializer (why?).

Based on discussions on IRC, here is a WIP patch.

Unfortunately, there are 3 unresolved issues:
1) ipa-icf.cc uses
      && opt_for_fn (decl, flag_delete_null_pointer_checks))
   there is a pointer type, but I guess we'd need to adjust the
   target hook to take a defaulted fndecl argument and use that
   for the options
2) rtlanal.cc has:
    case SYMBOL_REF:
      return flag_delete_null_pointer_checks && !SYMBOL_REF_WEAK (x);
   Is there any way how to find out address space of a SYMBOL_REF?
   Or shall it hardcode ADDR_SPACE_GENERIC?
3) tree-ssa-structalias.cc has:
  if ((TREE_CODE (t) == INTEGER_CST
       && integer_zerop (t))
      /* The only valid CONSTRUCTORs in gimple with pointer typed
         elements are zero-initializer.  But in IPA mode we also
         process global initializers, so verify at least.  */
      || (TREE_CODE (t) == CONSTRUCTOR
          && CONSTRUCTOR_NELTS (t) == 0))
    {
      if (flag_delete_null_pointer_checks)
        temp.var = nothing_id;
      else
        temp.var = nonlocal_id;
      temp.type = ADDRESSOF;
      temp.offset = 0;
      results->safe_push (temp);
      return;
    }
   mpt really sure where to get the address space from in that case

And perhaps I didn't do it right in some other spots too.

--- gcc/targhooks.cc.jj	2022-01-18 11:58:59.919977242 +0100
+++ gcc/targhooks.cc	2022-02-09 13:21:08.958835833 +0100
@@ -1598,7 +1598,7 @@ default_addr_space_subset_p (addr_space_
 bool
 default_addr_space_zero_address_valid (addr_space_t as ATTRIBUTE_UNUSED)
 {
-  return false;
+  return !flag_delete_null_pointer_checks_;
 }
 
 /* The default hook for debugging the address space is to return the
--- gcc/tree.cc.jj	2022-02-08 20:08:04.001539492 +0100
+++ gcc/tree.cc	2022-02-09 14:44:01.602693848 +0100
@@ -69,6 +69,7 @@ along with GCC; see the file COPYING3.
 #include "gimple-fold.h"
 #include "escaped_string.h"
 #include "gimple-range.h"
+#include "asan.h"
 
 /* Tree code classes.  */
 
@@ -13937,12 +13938,13 @@ nonnull_arg_p (const_tree arg)
   /* THIS argument of method is always non-NULL.  */
   if (TREE_CODE (TREE_TYPE (cfun->decl)) == METHOD_TYPE
       && arg == DECL_ARGUMENTS (cfun->decl)
-      && flag_delete_null_pointer_checks)
+      && POINTER_TYPE_P (TREE_TYPE (arg))
+      && !zero_address_valid (TREE_TYPE (arg)))
     return true;
 
   /* Values passed by reference are always non-NULL.  */
   if (TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE
-      && flag_delete_null_pointer_checks)
+      && !zero_address_valid (TREE_TYPE (arg)))
     return true;
 
   fntype = TREE_TYPE (cfun->decl);
@@ -14549,6 +14551,24 @@ get_attr_nonstring_decl (tree expr, tree
   return NULL_TREE;
 }
 
+/* Return true if NULL is a valid address in AS.  */
+
+bool
+zero_address_valid (addr_space_t as)
+{
+  if (targetm.addr_space.zero_address_valid (as))
+    return true;
+  /* -fsanitize={null,{,returns-}nonnull-attribute sanitizers need
+     NULL pointer checks to be preserved, so pretend NULL addresses
+     are valid for it as well.
+     But don't do that in constant expressions or initializers.  */
+  if (folding_initializer)
+    return false;
+  return sanitize_flags_p (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE                                                                                                                 
+			   | SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
+			   current_function_decl);
+}
+
 #if CHECKING_P
 
 namespace selftest {
--- gcc/config/i386/i386.cc.jj	2022-02-09 12:55:50.716774241 +0100
+++ gcc/config/i386/i386.cc	2022-02-09 13:23:01.041272540 +0100
@@ -23804,7 +23804,9 @@ ix86_gen_scratch_sse_rtx (machine_mode m
 static bool
 ix86_addr_space_zero_address_valid (addr_space_t as)
 {
-  return as != ADDR_SPACE_GENERIC;
+  if (as != ADDR_SPACE_GENERIC)
+    return true;
+  return default_addr_space_zero_address_valid (as);
 }
 
 static void
--- gcc/config/nios2/elf.h.jj	2022-01-11 23:11:21.915296829 +0100
+++ gcc/config/nios2/elf.h	2022-02-09 13:04:42.643433282 +0100
@@ -57,5 +57,5 @@
    vector).  Users can override this on the command line to get the
    additional optimizations it enables.  */
 #define SUBTARGET_OVERRIDE_OPTIONS 		\
-  if (flag_delete_null_pointer_checks < 0)	\
-    flag_delete_null_pointer_checks = 0
+  if (flag_delete_null_pointer_checks_ < 0)	\
+    flag_delete_null_pointer_checks_ = 0
--- gcc/config/msp430/msp430.cc.jj	2022-02-04 14:36:54.410613609 +0100
+++ gcc/config/msp430/msp430.cc	2022-02-09 13:04:09.372888416 +0100
@@ -161,7 +161,7 @@ msp430_option_override (void)
 {
   /* The MSP430 architecture can safely dereference a NULL pointer.  In fact,
      there are memory mapped registers there.  */
-  flag_delete_null_pointer_checks = 0;
+  flag_delete_null_pointer_checks_ = 0;
 
   init_machine_status = msp430_init_machine_status;
 
--- gcc/config/cr16/cr16.cc.jj	2022-01-18 11:58:59.076989285 +0100
+++ gcc/config/cr16/cr16.cc	2022-02-09 13:03:47.292190471 +0100
@@ -277,7 +277,7 @@ cr16_override_options (void)
      Programs which rely on NULL pointer dereferences _not_ halting the 
      program may not work properly with this option. So disable this 
      option.  */
-  flag_delete_null_pointer_checks = 0;
+  flag_delete_null_pointer_checks_ = 0;
 
   /* FIXME: To avoid spill_failure ICE during exception handling,
    * disable cse_fllow_jumps. The spill error occurs when compiler
--- gcc/range-op.cc.jj	2022-02-08 20:08:03.888541042 +0100
+++ gcc/range-op.cc	2022-02-09 14:01:50.561978439 +0100
@@ -3870,8 +3870,7 @@ pointer_plus_operator::wi_fold (irange &
   if ((!wi_includes_zero_p (type, lh_lb, lh_ub)
        || !wi_includes_zero_p (type, rh_lb, rh_ub))
       && !TYPE_OVERFLOW_WRAPS (type)
-      && (flag_delete_null_pointer_checks
-	  || !wi::sign_mask (rh_ub)))
+      && (!zero_address_valid (type) || !wi::sign_mask (rh_ub)))
     r = range_nonzero (type);
   else if (lh_lb == lh_ub && lh_lb == 0
 	   && rh_lb == rh_ub && rh_lb == 0)
--- gcc/gimple-range-fold.cc.jj	2022-02-08 20:08:03.829541850 +0100
+++ gcc/gimple-range-fold.cc	2022-02-09 13:53:24.595030465 +0100
@@ -721,7 +721,7 @@ fold_using_range::range_of_address (iran
       /* If &X->a is equal to X, the range of X is the result.  */
       if (off_cst && known_eq (off, 0))
 	return true;
-      else if (flag_delete_null_pointer_checks
+      else if (!zero_address_valid (TREE_TYPE (expr))
 	       && !TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr)))
 	{
 	  /* For -fdelete-null-pointer-checks -fno-wrapv-pointer we don't
@@ -741,7 +741,7 @@ fold_using_range::range_of_address (iran
       if (!TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))
 	  && off_cst
 	  && known_ne (off, 0)
-	  && (flag_delete_null_pointer_checks || known_gt (off, 0)))
+	  && (!zero_address_valid (TREE_TYPE (expr)) || known_gt (off, 0)))
 	{
 	  r = range_nonzero (TREE_TYPE (gimple_assign_rhs1 (stmt)));
 	  return true;
--- gcc/cp/typeck.cc.jj	2022-02-08 20:08:03.783542481 +0100
+++ gcc/cp/typeck.cc	2022-02-09 14:47:47.552526504 +0100
@@ -7917,7 +7917,7 @@ build_static_cast_1 (location_t loc, tre
 	 that expr is NULL, if the static_cast is to a reference type,
 	 it is UB if it would be NULL, so omit the non-NULL check.  */
       expr = build_base_path (MINUS_EXPR, expr, base,
-			      /*nonnull=*/flag_delete_null_pointer_checks,
+			      /*nonnull=*/!zero_address_valid (type),
 			      complain);
 
       /* Convert the pointer to a reference -- but then remember that
--- gcc/tree-ssa-structalias.cc.jj	2022-02-08 20:08:03.952540164 +0100
+++ gcc/tree-ssa-structalias.cc	2022-02-09 14:32:39.127226322 +0100
@@ -4591,8 +4591,10 @@ find_func_aliases_for_builtin_call (stru
 	    }
 	  get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc);
 	  do_deref (&lhsc);
-	  if (flag_delete_null_pointer_checks
-	      && integer_zerop (gimple_call_arg (t, 1)))
+	  tree arg1 = gimple_call_arg (t, 1);
+	  if (integer_zerop (arg1)
+	      && POINTER_TYPE_P (TREE_TYPE (arg1))
+	      && !zero_address_valid (TREE_TYPE (arg1)))
 	    {
 	      ac.type = ADDRESSOF;
 	      ac.var = nothing_id;
--- gcc/testsuite/g++.dg/ubsan/pr104426.C.jj	2022-02-09 13:20:26.419429166 +0100
+++ gcc/testsuite/g++.dg/ubsan/pr104426.C	2022-02-09 13:20:26.419429166 +0100
@@ -0,0 +1,29 @@
+// PR c++/104426
+// { dg-do compile }
+// { dg-options "-std=c++17 -fsanitize=undefined" }
+
+struct category
+{
+  constexpr bool failed () const noexcept { return true; }
+};
+
+inline constexpr category s_cat;
+
+struct condition
+{
+  category const *cat_;
+  constexpr bool failed () const noexcept
+  {
+    if (cat_)
+      return cat_->failed ();
+    else
+      return false;
+  }
+};
+
+int
+main ()
+{
+  constexpr condition cond { &s_cat };
+  static_assert (cond.failed ());
+}
--- gcc/testsuite/g++.dg/ubsan/pr71962.C.jj	2022-02-09 13:20:26.419429166 +0100
+++ gcc/testsuite/g++.dg/ubsan/pr71962.C	2022-02-09 13:20:26.419429166 +0100
@@ -0,0 +1,17 @@
+// PR c++/71962
+// { dg-do compile }
+// { dg-options "-std=c++14 -fsanitize=undefined" }
+
+struct P {
+  constexpr P (const int *p) : p(p) { }
+  const int *p;
+  explicit constexpr operator bool() const { return (bool) p; }
+};
+
+int
+main ()
+{
+  static constexpr int x{1};
+  constexpr P p{&x};
+  static_assert ((bool)p, "");
+}
--- gcc/symtab.cc.jj	2022-02-08 20:08:03.913540699 +0100
+++ gcc/symtab.cc	2022-02-09 14:10:50.866452450 +0100
@@ -2135,11 +2135,11 @@ symtab_node::nonzero_address ()
 	     could be useful to eliminate the NULL pointer checks in LTO
 	     programs.  */
 	  if (target->definition && !DECL_EXTERNAL (target->decl))
-	      return true;
+	    return true;
 	  if (target->resolution != LDPR_UNKNOWN
 	      && target->resolution != LDPR_UNDEF
 	      && !target->can_be_discarded_p ()
-	      && flag_delete_null_pointer_checks)
+	      && !zero_address_valid (TYPE_ADDR_SPACE (TREE_TYPE (decl))))
 	    return true;
 	  return false;
 	}
@@ -2147,7 +2147,7 @@ symtab_node::nonzero_address ()
         return false;
     }
 
-  /* With !flag_delete_null_pointer_checks we assume that symbols may
+  /* With -fno-delete-null-pointer-checks we assume that symbols may
      bind to NULL. This is on by default on embedded targets only.
 
      Otherwise all non-WEAK symbols must be defined and thus non-NULL or
@@ -2156,7 +2156,7 @@ symtab_node::nonzero_address ()
 
      When parsing, beware the cases when WEAK attribute is added later.  */
   if ((!DECL_WEAK (decl) || DECL_COMDAT (decl))
-      && flag_delete_null_pointer_checks)
+      && !zero_address_valid (TYPE_ADDR_SPACE (TREE_TYPE (decl))))
     {
       refuse_visibility_changes = true;
       return true;
@@ -2164,10 +2164,11 @@ symtab_node::nonzero_address ()
 
   /* If target is defined and not extern, we know it will be
      output and thus it will bind to non-NULL.
-     Play safe for flag_delete_null_pointer_checks where weak definition may
+     Play safe for -fno-delete-null-pointer-checks where weak definition may
      be re-defined by NULL.  */
   if (definition && !DECL_EXTERNAL (decl)
-      && (flag_delete_null_pointer_checks || !DECL_WEAK (decl)))
+      && (!zero_address_valid (TYPE_ADDR_SPACE (TREE_TYPE (decl)))
+	  || !DECL_WEAK (decl)))
     {
       if (!DECL_WEAK (decl))
         refuse_visibility_changes = true;
@@ -2178,7 +2179,7 @@ symtab_node::nonzero_address ()
   if (resolution != LDPR_UNKNOWN
       && resolution != LDPR_UNDEF
       && !can_be_discarded_p ()
-      && flag_delete_null_pointer_checks)
+      && !zero_address_valid (TYPE_ADDR_SPACE (TREE_TYPE (decl))))
     return true;
   return false;
 }
--- gcc/ipa-fnsummary.cc.jj	2022-02-08 20:08:03.855541494 +0100
+++ gcc/ipa-fnsummary.cc	2022-02-09 13:54:40.594971201 +0100
@@ -2573,7 +2573,7 @@ points_to_local_or_readonly_memory_p (tr
 {
   /* See if memory location is clearly invalid.  */
   if (integer_zerop (t))
-    return flag_delete_null_pointer_checks;
+    return !zero_address_valid (TREE_TYPE (t));
   if (TREE_CODE (t) == SSA_NAME)
     {
       /* For IPA passes we can consinder accesses to return slot local
--- gcc/tree.h.jj	2022-02-04 14:36:56.382586067 +0100
+++ gcc/tree.h	2022-02-09 13:37:22.944332825 +0100
@@ -6579,4 +6579,14 @@ extern unsigned fndecl_dealloc_argno (tr
    object or pointer.  Otherwise return null.  */
 extern tree get_attr_nonstring_decl (tree, tree * = NULL);
 
+/* Return true if NULL is a valid address in AS.  */
+extern bool zero_address_valid (addr_space_t);
+
+inline bool
+zero_address_valid (tree type)
+{
+  gcc_checking_assert (POINTER_TYPE_P (type));
+  return zero_address_valid (TYPE_ADDR_SPACE (TREE_TYPE (type)));
+}
+
 #endif  /* GCC_TREE_H  */
--- gcc/tree-ssa-alias.cc.jj	2022-02-08 20:08:03.917540644 +0100
+++ gcc/tree-ssa-alias.cc	2022-02-09 14:16:14.231951242 +0100
@@ -2644,13 +2644,13 @@ modref_may_conflict (const gcall *stmt,
 
 	      alias_stats.modref_baseptr_tests++;
 
-	      if (integer_zerop (arg) && flag_delete_null_pointer_checks)
-		continue;
-
-	      /* PTA oracle will be unhapy of arg is not an pointer.  */
+	      /* PTA oracle will be unhapy if arg is not an pointer.  */
 	      if (!POINTER_TYPE_P (TREE_TYPE (arg)))
 		return true;
 
+	      if (integer_zerop (arg) && !zero_address_valid (TREE_TYPE (arg)))
+		continue;
+
 	      /* If we don't have base pointer, give up.  */
 	      if (!ref->ref && !ref->base)
 		continue;
--- gcc/opts.cc.jj	2022-02-08 20:08:03.873541247 +0100
+++ gcc/opts.cc	2022-02-09 13:20:26.418429180 +0100
@@ -1224,12 +1224,6 @@ finish_options (struct gcc_options *opts
       error_at (loc, "%<-fsanitize-recover=%s%> is not supported",
 		sanitizer_opts[i].name);
 
-  /* When instrumenting the pointers, we don't want to remove
-     the null pointer checks.  */
-  if (opts->x_flag_sanitize & (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
-				| SANITIZE_RETURNS_NONNULL_ATTRIBUTE))
-    opts->x_flag_delete_null_pointer_checks = 0;
-
   /* Aggressive compiler optimizations may cause false negatives.  */
   if (opts->x_flag_sanitize & ~(SANITIZE_LEAK | SANITIZE_UNREACHABLE))
     opts->x_flag_aggressive_loop_optimizations = 0;
--- gcc/ubsan.cc.jj	2022-02-08 20:08:04.001539492 +0100
+++ gcc/ubsan.cc	2022-02-09 13:20:26.419429166 +0100
@@ -1946,10 +1946,11 @@ instrument_nonnull_arg (gimple_stmt_iter
 {
   gimple *stmt = gsi_stmt (*gsi);
   location_t loc[2];
-  /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
+  /* infer_nonnull_range needs zero_address_valid true,
      while for nonnull sanitization it is clear.  */
-  int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
-  flag_delete_null_pointer_checks = 1;
+  int save_flag_sanitize = flag_sanitize;
+  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
+		     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
   loc[0] = gimple_location (stmt);
   loc[1] = UNKNOWN_LOCATION;
   for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++)
@@ -2001,7 +2002,7 @@ instrument_nonnull_arg (gimple_stmt_iter
 	}
       *gsi = gsi_for_stmt (stmt);
     }
-  flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
+  flag_sanitize = save_flag_sanitize;
 }
 
 /* Instrument returns in functions with returns_nonnull attribute.  */
@@ -2012,10 +2013,11 @@ instrument_nonnull_return (gimple_stmt_i
   greturn *stmt = as_a <greturn *> (gsi_stmt (*gsi));
   location_t loc[2];
   tree arg = gimple_return_retval (stmt);
-  /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
-     while for nonnull return sanitization it is clear.  */
-  int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
-  flag_delete_null_pointer_checks = 1;
+  /* infer_nonnull_range needs zero_address_valid true,
+     while for nonnull sanitization it is clear.  */
+  int save_flag_sanitize = flag_sanitize;
+  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
+		     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
   loc[0] = gimple_location (stmt);
   loc[1] = UNKNOWN_LOCATION;
   if (arg
@@ -2056,7 +2058,7 @@ instrument_nonnull_return (gimple_stmt_i
       ubsan_create_edge (g);
       *gsi = gsi_for_stmt (stmt);
     }
-  flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
+  flag_sanitize = save_flag_sanitize;
 }
 
 /* Instrument memory references.  Here we check whether the pointer
--- gcc/tree-vrp.cc.jj	2022-02-08 20:08:03.977539821 +0100
+++ gcc/tree-vrp.cc	2022-02-09 14:33:26.884560195 +0100
@@ -732,7 +732,7 @@ extract_range_from_pointer_plus_expr (va
   if ((!range_includes_zero_p (vr0)
        || !range_includes_zero_p (vr1))
       && !TYPE_OVERFLOW_WRAPS (expr_type)
-      && (flag_delete_null_pointer_checks
+      && (!zero_address_valid (expr_type)
 	  || (range_int_cst_p (vr1)
 	      && !tree_int_cst_sign_bit (vr1->max ()))))
     vr->set_nonzero (expr_type);
--- gcc/vr-values.cc.jj	2022-02-08 20:08:04.001539492 +0100
+++ gcc/vr-values.cc	2022-02-09 14:34:32.257648360 +0100
@@ -426,7 +426,7 @@ vr_values::vrp_stmt_computes_nonzero (gi
 	     For -fdelete-null-pointer-checks -fno-wrapv-pointer we don't
 	     allow going from non-NULL pointer to NULL.  */
 	  if ((off_cst && known_eq (off, 0))
-	      || (flag_delete_null_pointer_checks
+	      || (!zero_address_valid (TREE_TYPE (expr))
 		  && !TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))))
 	    {
 	      const value_range_equiv *vr
@@ -440,7 +440,7 @@ vr_values::vrp_stmt_computes_nonzero (gi
 	  if (!TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))
 	      && off_cst
 	      && known_ne (off, 0)
-	      && (flag_delete_null_pointer_checks || known_gt (off, 0)))
+	      && (!zero_address_valid (TREE_TYPE (expr)) || known_gt (off, 0)))
 	    return true;
 	}
     }
--- gcc/tree-ssa-loop-niter.cc.jj	2022-02-08 20:08:03.952540164 +0100
+++ gcc/tree-ssa-loop-niter.cc	2022-02-09 14:17:40.614748806 +0100
@@ -3899,7 +3899,7 @@ infer_loop_bounds_from_pointer_arith (cl
      NULL pointer since that would mean wrapping, which we assume here not to
      happen.  So, we can exclude NULL from the valid range of pointer
      arithmetic.  */
-  if (flag_delete_null_pointer_checks && int_cst_value (low) == 0)
+  if (!zero_address_valid (type) && int_cst_value (low) == 0)
     low = build_int_cstu (TREE_TYPE (low), TYPE_ALIGN_UNIT (TREE_TYPE (type)));
 
   record_nonwrapping_iv (loop, base, step, stmt, low, high, false, true);
--- gcc/common.opt.jj	2022-02-08 20:08:03.655544236 +0100
+++ gcc/common.opt	2022-02-09 13:02:46.907016534 +0100
@@ -1288,7 +1288,7 @@ Common Var(flag_delete_dead_exceptions)
 Delete dead instructions that may throw exceptions.
 
 fdelete-null-pointer-checks
-Common Var(flag_delete_null_pointer_checks) Init(-1) Optimization
+Common Var(flag_delete_null_pointer_checks_) Init(-1) Optimization
 Delete useless null pointer checks.
 
 fdevirtualize-at-ltrans
--- gcc/c-family/c-ubsan.cc.jj	2022-02-08 20:08:03.611544839 +0100
+++ gcc/c-family/c-ubsan.cc	2022-02-09 13:20:26.419429166 +0100
@@ -464,17 +464,16 @@ ubsan_maybe_instrument_reference_or_call
 	{
 	  bool strict_overflow_p = false;
 	  /* tree_single_nonzero_warnv_p will not return true for non-weak
-	     non-automatic decls with -fno-delete-null-pointer-checks,
+	     non-automatic decls with !zero_address_valid,
 	     which is disabled during -fsanitize=null.  We don't want to
 	     instrument those, just weak vars though.  */
-	  int save_flag_delete_null_pointer_checks
-	    = flag_delete_null_pointer_checks;
-	  flag_delete_null_pointer_checks = 1;
+	  int save_flag_sanitize = flag_sanitize;
+	  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
+			     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
 	  if (!tree_single_nonzero_warnv_p (op, &strict_overflow_p)
 	      || strict_overflow_p)
 	    instrument = true;
-	  flag_delete_null_pointer_checks
-	    = save_flag_delete_null_pointer_checks;
+	  flag_sanitize = save_flag_sanitize;
 	}
       else if (sanitize_flags_p (SANITIZE_NULL))
 	instrument = true;
--- gcc/ipa-pure-const.cc.jj	2022-02-08 20:08:03.856541480 +0100
+++ gcc/ipa-pure-const.cc	2022-02-09 14:00:45.161889970 +0100
@@ -971,8 +971,7 @@ malloc_candidate_p (function *fun, bool
   edge_iterator ei;
   cgraph_node *node = cgraph_node::get_create (fun->decl);
 
-  if (EDGE_COUNT (exit_block->preds) == 0
-      || !flag_delete_null_pointer_checks)
+  if (EDGE_COUNT (exit_block->preds) == 0)
     return false;
 
   auto_bitmap visited;
@@ -992,6 +991,9 @@ malloc_candidate_p (function *fun, bool
 	  || TREE_CODE (TREE_TYPE (retval)) != POINTER_TYPE)
 	DUMP_AND_RETURN("Return value is not SSA_NAME or not a pointer type.")
 
+      if (zero_address_valid (TREE_TYPE (retval)))
+	return false;
+
       if (!malloc_candidate_p_1 (fun, retval, ret_stmt, ipa, visited))
 	return false;
     }
--- gcc/gimple.cc.jj	2022-02-08 20:08:03.851541549 +0100
+++ gcc/gimple.cc	2022-02-09 13:47:54.762626732 +0100
@@ -1673,19 +1673,22 @@ gimple_call_nonnull_result_p (gcall *cal
   tree fndecl = gimple_call_fndecl (call);
   if (!fndecl)
     return false;
-  if (flag_delete_null_pointer_checks && !flag_check_new
+  if (!flag_check_new
       && DECL_IS_OPERATOR_NEW_P (fndecl)
-      && !TREE_NOTHROW (fndecl))
+      && !TREE_NOTHROW (fndecl)
+      && POINTER_TYPE_P (TREE_TYPE (fndecl))
+      && !zero_address_valid (TREE_TYPE (fndecl)))
     return true;
 
   /* References are always non-NULL.  */
-  if (flag_delete_null_pointer_checks
-      && TREE_CODE (TREE_TYPE (fndecl)) == REFERENCE_TYPE)
+  if (TREE_CODE (TREE_TYPE (fndecl)) == REFERENCE_TYPE
+      && !zero_address_valid (TREE_TYPE (fndecl)))
     return true;
 
-  if (flag_delete_null_pointer_checks
-      && lookup_attribute ("returns_nonnull",
-			   TYPE_ATTRIBUTES (gimple_call_fntype (call))))
+  tree fntype = gimple_call_fntype (call);
+  if (lookup_attribute ("returns_nonnull", TYPE_ATTRIBUTES (fntype))
+      && POINTER_TYPE_P (TREE_TYPE (fntype))
+      && !zero_address_valid (TREE_TYPE (fntype)))
     return true;
   return gimple_alloca_call_p (call);
 }
@@ -3033,10 +3036,10 @@ infer_nonnull_range_by_dereference (gimp
 {
   /* We can only assume that a pointer dereference will yield
      non-NULL if -fdelete-null-pointer-checks is enabled.  */
-  if (!flag_delete_null_pointer_checks
-      || !POINTER_TYPE_P (TREE_TYPE (op))
+  if (!POINTER_TYPE_P (TREE_TYPE (op))
       || gimple_code (stmt) == GIMPLE_ASM
-      || gimple_clobber_p (stmt))
+      || gimple_clobber_p (stmt)
+      || zero_address_valid (TREE_TYPE (TREE_TYPE (op))))
     return false;
 
   if (walk_stmt_load_store_ops (stmt, (void *)op,
@@ -3053,9 +3056,9 @@ infer_nonnull_range_by_attribute (gimple
 {
   /* We can only assume that a pointer dereference will yield
      non-NULL if -fdelete-null-pointer-checks is enabled.  */
-  if (!flag_delete_null_pointer_checks
-      || !POINTER_TYPE_P (TREE_TYPE (op))
-      || gimple_code (stmt) == GIMPLE_ASM)
+  if (!POINTER_TYPE_P (TREE_TYPE (op))
+      || gimple_code (stmt) == GIMPLE_ASM
+      || zero_address_valid (TREE_TYPE (TREE_TYPE (op))))
     return false;
 
   if (is_gimple_call (stmt) && !gimple_call_internal_p (stmt))
--- gcc/fold-const.cc.jj	2022-02-08 20:08:03.800542248 +0100
+++ gcc/fold-const.cc	2022-02-09 13:43:49.947029100 +0100
@@ -10713,13 +10713,16 @@ tree_expr_nonzero_warnv_p (tree t, bool
       {
 	tree fndecl = get_callee_fndecl (t);
 	if (!fndecl) return false;
-	if (flag_delete_null_pointer_checks && !flag_check_new
+	if (!flag_check_new
 	    && DECL_IS_OPERATOR_NEW_P (fndecl)
-	    && !TREE_NOTHROW (fndecl))
+	    && !TREE_NOTHROW (fndecl)
+	    && POINTER_TYPE_P (TREE_TYPE (fndecl))
+	    && !zero_address_valid (TREE_TYPE (fndecl)))
 	  return true;
-	if (flag_delete_null_pointer_checks
-	    && lookup_attribute ("returns_nonnull",
-		 TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
+	if (lookup_attribute ("returns_nonnull",
+		 TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))
+	    && POINTER_TYPE_P (TREE_TYPE (fndecl))
+	    && !zero_address_valid (TREE_TYPE (fndecl)))
 	  return true;
 	return alloca_call_p (t);
       }


	Jakub
  
Richard Biener Feb. 9, 2022, 2:41 p.m. UTC | #5
On Wed, 9 Feb 2022, Jakub Jelinek wrote:

> On Wed, Feb 09, 2022 at 11:19:25AM +0100, Richard Biener wrote:
> > That does look like bogus abstraction though - I'd rather have
> > the target be specific w/o option checks and replace 
> > targetm.zero_addres_valid uses with a wrapper (like you do for
> > flag_delete_null_pointer_checks), if we think that the specific
> > query should be adjusted by sanitize flags (why?) or
> > folding_initializer (why?).
> 
> Based on discussions on IRC, here is a WIP patch.
> 
> Unfortunately, there are 3 unresolved issues:
> 1) ipa-icf.cc uses
>       && opt_for_fn (decl, flag_delete_null_pointer_checks))
>    there is a pointer type, but I guess we'd need to adjust the
>    target hook to take a defaulted fndecl argument and use that
>    for the options

Yeah, I'd use a struct function arg tho, not a decl.

> 2) rtlanal.cc has:
>     case SYMBOL_REF:
>       return flag_delete_null_pointer_checks && !SYMBOL_REF_WEAK (x);
>    Is there any way how to find out address space of a SYMBOL_REF?

TYPE_ADDR_SPACE (TREE_TYPE (SYMBOL_REF_DECL ())) I guess.

>    Or shall it hardcode ADDR_SPACE_GENERIC?
> 3) tree-ssa-structalias.cc has:
>   if ((TREE_CODE (t) == INTEGER_CST
>        && integer_zerop (t))
>       /* The only valid CONSTRUCTORs in gimple with pointer typed
>          elements are zero-initializer.  But in IPA mode we also
>          process global initializers, so verify at least.  */
>       || (TREE_CODE (t) == CONSTRUCTOR
>           && CONSTRUCTOR_NELTS (t) == 0))
>     {
>       if (flag_delete_null_pointer_checks)
>         temp.var = nothing_id;
>       else
>         temp.var = nonlocal_id;
>       temp.type = ADDRESSOF;
>       temp.offset = 0;
>       results->safe_push (temp);
>       return;
>     }
>    mpt really sure where to get the address space from in that case
> 
> And perhaps I didn't do it right in some other spots too.

This case is really difficult since we track pointers through integers
(mind the missing POINTER_TYPE_P check above).  Of course we have
no idea what address-space the integer was converted from or will
be converted to so what the above wants to check is whether
there is _any_ address-space that could have a zero pointer pointing
to a valid object ...

> --- gcc/targhooks.cc.jj	2022-01-18 11:58:59.919977242 +0100
> +++ gcc/targhooks.cc	2022-02-09 13:21:08.958835833 +0100
> @@ -1598,7 +1598,7 @@ default_addr_space_subset_p (addr_space_
>  bool
>  default_addr_space_zero_address_valid (addr_space_t as ATTRIBUTE_UNUSED)
>  {
> -  return false;
> +  return !flag_delete_null_pointer_checks_;

As said, I'd not do that, but check it in zero_address_valid only.
Otherwise all targets overriding the hook have to remember to check
this flag.  I suppose we'd then do

  if (option_set (flag_delete_null_pointer_check))
    use flag_delete_null_pointer_check;
  else
    use targetm.zero_address_valid;

possibly only for the default address-space.

>  }
>  
>  /* The default hook for debugging the address space is to return the
> --- gcc/tree.cc.jj	2022-02-08 20:08:04.001539492 +0100
> +++ gcc/tree.cc	2022-02-09 14:44:01.602693848 +0100
> @@ -69,6 +69,7 @@ along with GCC; see the file COPYING3.
>  #include "gimple-fold.h"
>  #include "escaped_string.h"
>  #include "gimple-range.h"
> +#include "asan.h"
>  
>  /* Tree code classes.  */
>  
> @@ -13937,12 +13938,13 @@ nonnull_arg_p (const_tree arg)
>    /* THIS argument of method is always non-NULL.  */
>    if (TREE_CODE (TREE_TYPE (cfun->decl)) == METHOD_TYPE
>        && arg == DECL_ARGUMENTS (cfun->decl)
> -      && flag_delete_null_pointer_checks)
> +      && POINTER_TYPE_P (TREE_TYPE (arg))
> +      && !zero_address_valid (TREE_TYPE (arg)))
>      return true;
>  
>    /* Values passed by reference are always non-NULL.  */
>    if (TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE
> -      && flag_delete_null_pointer_checks)
> +      && !zero_address_valid (TREE_TYPE (arg)))
>      return true;
>  
>    fntype = TREE_TYPE (cfun->decl);
> @@ -14549,6 +14551,24 @@ get_attr_nonstring_decl (tree expr, tree
>    return NULL_TREE;
>  }
>  
> +/* Return true if NULL is a valid address in AS.  */
> +
> +bool
> +zero_address_valid (addr_space_t as)
> +{
> +  if (targetm.addr_space.zero_address_valid (as))
> +    return true;
> +  /* -fsanitize={null,{,returns-}nonnull-attribute sanitizers need
> +     NULL pointer checks to be preserved, so pretend NULL addresses
> +     are valid for it as well.
> +     But don't do that in constant expressions or initializers.  */
> +  if (folding_initializer)
> +    return false;
> +  return sanitize_flags_p (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE                                                                                                                 
> +			   | SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
> +			   current_function_decl);
> +}
> +
>  #if CHECKING_P
>  
>  namespace selftest {
> --- gcc/config/i386/i386.cc.jj	2022-02-09 12:55:50.716774241 +0100
> +++ gcc/config/i386/i386.cc	2022-02-09 13:23:01.041272540 +0100
> @@ -23804,7 +23804,9 @@ ix86_gen_scratch_sse_rtx (machine_mode m
>  static bool
>  ix86_addr_space_zero_address_valid (addr_space_t as)
>  {
> -  return as != ADDR_SPACE_GENERIC;
> +  if (as != ADDR_SPACE_GENERIC)
> +    return true;

so this makes it not possibel to use -fdelete-null-pointer-checks to
override the non-default address space behavior (on x86)

> +  return default_addr_space_zero_address_valid (as);
>  }
>  
>  static void
> --- gcc/config/nios2/elf.h.jj	2022-01-11 23:11:21.915296829 +0100
> +++ gcc/config/nios2/elf.h	2022-02-09 13:04:42.643433282 +0100
> @@ -57,5 +57,5 @@
>     vector).  Users can override this on the command line to get the
>     additional optimizations it enables.  */
>  #define SUBTARGET_OVERRIDE_OPTIONS 		\
> -  if (flag_delete_null_pointer_checks < 0)	\
> -    flag_delete_null_pointer_checks = 0
> +  if (flag_delete_null_pointer_checks_ < 0)	\
> +    flag_delete_null_pointer_checks_ = 0
> --- gcc/config/msp430/msp430.cc.jj	2022-02-04 14:36:54.410613609 +0100
> +++ gcc/config/msp430/msp430.cc	2022-02-09 13:04:09.372888416 +0100
> @@ -161,7 +161,7 @@ msp430_option_override (void)
>  {
>    /* The MSP430 architecture can safely dereference a NULL pointer.  In fact,
>       there are memory mapped registers there.  */
> -  flag_delete_null_pointer_checks = 0;
> +  flag_delete_null_pointer_checks_ = 0;

I'd use the target hook to return false and let 
-fdelete-null-pointer-checks override it.

>    init_machine_status = msp430_init_machine_status;
>  
> --- gcc/config/cr16/cr16.cc.jj	2022-01-18 11:58:59.076989285 +0100
> +++ gcc/config/cr16/cr16.cc	2022-02-09 13:03:47.292190471 +0100
> @@ -277,7 +277,7 @@ cr16_override_options (void)
>       Programs which rely on NULL pointer dereferences _not_ halting the 
>       program may not work properly with this option. So disable this 
>       option.  */
> -  flag_delete_null_pointer_checks = 0;
> +  flag_delete_null_pointer_checks_ = 0;
>  
>    /* FIXME: To avoid spill_failure ICE during exception handling,
>     * disable cse_fllow_jumps. The spill error occurs when compiler
> --- gcc/range-op.cc.jj	2022-02-08 20:08:03.888541042 +0100
> +++ gcc/range-op.cc	2022-02-09 14:01:50.561978439 +0100
> @@ -3870,8 +3870,7 @@ pointer_plus_operator::wi_fold (irange &
>    if ((!wi_includes_zero_p (type, lh_lb, lh_ub)
>         || !wi_includes_zero_p (type, rh_lb, rh_ub))
>        && !TYPE_OVERFLOW_WRAPS (type)
> -      && (flag_delete_null_pointer_checks
> -	  || !wi::sign_mask (rh_ub)))
> +      && (!zero_address_valid (type) || !wi::sign_mask (rh_ub)))
>      r = range_nonzero (type);
>    else if (lh_lb == lh_ub && lh_lb == 0
>  	   && rh_lb == rh_ub && rh_lb == 0)
> --- gcc/gimple-range-fold.cc.jj	2022-02-08 20:08:03.829541850 +0100
> +++ gcc/gimple-range-fold.cc	2022-02-09 13:53:24.595030465 +0100
> @@ -721,7 +721,7 @@ fold_using_range::range_of_address (iran
>        /* If &X->a is equal to X, the range of X is the result.  */
>        if (off_cst && known_eq (off, 0))
>  	return true;
> -      else if (flag_delete_null_pointer_checks
> +      else if (!zero_address_valid (TREE_TYPE (expr))
>  	       && !TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr)))
>  	{
>  	  /* For -fdelete-null-pointer-checks -fno-wrapv-pointer we don't
> @@ -741,7 +741,7 @@ fold_using_range::range_of_address (iran
>        if (!TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))
>  	  && off_cst
>  	  && known_ne (off, 0)
> -	  && (flag_delete_null_pointer_checks || known_gt (off, 0)))
> +	  && (!zero_address_valid (TREE_TYPE (expr)) || known_gt (off, 0)))
>  	{
>  	  r = range_nonzero (TREE_TYPE (gimple_assign_rhs1 (stmt)));
>  	  return true;
> --- gcc/cp/typeck.cc.jj	2022-02-08 20:08:03.783542481 +0100
> +++ gcc/cp/typeck.cc	2022-02-09 14:47:47.552526504 +0100
> @@ -7917,7 +7917,7 @@ build_static_cast_1 (location_t loc, tre
>  	 that expr is NULL, if the static_cast is to a reference type,
>  	 it is UB if it would be NULL, so omit the non-NULL check.  */
>        expr = build_base_path (MINUS_EXPR, expr, base,
> -			      /*nonnull=*/flag_delete_null_pointer_checks,
> +			      /*nonnull=*/!zero_address_valid (type),
>  			      complain);
>  
>        /* Convert the pointer to a reference -- but then remember that
> --- gcc/tree-ssa-structalias.cc.jj	2022-02-08 20:08:03.952540164 +0100
> +++ gcc/tree-ssa-structalias.cc	2022-02-09 14:32:39.127226322 +0100
> @@ -4591,8 +4591,10 @@ find_func_aliases_for_builtin_call (stru
>  	    }
>  	  get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc);
>  	  do_deref (&lhsc);
> -	  if (flag_delete_null_pointer_checks
> -	      && integer_zerop (gimple_call_arg (t, 1)))
> +	  tree arg1 = gimple_call_arg (t, 1);
> +	  if (integer_zerop (arg1)
> +	      && POINTER_TYPE_P (TREE_TYPE (arg1))
> +	      && !zero_address_valid (TREE_TYPE (arg1)))
>  	    {
>  	      ac.type = ADDRESSOF;
>  	      ac.var = nothing_id;
> --- gcc/testsuite/g++.dg/ubsan/pr104426.C.jj	2022-02-09 13:20:26.419429166 +0100
> +++ gcc/testsuite/g++.dg/ubsan/pr104426.C	2022-02-09 13:20:26.419429166 +0100
> @@ -0,0 +1,29 @@
> +// PR c++/104426
> +// { dg-do compile }
> +// { dg-options "-std=c++17 -fsanitize=undefined" }
> +
> +struct category
> +{
> +  constexpr bool failed () const noexcept { return true; }
> +};
> +
> +inline constexpr category s_cat;
> +
> +struct condition
> +{
> +  category const *cat_;
> +  constexpr bool failed () const noexcept
> +  {
> +    if (cat_)
> +      return cat_->failed ();
> +    else
> +      return false;
> +  }
> +};
> +
> +int
> +main ()
> +{
> +  constexpr condition cond { &s_cat };
> +  static_assert (cond.failed ());
> +}
> --- gcc/testsuite/g++.dg/ubsan/pr71962.C.jj	2022-02-09 13:20:26.419429166 +0100
> +++ gcc/testsuite/g++.dg/ubsan/pr71962.C	2022-02-09 13:20:26.419429166 +0100
> @@ -0,0 +1,17 @@
> +// PR c++/71962
> +// { dg-do compile }
> +// { dg-options "-std=c++14 -fsanitize=undefined" }
> +
> +struct P {
> +  constexpr P (const int *p) : p(p) { }
> +  const int *p;
> +  explicit constexpr operator bool() const { return (bool) p; }
> +};
> +
> +int
> +main ()
> +{
> +  static constexpr int x{1};
> +  constexpr P p{&x};
> +  static_assert ((bool)p, "");
> +}
> --- gcc/symtab.cc.jj	2022-02-08 20:08:03.913540699 +0100
> +++ gcc/symtab.cc	2022-02-09 14:10:50.866452450 +0100
> @@ -2135,11 +2135,11 @@ symtab_node::nonzero_address ()
>  	     could be useful to eliminate the NULL pointer checks in LTO
>  	     programs.  */
>  	  if (target->definition && !DECL_EXTERNAL (target->decl))
> -	      return true;
> +	    return true;
>  	  if (target->resolution != LDPR_UNKNOWN
>  	      && target->resolution != LDPR_UNDEF
>  	      && !target->can_be_discarded_p ()
> -	      && flag_delete_null_pointer_checks)
> +	      && !zero_address_valid (TYPE_ADDR_SPACE (TREE_TYPE (decl))))
>  	    return true;
>  	  return false;
>  	}
> @@ -2147,7 +2147,7 @@ symtab_node::nonzero_address ()
>          return false;
>      }
>  
> -  /* With !flag_delete_null_pointer_checks we assume that symbols may
> +  /* With -fno-delete-null-pointer-checks we assume that symbols may
>       bind to NULL. This is on by default on embedded targets only.
>  
>       Otherwise all non-WEAK symbols must be defined and thus non-NULL or
> @@ -2156,7 +2156,7 @@ symtab_node::nonzero_address ()
>  
>       When parsing, beware the cases when WEAK attribute is added later.  */
>    if ((!DECL_WEAK (decl) || DECL_COMDAT (decl))
> -      && flag_delete_null_pointer_checks)
> +      && !zero_address_valid (TYPE_ADDR_SPACE (TREE_TYPE (decl))))
>      {
>        refuse_visibility_changes = true;
>        return true;
> @@ -2164,10 +2164,11 @@ symtab_node::nonzero_address ()
>  
>    /* If target is defined and not extern, we know it will be
>       output and thus it will bind to non-NULL.
> -     Play safe for flag_delete_null_pointer_checks where weak definition may
> +     Play safe for -fno-delete-null-pointer-checks where weak definition may
>       be re-defined by NULL.  */
>    if (definition && !DECL_EXTERNAL (decl)
> -      && (flag_delete_null_pointer_checks || !DECL_WEAK (decl)))
> +      && (!zero_address_valid (TYPE_ADDR_SPACE (TREE_TYPE (decl)))
> +	  || !DECL_WEAK (decl)))
>      {
>        if (!DECL_WEAK (decl))
>          refuse_visibility_changes = true;
> @@ -2178,7 +2179,7 @@ symtab_node::nonzero_address ()
>    if (resolution != LDPR_UNKNOWN
>        && resolution != LDPR_UNDEF
>        && !can_be_discarded_p ()
> -      && flag_delete_null_pointer_checks)
> +      && !zero_address_valid (TYPE_ADDR_SPACE (TREE_TYPE (decl))))
>      return true;
>    return false;
>  }
> --- gcc/ipa-fnsummary.cc.jj	2022-02-08 20:08:03.855541494 +0100
> +++ gcc/ipa-fnsummary.cc	2022-02-09 13:54:40.594971201 +0100
> @@ -2573,7 +2573,7 @@ points_to_local_or_readonly_memory_p (tr
>  {
>    /* See if memory location is clearly invalid.  */
>    if (integer_zerop (t))
> -    return flag_delete_null_pointer_checks;
> +    return !zero_address_valid (TREE_TYPE (t));
>    if (TREE_CODE (t) == SSA_NAME)
>      {
>        /* For IPA passes we can consinder accesses to return slot local
> --- gcc/tree.h.jj	2022-02-04 14:36:56.382586067 +0100
> +++ gcc/tree.h	2022-02-09 13:37:22.944332825 +0100
> @@ -6579,4 +6579,14 @@ extern unsigned fndecl_dealloc_argno (tr
>     object or pointer.  Otherwise return null.  */
>  extern tree get_attr_nonstring_decl (tree, tree * = NULL);
>  
> +/* Return true if NULL is a valid address in AS.  */
> +extern bool zero_address_valid (addr_space_t);
> +
> +inline bool
> +zero_address_valid (tree type)
> +{
> +  gcc_checking_assert (POINTER_TYPE_P (type));
> +  return zero_address_valid (TYPE_ADDR_SPACE (TREE_TYPE (type)));
> +}
> +
>  #endif  /* GCC_TREE_H  */
> --- gcc/tree-ssa-alias.cc.jj	2022-02-08 20:08:03.917540644 +0100
> +++ gcc/tree-ssa-alias.cc	2022-02-09 14:16:14.231951242 +0100
> @@ -2644,13 +2644,13 @@ modref_may_conflict (const gcall *stmt,
>  
>  	      alias_stats.modref_baseptr_tests++;
>  
> -	      if (integer_zerop (arg) && flag_delete_null_pointer_checks)
> -		continue;
> -
> -	      /* PTA oracle will be unhapy of arg is not an pointer.  */
> +	      /* PTA oracle will be unhapy if arg is not an pointer.  */
>  	      if (!POINTER_TYPE_P (TREE_TYPE (arg)))
>  		return true;
>  
> +	      if (integer_zerop (arg) && !zero_address_valid (TREE_TYPE (arg)))
> +		continue;
> +
>  	      /* If we don't have base pointer, give up.  */
>  	      if (!ref->ref && !ref->base)
>  		continue;
> --- gcc/opts.cc.jj	2022-02-08 20:08:03.873541247 +0100
> +++ gcc/opts.cc	2022-02-09 13:20:26.418429180 +0100
> @@ -1224,12 +1224,6 @@ finish_options (struct gcc_options *opts
>        error_at (loc, "%<-fsanitize-recover=%s%> is not supported",
>  		sanitizer_opts[i].name);
>  
> -  /* When instrumenting the pointers, we don't want to remove
> -     the null pointer checks.  */
> -  if (opts->x_flag_sanitize & (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
> -				| SANITIZE_RETURNS_NONNULL_ATTRIBUTE))
> -    opts->x_flag_delete_null_pointer_checks = 0;
> -
>    /* Aggressive compiler optimizations may cause false negatives.  */
>    if (opts->x_flag_sanitize & ~(SANITIZE_LEAK | SANITIZE_UNREACHABLE))
>      opts->x_flag_aggressive_loop_optimizations = 0;
> --- gcc/ubsan.cc.jj	2022-02-08 20:08:04.001539492 +0100
> +++ gcc/ubsan.cc	2022-02-09 13:20:26.419429166 +0100
> @@ -1946,10 +1946,11 @@ instrument_nonnull_arg (gimple_stmt_iter
>  {
>    gimple *stmt = gsi_stmt (*gsi);
>    location_t loc[2];
> -  /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
> +  /* infer_nonnull_range needs zero_address_valid true,
>       while for nonnull sanitization it is clear.  */
> -  int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
> -  flag_delete_null_pointer_checks = 1;
> +  int save_flag_sanitize = flag_sanitize;
> +  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
> +		     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
>    loc[0] = gimple_location (stmt);
>    loc[1] = UNKNOWN_LOCATION;
>    for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++)
> @@ -2001,7 +2002,7 @@ instrument_nonnull_arg (gimple_stmt_iter
>  	}
>        *gsi = gsi_for_stmt (stmt);
>      }
> -  flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
> +  flag_sanitize = save_flag_sanitize;
>  }
>  
>  /* Instrument returns in functions with returns_nonnull attribute.  */
> @@ -2012,10 +2013,11 @@ instrument_nonnull_return (gimple_stmt_i
>    greturn *stmt = as_a <greturn *> (gsi_stmt (*gsi));
>    location_t loc[2];
>    tree arg = gimple_return_retval (stmt);
> -  /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
> -     while for nonnull return sanitization it is clear.  */
> -  int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
> -  flag_delete_null_pointer_checks = 1;
> +  /* infer_nonnull_range needs zero_address_valid true,
> +     while for nonnull sanitization it is clear.  */
> +  int save_flag_sanitize = flag_sanitize;
> +  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
> +		     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
>    loc[0] = gimple_location (stmt);
>    loc[1] = UNKNOWN_LOCATION;
>    if (arg
> @@ -2056,7 +2058,7 @@ instrument_nonnull_return (gimple_stmt_i
>        ubsan_create_edge (g);
>        *gsi = gsi_for_stmt (stmt);
>      }
> -  flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
> +  flag_sanitize = save_flag_sanitize;
>  }
>  
>  /* Instrument memory references.  Here we check whether the pointer
> --- gcc/tree-vrp.cc.jj	2022-02-08 20:08:03.977539821 +0100
> +++ gcc/tree-vrp.cc	2022-02-09 14:33:26.884560195 +0100
> @@ -732,7 +732,7 @@ extract_range_from_pointer_plus_expr (va
>    if ((!range_includes_zero_p (vr0)
>         || !range_includes_zero_p (vr1))
>        && !TYPE_OVERFLOW_WRAPS (expr_type)
> -      && (flag_delete_null_pointer_checks
> +      && (!zero_address_valid (expr_type)
>  	  || (range_int_cst_p (vr1)
>  	      && !tree_int_cst_sign_bit (vr1->max ()))))
>      vr->set_nonzero (expr_type);
> --- gcc/vr-values.cc.jj	2022-02-08 20:08:04.001539492 +0100
> +++ gcc/vr-values.cc	2022-02-09 14:34:32.257648360 +0100
> @@ -426,7 +426,7 @@ vr_values::vrp_stmt_computes_nonzero (gi
>  	     For -fdelete-null-pointer-checks -fno-wrapv-pointer we don't
>  	     allow going from non-NULL pointer to NULL.  */
>  	  if ((off_cst && known_eq (off, 0))
> -	      || (flag_delete_null_pointer_checks
> +	      || (!zero_address_valid (TREE_TYPE (expr))
>  		  && !TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))))
>  	    {
>  	      const value_range_equiv *vr
> @@ -440,7 +440,7 @@ vr_values::vrp_stmt_computes_nonzero (gi
>  	  if (!TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))
>  	      && off_cst
>  	      && known_ne (off, 0)
> -	      && (flag_delete_null_pointer_checks || known_gt (off, 0)))
> +	      && (!zero_address_valid (TREE_TYPE (expr)) || known_gt (off, 0)))
>  	    return true;
>  	}
>      }
> --- gcc/tree-ssa-loop-niter.cc.jj	2022-02-08 20:08:03.952540164 +0100
> +++ gcc/tree-ssa-loop-niter.cc	2022-02-09 14:17:40.614748806 +0100
> @@ -3899,7 +3899,7 @@ infer_loop_bounds_from_pointer_arith (cl
>       NULL pointer since that would mean wrapping, which we assume here not to
>       happen.  So, we can exclude NULL from the valid range of pointer
>       arithmetic.  */
> -  if (flag_delete_null_pointer_checks && int_cst_value (low) == 0)
> +  if (!zero_address_valid (type) && int_cst_value (low) == 0)
>      low = build_int_cstu (TREE_TYPE (low), TYPE_ALIGN_UNIT (TREE_TYPE (type)));
>  
>    record_nonwrapping_iv (loop, base, step, stmt, low, high, false, true);
> --- gcc/common.opt.jj	2022-02-08 20:08:03.655544236 +0100
> +++ gcc/common.opt	2022-02-09 13:02:46.907016534 +0100
> @@ -1288,7 +1288,7 @@ Common Var(flag_delete_dead_exceptions)
>  Delete dead instructions that may throw exceptions.
>  
>  fdelete-null-pointer-checks
> -Common Var(flag_delete_null_pointer_checks) Init(-1) Optimization
> +Common Var(flag_delete_null_pointer_checks_) Init(-1) Optimization
>  Delete useless null pointer checks.
>  
>  fdevirtualize-at-ltrans
> --- gcc/c-family/c-ubsan.cc.jj	2022-02-08 20:08:03.611544839 +0100
> +++ gcc/c-family/c-ubsan.cc	2022-02-09 13:20:26.419429166 +0100
> @@ -464,17 +464,16 @@ ubsan_maybe_instrument_reference_or_call
>  	{
>  	  bool strict_overflow_p = false;
>  	  /* tree_single_nonzero_warnv_p will not return true for non-weak
> -	     non-automatic decls with -fno-delete-null-pointer-checks,
> +	     non-automatic decls with !zero_address_valid,
>  	     which is disabled during -fsanitize=null.  We don't want to
>  	     instrument those, just weak vars though.  */
> -	  int save_flag_delete_null_pointer_checks
> -	    = flag_delete_null_pointer_checks;
> -	  flag_delete_null_pointer_checks = 1;
> +	  int save_flag_sanitize = flag_sanitize;
> +	  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
> +			     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
>  	  if (!tree_single_nonzero_warnv_p (op, &strict_overflow_p)
>  	      || strict_overflow_p)
>  	    instrument = true;
> -	  flag_delete_null_pointer_checks
> -	    = save_flag_delete_null_pointer_checks;
> +	  flag_sanitize = save_flag_sanitize;
>  	}
>        else if (sanitize_flags_p (SANITIZE_NULL))
>  	instrument = true;
> --- gcc/ipa-pure-const.cc.jj	2022-02-08 20:08:03.856541480 +0100
> +++ gcc/ipa-pure-const.cc	2022-02-09 14:00:45.161889970 +0100
> @@ -971,8 +971,7 @@ malloc_candidate_p (function *fun, bool
>    edge_iterator ei;
>    cgraph_node *node = cgraph_node::get_create (fun->decl);
>  
> -  if (EDGE_COUNT (exit_block->preds) == 0
> -      || !flag_delete_null_pointer_checks)
> +  if (EDGE_COUNT (exit_block->preds) == 0)
>      return false;
>  
>    auto_bitmap visited;
> @@ -992,6 +991,9 @@ malloc_candidate_p (function *fun, bool
>  	  || TREE_CODE (TREE_TYPE (retval)) != POINTER_TYPE)
>  	DUMP_AND_RETURN("Return value is not SSA_NAME or not a pointer type.")
>  
> +      if (zero_address_valid (TREE_TYPE (retval)))
> +	return false;
> +
>        if (!malloc_candidate_p_1 (fun, retval, ret_stmt, ipa, visited))
>  	return false;
>      }
> --- gcc/gimple.cc.jj	2022-02-08 20:08:03.851541549 +0100
> +++ gcc/gimple.cc	2022-02-09 13:47:54.762626732 +0100
> @@ -1673,19 +1673,22 @@ gimple_call_nonnull_result_p (gcall *cal
>    tree fndecl = gimple_call_fndecl (call);
>    if (!fndecl)
>      return false;
> -  if (flag_delete_null_pointer_checks && !flag_check_new
> +  if (!flag_check_new
>        && DECL_IS_OPERATOR_NEW_P (fndecl)
> -      && !TREE_NOTHROW (fndecl))
> +      && !TREE_NOTHROW (fndecl)
> +      && POINTER_TYPE_P (TREE_TYPE (fndecl))
> +      && !zero_address_valid (TREE_TYPE (fndecl)))
>      return true;
>  
>    /* References are always non-NULL.  */
> -  if (flag_delete_null_pointer_checks
> -      && TREE_CODE (TREE_TYPE (fndecl)) == REFERENCE_TYPE)
> +  if (TREE_CODE (TREE_TYPE (fndecl)) == REFERENCE_TYPE
> +      && !zero_address_valid (TREE_TYPE (fndecl)))
>      return true;
>  
> -  if (flag_delete_null_pointer_checks
> -      && lookup_attribute ("returns_nonnull",
> -			   TYPE_ATTRIBUTES (gimple_call_fntype (call))))
> +  tree fntype = gimple_call_fntype (call);
> +  if (lookup_attribute ("returns_nonnull", TYPE_ATTRIBUTES (fntype))
> +      && POINTER_TYPE_P (TREE_TYPE (fntype))
> +      && !zero_address_valid (TREE_TYPE (fntype)))
>      return true;
>    return gimple_alloca_call_p (call);
>  }
> @@ -3033,10 +3036,10 @@ infer_nonnull_range_by_dereference (gimp
>  {
>    /* We can only assume that a pointer dereference will yield
>       non-NULL if -fdelete-null-pointer-checks is enabled.  */
> -  if (!flag_delete_null_pointer_checks
> -      || !POINTER_TYPE_P (TREE_TYPE (op))
> +  if (!POINTER_TYPE_P (TREE_TYPE (op))
>        || gimple_code (stmt) == GIMPLE_ASM
> -      || gimple_clobber_p (stmt))
> +      || gimple_clobber_p (stmt)
> +      || zero_address_valid (TREE_TYPE (TREE_TYPE (op))))
>      return false;
>  
>    if (walk_stmt_load_store_ops (stmt, (void *)op,
> @@ -3053,9 +3056,9 @@ infer_nonnull_range_by_attribute (gimple
>  {
>    /* We can only assume that a pointer dereference will yield
>       non-NULL if -fdelete-null-pointer-checks is enabled.  */
> -  if (!flag_delete_null_pointer_checks
> -      || !POINTER_TYPE_P (TREE_TYPE (op))
> -      || gimple_code (stmt) == GIMPLE_ASM)
> +  if (!POINTER_TYPE_P (TREE_TYPE (op))
> +      || gimple_code (stmt) == GIMPLE_ASM
> +      || zero_address_valid (TREE_TYPE (TREE_TYPE (op))))
>      return false;
>  
>    if (is_gimple_call (stmt) && !gimple_call_internal_p (stmt))
> --- gcc/fold-const.cc.jj	2022-02-08 20:08:03.800542248 +0100
> +++ gcc/fold-const.cc	2022-02-09 13:43:49.947029100 +0100
> @@ -10713,13 +10713,16 @@ tree_expr_nonzero_warnv_p (tree t, bool
>        {
>  	tree fndecl = get_callee_fndecl (t);
>  	if (!fndecl) return false;
> -	if (flag_delete_null_pointer_checks && !flag_check_new
> +	if (!flag_check_new
>  	    && DECL_IS_OPERATOR_NEW_P (fndecl)
> -	    && !TREE_NOTHROW (fndecl))
> +	    && !TREE_NOTHROW (fndecl)
> +	    && POINTER_TYPE_P (TREE_TYPE (fndecl))
> +	    && !zero_address_valid (TREE_TYPE (fndecl)))
>  	  return true;
> -	if (flag_delete_null_pointer_checks
> -	    && lookup_attribute ("returns_nonnull",
> -		 TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
> +	if (lookup_attribute ("returns_nonnull",
> +		 TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))
> +	    && POINTER_TYPE_P (TREE_TYPE (fndecl))
> +	    && !zero_address_valid (TREE_TYPE (fndecl)))
>  	  return true;
>  	return alloca_call_p (t);
>        }
> 
> 
> 	Jakub
> 
>
  
Jakub Jelinek Feb. 9, 2022, 3:08 p.m. UTC | #6
On Wed, Feb 09, 2022 at 03:41:23PM +0100, Richard Biener wrote:
> On Wed, 9 Feb 2022, Jakub Jelinek wrote:
> 
> > On Wed, Feb 09, 2022 at 11:19:25AM +0100, Richard Biener wrote:
> > > That does look like bogus abstraction though - I'd rather have
> > > the target be specific w/o option checks and replace 
> > > targetm.zero_addres_valid uses with a wrapper (like you do for
> > > flag_delete_null_pointer_checks), if we think that the specific
> > > query should be adjusted by sanitize flags (why?) or
> > > folding_initializer (why?).
> > 
> > Based on discussions on IRC, here is a WIP patch.
> > 
> > Unfortunately, there are 3 unresolved issues:
> > 1) ipa-icf.cc uses
> >       && opt_for_fn (decl, flag_delete_null_pointer_checks))
> >    there is a pointer type, but I guess we'd need to adjust the
> >    target hook to take a defaulted fndecl argument and use that
> >    for the options
> 
> Yeah, I'd use a struct function arg tho, not a decl.

But both opts_for_fn and sanitizer_flag_p take a fndecl tree, not cfun.

> > 2) rtlanal.cc has:
> >     case SYMBOL_REF:
> >       return flag_delete_null_pointer_checks && !SYMBOL_REF_WEAK (x);
> >    Is there any way how to find out address space of a SYMBOL_REF?
> 
> TYPE_ADDR_SPACE (TREE_TYPE (SYMBOL_REF_DECL ())) I guess.

And default to ADDR_SPACE_GENERIC if there is no SYMBOL_REF_DECL?
That can work.

> >    Or shall it hardcode ADDR_SPACE_GENERIC?
> > 3) tree-ssa-structalias.cc has:
> >   if ((TREE_CODE (t) == INTEGER_CST
> >        && integer_zerop (t))
> >       /* The only valid CONSTRUCTORs in gimple with pointer typed
> >          elements are zero-initializer.  But in IPA mode we also
> >          process global initializers, so verify at least.  */
> >       || (TREE_CODE (t) == CONSTRUCTOR
> >           && CONSTRUCTOR_NELTS (t) == 0))
> >     {
> >       if (flag_delete_null_pointer_checks)
> >         temp.var = nothing_id;
> >       else
> >         temp.var = nonlocal_id;
> >       temp.type = ADDRESSOF;
> >       temp.offset = 0;
> >       results->safe_push (temp);
> >       return;
> >     }
> >    mpt really sure where to get the address space from in that case
> > 
> > And perhaps I didn't do it right in some other spots too.
> 
> This case is really difficult since we track pointers through integers
> (mind the missing POINTER_TYPE_P check above).  Of course we have
> no idea what address-space the integer was converted from or will
> be converted to so what the above wants to check is whether
> there is _any_ address-space that could have a zero pointer pointing
> to a valid object ...

Ugh.  So that would be ADDR_SPACE_ANY ((unsigned char) -1) and use that
in the hook?
But we'd penalize x86 through it because for the __seg_?s address spaces
we allow 0 address...

> > --- gcc/targhooks.cc.jj	2022-01-18 11:58:59.919977242 +0100
> > +++ gcc/targhooks.cc	2022-02-09 13:21:08.958835833 +0100
> > @@ -1598,7 +1598,7 @@ default_addr_space_subset_p (addr_space_
> >  bool
> >  default_addr_space_zero_address_valid (addr_space_t as ATTRIBUTE_UNUSED)
> >  {
> > -  return false;
> > +  return !flag_delete_null_pointer_checks_;
> 
> As said, I'd not do that, but check it in zero_address_valid only.
> Otherwise all targets overriding the hook have to remember to check
> this flag.  I suppose we'd then do
> 
>   if (option_set (flag_delete_null_pointer_check))
>     use flag_delete_null_pointer_check;
>   else
>     use targetm.zero_address_valid;
> 
> possibly only for the default address-space.

The advantage of checking the option in the hook is that it can precisely
decide what exactly it wants for each address space.  It can e.g. decide
to ignore the flag and say that in some address space 0 is always valid or 0
is never valid, or honor it under some conditions etc.
Doing it outside of the hook means we do the decision globally, and either
we hardcode targetm.addr_space.zero_address_valid || !flag_delete_null_pointer_check_, or
targetm.addr_space.zero_address_valid && !flag_delete_null_pointer_check_

> > --- gcc/config/i386/i386.cc.jj	2022-02-09 12:55:50.716774241 +0100
> > +++ gcc/config/i386/i386.cc	2022-02-09 13:23:01.041272540 +0100
> > @@ -23804,7 +23804,9 @@ ix86_gen_scratch_sse_rtx (machine_mode m
> >  static bool
> >  ix86_addr_space_zero_address_valid (addr_space_t as)
> >  {
> > -  return as != ADDR_SPACE_GENERIC;
> > +  if (as != ADDR_SPACE_GENERIC)
> > +    return true;
> 
> so this makes it not possibel to use -fdelete-null-pointer-checks to
> override the non-default address space behavior (on x86)

Yes.  To some extent that is already the current behavior as
targetm.addr_space.zero_address_valid is used in some spots explicitly.
But at least it is a target's decision and without introducing further
options like -fdelete-null-pointer-check=address_space
we need one or the other choice.

> > --- gcc/config/msp430/msp430.cc.jj	2022-02-04 14:36:54.410613609 +0100
> > +++ gcc/config/msp430/msp430.cc	2022-02-09 13:04:09.372888416 +0100
> > @@ -161,7 +161,7 @@ msp430_option_override (void)
> >  {
> >    /* The MSP430 architecture can safely dereference a NULL pointer.  In fact,
> >       there are memory mapped registers there.  */
> > -  flag_delete_null_pointer_checks = 0;
> > +  flag_delete_null_pointer_checks_ = 0;
> 
> I'd use the target hook to return false and let 
> -fdelete-null-pointer-checks override it.

This one is a hardcoded override, so users have no choice, so perhaps
a different hook would work.
But the nios2 case is that it just provides a different default for the
switch, so if we hardcode && or || in the generic code, one or the other
option wouldn't work.

BTW, perhaps we should have a more nuanced function and differentiate
between
1) address 0 can be dereferenced in a particular as
2) variables can appear at address 0 in a particular as
3) functions can appear at address 0 in a particular as
(perhaps just use 2+3 together).
Because e.g. for the x86 TLS segment if TLS is supported address 0 can
be dereferenced, but no variable nor function can appear there - the ABI
says that address 0 contains the generic address space pointer to that
address.

	Jakub
  
Richard Biener Feb. 10, 2022, 10:09 a.m. UTC | #7
On Wed, 9 Feb 2022, Jakub Jelinek wrote:

> On Wed, Feb 09, 2022 at 03:41:23PM +0100, Richard Biener wrote:
> > On Wed, 9 Feb 2022, Jakub Jelinek wrote:
> > 
> > > On Wed, Feb 09, 2022 at 11:19:25AM +0100, Richard Biener wrote:
> > > > That does look like bogus abstraction though - I'd rather have
> > > > the target be specific w/o option checks and replace 
> > > > targetm.zero_addres_valid uses with a wrapper (like you do for
> > > > flag_delete_null_pointer_checks), if we think that the specific
> > > > query should be adjusted by sanitize flags (why?) or
> > > > folding_initializer (why?).
> > > 
> > > Based on discussions on IRC, here is a WIP patch.
> > > 
> > > Unfortunately, there are 3 unresolved issues:
> > > 1) ipa-icf.cc uses
> > >       && opt_for_fn (decl, flag_delete_null_pointer_checks))
> > >    there is a pointer type, but I guess we'd need to adjust the
> > >    target hook to take a defaulted fndecl argument and use that
> > >    for the options
> > 
> > Yeah, I'd use a struct function arg tho, not a decl.
> 
> But both opts_for_fn and sanitizer_flag_p take a fndecl tree, not cfun.

Hmm, ok - the go for decl.

> > > 2) rtlanal.cc has:
> > >     case SYMBOL_REF:
> > >       return flag_delete_null_pointer_checks && !SYMBOL_REF_WEAK (x);
> > >    Is there any way how to find out address space of a SYMBOL_REF?
> > 
> > TYPE_ADDR_SPACE (TREE_TYPE (SYMBOL_REF_DECL ())) I guess.
> 
> And default to ADDR_SPACE_GENERIC if there is no SYMBOL_REF_DECL?
> That can work.

Yeah, alternatively the caller would need to pass down the MEM
since the address-space is only in the MEM attrs :/

> > >    Or shall it hardcode ADDR_SPACE_GENERIC?
> > > 3) tree-ssa-structalias.cc has:
> > >   if ((TREE_CODE (t) == INTEGER_CST
> > >        && integer_zerop (t))
> > >       /* The only valid CONSTRUCTORs in gimple with pointer typed
> > >          elements are zero-initializer.  But in IPA mode we also
> > >          process global initializers, so verify at least.  */
> > >       || (TREE_CODE (t) == CONSTRUCTOR
> > >           && CONSTRUCTOR_NELTS (t) == 0))
> > >     {
> > >       if (flag_delete_null_pointer_checks)
> > >         temp.var = nothing_id;
> > >       else
> > >         temp.var = nonlocal_id;
> > >       temp.type = ADDRESSOF;
> > >       temp.offset = 0;
> > >       results->safe_push (temp);
> > >       return;
> > >     }
> > >    mpt really sure where to get the address space from in that case
> > > 
> > > And perhaps I didn't do it right in some other spots too.
> > 
> > This case is really difficult since we track pointers through integers
> > (mind the missing POINTER_TYPE_P check above).  Of course we have
> > no idea what address-space the integer was converted from or will
> > be converted to so what the above wants to check is whether
> > there is _any_ address-space that could have a zero pointer pointing
> > to a valid object ...
> 
> Ugh.  So that would be ADDR_SPACE_ANY ((unsigned char) -1) and use that
> in the hook?
> But we'd penalize x86 through it because for the __seg_?s address spaces
> we allow 0 address...

Yes :/  Alternatively we can have PTA give up on non-default
address-space to/from non-pointer conversions which means not
to track points-to across such transitions.

Might be worth filing a tracking bug for this and leave things
in the current slightly broken state?  In this case it would
mean using ADDR_SPACE_GENERIC.  Also note that the specific
place can cobble up multiple fields and thus fields with
pointers to _different_ address-spaces ...

> > > --- gcc/targhooks.cc.jj	2022-01-18 11:58:59.919977242 +0100
> > > +++ gcc/targhooks.cc	2022-02-09 13:21:08.958835833 +0100
> > > @@ -1598,7 +1598,7 @@ default_addr_space_subset_p (addr_space_
> > >  bool
> > >  default_addr_space_zero_address_valid (addr_space_t as ATTRIBUTE_UNUSED)
> > >  {
> > > -  return false;
> > > +  return !flag_delete_null_pointer_checks_;
> > 
> > As said, I'd not do that, but check it in zero_address_valid only.
> > Otherwise all targets overriding the hook have to remember to check
> > this flag.  I suppose we'd then do
> > 
> >   if (option_set (flag_delete_null_pointer_check))
> >     use flag_delete_null_pointer_check;
> >   else
> >     use targetm.zero_address_valid;
> > 
> > possibly only for the default address-space.
> 
> The advantage of checking the option in the hook is that it can precisely
> decide what exactly it wants for each address space.  It can e.g. decide
> to ignore the flag and say that in some address space 0 is always valid or 0
> is never valid, or honor it under some conditions etc.
> Doing it outside of the hook means we do the decision globally, and either
> we hardcode targetm.addr_space.zero_address_valid || !flag_delete_null_pointer_check_, or
> targetm.addr_space.zero_address_valid && !flag_delete_null_pointer_check_

As said I'm leaning towards documenting that 
-f[no-]delete-null-pointer-checks only has effects on the generic
address-space.  It's really a mess, unfortunately :/

> > > --- gcc/config/i386/i386.cc.jj	2022-02-09 12:55:50.716774241 +0100
> > > +++ gcc/config/i386/i386.cc	2022-02-09 13:23:01.041272540 +0100
> > > @@ -23804,7 +23804,9 @@ ix86_gen_scratch_sse_rtx (machine_mode m
> > >  static bool
> > >  ix86_addr_space_zero_address_valid (addr_space_t as)
> > >  {
> > > -  return as != ADDR_SPACE_GENERIC;
> > > +  if (as != ADDR_SPACE_GENERIC)
> > > +    return true;
> > 
> > so this makes it not possibel to use -fdelete-null-pointer-checks to
> > override the non-default address space behavior (on x86)
> 
> Yes.  To some extent that is already the current behavior as
> targetm.addr_space.zero_address_valid is used in some spots explicitly.
> But at least it is a target's decision and without introducing further
> options like -fdelete-null-pointer-check=address_space
> we need one or the other choice.

See above.  As you say, it's the current behavior already on some
targets.

> > > --- gcc/config/msp430/msp430.cc.jj	2022-02-04 14:36:54.410613609 +0100
> > > +++ gcc/config/msp430/msp430.cc	2022-02-09 13:04:09.372888416 +0100
> > > @@ -161,7 +161,7 @@ msp430_option_override (void)
> > >  {
> > >    /* The MSP430 architecture can safely dereference a NULL pointer.  In fact,
> > >       there are memory mapped registers there.  */
> > > -  flag_delete_null_pointer_checks = 0;
> > > +  flag_delete_null_pointer_checks_ = 0;
> > 
> > I'd use the target hook to return false and let 
> > -fdelete-null-pointer-checks override it.
> 
> This one is a hardcoded override, so users have no choice, so perhaps
> a different hook would work.
> But the nios2 case is that it just provides a different default for the
> switch, so if we hardcode && or || in the generic code, one or the other
> option wouldn't work.
> 
> BTW, perhaps we should have a more nuanced function and differentiate
> between
> 1) address 0 can be dereferenced in a particular as
> 2) variables can appear at address 0 in a particular as
> 3) functions can appear at address 0 in a particular as
> (perhaps just use 2+3 together).
> Because e.g. for the x86 TLS segment if TLS is supported address 0 can
> be dereferenced, but no variable nor function can appear there - the ABI
> says that address 0 contains the generic address space pointer to that
> address.

Hmm, I see.  That makes things more complicated but yes ... if
we split it up we can as well split 2 and 3.  Also whether the
constant pool can appear at address 0?  Or whether the stack
starts at address 0?

Richard.
  

Patch

--- gcc/asan.h.jj	2022-01-11 23:11:21.542302070 +0100
+++ gcc/asan.h	2022-02-08 10:26:18.695464165 +0100
@@ -260,4 +260,12 @@  sanitize_coverage_p (const_tree fn = cur
 				   DECL_ATTRIBUTES (fn)) == NULL_TREE));
 }
 
+static inline bool
+delete_null_pointer_checks (const_tree fn = current_function_decl)
+{
+  return (flag_delete_null_pointer_checks
+	  && !sanitize_flags_p (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
+				| SANITIZE_RETURNS_NONNULL_ATTRIBUTE, fn));
+}
+
 #endif /* TREE_ASAN */
--- gcc/opts.cc.jj	2022-02-04 14:36:55.440599224 +0100
+++ gcc/opts.cc	2022-02-08 10:33:23.128566415 +0100
@@ -1224,12 +1224,6 @@  finish_options (struct gcc_options *opts
       error_at (loc, "%<-fsanitize-recover=%s%> is not supported",
 		sanitizer_opts[i].name);
 
-  /* When instrumenting the pointers, we don't want to remove
-     the null pointer checks.  */
-  if (opts->x_flag_sanitize & (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
-				| SANITIZE_RETURNS_NONNULL_ATTRIBUTE))
-    opts->x_flag_delete_null_pointer_checks = 0;
-
   /* Aggressive compiler optimizations may cause false negatives.  */
   if (opts->x_flag_sanitize & ~(SANITIZE_LEAK | SANITIZE_UNREACHABLE))
     opts->x_flag_aggressive_loop_optimizations = 0;
--- gcc/tree.cc.jj	2022-02-04 14:36:56.380586095 +0100
+++ gcc/tree.cc	2022-02-08 11:04:17.937686466 +0100
@@ -69,6 +69,7 @@  along with GCC; see the file COPYING3.
 #include "gimple-fold.h"
 #include "escaped_string.h"
 #include "gimple-range.h"
+#include "asan.h"
 
 /* Tree code classes.  */
 
@@ -13937,12 +13938,12 @@  nonnull_arg_p (const_tree arg)
   /* THIS argument of method is always non-NULL.  */
   if (TREE_CODE (TREE_TYPE (cfun->decl)) == METHOD_TYPE
       && arg == DECL_ARGUMENTS (cfun->decl)
-      && flag_delete_null_pointer_checks)
+      && delete_null_pointer_checks ())
     return true;
 
   /* Values passed by reference are always non-NULL.  */
   if (TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE
-      && flag_delete_null_pointer_checks)
+      && delete_null_pointer_checks ())
     return true;
 
   fntype = TREE_TYPE (cfun->decl);
--- gcc/range-op.cc.jj	2022-02-04 14:36:55.500598385 +0100
+++ gcc/range-op.cc	2022-02-08 11:03:26.136410211 +0100
@@ -46,6 +46,8 @@  along with GCC; see the file COPYING3.
 #include "wide-int.h"
 #include "value-relation.h"
 #include "range-op.h"
+#include "attribs.h"
+#include "asan.h"
 
 // Return the upper limit for a type.
 
@@ -3870,7 +3872,7 @@  pointer_plus_operator::wi_fold (irange &
   if ((!wi_includes_zero_p (type, lh_lb, lh_ub)
        || !wi_includes_zero_p (type, rh_lb, rh_ub))
       && !TYPE_OVERFLOW_WRAPS (type)
-      && (flag_delete_null_pointer_checks
+      && (delete_null_pointer_checks ()
 	  || !wi::sign_mask (rh_ub)))
     r = range_nonzero (type);
   else if (lh_lb == lh_ub && lh_lb == 0
--- gcc/gimple-range-fold.cc.jj	2022-02-04 14:36:55.198602603 +0100
+++ gcc/gimple-range-fold.cc	2022-02-08 10:57:57.461002325 +0100
@@ -43,6 +43,9 @@  along with GCC; see the file COPYING3.
 #include "value-query.h"
 #include "range-op.h"
 #include "gimple-range.h"
+#include "attribs.h"
+#include "asan.h"
+
 // Construct a fur_source, and set the m_query field.
 
 fur_source::fur_source (range_query *q)
@@ -721,7 +724,7 @@  fold_using_range::range_of_address (iran
       /* If &X->a is equal to X, the range of X is the result.  */
       if (off_cst && known_eq (off, 0))
 	return true;
-      else if (flag_delete_null_pointer_checks
+      else if (delete_null_pointer_checks ()
 	       && !TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr)))
 	{
 	  /* For -fdelete-null-pointer-checks -fno-wrapv-pointer we don't
@@ -741,7 +744,7 @@  fold_using_range::range_of_address (iran
       if (!TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))
 	  && off_cst
 	  && known_ne (off, 0)
-	  && (flag_delete_null_pointer_checks || known_gt (off, 0)))
+	  && (delete_null_pointer_checks () || known_gt (off, 0)))
 	{
 	  r = range_nonzero (TREE_TYPE (gimple_assign_rhs1 (stmt)));
 	  return true;
--- gcc/tree-ssa-structalias.cc.jj	2022-01-18 11:59:00.093974757 +0100
+++ gcc/tree-ssa-structalias.cc	2022-02-08 11:02:47.128955208 +0100
@@ -47,6 +47,7 @@ 
 #include "ipa-modref-tree.h"
 #include "ipa-modref.h"
 #include "attr-fnspec.h"
+#include "asan.h"
 
 /* The idea behind this analyzer is to generate set constraints from the
    program, then solve the resulting constraints in order to generate the
@@ -3531,7 +3532,7 @@  get_constraint_for_1 (tree t, vec<ce_s>
       || (TREE_CODE (t) == CONSTRUCTOR
 	  && CONSTRUCTOR_NELTS (t) == 0))
     {
-      if (flag_delete_null_pointer_checks)
+      if (delete_null_pointer_checks ())
 	temp.var = nothing_id;
       else
 	temp.var = nonlocal_id;
@@ -4591,7 +4592,7 @@  find_func_aliases_for_builtin_call (stru
 	    }
 	  get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc);
 	  do_deref (&lhsc);
-	  if (flag_delete_null_pointer_checks
+	  if (delete_null_pointer_checks ()
 	      && integer_zerop (gimple_call_arg (t, 1)))
 	    {
 	      ac.type = ADDRESSOF;
--- gcc/symtab.cc.jj	2022-01-18 11:58:59.892977628 +0100
+++ gcc/symtab.cc	2022-02-08 10:57:00.150803038 +0100
@@ -39,6 +39,7 @@  along with GCC; see the file COPYING3.
 #include "attribs.h"
 #include "builtins.h"
 #include "fold-const.h"
+#include "asan.h"
 
 static const char *ipa_ref_use_name[] = {"read","write","addr","alias"};
 
@@ -2117,6 +2118,13 @@  symtab_node::get_partitioning_class (voi
 bool
 symtab_node::nonzero_address ()
 {
+  /* When folding initializers, only look at -fdelete-null-pointer-checks,
+     otherwise also consider UBSan.  */
+  bool do_delete_null_pointer_checks
+    = (folding_initializer
+       ? flag_delete_null_pointer_checks
+       : delete_null_pointer_checks ());
+
   /* Weakrefs may be NULL when their target is not defined.  */
   if (alias && weakref)
     {
@@ -2135,11 +2143,11 @@  symtab_node::nonzero_address ()
 	     could be useful to eliminate the NULL pointer checks in LTO
 	     programs.  */
 	  if (target->definition && !DECL_EXTERNAL (target->decl))
-	      return true;
+	    return true;
 	  if (target->resolution != LDPR_UNKNOWN
 	      && target->resolution != LDPR_UNDEF
 	      && !target->can_be_discarded_p ()
-	      && flag_delete_null_pointer_checks)
+	      && do_delete_null_pointer_checks)
 	    return true;
 	  return false;
 	}
@@ -2156,7 +2164,7 @@  symtab_node::nonzero_address ()
 
      When parsing, beware the cases when WEAK attribute is added later.  */
   if ((!DECL_WEAK (decl) || DECL_COMDAT (decl))
-      && flag_delete_null_pointer_checks)
+      && do_delete_null_pointer_checks)
     {
       refuse_visibility_changes = true;
       return true;
@@ -2167,7 +2175,7 @@  symtab_node::nonzero_address ()
      Play safe for flag_delete_null_pointer_checks where weak definition may
      be re-defined by NULL.  */
   if (definition && !DECL_EXTERNAL (decl)
-      && (flag_delete_null_pointer_checks || !DECL_WEAK (decl)))
+      && (do_delete_null_pointer_checks || !DECL_WEAK (decl)))
     {
       if (!DECL_WEAK (decl))
         refuse_visibility_changes = true;
@@ -2178,7 +2186,7 @@  symtab_node::nonzero_address ()
   if (resolution != LDPR_UNKNOWN
       && resolution != LDPR_UNDEF
       && !can_be_discarded_p ()
-      && flag_delete_null_pointer_checks)
+      && do_delete_null_pointer_checks)
     return true;
   return false;
 }
--- gcc/ipa-fnsummary.cc.jj	2022-02-04 14:36:55.259601752 +0100
+++ gcc/ipa-fnsummary.cc	2022-02-08 10:58:17.435723245 +0100
@@ -88,6 +88,7 @@  along with GCC; see the file COPYING3.
 #include "symtab-clones.h"
 #include "gimple-range.h"
 #include "tree-dfa.h"
+#include "asan.h"
 
 /* Summaries.  */
 fast_function_summary <ipa_fn_summary *, va_gc> *ipa_fn_summaries;
@@ -2573,7 +2574,7 @@  points_to_local_or_readonly_memory_p (tr
 {
   /* See if memory location is clearly invalid.  */
   if (integer_zerop (t))
-    return flag_delete_null_pointer_checks;
+    return delete_null_pointer_checks ();
   if (TREE_CODE (t) == SSA_NAME)
     {
       /* For IPA passes we can consinder accesses to return slot local
--- gcc/tree-ssa-alias.cc.jj	2022-01-18 11:58:59.992976199 +0100
+++ gcc/tree-ssa-alias.cc	2022-02-08 11:03:40.523209202 +0100
@@ -47,6 +47,8 @@  along with GCC; see the file COPYING3.
 #include "print-tree.h"
 #include "tree-ssa-alias-compare.h"
 #include "builtins.h"
+#include "attribs.h"
+#include "asan.h"
 
 /* Broad overview of how alias analysis on gimple works:
 
@@ -2644,7 +2646,7 @@  modref_may_conflict (const gcall *stmt,
 
 	      alias_stats.modref_baseptr_tests++;
 
-	      if (integer_zerop (arg) && flag_delete_null_pointer_checks)
+	      if (integer_zerop (arg) && delete_null_pointer_checks ())
 		continue;
 
 	      /* PTA oracle will be unhapy of arg is not an pointer.  */
--- gcc/ubsan.cc.jj	2022-01-18 11:59:00.252972485 +0100
+++ gcc/ubsan.cc	2022-02-08 10:45:35.882359636 +0100
@@ -1946,10 +1946,11 @@  instrument_nonnull_arg (gimple_stmt_iter
 {
   gimple *stmt = gsi_stmt (*gsi);
   location_t loc[2];
-  /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
+  /* infer_nonnull_range needs delete_null_pointer_checks () true,
      while for nonnull sanitization it is clear.  */
-  int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
-  flag_delete_null_pointer_checks = 1;
+  int save_flag_sanitize = flag_sanitize;
+  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
+		     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
   loc[0] = gimple_location (stmt);
   loc[1] = UNKNOWN_LOCATION;
   for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++)
@@ -2001,7 +2002,7 @@  instrument_nonnull_arg (gimple_stmt_iter
 	}
       *gsi = gsi_for_stmt (stmt);
     }
-  flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
+  flag_sanitize = save_flag_sanitize;
 }
 
 /* Instrument returns in functions with returns_nonnull attribute.  */
@@ -2012,10 +2013,11 @@  instrument_nonnull_return (gimple_stmt_i
   greturn *stmt = as_a <greturn *> (gsi_stmt (*gsi));
   location_t loc[2];
   tree arg = gimple_return_retval (stmt);
-  /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
-     while for nonnull return sanitization it is clear.  */
-  int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
-  flag_delete_null_pointer_checks = 1;
+  /* infer_nonnull_range needs delete_null_pointer_checks () true,
+     while for nonnull sanitization it is clear.  */
+  int save_flag_sanitize = flag_sanitize;
+  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
+		     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
   loc[0] = gimple_location (stmt);
   loc[1] = UNKNOWN_LOCATION;
   if (arg
@@ -2056,7 +2058,7 @@  instrument_nonnull_return (gimple_stmt_i
       ubsan_create_edge (g);
       *gsi = gsi_for_stmt (stmt);
     }
-  flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
+  flag_sanitize = save_flag_sanitize;
 }
 
 /* Instrument memory references.  Here we check whether the pointer
--- gcc/tree-vrp.cc.jj	2022-01-18 11:59:00.249972528 +0100
+++ gcc/tree-vrp.cc	2022-02-08 11:04:47.829268939 +0100
@@ -50,6 +50,8 @@  along with GCC; see the file COPYING3.
 #include "gimple-range-path.h"
 #include "value-pointer-equiv.h"
 #include "gimple-fold.h"
+#include "attribs.h"
+#include "asan.h"
 
 /* Set of SSA names found live during the RPO traversal of the function
    for still active basic-blocks.  */
@@ -732,7 +734,7 @@  extract_range_from_pointer_plus_expr (va
   if ((!range_includes_zero_p (vr0)
        || !range_includes_zero_p (vr1))
       && !TYPE_OVERFLOW_WRAPS (expr_type)
-      && (flag_delete_null_pointer_checks
+      && (delete_null_pointer_checks ()
 	  || (range_int_cst_p (vr1)
 	      && !tree_int_cst_sign_bit (vr1->max ()))))
     vr->set_nonzero (expr_type);
--- gcc/vr-values.cc.jj	2022-01-18 11:59:00.255972442 +0100
+++ gcc/vr-values.cc	2022-02-08 11:04:05.241863846 +0100
@@ -51,6 +51,7 @@  along with GCC; see the file COPYING3.
 #include "cfghooks.h"
 #include "range-op.h"
 #include "gimple-range.h"
+#include "asan.h"
 
 /* Set value range VR to a non-negative range of type TYPE.  */
 
@@ -426,7 +427,7 @@  vr_values::vrp_stmt_computes_nonzero (gi
 	     For -fdelete-null-pointer-checks -fno-wrapv-pointer we don't
 	     allow going from non-NULL pointer to NULL.  */
 	  if ((off_cst && known_eq (off, 0))
-	      || (flag_delete_null_pointer_checks
+	      || (delete_null_pointer_checks ()
 		  && !TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))))
 	    {
 	      const value_range_equiv *vr
@@ -440,7 +441,7 @@  vr_values::vrp_stmt_computes_nonzero (gi
 	  if (!TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))
 	      && off_cst
 	      && known_ne (off, 0)
-	      && (flag_delete_null_pointer_checks || known_gt (off, 0)))
+	      && (delete_null_pointer_checks () || known_gt (off, 0)))
 	    return true;
 	}
     }
--- gcc/tree-ssa-loop-niter.cc.jj	2022-02-04 18:30:34.697003947 +0100
+++ gcc/tree-ssa-loop-niter.cc	2022-02-08 11:03:14.416573955 +0100
@@ -43,6 +43,8 @@  along with GCC; see the file COPYING3.
 #include "tree-scalar-evolution.h"
 #include "tree-dfa.h"
 #include "gimple-range.h"
+#include "attribs.h"
+#include "asan.h"
 
 
 /* The maximum number of dominator BBs we search for conditions
@@ -3899,7 +3901,7 @@  infer_loop_bounds_from_pointer_arith (cl
      NULL pointer since that would mean wrapping, which we assume here not to
      happen.  So, we can exclude NULL from the valid range of pointer
      arithmetic.  */
-  if (flag_delete_null_pointer_checks && int_cst_value (low) == 0)
+  if (delete_null_pointer_checks () && int_cst_value (low) == 0)
     low = build_int_cstu (TREE_TYPE (low), TYPE_ALIGN_UNIT (TREE_TYPE (type)));
 
   record_nonwrapping_iv (loop, base, step, stmt, low, high, false, true);
--- gcc/ipa-pure-const.cc.jj	2022-01-18 11:58:59.655981014 +0100
+++ gcc/ipa-pure-const.cc	2022-02-08 10:59:22.325816627 +0100
@@ -63,6 +63,8 @@  along with GCC; see the file COPYING3.
 #include "ipa-fnsummary.h"
 #include "symtab-thunks.h"
 #include "dbgcnt.h"
+#include "attribs.h"
+#include "asan.h"
 
 /* Lattice values for const and pure functions.  Everything starts out
    being const, then may drop to pure and then neither depending on
@@ -972,7 +974,7 @@  malloc_candidate_p (function *fun, bool
   cgraph_node *node = cgraph_node::get_create (fun->decl);
 
   if (EDGE_COUNT (exit_block->preds) == 0
-      || !flag_delete_null_pointer_checks)
+      || !delete_null_pointer_checks ())
     return false;
 
   auto_bitmap visited;
--- gcc/gimple.cc.jj	2022-02-04 14:36:55.217602338 +0100
+++ gcc/gimple.cc	2022-02-08 10:29:43.887620018 +0100
@@ -1673,17 +1673,18 @@  gimple_call_nonnull_result_p (gcall *cal
   tree fndecl = gimple_call_fndecl (call);
   if (!fndecl)
     return false;
-  if (flag_delete_null_pointer_checks && !flag_check_new
+  if (delete_null_pointer_checks ()
+      && !flag_check_new
       && DECL_IS_OPERATOR_NEW_P (fndecl)
       && !TREE_NOTHROW (fndecl))
     return true;
 
   /* References are always non-NULL.  */
-  if (flag_delete_null_pointer_checks
+  if (delete_null_pointer_checks ()
       && TREE_CODE (TREE_TYPE (fndecl)) == REFERENCE_TYPE)
     return true;
 
-  if (flag_delete_null_pointer_checks
+  if (delete_null_pointer_checks ()
       && lookup_attribute ("returns_nonnull",
 			   TYPE_ATTRIBUTES (gimple_call_fntype (call))))
     return true;
@@ -3033,7 +3034,7 @@  infer_nonnull_range_by_dereference (gimp
 {
   /* We can only assume that a pointer dereference will yield
      non-NULL if -fdelete-null-pointer-checks is enabled.  */
-  if (!flag_delete_null_pointer_checks
+  if (!delete_null_pointer_checks ()
       || !POINTER_TYPE_P (TREE_TYPE (op))
       || gimple_code (stmt) == GIMPLE_ASM
       || gimple_clobber_p (stmt))
@@ -3053,7 +3054,7 @@  infer_nonnull_range_by_attribute (gimple
 {
   /* We can only assume that a pointer dereference will yield
      non-NULL if -fdelete-null-pointer-checks is enabled.  */
-  if (!flag_delete_null_pointer_checks
+  if (!delete_null_pointer_checks ()
       || !POINTER_TYPE_P (TREE_TYPE (op))
       || gimple_code (stmt) == GIMPLE_ASM)
     return false;
--- gcc/fold-const.cc.jj	2022-02-06 11:16:22.352814445 +0100
+++ gcc/fold-const.cc	2022-02-08 10:30:08.237281118 +0100
@@ -10713,11 +10713,12 @@  tree_expr_nonzero_warnv_p (tree t, bool
       {
 	tree fndecl = get_callee_fndecl (t);
 	if (!fndecl) return false;
-	if (flag_delete_null_pointer_checks && !flag_check_new
+	if (delete_null_pointer_checks ()
+	    && !flag_check_new
 	    && DECL_IS_OPERATOR_NEW_P (fndecl)
 	    && !TREE_NOTHROW (fndecl))
 	  return true;
-	if (flag_delete_null_pointer_checks
+	if (delete_null_pointer_checks ()
 	    && lookup_attribute ("returns_nonnull",
 		 TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
 	  return true;
--- gcc/rtlanal.cc.jj	2022-01-18 11:58:59.886977714 +0100
+++ gcc/rtlanal.cc	2022-02-08 11:00:01.568268352 +0100
@@ -38,6 +38,9 @@  along with GCC; see the file COPYING3.
 #include "rtl-iter.h"
 #include "hard-reg-set.h"
 #include "function-abi.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "asan.h"
 
 /* Forward declarations */
 static void set_of_1 (rtx, const_rtx, void *);
@@ -721,7 +724,7 @@  nonzero_address_p (const_rtx x)
   switch (code)
     {
     case SYMBOL_REF:
-      return flag_delete_null_pointer_checks && !SYMBOL_REF_WEAK (x);
+      return delete_null_pointer_checks () && !SYMBOL_REF_WEAK (x);
 
     case LABEL_REF:
       return true;
--- gcc/c-family/c-ubsan.cc.jj	2022-01-18 11:58:58.885992014 +0100
+++ gcc/c-family/c-ubsan.cc	2022-02-08 10:50:50.620965960 +0100
@@ -464,17 +464,16 @@  ubsan_maybe_instrument_reference_or_call
 	{
 	  bool strict_overflow_p = false;
 	  /* tree_single_nonzero_warnv_p will not return true for non-weak
-	     non-automatic decls with -fno-delete-null-pointer-checks,
+	     non-automatic decls with !delete-null-pointer-checks (),
 	     which is disabled during -fsanitize=null.  We don't want to
 	     instrument those, just weak vars though.  */
-	  int save_flag_delete_null_pointer_checks
-	    = flag_delete_null_pointer_checks;
-	  flag_delete_null_pointer_checks = 1;
+	  int save_flag_sanitize = flag_sanitize;
+	  flag_sanitize &= ~(SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
+			     | SANITIZE_RETURNS_NONNULL_ATTRIBUTE);
 	  if (!tree_single_nonzero_warnv_p (op, &strict_overflow_p)
 	      || strict_overflow_p)
 	    instrument = true;
-	  flag_delete_null_pointer_checks
-	    = save_flag_delete_null_pointer_checks;
+	  flag_sanitize = save_flag_sanitize;
 	}
       else if (sanitize_flags_p (SANITIZE_NULL))
 	instrument = true;
--- gcc/cp/typeck.cc.jj	2022-02-04 14:36:54.818607910 +0100
+++ gcc/cp/typeck.cc	2022-02-08 10:49:17.747263557 +0100
@@ -7917,7 +7917,7 @@  build_static_cast_1 (location_t loc, tre
 	 that expr is NULL, if the static_cast is to a reference type,
 	 it is UB if it would be NULL, so omit the non-NULL check.  */
       expr = build_base_path (MINUS_EXPR, expr, base,
-			      /*nonnull=*/flag_delete_null_pointer_checks,
+			      /*nonnull=*/delete_null_pointer_checks (),
 			      complain);
 
       /* Convert the pointer to a reference -- but then remember that
--- gcc/testsuite/g++.dg/ubsan/pr71962.C.jj	2022-02-08 11:19:18.985101982 +0100
+++ gcc/testsuite/g++.dg/ubsan/pr71962.C	2022-02-08 11:12:59.582400892 +0100
@@ -0,0 +1,17 @@ 
+// PR c++/71962
+// { dg-do compile }
+// { dg-options "-std=c++14 -fsanitize=undefined" }
+
+struct P {
+  constexpr P (const int *p) : p(p) { }
+  const int *p;
+  explicit constexpr operator bool() const { return (bool) p; }
+};
+
+int
+main ()
+{
+  static constexpr int x{1};
+  constexpr P p{&x};
+  static_assert ((bool)p, "");
+}
--- gcc/testsuite/g++.dg/ubsan/pr104426.C.jj	2022-02-08 11:11:28.323675454 +0100
+++ gcc/testsuite/g++.dg/ubsan/pr104426.C	2022-02-08 11:11:15.280857618 +0100
@@ -0,0 +1,29 @@ 
+// PR c++/104426
+// { dg-do compile }
+// { dg-options "-std=c++17 -fsanitize=undefined" }
+
+struct category
+{
+  constexpr bool failed () const noexcept { return true; }
+};
+
+inline constexpr category s_cat;
+
+struct condition
+{
+  category const *cat_;
+  constexpr bool failed () const noexcept
+  {
+    if (cat_)
+      return cat_->failed ();
+    else
+      return false;
+  }
+};
+
+int
+main ()
+{
+  constexpr condition cond { &s_cat };
+  static_assert (cond.failed ());
+}