llvm-reduce: Support multiple MachineFunctions
authorMatt Arsenault <Matthew.Arsenault@amd.com>
Tue, 19 Apr 2022 02:19:57 +0000 (22:19 -0400)
committerMatt Arsenault <arsenm2@gmail.com>
Wed, 27 Apr 2022 22:11:59 +0000 (18:11 -0400)
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.

llvm/include/llvm/CodeGen/MachineModuleInfo.h
llvm/lib/CodeGen/MachineModuleInfo.cpp
llvm/test/tools/llvm-reduce/mir/multiple-functions.mir [new file with mode: 0644]
llvm/tools/llvm-reduce/ReducerWorkItem.cpp
llvm/tools/llvm-reduce/ReducerWorkItem.h
llvm/tools/llvm-reduce/TestRunner.cpp
llvm/tools/llvm-reduce/TestRunner.h
llvm/tools/llvm-reduce/deltas/Delta.cpp
llvm/tools/llvm-reduce/deltas/Delta.h
llvm/tools/llvm-reduce/deltas/ReduceInstructionsMIR.cpp
llvm/tools/llvm-reduce/llvm-reduce.cpp

index e967853..d4e7888 100644 (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>
index 92e7332..7dee06d 100644 (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.
diff --git a/llvm/test/tools/llvm-reduce/mir/multiple-functions.mir b/llvm/test/tools/llvm-reduce/mir/multiple-functions.mir
new file mode 100644 (file)
index 0000000..e83ce84
--- /dev/null
@@ -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
+...
index 4ea7b61..d77fe02 100644 (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);
index 90d64a5..d20ca18 100644 (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);
 
index e8c1213..6c92997 100644 (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?");
 }
 
index c14d045..1074902 100644 (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
index d480527..3533afa 100644 (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);
-}
index a98da41..76aaf25 100644 (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
index 93e457e..3be2eae 100644 (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);
index db7bfa9..5581745 100644 (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);