[PATCH 1/7] Add __builtin_speculation_safe_value

Richard Earnshaw (lists) Richard.Earnshaw@arm.com
Mon Jul 23 14:28:00 GMT 2018


Ping.

This patch needs reviewing an appropriate maintainer.

I'm aware that the documentation needs extending a bit to provide some
example use cases as discussed in the follow-up to the generic
discussion, but the rest of the patch still stands.

R.

On 09/07/18 17:38, Richard Earnshaw wrote:
> 
> This patch defines a new intrinsic function
> __builtin_speculation_safe_value.  A generic default implementation is
> defined which will attempt to use the backend pattern
> "speculation_safe_barrier".  If this pattern is not defined, or if it
> is not available, then the compiler will emit a warning, but
> compilation will continue.
> 
> Note that the test spec-barrier-1.c will currently fail on all
> targets.  This is deliberate, the failure will go away when
> appropriate action is taken for each target backend.
> 
> gcc:
> 	* builtin-types.def (BT_FN_PTR_PTR_VAR): New function type.
> 	(BT_FN_I1_I1_VAR, BT_FN_I2_I2_VAR, BT_FN_I4_I4_VAR): Likewise.
> 	(BT_FN_I8_I8_VAR, BT_FN_I16_I16_VAR): Likewise.
> 	* builtins.def (BUILT_IN_SPECULATION_SAFE_VALUE_N): New builtin.
> 	(BUILT_IN_SPECULATION_SAFE_VALUE_PTR): New internal builtin.
> 	(BUILT_IN_SPECULATION_SAFE_VALUE_1): Likewise.
> 	(BUILT_IN_SPECULATION_SAFE_VALUE_2): Likewise.
> 	(BUILT_IN_SPECULATION_SAFE_VALUE_4): Likewise.
> 	(BUILT_IN_SPECULATION_SAFE_VALUE_8): Likewise.
> 	(BUILT_IN_SPECULATION_SAFE_VALUE_16): Likewise.
> 	* builtins.c (expand_speculation_safe_value): New function.
> 	(expand_builtin): Call it.
> 	* doc/cpp.texi: Document predefine __HAVE_SPECULATION_SAFE_VALUE.
> 	* doc/extend.texi: Document __builtin_speculation_safe_value.
> 	* doc/md.texi: Document "speculation_barrier" pattern.
> 	* doc/tm.texi.in: Pull in TARGET_SPECULATION_SAFE_VALUE.
> 	* doc/tm.texi: Regenerated.
> 	* target.def (speculation_safe_value): New hook.
> 	* targhooks.c (default_speculation_safe_value): New function.
> 	* targhooks.h (default_speculation_safe_value): Add prototype.
> 
> c-family:
> 	* c-common.c (speculation_safe_resolve_size): New function.
> 	(speculation_safe_resolve_params): New function.
> 	(speculation_safe_resolve_return): New function.
> 	(resolve_overloaded_builtin): Handle __builtin_speculation_safe_value.
> 	* c-cppbuiltin.c (c_cpp_builtins): Add pre-define for
> 	__HAVE_SPECULATION_SAFE_VALUE.
> 
> testsuite:
> 	* gcc.dg/spec-barrier-1.c: New test.
> 	* gcc.dg/spec-barrier-2.c: New test.
> 	* gcc.dg/spec-barrier-3.c: New test.
> ---
>  gcc/builtin-types.def                 |   6 ++
>  gcc/builtins.c                        |  57 ++++++++++++++
>  gcc/builtins.def                      |  20 +++++
>  gcc/c-family/c-common.c               | 143 ++++++++++++++++++++++++++++++++++
>  gcc/c-family/c-cppbuiltin.c           |   5 +-
>  gcc/doc/cpp.texi                      |   4 +
>  gcc/doc/extend.texi                   |  29 +++++++
>  gcc/doc/md.texi                       |  15 ++++
>  gcc/doc/tm.texi                       |  20 +++++
>  gcc/doc/tm.texi.in                    |   2 +
>  gcc/target.def                        |  23 ++++++
>  gcc/targhooks.c                       |  27 +++++++
>  gcc/targhooks.h                       |   2 +
>  gcc/testsuite/gcc.dg/spec-barrier-1.c |  40 ++++++++++
>  gcc/testsuite/gcc.dg/spec-barrier-2.c |  19 +++++
>  gcc/testsuite/gcc.dg/spec-barrier-3.c |  13 ++++
>  16 files changed, 424 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/testsuite/gcc.dg/spec-barrier-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/spec-barrier-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/spec-barrier-3.c
> 
> 
> 0001-Add-__builtin_speculation_safe_value.patch
> 
> 
> diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
> index b01095c..70fae35 100644
> --- a/gcc/builtin-types.def
> +++ b/gcc/builtin-types.def
> @@ -763,6 +763,12 @@ DEF_FUNCTION_TYPE_VAR_1 (BT_FN_VOID_LONG_VAR,
>  			 BT_VOID, BT_LONG)
>  DEF_FUNCTION_TYPE_VAR_1 (BT_FN_VOID_ULL_VAR,
>  			 BT_VOID, BT_ULONGLONG)
> +DEF_FUNCTION_TYPE_VAR_1 (BT_FN_PTR_PTR_VAR, BT_PTR, BT_PTR)
> +DEF_FUNCTION_TYPE_VAR_1 (BT_FN_I1_I1_VAR, BT_I1, BT_I1)
> +DEF_FUNCTION_TYPE_VAR_1 (BT_FN_I2_I2_VAR, BT_I2, BT_I2)
> +DEF_FUNCTION_TYPE_VAR_1 (BT_FN_I4_I4_VAR, BT_I4, BT_I4)
> +DEF_FUNCTION_TYPE_VAR_1 (BT_FN_I8_I8_VAR, BT_I8, BT_I8)
> +DEF_FUNCTION_TYPE_VAR_1 (BT_FN_I16_I16_VAR, BT_I16, BT_I16)
>  
>  DEF_FUNCTION_TYPE_VAR_2 (BT_FN_INT_FILEPTR_CONST_STRING_VAR,
>  			 BT_INT, BT_FILEPTR, BT_CONST_STRING)
> diff --git a/gcc/builtins.c b/gcc/builtins.c
> index 91658e8..9f97ecf 100644
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -6716,6 +6716,52 @@ expand_builtin_goacc_parlevel_id_size (tree exp, rtx target, int ignore)
>    return target;
>  }
>  
> +/* Expand a call to __builtin_speculation_safe_value_<N>.  MODE
> +   represents the size of the first argument to that call, or VOIDmode
> +   if the argument is a pointer.  IGNORE will be true if the result
> +   isn't used.  */
> +static rtx
> +expand_speculation_safe_value (machine_mode mode, tree exp, rtx target,
> +			       bool ignore)
> +{
> +  rtx val, failsafe;
> +  unsigned nargs = call_expr_nargs (exp);
> +
> +  tree arg0 = CALL_EXPR_ARG (exp, 0);
> +
> +  if (mode == VOIDmode)
> +    {
> +      mode = TYPE_MODE (TREE_TYPE (arg0));
> +      gcc_assert (GET_MODE_CLASS (mode) == MODE_INT);
> +    }
> +
> +  val = expand_expr (arg0, NULL_RTX, mode, EXPAND_NORMAL);
> +
> +  /* An optional second argument can be used as a failsafe value on
> +     some machines.  If it isn't present, then the failsafe value is
> +     assumed to be 0.  */
> +  if (nargs > 1)
> +    {
> +      tree arg1 = CALL_EXPR_ARG (exp, 1);
> +      failsafe = expand_expr (arg1, NULL_RTX, mode, EXPAND_NORMAL);
> +    }
> +  else
> +    failsafe = const0_rtx;
> +
> +  /* If the result isn't used, the behavior is undefined.  It would be
> +     nice to emit a warning here, but path splitting means this might
> +     happen with legitimate code.  So simply drop the builtin
> +     expansion in that case; we've handled any side-effects above.  */
> +  if (ignore)
> +    return const0_rtx;
> +
> +  /* If we don't have a suitable target, create one to hold the result.  */
> +  if (target == NULL)
> +    target = gen_reg_rtx (mode);
> +
> +  return targetm.speculation_safe_value (mode, target, val, failsafe);
> +}
> +
>  /* Expand an expression EXP that calls a built-in function,
>     with result going to TARGET if that's convenient
>     (and in mode MODE if that's convenient).
> @@ -7827,6 +7873,17 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
>      case BUILT_IN_GOACC_PARLEVEL_SIZE:
>        return expand_builtin_goacc_parlevel_id_size (exp, target, ignore);
>  
> +    case BUILT_IN_SPECULATION_SAFE_VALUE_PTR:
> +      return expand_speculation_safe_value (VOIDmode, exp, target, ignore);
> +
> +    case BUILT_IN_SPECULATION_SAFE_VALUE_1:
> +    case BUILT_IN_SPECULATION_SAFE_VALUE_2:
> +    case BUILT_IN_SPECULATION_SAFE_VALUE_4:
> +    case BUILT_IN_SPECULATION_SAFE_VALUE_8:
> +    case BUILT_IN_SPECULATION_SAFE_VALUE_16:
> +      mode = get_builtin_sync_mode (fcode - BUILT_IN_SPECULATION_SAFE_VALUE_1);
> +      return expand_speculation_safe_value (mode, exp, target, ignore);
> +
>      default:	/* just do library call, if unknown builtin */
>        break;
>      }
> diff --git a/gcc/builtins.def b/gcc/builtins.def
> index aacbd51..b71d89c 100644
> --- a/gcc/builtins.def
> +++ b/gcc/builtins.def
> @@ -1003,6 +1003,26 @@ DEF_BUILTIN (BUILT_IN_EMUTLS_REGISTER_COMMON,
>  	     true, true, true, ATTR_NOTHROW_LEAF_LIST, false,
>  	     !targetm.have_tls)
>  
> +/* Suppressing speculation.  Users are expected to use the first (N)
> +   variant, which will be translated internally into one of the other
> +   types.  */
> +
> +DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_N, "speculation_safe_value",
> +		 BT_FN_VOID_VAR, ATTR_NULL)
> +
> +DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_PTR,
> +		 "speculation_safe_value_ptr", BT_FN_PTR_PTR_VAR, ATTR_NULL)
> +DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_1, "speculation_safe_value_1",
> +		 BT_FN_I1_I1_VAR, ATTR_NULL)
> +DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_2, "speculation_safe_value_2",
> +		 BT_FN_I2_I2_VAR, ATTR_NULL)
> +DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_4, "speculation_safe_value_4",
> +		 BT_FN_I4_I4_VAR, ATTR_NULL)
> +DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_8, "speculation_safe_value_8",
> +		 BT_FN_I8_I8_VAR, ATTR_NULL)
> +DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_16,
> +		 "speculation_safe_value_16", BT_FN_I16_I16_VAR, ATTR_NULL)
> +
>  /* Exception support.  */
>  DEF_BUILTIN_STUB (BUILT_IN_UNWIND_RESUME, "__builtin_unwind_resume")
>  DEF_BUILTIN_STUB (BUILT_IN_CXA_END_CLEANUP, "__builtin_cxa_end_cleanup")
> diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
> index f5e1111..32a2de2 100644
> --- a/gcc/c-family/c-common.c
> +++ b/gcc/c-family/c-common.c
> @@ -6457,6 +6457,121 @@ builtin_type_for_size (int size, bool unsignedp)
>    return type ? type : error_mark_node;
>  }
>  
> +/* Work out the size of the first argument of a call to
> +   __builtin_speculation_safe_value.  Only pointers and integral types
> +   are permitted.  Return -1 if the argument type is not supported or
> +   the size is too large; 0 if the argument type is a pointer or the
> +   size if it is integral.  */
> +static int
> +speculation_safe_value_resolve_size (tree function, vec<tree, va_gc> *params)
> +{
> +  /* Type of the argument.  */
> +  tree type;
> +  int size;
> +
> +  if (vec_safe_is_empty (params))
> +    {
> +      error ("too few arguments to function %qE", function);
> +      return -1;
> +    }
> +
> +  type = TREE_TYPE ((*params)[0]);
> +  if (TREE_CODE (type) == ARRAY_TYPE && c_dialect_cxx ())
> +    {
> +      /* Force array-to-pointer decay for C++.   */
> +      (*params)[0] = default_conversion ((*params)[0]);
> +      type = TREE_TYPE ((*params)[0]);
> +    }
> +
> +  if (POINTER_TYPE_P (type))
> +    return 0;
> +
> +  if (!INTEGRAL_TYPE_P (type))
> +    goto incompatible;
> +
> +  if (!COMPLETE_TYPE_P (type))
> +    goto incompatible;
> +
> +  size = tree_to_uhwi (TYPE_SIZE_UNIT (type));
> +  if (size == 1 || size == 2 || size == 4 || size == 8 || size == 16)
> +    return size;
> +
> + incompatible:
> +  /* Issue the diagnostic only if the argument is valid, otherwise
> +     it would be redundant at best and could be misleading.  */
> +  if (type != error_mark_node)
> +    error ("operand type %qT is incompatible with argument %d of %qE",
> +	   type, 1, function);
> +
> +  return -1;
> +}
> +
> +/* Validate and coerce PARAMS, the arguments to ORIG_FUNCTION to fit
> +   the prototype for FUNCTION.  The first argument is mandatory, a second
> +   argument, if present, must be type compatible with the first.  */
> +static bool
> +speculation_safe_value_resolve_params (location_t loc, tree orig_function,
> +				       vec<tree, va_gc> *params)
> +{
> +  tree val;
> +
> +  if (params->length () == 0)
> +    {
> +      error_at (loc, "too few arguments to function %qE", orig_function);
> +      return false;
> +    }
> +
> +  else if (params->length () > 2)
> +    {
> +      error_at (loc, "too many arguments to function %qE", orig_function);
> +      return false;
> +    }
> +
> +  val = (*params)[0];
> +  if (TREE_CODE (TREE_TYPE (val)) == ARRAY_TYPE)
> +    val = default_conversion (val);
> +  if (!(TREE_CODE (TREE_TYPE (val)) == POINTER_TYPE
> +	|| TREE_CODE (TREE_TYPE (val)) == INTEGER_TYPE))
> +    {
> +      error_at (loc,
> +		"expecting argument of type pointer or of type integer "
> +		"for argument 1");
> +      return false;
> +    }
> +  (*params)[0] = val;
> +
> +  if (params->length () == 2)
> +    {
> +      tree val2 = (*params)[1];
> +      if (TREE_CODE (TREE_TYPE (val2)) == ARRAY_TYPE)
> +	val2 = default_conversion (val2);
> +      if (!(TREE_TYPE (val) == TREE_TYPE (val2)
> +	    || useless_type_conversion_p (TREE_TYPE (val), TREE_TYPE (val2))))
> +	{
> +	  error_at (loc, "both arguments must be compatible");
> +	  return false;
> +	}
> +      (*params)[1] = val2;
> +    }
> +
> +  return true;
> +}
> +
> +/* Cast the result of the builtin back to the type of the first argument,
> +   preserving any qualifiers that it might have.  */
> +static tree
> +speculation_safe_value_resolve_return (tree first_param, tree result)
> +{
> +  tree ptype = TREE_TYPE (first_param);
> +  tree rtype = TREE_TYPE (result);
> +  ptype = TYPE_MAIN_VARIANT (ptype);
> +
> +  if (tree_int_cst_equal (TYPE_SIZE (ptype), TYPE_SIZE (rtype)))
> +    return convert (ptype, result);
> +
> +  return result;
> +}
> +
>  /* A helper function for resolve_overloaded_builtin in resolving the
>     overloaded __sync_ builtins.  Returns a positive power of 2 if the
>     first operand of PARAMS is a pointer to a supported data type.
> @@ -7111,6 +7226,34 @@ resolve_overloaded_builtin (location_t loc, tree function,
>    /* Handle BUILT_IN_NORMAL here.  */
>    switch (orig_code)
>      {
> +    case BUILT_IN_SPECULATION_SAFE_VALUE_N:
> +      {
> +	int n = speculation_safe_value_resolve_size (function, params);
> +	tree new_function, first_param, result;
> +	enum built_in_function fncode;
> +
> +	if (n == -1)
> +	  return error_mark_node;
> +	else if (n == 0)
> +	  fncode = (enum built_in_function)((int)orig_code + 1);
> +	else
> +	  fncode
> +	    = (enum built_in_function)((int)orig_code + exact_log2 (n) + 2);
> +
> +	new_function = builtin_decl_explicit (fncode);
> +	first_param = (*params)[0];
> +	if (!speculation_safe_value_resolve_params (loc, function, params))
> +	  return error_mark_node;
> +
> +	result = build_function_call_vec (loc, vNULL, new_function, params,
> +					  NULL);
> +
> +	if (result == error_mark_node)
> +	  return result;
> +
> +	return speculation_safe_value_resolve_return (first_param, result);
> +      }
> +
>      case BUILT_IN_ATOMIC_EXCHANGE:
>      case BUILT_IN_ATOMIC_COMPARE_EXCHANGE:
>      case BUILT_IN_ATOMIC_LOAD:
> diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
> index bdb5691..0b10e65 100644
> --- a/gcc/c-family/c-cppbuiltin.c
> +++ b/gcc/c-family/c-cppbuiltin.c
> @@ -1361,7 +1361,10 @@ c_cpp_builtins (cpp_reader *pfile)
>      cpp_define (pfile, "__WCHAR_UNSIGNED__");
>  
>    cpp_atomic_builtins (pfile);
> -    
> +
> +  /* Show support for __builtin_speculation_safe_value ().  */
> +  cpp_define (pfile, "__HAVE_SPECULATION_SAFE_VALUE");
> +
>  #ifdef DWARF2_UNWIND_INFO
>    if (dwarf2out_do_cfi_asm ())
>      cpp_define (pfile, "__GCC_HAVE_DWARF2_CFI_ASM");
> diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
> index 3f7a8fc..efad2c8 100644
> --- a/gcc/doc/cpp.texi
> +++ b/gcc/doc/cpp.texi
> @@ -2381,6 +2381,10 @@ If GCC cannot determine the current date, it will emit a warning message
>  These macros are defined when the target processor supports atomic compare
>  and swap operations on operands 1, 2, 4, 8 or 16 bytes in length, respectively.
>  
> +@item __HAVE_SPECULATION_SAFE_VALUE
> +This macro is defined with the value 1 to show that this version of GCC
> +supports @code{__builtin_speculation_safe_value}.
> +
>  @item __GCC_HAVE_DWARF2_CFI_ASM
>  This macro is defined when the compiler is emitting DWARF CFI directives
>  to the assembler.  When this is defined, it is possible to emit those same
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index c7745c4..6eb0c6b 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -10935,6 +10935,7 @@ is called and the @var{flag} argument passed to it.
>  @findex __builtin_powi
>  @findex __builtin_powif
>  @findex __builtin_powil
> +@findex __builtin_speculation_safe_value
>  @findex _Exit
>  @findex _exit
>  @findex abort
> @@ -11579,6 +11580,34 @@ check its compatibility with @var{size}.
>  
>  @end deftypefn
>  
> +@deftypefn {Built-in Function} @var{type} __builtin_speculation_safe_value (@var{type} val, @var{type} failval)
> +
> +This builtin can be used to help mitigate against unsafe speculative
> +execution.  @var{type} may be any integral type or any pointer type.
> +
> +@enumerate
> +@item
> +If the CPU is not speculatively executing the code, then @var{val}
> +is returned.
> +@item
> +If the CPU is executing speculatively then either:
> +@itemize
> +@item
> +The function may cause execution to pause until it is known that the
> +code is no-longer being executed speculatively (in which case
> +@var{val} can be returned, as above); or
> +@item
> +The function may use target-dependent speculation tracking state to cause
> +@var{failval} to be returned when it is known that speculative
> +execution has incorrectly predicted a conditional branch operation.
> +@end itemize
> +@end enumerate
> +
> +The second argument, @var{failval}, is optional and defaults to zero
> +if omitted.
> +
> +@end deftypefn
> +
>  @deftypefn {Built-in Function} int __builtin_types_compatible_p (@var{type1}, @var{type2})
>  
>  You can use the built-in function @code{__builtin_types_compatible_p} to
> diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
> index 6d15d99..5de27f6 100644
> --- a/gcc/doc/md.texi
> +++ b/gcc/doc/md.texi
> @@ -7026,6 +7026,21 @@ should be defined to an instruction that orders both loads and stores
>  before the instruction with respect to loads and stores after the instruction.
>  This pattern has no operands.
>  
> +@cindex @code{speculation_barrier} instruction pattern
> +@item @samp{speculation_barrier}
> +If the target can support speculative execution, then this pattern should
> +be defined to an instruction that will block subsequent execution until
> +any prior speculation conditions has been resolved.  The pattern must also
> +ensure that the compiler cannot move memory operations past the barrier,
> +so it needs to be an UNSPEC_VOLATILE pattern.  The pattern has no
> +operands.
> +
> +If this pattern is not defined then the default expansion of
> +@code{__builtin_speculation_safe_value} will emit a warning.  You can
> +suppress this warning by defining this pattern with a final condition
> +of @code{0} (zero), which tells the compiler that a speculation
> +barrier is not needed for this target.
> +
>  @cindex @code{sync_compare_and_swap@var{mode}} instruction pattern
>  @item @samp{sync_compare_and_swap@var{mode}}
>  This pattern, if defined, emits code for an atomic compare-and-swap
> diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
> index 7e2cdc2..681e53b 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -11932,6 +11932,26 @@ maintainer is familiar with.
>  
>  @end defmac
>  
> +@deftypefn {Target Hook} rtx TARGET_SPECULATION_SAFE_VALUE (machine_mode @var{mode}, rtx @var{result}, rtx @var{val}, rtx @var{failval})
> +This target hook can be used to generate a target-specific code
> + sequence that implements the @code{__builtin_speculation_safe_value}
> + built-in function.  The function must always return @var{val} in
> + @var{result} in mode @var{mode} when the cpu is not executing
> + speculatively, but must never return that when speculating until it
> + is known that the speculation will not be unwound.  The hook supports
> + two primary mechanisms for implementing the requirements.  The first
> + is to emit a speculation barrier which forces the processor to wait
> + until all prior speculative operations have been resolved; the second
> + is to use a target-specific mechanism that can track the speculation
> + state and to return @var{failval} if it can determine that
> + speculation must be unwound at a later time.
> + 
> + The default implementation simply copies @var{val} to @var{result} and
> + emits a @code{speculation_barrier} instruction if that is defined.  If
> + @code{speculation_barrier} is not defined for the target a warning will
> + be generated.
> +@end deftypefn
> +
>  @deftypefn {Target Hook} void TARGET_RUN_TARGET_SELFTESTS (void)
>  If selftests are enabled, run any selftests for this target.
>  @end deftypefn
> diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
> index b7b0e8a..6e20afb 100644
> --- a/gcc/doc/tm.texi.in
> +++ b/gcc/doc/tm.texi.in
> @@ -8107,4 +8107,6 @@ maintainer is familiar with.
>  
>  @end defmac
>  
> +@hook TARGET_SPECULATION_SAFE_VALUE
> +
>  @hook TARGET_RUN_TARGET_SELFTESTS
> diff --git a/gcc/target.def b/gcc/target.def
> index 112c772..c8bd7f8 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -4177,6 +4177,29 @@ DEFHOOK
>   hook_bool_void_true)
>  
>  DEFHOOK
> +(speculation_safe_value,
> +"This target hook can be used to generate a target-specific code\n\
> + sequence that implements the @code{__builtin_speculation_safe_value}\n\
> + built-in function.  The function must always return @var{val} in\n\
> + @var{result} in mode @var{mode} when the cpu is not executing\n\
> + speculatively, but must never return that when speculating until it\n\
> + is known that the speculation will not be unwound.  The hook supports\n\
> + two primary mechanisms for implementing the requirements.  The first\n\
> + is to emit a speculation barrier which forces the processor to wait\n\
> + until all prior speculative operations have been resolved; the second\n\
> + is to use a target-specific mechanism that can track the speculation\n\
> + state and to return @var{failval} if it can determine that\n\
> + speculation must be unwound at a later time.\n\
> + \n\
> + The default implementation simply copies @var{val} to @var{result} and\n\
> + emits a @code{speculation_barrier} instruction if that is defined.  If\n\
> + @code{speculation_barrier} is not defined for the target a warning will\n\
> + be generated.",
> +rtx, (machine_mode mode, rtx result, rtx val, rtx failval),
> + default_speculation_safe_value)
> + 
> +
> +DEFHOOK
>  (can_use_doloop_p,
>   "Return true if it is possible to use low-overhead loops (@code{doloop_end}\n\
>  and @code{doloop_begin}) for a particular loop.  @var{iterations} gives the\n\
> diff --git a/gcc/targhooks.c b/gcc/targhooks.c
> index 7315f1a..2061f07 100644
> --- a/gcc/targhooks.c
> +++ b/gcc/targhooks.c
> @@ -2306,4 +2306,31 @@ default_select_early_remat_modes (sbitmap)
>  {
>  }
>  
> +/* Default implementation of the speculation-safe-load builtin.  This
> +   implementation simply copies val to result and generates a
> +   speculation_barrier insn, if such a pattern is defined.  If
> +   speculation_barrier is not defined at all, a warning is generated.  */
> +
> +rtx
> +default_speculation_safe_value (machine_mode mode ATTRIBUTE_UNUSED,
> +				rtx result, rtx val,
> +				rtx failval ATTRIBUTE_UNUSED)
> +{
> +  emit_move_insn (result, val);
> +#ifdef HAVE_speculation_barrier
> +  /* Assume the target knows what it is doing: if it defines a
> +     speculation barrier, but it is not enabled, then assume that one
> +     isn't needed.  */
> +  if (HAVE_speculation_barrier)
> +    emit_insn (gen_speculation_barrier ());
> +
> +#else
> +  warning_at (input_location, 0,
> +	      "this target does not define a speculation barrier; "
> +	      "your program will still execute correctly, but speculation "
> +	      "will not be inhibited");
> +#endif
> +  return result;
> +}
> +
>  #include "gt-targhooks.h"
> diff --git a/gcc/targhooks.h b/gcc/targhooks.h
> index 4107e22..80ac283 100644
> --- a/gcc/targhooks.h
> +++ b/gcc/targhooks.h
> @@ -284,4 +284,6 @@ default_excess_precision (enum excess_precision_type ATTRIBUTE_UNUSED);
>  extern bool default_stack_clash_protection_final_dynamic_probe (rtx);
>  extern void default_select_early_remat_modes (sbitmap);
>  
> +extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
> +
>  #endif /* GCC_TARGHOOKS_H */
> diff --git a/gcc/testsuite/gcc.dg/spec-barrier-1.c b/gcc/testsuite/gcc.dg/spec-barrier-1.c
> new file mode 100644
> index 0000000..106f89a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/spec-barrier-1.c
> @@ -0,0 +1,40 @@
> +/* { dg-do run } */
> +/* { dg-options "-O" } */
> +
> +/* Test that __builtin_speculation_safe_value returns the correct value.  */
> +/* This test will cause an unfiltered warning to be emitted on targets
> +   that have not implemented support for speculative execution
> +   barriers.  They should fix that rather than disabling this
> +   test.  */
> +char a = 1;
> +short b = 2;
> +int c = 3;
> +long d = 4;
> +long long e = 5;
> +int *f = (int*) &c;
> +#ifdef __SIZEOF_INT128__
> +__int128 g = 9;
> +#endif
> +
> +extern void abort (void);
> +
> +int main ()
> +{
> +  if (__builtin_speculation_safe_value (a) != 1)
> +    abort ();
> +  if (__builtin_speculation_safe_value (b) != 2)
> +    abort ();
> +  if (__builtin_speculation_safe_value (c) != 3)
> +    abort ();
> +  if (__builtin_speculation_safe_value (d) != 4)
> +    abort ();
> +  if (__builtin_speculation_safe_value (e) != 5)
> +    abort ();
> +  if (__builtin_speculation_safe_value (f) != &c)
> +    abort ();
> +#ifdef __SIZEOF_INT128__
> +  if (__builtin_speculation_safe_value (g) != 9)
> +    abort ();
> +#endif
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/spec-barrier-2.c b/gcc/testsuite/gcc.dg/spec-barrier-2.c
> new file mode 100644
> index 0000000..7e9c497
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/spec-barrier-2.c
> @@ -0,0 +1,19 @@
> +/* { dg-do run } */
> +
> +/* Even on targets that don't need the optional failval parameter,
> +   side-effects on the operand should still be calculated.  */
> +
> +int x = 3;
> +volatile int y = 9;
> +
> +extern void abort (void);
> +
> +int main ()
> +{
> +  int z = __builtin_speculation_safe_value (x, y++);
> +  if (z != 3 || y != 10)
> +    abort ();
> +  return 0;
> +}
> +
> +/* { dg-prune-output "this target does not define a speculation barrier;" } */
> diff --git a/gcc/testsuite/gcc.dg/spec-barrier-3.c b/gcc/testsuite/gcc.dg/spec-barrier-3.c
> new file mode 100644
> index 0000000..3ed4d39
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/spec-barrier-3.c
> @@ -0,0 +1,13 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Wpedantic" } */
> +
> +/* __builtin_speculation_safe_value returns a value with the same type
> +   as its first argument.  There should be a warning if that isn't
> +   type-compatible with the use.  */
> +int *
> +f (int x)
> +{
> +  return __builtin_speculation_safe_value (x);  /* { dg-warning "returning 'int' from a function with return type 'int \\*' makes pointer from integer without a cast" } */
> +}
> +
> +/* { dg-prune-output "this target does not define a speculation barrier;" } */
> 



More information about the Gcc-patches mailing list