From 81d690d8301ca1ae084a5f627e4a101577dd0550 Mon Sep 17 00:00:00 2001 From: Dario Domizioli <dario.domizioli@gmail.com> Date: Fri, 23 May 2014 12:13:25 +0000 Subject: [PATCH] Implemented support for "pragma clang optimize on/off", based on attribute 'optnone'. This patch implements support for selectively disabling optimizations on a range of function definitions through a pragma. The implementation is that all function definitions in the range are decorated with attribute 'optnone'. #pragma clang optimize off // All function definitions in here are decorated with 'optnone'. #pragma clang optimize on // Compilation resumes as normal. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@209510 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticParseKinds.td | 8 ++ include/clang/Parse/Parser.h | 1 + include/clang/Sema/Sema.h | 24 +++++ include/clang/Serialization/ASTBitCodes.h | 5 +- include/clang/Serialization/ASTReader.h | 3 + include/clang/Serialization/ASTWriter.h | 1 + lib/Parse/ParsePragma.cpp | 53 +++++++++ lib/Sema/SemaAttr.cpp | 28 +++++ lib/Sema/SemaDecl.cpp | 5 + lib/Serialization/ASTReader.cpp | 13 +++ lib/Serialization/ASTWriter.cpp | 11 ++ test/PCH/pragma-optimize.c | 27 +++++ test/Parser/pragma-optimize-diagnostics.cpp | 28 +++++ test/SemaCXX/pragma-optimize.cpp | 113 ++++++++++++++++++++ 14 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 test/PCH/pragma-optimize.c create mode 100644 test/Parser/pragma-optimize-diagnostics.cpp create mode 100644 test/SemaCXX/pragma-optimize.cpp diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index ddf69e915e8..6a01dfcbb6f 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -843,6 +843,14 @@ def err_pragma_detect_mismatch_malformed : Error< def err_pragma_pointers_to_members_unknown_kind : Error< "unexpected %0, expected to see one of %select{|'best_case', 'full_generality', }1" "'single_inheritance', 'multiple_inheritance', or 'virtual_inheritance'">; +// - #pragma clang optimize on/off +def err_pragma_optimize_missing_argument : Error< + "missing argument to '#pragma clang optimize'; expected 'on' or 'off'">; +def err_pragma_optimize_invalid_argument : Error< + "unexpected argument '%0' to '#pragma clang optimize'; " + "expected 'on' or 'off'">; +def err_pragma_optimize_extra_argument : Error< + "unexpected extra argument '%0' to '#pragma clang optimize'">; // OpenCL Section 6.8.g def err_not_opencl_storage_class_specifier : Error< diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 7dd8f5019d4..83fa1a52385 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -160,6 +160,7 @@ class Parser : public CodeCompletionHandler { std::unique_ptr<PragmaHandler> MSConstSeg; std::unique_ptr<PragmaHandler> MSCodeSeg; std::unique_ptr<PragmaHandler> MSSection; + std::unique_ptr<PragmaHandler> OptimizeHandler; std::unique_ptr<CommentHandler> CommentSemaHandler; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index f4a1a35fdb0..f1354e5b8ff 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -333,6 +333,11 @@ public: /// VisContext - Manages the stack for \#pragma GCC visibility. void *VisContext; // Really a "PragmaVisStack*" + /// \brief This represents the last location of a "#pragma clang optimize off" + /// directive if such a directive has not been closed by an "on" yet. If + /// optimizations are currently "on", this is set to an invalid location. + SourceLocation OptimizeOffPragmaLocation; + /// \brief Flag indicating if Sema is building a recovery call expression. /// /// This flag is used to avoid building recovery call expressions @@ -7231,6 +7236,25 @@ public: /// the appropriate attribute. void AddCFAuditedAttribute(Decl *D); + /// \brief Called on well formed \#pragma clang optimize. + void ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc); + + /// \brief Get the location for the currently active "\#pragma clang optimize + /// off". If this location is invalid, then the state of the pragma is "on". + SourceLocation getOptimizeOffPragmaLocation() const { + return OptimizeOffPragmaLocation; + } + + /// \brief Only called on function definitions; if there is a pragma in scope + /// with the effect of a range-based optnone, consider marking the function + /// with attribute optnone. + void AddRangeBasedOptnone(FunctionDecl *FD); + + /// \brief 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). + void AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD, SourceLocation Loc); + /// AddAlignedAttr - Adds an aligned attribute to a particular declaration. void AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, unsigned SpellingListIndex, bool IsPackExpansion); diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 89435f3afea..315c8520163 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -542,7 +542,10 @@ namespace clang { UNDEFINED_BUT_USED = 49, /// \brief Record code for late parsed template functions. - LATE_PARSED_TEMPLATE = 50 + LATE_PARSED_TEMPLATE = 50, + + /// \brief Record code for \#pragma optimize options. + OPTIMIZE_PRAGMA_OPTIONS = 51 }; /// \brief Record types used within a source manager block. diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 3389885a22d..be29d8f9daa 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -768,6 +768,9 @@ private: /// \brief The floating point pragma option settings. SmallVector<uint64_t, 1> FPPragmaOptions; + /// \brief The pragma clang optimize location (if the pragma state is "off"). + SourceLocation OptimizeOffPragmaLocation; + /// \brief The OpenCL extension settings. SmallVector<uint64_t, 1> OpenCLExtensions; diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index 80ac77c4e59..ad6ecdd3519 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -488,6 +488,7 @@ private: void WriteRedeclarations(); void WriteMergedDecls(); void WriteLateParsedTemplates(Sema &SemaRef); + void WriteOptimizePragmaOptions(Sema &SemaRef); unsigned DeclParmVarAbbrev; unsigned DeclContextLexicalAbbrev; diff --git a/lib/Parse/ParsePragma.cpp b/lib/Parse/ParsePragma.cpp index 133c0e028f5..787d3f0bdf4 100644 --- a/lib/Parse/ParsePragma.cpp +++ b/lib/Parse/ParsePragma.cpp @@ -131,6 +131,16 @@ struct PragmaMSPragma : public PragmaHandler { Token &FirstToken) override; }; +/// PragmaOptimizeHandler - "\#pragma clang optimize on/off". +struct PragmaOptimizeHandler : public PragmaHandler { + PragmaOptimizeHandler(Sema &S) + : PragmaHandler("optimize"), Actions(S) {} + void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, + Token &FirstToken) override; +private: + Sema &Actions; +}; + } // end namespace void Parser::initializePragmaHandlers() { @@ -195,6 +205,9 @@ void Parser::initializePragmaHandlers() { MSSection.reset(new PragmaMSPragma("section")); PP.AddPragmaHandler(MSSection.get()); } + + OptimizeHandler.reset(new PragmaOptimizeHandler(Actions)); + PP.AddPragmaHandler("clang", OptimizeHandler.get()); } void Parser::resetPragmaHandlers() { @@ -249,6 +262,9 @@ void Parser::resetPragmaHandlers() { PP.RemovePragmaHandler("STDC", FPContractHandler.get()); FPContractHandler.reset(); + + PP.RemovePragmaHandler("clang", OptimizeHandler.get()); + OptimizeHandler.reset(); } /// \brief Handle the annotation token produced for #pragma unused(...) @@ -1531,3 +1547,40 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP, Actions.ActOnPragmaMSComment(Kind, ArgumentString); } + +// #pragma clang optimize off +// #pragma clang optimize on +void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducerKind Introducer, + Token &FirstToken) { + Token Tok; + PP.Lex(Tok); + if (Tok.is(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_missing_argument); + return; + } + if (Tok.isNot(tok::identifier)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_invalid_argument) + << PP.getSpelling(Tok); + return; + } + const IdentifierInfo *II = Tok.getIdentifierInfo(); + // The only accepted values are 'on' or 'off'. + bool IsOn = false; + if (II->isStr("on")) { + IsOn = true; + } else if (!II->isStr("off")) { + PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_invalid_argument) + << PP.getSpelling(Tok); + return; + } + PP.Lex(Tok); + + if (Tok.isNot(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_extra_argument) + << PP.getSpelling(Tok); + return; + } + + Actions.ActOnPragmaOptimize(IsOn, FirstToken.getLocation()); +} diff --git a/lib/Sema/SemaAttr.cpp b/lib/Sema/SemaAttr.cpp index bb25be7da17..6397487981b 100644 --- a/lib/Sema/SemaAttr.cpp +++ b/lib/Sema/SemaAttr.cpp @@ -470,6 +470,34 @@ void Sema::AddCFAuditedAttribute(Decl *D) { D->addAttr(CFAuditedTransferAttr::CreateImplicit(Context, Loc)); } +void Sema::ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc) { + if(On) + OptimizeOffPragmaLocation = SourceLocation(); + else + OptimizeOffPragmaLocation = PragmaLoc; +} + +void Sema::AddRangeBasedOptnone(FunctionDecl *FD) { + // In the future, check other pragmas if they're implemented (e.g. pragma + // optimize 0 will probably map to this functionality too). + if(OptimizeOffPragmaLocation.isValid()) + AddOptnoneAttributeIfNoConflicts(FD, OptimizeOffPragmaLocation); +} + +void Sema::AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD, + SourceLocation Loc) { + // Don't add a conflicting attribute. No diagnostic is needed. + if (FD->hasAttr<MinSizeAttr>() || FD->hasAttr<AlwaysInlineAttr>()) + return; + + // Add attributes only if required. Optnone requires noinline as well, but if + // either is already present then don't bother adding them. + if (!FD->hasAttr<OptimizeNoneAttr>()) + FD->addAttr(OptimizeNoneAttr::CreateImplicit(Context, Loc)); + if (!FD->hasAttr<NoInlineAttr>()) + FD->addAttr(NoInlineAttr::CreateImplicit(Context, Loc)); +} + typedef std::vector<std::pair<unsigned, SourceLocation> > VisStack; enum : unsigned { NoVisibility = ~0U }; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index a3dd2e7462c..a522ef4784b 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -7373,6 +7373,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // marking the function. AddCFAuditedAttribute(NewFD); + // If this is a function definition, check if we have to apply optnone due to + // a pragma. + if(D.isFunctionDefinition()) + AddRangeBasedOptnone(NewFD); + // If this is the first declaration of an extern C variable, update // the map of such variables. if (NewFD->isFirstDecl() && !NewFD->isInvalidDecl() && diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 143a7386ff2..4ea95d07120 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -3281,6 +3281,14 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { LateParsedTemplates.append(Record.begin(), Record.end()); break; } + + case OPTIMIZE_PRAGMA_OPTIONS: + if (Record.size() != 1) { + Error("invalid pragma optimize record"); + return Failure; + } + OptimizeOffPragmaLocation = ReadSourceLocation(F, Record[0]); + break; } } } @@ -6808,6 +6816,11 @@ void ASTReader::UpdateSema() { } SemaDeclRefs.clear(); } + + // Update the state of 'pragma clang optimize'. Use the same API as if we had + // encountered the pragma in the source. + if(OptimizeOffPragmaLocation.isValid()) + SemaObj->ActOnPragmaOptimize(/* IsOn = */ false, OptimizeOffPragmaLocation); } IdentifierInfo* ASTReader::get(const char *NameStart, const char *NameEnd) { diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 7753986d58f..35da82c6e4c 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -866,6 +866,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(MACRO_OFFSET); RECORD(MACRO_TABLE); RECORD(LATE_PARSED_TEMPLATE); + RECORD(OPTIMIZE_PRAGMA_OPTIONS); // SourceManager Block. BLOCK(SOURCE_MANAGER_BLOCK); @@ -3850,6 +3851,14 @@ void ASTWriter::WriteLateParsedTemplates(Sema &SemaRef) { Stream.EmitRecord(LATE_PARSED_TEMPLATE, Record); } +/// \brief Write the state of 'pragma clang optimize' at the end of the module. +void ASTWriter::WriteOptimizePragmaOptions(Sema &SemaRef) { + RecordData Record; + SourceLocation PragmaLoc = SemaRef.getOptimizeOffPragmaLocation(); + AddSourceLocation(PragmaLoc, Record); + Stream.EmitRecord(OPTIMIZE_PRAGMA_OPTIONS, Record); +} + //===----------------------------------------------------------------------===// // General Serialization Routines //===----------------------------------------------------------------------===// @@ -4466,6 +4475,8 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, WriteMergedDecls(); WriteObjCCategories(); WriteLateParsedTemplates(SemaRef); + if(!WritingModule) + WriteOptimizePragmaOptions(SemaRef); // Some simple statistics Record.clear(); diff --git a/test/PCH/pragma-optimize.c b/test/PCH/pragma-optimize.c new file mode 100644 index 00000000000..2206fe75cd8 --- /dev/null +++ b/test/PCH/pragma-optimize.c @@ -0,0 +1,27 @@ +// Test this without pch. +// RUN: %clang_cc1 %s -include %s -verify -fsyntax-only + +// Test with pch. +// RUN: %clang_cc1 %s -emit-pch -o %t +// RUN: %clang_cc1 %s -emit-llvm -include-pch %t -o - | FileCheck %s + +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER +#pragma clang optimize off + +#else + +int a; + +void f() { + a = 12345; +} + +// Check that the function is decorated with optnone + +// CHECK-DAG: @f() [[ATTRF:#[0-9]+]] +// CHECK-DAG: attributes [[ATTRF]] = { {{.*}}noinline{{.*}}optnone{{.*}} } + +#endif diff --git a/test/Parser/pragma-optimize-diagnostics.cpp b/test/Parser/pragma-optimize-diagnostics.cpp new file mode 100644 index 00000000000..af11a4ef815 --- /dev/null +++ b/test/Parser/pragma-optimize-diagnostics.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#pragma clang optimize off + +#pragma clang optimize on + +// Extra arguments +#pragma clang optimize on top of spaghetti // expected-error {{unexpected extra argument 'top' to '#pragma clang optimize'}} + +// Wrong argument +#pragma clang optimize something_wrong // expected-error {{unexpected argument 'something_wrong' to '#pragma clang optimize'; expected 'on' or 'off'}} + +// No argument +#pragma clang optimize // expected-error {{missing argument to '#pragma clang optimize'; expected 'on' or 'off'}} + +// Check that macros can be used in the pragma +#define OFF off +#define ON on +#pragma clang optimize OFF +#pragma clang optimize ON + +// Check that _Pragma can also be used to address the use case where users want +// to define optimization control macros to abstract out which compiler they are +// using. +#define OPT_OFF _Pragma("clang optimize off") +#define OPT_ON _Pragma("clang optimize on") +OPT_OFF +OPT_ON diff --git a/test/SemaCXX/pragma-optimize.cpp b/test/SemaCXX/pragma-optimize.cpp new file mode 100644 index 00000000000..390070a8262 --- /dev/null +++ b/test/SemaCXX/pragma-optimize.cpp @@ -0,0 +1,113 @@ +// RUN: %clang_cc1 -x c++ -std=c++11 -emit-llvm -O2 < %s | FileCheck %s + +#pragma clang optimize off + +// This is a macro definition and therefore its text is not present after +// preprocessing. The pragma has no effect here. +#define CREATE_FUNC(name) \ +int name (int param) { \ + return param; \ +} \ + +// This is a declaration and therefore it is not decorated with `optnone`. +extern int foo(int a, int b); +// CHECK-DAG: @_Z3fooii{{.*}} [[ATTRFOO:#[0-9]+]] + +// This is a definition and therefore it will be decorated with `optnone`. +int bar(int x, int y) { + for(int i = 0; i < x; ++i) + y += x; + return y + foo(x, y); +} +// CHECK-DAG: @_Z3barii{{.*}} [[ATTRBAR:#[0-9]+]] + +// The function "int created (int param)" created by the macro invocation +// is also decorated with the `optnone` attribute because it is within a +// region of code affected by the functionality (not because of the position +// of the macro definition). +CREATE_FUNC (created) +// CHECK-DAG: @_Z7createdi{{.*}} [[ATTRCREATED:#[0-9]+]] + +class MyClass { + public: + // The declaration of the method is not decorated with `optnone`. + int method(int blah); +}; + +// The definition of the method instead is decorated with `optnone`. +int MyClass::method(int blah) { + return blah + 1; +} +// CHECK-DAG: @_ZN7MyClass6methodEi{{.*}} [[ATTRMETHOD:#[0-9]+]] + +// A template declaration will not be decorated with `optnone`. +template <typename T> T twice (T param); + +// The template definition will be decorated with the attribute `optnone`. +template <typename T> T thrice (T param) { + return 3 * param; +} + +// This function definition will not be decorated with `optnone` because the +// attribute would conflict with `always_inline`. +int __attribute__((always_inline)) baz(int z) { + return foo(z, 2); +} +// CHECK-DAG: @_Z3bazi{{.*}} [[ATTRBAZ:#[0-9]+]] + +#pragma clang optimize on + +// The function "int wombat(int param)" created by the macro is not +// decorated with `optnone`, because the pragma applies its effects only +// after preprocessing. The position of the macro definition is not +// relevant. +CREATE_FUNC (wombat) +// CHECK-DAG: @_Z6wombati{{.*}} [[ATTRWOMBAT:#[0-9]+]] + +// This instantiation of the "twice" template function with a "float" type +// will not have an `optnone` attribute because the template declaration was +// not affected by the pragma. +float container (float par) { + return twice(par); +} +// CHECK-DAG: @_Z9containerf{{.*}} [[ATTRCONTAINER:#[0-9]+]] +// CHECK-DAG: @_Z5twiceIfET_S0_{{.*}} [[ATTRTWICE:#[0-9]+]] + +// This instantiation of the "thrice" template function with a "float" type +// will have an `optnone` attribute because the template definition was +// affected by the pragma. +float container2 (float par) { + return thrice(par); +} +// CHECK-DAG: @_Z10container2f{{.*}} [[ATTRCONTAINER2:#[0-9]+]] +// CHECK-DAG: @_Z6thriceIfET_S0_{{.*}} [[ATTRTHRICEFLOAT:#[0-9]+]] + + +// A template specialization is a new definition and it will not be +// decorated with an `optnone` attribute because it is now outside of the +// affected region. +template<> int thrice(int par) { + return (par << 1) + par; +} +int container3 (int par) { + return thrice(par); +} +// CHECK-DAG: @_Z10container3i{{.*}} [[ATTRCONTAINER3:#[0-9]+]] +// CHECK-DAG: @_Z6thriceIiET_S0_{{.*}} [[ATTRTHRICEINT:#[0-9]+]] + + +// Check for both noinline and optnone on each function that should have them. +// CHECK-DAG: attributes [[ATTRBAR]] = { {{.*}}noinline{{.*}}optnone{{.*}} } +// CHECK-DAG: attributes [[ATTRCREATED]] = { {{.*}}noinline{{.*}}optnone{{.*}} } +// CHECK-DAG: attributes [[ATTRMETHOD]] = { {{.*}}noinline{{.*}}optnone{{.*}} } +// CHECK-DAG: attributes [[ATTRTHRICEFLOAT]] = { {{.*}}noinline{{.*}}optnone{{.*}} } + +// Check that the other functions do NOT have optnone. +// CHECK-DAG-NOT: attributes [[ATTRFOO]] = { {{.*}}optnone{{.*}} } +// CHECK-DAG-NOT: attributes [[ATTRBAZ]] = { {{.*}}optnone{{.*}} } +// CHECK-DAG-NOT: attributes [[ATTRWOMBAT]] = { {{.*}}optnone{{.*}} } +// CHECK-DAG-NOT: attributes [[ATTRCONTAINER]] = { {{.*}}optnone{{.*}} } +// CHECK-DAG-NOT: attributes [[ATTRTWICE]] = { {{.*}}optnone{{.*}} } +// CHECK-DAG-NOT: attributes [[ATTRCONTAINER2]] = { {{.*}}optnone{{.*}} } +// CHECK-DAG-NOT: attributes [[ATTRCONTAINER3]] = { {{.*}}optnone{{.*}} } +// CHECK-DAG-NOT: attributes [[ATTRTHRICEINT]] = { {{.*}}optnone{{.*}} } -- GitLab