llvm-project/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp
Puneeth A R ea9e47d534 ForceFunctionAttrs: support overriding attributes via csv file
Update `ForceFunctionAttrs` pass to optionally take its input from a csv file, for example, function-level optimization attributes. A subsequent patch will enable the pass pipeline to be aware of these attributes, and this pass will be used to test that is the case. Eventually, the annotations would be driven by an agent, e.g. a machine learning-based policy.

This patch is a part of GSoC 2023, more details can be found [[ https://summerofcode.withgoogle.com/programs/2023/projects/T8rB84Sr | here ]]

Reviewed By: mtrofin, aeubanks

Differential Revision: https://reviews.llvm.org/D155617
2023-09-07 18:39:19 -04:00

140 lines
5.2 KiB
C++

//===- 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`."));
/// 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))
continue;
F.addFnAttr(Kind);
}
for (const auto &S : ForceRemoveAttributes) {
auto Kind = ParseFunctionAndAttr(S);
if (Kind == Attribute::None || !F.hasFnAttribute(Kind))
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)
report_fatal_error("Cannot open CSV file.");
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)) {
// TODO: There could be string attributes without a value, we should
// support those, too.
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();
}