diff options
author | rearnsha <rearnsha@138bc75d-0d04-0410-961f-82ee72b054a4> | 2018-07-31 17:35:32 +0000 |
---|---|---|
committer | Dongkyun Son <dongkyun.s@samsung.com> | 2019-02-06 15:46:42 +0000 |
commit | 839e47f64150c6130cfdbf6bd43cfc04df3ac286 (patch) | |
tree | 49d89fe2961ac71ef617d250fd512c60a2a939b5 | |
parent | 81c87ae477be33caa487ee363c0d58a494fcd487 (diff) | |
download | linaro-gcc-839e47f64150c6130cfdbf6bd43cfc04df3ac286.tar.gz linaro-gcc-839e47f64150c6130cfdbf6bd43cfc04df3ac286.tar.bz2 linaro-gcc-839e47f64150c6130cfdbf6bd43cfc04df3ac286.zip |
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.
(backported 123081efd41261af8830d6c796a9b12435806bac)
Change-Id: I1d0e777c8e098f5a1d30aa5fa3090c8ce6b3dbee
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@263168 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r-- | gcc/ChangeLog | 29 | ||||
-rw-r--r-- | gcc/builtin-attrs.def | 2 | ||||
-rw-r--r-- | gcc/builtin-types.def | 6 | ||||
-rw-r--r-- | gcc/builtins.c | 60 | ||||
-rw-r--r-- | gcc/builtins.def | 22 | ||||
-rw-r--r-- | gcc/c-family/ChangeLog | 9 | ||||
-rw-r--r-- | gcc/c-family/c-common.c | 164 | ||||
-rw-r--r-- | gcc/c-family/c-cppbuiltin.c | 7 | ||||
-rw-r--r-- | gcc/doc/cpp.texi | 4 | ||||
-rw-r--r-- | gcc/doc/extend.texi | 91 | ||||
-rw-r--r-- | gcc/doc/md.texi | 15 | ||||
-rw-r--r-- | gcc/doc/tm.texi | 31 | ||||
-rw-r--r-- | gcc/doc/tm.texi.in | 4 | ||||
-rw-r--r-- | gcc/target.def | 35 | ||||
-rw-r--r-- | gcc/targhooks.c | 32 | ||||
-rw-r--r-- | gcc/targhooks.h | 3 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 6 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/spec-barrier-1.c | 38 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/spec-barrier-2.c | 17 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/spec-barrier-3.c | 13 |
20 files changed, 587 insertions, 1 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 76eb2267375..9c1bbc574ca 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -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. + 2017-06-02 Kyrylo Tkachov <kyrylo.tkachov@arm.com> * config/aarch64/aarch64.c (aarch64_split_compare_and_swap): diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def index 089817a5c4d..80edf763fef 100644 --- a/gcc/builtin-attrs.def +++ b/gcc/builtin-attrs.def @@ -119,6 +119,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, \ diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def index 7fab9f83158..69c455a2a1f 100644 --- a/gcc/builtin-types.def +++ b/gcc/builtin-types.def @@ -588,6 +588,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 d32a1d1bd8f..cb15cb22a57 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -5608,6 +5608,55 @@ expand_stack_save (void) } +/* 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). @@ -6702,6 +6751,17 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, folding. */ break; + 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 10cbdbe05db..157ee06f1ec 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -916,6 +916,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") diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index bdd4e9e273c..2b19e8fa6d8 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -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. + 2017-01-10 Martin Liska <mliska@suse.cz> Backport from mainline diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index b2b36b56406..1a7c3753d3e 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -10787,6 +10787,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. @@ -11419,6 +11535,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: diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index 3d4587e6db6..a560fea8129 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/gcc/c-family/c-cppbuiltin.c @@ -1202,7 +1202,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"); diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi index 9f914b2759d..40e7be721df 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 0c91d85ba8c..7bb173f3d14 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -10228,6 +10228,7 @@ in the Cilk Plus language manual which can be found at @findex __builtin_powi @findex __builtin_powif @findex __builtin_powil +@findex __builtin_speculation_safe_value @findex _Exit @findex _exit @findex abort @@ -10854,6 +10855,96 @@ an extension. @xref{Variable Length}, for details. @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 diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi index d12c7b1674e..45406c9e7f8 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -6645,6 +6645,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 745910f9a33..e8166b8d68d 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -11678,3 +11678,34 @@ All and all it does not take long to convert ports that the 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 diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index f31c763991c..a7f57905cba 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -8262,3 +8262,7 @@ All and all it does not take long to convert ports that the maintainer is familiar with. @end defmac + +@hook TARGET_HAVE_SPECULATION_SAFE_VALUE + +@hook TARGET_SPECULATION_SAFE_VALUE diff --git a/gcc/target.def b/gcc/target.def index 20f2b32da1e..a4f21ad3439 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -3942,6 +3942,41 @@ normally defined in @file{libgcc2.c}.", default_external_stack_protect_fail) 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\ and @code{doloop_begin}) for a particular loop. @var{iterations} gives the\n\ diff --git a/gcc/targhooks.c b/gcc/targhooks.c index 6b4601b719a..d9db06fd10b 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -1965,4 +1965,36 @@ default_optab_supported_p (int, machine_mode, machine_mode, optimization_type) return true; } +/* 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" diff --git a/gcc/targhooks.h b/gcc/targhooks.h index 7687c39b53b..01501b28a8a 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -254,4 +254,7 @@ extern void default_setup_incoming_vararg_bounds (cumulative_args_t ca ATTRIBUTE extern bool default_optab_supported_p (int, machine_mode, machine_mode, optimization_type); +extern bool default_have_speculation_safe_value (bool); +extern rtx default_speculation_safe_value (machine_mode, rtx, rtx, rtx); + #endif /* GCC_TARGHOOKS_H */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index f3714bbb82b..9d3e242069f 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -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. + 2017-06-02 Kyrylo Tkachov <kyrylo.tkachov@arm.com> * gcc.target/aarch64/atomic_cmp_exchange_zero_strong_1.c: New test. diff --git a/gcc/testsuite/c-c++-common/spec-barrier-1.c b/gcc/testsuite/c-c++-common/spec-barrier-1.c new file mode 100644 index 00000000000..e4b44f2af57 --- /dev/null +++ b/gcc/testsuite/c-c++-common/spec-barrier-1.c @@ -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; +} diff --git a/gcc/testsuite/c-c++-common/spec-barrier-2.c b/gcc/testsuite/c-c++-common/spec-barrier-2.c new file mode 100644 index 00000000000..b09567e62a9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/spec-barrier-2.c @@ -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;" } */ 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 00000000000..7376f3e37a9 --- /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 "return makes pointer from integer without a cast" } */ +} + +/* { dg-prune-output "this target does not define a speculation barrier;" } */ |