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 e967853547a82f36433dd5826011c58cd1bfb37e..d4e788878f92b2d81169982a744317b5a45b892e 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 92e73324e9c36fa13e055598842d34d7a60e216f..7dee06dfee655858ece4b6ddde3340ef5decac1f 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 4ea7b615e9d163d1e00f5d33cb82cfc2123775ed..d77fe024e475b08162acb411b94454e2bdb83e7b 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 90d64a5ea17055b129b609291773f251425ecdc1..d20ca1834c914fa37107503f1cc60bf73ab729ce 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 e8c12138936fa1ec97cfcdc6e9fc56b22af7791e..6c9299797b354f0881847fc5a6473d895466db97 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 c14d0459a0fb412f994b126c4a6033efdb90d530..1074902554aacda9ef09202b05408164b49c0374 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 d48052742f938c2c9696649ce48de3e6238141bd..3533afac0850f9ea1fc042b61e317f572c1e27c1 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 a98da41957d012867e22e5a87cdb5aea2915caff..76aaf2504482630a501ddc18b1cd30a4de8472aa 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 93e457edb98609ca2a2e50aa1ef0d80d72fd51c5..3be2eae01df0590fc375f3dfffc40cf5742bd485 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 db7bfa90cd6d92b032111adc787fa642bfcff47f..5581745ff22e234d48bab7870e83b9af4ee35c91 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);