diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3793,6 +3793,43 @@ these two instantiations, ``twice`` will be optimized (because its definition was outside the region) and ``thrice`` will not be optimized. +Clang also implements MSVC's range-based pragma, +``#pragma optimize("[optimization-list]", on | off)``. At the moment, Clang only +supports an empty optimization list, whereas MSVC supports the parameters, ``s``, +``g``, ``t``, and ``y``. Clang's current implementation of the pragma behaves in +the same way as the clang pragma, ``#pragma clang optimize``. All functions +between ``off`` and ``on`` will be decorated with the ``optnone`` attribute. + +.. code-block:: c++ + + #pragma optimize("", off) + // This function will be decorated with optnone. + void f1() {} + + #pragma optimize("", on) + // This function will be optimized with whatever was specified on + // the commandline. + void f2() {} + + // This will warn with Clang's current implementation. + #pragma optimize("g", on) + void f3() {} + +For MSVC, an empty optimization list and ``off`` parameter will turn off +all optimizations, ``s``, ``g``, ``t``, and ``y``. An empty optimization and +``on`` parameter will reset the optimizations to the ones specified on the +commandline. + +.. list-table:: Parameters (unsupported by Clang) + * - Parameter + - Type of optimization + * - g + - Deprecated + * - s or t + - Short or fast sequences of machine code + * - y + - Enable frame pointers + Extensions for loop hint optimizations ====================================== 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,10 @@ - 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 an empty optimization list 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. At the moment, only an empty list is + supported. - ... 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 @@ -1169,10 +1169,6 @@ def warn_pragma_intrinsic_builtin : Warning< "%0 is not a recognized builtin%select{|; consider including to access non-builtin intrinsics}1">, InGroup; -// - #pragma optimize -def warn_pragma_optimize : Warning< - "'#pragma optimize' is not supported">, - InGroup; // - #pragma unused def warn_pragma_unused_expected_var : Warning< "expected '#pragma unused' argument to be a variable name">, 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,13 @@ /// optimizations are currently "on", this is set to an invalid location. SourceLocation OptimizeOffPragmaLocation; + /// The "on" or "off" argument passed by \#pragma optimize, that denotes + /// whether the optimizations in the list passed to the pragma 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 MSFunctionNoBuiltins; @@ -10334,6 +10341,9 @@ /// 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); + /// Call on well formed \#pragma function. void ActOnPragmaMSFunction(SourceLocation Loc, @@ -10360,6 +10370,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("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,64 @@ } // #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; } - // 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; + ExprResult StringResult = 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; } - 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)) + return false; - if (Tok.isNot(tok::eod)) { - PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) - << "optimize"; - return; + // TODO: Add support for "sgty" + if (!OptimizationList->getString().empty()) { + PP.Diag(PragmaLocation, diag::warn_pragma_invalid_argument) + << OptimizationList->getString() << PragmaName << /*Expected=*/true + << "\"\""; + return false; } - PP.Diag(StartLoc, diag::warn_pragma_optimize); + + if (ExpectAndConsume(tok::eof, diag::warn_pragma_extra_tokens_at_eol, + PragmaName)) + return false; + + Actions.ActOnPragmaMSOptimize(FirstTok.getLocation(), IsOn); + 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,15 @@ OptimizeOffPragmaLocation = PragmaLoc; } +void Sema::ActOnPragmaMSOptimize(SourceLocation Loc, bool IsOn) { + if (!CurContext->getRedeclContext()->isFileContext()) { + Diag(Loc, diag::err_pragma_expected_file_scope) << "optimize"; + return; + } + + MSPragmaOptimizeIsOn = IsOn; +} + void Sema::ActOnPragmaMSFunction( SourceLocation Loc, const llvm::SmallVectorImpl &NoBuiltins) { if (!CurContext->getRedeclContext()->isFileContext()) { @@ -1129,6 +1138,13 @@ } } +void Sema::ModifyFnAttributesMSPragmaOptimize(FunctionDecl *FD) { + // 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()); +} + 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,20 @@ +// RUN: %clang_cc1 -O2 -emit-llvm -fms-extensions -o - %s | FileCheck %s + +#pragma optimize("", off) + +// CHECK: define{{.*}} void @f0(){{.*}} #[[OPTNONE:[0-9]+]] +void f0() {} + +// CHECK: define{{.*}} void @f1(){{.*}} #[[OPTNONE]] +void f1() {} + +#pragma optimize("", on) + +// CHECK: define{{.*}} void @f2(){{.*}} #[[NO_OPTNONE:[0-9]+]] +void f2() {} + +// CHECK: define{{.*}} void @f3(){{.*}} #[[NO_OPTNONE]] +void f3() {} + +// CHECK: attributes #[[OPTNONE]] = {{{.*}}optnone{{.*}}} +// CHECK-NOT: attributes #[[NO_OPTNONE]] = {{{.*}}optnone{{.*}}} 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,13 @@ #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) // expected-warning{{unexpected argument 'g' to '#pragma optimize'; expected ""}} +#pragma optimize("",on) // no-warning +#pragma optimize("", on) asdf // expected-warning{{extra tokens at end of '#pragma optimize'}} + +void pragma_optimize_foo() { +#pragma optimize("", 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'}}