diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -284,6 +284,9 @@ - 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. +- Added support for MSVC's ``#pragma optimize``. The pragma takes a list of + optimizations to turn on or off which applies to all functions following the + pragma. - ... 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 @@ -728,6 +728,8 @@ SourceLocation PragmaLocation); bool HandlePragmaMSAllocText(StringRef PragmaName, SourceLocation PragmaLocation); + bool HandlePragmaMSOptimize(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 @@ -758,6 +758,11 @@ /// optimizations are currently "on", this is set to an invalid location. SourceLocation OptimizeOffPragmaLocation; + /// This represents the optimizations to add or remove after seeing a + /// "#pragma optimize("[optimization-list]", on | off)". + std::vector + MSOptimizeOperations; + /// Set of no-builtin functions listed by \#pragma function. llvm::SmallSetVector MSFunctionNoBuiltins; @@ -10334,6 +10339,10 @@ /// Called on well formed \#pragma clang optimize. void ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc); + /// #pragma optimize("[optimization-list]", on | off). + void ActOnPragmaMSOptimize(SourceLocation Loc, bool On, + StringRef OptimizationList); + /// Call on well formed \#pragma function. void ActOnPragmaMSFunction(SourceLocation Loc, @@ -10360,6 +10369,19 @@ /// attribute to be added (usually because of a pragma). void AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD, SourceLocation Loc); + /// Operations used by MSVC #pragma optimize("sgty", on | off) to turn + /// optimizations on or off. + void AddFramePointerNonLeaf(FunctionDecl *FD, SourceLocation Loc); + void AddFramePointerNone(FunctionDecl *FD, SourceLocation Loc); + void AddOptsize(FunctionDecl *FD, SourceLocation Loc); + void RemoveOptsize(FunctionDecl *FD, SourceLocation Loc); + void RemoveOptnone(FunctionDecl *FD, SourceLocation Loc); + + /// Only called on function definitions; if there is a MSVC #pragma optimize + /// in scope, consider changing the function's attributes based on the + /// optimization list passed to the pragma. + void ModifyFnAttributesMSPragmaOptimize(FunctionDecl *FD); + /// 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. 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 @@ -255,12 +255,6 @@ Token &FirstToken) override; }; -struct PragmaMSOptimizeHandler : public PragmaHandler { - PragmaMSOptimizeHandler() : PragmaHandler("optimize") {} - void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, - Token &FirstToken) override; -}; - // "\#pragma fenv_access (on)". struct PragmaMSFenvAccessHandler : public PragmaHandler { PragmaMSFenvAccessHandler() : PragmaHandler("fenv_access") {} @@ -449,12 +443,12 @@ PP.AddPragmaHandler(MSFunction.get()); MSAllocText = std::make_unique("alloc_text"); PP.AddPragmaHandler(MSAllocText.get()); + MSOptimize = std::make_unique("optimize"); + PP.AddPragmaHandler(MSOptimize.get()); MSRuntimeChecks = std::make_unique(); PP.AddPragmaHandler(MSRuntimeChecks.get()); MSIntrinsic = std::make_unique(); PP.AddPragmaHandler(MSIntrinsic.get()); - MSOptimize = std::make_unique(); - PP.AddPragmaHandler(MSOptimize.get()); MSFenvAccess = std::make_unique(); PP.AddPragmaHandler(MSFenvAccess.get()); } @@ -923,7 +917,8 @@ .Case("section", &Parser::HandlePragmaMSSection) .Case("init_seg", &Parser::HandlePragmaMSInitSeg) .Case("function", &Parser::HandlePragmaMSFunction) - .Case("alloc_text", &Parser::HandlePragmaMSAllocText); + .Case("alloc_text", &Parser::HandlePragmaMSAllocText) + .Case("optimize", &Parser::HandlePragmaMSOptimize); if (!(this->*Handler)(PragmaName, PragmaLocation)) { // Pragma handling failed, and has been diagnosed. Slurp up the tokens @@ -3653,57 +3648,56 @@ } // #pragma optimize("gsty", on|off) -void PragmaMSOptimizeHandler::HandlePragma(Preprocessor &PP, - PragmaIntroducer Introducer, - Token &Tok) { - SourceLocation StartLoc = Tok.getLocation(); - PP.Lex(Tok); - - if (Tok.isNot(tok::l_paren)) { - PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen) << "optimize"; - return; - } - PP.Lex(Tok); +bool Parser::HandlePragmaMSOptimize(StringRef PragmaName, + SourceLocation PragmaLocation) { + Token FirstTok = Tok; + if (ExpectAndConsume(tok::l_paren, diag::warn_pragma_expected_lparen, + PragmaName)) + return false; if (Tok.isNot(tok::string_literal)) { - PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_string) << "optimize"; - return; + PP.Diag(PragmaLocation, diag::warn_pragma_expected_string) << PragmaName; + return false; + } + ExprResult StringResult = Parser::ParseStringLiteralExpression(); + if (StringResult.isInvalid()) + return false; // Already diagnosed. + StringLiteral *OptimizationList = cast(StringResult.get()); + if (OptimizationList->getCharByteWidth() != 1) { + PP.Diag(PragmaLocation, diag::warn_pragma_expected_non_wide_string) + << PragmaName; + return false; } // We could syntax check the string but it's probably not worth the effort. - PP.Lex(Tok); - if (Tok.isNot(tok::comma)) { - PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_comma) << "optimize"; - return; - } - PP.Lex(Tok); + if (ExpectAndConsume(tok::comma, diag::warn_pragma_expected_comma, + PragmaName)) + return false; - if (Tok.is(tok::eod) || Tok.is(tok::r_paren)) { - PP.Diag(Tok.getLocation(), diag::warn_pragma_missing_argument) - << "optimize" << /*Expected=*/true << "'on' or 'off'"; - return; + if (Tok.is(tok::eof) || Tok.is(tok::r_paren)) { + PP.Diag(PragmaLocation, diag::warn_pragma_missing_argument) + << PragmaName << /*Expected=*/true << "'on' or 'off'"; + return false; } IdentifierInfo *II = Tok.getIdentifierInfo(); if (!II || (!II->isStr("on") && !II->isStr("off"))) { - PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument) - << PP.getSpelling(Tok) << "optimize" << /*Expected=*/true + PP.Diag(PragmaLocation, diag::warn_pragma_invalid_argument) + << PP.getSpelling(Tok) << PragmaName << /*Expected=*/true << "'on' or 'off'"; - return; + return false; } + bool IsOn = II->isStr("on"); PP.Lex(Tok); - if (Tok.isNot(tok::r_paren)) { - PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen) << "optimize"; - return; - } - 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; - if (Tok.isNot(tok::eod)) { - PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) - << "optimize"; - return; - } - PP.Diag(StartLoc, diag::warn_pragma_optimize); + Actions.ActOnPragmaMSOptimize(FirstTok.getLocation(), IsOn, + OptimizationList->getString()); + return true; } void PragmaForceCUDAHostDeviceHandler::HandlePragma( 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 @@ -1096,6 +1096,44 @@ OptimizeOffPragmaLocation = PragmaLoc; } +void Sema::ActOnPragmaMSOptimize(SourceLocation Loc, bool On, + StringRef OptimizationList) { + if (!CurContext->getRedeclContext()->isFileContext()) { + Diag(Loc, diag::err_pragma_expected_file_scope) << "optimize"; + return; + } + + if (OptimizationList.empty()) { + MSOptimizeOperations.clear(); + if (!On) { + MSOptimizeOperations.push_back(&Sema::AddOptnoneAttributeIfNoConflicts); + MSOptimizeOperations.push_back(&Sema::AddFramePointerNone); + MSOptimizeOperations.push_back(&Sema::RemoveOptsize); + } + return; + } + + static std::pair + Operations[][3] = { + { + // OFF + {'s', &Sema::RemoveOptsize}, + {'y', &Sema::AddFramePointerNone}, + {'t', &Sema::AddOptnoneAttributeIfNoConflicts}, + }, + { + // ON + {'s', &Sema::AddOptsize}, + {'y', &Sema::AddFramePointerNonLeaf}, + {'t', &Sema::RemoveOptnone}, + }, + }; + + for (auto &Operation : Operations[On]) + if (OptimizationList.find(Operation.first) != std::string::npos) + MSOptimizeOperations.push_back(Operation.second); +} + void Sema::ActOnPragmaMSFunction( SourceLocation Loc, const llvm::SmallVectorImpl &NoBuiltins) { if (!CurContext->getRedeclContext()->isFileContext()) { @@ -1129,8 +1167,15 @@ } } +void Sema::ModifyFnAttributesMSPragmaOptimize(FunctionDecl *FD) { + for (auto &F : MSOptimizeOperations) + if (F) + (this->*F)(FD, SourceLocation()); +} + void Sema::AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD, SourceLocation Loc) { + FD->dropAttr(); // Don't add a conflicting attribute. No diagnostic is needed. if (FD->hasAttr() || FD->hasAttr()) return; @@ -1150,6 +1195,35 @@ FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size())); } +void Sema::AddFramePointerNonLeaf(FunctionDecl *FD, SourceLocation Loc) { + FD->dropAttr(); + FramePointerAttr::Kind Kind = FramePointerAttr::NonLeaf; + FD->addAttr(FramePointerAttr::CreateImplicit(Context, Kind)); +} + +void Sema::AddFramePointerNone(FunctionDecl *FD, SourceLocation Loc) { + FD->dropAttr(); + FramePointerAttr::Kind Kind = FramePointerAttr::None; + FD->addAttr(FramePointerAttr::CreateImplicit(Context, Kind)); +} + +void Sema::AddOptsize(FunctionDecl *FD, SourceLocation Loc) { + FD->dropAttr(); + OptimizeSizeAttr::Kind Kind = OptimizeSizeAttr::On; + FD->addAttr(OptimizeSizeAttr::CreateImplicit(Context, Kind)); +} + +void Sema::RemoveOptsize(FunctionDecl *FD, SourceLocation Loc) { + FD->dropAttr(); + OptimizeSizeAttr::Kind Kind = OptimizeSizeAttr::Off; + FD->addAttr(OptimizeSizeAttr::CreateImplicit(Context, Kind)); +} + +void Sema::RemoveOptnone(FunctionDecl *FD, SourceLocation Loc) { + FD->dropAttr(); + FD->addAttr(NeverOptimizeNoneAttr::CreateImplicit(Context)); +} + typedef std::vector > VisStack; enum : unsigned { NoVisibility = ~0U }; 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,6 +10220,7 @@ AddRangeBasedOptnone(NewFD); AddImplicitMSFunctionNoBuiltinAttr(NewFD); AddSectionMSAllocText(NewFD); + ModifyFnAttributesMSPragmaOptimize(NewFD); } // If this is the first declaration of an extern C variable, update diff --git a/clang/test/CodeGen/pragma-msvc-optimize.c b/clang/test/CodeGen/pragma-msvc-optimize.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/pragma-msvc-optimize.c @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -O2 -emit-llvm -fms-extensions -o - %s | FileCheck %s -check-prefix CHECK-O2 +// RUN: %clang_cc1 -O0 -emit-llvm -fms-extensions -o - %s | FileCheck %s -check-prefix CHECK-O0 +// RUN: %clang_cc1 -Os -emit-llvm -fms-extensions -o - %s | FileCheck %s -check-prefix CHECK-Os + +#pragma optimize("s", on) + +// CHECK-O2: define{{.*}} void @f0(){{.*}} #[[OPTSIZE:[0-9]+]] +void f0() {} + +#pragma optimize("y", on) + +// CHECK-O2: define{{.*}} void @f1(){{.*}} #[[OPTSIZE_FRAMEPTR_NONLEAF:[0-9]+]] +void f1() {} + +#pragma optimize("t", on) + +// CHECK-O2: define{{.*}} void @f2(){{.*}} #[[OPTSIZE_FRAMEPTR_NONLEAF]] +// CHECK-O0: define{{.*}} void @f2(){{.*}} #[[NO_OPTNONE:[0-9]+]] +void f2() {} + +#pragma optimize("s", off) + +// CHECK-O2: define{{.*}} void @f3(){{.*}} #[[FRAMEPTR_NONLEAF:[0-9]+]] +// CHECK-Os: define{{.*}} void @f3(){{.*}} #[[FRAMEPTR_NONLEAF:[0-9]+]] +void f3() {} + +#pragma optimize("y", off) + +// CHECK-O2: define{{.*}} void @f4(){{.*}} #[[FRAMEPTR_NONE:[0-9]+]] +void f4() {} + +#pragma optimize("t", off) + +// CHECK-O2: define{{.*}} void @f5(){{.*}} #[[OPTNONE_FRAMEPTR_NONE:[0-9]+]] +void f5() {} + +// CHECK-O2: attributes #[[OPTSIZE]] = {{{.*}}optsize{{.*}}} +// CHECK-O2-NOT: attributes #[[OPTSIZE]] = {{{.*}}optsize{{.*}}"frame-pointer"="non-leaf"{{.*}}} +// CHECK-O2: attributes #[[OPTSIZE_FRAMEPTR_NONLEAF]] = {{{.*}}optsize{{.*}}"frame-pointer"="non-leaf"{{.*}}} +// CHECK-O2: attributes #[[FRAMEPTR_NONLEAF]] = {{{.*}}"frame-pointer"="non-leaf"{{.*}}} +// CHECK-O2-NOT: attributes #[[FRAMEPTR_NONLEAF]] = {{{.*}}optsize{{.*}}"frame-pointer"="non-leaf"{{.*}}} +// CHECK-O2: attributes #[[FRAMEPTR_NONE]] = {{{.*}}"frame-pointer"="none"{{.*}}} +// CHECK-O2: attributes #[[OPTNONE_FRAMEPTR_NONE]] = {{{.*}}optnone{{.*}}"frame-pointer"="none"{{.*}}} + +// CHECK-O0-NOT: attributes #[[NO_OPTNONE]] = {{{.*}}optnone{{.*}}} + +// CHECK-Os: attributes #[[FRAMEPTR_NONLEAF]] = {{{.*}}"frame-pointer"="non-leaf"{{.*}}} +// CHECK-Os-NOT: attributes #[[FRAMEPTR_NONLEAF]] = {{{.*}}optsize{{.*}}"frame-pointer"="non-leaf"{{.*}}} diff --git a/clang/test/Preprocessor/pragma_microsoft.c b/clang/test/Preprocessor/pragma_microsoft.c --- a/clang/test/Preprocessor/pragma_microsoft.c +++ b/clang/test/Preprocessor/pragma_microsoft.c @@ -228,7 +228,12 @@ #pragma optimize("g" // expected-warning{{expected ',' in '#pragma optimize'}} #pragma optimize("g", // expected-warning{{missing argument to '#pragma optimize'; expected 'on' or 'off'}} #pragma optimize("g",xyz // expected-warning{{unexpected argument 'xyz' to '#pragma optimize'; expected 'on' or 'off'}} -#pragma optimize("g",on) // expected-warning{{#pragma optimize' is not supported}} +#pragma optimize("g",on) // no-warning +#pragma optimize("g", on) asdf // expected-warning{{extra tokens at end of '#pragma optimize'}} + +void pragma_optimize_foo() { +#pragma optimize("g", on) // expected-error {{'#pragma optimize' can only appear at file scope}} +} #pragma execution_character_set // expected-warning {{expected '('}} #pragma execution_character_set( // expected-warning {{expected 'push' or 'pop'}}