From 186bea3750d6b349de0e71044d95e2a42e087b4c Mon Sep 17 00:00:00 2001 From: Stephen Long Date: Wed, 22 Jun 2022 10:54:50 -0700 Subject: [PATCH] [MSVC] Add initial support for MSVC pragma optimize MSVC's pragma optimize turns optimizations on or off based on the list passed. At the moment, we only support an empty optimization list. i.e. `#pragma optimize("", on | off)` From MSVC's docs: | Parameter | Type of optimization | |-----------|--------------------------------------------------| | g | Enable global optimizations. Deprecated | | s or t | Specify short or fast sequences of machine code | | y | Generate frame pointers on the program stack | https://docs.microsoft.com/en-us/cpp/preprocessor/optimize?view=msvc-170 Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D125723 --- clang/docs/LanguageExtensions.rst | 37 +++++++++ clang/docs/ReleaseNotes.rst | 4 + clang/include/clang/Basic/DiagnosticParseKinds.td | 4 - clang/include/clang/Parse/Parser.h | 2 + clang/include/clang/Sema/Sema.h | 15 ++++ clang/lib/Parse/ParsePragma.cpp | 94 ++++++++++++----------- clang/lib/Sema/SemaAttr.cpp | 16 ++++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/test/CodeGen/pragma-msvc-optimize.c | 20 +++++ clang/test/Preprocessor/pragma_microsoft.c | 8 +- 10 files changed, 150 insertions(+), 51 deletions(-) create mode 100644 clang/test/CodeGen/pragma-msvc-optimize.c diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index a69b798..59c9889 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3813,6 +3813,43 @@ it causes the instantiation of ``twice`` and ``thrice`` with an ``int`` type; of 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 arguments, ``s``, +``g``, ``t``, and ``y``. Currently, the implementation of ``pragma optimize`` behaves +the same as ``#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 index 1b0bd5e..c884f74 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -336,6 +336,10 @@ New Pragmas in Clang - 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 index b0abe2a..352a050b 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1169,10 +1169,6 @@ def warn_pragma_pack_malformed : Warning< 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 index 0eb6f71..76e1c9d 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -728,6 +728,8 @@ private: 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 index 22ebdfe..587ddac 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -758,6 +758,13 @@ public: /// 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; @@ -10363,6 +10370,9 @@ public: /// 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, @@ -10389,6 +10399,11 @@ public: /// 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 index d69081c..6ca9887 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -255,12 +255,6 @@ struct PragmaMSIntrinsicHandler : public PragmaHandler { 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 @@ void Parser::initializePragmaHandlers() { 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 @@ void Parser::HandlePragmaMSPragma() { .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 @@ -3645,57 +3640,64 @@ bool Parser::HandlePragmaMSFunction(StringRef PragmaName, } // #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 index 8e6f029..c7e62e5 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -1144,6 +1144,15 @@ void Sema::ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc) { 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()) { @@ -1177,6 +1186,13 @@ void Sema::AddSectionMSAllocText(FunctionDecl *FD) { } } +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 index a5870ef..2e06207 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10197,6 +10197,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, 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 index 0000000..17ba40d --- /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 index ab60902..057f5ea 100644 --- a/clang/test/Preprocessor/pragma_microsoft.c +++ b/clang/test/Preprocessor/pragma_microsoft.c @@ -228,7 +228,13 @@ void pragma_function_foo() { #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'}} -- 2.7.4