Add __builtin_speculation_safe_value

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.
	* builtin-attrs.def (ATTR_NOVOPS_NOTHROW_LEAF_LIST): New attribute
	list.
	* 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 and
	TARGET_HAVE_SPECULATION_SAFE_VALUE.
	* doc/tm.texi: Regenerated.
	* target.def (have_speculation_safe_value, speculation_safe_value): New
	hooks.
	* targhooks.c (default_have_speculation_safe_value): New function.
	(default_speculation_safe_value): New function.
	* targhooks.h (default_have_speculation_safe_value): Add prototype.
	(default_speculation_safe_value): Add prototype.

c-family:
	* c-common.c (speculation_safe_resolve_call): 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:
	* c-c++-common/spec-barrier-1.c: New test.
	* c-c++-common/spec-barrier-2.c: New test.
	* gcc.dg/spec-barrier-3.c: New test.

From-SVN: r263168
This commit is contained in:
Richard Earnshaw 2018-07-31 17:35:32 +00:00 committed by Richard Earnshaw
parent 1d8693a0ce
commit 425fc685dd
20 changed files with 587 additions and 1 deletions

View File

@ -1,3 +1,32 @@
2018-07-31 Richard Earnshaw <rearnsha@arm.com>
* 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.
* builtin-attrs.def (ATTR_NOVOPS_NOTHROW_LEAF_LIST): New attribute
list.
* 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 and
TARGET_HAVE_SPECULATION_SAFE_VALUE.
* doc/tm.texi: Regenerated.
* target.def (have_speculation_safe_value, speculation_safe_value): New
hooks.
* targhooks.c (default_have_speculation_safe_value): New function.
(default_speculation_safe_value): New function.
* targhooks.h (default_have_speculation_safe_value): Add prototype.
(default_speculation_safe_value): Add prototype.
2018-07-31 David Malcolm <dmalcolm@redhat.com>
* dump-context.h (dump_context::dump_loc): New decl.

View File

@ -129,6 +129,8 @@ DEF_ATTR_TREE_LIST (ATTR_NOTHROW_LIST, ATTR_NOTHROW, ATTR_NULL, ATTR_NULL)
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_LEAF_LIST, ATTR_LEAF, ATTR_NULL, ATTR_NOTHROW_LIST)
DEF_ATTR_TREE_LIST (ATTR_NOVOPS_NOTHROW_LEAF_LIST, ATTR_NOVOPS, \
ATTR_NULL, ATTR_NOTHROW_LEAF_LIST)
DEF_ATTR_TREE_LIST (ATTR_CONST_NOTHROW_LIST, ATTR_CONST, \
ATTR_NULL, ATTR_NOTHROW_LIST)
DEF_ATTR_TREE_LIST (ATTR_CONST_NOTHROW_LEAF_LIST, ATTR_CONST, \

View File

@ -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)

View File

@ -6918,6 +6918,55 @@ inline_expand_builtin_string_cmp (tree exp, rtx target)
const_str_n, mode);
}
/* 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 || GET_MODE (target) != mode)
target = gen_reg_rtx (mode);
if (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode)
val = convert_modes (mode, VOIDmode, val, false);
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).
@ -8029,6 +8078,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;
}

View File

@ -1003,6 +1003,28 @@ 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_NOVOPS_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_PTR,
"speculation_safe_value_ptr", BT_FN_PTR_PTR_VAR,
ATTR_NOVOPS_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_1, "speculation_safe_value_1",
BT_FN_I1_I1_VAR, ATTR_NOVOPS_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_2, "speculation_safe_value_2",
BT_FN_I2_I2_VAR, ATTR_NOVOPS_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_4, "speculation_safe_value_4",
BT_FN_I4_I4_VAR, ATTR_NOVOPS_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_8, "speculation_safe_value_8",
BT_FN_I8_I8_VAR, ATTR_NOVOPS_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_16,
"speculation_safe_value_16", BT_FN_I16_I16_VAR,
ATTR_NOVOPS_NOTHROW_LEAF_LIST)
/* Exception support. */
DEF_BUILTIN_STUB (BUILT_IN_UNWIND_RESUME, "__builtin_unwind_resume")
DEF_BUILTIN_STUB (BUILT_IN_CXA_END_CLEANUP, "__builtin_cxa_end_cleanup")

View File

@ -1,3 +1,12 @@
2018-07-31 Richard Earnshaw <rearnsha@arm.com>
* c-common.c (speculation_safe_resolve_call): 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.
2018-07-20 David Malcolm <dmalcolm@redhat.com>
* c-common.c (c_cpp_error): Remove redundant "line_table"

