diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -298,6 +298,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/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1117,6 +1117,13 @@ "expected 'compiler', 'lib', 'user', or a string literal for the section name in '#pragma %0' - ignored">, InGroup<IgnoredPragmas>; +def warn_pragma_unsupported_char_msvc_optimize : Warning< + "unsupported character '%0' in optimization list for '#pragma optimize'">; +def warn_pragma_invalid_char_msvc_optimize : Warning< + "invalid character '%0' in optimization list for '#pragma optimize'">; +def warn_pragma_deprecated_char_msvc_optimize : Warning< + "deprecated character '%0' in optimization list for '#pragma optimize'">; + def err_pragma_expected_integer : Error<"expected an integer argument in '#pragma %0'">; def warn_pragma_expected_integer : Warning< "expected integer between %0 and %1 inclusive in '#pragma %2' - ignored">, 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,15 @@ /// optimizations are currently "on", this is set to an invalid location. SourceLocation OptimizeOffPragmaLocation; + /// The list of valid optimization parameters passed by \#pragma optimize. + std::string MSPragmaOptimizeValidParams; + /// The "on" or "off" argument passed by \#pragma optimize, that denotes + /// whether the optimizations in the list above should be turned off or on. + /// This boolean is true by default because command line options are honored + /// when `#pragma optimize("", on)`. + /// (i.e. `ModifyFnAttributeMSPragmaOptimze()` does nothing) + bool MSPragmaOptimizeIsOn = true; + /// Set of no-builtin functions listed by \#pragma function. llvm::SmallSetVector<StringRef, 4> MSFunctionNoBuiltins; @@ -10334,6 +10343,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 IsOn, + std::string ValidOptimizationList); + /// Call on well formed \#pragma function. void ActOnPragmaMSFunction(SourceLocation Loc, @@ -10360,6 +10373,11 @@ /// attribute to be added (usually because of a pragma). void AddOptnoneAttributeIfNoConflicts(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<PragmaMSPragma>("alloc_text"); PP.AddPragmaHandler(MSAllocText.get()); + MSOptimize = std::make_unique<PragmaMSPragma>("optimize"); + PP.AddPragmaHandler(MSOptimize.get()); MSRuntimeChecks = std::make_unique<PragmaMSRuntimeChecksHandler>(); PP.AddPragmaHandler(MSRuntimeChecks.get()); MSIntrinsic = std::make_unique<PragmaMSIntrinsicHandler>(); PP.AddPragmaHandler(MSIntrinsic.get()); - MSOptimize = std::make_unique<PragmaMSOptimizeHandler>(); - PP.AddPragmaHandler(MSOptimize.get()); MSFenvAccess = std::make_unique<PragmaMSFenvAccessHandler>(); 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,73 @@ } // #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 = ParseStringLiteralExpression(); + if (StringResult.isInvalid()) + return false; // Already diagnosed. + StringLiteral *OptimizationList = cast<StringLiteral>(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; + std::string ValidOptimizationList; + for (auto &c : OptimizationList->getString()) { + // Deprecated parameters: g + if (c == 'g') + PP.Diag(PragmaLocation, diag::warn_pragma_deprecated_char_msvc_optimize) + << std::string(1, c); + // Unsupported parameters (supported by MSVC): y + else if (c == 'y') + PP.Diag(PragmaLocation, diag::warn_pragma_unsupported_char_msvc_optimize) + << std::string(1, c); + // Supported parameters: s, t + else if (c != 's' || c != 't') + PP.Diag(PragmaLocation, diag::warn_pragma_invalid_char_msvc_optimize) + << std::string(1, c); + else + ValidOptimizationList.push_back(c); } - PP.Lex(Tok); - 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 (ExpectAndConsume(tok::comma, diag::warn_pragma_expected_comma, + PragmaName)) + return false; + + 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, + ValidOptimizationList); + 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,17 @@ OptimizeOffPragmaLocation = PragmaLoc; } +void Sema::ActOnPragmaMSOptimize(SourceLocation Loc, bool IsOn, + std::string ValidOptimizationList) { + if (!CurContext->getRedeclContext()->isFileContext()) { + Diag(Loc, diag::err_pragma_expected_file_scope) << "optimize"; + return; + } + + MSPragmaOptimizeValidParams = ValidOptimizationList; + MSPragmaOptimizeIsOn = IsOn; +} + void Sema::ActOnPragmaMSFunction( SourceLocation Loc, const llvm::SmallVectorImpl<StringRef> &NoBuiltins) { if (!CurContext->getRedeclContext()->isFileContext()) { @@ -1129,6 +1140,33 @@ } } +void Sema::ModifyFnAttributesMSPragmaOptimize(FunctionDecl *FD) { + if (MSPragmaOptimizeValidParams.empty()) + // Don't modify the function attributes if it's "on". "on" resets the + // optimizations to the ones listed on the command line + if (!MSPragmaOptimizeIsOn) { + AddOptnoneAttributeIfNoConflicts(FD, FD->getBeginLoc()); + return; + } + + for (auto &c : MSPragmaOptimizeValidParams) { + FD->dropAttr<OptimizeAttr>(); + if (MSPragmaOptimizeIsOn) { + if (c == 's') { + FD->addAttr( + OptimizeAttr::CreateImplicit(Context, "", OptimizeAttr::Os)); + } else if (c == 't') { + // OptimizeAttr::O2 should be a noop + FD->addAttr( + OptimizeAttr::CreateImplicit(Context, "", OptimizeAttr::O2)); + } + } else { + // If turning off, add optnone for both "s" and "t" cases + AddOptnoneAttributeIfNoConflicts(FD, FD->getBeginLoc()); + } + } +} + 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 @@ -10192,6 +10192,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'}}