diff --git a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp --- a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp @@ -26,6 +26,13 @@ "example -force-attribute=foo:noinline. This " "option can be specified multiple times.")); +static cl::list ForceRemoveAttributes( + "force-remove-attribute", cl::Hidden, + cl::desc("Remove an attribute from a function. This should be a " + "pair of 'function-name:attribute-name', for " + "example -force-remove-attribute=foo:noinline. This " + "option can be specified multiple times.")); + static Attribute::AttrKind parseAttrKind(StringRef Kind) { return StringSwitch(Kind) .Case("alwaysinline", Attribute::AlwaysInline) @@ -70,25 +77,41 @@ } /// If F has any forced attributes given on the command line, add them. -static void addForcedAttributes(Function &F) { - for (auto &S : ForceAttributes) { +/// If F has any forced remove attributes given on the command line, remove +/// them. When both force and force-remove are given to a function, the latter +/// takes precedence. +static void forceAttributes(Function &F) { + auto ParseFunctionAndAttr = [&](StringRef S) { + auto Kind = Attribute::None; auto KV = StringRef(S).split(':'); if (KV.first != F.getName()) - continue; - - auto Kind = parseAttrKind(KV.second); + return Kind; + Kind = parseAttrKind(KV.second); if (Kind == Attribute::None) { LLVM_DEBUG(dbgs() << "ForcedAttribute: " << KV.second << " unknown or not handled!\n"); - continue; } - if (F.hasFnAttribute(Kind)) + return Kind; + }; + + for (auto &S : ForceAttributes) { + auto Kind = ParseFunctionAndAttr(S); + if (Kind == Attribute::None || F.hasFnAttribute(Kind)) continue; F.addFnAttr(Kind); } + + for (auto &S : ForceRemoveAttributes) { + auto Kind = ParseFunctionAndAttr(S); + if (Kind == Attribute::None || !F.hasFnAttribute(Kind)) + continue; + F.removeFnAttr(Kind); + } } -static bool hasForceAttributes() { return !ForceAttributes.empty(); } +static bool hasForceAttributes() { + return !ForceAttributes.empty() || !ForceRemoveAttributes.empty(); +} PreservedAnalyses ForceFunctionAttrsPass::run(Module &M, ModuleAnalysisManager &) { @@ -96,7 +119,7 @@ return PreservedAnalyses::all(); for (Function &F : M.functions()) - addForcedAttributes(F); + forceAttributes(F); // Just conservatively invalidate analyses, this isn't likely to be important. return PreservedAnalyses::none(); @@ -111,11 +134,11 @@ } bool runOnModule(Module &M) override { - if (ForceAttributes.empty()) + if (!hasForceAttributes()) return false; for (Function &F : M.functions()) - addForcedAttributes(F); + forceAttributes(F); // Conservatively assume we changed something. return true; diff --git a/llvm/test/Transforms/ForcedFunctionAttrs/forced.ll b/llvm/test/Transforms/ForcedFunctionAttrs/forced.ll --- a/llvm/test/Transforms/ForcedFunctionAttrs/forced.ll +++ b/llvm/test/Transforms/ForcedFunctionAttrs/forced.ll @@ -1,6 +1,10 @@ ; RUN: opt < %s -S -forceattrs | FileCheck %s --check-prefix=CHECK-CONTROL ; RUN: opt < %s -S -forceattrs -force-attribute foo:noinline | FileCheck %s --check-prefix=CHECK-FOO ; RUN: opt < %s -S -passes=forceattrs -force-attribute foo:noinline | FileCheck %s --check-prefix=CHECK-FOO +; RUN: opt < %s -S -passes=forceattrs -force-remove-attribute goo:cold | FileCheck %s --check-prefix=REMOVE-COLD +; RUN: opt < %s -S -passes=forceattrs -force-remove-attribute goo:noinline | FileCheck %s --check-prefix=REMOVE-NOINLINE +; RUN: opt < %s -S -passes=forceattrs -force-attribute goo:cold -force-remove-attribute goo:noinline | FileCheck %s --check-prefix=ADD-COLD-REMOVE-NOINLINE +; RUN: opt < %s -S -passes=forceattrs -force-attribute goo:noinline -force-remove-attribute goo:noinline | FileCheck %s --check-prefix=ADD-NOINLINE-REMOVE-NOINLINE ; CHECK-CONTROL: define void @foo() { ; CHECK-FOO: define void @foo() #0 { @@ -8,5 +12,24 @@ ret void } +; Ignore `cold` which does not exist before. +; REMOVE-COLD: define void @goo() #0 { + +; Remove `noinline` attribute. +; REMOVE-NOINLINE: define void @goo() { + +; Add `cold` and remove `noinline` leaving `cold` only. +; ADD-COLD-REMOVE-NOINLINE: define void @goo() #0 { + +; `force-remove` takes precedence over `force`. +; `noinline` is removed. +; ADD-NOINLINE-REMOVE-NOINLINE: define void @goo() { + +define void @goo() #0 { + ret void +} +attributes #0 = { noinline } ; CHECK-FOO: attributes #0 = { noinline } +; REMOVE-COLD: attributes #0 = { noinline } +; ADD-COLD-REMOVE-NOINLINE: attributes #0 = { cold }