[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