llvm-reduce: Support multiple MachineFunctions

The current testcase I'm trying to reduce only reproduces with IPRA
enabled and requires handling multiple functions.

The only real difference vs. the IR is the extra indirect to look for
the underlying MachineFunction, so treat the ReduceWorkItem as the
module instead of the function.

The ugliest piece of this is really the ugliness of
MachineModuleInfo. It not only tracks actual module state, but has a
number of transient fields used for isel and/or the asm printer. These
shouldn't do any harm for the use here, though they should be
separated out.
This commit is contained in:
Matt Arsenault 2022-04-18 22:19:57 -04:00 committed by Matt Arsenault
parent 49c7534587
commit 7c2db66632
11 changed files with 139 additions and 75 deletions

View File

@ -163,6 +163,9 @@ public:
/// Machine Function map.
void deleteMachineFunctionFor(Function &F);
/// Add an externally created MachineFunction \p MF for \p F.
void insertFunction(const Function &F, std::unique_ptr<MachineFunction> &&MF);
/// Keep track of various per-module pieces of information for backends
/// that would like to do so.
template<typename Ty>

View File

@ -141,6 +141,13 @@ void MachineModuleInfo::deleteMachineFunctionFor(Function &F) {
LastResult = nullptr;
}
void MachineModuleInfo::insertFunction(const Function &F,
std::unique_ptr<MachineFunction> &&MF) {
auto I = MachineFunctions.insert(std::make_pair(&F, std::move(MF)));
assert(I.second && "machine function already mapped");
(void)I;
}
namespace {
/// This pass frees the MachineFunction object associated with a Function.

View File

@ -0,0 +1,45 @@
# REQUIRES: amdgpu-registered-target
# RUN: llvm-reduce -simplify-mir -mtriple=amdgcn-amd-amdhsa --test FileCheck --test-arg --check-prefix=CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t 2> %t.log
# RUN: FileCheck --check-prefix=RESULT %s < %t
# CHECK-INTERESTINGNESS: S_NOP 0
# RESULT: name: func0
# RESULT: S_NOP 0
# RESULT: name: func1
# RESULT-NOT: S_NOP
--- |
define void @func0() {
ret void
}
define void @func1() {
ret void
}
...
---
name: func0
tracksRegLiveness: true
body: |
bb.0:
S_WAITCNT 0
S_NOP 0
%0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
INLINEASM &"", 1 /* sideeffect attdialect */
S_ENDPGM 0, implicit %0
...
---
name: func1
tracksRegLiveness: true
body: |
bb.0:
S_WAITCNT 0
S_NOP 1
%0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
INLINEASM &"", 1 /* sideeffect attdialect */
S_ENDPGM 0, implicit %0
...

View File

@ -127,10 +127,11 @@ static void cloneFrameInfo(
}
}
static std::unique_ptr<MachineFunction> cloneMF(MachineFunction *SrcMF) {
static std::unique_ptr<MachineFunction> cloneMF(MachineFunction *SrcMF,
MachineModuleInfo &DestMMI) {
auto DstMF = std::make_unique<MachineFunction>(
SrcMF->getFunction(), SrcMF->getTarget(), SrcMF->getSubtarget(),
SrcMF->getFunctionNumber(), SrcMF->getMMI());
SrcMF->getFunctionNumber(), DestMMI);
DenseMap<MachineBasicBlock *, MachineBasicBlock *> Src2DstMBB;
auto *SrcMRI = &SrcMF->getRegInfo();
@ -292,7 +293,7 @@ static std::unique_ptr<MachineFunction> cloneMF(MachineFunction *SrcMF) {
std::unique_ptr<ReducerWorkItem>
parseReducerWorkItem(const char *ToolName, StringRef Filename,
LLVMContext &Ctxt, std::unique_ptr<TargetMachine> &TM,
std::unique_ptr<MachineModuleInfo> &MMI, bool IsMIR) {
bool IsMIR) {
Triple TheTriple;
auto MMM = std::make_unique<ReducerWorkItem>();
@ -336,23 +337,9 @@ parseReducerWorkItem(const char *ToolName, StringRef Filename,
std::unique_ptr<Module> M = MParser->parseIRModule(SetDataLayout);
LLVMTargetMachine *LLVMTM = static_cast<LLVMTargetMachine *>(TM.get());
MMI = std::make_unique<MachineModuleInfo>(LLVMTM);
MParser->parseMachineFunctions(*M, *MMI);
MachineFunction *MF = nullptr;
for (auto &F : *M) {
if (auto *MF4F = MMI->getMachineFunction(F)) {
// XXX: Maybe it would not be a lot of effort to handle multiple MFs by
// simply storing them in a ReducerWorkItem::SmallVector or similar. The
// single MF use-case seems a lot more common though so that will do for
// now.
assert(!MF && "Only single MF supported!");
MF = MF4F;
}
}
assert(MF && "No MF found!");
MMM->MMI = std::make_unique<MachineModuleInfo>(LLVMTM);
MParser->parseMachineFunctions(*M, *MMM->MMI);
MMM->M = std::move(M);
MMM->MF = cloneMF(MF);
} else {
SMDiagnostic Err;
std::unique_ptr<Module> Result = parseIRFile(Filename, Err, Ctxt);
@ -371,16 +358,24 @@ parseReducerWorkItem(const char *ToolName, StringRef Filename,
}
std::unique_ptr<ReducerWorkItem>
cloneReducerWorkItem(const ReducerWorkItem &MMM) {
cloneReducerWorkItem(const ReducerWorkItem &MMM, const TargetMachine *TM) {
auto CloneMMM = std::make_unique<ReducerWorkItem>();
if (MMM.MF) {
// Note that we cannot clone the Module as then we would need a way to
// updated the cloned MachineFunction's IR references.
// XXX: Actually have a look at
// std::unique_ptr<Module> CloneModule(const Module &M, ValueToValueMapTy
// &VMap);
if (TM) {
// We're assuming the Module IR contents are always unchanged by MIR
// reductions, and can share it as a constant.
CloneMMM->M = MMM.M;
CloneMMM->MF = cloneMF(MMM.MF.get());
// MachineModuleInfo contains a lot of other state used during codegen which
// we won't be using here, but we should be able to ignore it (although this
// is pretty ugly).
const LLVMTargetMachine *LLVMTM =
static_cast<const LLVMTargetMachine *>(TM);
CloneMMM->MMI = std::make_unique<MachineModuleInfo>(LLVMTM);
for (const Function &F : MMM.getModule()) {
if (auto *MF = MMM.MMI->getMachineFunction(F))
CloneMMM->MMI->insertFunction(F, cloneMF(MF, *CloneMMM->MMI));
}
} else {
CloneMMM->M = CloneModule(*MMM.M);
}
@ -390,15 +385,27 @@ cloneReducerWorkItem(const ReducerWorkItem &MMM) {
bool verifyReducerWorkItem(const ReducerWorkItem &MMM, raw_fd_ostream *OS) {
if (verifyModule(*MMM.M, OS))
return true;
if (MMM.MF && !MMM.MF->verify(nullptr, "", /*AbortOnError=*/false))
return true;
if (!MMM.MMI)
return false;
for (const Function &F : MMM.getModule()) {
if (const MachineFunction *MF = MMM.MMI->getMachineFunction(F)) {
if (!MF->verify(nullptr, "", /*AbortOnError=*/false))
return true;
}
}
return false;
}
void ReducerWorkItem::print(raw_ostream &ROS, void *p) const {
if (MF) {
if (MMI) {
printMIR(ROS, *M);
printMIR(ROS, *MF);
for (Function &F : *M) {
if (auto *MF = MMI->getMachineFunction(F))
printMIR(ROS, *MF);
}
} else {
M->print(ROS, /*AssemblyAnnotationWriter=*/nullptr,
/*ShouldPreserveUseListOrder=*/true);

View File

@ -19,20 +19,23 @@ using namespace llvm;
class ReducerWorkItem {
public:
std::shared_ptr<Module> M;
std::unique_ptr<MachineFunction> MF;
std::unique_ptr<MachineModuleInfo> MMI;
bool isMIR() const { return MMI != nullptr; }
const Module &getModule() const { return *M; }
void print(raw_ostream &ROS, void *p = nullptr) const;
bool isMIR() { return MF != nullptr; }
operator Module &() const { return *M; }
operator MachineFunction &() const { return *MF; }
};
std::unique_ptr<ReducerWorkItem>
parseReducerWorkItem(const char *ToolName, StringRef Filename,
LLVMContext &Ctxt, std::unique_ptr<TargetMachine> &TM,
std::unique_ptr<MachineModuleInfo> &MMI, bool IsMIR);
bool IsMIR);
std::unique_ptr<ReducerWorkItem>
cloneReducerWorkItem(const ReducerWorkItem &MMM);
cloneReducerWorkItem(const ReducerWorkItem &MMM, const TargetMachine *TM);
bool verifyReducerWorkItem(const ReducerWorkItem &MMM, raw_fd_ostream *OS);

View File

@ -12,8 +12,10 @@ using namespace llvm;
TestRunner::TestRunner(StringRef TestName,
const std::vector<std::string> &TestArgs,
std::unique_ptr<ReducerWorkItem> Program)
: TestName(TestName), TestArgs(TestArgs), Program(std::move(Program)) {
std::unique_ptr<ReducerWorkItem> Program,
std::unique_ptr<TargetMachine> TM)
: TestName(TestName), TestArgs(TestArgs), Program(std::move(Program)),
TM(std::move(TM)) {
assert(this->Program && "Initialized with null program?");
}

View File

@ -16,6 +16,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Target/TargetMachine.h"
#include <vector>
namespace llvm {
@ -26,7 +27,8 @@ namespace llvm {
class TestRunner {
public:
TestRunner(StringRef TestName, const std::vector<std::string> &TestArgs,
std::unique_ptr<ReducerWorkItem> Program);
std::unique_ptr<ReducerWorkItem> Program,
std::unique_ptr<TargetMachine> TM);
/// Runs the interesting-ness test for the specified file
/// @returns 0 if test was successful, 1 if otherwise
@ -40,10 +42,13 @@ public:
Program = std::move(P);
}
const TargetMachine *getTargetMachine() const { return TM.get(); }
private:
StringRef TestName;
const std::vector<std::string> &TestArgs;
std::unique_ptr<ReducerWorkItem> Program;
std::unique_ptr<TargetMachine> TM;
};
} // namespace llvm

View File

@ -132,13 +132,14 @@ static bool increaseGranularity(std::vector<Chunk> &Chunks) {
}
return SplitOne;
}
// Check if \p ChunkToCheckForUninterestingness is interesting. Returns the
// modified module if the chunk resulted in a reduction.
template <typename T>
template <typename FuncType>
static std::unique_ptr<ReducerWorkItem>
CheckChunk(Chunk &ChunkToCheckForUninterestingness,
std::unique_ptr<ReducerWorkItem> Clone, TestRunner &Test,
function_ref<void(Oracle &, T &)> ExtractChunksFromModule,
FuncType ExtractChunksFromModule,
std::set<Chunk> &UninterestingChunks,
std::vector<Chunk> &ChunksStillConsideredInteresting) {
// Take all of ChunksStillConsideredInteresting chunks, except those we've
@ -183,11 +184,10 @@ CheckChunk(Chunk &ChunkToCheckForUninterestingness,
return Clone;
}
template <typename T>
template <typename FuncType>
SmallString<0> ProcessChunkFromSerializedBitcode(
Chunk &ChunkToCheckForUninterestingness, TestRunner &Test,
function_ref<void(Oracle &, T &)> ExtractChunksFromModule,
std::set<Chunk> &UninterestingChunks,
FuncType ExtractChunksFromModule, std::set<Chunk> &UninterestingChunks,
std::vector<Chunk> &ChunksStillConsideredInteresting,
SmallString<0> &OriginalBC, std::atomic<bool> &AnyReduced) {
LLVMContext Ctx;
@ -217,10 +217,8 @@ SmallString<0> ProcessChunkFromSerializedBitcode(
/// reduces the amount of chunks that are considered interesting by the
/// given test. The number of chunks is determined by a preliminary run of the
/// reduction pass where no change must be made to the module.
template <typename T>
void runDeltaPassInt(
TestRunner &Test,
function_ref<void(Oracle &, T &)> ExtractChunksFromModule) {
void llvm::runDeltaPass(TestRunner &Test,
ReductionFunc ExtractChunksFromModule) {
assert(!verifyReducerWorkItem(Test.getProgram(), &errs()) &&
"input module is broken before making changes");
@ -250,7 +248,7 @@ void runDeltaPassInt(
std::vector<Chunk> NoChunks;
Oracle NoChunksCounter(NoChunks);
std::unique_ptr<ReducerWorkItem> Clone =
cloneReducerWorkItem(Test.getProgram());
cloneReducerWorkItem(Test.getProgram(), Test.getTargetMachine());
ExtractChunksFromModule(NoChunksCounter, *Clone);
assert(Targets == NoChunksCounter.count() &&
"number of chunks changes when reducing");
@ -365,9 +363,11 @@ void runDeltaPassInt(
// Forward I to the last chunk processed in parallel.
I += NumChunksProcessed - 1;
} else {
Result = CheckChunk(*I, cloneReducerWorkItem(Test.getProgram()), Test,
ExtractChunksFromModule, UninterestingChunks,
ChunksStillConsideredInteresting);
Result = CheckChunk(
*I,
cloneReducerWorkItem(Test.getProgram(), Test.getTargetMachine()),
Test, ExtractChunksFromModule, UninterestingChunks,
ChunksStillConsideredInteresting);
}
if (!Result)
@ -394,15 +394,3 @@ void runDeltaPassInt(
Test.setProgram(std::move(ReducedProgram));
errs() << "Couldn't increase anymore.\n";
}
void llvm::runDeltaPass(
TestRunner &Test,
function_ref<void(Oracle &, Module &)> ExtractChunksFromModule) {
runDeltaPassInt<Module>(Test, ExtractChunksFromModule);
}
void llvm::runDeltaPass(
TestRunner &Test,
function_ref<void(Oracle &, MachineFunction &)> ExtractChunksFromModule) {
runDeltaPassInt<MachineFunction>(Test, ExtractChunksFromModule);
}

View File

@ -85,6 +85,8 @@ public:
int count() { return Index; }
};
using ReductionFunc = function_ref<void(Oracle &, ReducerWorkItem &)>;
/// This function implements the Delta Debugging algorithm, it receives a
/// number of Targets (e.g. Functions, Instructions, Basic Blocks, etc.) and
/// splits them in half; these chunks of targets are then tested while ignoring
@ -103,12 +105,7 @@ public:
///
/// Other implementations of the Delta Debugging algorithm can also be found in
/// the CReduce, Delta, and Lithium projects.
void runDeltaPass(
TestRunner &Test,
function_ref<void(Oracle &, Module &)> ExtractChunksFromModule);
void runDeltaPass(
TestRunner &Test,
function_ref<void(Oracle &, MachineFunction &)> ExtractChunksFromModule);
void runDeltaPass(TestRunner &Test, ReductionFunc ExtractChunksFromModule);
} // namespace llvm
#endif

View File

@ -45,7 +45,7 @@ static Register getPrevDefOfRCInMBB(MachineBasicBlock &MBB,
return 0;
}
static void extractInstrFromModule(Oracle &O, MachineFunction &MF) {
static void extractInstrFromFunction(Oracle &O, MachineFunction &MF) {
MachineDominatorTree MDT;
MDT.runOnMachineFunction(MF);
@ -129,6 +129,13 @@ static void extractInstrFromModule(Oracle &O, MachineFunction &MF) {
MI->eraseFromParent();
}
static void extractInstrFromModule(Oracle &O, ReducerWorkItem &WorkItem) {
for (const Function &F : WorkItem.getModule()) {
if (MachineFunction *MF = WorkItem.MMI->getMachineFunction(F))
extractInstrFromFunction(O, *MF);
}
}
void llvm::reduceInstructionsMIRDeltaPass(TestRunner &Test) {
outs() << "*** Reducing Instructions...\n";
runDeltaPass(Test, extractInstrFromModule);

View File

@ -138,16 +138,16 @@ int main(int Argc, char **Argv) {
LLVMContext Context;
std::unique_ptr<TargetMachine> TM;
std::unique_ptr<MachineModuleInfo> MMI;
std::unique_ptr<ReducerWorkItem> OriginalProgram = parseReducerWorkItem(
Argv[0], InputFilename, Context, TM, MMI, ReduceModeMIR);
std::unique_ptr<ReducerWorkItem> OriginalProgram =
parseReducerWorkItem(Argv[0], InputFilename, Context, TM, ReduceModeMIR);
if (!OriginalProgram) {
return 1;
}
// Initialize test environment
TestRunner Tester(TestFilename, TestArguments, std::move(OriginalProgram));
TestRunner Tester(TestFilename, TestArguments, std::move(OriginalProgram),
std::move(TM));
// Try to reduce code
runDeltaPasses(Tester, MaxPassIterations);