View File

@ -6457,6 +6457,122 @@ 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 enum built_in_function
speculation_safe_value_resolve_call (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 BUILT_IN_NONE;
}
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 BUILT_IN_SPECULATION_SAFE_VALUE_PTR;
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 ((enum built_in_function)
((int) BUILT_IN_SPECULATION_SAFE_VALUE_1 + exact_log2 (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 BUILT_IN_NONE;
}
/* 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 +7227,54 @@ resolve_overloaded_builtin (location_t loc, tree function,
/* Handle BUILT_IN_NORMAL here. */
switch (orig_code)
{
case BUILT_IN_SPECULATION_SAFE_VALUE_N:
{
tree new_function, first_param, result;
enum built_in_function fncode
= speculation_safe_value_resolve_call (function, params);;
first_param = (*params)[0];
if (fncode == BUILT_IN_NONE
|| !speculation_safe_value_resolve_params (loc, function, params))
return error_mark_node;
if (targetm.have_speculation_safe_value (true))
{
new_function = builtin_decl_explicit (fncode);
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);
}
else
{
/* This target doesn't have, or doesn't need, active mitigation
against incorrect speculative execution. Simply return the
first parameter to the builtin. */
if (!targetm.have_speculation_safe_value (false))
/* The user has invoked __builtin_speculation_safe_value
even though __HAVE_SPECULATION_SAFE_VALUE is not
defined: emit a warning. */
warning_at (input_location, 0,
"this target does not define a speculation barrier; "
"your program will still execute correctly, "
"but incorrect speculation may not be be "
"restricted");
/* If the optional second argument is present, handle any side
effects now. */
if (params->length () == 2
&& TREE_SIDE_EFFECTS ((*params)[1]))
return build2 (COMPOUND_EXPR, TREE_TYPE (first_param),
(*params)[1], first_param);
return first_param;
}
}
case BUILT_IN_ATOMIC_EXCHANGE:
case BUILT_IN_ATOMIC_COMPARE_EXCHANGE:
case BUILT_IN_ATOMIC_LOAD:

View File

@ -1361,7 +1361,12 @@ c_cpp_builtins (cpp_reader *pfile)
cpp_define (pfile, "__WCHAR_UNSIGNED__");
cpp_atomic_builtins (pfile);
/* Show support for __builtin_speculation_safe_value () if the target
has been updated to fully support it. */
if (targetm.have_speculation_safe_value (false))
cpp_define (pfile, "__HAVE_SPECULATION_SAFE_VALUE");
#ifdef DWARF2_UNWIND_INFO
if (dwarf2out_do_cfi_asm ())
cpp_define (pfile, "__GCC_HAVE_DWARF2_CFI_ASM");

View File

@ -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

View File

@ -10960,6 +10960,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
@ -11604,6 +11605,96 @@ 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 built-in function 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.
GCC defines the preprocessor macro
@code{__HAVE_BUILTIN_SPECULATION_SAFE_VALUE} for targets that have been
updated to support this builtin.
The built-in function can be used where a variable appears to be used in a
safe way, but the CPU, due to speculative execution may temporarily ignore
the bounds checks. Consider, for example, the following function:
@smallexample
int array[500];
int f (unsigned untrusted_index)
@{
if (untrusted_index < 500)
return array[untrusted_index];
return 0;
@}
@end smallexample
If the function is called repeatedly with @code{untrusted_index} less
than the limit of 500, then a branch predictor will learn that the
block of code that returns a value stored in @code{array} will be
executed. If the function is subsequently called with an
out-of-range value it will still try to execute that block of code
first until the CPU determines that the prediction was incorrect
(the CPU will unwind any incorrect operations at that point).
However, depending on how the result of the function is used, it might be
possible to leave traces in the cache that can reveal what was stored
at the out-of-bounds location. The built-in function can be used to
provide some protection against leaking data in this way by changing
the code to:
@smallexample
int array[500];
int f (unsigned untrusted_index)
@{
if (untrusted_index < 500)
return array[__builtin_speculation_safe_value (untrusted_index)];
return 0;
@}
@end smallexample
The built-in function will either cause execution to stall until the
conditional branch has been fully resolved, or it may permit
speculative execution to continue, but using 0 instead of
@code{untrusted_value} if that exceeds the limit.
If accessing any memory location is potentially unsafe when speculative
execution is incorrect, then the code can be rewritten as
@smallexample
int array[500];
int f (unsigned untrusted_index)
@{
if (untrusted_index < 500)
return *__builtin_speculation_safe_value (&array[untrusted_index], NULL);
return 0;
@}
@end smallexample
which will cause a @code{NULL} pointer to be used for the unsafe case.
@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

View File

@ -7032,6 +7032,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

View File

@ -11924,6 +11924,37 @@ maintainer is familiar with.
@end defmac
@deftypefn {Target Hook} bool TARGET_HAVE_SPECULATION_SAFE_VALUE (bool @var{active})
This hook is used to determine the level of target support for
@code{__builtin_speculation_safe_value}. If called with an argument
of false, it returns true if the target has been modified to support
this builtin. If called with an argument of true, it returns true
if the target requires active mitigation execution might be speculative.
The default implementation returns false if the target does not define
a pattern named @code{speculation_barrier}. Else it returns true
for the first case and whether the pattern is enabled for the current
compilation for the second case.
@end deftypefn
@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.
@end deftypefn
@deftypefn {Target Hook} void TARGET_RUN_TARGET_SELFTESTS (void)
If selftests are enabled, run any selftests for this target.
@end deftypefn

View File

@ -8101,4 +8101,8 @@ maintainer is familiar with.
@end defmac
@hook TARGET_HAVE_SPECULATION_SAFE_VALUE
@hook TARGET_SPECULATION_SAFE_VALUE
@hook TARGET_RUN_TARGET_SELFTESTS

View File

@ -4155,6 +4155,41 @@ DEFHOOK
bool, (void),
hook_bool_void_true)
DEFHOOK
(have_speculation_safe_value,
"This hook is used to determine the level of target support for\n\
@code{__builtin_speculation_safe_value}. If called with an argument\n\
of false, it returns true if the target has been modified to support\n\
this builtin. If called with an argument of true, it returns true\n\
if the target requires active mitigation execution might be speculative.\n\
\n\
The default implementation returns false if the target does not define\n\
a pattern named @code{speculation_barrier}. Else it returns true\n\
for the first case and whether the pattern is enabled for the current\n\
compilation for the second case.",
bool, (bool active), default_have_speculation_safe_value)
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.",
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\

View File

@ -2314,4 +2314,36 @@ default_preferred_else_value (unsigned, tree type, unsigned, tree *)
return build_zero_cst (type);
}
/* Default implementation of TARGET_HAVE_SPECULATION_SAFE_VALUE. */
bool
default_have_speculation_safe_value (bool active)
{
#ifdef HAVE_speculation_barrier
return active ? HAVE_speculation_barrier : true;
#else
return false;
#endif
}
/* 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. */
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 ());
#endif
return result;
}
#include "gt-targhooks.h"

