Index: lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- lib/Transforms/IPO/FunctionAttrs.cpp +++ lib/Transforms/IPO/FunctionAttrs.cpp @@ -23,6 +23,7 @@ #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/BasicAliasAnalysis.h" @@ -52,6 +53,13 @@ STATISTIC(NumAnnotated, "Number of attributes added to library functions"); STATISTIC(NumNoRecurse, "Number of functions marked as norecurse"); +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-add-attribute=foo:noinline. This " + "option can be given multiple times.")); + namespace { typedef SmallSetVector SCCNodeSet; } @@ -1843,6 +1851,62 @@ return false; } +static Attribute::AttrKind parseAttrKind(StringRef Kind) { + return StringSwitch(Kind) + .Case("alwaysinline", Attribute::AlwaysInline) + .Case("builtin", Attribute::Builtin) + .Case("cold", Attribute::Cold) + .Case("convergent", Attribute::Convergent) + .Case("inlinehint", Attribute::InlineHint) + .Case("jumptable", Attribute::JumpTable) + .Case("minsize", Attribute::MinSize) + .Case("naked", Attribute::Naked) + .Case("nobuiltin", Attribute::NoBuiltin) + .Case("noduplicate", Attribute::NoDuplicate) + .Case("noimplicitfloat", Attribute::NoImplicitFloat) + .Case("noinline", Attribute::NoInline) + .Case("nonlazybind", Attribute::NonLazyBind) + .Case("noredzone", Attribute::NoRedZone) + .Case("noreturn", Attribute::NoReturn) + .Case("norecurse", Attribute::NoRecurse) + .Case("nounwind", Attribute::NoUnwind) + .Case("optnone", Attribute::OptimizeNone) + .Case("optsize", Attribute::OptimizeForSize) + .Case("readnone", Attribute::ReadNone) + .Case("readonly", Attribute::ReadOnly) + .Case("argmemonly", Attribute::ArgMemOnly) + .Case("returns_twice", Attribute::ReturnsTwice) + .Case("safestack", Attribute::SafeStack) + .Case("sanitize_address", Attribute::SanitizeAddress) + .Case("sanitize_memory", Attribute::SanitizeMemory) + .Case("sanitize_thread", Attribute::SanitizeThread) + .Case("ssp", Attribute::StackProtect) + .Case("sspreq", Attribute::StackProtectReq) + .Case("sspstrong", Attribute::StackProtectStrong) + .Case("uwtable", Attribute::UWTable) + .Default(Attribute::None); +} + +/// If F has any forced attributes given on the command line, add them. +static bool addForcedAttributes(Function *F) { + bool Changed = false; + for (auto &S : ForceAttributes) { + auto KV = StringRef(S).split(':'); + if (KV.first != F->getName()) + continue; + + if (auto Kind = parseAttrKind(KV.second)) { + if (!F->hasFnAttribute(Kind)) { + Changed = true; + F->addFnAttr(Kind); + } + } else { + assert(0 && "Unknown forced attribute!"); + } + } + return Changed; +} + bool FunctionAttrs::runOnSCC(CallGraphSCC &SCC) { TLI = &getAnalysis().getTLI(); bool Changed = false; @@ -1878,6 +1942,7 @@ if (F->isDeclaration()) Changed |= inferPrototypeAttributes(*F, *TLI); + Changed |= addForcedAttributes(F); SCCNodes.insert(F); } Index: test/Transforms/FunctionAttrs/forced.ll =================================================================== --- /dev/null +++ test/Transforms/FunctionAttrs/forced.ll @@ -0,0 +1,12 @@ +; RUN: opt < %s -S -functionattrs | FileCheck %s --check-prefix=CHECK-CONTROL +; RUN: opt < %s -S -functionattrs -force-attribute foo:noinline | FileCheck %s --check-prefix=CHECK-FOO + +; CHECK-CONTROL: define void @foo() #0 { +; CHECK-FOO: define void @foo() #0 { +define void @foo() { + ret void +} + + +; CHECK-CONTROL: attributes #0 = { norecurse readnone } +; CHECK-FOO: attributes #0 = { noinline norecurse readnone }