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++ PATCH] __builtin_launder


On Mon, 24 Oct 2016, Jakub Jelinek wrote:

> On Mon, Oct 24, 2016 at 02:11:42PM +0200, Richard Biener wrote:
> > On Mon, 24 Oct 2016, Jakub Jelinek wrote:
> > 
> > > On Mon, Oct 24, 2016 at 02:01:12PM +0200, Richard Biener wrote:
> > > > Ok, so with two std::launder (&a) and launder being CONST we'd happily
> > > > CSE them.  Which means repeatedly laundering &a wouldn't work.  You'd
> > > > have to do
> > > > 
> > > >   A *p = std::launder (&a)->g();
> > > >   A *q = std::launder(p)->f();
> > > >   std::launder (q)->g();
> > > > ...
> > > 
> > > I agree CSEing std::launder is undesirable, not in the testcase I've posted,
> > > there it is just fine, but in others.  So I'll test with ECF_LEAF | ECF_NOTHROW.
> > 
> > OTOH if we have to use the result of launder to access the laundered 
> > object we can DCE launder and also optimize launder(launder(&a)) to
> > launder(&a).
> > 
> > Thus if you continue with making it work semantically by pessimizing
> > the points-to for the result you can use ECF_NOVOPS (otherwise you
> > have to play tricks with DCE and pattern matching launder(launder())
> > gets interesting due to vops).
> 
> Here is what I've bootstrapped/regtested on x86_64-linux and i686-linux.
> ECF_NOVOPS is in there, launder(launder (...)) opt still missing, but can be
> added incrementally, if we find a need for that.  Ok for trunk?

Ok.

Thanks,
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_LEAF | ECF_NOTHROW | ECF_NOVOPS, 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]