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 @@ -19,12 +19,19 @@ #define DEBUG_TYPE "forceattrs" -static cl::list - ForceAttributes("force-attribute", cl::Hidden, - cl::desc("Add an attribute to a function. This should be a " - "pair of 'function-name:attribute-name', for " - "example -force-attribute=foo:noinline. This " - "option can be specified multiple times.")); +static cl::list ForceAddAttributes( + "force-add-attribute", cl::Hidden, + cl::desc("Add an attribute to a function. This should be a " + "pair of 'function-name:attribute-name', for " + "example -force-add-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) @@ -69,32 +76,50 @@ .Default(Attribute::None); } -/// 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 add attributes given on the command line, add them. +/// If F has any forced remove attributes given on the command line, remove +/// them. When both add and 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 : ForceAddAttributes) { + 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 !ForceAddAttributes.empty() || !ForceRemoveAttributes.empty(); } PreservedAnalyses ForceFunctionAttrsPass::run(Module &M, ModuleAnalysisManager &) { - if (ForceAttributes.empty()) + if (!hasForceAttributes()) 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(); @@ -109,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,8 @@ ; 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 -forceattrs -force-add-attribute foo:noinline | FileCheck %s --check-prefix=CHECK-FOO +; RUN: opt < %s -S -passes=forceattrs -force-add-attribute foo:noinline | FileCheck %s --check-prefix=CHECK-FOO +; RUN: opt < %s -S -passes=forceattrs -force-remove-attribute goo:noinline | FileCheck %s --check-prefix=REMOVE +; RUN: opt < %s -S -passes=forceattrs -force-add-attribute foo:noinline -force-remove-attribute goo:noinline | FileCheck %s --check-prefix=ADD-REMOVE ; CHECK-CONTROL: define void @foo() { ; CHECK-FOO: define void @foo() #0 { @@ -8,5 +10,11 @@ ret void } +; REMOVE: define void @goo() { +; `remove` takes precedence over `add`. +; ADD-REMOVE: define void @goo() { +define void @goo() noinline { + ret void +} ; CHECK-FOO: attributes #0 = { noinline }