diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -281,6 +281,9 @@ - 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 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -990,6 +990,8 @@ 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 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -199,6 +199,7 @@ std::unique_ptr MSFunction; std::unique_ptr MSOptimize; std::unique_ptr MSFenvAccess; + std::unique_ptr MSAllocText; std::unique_ptr CUDAForceHostDeviceHandler; std::unique_ptr OptimizeHandler; std::unique_ptr LoopHintHandler; @@ -725,6 +726,8 @@ 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 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -724,6 +724,9 @@ StringLiteral *CurInitSeg; SourceLocation CurInitSegLoc; + /// Sections used with #pragma alloc_text. + llvm::StringMap> FunctionToSectionMap; + /// VisContext - Manages the stack for \#pragma GCC visibility. void *VisContext; // Really a "PragmaVisStack*" @@ -10200,6 +10203,12 @@ void ActOnPragmaMSInitSeg(SourceLocation PragmaLocation, StringLiteral *SegmentName); + /// Called on well-formed \#pragma alloc_text(). + void ActOnPragmaMSAllocText( + SourceLocation PragmaLocation, StringRef Section, + const SmallVector> + &Functions); + /// Called on #pragma clang __debug dump II void ActOnPragmaDump(Scope *S, SourceLocation Loc, IdentifierInfo *II); @@ -10341,6 +10350,11 @@ /// 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 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -447,6 +447,8 @@ PP.AddPragmaHandler(MSSection.get()); MSFunction = std::make_unique("function"); PP.AddPragmaHandler(MSFunction.get()); + MSAllocText = std::make_unique("alloc_text"); + PP.AddPragmaHandler(MSAllocText.get()); MSRuntimeChecks = std::make_unique(); PP.AddPragmaHandler(MSRuntimeChecks.get()); MSIntrinsic = std::make_unique(); @@ -558,6 +560,8 @@ 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 @@ .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 @@ 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(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> 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 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -741,6 +741,37 @@ CurInitSegLoc = PragmaLocation; } +void Sema::ActOnPragmaMSAllocText( + SourceLocation PragmaLocation, StringRef Section, + const SmallVector> + &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 @@ 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()) + 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 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10219,6 +10219,7 @@ 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 --- /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 --- /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 different language linkage}}