View File

@ -281,4 +281,7 @@ extern bool default_stack_clash_protection_final_dynamic_probe (rtx);
extern void default_select_early_remat_modes (sbitmap);
extern tree default_preferred_else_value (unsigned, tree, unsigned, tree *);
extern bool default_have_speculation_safe_value (bool);
extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx);
#endif /* GCC_TARGHOOKS_H */

View File

@ -1,3 +1,9 @@
2018-07-31 Richard Earnshaw <rearnsha@arm.com>
* c-c++-common/spec-barrier-1.c: New test.
* c-c++-common/spec-barrier-2.c: New test.
* gcc.dg/spec-barrier-3.c: New test.
2018-07-31 Martin Sebor <msebor@redhat.com>
PR tree-optimization/86741

View File

@ -0,0 +1,38 @@
/* { 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
int main ()
{
if (__builtin_speculation_safe_value (a) != 1)
__builtin_abort ();
if (__builtin_speculation_safe_value (b) != 2)
__builtin_abort ();
if (__builtin_speculation_safe_value (c) != 3)
__builtin_abort ();
if (__builtin_speculation_safe_value (d) != 4)
__builtin_abort ();
if (__builtin_speculation_safe_value (e) != 5)
__builtin_abort ();
if (__builtin_speculation_safe_value (f) != &c)
__builtin_abort ();
#ifdef __SIZEOF_INT128__
if (__builtin_speculation_safe_value (g) != 9)
__builtin_abort ();
#endif
return 0;
}

View File

@ -0,0 +1,17 @@
/* { 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;
int main ()
{
int z = __builtin_speculation_safe_value (x, y++);
if (z != 3 || y != 10)
__builtin_abort ();
return 0;
}
/* { dg-prune-output "this target does not define a speculation barrier;" } */

View File

@ -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;" } */