From a7ff62f9443efa3b13a28a1e566d4625b15b8553 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Tue, 4 Jun 2013 02:07:14 +0000 Subject: [PATCH] Adding support for MSVC #pragma detect_mismatch functionality by emitting a FAILIFMISMATCH linker command into the object file. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@183178 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTConsumer.h | 6 +++ include/clang/Basic/DiagnosticParseKinds.td | 5 +- include/clang/Lex/PPCallbacks.h | 14 +++++ include/clang/Parse/Parser.h | 1 + include/clang/Sema/Sema.h | 3 ++ lib/CodeGen/CodeGenAction.cpp | 5 ++ lib/CodeGen/CodeGenModule.cpp | 7 +++ lib/CodeGen/CodeGenModule.h | 3 ++ lib/CodeGen/ModuleBuilder.cpp | 5 ++ lib/CodeGen/TargetInfo.cpp | 12 +++++ lib/CodeGen/TargetInfo.h | 6 +++ lib/Frontend/PrintPreprocessedOutput.cpp | 49 +++++++++++------- lib/Parse/ParsePragma.cpp | 57 +++++++++++++++++++++ lib/Parse/ParsePragma.h | 10 ++++ lib/Parse/Parser.cpp | 4 ++ lib/Sema/SemaAttr.cpp | 6 +++ test/CodeGen/pragma-detect_mismatch.c | 12 +++++ test/Preprocessor/pragma_microsoft.c | 5 ++ 18 files changed, 190 insertions(+), 20 deletions(-) create mode 100644 test/CodeGen/pragma-detect_mismatch.c diff --git a/include/clang/AST/ASTConsumer.h b/include/clang/AST/ASTConsumer.h index 33e9bd92a4..9109a6741f 100644 --- a/include/clang/AST/ASTConsumer.h +++ b/include/clang/AST/ASTConsumer.h @@ -92,6 +92,12 @@ public: /// only exists to support Microsoft's #pragma comment(linker, "/foo"). virtual void HandleLinkerOptionPragma(llvm::StringRef Opts) {} + /// \brief Handle a pragma that emits a mismatch identifier and value to the + /// object file for the linker to work with. Currently, this only exists to + /// support Microsoft's #pragma detect_mismatch. + virtual void HandleDetectMismatch(llvm::StringRef Name, + llvm::StringRef Value) {} + /// \brief Handle a dependent library created by a pragma in the source. /// Currently this only exists to support Microsoft's /// #pragma comment(lib, "/foo"). diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 69314e93d9..74b94d3413 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -789,7 +789,10 @@ def err_pragma_fp_contract_scope : Error< def err_pragma_comment_malformed : Error< "pragma comment requires parenthesized identifier and optional string">; def err_pragma_comment_unknown_kind : Error<"unknown kind of pragma comment">; - +// - #pragma detect_mismatch +def err_pragma_detect_mismatch_malformed : Error< + "pragma detect_mismatch is malformed; it requires two comma-separated " + "string literals">; // OpenCL Section 6.8.g def err_not_opencl_storage_class_specifier : Error< diff --git a/include/clang/Lex/PPCallbacks.h b/include/clang/Lex/PPCallbacks.h index db2ecd247f..021cef00b3 100644 --- a/include/clang/Lex/PPCallbacks.h +++ b/include/clang/Lex/PPCallbacks.h @@ -160,6 +160,13 @@ public: const std::string &Str) { } + /// \brief Callback invoked when a \#pragma detect_mismatch directive is + /// read. + virtual void PragmaDetectMismatch(SourceLocation Loc, + const std::string &Name, + const std::string &Value) { + } + /// \brief Callback invoked when a \#pragma clang __debug directive is read. /// \param Loc The location of the debug directive. /// \param DebugType The identifier following __debug. @@ -352,6 +359,13 @@ public: Second->PragmaComment(Loc, Kind, Str); } + virtual void PragmaDetectMismatch(SourceLocation Loc, + const std::string &Name, + const std::string &Value) { + First->PragmaDetectMismatch(Loc, Name, Value); + Second->PragmaDetectMismatch(Loc, Name, Value); + } + virtual void PragmaMessage(SourceLocation Loc, StringRef Namespace, PragmaMessageKind Kind, StringRef Str) { First->PragmaMessage(Loc, Namespace, Kind, Str); diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index de45268e04..d3563bcf3e 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -150,6 +150,7 @@ class Parser : public CodeCompletionHandler { OwningPtr CommentSemaHandler; OwningPtr OpenMPHandler; OwningPtr MSCommentHandler; + OwningPtr MSDetectMismatchHandler; /// Whether the '>' token acts as an operator or not. This will be /// true except when we are parsing an expression within a C++ diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 22490171c8..d587386490 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -6658,6 +6658,9 @@ public: /// ActOnPragmaMSStruct - Called on well formed \#pragma comment(kind, "arg"). void ActOnPragmaMSComment(PragmaMSCommentKind Kind, StringRef Arg); + /// ActOnPragmaDetectMismatch - Call on well-formed \#pragma detect_mismatch + void ActOnPragmaDetectMismatch(StringRef Name, StringRef Value); + /// ActOnPragmaUnused - Called on well-formed '\#pragma unused'. void ActOnPragmaUnused(const Token &Identifier, Scope *curScope, diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index 9570076574..804af31c31 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -183,6 +183,11 @@ namespace clang { Gen->HandleLinkerOptionPragma(Opts); } + virtual void HandleDetectMismatch(llvm::StringRef Name, + llvm::StringRef Value) { + Gen->HandleDetectMismatch(Name, Value); + } + virtual void HandleDependentLibrary(llvm::StringRef Opts) { Gen->HandleDependentLibrary(Opts); } diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 7ad08e0e0a..3675e849aa 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -773,6 +773,13 @@ void CodeGenModule::AppendLinkerOptions(StringRef Opts) { LinkerOptionsMetadata.push_back(llvm::MDNode::get(getLLVMContext(), MDOpts)); } +void CodeGenModule::AddDetectMismatch(StringRef Name, StringRef Value) { + llvm::SmallString<32> Opt; + getTargetCodeGenInfo().getDetectMismatchOption(Name, Value, Opt); + llvm::Value *MDOpts = llvm::MDString::get(getLLVMContext(), Opt); + LinkerOptionsMetadata.push_back(llvm::MDNode::get(getLLVMContext(), MDOpts)); +} + void CodeGenModule::AddDependentLib(StringRef Lib) { llvm::SmallString<24> Opt; getTargetCodeGenInfo().getDependentLibraryOption(Lib, Opt); diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index e4e29a8d6b..bc87316696 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -912,6 +912,9 @@ public: /// \brief Appends Opts to the "Linker Options" metadata value. void AppendLinkerOptions(StringRef Opts); + /// \bried Appends a detect mismatch command to the linker options. + void AddDetectMismatch(StringRef Name, StringRef Value); + /// \brief Appends a dependent lib to the "Linker Options" metadata value. void AddDependentLib(StringRef Lib); diff --git a/lib/CodeGen/ModuleBuilder.cpp b/lib/CodeGen/ModuleBuilder.cpp index 56067a47bb..763aa12aab 100644 --- a/lib/CodeGen/ModuleBuilder.cpp +++ b/lib/CodeGen/ModuleBuilder.cpp @@ -121,6 +121,11 @@ namespace { Builder->AppendLinkerOptions(Opts); } + virtual void HandleDetectMismatch(llvm::StringRef Name, + llvm::StringRef Value) { + Builder->AddDetectMismatch(Name, Value); + } + virtual void HandleDependentLibrary(llvm::StringRef Lib) { Builder->AddDependentLib(Lib); } diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index 4fa0c3bbdf..6e5084a149 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -1287,6 +1287,12 @@ public: Opt = "/DEFAULTLIB:"; Opt += qualifyWindowsLibrary(Lib); } + + void getDetectMismatchOption(llvm::StringRef Name, + llvm::StringRef Value, + llvm::SmallString<32> &Opt) const { + Opt = "/FAILIFMISMATCH:\"" + Name.str() + "=" + Value.str() + "\""; + } }; class WinX86_64TargetCodeGenInfo : public TargetCodeGenInfo { @@ -1313,6 +1319,12 @@ public: Opt = "/DEFAULTLIB:"; Opt += qualifyWindowsLibrary(Lib); } + + void getDetectMismatchOption(llvm::StringRef Name, + llvm::StringRef Value, + llvm::SmallString<32> &Opt) const { + Opt = "/FAILIFMISMATCH:\"" + Name.str() + "=" + Value.str() + "\""; + } }; } diff --git a/lib/CodeGen/TargetInfo.h b/lib/CodeGen/TargetInfo.h index dee7fd8dd4..b0ddebc1a0 100644 --- a/lib/CodeGen/TargetInfo.h +++ b/lib/CodeGen/TargetInfo.h @@ -173,6 +173,12 @@ namespace clang { /// platform. virtual void getDependentLibraryOption(llvm::StringRef Lib, llvm::SmallString<24> &Opt) const; + + /// Gets the linker options necessary to detect object file mismatches on + /// this platform. + virtual void getDetectMismatchOption(llvm::StringRef Name, + llvm::StringRef Value, + llvm::SmallString<32> &Opt) const {} }; } diff --git a/lib/Frontend/PrintPreprocessedOutput.cpp b/lib/Frontend/PrintPreprocessedOutput.cpp index 9fd3649435..83b2a271ec 100644 --- a/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/lib/Frontend/PrintPreprocessedOutput.cpp @@ -140,6 +140,9 @@ public: virtual void PragmaCaptured(SourceLocation Loc, StringRef Str); virtual void PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind, const std::string &Str); + virtual void PragmaDetectMismatch(SourceLocation Loc, + const std::string &Name, + const std::string &Value); virtual void PragmaMessage(SourceLocation Loc, StringRef Namespace, PragmaMessageKind Kind, StringRef Str); virtual void PragmaDebug(SourceLocation Loc, StringRef DebugType); @@ -382,16 +385,8 @@ void PrintPPOutputPPCallbacks::MacroUndefined(const Token &MacroNameTok, setEmittedDirectiveOnThisLine(); } -void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc, - const IdentifierInfo *Kind, +static void outputPrintable(llvm::raw_ostream& OS, const std::string &Str) { - startNewLineIfNeeded(); - MoveToLine(Loc); - OS << "#pragma comment(" << Kind->getName(); - - if (!Str.empty()) { - OS << ", \""; - for (unsigned i = 0, e = Str.size(); i != e; ++i) { unsigned char Char = Str[i]; if (isPrintable(Char) && Char != '\\' && Char != '"') @@ -402,6 +397,18 @@ void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc, << (char)('0'+ ((Char >> 3) & 7)) << (char)('0'+ ((Char >> 0) & 7)); } +} + +void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc, + const IdentifierInfo *Kind, + const std::string &Str) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma comment(" << Kind->getName(); + + if (!Str.empty()) { + OS << ", \""; + outputPrintable(OS, Str); OS << '"'; } @@ -409,6 +416,19 @@ void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc, setEmittedDirectiveOnThisLine(); } +void PrintPPOutputPPCallbacks::PragmaDetectMismatch(SourceLocation Loc, + const std::string &Name, + const std::string &Value) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma detect_mismatch(\"" << Name << '"'; + outputPrintable(OS, Name); + OS << "\", \""; + outputPrintable(OS, Value); + OS << "\")"; + setEmittedDirectiveOnThisLine(); +} + void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc, StringRef Namespace, PragmaMessageKind Kind, @@ -430,16 +450,7 @@ void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc, break; } - for (unsigned i = 0, e = Str.size(); i != e; ++i) { - unsigned char Char = Str[i]; - if (isPrintable(Char) && Char != '\\' && Char != '"') - OS << (char)Char; - else // Output anything hard as an octal escape. - OS << '\\' - << (char)('0'+ ((Char >> 6) & 7)) - << (char)('0'+ ((Char >> 3) & 7)) - << (char)('0'+ ((Char >> 0) & 7)); - } + outputPrintable(OS, Str); OS << '"'; if (Kind == PMK_Message) OS << ')'; diff --git a/lib/Parse/ParsePragma.cpp b/lib/Parse/ParsePragma.cpp index 3c6f96ac2b..10ad5df629 100644 --- a/lib/Parse/ParsePragma.cpp +++ b/lib/Parse/ParsePragma.cpp @@ -794,6 +794,63 @@ PragmaOpenMPHandler::HandlePragma(Preprocessor &PP, /*DisableMacroExpansion=*/true, /*OwnsTokens=*/true); } +/// \brief Handle the Microsoft \#pragma detect_mismatch extension. +/// +/// The syntax is: +/// \code +/// #pragma detect_mismatch("name", "value") +/// \endcode +/// Where 'name' and 'value' are quoted strings. The values are embedded in +/// the object file and passed along to the linker. If the linker detects a +/// mismatch in the object file's values for the given name, a LNK2038 error +/// is emitted. See MSDN for more details. +void PragmaDetectMismatchHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducerKind Introducer, + Token &Tok) { + SourceLocation CommentLoc = Tok.getLocation(); + PP.Lex(Tok); + if (Tok.isNot(tok::l_paren)) { + PP.Diag(CommentLoc, diag::err_expected_lparen); + return; + } + + // Read the name to embed, which must be a string literal. + std::string NameString; + if (!PP.LexStringLiteral(Tok, NameString, + "pragma detect_mismatch", + /*MacroExpansion=*/true)) + return; + + // Read the comma followed by a second string literal. + std::string ValueString; + if (Tok.isNot(tok::comma)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_detect_mismatch_malformed); + return; + } + + if (!PP.LexStringLiteral(Tok, ValueString, "pragma detect_mismatch", + /*MacroExpansion=*/true)) + return; + + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::err_expected_rparen); + return; + } + PP.Lex(Tok); // Eat the r_paren. + + if (Tok.isNot(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_detect_mismatch_malformed); + return; + } + + // If the pragma is lexically sound, notify any interested PPCallbacks. + if (PP.getPPCallbacks()) + PP.getPPCallbacks()->PragmaDetectMismatch(CommentLoc, NameString, + ValueString); + + Actions.ActOnPragmaDetectMismatch(NameString, ValueString); +} + /// \brief Handle the microsoft \#pragma comment extension. /// /// The syntax is: diff --git a/lib/Parse/ParsePragma.h b/lib/Parse/ParsePragma.h index 3c6f3434ad..b41450f4ea 100644 --- a/lib/Parse/ParsePragma.h +++ b/lib/Parse/ParsePragma.h @@ -124,6 +124,16 @@ private: Sema &Actions; }; +class PragmaDetectMismatchHandler : public PragmaHandler { +public: + PragmaDetectMismatchHandler(Sema &Actions) + : PragmaHandler("detect_mismatch"), Actions(Actions) {} + virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, + Token &FirstToken); +private: + Sema &Actions; +}; + } // end namespace clang #endif diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 3124b96455..f19d24299a 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -105,6 +105,8 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies) if (getLangOpts().MicrosoftExt) { MSCommentHandler.reset(new PragmaCommentHandler(actions)); PP.AddPragmaHandler(MSCommentHandler.get()); + MSDetectMismatchHandler.reset(new PragmaDetectMismatchHandler(actions)); + PP.AddPragmaHandler(MSDetectMismatchHandler.get()); } CommentSemaHandler.reset(new ActionCommentHandler(actions)); @@ -444,6 +446,8 @@ Parser::~Parser() { if (getLangOpts().MicrosoftExt) { PP.RemovePragmaHandler(MSCommentHandler.get()); MSCommentHandler.reset(); + PP.RemovePragmaHandler(MSDetectMismatchHandler.get()); + MSDetectMismatchHandler.reset(); } PP.RemovePragmaHandler("STDC", FPContractHandler.get()); diff --git a/lib/Sema/SemaAttr.cpp b/lib/Sema/SemaAttr.cpp index 943054be34..3fbb11f2fd 100644 --- a/lib/Sema/SemaAttr.cpp +++ b/lib/Sema/SemaAttr.cpp @@ -283,6 +283,12 @@ void Sema::ActOnPragmaMSComment(PragmaMSCommentKind Kind, llvm::StringRef Arg) { llvm_unreachable("invalid pragma comment kind"); } +void Sema::ActOnPragmaDetectMismatch(llvm::StringRef Name, + llvm::StringRef Value) { + // FIXME: Serialize this. + Consumer.HandleDetectMismatch(Name, Value); +} + void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope, SourceLocation PragmaLoc) { diff --git a/test/CodeGen/pragma-detect_mismatch.c b/test/CodeGen/pragma-detect_mismatch.c new file mode 100644 index 0000000000..86cc6d8586 --- /dev/null +++ b/test/CodeGen/pragma-detect_mismatch.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 %s -triple i686-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s + +#pragma detect_mismatch("test", "1") + +#define BAR "2" +#pragma detect_mismatch("test2", BAR) + +// CHECK: !llvm.module.flags = !{!0} +// CHECK: !0 = metadata !{i32 6, metadata !"Linker Options", metadata ![[link_opts:[0-9]+]]} +// CHECK: ![[link_opts]] = metadata !{metadata ![[test:[0-9]+]], metadata ![[test2:[0-9]+]]} +// CHECK: ![[test]] = metadata !{metadata !"/FAILIFMISMATCH:\22test=1\22"} +// CHECK: ![[test2]] = metadata !{metadata !"/FAILIFMISMATCH:\22test2=2\22"} diff --git a/test/Preprocessor/pragma_microsoft.c b/test/Preprocessor/pragma_microsoft.c index c0ddf74340..26f0a1df8c 100644 --- a/test/Preprocessor/pragma_microsoft.c +++ b/test/Preprocessor/pragma_microsoft.c @@ -18,6 +18,11 @@ #pragma comment(user, "foo\abar\nbaz\tsome thing") +#pragma detect_mismatch("test", "1") +#pragma detect_mismatch() // expected-error {{expected string literal in pragma detect_mismatch}} +#pragma detect_mismatch("test") // expected-error {{pragma detect_mismatch is malformed; it requires two comma-separated string literals}} +#pragma detect_mismatch("test", 1) // expected-error {{expected string literal in pragma detect_mismatch}} +#pragma detect_mismatch("test", BAR) // __pragma -- 2.40.0