[clang] b147717 - [MSVC] Add support for pragma alloc_text

Stephen Long via cfe-commits cfe-commits at lists.llvm.org
Mon May 16 07:00:44 PDT 2022


Author: Stephen Long
Date: 2022-05-16T07:00:17-07:00
New Revision: b147717bb36c915bedfb33c07259cac4f09502a1

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

LOG: [MSVC] Add support for pragma alloc_text

`#pragma alloc_text` is a MSVC pragma that names the code section where functions should be placed. It only
applies to functions with C linkage.

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

Reviewed By: aaron.ballman

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

Added: 
    clang/test/CodeGen/msvc_pragma_alloc_text.cpp
    clang/test/Sema/pragma-ms-alloc-text.cpp

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

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2a43880c2fd74..59c42384c4ed7 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -281,6 +281,9 @@ 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.
+- Added support for MSVC's ``#pragma alloc_text``. The pragma names the code
+  section functions are placed in. The pragma only applies to functions with
+  C linkage.
 
 - ...
 

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index dfca233033a9f..6dcf2eafbf210 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -990,6 +990,8 @@ def err_super_in_lambda_unsupported : Error<
 
 def err_pragma_expected_file_scope : Error<
   "'#pragma %0' can only appear at file scope">;
+def err_pragma_alloc_text_c_linkage: Error<
+  "'#pragma alloc_text' is applicable only to functions with C linkage">;
 
 def warn_pragma_unused_undeclared_var : Warning<
   "undeclared variable %0 used as an argument for '#pragma unused'">,

diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 4e4fcb53474ec..e44ef6bb8f19d 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -199,6 +199,7 @@ class Parser : public CodeCompletionHandler {
   std::unique_ptr<PragmaHandler> MSFunction;
   std::unique_ptr<PragmaHandler> MSOptimize;
   std::unique_ptr<PragmaHandler> MSFenvAccess;
+  std::unique_ptr<PragmaHandler> MSAllocText;
   std::unique_ptr<PragmaHandler> CUDAForceHostDeviceHandler;
   std::unique_ptr<PragmaHandler> OptimizeHandler;
   std::unique_ptr<PragmaHandler> LoopHintHandler;
@@ -725,6 +726,8 @@ class Parser : public CodeCompletionHandler {
                              SourceLocation PragmaLocation);
   bool HandlePragmaMSFunction(StringRef PragmaName,
                               SourceLocation PragmaLocation);
+  bool HandlePragmaMSAllocText(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 13d403bf8db94..e9bd7569984c9 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -724,6 +724,9 @@ class Sema final {
   StringLiteral *CurInitSeg;
   SourceLocation CurInitSegLoc;
 
+  /// Sections used with #pragma alloc_text.
+  llvm::StringMap<std::tuple<StringRef, SourceLocation>> FunctionToSectionMap;
+
   /// VisContext - Manages the stack for \#pragma GCC visibility.
   void *VisContext; // Really a "PragmaVisStack*"
 
@@ -10200,6 +10203,12 @@ class Sema final {
   void ActOnPragmaMSInitSeg(SourceLocation PragmaLocation,
                             StringLiteral *SegmentName);
 
+  /// Called on well-formed \#pragma alloc_text().
+  void ActOnPragmaMSAllocText(
+      SourceLocation PragmaLocation, StringRef Section,
+      const SmallVector<std::tuple<IdentifierInfo *, SourceLocation>>
+          &Functions);
+
   /// Called on #pragma clang __debug dump II
   void ActOnPragmaDump(Scope *S, SourceLocation Loc, IdentifierInfo *II);
 
@@ -10341,6 +10350,11 @@ class Sema final {
   /// with attribute optnone.
   void AddRangeBasedOptnone(FunctionDecl *FD);
 
+  /// Only called on function definitions; if there is a `#pragma alloc_text`
+  /// that decides which code section the function should be in, add
+  /// attribute section to the function.
+  void AddSectionMSAllocText(FunctionDecl *FD);
+
   /// Adds the 'optnone' attribute to the function declaration if there
   /// are no conflicts; Loc represents the location causing the 'optnone'
   /// attribute to be added (usually because of a pragma).

diff  --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index cf637fc0d80b7..f8e311c7c35ad 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -447,6 +447,8 @@ void Parser::initializePragmaHandlers() {
     PP.AddPragmaHandler(MSSection.get());
     MSFunction = std::make_unique<PragmaMSPragma>("function");
     PP.AddPragmaHandler(MSFunction.get());
+    MSAllocText = std::make_unique<PragmaMSPragma>("alloc_text");
+    PP.AddPragmaHandler(MSAllocText.get());
     MSRuntimeChecks = std::make_unique<PragmaMSRuntimeChecksHandler>();
     PP.AddPragmaHandler(MSRuntimeChecks.get());
     MSIntrinsic = std::make_unique<PragmaMSIntrinsicHandler>();
@@ -558,6 +560,8 @@ void Parser::resetPragmaHandlers() {
     MSSection.reset();
     PP.RemovePragmaHandler(MSFunction.get());
     MSFunction.reset();
+    PP.RemovePragmaHandler(MSAllocText.get());
+    MSAllocText.reset();
     PP.RemovePragmaHandler(MSRuntimeChecks.get());
     MSRuntimeChecks.reset();
     PP.RemovePragmaHandler(MSIntrinsic.get());
@@ -918,7 +922,8 @@ void Parser::HandlePragmaMSPragma() {
           .Case("code_seg", &Parser::HandlePragmaMSSegment)
           .Case("section", &Parser::HandlePragmaMSSection)
           .Case("init_seg", &Parser::HandlePragmaMSInitSeg)
-          .Case("function", &Parser::HandlePragmaMSFunction);
+          .Case("function", &Parser::HandlePragmaMSFunction)
+          .Case("alloc_text", &Parser::HandlePragmaMSAllocText);
 
   if (!(this->*Handler)(PragmaName, PragmaLocation)) {
     // Pragma handling failed, and has been diagnosed.  Slurp up the tokens
@@ -1155,6 +1160,65 @@ bool Parser::HandlePragmaMSInitSeg(StringRef PragmaName,
   return true;
 }
 
+bool Parser::HandlePragmaMSAllocText(StringRef PragmaName,
+                                     SourceLocation PragmaLocation) {
+  Token FirstTok = Tok;
+  if (ExpectAndConsume(tok::l_paren, diag::warn_pragma_expected_lparen,
+                       PragmaName))
+    return false;
+
+  StringRef Section;
+  if (Tok.is(tok::string_literal)) {
+    ExprResult StringResult = ParseStringLiteralExpression();
+    if (StringResult.isInvalid())
+      return false; // Already diagnosed.
+    StringLiteral *SegmentName = cast<StringLiteral>(StringResult.get());
+    if (SegmentName->getCharByteWidth() != 1) {
+      PP.Diag(PragmaLocation, diag::warn_pragma_expected_non_wide_string)
+          << PragmaName;
+      return false;
+    }
+    Section = SegmentName->getString();
+  } else if (Tok.is(tok::identifier)) {
+    Section = Tok.getIdentifierInfo()->getName();
+    PP.Lex(Tok);
+  } else {
+    PP.Diag(PragmaLocation, diag::warn_pragma_expected_section_name)
+        << PragmaName;
+    return false;
+  }
+
+  if (ExpectAndConsume(tok::comma, diag::warn_pragma_expected_comma,
+                       PragmaName))
+    return false;
+
+  SmallVector<std::tuple<IdentifierInfo *, SourceLocation>> Functions;
+  while (true) {
+    if (Tok.isNot(tok::identifier)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+          << PragmaName;
+      return false;
+    }
+
+    IdentifierInfo *II = Tok.getIdentifierInfo();
+    Functions.emplace_back(II, Tok.getLocation());
+
+    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.ActOnPragmaMSAllocText(FirstTok.getLocation(), Section, Functions);
+  return true;
+}
+
 namespace {
 struct PragmaLoopHintInfo {
   Token PragmaName;

diff  --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index d4d773bd4e7b7..9681117abe424 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -741,6 +741,37 @@ void Sema::ActOnPragmaMSInitSeg(SourceLocation PragmaLocation,
   CurInitSegLoc = PragmaLocation;
 }
 
+void Sema::ActOnPragmaMSAllocText(
+    SourceLocation PragmaLocation, StringRef Section,
+    const SmallVector<std::tuple<IdentifierInfo *, SourceLocation>>
+        &Functions) {
+  if (!CurContext->getRedeclContext()->isFileContext()) {
+    Diag(PragmaLocation, diag::err_pragma_expected_file_scope) << "alloc_text";
+    return;
+  }
+
+  for (auto &Function : Functions) {
+    IdentifierInfo *II;
+    SourceLocation Loc;
+    std::tie(II, Loc) = Function;
+
+    DeclarationName DN(II);
+    NamedDecl *ND = LookupSingleName(TUScope, DN, Loc, LookupOrdinaryName);
+    if (!ND) {
+      Diag(Loc, diag::err_undeclared_use) << II->getName();
+      return;
+    }
+
+    DeclContext *DC = ND->getDeclContext();
+    if (!DC->isExternCContext()) {
+      Diag(Loc, diag::err_pragma_alloc_text_c_linkage);
+      return;
+    }
+
+    FunctionToSectionMap[II->getName()] = std::make_tuple(Section, Loc);
+  }
+}
+
 void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope,
                              SourceLocation PragmaLoc) {
 
@@ -1082,6 +1113,22 @@ void Sema::AddRangeBasedOptnone(FunctionDecl *FD) {
     AddOptnoneAttributeIfNoConflicts(FD, OptimizeOffPragmaLocation);
 }
 
+void Sema::AddSectionMSAllocText(FunctionDecl *FD) {
+  if (!FD->getIdentifier())
+    return;
+
+  StringRef Name = FD->getName();
+  auto It = FunctionToSectionMap.find(Name);
+  if (It != FunctionToSectionMap.end()) {
+    StringRef Section;
+    SourceLocation Loc;
+    std::tie(Section, Loc) = It->second;
+
+    if (!FD->hasAttr<SectionAttr>())
+      FD->addAttr(SectionAttr::CreateImplicit(Context, Section));
+  }
+}
+
 void Sema::AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD,
                                             SourceLocation Loc) {
   // Don't add a conflicting attribute. No diagnostic is needed.

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 315f328fca6a1..426985fea64b0 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10219,6 +10219,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
   if (D.isFunctionDefinition()) {
     AddRangeBasedOptnone(NewFD);
     AddImplicitMSFunctionNoBuiltinAttr(NewFD);
+    AddSectionMSAllocText(NewFD);
   }
 
   // If this is the first declaration of an extern C variable, update

diff  --git a/clang/test/CodeGen/msvc_pragma_alloc_text.cpp b/clang/test/CodeGen/msvc_pragma_alloc_text.cpp
new file mode 100644
index 0000000000000..2703fbe6efc06
--- /dev/null
+++ b/clang/test/CodeGen/msvc_pragma_alloc_text.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -fms-extensions -S -emit-llvm -o - %s | FileCheck %s
+
+extern "C" {
+
+void foo();
+void foo1();
+void foo2();
+void foo3();
+
+#pragma alloc_text("abc", foo, foo1)
+#pragma alloc_text("def", foo2)
+#pragma alloc_text("def", foo3)
+
+// CHECK-LABEL: define{{.*}} void @foo() {{.*}} section "abc"
+void foo() {}
+
+// CHECK-LABEL: define{{.*}} void @foo1() {{.*}} section "abc"
+void foo1() {}
+
+// CHECK-LABEL: define{{.*}} void @foo2() {{.*}} section "def"
+void foo2() {}
+
+// CHECK-LABEL: define{{.*}} void @foo3() {{.*}} section "def"
+void foo3() {}
+}

diff  --git a/clang/test/Sema/pragma-ms-alloc-text.cpp b/clang/test/Sema/pragma-ms-alloc-text.cpp
new file mode 100644
index 0000000000000..e1fb0ab4b2538
--- /dev/null
+++ b/clang/test/Sema/pragma-ms-alloc-text.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify %s
+
+#pragma alloc_text()        // expected-warning {{expected a string literal for the section name in '#pragma alloc_text'}}
+#pragma alloc_text(a        // expected-warning {{expected ',' in '#pragma alloc_text'}}
+#pragma alloc_text(a, a     // expected-warning {{missing ')' after '#pragma alloc_text'}}
+#pragma alloc_text(a, a)    // expected-error {{use of undeclared a}}
+#pragma alloc_text(L"a", a) // expected-warning {{expected a string literal for the section name}}
+
+void foo();
+#pragma alloc_text(a, foo) // expected-error {{'#pragma alloc_text' is applicable only to functions with C linkage}}
+
+extern "C" void foo1();
+#pragma alloc_text(a, foo1)      // no-warning
+#pragma alloc_text(a, foo1) asdf // expected-warning {{extra tokens at end of '#pragma alloc_text'}}
+#pragma alloc_text(a, foo1       // expected-warning {{missing ')' after '#pragma alloc_text'}}
+
+namespace N {
+#pragma alloc_text(b, foo1) // no-warning
+}
+
+extern "C" {
+void foo2();
+#pragma alloc_text(a, foo2) // no-warning
+}
+
+void foo3() {
+#pragma alloc_text(a, foo1) // expected-error {{'#pragma alloc_text' can only appear at file scope}}
+}
+
+extern "C" void foo4();
+#pragma alloc_text(c, foo4) // no-warning
+void foo4() {}
+
+void foo5();                // expected-note {{previous declaration is here}}
+#pragma alloc_text(c, foo5) // expected-error {{'#pragma alloc_text' is applicable only to functions with C linkage}}
+extern "C" void foo5() {}   // expected-error {{declaration of 'foo5' has a 
diff erent language linkage}}


        


More information about the cfe-commits mailing list