This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: C++17 std::launder and aliasing


On Mon, 24 Oct 2016, Jakub Jelinek wrote:

> On Mon, Oct 24, 2016 at 11:55:08AM +0200, Richard Biener wrote:
> > > > Note that std::launder () then has to be implemented as clobbering
> > > > memory (the vtable pointer) as otherwise the devirt machiner won't see it.
> > > > This means that std::launder () will pessimize optimizations (well,
> > > > we can try to special-case it in the alias machinery to only consider
> > > > clobbering its argument directly pointed to memory and not keeping
> > > > its argument pointed to memory live).
> > > 
> > > It's OK for std::launder to pessimize optimizations. That's what it's
> > > for, after all :-)
> > 
> > Then hopefully there's no need to use it in libstdc++ itself ;)
> 
> Here is my current (so far untested) __builtin_launder patch.
> I'm actually using ECF_CONST | ECF_LEAF (besides ECF_NOTHROW) and it is good
> enough for the provided testcase - it acts as an optimization barrier in
> that points-to doesn't know what the result points to.
> But if you can come up with a testcase that would need either ECF_CONST
> or ECF_LEAF dropped, that is an option, sure.

Certainly a possibility - though points-to information is _not_ affected
by launder semantics.  std::launder only is a memory optimization
barrier for aliasing accesses (I believe even TBAA is valid as it
constrains the types that can be instantiated at the place).  For that
to work you'd have to instead make it have a VDEF and (optionally)
add special code to the stmt_may_use/clobber alias helpers.

> At RTL level it expands to just assignment, if we are sure aliasing isn't a
> problem then we don't need to do anything at the RTL level.

Good question.  I suppose RTL is fine (no such thing as devirt there).

Richard.

