diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -328,6 +328,12 @@ builtins (corresponding to the specific names listed in the attribute) in the body of the function the attribute is on. +- Added some support for GCC's attribute ``optimize``, which allows + functions to be compiled with different optimization options than what was + specified on the command line. Clang's support only adds certain function + attributes (depending on the optimization level passed to the attribute) and + is not intended to be like GCC's attribute. + Windows Support --------------- 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", "Oz", "Os", "O0"], + ["Fast", "MinSize", "OptSize", "NoOpts"], + /*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,22 @@ }]; } +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. Clang does not intend to fully +support the GCC semantics. Optimization levels `-O1` through `-O4` are +ignored. Only "-O0", "-Oz", "-Os", and "-Ofast" are supported. + +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; only " + "'-O0', '-Os', '-Oz', and '-Ofast' are supported; 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 @@ -1792,15 +1792,14 @@ return ReturnType.isTriviallyCopyableType(Context); } -void CodeGenModule::getDefaultFunctionAttributes(StringRef Name, - bool HasOptnone, - bool AttrOnCallSite, - llvm::AttrBuilder &FuncAttrs) { +void CodeGenModule::getDefaultFunctionAttributes( + StringRef Name, bool HasOptnone, bool HasOptsize, bool HasMinsize, + bool AttrOnCallSite, 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) + if (CodeGenOpts.OptimizeSize == 2 || HasMinsize) FuncAttrs.addAttribute(llvm::Attribute::MinSize); } @@ -1945,7 +1944,8 @@ void CodeGenModule::addDefaultFunctionDefinitionAttributes(llvm::Function &F) { llvm::AttrBuilder FuncAttrs(F.getContext()); - getDefaultFunctionAttributes(F.getName(), F.hasOptNone(), + getDefaultFunctionAttributes(F.getName(), F.hasOptNone(), F.hasOptSize(), + F.hasMinSize(), /* AttrOnCallSite = */ false, FuncAttrs); // TODO: call GetCPUAndFeaturesAttributes? F.addFnAttrs(FuncAttrs); @@ -1954,6 +1954,7 @@ void CodeGenModule::addDefaultFunctionDefinitionAttributes( llvm::AttrBuilder &attrs) { getDefaultFunctionAttributes(/*function name*/ "", /*optnone*/ false, + /*optsize*/ false, /*minsize*/ false, /*for call*/ false, attrs); GetCPUAndFeaturesAttributes(GlobalDecl(), attrs); } @@ -2088,6 +2089,8 @@ AddAttributesFromAssumes(FuncAttrs, TargetDecl); bool HasOptnone = false; + bool HasOptsize = false; + bool HasMinsize = false; // The NoBuiltinAttr attached to the target FunctionDecl. const NoBuiltinAttr *NBA = nullptr; @@ -2164,6 +2167,13 @@ FuncAttrs.addAttribute(llvm::Attribute::NoCallback); HasOptnone = TargetDecl->hasAttr(); + if (const auto *OA = TargetDecl->getAttr()) { + OptimizeAttr::OptLevelKind Kind = OA->getOptLevel(); + HasOptnone = HasOptnone || (Kind == OptimizeAttr::NoOpts); + HasOptsize = Kind == OptimizeAttr::OptSize || OptimizeAttr::MinSize || + OptimizeAttr::Fast; + HasMinsize = Kind == OptimizeAttr::MinSize; + } if (auto *AllocSize = TargetDecl->getAttr()) { Optional NumElemsParam; if (AllocSize->getNumElemsParam().isValid()) @@ -2197,7 +2207,8 @@ addNoBuiltinAttributes(FuncAttrs, getLangOpts(), NBA); // Collect function IR attributes based on global settiings. - getDefaultFunctionAttributes(Name, HasOptnone, AttrOnCallSite, FuncAttrs); + getDefaultFunctionAttributes(Name, HasOptnone, HasOptsize, HasMinsize, + 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,6 +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 HasOptsize, bool HasMinsize, bool AttrOnCallSite, llvm::AttrBuilder &FuncAttrs); 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,6 +1928,10 @@ ShouldAddOptNone &= !D->hasAttr(); ShouldAddOptNone &= !D->hasAttr(); + if (const auto *OA = D->getAttr()) + ShouldAddOptNone = + ShouldAddOptNone || (OA->getOptLevel() == OptimizeAttr::NoOpts); + // Add optnone, but do so only if the function isn't always_inline. if ((ShouldAddOptNone || D->hasAttr()) && !F->hasFnAttribute(llvm::Attribute::AlwaysInline)) { @@ -1950,7 +1954,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,43 @@ 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::NoOpts}, {"s", OptimizeAttr::OptSize}, + {"fast", OptimizeAttr::Fast}, {"z", OptimizeAttr::MinSize}, + {"0", OptimizeAttr::NoOpts}, + }; + + auto It = StrToKind.find(Level.str()); + if (It != StrToKind.end()) { + D->addAttr(::new (S.Context) OptimizeAttr(S.Context, AL, Arg, It->second)); + return; + } + + llvm::APInt Num; + if (!Level.getAsInteger(10, Num) && Num.isZero()) { + // We only support -O0 + D->addAttr(::new (S.Context) + OptimizeAttr(S.Context, AL, Arg, OptimizeAttr::NoOpts)); + 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 +8562,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,25 @@ +// 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("Oz"))) void f4(void) {} +// O2: @f4{{.*}}[[ATTR_MINSIZE:#[0-9]+]] +// 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{{.*}} } +// O2: attributes [[ATTR_MINSIZE]] = { {{.*}}minsize{{.*}}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-warning {{invalid optimization level 'Og' specified; only '-O0', '-Os', '-Oz', and '-Ofast' are supported; attribute ignored}} +void +f4() {} + +__attribute__((optimize("O-1"))) // expected-warning {{invalid optimization level 'O-1' specified; only '-O0', '-Os', '-Oz', and '-Ofast' are supported; attribute ignored}} +void +f5() {} + +__attribute__((optimize("O+1"))) // expected-warning {{invalid optimization level 'O+1' specified; only '-O0', '-Os', '-Oz', and '-Ofast' are supported; attribute ignored}} +void +f6() {} + +__attribute__((optimize("O0"))) // expected-no-error +void +f7() {} + +__attribute__((optimize("Os"))) // expected-no-error +void +f8() {} + +__attribute__((optimize("O44"))) // expected-warning {{invalid optimization level 'O44' specified; only '-O0', '-Os', '-Oz', and '-Ofast' are supported; attribute ignored}} +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 '('}}