diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -196,6 +196,7 @@ std::unique_ptr MSSection; std::unique_ptr MSRuntimeChecks; std::unique_ptr MSIntrinsic; + std::unique_ptr MSFunction; std::unique_ptr MSOptimize; std::unique_ptr MSFenvAccess; std::unique_ptr CUDAForceHostDeviceHandler; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -755,6 +755,8 @@ /// optimizations are currently "on", this is set to an invalid location. SourceLocation OptimizeOffPragmaLocation; + std::vector MSFunctionNoBuiltins; + /// Flag indicating if Sema is building a recovery call expression. /// /// This flag is used to avoid building recovery call expressions @@ -10314,6 +10316,10 @@ /// Called on well formed \#pragma clang optimize. void ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc); + void ActOnPragmaMSIntrinsic(const std::vector &Intrinsics); + + void ActOnPragmaMSFunction(const std::vector &NoBuiltins); + /// Get the location for the currently active "\#pragma clang optimize /// off". If this location is invalid, then the state of the pragma is "on". SourceLocation getOptimizeOffPragmaLocation() const { @@ -10330,6 +10336,11 @@ /// attribute to be added (usually because of a pragma). void AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD, SourceLocation Loc); + /// Only called on function definitions; if there is a pragma in scope + /// with the effect of a range-based no_builtin, consider marking the function + /// with attribute no_builtin. + void AddRangeBasedNoBuiltin(FunctionDecl *FD); + /// AddAlignedAttr - Adds an aligned attribute to a particular declaration. void AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E, bool IsPackExpansion); diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -250,9 +250,21 @@ }; struct PragmaMSIntrinsicHandler : public PragmaHandler { - PragmaMSIntrinsicHandler() : PragmaHandler("intrinsic") {} + PragmaMSIntrinsicHandler(Sema &S) : PragmaHandler("intrinsic"), Actions(S) {} void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, Token &FirstToken) override; + +private: + Sema &Actions; +}; + +struct PragmaMSFunctionHandler : public PragmaHandler { + PragmaMSFunctionHandler(Sema &S) : PragmaHandler("function"), Actions(S) {} + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &FirstToken) override; + +private: + Sema &Actions; }; struct PragmaMSOptimizeHandler : public PragmaHandler { @@ -447,8 +459,10 @@ PP.AddPragmaHandler(MSSection.get()); MSRuntimeChecks = std::make_unique(); PP.AddPragmaHandler(MSRuntimeChecks.get()); - MSIntrinsic = std::make_unique(); + MSIntrinsic = std::make_unique(Actions); PP.AddPragmaHandler(MSIntrinsic.get()); + MSFunction = std::make_unique(Actions); + PP.AddPragmaHandler(MSFunction.get()); MSOptimize = std::make_unique(); PP.AddPragmaHandler(MSOptimize.get()); MSFenvAccess = std::make_unique(); @@ -558,6 +572,8 @@ MSRuntimeChecks.reset(); PP.RemovePragmaHandler(MSIntrinsic.get()); MSIntrinsic.reset(); + PP.RemovePragmaHandler(MSFunction.get()); + MSFunction.reset(); PP.RemovePragmaHandler(MSOptimize.get()); MSOptimize.reset(); PP.RemovePragmaHandler(MSFenvAccess.get()); @@ -3523,11 +3539,15 @@ bool SuggestIntrinH = !PP.isMacroDefined("__INTRIN_H"); + std::vector Intrinsics; while (Tok.is(tok::identifier)) { IdentifierInfo *II = Tok.getIdentifierInfo(); - if (!II->getBuiltinID()) + if (!II->getBuiltinID()) { PP.Diag(Tok.getLocation(), diag::warn_pragma_intrinsic_builtin) << II << SuggestIntrinH; + } else { + Intrinsics.push_back(II->getName()); + } PP.Lex(Tok); if (Tok.isNot(tok::comma)) @@ -3545,6 +3565,52 @@ if (Tok.isNot(tok::eod)) PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) << "intrinsic"; + + Actions.ActOnPragmaMSIntrinsic(Intrinsics); +} + +void PragmaMSFunctionHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducer Introducer, + Token &Tok) { + PP.Lex(Tok); + + if (Tok.isNot(tok::l_paren)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen) + << "function"; + return; + } + PP.Lex(Tok); // ( + + bool SuggestIntrinH = !PP.isMacroDefined("__INTRIN_H"); + + std::vector NoBuiltins; + while (Tok.is(tok::identifier)) { + IdentifierInfo *II = Tok.getIdentifierInfo(); + if (!II->getBuiltinID()) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_intrinsic_builtin) + << II << SuggestIntrinH; + } else { + NoBuiltins.push_back(II->getName()); + } + + PP.Lex(Tok); + if (Tok.isNot(tok::comma)) + break; + PP.Lex(Tok); // , + } + + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen) + << "function"; + return; + } + PP.Lex(Tok); // ) + + if (Tok.isNot(tok::eod)) + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << "function"; + + Actions.ActOnPragmaMSFunction(NoBuiltins); } // #pragma optimize("gsty", on|off) diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -1065,6 +1065,19 @@ OptimizeOffPragmaLocation = PragmaLoc; } +void Sema::ActOnPragmaMSIntrinsic(const std::vector &Intrinsics) { + for (auto &Intrinsic : Intrinsics) { + MSFunctionNoBuiltins.erase(std::remove(MSFunctionNoBuiltins.begin(), + MSFunctionNoBuiltins.end(), + Intrinsic)); + } +} + +void Sema::ActOnPragmaMSFunction(const std::vector &NoBuiltins) { + MSFunctionNoBuiltins.insert(MSFunctionNoBuiltins.end(), + NoBuiltins.begin(), NoBuiltins.end()); +} + void Sema::AddRangeBasedOptnone(FunctionDecl *FD) { // In the future, check other pragmas if they're implemented (e.g. pragma // optimize 0 will probably map to this functionality too). @@ -1086,6 +1099,13 @@ FD->addAttr(NoInlineAttr::CreateImplicit(Context, Loc)); } +void Sema::AddRangeBasedNoBuiltin(FunctionDecl *FD) { + if (!MSFunctionNoBuiltins.empty()) { + FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, + MSFunctionNoBuiltins.data(), MSFunctionNoBuiltins.size())); + } +} + typedef std::vector > VisStack; enum : unsigned { NoVisibility = ~0U }; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10218,10 +10218,12 @@ // marking the function. AddCFAuditedAttribute(NewFD); - // If this is a function definition, check if we have to apply optnone due to - // a pragma. - if(D.isFunctionDefinition()) + // If this is a function definition, check if we have to apply any + // attributes (i.e. optnone and no_builtin) due to a pragma. + if(D.isFunctionDefinition()) { AddRangeBasedOptnone(NewFD); + AddRangeBasedNoBuiltin(NewFD); + } // If this is the first declaration of an extern C variable, update // the map of such variables. diff --git a/clang/test/CodeGen/pragma-ms-function-intrinsic.c b/clang/test/CodeGen/pragma-ms-function-intrinsic.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/pragma-ms-function-intrinsic.c @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -emit-llvm -fms-extensions -o - %s | FileCheck %s + +typedef typeof(sizeof(0)) size_t; + +void bar(char *s); +void *memset(void *s, int c, size_t n); +void *memcpy(void *d, const void *s, size_t n); + +// CHECK: define{{.*}} void @foo1({{.*}}) #[[NO_NOBUILTIN:[0-9]+]] +// CHECK: call void @bar +// CHECK: call void @llvm.memset +// CHECK: call void @llvm.memcpy +void foo1(char *s, char *d, size_t n) +{ + bar(s); + memset(s, 0, n); + memcpy(d, s, n); +} + +#pragma function(strlen, memset) + +// CHECK: define{{.*}} void @foo2({{.*}}) #[[NOBUILTIN_MEMSET:[0-9]+]] +// CHECK: call void @bar +// CHECK: {{.*}}call i8* @memset +// CHECK: call void @llvm.memcpy +void foo2(char *s, char *d, size_t n) +{ + bar(s); + memset(s, 1, n); + memcpy(d, s, n); +} + +#pragma function(memcpy) + +// CHECK: define{{.*}} void @foo3({{.*}}) #[[NOBUILTIN_MEMSET_MEMCPY:[0-9]+]] +// CHECK: call void @bar +// CHECK: {{.*}}call i8* @memset +// CHECK: {{.*}}call i8* @memcpy +void foo3(char *s, char *d, size_t n) +{ + bar(s); + memset(s, 2, n); + memcpy(d, s, n); +} + +#pragma intrinsic(memset, strlen) + +// CHECK: define{{.*}} void @foo4({{.*}}) #[[NOBUILTIN_MEMCPY:[0-9]+]] +// CHECK: call void @bar +// CHECK: call void @llvm.memset +// CHECK: {{.*}}call i8* @memcpy +void foo4(char *s, char *d, size_t n) +{ + bar(s); + memset(s, 3, n); + memcpy(d, s, n); +} + +#pragma intrinsic(memcpy) + +// CHECK: define{{.*}} void @foo5({{.*}}) #[[NO_NOBUILTIN]] +// CHECK: call void @bar +// CHECK: call void @llvm.memset +// CHECK: call void @llvm.memcpy +void foo5(char *s, char *d, size_t n) +{ + bar(s); + memset(s, 4, n); + memcpy(d, s, n); +} + +// CHECK-NOT: attributes #[[NO_NOBUILTIN]] = {{{.*}}"no-builtin-memset"{{.*}}} +// CHECK-NOT: attributes #[[NO_NOBUILTIN]] = {{{.*}}"no-builtin-memcpy"{{.*}}"no-builtin-memset"{{.*}}} +// CHECK: attributes #[[NOBUILTIN_MEMSET]] = {{{.*}}"no-builtin-memset"{{.*}}} +// CHECK-NOT: attributes #[[NOBUILTIN_MEMSET]] = {{{.*}}"no-builtin-memcpy"{{.*}}"no-builtin-memset"{{.*}}} +// CHECK: attributes #[[NOBUILTIN_MEMSET_MEMCPY]] = {{{.*}}"no-builtin-memcpy"{{.*}}"no-builtin-memset"{{.*}}} +// CHECK: attributes #[[NOBUILTIN_MEMCPY]] = {{{.*}}"no-builtin-memcpy"{{.*}}} +// CHECK-NOT: attributes #[[NOBUILTIN_MEMCPY]] = {{{.*}}"no-builtin-memcpy"{{.*}}"no-builtin-memset"{{.*}}}