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 @@ -984,6 +984,11 @@ def err_super_in_lambda_unsupported : Error< "use of '__super' inside a lambda is unsupported">; +def err_pragma_alloc_text_scope : Error< + "'#pragma alloc_text' can't be used inside a function">; +def err_pragma_alloc_text_c_linkage: Error< + "'#pragma alloc_text' is applicable only to functions declared with C linkage">; + def warn_pragma_unused_undeclared_var : Warning< "undeclared variable %0 used as an argument for '#pragma unused'">, InGroup; 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 @@ -198,6 +198,7 @@ std::unique_ptr MSIntrinsic; std::unique_ptr MSOptimize; std::unique_ptr MSFenvAccess; + std::unique_ptr MSAllocText; std::unique_ptr CUDAForceHostDeviceHandler; std::unique_ptr OptimizeHandler; std::unique_ptr LoopHintHandler; @@ -720,6 +721,8 @@ SourceLocation PragmaLocation); bool HandlePragmaMSInitSeg(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*" @@ -10189,6 +10192,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); @@ -10325,6 +10334,8 @@ /// with attribute optnone. void AddRangeBasedOptnone(FunctionDecl *FD); + 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 @@ -445,6 +445,8 @@ PP.AddPragmaHandler(MSCodeSeg.get()); MSSection = std::make_unique("section"); PP.AddPragmaHandler(MSSection.get()); + MSAllocText = std::make_unique("alloc_text"); + PP.AddPragmaHandler(MSAllocText.get()); MSRuntimeChecks = std::make_unique(); PP.AddPragmaHandler(MSRuntimeChecks.get()); MSIntrinsic = std::make_unique(); @@ -554,6 +556,8 @@ MSCodeSeg.reset(); PP.RemovePragmaHandler(MSSection.get()); MSSection.reset(); + PP.RemovePragmaHandler(MSAllocText.get()); + MSAllocText.reset(); PP.RemovePragmaHandler(MSRuntimeChecks.get()); MSRuntimeChecks.reset(); PP.RemovePragmaHandler(MSIntrinsic.get()); @@ -912,7 +916,8 @@ .Case("const_seg", &Parser::HandlePragmaMSSegment) .Case("code_seg", &Parser::HandlePragmaMSSegment) .Case("section", &Parser::HandlePragmaMSSection) - .Case("init_seg", &Parser::HandlePragmaMSInitSeg); + .Case("init_seg", &Parser::HandlePragmaMSInitSeg) + .Case("alloc_text", &Parser::HandlePragmaMSAllocText); if (!(this->*Handler)(PragmaName, PragmaLocation)) { // Pragma handling failed, and has been diagnosed. Slurp up the tokens @@ -1149,6 +1154,72 @@ return true; } +bool Parser::HandlePragmaMSAllocText(StringRef PragmaName, + SourceLocation PragmaLocation) { + Token FirstTok = Tok; + if (Tok.isNot(tok::l_paren)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen) + << PragmaName; + return false; + } + PP.Lex(Tok); + + if (Tok.isNot(tok::string_literal)) { + PP.Diag(PragmaLocation, diag::warn_pragma_expected_section_name) + << PragmaName; + return false; + } + 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; + } + + if (Tok.isNot(tok::comma)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_comma) << PragmaName; + return false; + } + PP.Lex(Tok); + + 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->getName(), Tok.getLocation()); + + PP.Lex(Tok); + if (Tok.isNot(tok::comma)) + break; + PP.Lex(Tok); + } + + if (Tok.isNot(tok::r_paren)) { + PP.Diag(PragmaLocation, diag::warn_pragma_expected_rparen) << PragmaName; + return false; + } + PP.Lex(Tok); + + if (Tok.isNot(tok::eof)) { + PP.Diag(PragmaLocation, diag::warn_pragma_extra_tokens_at_eol) + << PragmaName; + return false; + } + PP.Lex(Tok); + + Actions.ActOnPragmaMSAllocText( + FirstTok.getLocation(), SegmentName->getString(), 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,24 @@ CurInitSegLoc = PragmaLocation; } +void Sema::ActOnPragmaMSAllocText( + SourceLocation PragmaLocation, + StringRef Section, + const SmallVector> &Functions) { + if (!CurContext->getRedeclContext()->isFileContext()) { + Diag(PragmaLocation, diag::err_pragma_alloc_text_scope); + return; + } + + for (auto &Function : Functions) { + StringRef Name; + SourceLocation Loc; + std::tie(Name, Loc) = Function; + + FunctionToSectionMap[Name] = std::make_tuple(Section, Loc); + } +} + void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope, SourceLocation PragmaLoc) { @@ -1072,6 +1090,27 @@ 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->isExternC()) { + Diag(Loc, diag::err_pragma_alloc_text_c_linkage); + return; + } + + 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 @@ -10220,8 +10220,10 @@ // If this is a function definition, check if we have to apply optnone due to // a pragma. - if(D.isFunctionDefinition()) + if(D.isFunctionDefinition()) { AddRangeBasedOptnone(NewFD); + AddSectionMSAllocText(NewFD); + } // If this is the first declaration of an extern C variable, update // the map of such variables. 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,22 @@ +// RUN: %clang_cc1 -fms-extensions -S -emit-llvm -o - %s | FileCheck %s + +extern "C" void foo(); +extern "C" void foo1(); +extern "C" void foo2(); +extern "C" 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" +extern "C" void foo() {} + +// CHECK-LABEL: define{{.*}} void @foo1() {{.*}} section "abc" +extern "C" void foo1() {} + +// CHECK-LABEL: define{{.*}} void @foo2() {{.*}} section "def" +extern "C" void foo2() {} + +// CHECK-LABEL: define{{.*}} void @foo3() {{.*}} section "def" +extern "C" void foo3() {}