aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-family
diff options
context:
space:
mode:
authorMatthew Malcomson <mmalcomson@nvidia.com>2024-10-07 16:42:41 +0100
committerMatthew Malcomson <mmalcomson@nvidia.com>2024-12-09 14:05:50 +0000
commit9ed094a817ecaf43c79505286759b88eb0555be2 (patch)
tree3dd49e1f4edef2fb100ceafd64afd729918702c7 /gcc/c-family
parent548afd73cdbf310403a1e3f34226372c16c29706 (diff)
downloadgcc-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.cc442
-rw-r--r--gcc/c-family/c-common.h7
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);