diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2260,6 +2260,17 @@ let Documentation = [OptnoneDocs]; } +def Optimize : InheritableAttr { + let Spellings = [GCC<"optimize">]; + let Args = [StringArgument<"Level">, + EnumArgument<"OptLevel", "OptLevelKind", + ["Ofast", "Og", "Oz", "Os", "O0", "O1", "O2", "O3", "O4"], + ["Ofast", "Og", "Oz", "Os", "O0", "O1", "O2", "O3", "O4"], + /*optional*/0, /*fake*/1>]; + let Subjects = SubjectList<[Function]>; + let Documentation = [OptimizeDocs]; +} + def Overloadable : Attr { let Spellings = [Clang<"overloadable">]; let Subjects = SubjectList<[Function], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3456,6 +3456,20 @@ }]; } +def OptimizeDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``optimize`` attribute, when attached to a function, indicates that the +function should be compiled with a different optimization level than specified +on the command line. See the Function Attributes documentation on GCC's docs for +more information. Currently, the attribute differs from GCC in that Clang only +supports one argument, doesn't support ``-f`` arguments, and also doesn't +support expressions or integers as arguments. + +Refer to: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html + }]; +} + def LoopHintDocs : Documentation { let Category = DocCatStmt; let Heading = "#pragma clang loop"; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3016,6 +3016,10 @@ "%0 attribute takes no more than %1 argument%s1">; def err_attribute_too_few_arguments : Error< "%0 attribute takes at least %1 argument%s1">; +def warn_invalid_optimize_attr_level : Warning < + "invalid optimization level '%0' specified; the argument to '-O' should be a non-negative integer, " + "'-Os', '-Oz', '-Og', or '-Ofast'; attribute ignored">, + InGroup; def err_attribute_invalid_vector_type : Error<"invalid vector element type %0">; def err_attribute_invalid_matrix_type : Error<"invalid matrix element type %0">; def err_attribute_bad_neon_vector_size : Error< diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -1794,11 +1794,12 @@ void CodeGenModule::getDefaultFunctionAttributes(StringRef Name, bool HasOptnone, + bool HasOptsize, bool AttrOnCallSite, - llvm::AttrBuilder &FuncAttrs) { + llvm::AttrBuilder &FuncAttrs) { // OptimizeNoneAttr takes precedence over -Os or -Oz. No warning needed. if (!HasOptnone) { - if (CodeGenOpts.OptimizeSize) + if (CodeGenOpts.OptimizeSize || HasOptsize) FuncAttrs.addAttribute(llvm::Attribute::OptimizeForSize); if (CodeGenOpts.OptimizeSize == 2) FuncAttrs.addAttribute(llvm::Attribute::MinSize); @@ -1945,7 +1946,7 @@ void CodeGenModule::addDefaultFunctionDefinitionAttributes(llvm::Function &F) { llvm::AttrBuilder FuncAttrs(F.getContext()); - getDefaultFunctionAttributes(F.getName(), F.hasOptNone(), + getDefaultFunctionAttributes(F.getName(), F.hasOptNone(), F.hasOptSize(), /* AttrOnCallSite = */ false, FuncAttrs); // TODO: call GetCPUAndFeaturesAttributes? F.addFnAttrs(FuncAttrs); @@ -1954,7 +1955,7 @@ void CodeGenModule::addDefaultFunctionDefinitionAttributes( llvm::AttrBuilder &attrs) { getDefaultFunctionAttributes(/*function name*/ "", /*optnone*/ false, - /*for call*/ false, attrs); + /*optsize*/ false, /*for call*/ false, attrs); GetCPUAndFeaturesAttributes(GlobalDecl(), attrs); } @@ -2088,6 +2089,7 @@ AddAttributesFromAssumes(FuncAttrs, TargetDecl); bool HasOptnone = false; + bool HasOptsize = false; // The NoBuiltinAttr attached to the target FunctionDecl. const NoBuiltinAttr *NBA = nullptr; @@ -2164,6 +2166,12 @@ FuncAttrs.addAttribute(llvm::Attribute::NoCallback); HasOptnone = TargetDecl->hasAttr(); + if (const auto *OA = TargetDecl->getAttr()) { + OptimizeAttr::OptLevelKind Kind = OA->getOptLevel(); + HasOptnone = HasOptnone || (Kind == OptimizeAttr::O0); + HasOptsize = Kind == OptimizeAttr::Os || Kind == OptimizeAttr::Og || + Kind == OptimizeAttr::Ofast || Kind == OptimizeAttr::Oz; + } if (auto *AllocSize = TargetDecl->getAttr()) { Optional NumElemsParam; if (AllocSize->getNumElemsParam().isValid()) @@ -2197,7 +2205,8 @@ addNoBuiltinAttributes(FuncAttrs, getLangOpts(), NBA); // Collect function IR attributes based on global settiings. - getDefaultFunctionAttributes(Name, HasOptnone, AttrOnCallSite, FuncAttrs); + getDefaultFunctionAttributes(Name, HasOptnone, HasOptsize, AttrOnCallSite, + FuncAttrs); // Override some default IR attributes based on declaration-specific // information. diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1654,7 +1654,7 @@ /// addDefaultFunctionDefinitionAttributes. Builds a set of function /// attributes to add to a function with the given properties. void getDefaultFunctionAttributes(StringRef Name, bool HasOptnone, - bool AttrOnCallSite, + bool HasOptsize, bool AttrOnCallSite, llvm::AttrBuilder &FuncAttrs); llvm::Metadata *CreateMetadataIdentifierImpl(QualType T, MetadataTypeMap &Map, diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1928,8 +1928,13 @@ ShouldAddOptNone &= !D->hasAttr(); ShouldAddOptNone &= !D->hasAttr(); + bool HasOptimizeAttrO0 = false; + if (const auto *OA = D->getAttr()) + HasOptimizeAttrO0 = OA->getOptLevel() == OptimizeAttr::O0; + // Add optnone, but do so only if the function isn't always_inline. - if ((ShouldAddOptNone || D->hasAttr()) && + if ((ShouldAddOptNone || D->hasAttr() || + HasOptimizeAttrO0) && !F->hasFnAttribute(llvm::Attribute::AlwaysInline)) { B.addAttribute(llvm::Attribute::OptimizeNone); @@ -1950,7 +1955,8 @@ B.addAttribute(llvm::Attribute::NoInline); } else if (D->hasAttr()) { B.addAttribute(llvm::Attribute::NoDuplicate); - } else if (D->hasAttr() && !F->hasFnAttribute(llvm::Attribute::AlwaysInline)) { + } else if (D->hasAttr() && + !F->hasFnAttribute(llvm::Attribute::AlwaysInline)) { // Add noinline if the function isn't always_inline. B.addAttribute(llvm::Attribute::NoInline); } else if (D->hasAttr() && diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4833,6 +4833,46 @@ D->addAttr(Optnone); } +static void handleOptimizeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + StringRef Arg; + if (!S.checkStringLiteralArgumentAttr(AL, 0, Arg)) + return; + + StringRef Level; + // Check if argument is prefixed with "-O" or "O" + if (Arg.str().rfind("-O", 0) == 0) + Level = Arg.substr(2); + else if (Arg.str().rfind("O", 0) == 0) + Level = Arg.substr(1); + else + S.Diag(AL.getLoc(), diag::warn_invalid_optimize_attr_level) << Arg; + + llvm::StringMap StrToKind = { + {"", OptimizeAttr::O0}, {"s", OptimizeAttr::Os}, + {"g", OptimizeAttr::Og}, {"fast", OptimizeAttr::Ofast}, + {"z", OptimizeAttr::Oz}, {"0", OptimizeAttr::O0}, + {"1", OptimizeAttr::O1}, {"2", OptimizeAttr::O2}, + {"3", OptimizeAttr::O3}, {"4", OptimizeAttr::O4}, + }; + + auto It = StrToKind.find(Level.str()); + if (It != StrToKind.end()) { + D->addAttr(::new (S.Context) OptimizeAttr(S.Context, AL, "", It->second)); + return; + } + + llvm::APInt Num; + if (!Level.getAsInteger(10, Num)) { + // Limit level to -O4 if higher. + std::string Level = std::to_string(Num.getLimitedValue(4)); + D->addAttr(::new (S.Context) + OptimizeAttr(S.Context, AL, "", StrToKind[Level])); + return; + } + + S.Diag(AL.getLoc(), diag::warn_invalid_optimize_attr_level) << Arg; +} + static void handleConstantAttr(Sema &S, Decl *D, const ParsedAttr &AL) { const auto *VD = cast(D); if (VD->hasLocalStorage()) { @@ -8525,6 +8565,9 @@ case ParsedAttr::AT_OptimizeNone: handleOptimizeNoneAttr(S, D, AL); break; + case ParsedAttr::AT_Optimize: + handleOptimizeAttr(S, D, AL); + break; case ParsedAttr::AT_EnumExtensibility: handleEnumExtensibilityAttr(S, D, AL); break; diff --git a/clang/test/CodeGen/attr-optimize.c b/clang/test/CodeGen/attr-optimize.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/attr-optimize.c @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -O2 -S -emit-llvm %s -o - | FileCheck %s --check-prefix=O2 +// RUN: %clang_cc1 -O0 -S -emit-llvm %s -o - | FileCheck %s --check-prefix=O0 + +__attribute__((optimize("O0"))) void f1(void) {} +// O2: @f1{{.*}}[[ATTR_OPTNONE:#[0-9]+]] +// O0: @f1{{.*}}[[ATTR_OPTNONE:#[0-9]+]] + +__attribute__((optimize("Os"))) void f2(void) {} +// O2: @f2{{.*}}[[ATTR_OPTSIZE:#[0-9]+]] +// O0: @f2{{.*}}[[ATTR_OPTNONE]] + +__attribute__((optimize("Og"))) void f3(void) {} +// O2: @f3{{.*}}[[ATTR_OPTSIZE]] +// O0: @f3{{.*}}[[ATTR_OPTNONE]] + +__attribute__((optimize("Oz"))) void f4(void) {} +// O2: @f4{{.*}}[[ATTR_OPTSIZE]] +// O0: @f4{{.*}}[[ATTR_OPTNONE]] + +__attribute__((optimize("Ofast"))) void f5(void) {} +// O2: @f5{{.*}}[[ATTR_OPTSIZE]] +// O0: @f5{{.*}}[[ATTR_OPTNONE]] + +// O2: attributes [[ATTR_OPTNONE]] = { {{.*}}optnone{{.*}} } +// O2: attributes [[ATTR_OPTSIZE]] = { {{.*}}optsize{{.*}} } + +// Check that O0 overrides the attribute +// O0: attributes [[ATTR_OPTNONE]] = { {{.*}}optnone{{.*}} } diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -142,6 +142,7 @@ // CHECK-NEXT: ObjCSubclassingRestricted (SubjectMatchRule_objc_interface) // CHECK-NEXT: OpenCLIntelReqdSubGroupSize (SubjectMatchRule_function) // CHECK-NEXT: OpenCLNoSVM (SubjectMatchRule_variable) +// CHECK-NEXT: Optimize (SubjectMatchRule_function) // CHECK-NEXT: OptimizeNone (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: Overloadable (SubjectMatchRule_function) // CHECK-NEXT: Owner (SubjectMatchRule_record_not_is_union) diff --git a/clang/test/Sema/attr-optimize.c b/clang/test/Sema/attr-optimize.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-optimize.c @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s + +__attribute__((optimize(a))) // expected-error {{use of undeclared identifier 'a'}} +void +f1() {} + +int b = 1; +__attribute__((optimize(b))) // expected-error {{'optimize' attribute requires a string}} +void +f2() {} + +__attribute__((optimize("O0", "O1"))) // expected-error {{'optimize' attribute takes one argument}} +void +f3() {} + +__attribute__((optimize("Og"))) // expected-no-error +void +f4() {} + +__attribute__((optimize("O-1"))) // expected-warning {{invalid optimization level 'O-1' specified; the argument to '-O' should be a non-negative integer, '-Os', '-Oz', '-Og', or '-Ofast'; attribute ignored}} +void +f5() {} + +__attribute__((optimize("O+1"))) // expected-warning {{invalid optimization level 'O+1' specified; the argument to '-O' should be a non-negative integer, '-Os', '-Oz', '-Og', or '-Ofast'; attribute ignored}} +void +f6() {} + +__attribute__((optimize("O0"))) // expected-no-error +void +f7() {} + +__attribute__((optimize("Os"))) // expected-no-error +void +f8() {} + +__attribute__((optimize("O44"))) // expected-no-error +void +f9() {} + +__attribute__((optimize("Oz"))) // expected-no-error +void +f10() {} + +__attribute__((optimize("Ofast"))) // expected-no-error +void +f11() {} + +__attribute__((optimize("O"))) // expected-no-error +void +f12() {} + +__attribute__((optimize("O0"))) // expected-error {{expected identifier or '('}}