Adding support for MSVC #pragma detect_mismatch functionality by emitting a FAILIFMISMATCH linker command into the object file.

llvm-svn: 183178
This commit is contained in:
Aaron Ballman 2013-06-04 02:07:14 +00:00
parent 60c4118c88
commit 5d041beb4e
18 changed files with 190 additions and 20 deletions

View File

@ -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").

View File

@ -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<

View File

@ -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);

View File

@ -150,6 +150,7 @@ class Parser : public CodeCompletionHandler {
OwningPtr<CommentHandler> CommentSemaHandler;
OwningPtr<PragmaHandler> OpenMPHandler;
OwningPtr<PragmaHandler> MSCommentHandler;
OwningPtr<PragmaHandler> MSDetectMismatchHandler;
/// Whether the '>' token acts as an operator or not. This will be
/// true except when we are parsing an expression within a C++

View File

@ -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,

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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() + "\"";
}
};
}

View File

@ -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 {}
};
}

View File

@ -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 << ')';

View File

@ -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:

View File

@ -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

View File

@ -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());

View File

@ -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) {

View File

@ -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"}

View File

@ -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