diff options
author | Chris Lattner <sabre@nondot.org> | 2009-06-13 07:13:28 +0000 |
---|---|---|
committer | Chris Lattner <sabre@nondot.org> | 2009-06-13 07:13:28 +0000 |
commit | 148772a841cae6f32db16d890e788b92a763bb3f (patch) | |
tree | 5481ed924f1f63b790e7ead89fa047446b976746 | |
parent | 247baca66ca998de9c415c19019e199f4895e81c (diff) |
implement and document a new __has_feature and __has_builtin magic
builtin preprocessor macro. This appears to work with two caveats:
1) builtins are registered in -E mode, and 2) target-specific builtins
are unconditionally registered even if they aren't supported by the
target (e.g. SSE4 builtin when only SSE1 is enabled).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@73289 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | docs/LanguageExtensions.html | 77 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticLexKinds.td | 4 | ||||
-rw-r--r-- | include/clang/Lex/Preprocessor.h | 6 | ||||
-rw-r--r-- | lib/Lex/PPMacroExpansion.cpp | 95 | ||||
-rw-r--r-- | test/Preprocessor/feature_tests.c | 30 |
5 files changed, 194 insertions, 18 deletions
diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index c486562b10..55fb615834 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -19,6 +19,7 @@ td { <ul> <li><a href="#intro">Introduction</a></li> +<li><a href="#feature_check">Feature Checking Macros</a></li> <li><a href="#builtinmacros">Builtin Macros</a></li> <li><a href="#vectors">Vectors and Extended Vectors</a></li> <li><a href="#blocks">Blocks</a></li> @@ -45,12 +46,73 @@ td { <!-- ======================================================================= --> <p>This document describes the language extensions provided by Clang. In -addition to the langauge extensions listed here, Clang aims to support a broad +addition to the language extensions listed here, Clang aims to support a broad range of GCC extensions. Please see the <a href="http://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html">GCC manual</a> for more information on these extensions.</p> <!-- ======================================================================= --> +<h2 id="feature_check">Feature Checking Macros</h2> +<!-- ======================================================================= --> + +<p>Language extensions can be very useful, but only if you know you can depend +on them. In order to allow fine-grain features checks, we support two builtin +function-like macros. This allows you to directly test for a feature in your +code without having to resort to something like autoconf or fragile "compiler +version checks".</p> + +<!-- ======================================================================= --> +<h3 id="__has_builtin">__has_builtin</h3> +<!-- ======================================================================= --> + +<p>This function-like macro takes a single identifier argument that is the name +of a builtin function. It evaluates to 1 if the builtin is supported or 0 if +not. It can be used like this:</p> + +<blockquote> +<pre> +#ifndef __has_builtin // Optional of course. + #define __has_builtin(x) 0 // Compatibility with non-clang compilers. +#endif + +... +#if __has_builtin(__builtin_trap) + __builtin_trap(); +#else + abort(); +#endif +... +</pre> +</blockquote> + + +<!-- ======================================================================= --> +<h3 id="__has_feature">__has_feature</h3> +<!-- ======================================================================= --> + +<p>This function-like macro takes a single identifier argument that is the name +of a feature. It evaluates to 1 if the feature is supported or 0 if not. It +can be used like this:</p> + +<blockquote> +<pre> +#ifndef __has_feature // Optional of course. + #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +... +#if __has_feature(attribute_overloadable) || \ + __has_feature(blocks) +... +#endif +... +</pre> +</blockquote> + +<p>The feature tag is described along with the language feature below.</p> + + +<!-- ======================================================================= --> <h2 id="builtinmacros">Builtin Macros</h2> <!-- ======================================================================= --> @@ -64,6 +126,8 @@ more information on these extensions.</p> with V.xyzw syntax and other tidbits. See also <a href="#__builtin_shufflevector">__builtin_shufflevector</a>.</p> +<p>Query for this feature with __has_feature(attribute_ext_vector_type).</p> + <!-- ======================================================================= --> <h2 id="blocks">Blocks</h2> <!-- ======================================================================= --> @@ -73,6 +137,9 @@ href="BlockLanguageSpec.txt">BlockLanguageSpec.txt</a>. Implementation and ABI details for the clang implementation are in <a href="BlockImplementation.txt">BlockImplementation.txt</a>.</p> + +<p>Query for this feature with __has_feature(blocks).</p> + <!-- ======================================================================= --> <h2 id="overloading-in-c">Function Overloading in C</h2> <!-- ======================================================================= --> @@ -171,6 +238,9 @@ caveats to this use of name mangling:</p> C.</li> </ul> +<p>Query for this feature with __has_feature(attribute_overloadable).</p> + + <!-- ======================================================================= --> <h2 id="builtins">Builtin Functions</h2> <!-- ======================================================================= --> @@ -320,7 +390,10 @@ placed at the end of function prototypes:</p> <pre> void foo() <b>__attribute__((analyzer_noreturn))</b>; -</p> +</pre> + +<p>Query for this feature with __has_feature(attribute_analyzer_noreturn).</p> + </div> </body> diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 3d1f9320cd..6ca50db50a 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -205,6 +205,10 @@ def err_pp_expr_bad_token_start_expr : Error< "invalid token at start of a preprocessor expression">; def err_pp_invalid_poison : Error<"can only poison identifier tokens">; def err_pp_used_poisoned_id : Error<"attempt to use a poisoned identifier">; + +def err_feature_check_malformed : Error< + "builtin feature check macro requires a parenthesized identifier">; + def err__Pragma_malformed : Error< "_Pragma takes a parenthesized string literal">; def err_pragma_comment_malformed : Error< diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 5b9959c32a..4831b9bb25 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -69,6 +69,8 @@ class Preprocessor { IdentifierInfo *Ident__TIMESTAMP__; // __TIMESTAMP__ IdentifierInfo *Ident__COUNTER__; // __COUNTER__ IdentifierInfo *Ident_Pragma, *Ident__VA_ARGS__; // _Pragma, __VA_ARGS__ + IdentifierInfo *Ident__has_feature; // __has_feature + IdentifierInfo *Ident__has_builtin; // __has_builtin SourceLocation DATELoc, TIMELoc; unsigned CounterValue; // Next __COUNTER__ value. @@ -194,14 +196,13 @@ private: // Cached tokens state. public: Preprocessor(Diagnostic &diags, const LangOptions &opts, TargetInfo &target, SourceManager &SM, HeaderSearch &Headers, - IdentifierInfoLookup* IILookup = 0); + IdentifierInfoLookup *IILookup = 0); ~Preprocessor(); Diagnostic &getDiagnostics() const { return *Diags; } void setDiagnostics(Diagnostic &D) { Diags = &D; } - const LangOptions &getLangOptions() const { return Features; } TargetInfo &getTargetInfo() const { return Target; } FileManager &getFileManager() const { return FileMgr; } @@ -667,7 +668,6 @@ private: /// RegisterBuiltinMacros - Register builtin macros, such as __LINE__ with the /// identifier table. void RegisterBuiltinMacros(); - IdentifierInfo *RegisterBuiltinMacro(const char *Name); /// HandleMacroExpandedIdentifier - If an identifier token is read that is to /// be expanded as a macro, handle it and return the next token as 'Tok'. If diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 55222c944a..286705181c 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -36,14 +36,14 @@ void Preprocessor::setMacroInfo(IdentifierInfo *II, MacroInfo *MI) { /// RegisterBuiltinMacro - Register the specified identifier in the identifier /// table and mark it as a builtin macro to be expanded. -IdentifierInfo *Preprocessor::RegisterBuiltinMacro(const char *Name) { +static IdentifierInfo *RegisterBuiltinMacro(Preprocessor &PP, const char *Name){ // Get the identifier. - IdentifierInfo *Id = getIdentifierInfo(Name); + IdentifierInfo *Id = PP.getIdentifierInfo(Name); // Mark it as being a macro that is builtin. - MacroInfo *MI = AllocateMacroInfo(SourceLocation()); + MacroInfo *MI = PP.AllocateMacroInfo(SourceLocation()); MI->setIsBuiltinMacro(); - setMacroInfo(Id, MI); + PP.setMacroInfo(Id, MI); return Id; } @@ -51,17 +51,21 @@ IdentifierInfo *Preprocessor::RegisterBuiltinMacro(const char *Name) { /// RegisterBuiltinMacros - Register builtin macros, such as __LINE__ with the /// identifier table. void Preprocessor::RegisterBuiltinMacros() { - Ident__LINE__ = RegisterBuiltinMacro("__LINE__"); - Ident__FILE__ = RegisterBuiltinMacro("__FILE__"); - Ident__DATE__ = RegisterBuiltinMacro("__DATE__"); - Ident__TIME__ = RegisterBuiltinMacro("__TIME__"); - Ident__COUNTER__ = RegisterBuiltinMacro("__COUNTER__"); - Ident_Pragma = RegisterBuiltinMacro("_Pragma"); + Ident__LINE__ = RegisterBuiltinMacro(*this, "__LINE__"); + Ident__FILE__ = RegisterBuiltinMacro(*this, "__FILE__"); + Ident__DATE__ = RegisterBuiltinMacro(*this, "__DATE__"); + Ident__TIME__ = RegisterBuiltinMacro(*this, "__TIME__"); + Ident__COUNTER__ = RegisterBuiltinMacro(*this, "__COUNTER__"); + Ident_Pragma = RegisterBuiltinMacro(*this, "_Pragma"); // GCC Extensions. - Ident__BASE_FILE__ = RegisterBuiltinMacro("__BASE_FILE__"); - Ident__INCLUDE_LEVEL__ = RegisterBuiltinMacro("__INCLUDE_LEVEL__"); - Ident__TIMESTAMP__ = RegisterBuiltinMacro("__TIMESTAMP__"); + Ident__BASE_FILE__ = RegisterBuiltinMacro(*this, "__BASE_FILE__"); + Ident__INCLUDE_LEVEL__ = RegisterBuiltinMacro(*this, "__INCLUDE_LEVEL__"); + Ident__TIMESTAMP__ = RegisterBuiltinMacro(*this, "__TIMESTAMP__"); + + // Clang Extensions. + Ident__has_feature = RegisterBuiltinMacro(*this, "__has_feature"); + Ident__has_builtin = RegisterBuiltinMacro(*this, "__has_builtin"); } /// isTrivialSingleTokenExpansion - Return true if MI, which has a single token @@ -469,6 +473,34 @@ static void ComputeDATE_TIME(SourceLocation &DATELoc, SourceLocation &TIMELoc, TIMELoc = TmpTok.getLocation(); } + +/// HasFeature - Return true if we recognize and implement the specified feature +/// specified by the identifier. +static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { + const LangOptions &LangOpts = PP.getLangOptions(); + + switch (II->getLength()) { + default: return false; + case 6: + if (II->isStr("blocks")) return LangOpts.Blocks; + return false; + case 22: + if (II->isStr("attribute_overloadable")) return true; + return false; + case 25: + if (II->isStr("attribute_ext_vector_type")) return true; + return false; + case 27: + if (II->isStr("attribute_analyzer_noreturn")) return true; + return false; + case 29: + if (II->isStr("attribute_ns_returns_retained")) return true; + if (II->isStr("attribute_cf_returns_retained")) return true; + return false; + } +} + + /// ExpandBuiltinMacro - If an identifier token is read that is to be expanded /// as a builtin macro, handle it and return the next token as 'Tok'. void Preprocessor::ExpandBuiltinMacro(Token &Tok) { @@ -599,6 +631,43 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { sprintf(TmpBuffer, "%u", CounterValue++); Tok.setKind(tok::numeric_constant); CreateString(TmpBuffer, strlen(TmpBuffer), Tok, Tok.getLocation()); + } else if (II == Ident__has_feature || + II == Ident__has_builtin) { + // The argument to these two builtins should be a parenthesized identifier. + SourceLocation StartLoc = Tok.getLocation(); + + bool IsValid = false; + IdentifierInfo *FeatureII = 0; + + // Read the '('. + Lex(Tok); + if (Tok.is(tok::l_paren)) { + // Read the identifier + Lex(Tok); + if (Tok.is(tok::identifier)) { + FeatureII = Tok.getIdentifierInfo(); + + // Read the ')'. + Lex(Tok); + if (Tok.is(tok::r_paren)) + IsValid = true; + } + } + + bool Value = false; + if (!IsValid) + Diag(StartLoc, diag::err_feature_check_malformed); + else if (II == Ident__has_builtin) { + // Check for a builtin is trivial. + Value = FeatureII->getBuiltinID() != 0; + } else { + assert(II == Ident__has_feature && "Must be feature check"); + Value = HasFeature(*this, FeatureII); + } + + sprintf(TmpBuffer, "%d", (int)Value); + Tok.setKind(tok::numeric_constant); + CreateString(TmpBuffer, strlen(TmpBuffer), Tok, Tok.getLocation()); } else { assert(0 && "Unknown identifier!"); } diff --git a/test/Preprocessor/feature_tests.c b/test/Preprocessor/feature_tests.c new file mode 100644 index 0000000000..edca178192 --- /dev/null +++ b/test/Preprocessor/feature_tests.c @@ -0,0 +1,30 @@ +// RUN: clang-cc %s --triple=i686-apple-darwin9 +#ifndef __has_feature +#error Should have __has_feature +#endif + + +#if __has_feature(something_we_dont_have) +#error Bad +#endif + +#if !__has_builtin(__builtin_huge_val) || \ + !__has_builtin(__builtin_shufflevector) || \ + !__has_builtin(__builtin_trap) || \ + !__has_feature(attribute_analyzer_noreturn) || \ + !__has_feature(attribute_overloadable) +#error Clang should have these +#endif + +#if __has_builtin(__builtin_insanity) +#error Clang should not have this +#endif + + + +// Make sure we have x86 builtins only (forced with target triple). + +#if !__has_builtin(__builtin_ia32_emms) || \ + __has_builtin(__builtin_altivec_abs_v4sf) +#error Broken handling of target-specific builtins +#endif |