> 2016-10-24  Jakub Jelinek  <jakub@redhat.com>
> 
> 	* internal-fn.def (LAUNDER): New internal function.
> 	* internal-fn.c (expand_LAUNDER): New function.
> c-family/
> 	* c-common.h (enum rid): Add RID_BUILTIN_LAUNDER.
> 	* c-common.c (c_common_reswords): Add __builtin_launder.
> cp/
> 	* cp-tree.h (finish_builtin_launder): Declare.
> 	* parser.c (cp_parser_postfix_expression): Handle RID_BUILTIN_LAUNDER.
> 	* semantics.c (finish_builtin_launder): New function.
> 	* pt.c (tsubst_copy_and_build): Handle instantiation of IFN_LAUNDER.
> 	* constexpr.c (cxx_eval_internal_function): Handle IFN_LAUNDER.
> 	(potential_constant_expression_1): Likewise.
> testsuite/
> 	* g++.dg/cpp1z/launder1.C: New test.
> 	* g++.dg/cpp1z/launder2.C: New test.
> 
> --- gcc/internal-fn.def.jj	2016-10-22 20:58:11.649391643 +0200
> +++ gcc/internal-fn.def	2016-10-24 12:01:45.421259150 +0200
> @@ -198,6 +198,9 @@ DEF_INTERNAL_FN (ATOMIC_COMPARE_EXCHANGE
>  /* To implement [[fallthrough]].  */
>  DEF_INTERNAL_FN (FALLTHROUGH, ECF_LEAF | ECF_NOTHROW, NULL)
>  
> +/* To implement __builtin_launder.  */
> +DEF_INTERNAL_FN (LAUNDER, ECF_CONST | ECF_NOTHROW | ECF_LEAF, NULL)
> +
>  #undef DEF_INTERNAL_INT_FN
>  #undef DEF_INTERNAL_FLT_FN
>  #undef DEF_INTERNAL_OPTAB_FN
> --- gcc/internal-fn.c.jj	2016-10-22 20:58:11.612392109 +0200
> +++ gcc/internal-fn.c	2016-10-24 12:01:45.421259150 +0200
> @@ -2207,6 +2207,19 @@ expand_ATOMIC_COMPARE_EXCHANGE (internal
>    expand_ifn_atomic_compare_exchange (call);
>  }
>  
> +/* Expand LAUNDER to assignment, lhs = arg0.  */
> +
> +static void
> +expand_LAUNDER (internal_fn, gcall *call)
> +{
> +  tree lhs = gimple_call_lhs (call);
> +
> +  if (!lhs)
> +    return;
> +
> +  expand_assignment (lhs, gimple_call_arg (call, 0), false);
> +}
> +
>  /* Expand a call to FN using the operands in STMT.  FN has a single
>     output operand and NARGS input operands.  */
>  
> --- gcc/c-family/c-common.h.jj	2016-10-22 20:58:11.563392726 +0200
> +++ gcc/c-family/c-common.h	2016-10-24 12:01:45.423259124 +0200
> @@ -146,8 +146,8 @@ enum rid
>    RID_CONSTCAST, RID_DYNCAST, RID_REINTCAST, RID_STATCAST,
>  
>    /* C++ extensions */
> -  RID_ADDRESSOF,
> -  RID_BASES,                   RID_DIRECT_BASES,
> +  RID_ADDRESSOF,               RID_BASES,
> +  RID_BUILTIN_LAUNDER,         RID_DIRECT_BASES,
>    RID_HAS_NOTHROW_ASSIGN,      RID_HAS_NOTHROW_CONSTRUCTOR,
>    RID_HAS_NOTHROW_COPY,        RID_HAS_TRIVIAL_ASSIGN,
>    RID_HAS_TRIVIAL_CONSTRUCTOR, RID_HAS_TRIVIAL_COPY,
> --- gcc/c-family/c-common.c.jj	2016-10-22 20:58:11.534393091 +0200
> +++ gcc/c-family/c-common.c	2016-10-24 12:01:45.423259124 +0200
> @@ -375,6 +375,7 @@ const struct c_common_resword c_common_r
>      RID_BUILTIN_CALL_WITH_STATIC_CHAIN, D_CONLY },
>    { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
>    { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
> +  { "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
>    { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
>    { "__builtin_offsetof", RID_OFFSETOF, 0 },
>    { "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, D_CONLY },
> --- gcc/cp/cp-tree.h.jj	2016-10-14 12:31:49.000000000 +0200
> +++ gcc/cp/cp-tree.h	2016-10-24 12:31:43.271546769 +0200
> @@ -6494,6 +6494,8 @@ extern bool generic_lambda_fn_p			(tree)
>  extern void maybe_add_lambda_conv_op            (tree);
>  extern bool is_lambda_ignored_entity            (tree);
>  extern bool lambda_static_thunk_p		(tree);
> +extern tree finish_builtin_launder		(location_t, tree,
> +						 tsubst_flags_t);
>  
>  /* in tree.c */
>  extern int cp_tree_operand_length		(const_tree);
> --- gcc/cp/parser.c.jj	2016-10-24 12:01:39.080339247 +0200
> +++ gcc/cp/parser.c	2016-10-24 12:30:39.046359431 +0200
> @@ -6607,6 +6607,7 @@ cp_parser_postfix_expression (cp_parser
>  
>      case RID_ADDRESSOF:
>      case RID_BUILTIN_SHUFFLE:
> +    case RID_BUILTIN_LAUNDER:
>        {
>  	vec<tree, va_gc> *vec;
>  	unsigned int i;
> @@ -6638,6 +6639,18 @@ cp_parser_postfix_expression (cp_parser
>  		postfix_expression = error_mark_node;
>  	      }
>  	    break;
> +
> +	  case RID_BUILTIN_LAUNDER:
> +	    if (vec->length () == 1)
> +	      postfix_expression = finish_builtin_launder (loc, (*vec)[0],
> +							   tf_warning_or_error);
> +	    else
> +	      {
> +		error_at (loc, "wrong number of arguments to "
> +			       "%<__builtin_launder%>");
> +		postfix_expression = error_mark_node;
> +	      }
> +	    break;
>  
>  	  case RID_BUILTIN_SHUFFLE:
>  	    if (vec->length () == 2)
> --- gcc/cp/semantics.c.jj	2016-10-06 23:16:43.000000000 +0200
> +++ gcc/cp/semantics.c	2016-10-24 13:04:10.884987106 +0200
> @@ -9449,4 +9449,26 @@ finish_binary_fold_expr (tree expr1, tre
>    return error_mark_node;
>  }
>  
> +/* Finish __builtin_launder (arg).  */
> +
> +tree
> +finish_builtin_launder (location_t loc, tree arg, tsubst_flags_t complain)
> +{
> +  tree orig_arg = arg;
> +  if (!type_dependent_expression_p (arg))
> +    arg = decay_conversion (arg, complain);
> +  if (error_operand_p (arg))
> +    return error_mark_node;
> +  if (!type_dependent_expression_p (arg)
> +      && TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
> +    {
> +      error_at (loc, "non-pointer argument to %<__builtin_launder%>");
> +      return error_mark_node;
> +    }
> +  if (processing_template_decl)
> +    arg = orig_arg;
> +  return build_call_expr_internal_loc (loc, IFN_LAUNDER,
> +				       TREE_TYPE (arg), 1, arg);
> +}
> +
>  #include "gt-cp-semantics.h"
> --- gcc/cp/pt.c.jj	2016-10-22 18:57:49.000000000 +0200
> +++ gcc/cp/pt.c	2016-10-24 12:34:51.640163281 +0200
> @@ -16599,19 +16599,19 @@ tsubst_copy_and_build (tree t,
>  	tree ret;
>  
>  	function = CALL_EXPR_FN (t);
> -	if (function == NULL_TREE)
> -	  {
> -	    /* If you hit this assert, it means that you're trying to tsubst
> -	       an internal function with arguments.  This isn't yet supported,
> -	       so you need to build another internal call with the tsubsted
> -	       arguments after the arguments have been tsubsted down below.  */
> -	    gcc_assert (call_expr_nargs (t) == 0);
> -	    RETURN (t);
> -	  }
> +	/* Internal function with no arguments.  */
> +	if (function == NULL_TREE && call_expr_nargs (t) == 0)
> +	  RETURN (t);
> +
>  	/* When we parsed the expression, we determined whether or
>  	   not Koenig lookup should be performed.  */
>  	koenig_p = KOENIG_LOOKUP_P (t);
> -	if (TREE_CODE (function) == SCOPE_REF)
> +	if (function == NULL_TREE)
> +	  {
> +	    koenig_p = false;
> +	    qualified_p = false;
> +	  }
> +	else if (TREE_CODE (function) == SCOPE_REF)
>  	  {
>  	    qualified_p = true;
>  	    function = tsubst_qualified_id (function, args, complain, in_decl,
> @@ -16709,7 +16709,8 @@ tsubst_copy_and_build (tree t,
>  	    && !any_type_dependent_arguments_p (call_args))
>  	  function = perform_koenig_lookup (function, call_args, tf_none);
>  
> -	if (identifier_p (function)
> +	if (function != NULL_TREE
> +	    && identifier_p (function)
>  	    && !any_type_dependent_arguments_p (call_args))
>  	  {
>  	    if (koenig_p && (complain & tf_warning_or_error))
> @@ -16721,7 +16722,10 @@ tsubst_copy_and_build (tree t,
>  			    (function, args, complain, in_decl, true,
>  			     integral_constant_expression_p));
>  		if (unq == error_mark_node)
> -		  RETURN (error_mark_node);
> +		  {
> +		    release_tree_vector (call_args);
> +		    RETURN (error_mark_node);
> +		  }
>  
>  		if (unq != function)
>  		  {
> @@ -16774,14 +16778,40 @@ tsubst_copy_and_build (tree t,
>  	  }
>  
>  	/* Remember that there was a reference to this entity.  */
> -	if (DECL_P (function)
> +	if (function != NULL_TREE
> +	    && DECL_P (function)
>  	    && !mark_used (function, complain) && !(complain & tf_error))
> -	  RETURN (error_mark_node);
> +	  {
> +	    release_tree_vector (call_args);
> +	    RETURN (error_mark_node);
> +	  }
>  
>  	/* Put back tf_decltype for the actual call.  */
>  	complain |= decltype_flag;
>  
> -	if (TREE_CODE (function) == OFFSET_REF)
> +	if (function == NULL_TREE)
> +	  switch (CALL_EXPR_IFN (t))
> +	    {
> +	    case IFN_LAUNDER:
> +	      gcc_assert (nargs == 1);
> +	      if (vec_safe_length (call_args) != 1)
> +		{
> +		  error_at (EXPR_LOC_OR_LOC (t, input_location),
> +			    "wrong number of arguments to "
> +			    "%<__builtin_launder%>");
> +		  ret = error_mark_node;
> +		}
> +	      else
> +		ret = finish_builtin_launder (EXPR_LOC_OR_LOC (t,
> +							       input_location),
> +					      (*call_args)[0], complain);
> +	      break;
> +
> +	    default:
> +	      /* Unsupported internal function with arguments.  */
> +	      gcc_unreachable ();
> +	    }
> +	else if (TREE_CODE (function) == OFFSET_REF)
>  	  ret = build_offset_ref_call_from_tree (function, &call_args,
>  						 complain);
>  	else if (TREE_CODE (function) == COMPONENT_REF)
> --- gcc/cp/constexpr.c.jj	2016-10-22 20:58:11.900388483 +0200
> +++ gcc/cp/constexpr.c	2016-10-24 12:01:45.420259162 +0200
> @@ -1330,6 +1330,10 @@ cxx_eval_internal_function (const conste
>        opcode = MULT_EXPR;
>        break;
>  
> +    case IFN_LAUNDER:
> +      return cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
> +					   false, non_constant_p, overflow_p);
> +
>      default:
>        if (!ctx->quiet)
>  	error_at (EXPR_LOC_OR_LOC (t, input_location),
> @@ -4920,6 +4924,7 @@ potential_constant_expression_1 (tree t,
>  		case IFN_ADD_OVERFLOW:
>  		case IFN_SUB_OVERFLOW:
>  		case IFN_MUL_OVERFLOW:
> +		case IFN_LAUNDER:
>  		  bail = false;
>  
>  		default:
> --- gcc/testsuite/g++.dg/cpp1z/launder1.C.jj	2016-10-24 12:40:58.308526714 +0200
> +++ gcc/testsuite/g++.dg/cpp1z/launder1.C	2016-10-24 12:40:29.000000000 +0200
> @@ -0,0 +1,51 @@
> +// { dg-do run { target c++11 } }
> +// { dg-additional-options "-O2" }
> +
> +void *
> +operator new (decltype (sizeof (0)), void *p)
> +{
> +  return p;
> +}
> +
> +namespace std
> +{
> +  template <typename T>
> +  T *
> +  launder (T *p)
> +  {
> +    return __builtin_launder (p);
> +  }
> +}
> +
> +struct A
> +{
> +  virtual int f ();
> +};
> +
> +struct B : A
> +{
> +  virtual int f ()
> +  {
> +    new (this) A;
> +    return 1;
> +  }
> +};
> +
> +int
> +A::f ()
> +{
> +  new (this) B;
> +  return 2;
> +}
> +
> +static_assert (sizeof (B) == sizeof (A), "");
> +
> +int
> +main ()
> +{
> +  A a;
> +  int n = a.f ();
> +  int m = std::launder (&a)->f ();
> +  if (n != 2 || m != 1)
> +    __builtin_abort ();
> +}
> --- gcc/testsuite/g++.dg/cpp1z/launder2.C.jj	2016-10-24 13:12:40.487597036 +0200
> +++ gcc/testsuite/g++.dg/cpp1z/launder2.C	2016-10-24 13:12:36.286649713 +0200
> @@ -0,0 +1,42 @@
> +// { dg-do compile { target c++11 } }
> +
> +int a;
> +int *b = __builtin_launder ();		// { dg-error "wrong number of arguments to" }
> +int *c = __builtin_launder (&a, 2);	// { dg-error "wrong number of arguments to" }
> +int *d = __builtin_launder (&a);
> +int e = __builtin_launder (a);		// { dg-error "non-pointer argument to" }
> +int &f = a;
> +int g = __builtin_launder (f);		// { dg-error "non-pointer argument to" }
> +
> +template <typename T> T f1 (T x) { return __builtin_launder (x); }	// { dg-error "non-pointer argument to" }
> +template <typename T> T f2 (T x) { return __builtin_launder (x); }
> +
> +int h = f1 (a);
> +int *i = f2 (&a);
> +struct S { long s; int foo (); } *j;
> +S *k = f2 (j);
> +int l = __builtin_launder (j)->foo ();
> +
> +template <typename T> T *f3 (T *x) { return __builtin_launder (x); }
> +
> +long *m;
> +long *n = f3 (m);
> +int *o = f3 (&a);
> +
> +template <typename T, typename... U> T *f4 (U... x) { return __builtin_launder (x...); }
> +template <typename T, typename... U> T *f5 (U... x) { return __builtin_launder (x...); }	// { dg-error "wrong number of arguments to" }
> +template <typename T, typename... U> T *f6 (U... x) { return __builtin_launder (x...); }	// { dg-error "wrong number of arguments to" }
> +template <typename T, typename... U> T f7 (T x, U... y) { return __builtin_launder (x, y...); }	// { dg-error "wrong number of arguments to" }
> +
> +long *p = f4<long, long *> (m);
> +long *q = f5<long> ();
> +long *r = f6<long, long *, int> (m, 1);
> +S s;
> +int t = __builtin_launder (&s)->foo ();
> +
> +constexpr const int *f8 (const int *x) { return __builtin_launder (x); }
> +template <typename T> constexpr T f9 (T x) { return __builtin_launder (x); }
> +constexpr int u = 6;
> +constexpr const int *v = f8 (&u);
> +constexpr const int *w = f9 (&u);
> +static_assert (*f8 (&u) == 6 && *f9 (&u) == 6, "");
> 
> 
> 	Jakub
> 
> 

-- 
Richard Biener <rguenther@suse.de>
SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nuernberg)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]