[clang] 3946de0 - [MSVC] Add support for pragma function

Stephen Long via cfe-commits cfe-commits at lists.llvm.org
Fri May 13 06:40:08 PDT 2022


Author: Stephen Long
Date: 2022-05-13T06:39:47-07:00
New Revision: 3946de0456fcbcd3a6c9f0d09d16d46c7987e0d7

URL: https://github.com/llvm/llvm-project/commit/3946de0456fcbcd3a6c9f0d09d16d46c7987e0d7
DIFF: https://github.com/llvm/llvm-project/commit/3946de0456fcbcd3a6c9f0d09d16d46c7987e0d7.diff

LOG: [MSVC] Add support for pragma function

MSVC pragma function tells the compiler to generate calls to functions in the pragma function list, instead of using the builtin. Needs https://reviews.llvm.org/D124701

https://docs.microsoft.com/en-us/cpp/preprocessor/function-c-cpp?view=msvc-170

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D124702

Added: 
    clang/test/CodeGen/pragma-ms-function.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/Sema.h
    clang/lib/Parse/ParsePragma.cpp
    clang/lib/Sema/SemaAttr.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/test/Preprocessor/pragma_microsoft.c
    clang/test/Preprocessor/pragma_microsoft.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5b12400701c68..2a43880c2fd74 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -278,6 +278,9 @@ Removed Compiler Flags
 
 New Pragmas in Clang
 --------------------
+- Added support for MSVC's ``#pragma function``, which tells the compiler to
+  generate calls to functions listed in the pragma instead of using the
+  builtins.
 
 - ...
 

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d8f87747e58d0..dfca233033a9f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -988,6 +988,9 @@ def err_invalid_super_scope : Error<"invalid use of '__super', "
 def err_super_in_lambda_unsupported : Error<
   "use of '__super' inside a lambda is unsupported">;
 
+def err_pragma_expected_file_scope : Error<
+  "'#pragma %0' can only appear at file scope">;
+
 def warn_pragma_unused_undeclared_var : Warning<
   "undeclared variable %0 used as an argument for '#pragma unused'">,
   InGroup<IgnoredPragmas>;

diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index caa58d9260628..4e4fcb53474ec 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -196,6 +196,7 @@ class Parser : public CodeCompletionHandler {
   std::unique_ptr<PragmaHandler> MSSection;
   std::unique_ptr<PragmaHandler> MSRuntimeChecks;
   std::unique_ptr<PragmaHandler> MSIntrinsic;
+  std::unique_ptr<PragmaHandler> MSFunction;
   std::unique_ptr<PragmaHandler> MSOptimize;
   std::unique_ptr<PragmaHandler> MSFenvAccess;
   std::unique_ptr<PragmaHandler> CUDAForceHostDeviceHandler;
@@ -722,6 +723,8 @@ class Parser : public CodeCompletionHandler {
                              SourceLocation PragmaLocation);
   bool HandlePragmaMSInitSeg(StringRef PragmaName,
                              SourceLocation PragmaLocation);
+  bool HandlePragmaMSFunction(StringRef PragmaName,
+                              SourceLocation PragmaLocation);
 
   /// Handle the annotation token produced for
   /// #pragma align...

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 27603f0b891f3..13d403bf8db94 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -755,6 +755,9 @@ class Sema final {
   /// optimizations are currently "on", this is set to an invalid location.
   SourceLocation OptimizeOffPragmaLocation;
 
+  /// Set of no-builtin functions listed by \#pragma function.
+  llvm::SmallSetVector<StringRef, 4> MSFunctionNoBuiltins;
+
   /// Flag indicating if Sema is building a recovery call expression.
   ///
   /// This flag is used to avoid building recovery call expressions
@@ -10322,6 +10325,11 @@ class Sema final {
   /// Called on well formed \#pragma clang optimize.
   void ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc);
 
+  /// Call on well formed \#pragma function.
+  void
+  ActOnPragmaMSFunction(SourceLocation Loc,
+                        const llvm::SmallVectorImpl<StringRef> &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 {
@@ -10338,6 +10346,11 @@ class Sema final {
   /// 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 AddImplicitMSFunctionNoBuiltinAttr(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
index 75577f8f4e04c..cf637fc0d80b7 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -445,6 +445,8 @@ void Parser::initializePragmaHandlers() {
     PP.AddPragmaHandler(MSCodeSeg.get());
     MSSection = std::make_unique<PragmaMSPragma>("section");
     PP.AddPragmaHandler(MSSection.get());
+    MSFunction = std::make_unique<PragmaMSPragma>("function");
+    PP.AddPragmaHandler(MSFunction.get());
     MSRuntimeChecks = std::make_unique<PragmaMSRuntimeChecksHandler>();
     PP.AddPragmaHandler(MSRuntimeChecks.get());
     MSIntrinsic = std::make_unique<PragmaMSIntrinsicHandler>();
@@ -554,6 +556,8 @@ void Parser::resetPragmaHandlers() {
     MSCodeSeg.reset();
     PP.RemovePragmaHandler(MSSection.get());
     MSSection.reset();
+    PP.RemovePragmaHandler(MSFunction.get());
+    MSFunction.reset();
     PP.RemovePragmaHandler(MSRuntimeChecks.get());
     MSRuntimeChecks.reset();
     PP.RemovePragmaHandler(MSIntrinsic.get());
@@ -906,13 +910,15 @@ void Parser::HandlePragmaMSPragma() {
   // Figure out which #pragma we're dealing with.  The switch has no default
   // because lex shouldn't emit the annotation token for unrecognized pragmas.
   typedef bool (Parser::*PragmaHandler)(StringRef, SourceLocation);
-  PragmaHandler Handler = llvm::StringSwitch<PragmaHandler>(PragmaName)
-    .Case("data_seg", &Parser::HandlePragmaMSSegment)
-    .Case("bss_seg", &Parser::HandlePragmaMSSegment)
-    .Case("const_seg", &Parser::HandlePragmaMSSegment)
-    .Case("code_seg", &Parser::HandlePragmaMSSegment)
-    .Case("section", &Parser::HandlePragmaMSSection)
-    .Case("init_seg", &Parser::HandlePragmaMSInitSeg);
+  PragmaHandler Handler =
+      llvm::StringSwitch<PragmaHandler>(PragmaName)
+          .Case("data_seg", &Parser::HandlePragmaMSSegment)
+          .Case("bss_seg", &Parser::HandlePragmaMSSegment)
+          .Case("const_seg", &Parser::HandlePragmaMSSegment)
+          .Case("code_seg", &Parser::HandlePragmaMSSegment)
+          .Case("section", &Parser::HandlePragmaMSSection)
+          .Case("init_seg", &Parser::HandlePragmaMSInitSeg)
+          .Case("function", &Parser::HandlePragmaMSFunction);
 
   if (!(this->*Handler)(PragmaName, PragmaLocation)) {
     // Pragma handling failed, and has been diagnosed.  Slurp up the tokens
@@ -3547,6 +3553,41 @@ void PragmaMSIntrinsicHandler::HandlePragma(Preprocessor &PP,
         << "intrinsic";
 }
 
+bool Parser::HandlePragmaMSFunction(StringRef PragmaName,
+                                    SourceLocation PragmaLocation) {
+  Token FirstTok = Tok;
+
+  if (ExpectAndConsume(tok::l_paren, diag::warn_pragma_expected_lparen,
+                       PragmaName))
+    return false;
+
+  bool SuggestIntrinH = !PP.isMacroDefined("__INTRIN_H");
+
+  llvm::SmallVector<StringRef> 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.emplace_back(II->getName());
+
+    PP.Lex(Tok);
+    if (Tok.isNot(tok::comma))
+      break;
+    PP.Lex(Tok); // ,
+  }
+
+  if (ExpectAndConsume(tok::r_paren, diag::warn_pragma_expected_rparen,
+                       PragmaName) ||
+      ExpectAndConsume(tok::eof, diag::warn_pragma_extra_tokens_at_eol,
+                       PragmaName))
+    return false;
+
+  Actions.ActOnPragmaMSFunction(FirstTok.getLocation(), NoBuiltins);
+  return true;
+}
+
 // #pragma optimize("gsty", on|off)
 void PragmaMSOptimizeHandler::HandlePragma(Preprocessor &PP,
                                            PragmaIntroducer Introducer,

diff  --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 113b3ac89f463..d4d773bd4e7b7 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1065,6 +1065,16 @@ void Sema::ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc) {
     OptimizeOffPragmaLocation = PragmaLoc;
 }
 
+void Sema::ActOnPragmaMSFunction(
+    SourceLocation Loc, const llvm::SmallVectorImpl<StringRef> &NoBuiltins) {
+  if (!CurContext->getRedeclContext()->isFileContext()) {
+    Diag(Loc, diag::err_pragma_expected_file_scope) << "function";
+    return;
+  }
+
+  MSFunctionNoBuiltins.insert(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 +1096,13 @@ void Sema::AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD,
     FD->addAttr(NoInlineAttr::CreateImplicit(Context, Loc));
 }
 
+void Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) {
+  SmallVector<StringRef> V(MSFunctionNoBuiltins.begin(),
+                           MSFunctionNoBuiltins.end());
+  if (!MSFunctionNoBuiltins.empty())
+    FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size()));
+}
+
 typedef std::vector<std::pair<unsigned, SourceLocation> > VisStack;
 enum : unsigned { NoVisibility = ~0U };
 

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 498dacf8d844d..d599cd619420b 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10218,10 +10218,12 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
   // 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);
+    AddImplicitMSFunctionNoBuiltinAttr(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.c b/clang/test/CodeGen/pragma-ms-function.c
new file mode 100644
index 0000000000000..a95d22ce1d81e
--- /dev/null
+++ b/clang/test/CodeGen/pragma-ms-function.c
@@ -0,0 +1,38 @@
+// 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]+]]
+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]+]]
+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]+]]
+void foo3(char *s, char *d, size_t n) {
+  bar(s);
+  memset(s, 2, 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"{{.*}}}

diff  --git a/clang/test/Preprocessor/pragma_microsoft.c b/clang/test/Preprocessor/pragma_microsoft.c
index ea246c2e6891e..ab60902783573 100644
--- a/clang/test/Preprocessor/pragma_microsoft.c
+++ b/clang/test/Preprocessor/pragma_microsoft.c
@@ -201,6 +201,27 @@ void g(int k) {}
 #pragma clang diagnostic pop
 #pragma intrinsic(asdf) // expected-warning {{'asdf' is not a recognized builtin; consider including <intrin.h>}}
 
+// Test pragma function
+#pragma function(memset)                 // no-warning
+#pragma function(memcpy, strlen, strlen) // no-warning
+#pragma function()                       // no-warning
+#pragma function(asdf)                   // expected-warning {{'asdf' is not a recognized builtin; consider including <intrin.h>}}
+#pragma function(main)                   // expected-warning {{'main' is not a recognized builtin; consider including <intrin.h>}}
+#pragma function(                        // expected-warning {{missing ')' after}}
+#pragma function(int)                    // expected-warning {{missing ')' after}}
+#pragma function(strcmp) asdf            // expected-warning {{extra tokens at end}}
+
+#define __INTRIN_H       // there should be no notes after defining __INTRIN_H
+#pragma function(asdf)   // expected-warning-re {{'asdf' is not a recognized builtin{{$}}}}
+#pragma function(memset) // no-warning
+#undef __INTRIN_H
+#pragma function(asdf) // expected-warning {{'asdf' is not a recognized builtin; consider including <intrin.h>}}
+
+// MSVC accepts this, but we decide to reject it based on the MS docs saying the pragma must appear at the global level
+void pragma_function_foo() {
+#pragma function(memset) // expected-error {{'#pragma function' can only appear at file scope}}
+}
+
 #pragma optimize          // expected-warning{{missing '(' after '#pragma optimize'}}
 #pragma optimize(         // expected-warning{{expected string literal in '#pragma optimize'}}
 #pragma optimize(a        // expected-warning{{expected string literal in '#pragma optimize'}}

diff  --git a/clang/test/Preprocessor/pragma_microsoft.cpp b/clang/test/Preprocessor/pragma_microsoft.cpp
index e097d69a208fa..22c8a4c338136 100644
--- a/clang/test/Preprocessor/pragma_microsoft.cpp
+++ b/clang/test/Preprocessor/pragma_microsoft.cpp
@@ -1,3 +1,7 @@
 // RUN: %clang_cc1 %s -fsyntax-only -std=c++11 -verify -fms-extensions
 
 #pragma warning(push, 4_D) // expected-warning {{requires a level between 0 and 4}}
+
+extern "C" {
+#pragma function(memset) // no-warning
+}


        


More information about the cfe-commits mailing list