diff options
author | Matthew Malcomson <mmalcomson@nvidia.com> | 2024-10-07 16:42:41 +0100 |
---|---|---|
committer | Matthew Malcomson <mmalcomson@nvidia.com> | 2024-12-09 14:05:50 +0000 |
commit | 9ed094a817ecaf43c79505286759b88eb0555be2 (patch) | |
tree | 3dd49e1f4edef2fb100ceafd64afd729918702c7 /gcc/c-family | |
parent | 548afd73cdbf310403a1e3f34226372c16c29706 (diff) | |
download | gcc-9ed094a817ecaf43c79505286759b88eb0555be2.zip gcc-9ed094a817ecaf43c79505286759b88eb0555be2.tar.gz gcc-9ed094a817ecaf43c79505286759b88eb0555be2.tar.bz2 |
c++: Allow overloaded builtins to be used in SFINAE context
This commit newly introduces the ability to use overloaded builtins in
C++ SFINAE context.
The goal behind this is in order to ensure there is a single mechanism
that libstdc++ can use to determine whether a given type can be used in
the atomic fetch_add (and similar) builtins. I am working on another
patch that hopes to use this mechanism to identify whether fetch_add
(and similar) work on floating point types.
Current state of the world:
GCC currently exposes resolved versions of these builtins to the
user, so for GCC it's currently possible to use tests similar to the
below to check for atomic loads on a 2 byte sized object.
#if __has_builtin(__atomic_load_2)
Clang does not expose resolved versions of the atomic builtins.
clang currently allows SFINAE on builtins, so that C++ code can
check whether a builtin is available on a given type.
GCC does not (and that is what this patch aims to change).
C libraries like libatomic can check whether a given atomic builtin
can work on a given type by using autoconf to check for a
miscompilation when attempting such a use.
My goal:
I would like to enable floating point fetch_add (and similar) in
GCC, in order to use those overloads in libstdc++ implementation of
atomic<float>::fetch_add.
This should allow compilers targeting GPU's which have floating
point fetch_add instructions to emit optimal code.
In order to do that I need some consistent mechanism that libstdc++
can use to identify whether the fetch_add builtins have floating
point overloads (and for which types these exist).
I would hence like to enable SFINAE on builtins, so that libstdc++
can use that mechanism for the floating point fetch_add builtins.
Implementation follows the existing mechanism for handling SFINAE
contexts in c-common.cc. A boolean is passed into the c-common.cc
function indicating whether these functions should emit errors or not.
This boolean comes from `complain & tf_error` in the C++ frontend.
(Similar to other functions like valid_array_size_p and
c_build_vec_perm_expr).
This is done both for resolve_overloaded_builtin and
check_builtin_function_arguments, both of which can be used in SFINAE
contexts.
I attempted to trigger something using the `reject_gcc_builtin`
function in an SFINAE context. Given the context where this
function is called from the C++ frontend it looks like it may be
possible, but I did not manage to trigger this in template context
by attempting to do something similar to the testcases added around
those calls.
- I would appreciate any feedback on whether this is something that
can happen in a template context, and if so some help writing a
relevant testcase for it.
Both of these functions have target hooks for target specific builtins
that I have updated to take the extra boolean flag. I have not adjusted
the functions implementing those target hooks (except to update the
declarations) so target specific builtins will still error in SFINAE
contexts.
- I could imagine not updating the target hook definition since nothing
would use that change. However I figure that allowing targets to
decide this behaviour would be the right thing to do eventually, and
since this is the target-independent part of the change to do that
this patch should make that change.
Could adjust if others disagree.
Other relevant points that I'd appreciate reviewers check:
- I did not pass this new flag through
atomic_bitint_fetch_using_cas_loop since the _BitInt type is not
available in the C++ frontend and I didn't want if conditions that can
not be executed in the source.
- I only test non-compile-time-constant types with SVE types, since I do
not know of a way to get a VLA into a SFINAE context.
- While writing tests I noticed a few differences with clang in this
area. I don't think they are problematic but am mentioning them for
completeness and to allow others to judge if these are a problem).
- atomic_fetch_add on a boolean is allowed by clang.
- When __atomic_load is passed an invalid memory model (i.e. too
large), we give an SFINAE failure while clang does not.
Bootstrap and regression tested on AArch64 and x86_64.
Built first stage on targets whose target hook declaration needed
updated (though did not regtest etc). Targets triplets I built in order
to check the backend specific changes I made:
- arm-none-linux-gnueabihf
- avr-linux-gnu
- riscv-linux-gnu
- powerpc-linux-gnu
- s390x-linux-gnu
Ok for commit to trunk?
gcc/c-family/ChangeLog:
* c-common.cc (builtin_function_validate_nargs,
check_builtin_function_arguments,
speculation_safe_value_resolve_call,
speculation_safe_value_resolve_params, sync_resolve_size,
sync_resolve_params, get_atomic_generic_size,
resolve_overloaded_atomic_exchange,
resolve_overloaded_atomic_compare_exchange,
resolve_overloaded_atomic_load, resolve_overloaded_atomic_store,
resolve_overloaded_builtin): Add `complain` boolean parameter
and determine whether to emit errors based on its value.
* c-common.h (check_builtin_function_arguments,
resolve_overloaded_builtin): Mention `complain` boolean
parameter in declarations. Give it a default of `true`.
gcc/ChangeLog:
* config/aarch64/aarch64-c.cc
(aarch64_resolve_overloaded_builtin,aarch64_check_builtin_call):
Add new unused boolean parameter to match target hook
definition.
* config/arm/arm-builtins.cc (arm_check_builtin_call): Likewise.
* config/arm/arm-c.cc (arm_resolve_overloaded_builtin):
Likewise.
* config/arm/arm-protos.h (arm_check_builtin_call): Likewise.
* config/avr/avr-c.cc (avr_resolve_overloaded_builtin):
Likewise.
* config/riscv/riscv-c.cc (riscv_check_builtin_call,
riscv_resolve_overloaded_builtin): Likewise.
* config/rs6000/rs6000-c.cc (altivec_resolve_overloaded_builtin):
Likewise.
* config/rs6000/rs6000-protos.h (altivec_resolve_overloaded_builtin):
Likewise.
* config/s390/s390-c.cc (s390_resolve_overloaded_builtin):
Likewise.
* doc/tm.texi: Regenerate.
* target.def (TARGET_RESOLVE_OVERLOADED_BUILTIN,
TARGET_CHECK_BUILTIN_CALL): Update prototype to include a
boolean parameter that indicates whether errors should be
emitted. Update documentation to mention this fact.
gcc/cp/ChangeLog:
* call.cc (build_cxx_call): Pass `complain` parameter to
check_builtin_function_arguments. Take its value from the
`tsubst_flags_t` type `complain & tf_error`.
* semantics.cc (finish_call_expr): Pass `complain` parameter to
resolve_overloaded_builtin. Take its value from the
`tsubst_flags_t` type `complain & tf_error`.
gcc/testsuite/ChangeLog:
* g++.dg/template/builtin-atomic-overloads.def: New test.
* g++.dg/template/builtin-atomic-overloads1.C: New test.
* g++.dg/template/builtin-atomic-overloads2.C: New test.
* g++.dg/template/builtin-atomic-overloads3.C: New test.
* g++.dg/template/builtin-atomic-overloads4.C: New test.
* g++.dg/template/builtin-atomic-overloads5.C: New test.
* g++.dg/template/builtin-atomic-overloads6.C: New test.
* g++.dg/template/builtin-atomic-overloads7.C: New test.
* g++.dg/template/builtin-atomic-overloads8.C: New test.
* g++.dg/template/builtin-sfinae-check-function-arguments.C: New test.
* g++.dg/template/builtin-speculation-overloads.def: New test.
* g++.dg/template/builtin-speculation-overloads1.C: New test.
* g++.dg/template/builtin-speculation-overloads2.C: New test.
* g++.dg/template/builtin-speculation-overloads3.C: New test.
* g++.dg/template/builtin-speculation-overloads4.C: New test.
* g++.dg/template/builtin-speculation-overloads5.C: New test.
* g++.dg/template/builtin-validate-nargs.C: New test.
Signed-off-by: Matthew Malcomson <mmalcomson@nvidia.com>
Diffstat (limited to 'gcc/c-family')
-rw-r--r-- | gcc/c-family/c-common.cc | 442 | ||||
-rw-r--r-- | gcc/c-family/c-common.h | 7 |
2 files changed, 290 insertions, 159 deletions
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index ccf5f41..c3d9ce2 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -6401,16 +6401,18 @@ check_function_arguments_recurse (void (*callback) static bool builtin_function_validate_nargs (location_t loc, tree fndecl, int nargs, - int required) + int required, bool complain) { if (nargs < required) { - error_at (loc, "too few arguments to function %qE", fndecl); + if (complain) + error_at (loc, "too few arguments to function %qE", fndecl); return false; } else if (nargs > required) { - error_at (loc, "too many arguments to function %qE", fndecl); + if (complain) + error_at (loc, "too many arguments to function %qE", fndecl); return false; } return true; @@ -6431,16 +6433,16 @@ builtin_function_validate_nargs (location_t loc, tree fndecl, int nargs, bool check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, - tree fndecl, tree orig_fndecl, - int nargs, tree *args) + tree fndecl, tree orig_fndecl, int nargs, + tree *args, bool complain) { if (!fndecl_built_in_p (fndecl)) return true; if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) return (!targetm.check_builtin_call - || targetm.check_builtin_call (loc, arg_loc, fndecl, - orig_fndecl, nargs, args)); + || targetm.check_builtin_call (loc, arg_loc, fndecl, orig_fndecl, + nargs, args, complain)); if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_FRONTEND) return true; @@ -6451,9 +6453,11 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX: if (!tree_fits_uhwi_p (args[2])) { - error_at (ARG_LOCATION (2), - "third argument to function %qE must be a constant integer", - fndecl); + if (complain) + error_at ( + ARG_LOCATION (2), + "third argument to function %qE must be a constant integer", + fndecl); return false; } /* fall through */ @@ -6476,17 +6480,18 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, /* Reject invalid alignments. */ if (align < BITS_PER_UNIT || maxalign < align) { - error_at (ARG_LOCATION (1), - "second argument to function %qE must be a constant " - "integer power of 2 between %qi and %qu bits", - fndecl, BITS_PER_UNIT, maxalign); + if (complain) + error_at (ARG_LOCATION (1), + "second argument to function %qE must be a constant " + "integer power of 2 between %qi and %qu bits", + fndecl, BITS_PER_UNIT, maxalign); return false; } return true; } case BUILT_IN_CONSTANT_P: - return builtin_function_validate_nargs (loc, fndecl, nargs, 1); + return builtin_function_validate_nargs (loc, fndecl, nargs, 1, complain); case BUILT_IN_ISFINITE: case BUILT_IN_ISINF: @@ -6495,12 +6500,15 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, case BUILT_IN_ISNORMAL: case BUILT_IN_ISSIGNALING: case BUILT_IN_SIGNBIT: - if (builtin_function_validate_nargs (loc, fndecl, nargs, 1)) + if (builtin_function_validate_nargs (loc, fndecl, nargs, 1, complain)) { if (TREE_CODE (TREE_TYPE (args[0])) != REAL_TYPE) { - error_at (ARG_LOCATION (0), "non-floating-point argument in " - "call to function %qE", fndecl); + if (complain) + error_at (ARG_LOCATION (0), + "non-floating-point argument in " + "call to function %qE", + fndecl); return false; } return true; @@ -6514,7 +6522,7 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, case BUILT_IN_ISLESSGREATER: case BUILT_IN_ISUNORDERED: case BUILT_IN_ISEQSIG: - if (builtin_function_validate_nargs (loc, fndecl, nargs, 2)) + if (builtin_function_validate_nargs (loc, fndecl, nargs, 2, complain)) { enum tree_code code0, code1; code0 = TREE_CODE (TREE_TYPE (args[0])); @@ -6525,8 +6533,11 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, || ((code0 == INTEGER_TYPE || code0 == BITINT_TYPE) && code1 == REAL_TYPE))) { - error_at (loc, "non-floating-point arguments in call to " - "function %qE", fndecl); + if (complain) + error_at (loc, + "non-floating-point arguments in call to " + "function %qE", + fndecl); return false; } return true; @@ -6534,20 +6545,26 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, return false; case BUILT_IN_FPCLASSIFY: - if (builtin_function_validate_nargs (loc, fndecl, nargs, 6)) + if (builtin_function_validate_nargs (loc, fndecl, nargs, 6, complain)) { for (unsigned int i = 0; i < 5; i++) if (TREE_CODE (args[i]) != INTEGER_CST) { - error_at (ARG_LOCATION (i), "non-const integer argument %u in " - "call to function %qE", i + 1, fndecl); + if (complain) + error_at (ARG_LOCATION (i), + "non-const integer argument %u in " + "call to function %qE", + i + 1, fndecl); return false; } if (TREE_CODE (TREE_TYPE (args[5])) != REAL_TYPE) { - error_at (ARG_LOCATION (5), "non-floating-point argument in " - "call to function %qE", fndecl); + if (complain) + error_at (ARG_LOCATION (5), + "non-floating-point argument in " + "call to function %qE", + fndecl); return false; } return true; @@ -6555,14 +6572,18 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, return false; case BUILT_IN_ASSUME_ALIGNED: - if (builtin_function_validate_nargs (loc, fndecl, nargs, 2 + (nargs > 2))) + if (builtin_function_validate_nargs (loc, fndecl, nargs, 2 + (nargs > 2), + complain)) { if (nargs >= 3 && TREE_CODE (TREE_TYPE (args[2])) != INTEGER_TYPE && TREE_CODE (TREE_TYPE (args[2])) != BITINT_TYPE) { - error_at (ARG_LOCATION (2), "non-integer argument 3 in call to " - "function %qE", fndecl); + if (complain) + error_at (ARG_LOCATION (2), + "non-integer argument 3 in call to " + "function %qE", + fndecl); return false; } return true; @@ -6572,47 +6593,63 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, case BUILT_IN_ADD_OVERFLOW: case BUILT_IN_SUB_OVERFLOW: case BUILT_IN_MUL_OVERFLOW: - if (builtin_function_validate_nargs (loc, fndecl, nargs, 3)) + if (builtin_function_validate_nargs (loc, fndecl, nargs, 3, complain)) { unsigned i; for (i = 0; i < 2; i++) if (!INTEGRAL_TYPE_P (TREE_TYPE (args[i]))) { - error_at (ARG_LOCATION (i), "argument %u in call to function " - "%qE does not have integral type", i + 1, fndecl); + if (complain) + error_at (ARG_LOCATION (i), + "argument %u in call to function " + "%qE does not have integral type", + i + 1, fndecl); return false; } if (TREE_CODE (TREE_TYPE (args[2])) != POINTER_TYPE || !INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (args[2])))) { - error_at (ARG_LOCATION (2), "argument 3 in call to function %qE " - "does not have pointer to integral type", fndecl); + if (complain) + error_at (ARG_LOCATION (2), + "argument 3 in call to function %qE " + "does not have pointer to integral type", + fndecl); return false; } else if (TREE_CODE (TREE_TYPE (TREE_TYPE (args[2]))) == ENUMERAL_TYPE) { - error_at (ARG_LOCATION (2), "argument 3 in call to function %qE " - "has pointer to enumerated type", fndecl); + if (complain) + error_at (ARG_LOCATION (2), + "argument 3 in call to function %qE " + "has pointer to enumerated type", + fndecl); return false; } else if (TREE_CODE (TREE_TYPE (TREE_TYPE (args[2]))) == BOOLEAN_TYPE) { - error_at (ARG_LOCATION (2), "argument 3 in call to function %qE " - "has pointer to boolean type", fndecl); + if (complain) + error_at (ARG_LOCATION (2), + "argument 3 in call to function %qE " + "has pointer to boolean type", + fndecl); return false; } else if (TYPE_READONLY (TREE_TYPE (TREE_TYPE (args[2])))) { - error_at (ARG_LOCATION (2), "argument %u in call to function %qE " - "has pointer to %qs type (%qT)", 3, fndecl, "const", - TREE_TYPE (args[2])); + if (complain) + error_at (ARG_LOCATION (2), + "argument %u in call to function %qE " + "has pointer to %qs type (%qT)", + 3, fndecl, "const", TREE_TYPE (args[2])); return false; } else if (TYPE_ATOMIC (TREE_TYPE (TREE_TYPE (args[2])))) { - error_at (ARG_LOCATION (2), "argument %u in call to function %qE " - "has pointer to %qs type (%qT)", 3, fndecl, - "_Atomic", TREE_TYPE (args[2])); + if (complain) + error_at (ARG_LOCATION (2), + "argument %u in call to function %qE " + "has pointer to %qs type (%qT)", + 3, fndecl, "_Atomic", TREE_TYPE (args[2])); return false; } return true; @@ -6622,26 +6659,35 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, case BUILT_IN_ADD_OVERFLOW_P: case BUILT_IN_SUB_OVERFLOW_P: case BUILT_IN_MUL_OVERFLOW_P: - if (builtin_function_validate_nargs (loc, fndecl, nargs, 3)) + if (builtin_function_validate_nargs (loc, fndecl, nargs, 3, complain)) { unsigned i; for (i = 0; i < 3; i++) if (!INTEGRAL_TYPE_P (TREE_TYPE (args[i]))) { - error_at (ARG_LOCATION (i), "argument %u in call to function " - "%qE does not have integral type", i + 1, fndecl); + if (complain) + error_at (ARG_LOCATION (i), + "argument %u in call to function " + "%qE does not have integral type", + i + 1, fndecl); return false; } if (TREE_CODE (TREE_TYPE (args[2])) == ENUMERAL_TYPE) { - error_at (ARG_LOCATION (2), "argument %u in call to function " - "%qE has enumerated type", 3, fndecl); + if (complain) + error_at (ARG_LOCATION (2), + "argument %u in call to function " + "%qE has enumerated type", + 3, fndecl); return false; } else if (TREE_CODE (TREE_TYPE (args[2])) == BOOLEAN_TYPE) { - error_at (ARG_LOCATION (2), "argument %u in call to function " - "%qE has boolean type", 3, fndecl); + if (complain) + error_at (ARG_LOCATION (2), + "argument %u in call to function " + "%qE has boolean type", + 3, fndecl); return false; } return true; @@ -6649,32 +6695,42 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, return false; case BUILT_IN_CLEAR_PADDING: - if (builtin_function_validate_nargs (loc, fndecl, nargs, 1)) + if (builtin_function_validate_nargs (loc, fndecl, nargs, 1, complain)) { if (!POINTER_TYPE_P (TREE_TYPE (args[0]))) { - error_at (ARG_LOCATION (0), "argument %u in call to function " - "%qE does not have pointer type", 1, fndecl); + if (complain) + error_at (ARG_LOCATION (0), + "argument %u in call to function " + "%qE does not have pointer type", + 1, fndecl); return false; } else if (!COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (args[0])))) { - error_at (ARG_LOCATION (0), "argument %u in call to function " - "%qE points to incomplete type", 1, fndecl); + if (complain) + error_at (ARG_LOCATION (0), + "argument %u in call to function " + "%qE points to incomplete type", + 1, fndecl); return false; } else if (TYPE_READONLY (TREE_TYPE (TREE_TYPE (args[0])))) { - error_at (ARG_LOCATION (0), "argument %u in call to function %qE " - "has pointer to %qs type (%qT)", 1, fndecl, "const", - TREE_TYPE (args[0])); + if (complain) + error_at (ARG_LOCATION (0), + "argument %u in call to function %qE " + "has pointer to %qs type (%qT)", + 1, fndecl, "const", TREE_TYPE (args[0])); return false; } else if (TYPE_ATOMIC (TREE_TYPE (TREE_TYPE (args[0])))) { - error_at (ARG_LOCATION (0), "argument %u in call to function %qE " - "has pointer to %qs type (%qT)", 1, fndecl, - "_Atomic", TREE_TYPE (args[0])); + if (complain) + error_at (ARG_LOCATION (0), + "argument %u in call to function %qE " + "has pointer to %qs type (%qT)", + 1, fndecl, "_Atomic", TREE_TYPE (args[0])); return false; } return true; @@ -6693,8 +6749,11 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, { if (!INTEGRAL_TYPE_P (TREE_TYPE (args[1]))) { - error_at (ARG_LOCATION (1), "argument %u in call to function " - "%qE does not have integral type", 2, fndecl); + if (complain) + error_at (ARG_LOCATION (1), + "argument %u in call to function " + "%qE does not have integral type", + 2, fndecl); return false; } if ((TYPE_PRECISION (TREE_TYPE (args[1])) @@ -6703,30 +6762,43 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, == TYPE_PRECISION (integer_type_node) && TYPE_UNSIGNED (TREE_TYPE (args[1])))) { - error_at (ARG_LOCATION (1), "argument %u in call to function " - "%qE does not have %<int%> type", 2, fndecl); + if (complain) + error_at (ARG_LOCATION (1), + "argument %u in call to function " + "%qE does not have %<int%> type", + 2, fndecl); return false; } } - else if (!builtin_function_validate_nargs (loc, fndecl, nargs, 1)) + else if (!builtin_function_validate_nargs (loc, fndecl, nargs, 1, + complain)) return false; if (!INTEGRAL_TYPE_P (TREE_TYPE (args[0]))) { - error_at (ARG_LOCATION (0), "argument %u in call to function " - "%qE does not have integral type", 1, fndecl); + if (complain) + error_at (ARG_LOCATION (0), + "argument %u in call to function " + "%qE does not have integral type", + 1, fndecl); return false; } if (TREE_CODE (TREE_TYPE (args[0])) == ENUMERAL_TYPE) { - error_at (ARG_LOCATION (0), "argument %u in call to function " - "%qE has enumerated type", 1, fndecl); + if (complain) + error_at (ARG_LOCATION (0), + "argument %u in call to function " + "%qE has enumerated type", + 1, fndecl); return false; } if (TREE_CODE (TREE_TYPE (args[0])) == BOOLEAN_TYPE) { - error_at (ARG_LOCATION (0), "argument %u in call to function " - "%qE has boolean type", 1, fndecl); + if (complain) + error_at (ARG_LOCATION (0), + "argument %u in call to function " + "%qE has boolean type", + 1, fndecl); return false; } if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FFSG @@ -6734,15 +6806,21 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc, { if (TYPE_UNSIGNED (TREE_TYPE (args[0]))) { - error_at (ARG_LOCATION (0), "argument 1 in call to function " - "%qE has unsigned type", fndecl); + if (complain) + error_at (ARG_LOCATION (0), + "argument 1 in call to function " + "%qE has unsigned type", + fndecl); return false; } } else if (!TYPE_UNSIGNED (TREE_TYPE (args[0]))) { - error_at (ARG_LOCATION (0), "argument 1 in call to function " - "%qE has signed type", fndecl); + if (complain) + error_at (ARG_LOCATION (0), + "argument 1 in call to function " + "%qE has signed type", + fndecl); return false; } return true; @@ -7336,7 +7414,8 @@ builtin_type_for_size (int size, bool unsignedp) 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) +speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params, + bool complain) { /* Type of the argument. */ tree type; @@ -7344,7 +7423,8 @@ speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params) if (vec_safe_is_empty (params)) { - error ("too few arguments to function %qE", function); + if (complain) + error ("too few arguments to function %qE", function); return BUILT_IN_NONE; } @@ -7373,7 +7453,7 @@ speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params) 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) + if (type != error_mark_node && complain) error ("operand type %qT is incompatible with argument %d of %qE", type, 1, function); @@ -7385,19 +7465,21 @@ speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params) 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) + vec<tree, va_gc> *params, bool complain) { tree val; if (params->length () == 0) { - error_at (loc, "too few arguments to function %qE", orig_function); + if (complain) + 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); + if (complain) + error_at (loc, "too many arguments to function %qE", orig_function); return false; } @@ -7407,9 +7489,9 @@ speculation_safe_value_resolve_params (location_t loc, tree orig_function, 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"); + if (complain) + error_at (loc, "expecting argument of type pointer or of type integer " + "for argument 1"); return false; } (*params)[0] = val; @@ -7424,7 +7506,8 @@ speculation_safe_value_resolve_params (location_t loc, tree orig_function, 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"); + if (complain) + error_at (loc, "both arguments must be compatible"); return false; } (*params)[1] = val2; @@ -7460,7 +7543,7 @@ speculation_safe_value_resolve_return (tree first_param, tree result) static int sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch, - bool orig_format) + bool orig_format, bool complain) { /* Type of the argument. */ tree argtype; @@ -7470,7 +7553,8 @@ sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch, if (vec_safe_is_empty (params)) { - error ("too few arguments to function %qE", function); + if (complain) + error ("too few arguments to function %qE", function); return 0; } @@ -7513,7 +7597,7 @@ sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch, incompatible: /* Issue the diagnostic only if the argument is valid, otherwise it would be redundant at best and could be misleading. */ - if (argtype != error_mark_node) + if (argtype != error_mark_node && complain) error ("operand type %qT is incompatible with argument %d of %qE", argtype, 1, function); return 0; @@ -7526,7 +7610,7 @@ sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch, static bool sync_resolve_params (location_t loc, tree orig_function, tree function, - vec<tree, va_gc> *params, bool orig_format) + vec<tree, va_gc> *params, bool orig_format, bool complain) { function_args_iterator iter; tree ptype; @@ -7555,7 +7639,8 @@ sync_resolve_params (location_t loc, tree orig_function, tree function, ++parmnum; if (params->length () <= parmnum) { - error_at (loc, "too few arguments to function %qE", orig_function); + if (complain) + error_at (loc, "too few arguments to function %qE", orig_function); return false; } @@ -7581,7 +7666,8 @@ sync_resolve_params (location_t loc, tree orig_function, tree function, /* __atomic routines are not variadic. */ if (!orig_format && params->length () != parmnum + 1) { - error_at (loc, "too many arguments to function %qE", orig_function); + if (complain) + error_at (loc, "too many arguments to function %qE", orig_function); return false; } @@ -7618,7 +7704,7 @@ sync_resolve_return (tree first_param, tree result, bool orig_format) static int get_atomic_generic_size (location_t loc, tree function, - vec<tree, va_gc> *params) + vec<tree, va_gc> *params, bool complain) { unsigned int n_param; unsigned int n_model; @@ -7656,7 +7742,9 @@ get_atomic_generic_size (location_t loc, tree function, if (vec_safe_length (params) != n_param) { - error_at (loc, "incorrect number of arguments to function %qE", function); + if (complain) + error_at (loc, "incorrect number of arguments to function %qE", + function); return 0; } @@ -7670,24 +7758,27 @@ get_atomic_generic_size (location_t loc, tree function, } if (TREE_CODE (type_0) != POINTER_TYPE || VOID_TYPE_P (TREE_TYPE (type_0))) { - error_at (loc, "argument 1 of %qE must be a non-void pointer type", - function); + if (complain) + error_at (loc, "argument 1 of %qE must be a non-void pointer type", + function); return 0; } if (!COMPLETE_TYPE_P (TREE_TYPE (type_0))) { - error_at (loc, "argument 1 of %qE must be a pointer to a complete type", - function); + if (complain) + error_at (loc, "argument 1 of %qE must be a pointer to a complete type", + function); return 0; } /* Types must be compile time constant sizes. */ if (!tree_fits_uhwi_p ((TYPE_SIZE_UNIT (TREE_TYPE (type_0))))) { - error_at (loc, - "argument 1 of %qE must be a pointer to a constant size type", - function); + if (complain) + error_at (loc, + "argument 1 of %qE must be a pointer to a constant size type", + function); return 0; } @@ -7696,9 +7787,10 @@ get_atomic_generic_size (location_t loc, tree function, /* Zero size objects are not allowed. */ if (size_0 == 0) { - error_at (loc, - "argument 1 of %qE must be a pointer to a nonzero size object", - function); + if (complain) + error_at ( + loc, "argument 1 of %qE must be a pointer to a nonzero size object", + function); return 0; } @@ -7718,30 +7810,38 @@ get_atomic_generic_size (location_t loc, tree function, } if (!POINTER_TYPE_P (type)) { - error_at (loc, "argument %d of %qE must be a pointer type", x + 1, - function); + if (complain) + error_at (loc, "argument %d of %qE must be a pointer type", x + 1, + function); return 0; } else if (TYPE_SIZE_UNIT (TREE_TYPE (type)) && TREE_CODE ((TYPE_SIZE_UNIT (TREE_TYPE (type)))) != INTEGER_CST) { - error_at (loc, "argument %d of %qE must be a pointer to a constant " - "size type", x + 1, function); + if (complain) + error_at (loc, + "argument %d of %qE must be a pointer to a constant " + "size type", + x + 1, function); return 0; } else if (FUNCTION_POINTER_TYPE_P (type)) { - error_at (loc, "argument %d of %qE must not be a pointer to a " - "function", x + 1, function); + if (complain) + error_at (loc, + "argument %d of %qE must not be a pointer to a " + "function", + x + 1, function); return 0; } tree type_size = TYPE_SIZE_UNIT (TREE_TYPE (type)); size = type_size ? tree_to_uhwi (type_size) : 0; if (size != size_0) { - error_at (loc, "size mismatch in argument %d of %qE", x + 1, - function); + if (complain) + error_at (loc, "size mismatch in argument %d of %qE", x + 1, + function); return 0; } @@ -7753,8 +7853,11 @@ get_atomic_generic_size (location_t loc, tree function, { if (c_dialect_cxx ()) { - error_at (loc, "argument %d of %qE must not be a pointer to " - "a %<const%> type", x + 1, function); + if (complain) + error_at (loc, + "argument %d of %qE must not be a pointer to " + "a %<const%> type", + x + 1, function); return 0; } else @@ -7767,8 +7870,11 @@ get_atomic_generic_size (location_t loc, tree function, { if (c_dialect_cxx ()) { - error_at (loc, "argument %d of %qE must not be a pointer to " - "a %<volatile%> type", x + 1, function); + if (complain) + error_at (loc, + "argument %d of %qE must not be a pointer to " + "a %<volatile%> type", + x + 1, function); return 0; } else @@ -7785,8 +7891,9 @@ get_atomic_generic_size (location_t loc, tree function, tree p = (*params)[x]; if (!INTEGRAL_TYPE_P (TREE_TYPE (p))) { - error_at (loc, "non-integer memory model argument %d of %qE", x + 1, - function); + if (complain) + error_at (loc, "non-integer memory model argument %d of %qE", x + 1, + function); return 0; } p = fold_for_warn (p); @@ -7797,9 +7904,14 @@ get_atomic_generic_size (location_t loc, tree function, bits will be checked later during expansion in target specific way. */ if (memmodel_base (TREE_INT_CST_LOW (p)) >= MEMMODEL_LAST) - warning_at (loc, OPT_Winvalid_memory_model, - "invalid memory model argument %d of %qE", x + 1, - function); + { + if (complain) + warning_at (loc, OPT_Winvalid_memory_model, + "invalid memory model argument %d of %qE", x + 1, + function); + else + return 0; + } } } @@ -7877,11 +7989,12 @@ atomic_size_supported_p (int n) NEW_RETURN is set to the return value the result is copied into. */ static bool resolve_overloaded_atomic_exchange (location_t loc, tree function, - vec<tree, va_gc> *params, tree *new_return) + vec<tree, va_gc> *params, tree *new_return, + bool complain) { tree p0, p1, p2, p3; tree I_type, I_type_ptr; - int n = get_atomic_generic_size (loc, function, params); + int n = get_atomic_generic_size (loc, function, params, complain); /* Size of 0 is an error condition. */ if (n == 0) @@ -7934,7 +8047,6 @@ resolve_overloaded_atomic_exchange (location_t loc, tree function, return false; } - /* This will process an __atomic_compare_exchange function call, determine whether it needs to be mapped to the _N variation, or turned into a lib call. LOC is the location of the builtin call. @@ -7947,11 +8059,11 @@ resolve_overloaded_atomic_exchange (location_t loc, tree function, static bool resolve_overloaded_atomic_compare_exchange (location_t loc, tree function, vec<tree, va_gc> *params, - tree *new_return) + tree *new_return, bool complain) { tree p0, p1, p2; tree I_type, I_type_ptr; - int n = get_atomic_generic_size (loc, function, params); + int n = get_atomic_generic_size (loc, function, params, complain); /* Size of 0 is an error condition. */ if (n == 0) @@ -8015,7 +8127,6 @@ resolve_overloaded_atomic_compare_exchange (location_t loc, tree function, return false; } - /* This will process an __atomic_load function call, determine whether it needs to be mapped to the _N variation, or turned into a library call. LOC is the location of the builtin call. @@ -8028,11 +8139,12 @@ resolve_overloaded_atomic_compare_exchange (location_t loc, tree function, static bool resolve_overloaded_atomic_load (location_t loc, tree function, - vec<tree, va_gc> *params, tree *new_return) + vec<tree, va_gc> *params, tree *new_return, + bool complain) { tree p0, p1, p2; tree I_type, I_type_ptr; - int n = get_atomic_generic_size (loc, function, params); + int n = get_atomic_generic_size (loc, function, params, complain); /* Size of 0 is an error condition. */ if (n == 0) @@ -8075,7 +8187,6 @@ resolve_overloaded_atomic_load (location_t loc, tree function, return false; } - /* This will process an __atomic_store function call, determine whether it needs to be mapped to the _N variation, or turned into a library call. LOC is the location of the builtin call. @@ -8088,11 +8199,12 @@ resolve_overloaded_atomic_load (location_t loc, tree function, static bool resolve_overloaded_atomic_store (location_t loc, tree function, - vec<tree, va_gc> *params, tree *new_return) + vec<tree, va_gc> *params, tree *new_return, + bool complain) { tree p0, p1; tree I_type, I_type_ptr; - int n = get_atomic_generic_size (loc, function, params); + int n = get_atomic_generic_size (loc, function, params, complain); /* Size of 0 is an error condition. */ if (n == 0) @@ -8135,7 +8247,6 @@ resolve_overloaded_atomic_store (location_t loc, tree function, return false; } - /* Emit __atomic*fetch* on _BitInt which doesn't have a size of 1, 2, 4, 8 or 16 bytes using __atomic_compare_exchange loop. ORIG_CODE is the DECL_FUNCTION_CODE of ORIG_FUNCTION and @@ -8372,7 +8483,7 @@ atomic_bitint_fetch_using_cas_loop (location_t loc, tree resolve_overloaded_builtin (location_t loc, tree function, - vec<tree, va_gc> *params) + vec<tree, va_gc> *params, bool complain) { /* Is function one of the _FETCH_OP_ or _OP_FETCH_ built-ins? Those are not valid to call with a pointer to _Bool (or C++ bool) @@ -8387,7 +8498,8 @@ resolve_overloaded_builtin (location_t loc, tree function, break; case BUILT_IN_MD: if (targetm.resolve_overloaded_builtin) - return targetm.resolve_overloaded_builtin (loc, function, params); + return targetm.resolve_overloaded_builtin (loc, function, params, + complain); else return NULL_TREE; default: @@ -8402,13 +8514,14 @@ resolve_overloaded_builtin (location_t loc, tree function, { tree new_function, first_param, result; enum built_in_function fncode - = speculation_safe_value_resolve_call (function, params); + = speculation_safe_value_resolve_call (function, params, complain); if (fncode == BUILT_IN_NONE) return error_mark_node; first_param = (*params)[0]; - if (!speculation_safe_value_resolve_params (loc, function, params)) + if (!speculation_safe_value_resolve_params (loc, function, params, + complain)) return error_mark_node; if (targetm.have_speculation_safe_value (true)) @@ -8428,14 +8541,20 @@ resolve_overloaded_builtin (location_t loc, tree function, 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 " - "restricted"); + { + if (complain) + /* 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 " + "restricted"); + else + return error_mark_node; + } /* If the optional second argument is present, handle any side effects now. */ @@ -8460,7 +8579,7 @@ resolve_overloaded_builtin (location_t loc, tree function, case BUILT_IN_ATOMIC_EXCHANGE: { if (resolve_overloaded_atomic_exchange (loc, function, params, - &new_return)) + &new_return, complain)) return new_return; /* Change to the _N variant. */ orig_code = BUILT_IN_ATOMIC_EXCHANGE_N; @@ -8469,9 +8588,8 @@ resolve_overloaded_builtin (location_t loc, tree function, case BUILT_IN_ATOMIC_COMPARE_EXCHANGE: { - if (resolve_overloaded_atomic_compare_exchange (loc, function, - params, - &new_return)) + if (resolve_overloaded_atomic_compare_exchange ( + loc, function, params, &new_return, complain)) return new_return; /* Change to the _N variant. */ orig_code = BUILT_IN_ATOMIC_COMPARE_EXCHANGE_N; @@ -8480,7 +8598,7 @@ resolve_overloaded_builtin (location_t loc, tree function, case BUILT_IN_ATOMIC_LOAD: { if (resolve_overloaded_atomic_load (loc, function, params, - &new_return)) + &new_return, complain)) return new_return; /* Change to the _N variant. */ orig_code = BUILT_IN_ATOMIC_LOAD_N; @@ -8489,7 +8607,7 @@ resolve_overloaded_builtin (location_t loc, tree function, case BUILT_IN_ATOMIC_STORE: { if (resolve_overloaded_atomic_store (loc, function, params, - &new_return)) + &new_return, complain)) return new_return; /* Change to the _N variant. */ orig_code = BUILT_IN_ATOMIC_STORE_N; @@ -8545,7 +8663,8 @@ resolve_overloaded_builtin (location_t loc, tree function, && orig_code != BUILT_IN_SYNC_LOCK_TEST_AND_SET_N && orig_code != BUILT_IN_SYNC_LOCK_RELEASE_N); - int n = sync_resolve_size (function, params, fetch_op, orig_format); + int n = sync_resolve_size (function, params, fetch_op, orig_format, + complain); tree new_function, first_param, result; enum built_in_function fncode; @@ -8553,13 +8672,24 @@ resolve_overloaded_builtin (location_t loc, tree function, return error_mark_node; if (n == -1) - return atomic_bitint_fetch_using_cas_loop (loc, orig_code, - function, params); + { + /* complain is related to SFINAE context. + _BitInt is not defined in C++, hence can't enter this clause + with complain unset. Even if at the abstraction level + this complain is unset that still makes sense (whether + this function should report an error or not if anything is + wrong). + Since can't test avoiding an error when this value is false not + writing the code and instead asserting value is not set. */ + gcc_assert (complain); + return atomic_bitint_fetch_using_cas_loop (loc, orig_code, function, + params); + } fncode = (enum built_in_function)((int)orig_code + exact_log2 (n) + 1); new_function = builtin_decl_explicit (fncode); if (!sync_resolve_params (loc, function, new_function, params, - orig_format)) + orig_format, complain)) return error_mark_node; first_param = (*params)[0]; diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index e2195aa..ac574e9 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -859,8 +859,8 @@ extern void check_function_arguments_recurse (void (*) void *, tree, unsigned HOST_WIDE_INT, opt_code); -extern bool check_builtin_function_arguments (location_t, vec<location_t>, - tree, tree, int, tree *); +extern bool check_builtin_function_arguments (location_t, vec<location_t>, tree, + tree, int, tree *, bool = true); extern void check_function_format (const_tree, tree, int, tree *, vec<location_t> *, bool (*comp_types) (tree, tree)); @@ -1105,7 +1105,8 @@ extern tree build_function_call_vec (location_t, vec<location_t>, tree, vec<tree, va_gc> *, vec<tree, va_gc> *, tree = NULL_TREE); -extern tree resolve_overloaded_builtin (location_t, tree, vec<tree, va_gc> *); +extern tree resolve_overloaded_builtin (location_t, tree, vec<tree, va_gc> *, + bool = true); extern tree finish_label_address_expr (tree, location_t); |