[Clang] Add __has_target_builtin macro by sarnex · Pull Request #126324 · llvm/llvm-project (original) (raw)
@llvm/pr-subscribers-clang
Author: Nick Sarnie (sarnex)
Changes
As a follow-up to #121839, where we wanted to make __has_builtin
return false for aux builtins, but that broke existing code.
Instead, introduce a new macro __has_target_builtin
(name open to suggestions) that only considers builtins for the current target.
Full diff: https://github.com/llvm/llvm-project/pull/126324.diff
4 Files Affected:
- (modified) clang/docs/LanguageExtensions.rst (+33)
- (modified) clang/include/clang/Lex/Preprocessor.h (+1)
- (modified) clang/lib/Lex/PPMacroExpansion.cpp (+58-52)
- (added) clang/test/Preprocessor/has_target_builtin.cpp (+18)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 973cf8f9d091c30..057ad564f970bb4 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -67,6 +67,10 @@ It can be used like this:
__has_builtin
should not be used to detect support for a builtin macro;
use #ifdef
instead.
+ When using device offloading, a builtin is considered available if it is
+ available on either the host or the device targets.
+ Use __has_target_builtin
to consider only the current target.
+
__has_constexpr_builtin
@@ -96,6 +100,35 @@ the <cmath>
header file to conditionally make a function constexpr whenever
the constant evaluation of the corresponding builtin (for example,
std::fmax
calls __builtin_fmax
) is supported in Clang.
+__has_target_builtin
+------------------------
+
+This function-like macro takes a single identifier argument that is the name of
+a builtin function, a builtin pseudo-function (taking one or more type
+arguments), or a builtin template.
+It evaluates to 1 if the builtin is supported on the current target or 0 if not.
+The behavior is different than __has_builtin
when there is an auxiliary target,
+such when offloading to a target device.
+It can be used like this:
+
+.. code-block:: c++
+
#ifndef __has_target_builtin // Optional of course.
#define __has_target_builtin(x) 0 // Compatibility with non-clang compilers.
#endif
...
#if __has_target_builtin(__builtin_trap)
__builtin_trap();
#else
abort();
#endif
...
+.. note::
__has_target_builtin
should not be used to detect support for a builtin macro;use
#ifdef
instead... _langext-__has_feature-__has_extension:
__has_feature
and__has_extension
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 2bf4d1a16699430..240fe28aba93e33 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -174,6 +174,7 @@ class Preprocessor { IdentifierInfo *Ident__has_extension; // __has_extension IdentifierInfo *Ident__has_builtin; // __has_builtin IdentifierInfo *Ident__has_constexpr_builtin; // __has_constexpr_builtin
- IdentifierInfo *Ident__has_target_builtin; // __has_target_builtin IdentifierInfo *Ident__has_attribute; // __has_attribute IdentifierInfo *Ident__has_embed; // __has_embed IdentifierInfo *Ident__has_include; // __has_include diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 347c13da0ad215a..23a693b105fca3a 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -357,6 +357,7 @@ void Preprocessor::RegisterBuiltinMacros() { Ident__has_builtin = RegisterBuiltinMacro("__has_builtin"); Ident__has_constexpr_builtin = RegisterBuiltinMacro("__has_constexpr_builtin");
- Ident__has_target_builtin = RegisterBuiltinMacro("__has_target_builtin"); Ident__has_attribute = RegisterBuiltinMacro("__has_attribute"); if (!getLangOpts().CPlusPlus) Ident__has_c_attribute = RegisterBuiltinMacro("__has_c_attribute"); @@ -1797,55 +1798,62 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { diag::err_feature_check_malformed); return II && HasExtension(*this, II->getName()); });
- } else if (II == Ident__has_builtin) {
- EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
[this](Token &Tok, bool &HasLexedNextToken) -> int {
IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
diag::err_feature_check_malformed);
if (!II)
return false;
else if (II->getBuiltinID() != 0) {
switch (II->getBuiltinID()) {
case Builtin::BI__builtin_cpu_is:
return getTargetInfo().supportsCpuIs();
case Builtin::BI__builtin_cpu_init:
return getTargetInfo().supportsCpuInit();
case Builtin::BI__builtin_cpu_supports:
return getTargetInfo().supportsCpuSupports();
case Builtin::BI__builtin_operator_new:
case Builtin::BI__builtin_operator_delete:
// denotes date of behavior change to support calling arbitrary
// usual allocation and deallocation functions. Required by libc++
return 201802;
default:
return Builtin::evaluateRequiredTargetFeatures(
getBuiltinInfo().getRequiredFeatures(II->getBuiltinID()),
getTargetInfo().getTargetOpts().FeatureMap);
- } else if (II == Ident__has_builtin || II == Ident__has_target_builtin) {
- bool IsHasTargetBuiltin = II == Ident__has_target_builtin;
- EvaluateFeatureLikeBuiltinMacro(
OS, Tok, II, *this, false,
[this, IsHasTargetBuiltin](Token &Tok, bool &HasLexedNextToken) -> int {
IdentifierInfo *II = ExpectFeatureIdentifierInfo(
Tok, *this, diag::err_feature_check_malformed);
if (!II)
return false;
auto BuiltinID = II->getBuiltinID();
if (BuiltinID != 0) {
switch (BuiltinID) {
case Builtin::BI__builtin_cpu_is:
return getTargetInfo().supportsCpuIs();
case Builtin::BI__builtin_cpu_init:
return getTargetInfo().supportsCpuInit();
case Builtin::BI__builtin_cpu_supports:
return getTargetInfo().supportsCpuSupports();
case Builtin::BI__builtin_operator_new:
case Builtin::BI__builtin_operator_delete:
// denotes date of behavior change to support calling arbitrary
// usual allocation and deallocation functions. Required by libc++
return 201802;
default:
// __has_target_builtin should return false for aux builtins.
if (IsHasTargetBuiltin &&
getBuiltinInfo().isAuxBuiltinID(BuiltinID))
return false;
return Builtin::evaluateRequiredTargetFeatures(
getBuiltinInfo().getRequiredFeatures(BuiltinID),
getTargetInfo().getTargetOpts().FeatureMap);
}
return true;
} else if (IsBuiltinTrait(Tok)) {
return true;
} else if (II->getTokenID() != tok::identifier &&
II->getName().starts_with("__builtin_")) {
return true;
} else {
return llvm::StringSwitch<bool>(II->getName())
// Report builtin templates as being builtins.
.Case("__make_integer_seq", getLangOpts().CPlusPlus)
.Case("__type_pack_element", getLangOpts().CPlusPlus)
.Case("__builtin_common_type", getLangOpts().CPlusPlus)
// Likewise for some builtin preprocessor macros.
// FIXME: This is inconsistent; we usually suggest detecting
// builtin macros via #ifdef. Don't add more cases here.
.Case("__is_target_arch", true)
.Case("__is_target_vendor", true)
.Case("__is_target_os", true)
.Case("__is_target_environment", true)
.Case("__is_target_variant_os", true)
.Case("__is_target_variant_environment", true)
.Default(false); }
return true;
} else if (IsBuiltinTrait(Tok)) {
return true;
} else if (II->getTokenID() != tok::identifier &&
II->getName().starts_with("__builtin_")) {
return true;
} else {
return llvm::StringSwitch<bool>(II->getName())
// Report builtin templates as being builtins.
.Case("__make_integer_seq", getLangOpts().CPlusPlus)
.Case("__type_pack_element", getLangOpts().CPlusPlus)
.Case("__builtin_common_type", getLangOpts().CPlusPlus)
// Likewise for some builtin preprocessor macros.
// FIXME: This is inconsistent; we usually suggest detecting
// builtin macros via #ifdef. Don't add more cases here.
.Case("__is_target_arch", true)
.Case("__is_target_vendor", true)
.Case("__is_target_os", true)
.Case("__is_target_environment", true)
.Case("__is_target_variant_os", true)
.Case("__is_target_variant_environment", true)
.Default(false);
}
});
} else if (II == Ident__has_constexpr_builtin) { EvaluateFeatureLikeBuiltinMacro( OS, Tok, II, *this, false,});
@@ -1886,8 +1894,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
return false;
});
- } else if (II == Ident__has_cpp_attribute ||
II == Ident__has_c_attribute) {
- } else if (II == Ident__has_cpp_attribute || II == Ident__has_c_attribute) { bool IsCXX = II == Ident__has_cpp_attribute; EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, true, [&](Token &Tok, bool &HasLexedNextToken) -> int {
@@ -1917,8 +1924,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { getLangOpts()) : 0; });
- } else if (II == Ident__has_include ||
II == Ident__has_include_next) {
- } else if (II == Ident__has_include || II == Ident__has_include_next) {
// The argument to these two builtins should be a parenthesized
// file name string literal using angle brackets (<>) or
// double-quotes ("").
diff --git a/clang/test/Preprocessor/has_target_builtin.cpp b/clang/test/Preprocessor/has_target_builtin.cpp
new file mode 100644
index 000000000000000..64b2d7e1b35d9ef
--- /dev/null
+++ b/clang/test/Preprocessor/has_target_builtin.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -fopenmp -triple=spirv64 -fopenmp-is-target-device
+// RUN: -aux-triple x86_64-linux-unknown -E %s | FileCheck -implicit-check-not=BAD %s - +// RUN: %clang_cc1 -fopenmp -triple=nvptx64 -fopenmp-is-target-device
+// RUN: -aux-triple x86_64-linux-unknown -E %s | FileCheck -implicit-check-not=BAD %s - +// RUN: %clang_cc1 -fopenmp -triple=amdgcn-amd-amdhsa -fopenmp-is-target-device
+// RUN: -aux-triple x86_64-linux-unknown -E %s | FileCheck -implicit-check-not=BAD %s - +// RUN: %clang_cc1 -fopenmp -triple=aarch64 -fopenmp-is-target-device
+// RUN: -aux-triple x86_64-linux-unknown -E %s | FileCheck -implicit-check-not=BAD %s - +// CHECK: GOOD +#if __has_target_builtin(__builtin_ia32_pause)
- BAD +#else
- GOOD +#endif