| //===- ForceFunctionAttrs.cpp - Force function attrs for debugging --------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Transforms/IPO/ForceFunctionAttrs.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/Support/CommandLine.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/LineIterator.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "forceattrs" | |
| static cl::list<std::string> ForceAttributes( | |
| "force-attribute", cl::Hidden, | |
| cl::desc( | |
| "Add an attribute to a function. This can be a " | |
| "pair of 'function-name:attribute-name', to apply an attribute to a " | |
| "specific function. For " | |
| "example -force-attribute=foo:noinline. Specifying only an attribute " | |
| "will apply the attribute to every function in the module. This " | |
| "option can be specified multiple times.")); | |
| static cl::list<std::string> ForceRemoveAttributes( | |
| "force-remove-attribute", cl::Hidden, | |
| cl::desc("Remove an attribute from a function. This can be a " | |
| "pair of 'function-name:attribute-name' to remove an attribute " | |
| "from a specific function. For " | |
| "example -force-remove-attribute=foo:noinline. Specifying only an " | |
| "attribute will remove the attribute from all functions in the " | |
| "module. This " | |
| "option can be specified multiple times.")); | |
| static cl::opt<std::string> CSVFilePath( | |
| "forceattrs-csv-path", cl::Hidden, | |
| cl::desc( | |
| "Path to CSV file containing lines of function names and attributes to " | |
| "add to them in the form of `f1,attr1` or `f2,attr2=str`.")); | |
| static bool hasConflictingFnAttr(Attribute::AttrKind Kind, Function &F) { | |
| switch (Kind) { | |
| case Attribute::AlwaysInline: | |
| return F.hasFnAttribute(Attribute::NoInline) || | |
| F.hasFnAttribute(Attribute::OptimizeNone); | |
| case Attribute::NoInline: | |
| return F.hasFnAttribute(Attribute::AlwaysInline); | |
| case Attribute::OptimizeNone: | |
| return F.hasFnAttribute(Attribute::AlwaysInline) || | |
| F.hasFnAttribute(Attribute::MinSize) || | |
| F.hasFnAttribute(Attribute::OptimizeForSize) || | |
| F.hasFnAttribute(Attribute::OptimizeForDebugging); | |
| case Attribute::MinSize: | |
| return F.hasFnAttribute(Attribute::OptimizeNone) || | |
| F.hasFnAttribute(Attribute::OptimizeForDebugging); | |
| case Attribute::OptimizeForSize: | |
| return F.hasFnAttribute(Attribute::OptimizeNone) || | |
| F.hasFnAttribute(Attribute::OptimizeForDebugging); | |
| case Attribute::OptimizeForDebugging: | |
| return F.hasFnAttribute(Attribute::OptimizeNone) || | |
| F.hasFnAttribute(Attribute::MinSize) || | |
| F.hasFnAttribute(Attribute::OptimizeForSize); | |
| default: | |
| return false; | |
| } | |
| } | |
| static void addRequiredFnAttrs(Attribute::AttrKind Kind, Function &F) { | |
| if (Kind == Attribute::OptimizeNone && !F.hasFnAttribute(Attribute::NoInline)) | |
| F.addFnAttr(Attribute::NoInline); | |
| } | |
| static bool wouldRemoveRequiredFnAttr(Attribute::AttrKind Kind, Function &F) { | |
| if (Kind == Attribute::NoInline && F.hasFnAttribute(Attribute::OptimizeNone)) | |
| return true; | |
| return false; | |
| } | |
| /// If F has any forced attributes given on the command line, add them. | |
| /// 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) { | |
| StringRef AttributeText; | |
| if (S.contains(':')) { | |
| auto KV = StringRef(S).split(':'); | |
| if (KV.first != F.getName()) | |
| return Attribute::None; | |
| AttributeText = KV.second; | |
| } else { | |
| AttributeText = S; | |
| } | |
| auto Kind = Attribute::getAttrKindFromName(AttributeText); | |
| if (Kind == Attribute::None || !Attribute::canUseAsFnAttr(Kind)) { | |
| LLVM_DEBUG(dbgs() << "ForcedAttribute: " << AttributeText | |
| << " unknown or not a function attribute!\n"); | |
| } | |
| return Kind; | |
| }; | |
| for (const auto &S : ForceAttributes) { | |
| auto Kind = ParseFunctionAndAttr(S); | |
| if (Kind == Attribute::None || F.hasFnAttribute(Kind) || | |
| hasConflictingFnAttr(Kind, F)) | |
| continue; | |
| addRequiredFnAttrs(Kind, F); | |
| F.addFnAttr(Kind); | |
| } | |
| for (const auto &S : ForceRemoveAttributes) { | |
| auto Kind = ParseFunctionAndAttr(S); | |
| if (Kind == Attribute::None || !F.hasFnAttribute(Kind) || | |
| wouldRemoveRequiredFnAttr(Kind, F)) | |
| continue; | |
| F.removeFnAttr(Kind); | |
| } | |
| } | |
| static bool hasForceAttributes() { | |
| return !ForceAttributes.empty() || !ForceRemoveAttributes.empty(); | |
| } | |
| PreservedAnalyses ForceFunctionAttrsPass::run(Module &M, | |
| ModuleAnalysisManager &) { | |
| bool Changed = false; | |
| if (!CSVFilePath.empty()) { | |
| auto BufferOrError = MemoryBuffer::getFileOrSTDIN(CSVFilePath); | |
| if (!BufferOrError) { | |
| std::error_code EC = BufferOrError.getError(); | |
| M.getContext().emitError("cannot open CSV file: " + EC.message()); | |
| return PreservedAnalyses::all(); | |
| } | |
| StringRef Buffer = BufferOrError.get()->getBuffer(); | |
| auto MemoryBuffer = MemoryBuffer::getMemBuffer(Buffer); | |
| line_iterator It(*MemoryBuffer); | |
| for (; !It.is_at_end(); ++It) { | |
| auto SplitPair = It->split(','); | |
| if (SplitPair.second.empty()) | |
| continue; | |
| Function *Func = M.getFunction(SplitPair.first); | |
| if (Func) { | |
| if (Func->isDeclaration()) | |
| continue; | |
| auto SecondSplitPair = SplitPair.second.split('='); | |
| if (!SecondSplitPair.second.empty()) { | |
| Func->addFnAttr(SecondSplitPair.first, SecondSplitPair.second); | |
| Changed = true; | |
| } else { | |
| auto AttrKind = Attribute::getAttrKindFromName(SplitPair.second); | |
| if (AttrKind != Attribute::None && | |
| Attribute::canUseAsFnAttr(AttrKind) && | |
| !hasConflictingFnAttr(AttrKind, *Func)) { | |
| // TODO: There could be string attributes without a value, we should | |
| // support those, too. | |
| addRequiredFnAttrs(AttrKind, *Func); | |
| Func->addFnAttr(AttrKind); | |
| Changed = true; | |
| } else | |
| errs() << "Cannot add " << SplitPair.second | |
| << " as an attribute name.\n"; | |
| } | |
| } else { | |
| errs() << "Function in CSV file at line " << It.line_number() | |
| << " does not exist.\n"; | |
| // TODO: `report_fatal_error at end of pass for missing functions. | |
| continue; | |
| } | |
| } | |
| } | |
| if (hasForceAttributes()) { | |
| for (Function &F : M.functions()) | |
| forceAttributes(F); | |
| Changed = true; | |
| } | |
| // Just conservatively invalidate analyses if we've made any changes, this | |
| // isn't likely to be important. | |
| return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); | |
| } |