[llvm] 18fc5a8 - [TableGen][RISCV] Generate pre/post-RA statistics for macro fusions (#209394)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 16 05:12:21 PDT 2026


Author: Pengcheng Wang
Date: 2026-07-16T12:12:15Z
New Revision: 18fc5a8adbe5dba7dc281bacdb149e95dc7dc1be

URL: https://github.com/llvm/llvm-project/commit/18fc5a8adbe5dba7dc281bacdb149e95dc7dc1be
DIFF: https://github.com/llvm/llvm-project/commit/18fc5a8adbe5dba7dc281bacdb149e95dc7dc1be.diff

LOG: [TableGen][RISCV] Generate pre/post-RA statistics for macro fusions (#209394)

Teach `MacroFusionPredicatorEmitter` to emit `Statistic` counters
recording how often each macro fusion is matched, and wire them
up for RISC-V.

This is based on the idea in #186499, generalized so counters are
generated automatically for any target using the emitter.

Because both the pre-RA and post-RA schedulers run MacroFusion,
a single counter would conflate the two. Counters are split into
pre-RA/post-RA variants, distinguished by the `NoVRegs` property,
addressing the double-counting concern from #186499:

```cpp
  STATISTIC(NumTuneLDADDFusionPreRA,  "Times TuneLDADDFusion Triggered (pre-ra)");
  STATISTIC(NumTuneLDADDFusionPostRA, "Times TuneLDADDFusion Triggered (post-ra)");
  ...
    if (SecondMI.getMF()->getProperties().hasNoVRegs())
      ++NumTuneLDADDFusionPostRA;
    else
      ++NumTuneLDADDFusionPreRA;
```

Besides, RISC-V counters use the `riscv-macro-fusion` debug type.

Co-authored-by: Sam Elliott <aelliott at qti.qualcomm.com>
Assisted-by: TRAE CLI (DeepSeek V4 Pro)

Added: 
    

Modified: 
    llvm/lib/Target/RISCV/RISCVSubtarget.cpp
    llvm/test/TableGen/MacroFusion.td
    llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
index 0ba474abd4481..319dbdd03fb79 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
@@ -18,6 +18,7 @@
 #include "RISCVFrameLowering.h"
 #include "RISCVSelectionDAGInfo.h"
 #include "RISCVTargetMachine.h"
+#include "llvm/ADT/Statistic.h"
 #include "llvm/CodeGen/MachineFrameInfo.h"
 #include "llvm/MC/MCSchedule.h"
 #include "llvm/MC/TargetRegistry.h"
@@ -39,15 +40,18 @@ static cl::opt<unsigned> SchedLoadLatency(
     cl::desc("Override the load latency (in cycles) in the scheduler model. "
              "A non-negative value overrides the target default."));
 
+#define DEBUG_TYPE "riscv-macro-fusion"
+
+#define GET_RISCV_MACRO_FUSION_PRED_IMPL
+#include "RISCVGenMacroFusion.inc"
+
+#undef DEBUG_TYPE
 #define DEBUG_TYPE "riscv-subtarget"
 
 #define GET_SUBTARGETINFO_TARGET_DESC
 #define GET_SUBTARGETINFO_CTOR
 #include "RISCVGenSubtargetInfo.inc"
 
-#define GET_RISCV_MACRO_FUSION_PRED_IMPL
-#include "RISCVGenMacroFusion.inc"
-
 namespace llvm::RISCVTuneInfoTable {
 
 #define GET_RISCVTuneInfoTable_IMPL

diff  --git a/llvm/test/TableGen/MacroFusion.td b/llvm/test/TableGen/MacroFusion.td
index 8fd823184a2b1..64c0885874e71 100644
--- a/llvm/test/TableGen/MacroFusion.td
+++ b/llvm/test/TableGen/MacroFusion.td
@@ -82,12 +82,15 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:  } // namespace llvm
 // CHECK-PREDICATOR-EMPTY:
 // CHECK-PREDICATOR-NEXT:  #endif // GET_Test_MACRO_FUSION_PRED_DECL
-
-// CHECK-PREDICATOR:       #ifdef GET_Test_MACRO_FUSION_PRED_IMPL
+// CHECK-PREDICATOR-EMPTY:
+// CHECK-PREDICATOR-EMPTY:
+// CHECK-PREDICATOR-NEXT:  #ifdef GET_Test_MACRO_FUSION_PRED_IMPL
 // CHECK-PREDICATOR-NEXT:  #undef GET_Test_MACRO_FUSION_PRED_IMPL
 // CHECK-PREDICATOR-EMPTY:
 // CHECK-PREDICATOR-NEXT:  namespace llvm {
 // CHECK-PREDICATOR-EMPTY:
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestBothFusionPredicatePreRA, "Times TestBothFusionPredicate Triggered (pre-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestBothFusionPredicatePostRA, "Times TestBothFusionPredicate Triggered (post-ra)");
 // CHECK-PREDICATOR-NEXT:  bool isTestBothFusionPredicate(
 // CHECK-PREDICATOR-NEXT:      const TargetInstrInfo &TII,
 // CHECK-PREDICATOR-NEXT:      const TargetSubtargetInfo &STI,
@@ -95,17 +98,23 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:      const MachineInstr &SecondMI) {
 // CHECK-PREDICATOR-NEXT:    {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} auto &MRI = SecondMI.getMF()->getRegInfo();
 // CHECK-PREDICATOR-NEXT:    {
-// CHECK-PREDICATOR-NEXT:      const MachineInstr *MI = FirstMI;
+// CHECK-PREDICATOR-NEXT:      {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} const MachineInstr *MI = FirstMI;
 // CHECK-PREDICATOR-NEXT:      if (MI->getOperand(0).getReg() != Test::X0)
 // CHECK-PREDICATOR-NEXT:        return false;
 // CHECK-PREDICATOR-NEXT:    }
 // CHECK-PREDICATOR-NEXT:    {
-// CHECK-PREDICATOR-NEXT:      const MachineInstr *MI = &SecondMI;
+// CHECK-PREDICATOR-NEXT:      {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} const MachineInstr *MI = &SecondMI;
 // CHECK-PREDICATOR-NEXT:      if (MI->getOperand(0).getReg() != Test::X0)
 // CHECK-PREDICATOR-NEXT:        return false;
 // CHECK-PREDICATOR-NEXT:    }
+// CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
+// CHECK-PREDICATOR-NEXT:      ++NumTestBothFusionPredicatePostRA;
+// CHECK-PREDICATOR-NEXT:    else
+// CHECK-PREDICATOR-NEXT:      ++NumTestBothFusionPredicatePreRA;
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestCommutableFusionPreRA, "Times TestCommutableFusion Triggered (pre-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestCommutableFusionPostRA, "Times TestCommutableFusion Triggered (post-ra)");
 // CHECK-PREDICATOR-NEXT:  bool isTestCommutableFusion(
 // CHECK-PREDICATOR-NEXT:      const TargetInstrInfo &TII,
 // CHECK-PREDICATOR-NEXT:      const TargetSubtargetInfo &STI,
@@ -113,7 +122,7 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:      const MachineInstr &SecondMI) {
 // CHECK-PREDICATOR-NEXT:    {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} auto &MRI = SecondMI.getMF()->getRegInfo();
 // CHECK-PREDICATOR-NEXT:    {
-// CHECK-PREDICATOR-NEXT:      const MachineInstr *MI = &SecondMI;
+// CHECK-PREDICATOR-NEXT:      {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} const MachineInstr *MI = &SecondMI;
 // CHECK-PREDICATOR-NEXT:      if (!(
 // CHECK-PREDICATOR-NEXT:          ( MI->getOpcode() == Test::Inst1 )
 // CHECK-PREDICATOR-NEXT:          && MI->getOperand(0).getReg() == Test::X0
@@ -123,7 +132,7 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:    if (!FirstMI)
 // CHECK-PREDICATOR-NEXT:      return true;
 // CHECK-PREDICATOR-NEXT:    {
-// CHECK-PREDICATOR-NEXT:      const MachineInstr *MI = FirstMI;
+// CHECK-PREDICATOR-NEXT:      {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} const MachineInstr *MI = FirstMI;
 // CHECK-PREDICATOR-NEXT:      if (( MI->getOpcode() != Test::Inst0 ))
 // CHECK-PREDICATOR-NEXT:        return false;
 // CHECK-PREDICATOR-NEXT:    }
@@ -152,8 +161,14 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:        if (FirstMI->getOperand(0).getReg() != SecondMI.getOperand(SrcOpIdx2).getReg())
 // CHECK-PREDICATOR-NEXT:          return false;
 // CHECK-PREDICATOR-NEXT:    }
+// CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
+// CHECK-PREDICATOR-NEXT:      ++NumTestCommutableFusionPostRA;
+// CHECK-PREDICATOR-NEXT:    else
+// CHECK-PREDICATOR-NEXT:      ++NumTestCommutableFusionPreRA;
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFirstSameRegFusionPreRA, "Times TestFirstSameRegFusion Triggered (pre-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFirstSameRegFusionPostRA, "Times TestFirstSameRegFusion Triggered (post-ra)");
 // CHECK-PREDICATOR-NEXT:  bool isTestFirstSameRegFusion(
 // CHECK-PREDICATOR-NEXT:      const TargetInstrInfo &TII,
 // CHECK-PREDICATOR-NEXT:      const TargetSubtargetInfo &STI,
@@ -170,8 +185,14 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:            return false;
 // CHECK-PREDICATOR-NEXT:      }
 // CHECK-PREDICATOR-NEXT:    }
+// CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
+// CHECK-PREDICATOR-NEXT:      ++NumTestFirstSameRegFusionPostRA;
+// CHECK-PREDICATOR-NEXT:    else
+// CHECK-PREDICATOR-NEXT:      ++NumTestFirstSameRegFusionPreRA;
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFusionPreRA, "Times TestFusion Triggered (pre-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFusionPostRA, "Times TestFusion Triggered (post-ra)");
 // CHECK-PREDICATOR-NEXT:  bool isTestFusion(
 // CHECK-PREDICATOR-NEXT:      const TargetInstrInfo &TII,
 // CHECK-PREDICATOR-NEXT:      const TargetSubtargetInfo &STI,
@@ -179,7 +200,7 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:      const MachineInstr &SecondMI) {
 // CHECK-PREDICATOR-NEXT:    {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} auto &MRI = SecondMI.getMF()->getRegInfo();
 // CHECK-PREDICATOR-NEXT:    {
-// CHECK-PREDICATOR-NEXT:      const MachineInstr *MI = &SecondMI;
+// CHECK-PREDICATOR-NEXT:      {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} const MachineInstr *MI = &SecondMI;
 // CHECK-PREDICATOR-NEXT:      if (!(
 // CHECK-PREDICATOR-NEXT:          ( MI->getOpcode() == Test::Inst1 )
 // CHECK-PREDICATOR-NEXT:          && MI->getOperand(0).getReg() == Test::X0
@@ -189,7 +210,7 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:    if (!FirstMI)
 // CHECK-PREDICATOR-NEXT:      return true;
 // CHECK-PREDICATOR-NEXT:    {
-// CHECK-PREDICATOR-NEXT:      const MachineInstr *MI = FirstMI;
+// CHECK-PREDICATOR-NEXT:      {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} const MachineInstr *MI = FirstMI;
 // CHECK-PREDICATOR-NEXT:      if (!llvm::is_contained({Test::Inst0, Test::Inst1}, MI->getOpcode()))
 // CHECK-PREDICATOR-NEXT:        return false;
 // CHECK-PREDICATOR-NEXT:    }
@@ -206,8 +227,14 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:          SecondMI.getOperand(1).isReg() &&
 // CHECK-PREDICATOR-NEXT:          FirstMI->getOperand(0).getReg() == SecondMI.getOperand(1).getReg()))
 // CHECK-PREDICATOR-NEXT:      return false;
+// CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
+// CHECK-PREDICATOR-NEXT:      ++NumTestFusionPostRA;
+// CHECK-PREDICATOR-NEXT:    else
+// CHECK-PREDICATOR-NEXT:      ++NumTestFusionPreRA;
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestSingleFusionPreRA, "Times TestSingleFusion Triggered (pre-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestSingleFusionPostRA, "Times TestSingleFusion Triggered (post-ra)");
 // CHECK-PREDICATOR-NEXT:  bool isTestSingleFusion(
 // CHECK-PREDICATOR-NEXT:      const TargetInstrInfo &TII,
 // CHECK-PREDICATOR-NEXT:      const TargetSubtargetInfo &STI,
@@ -215,7 +242,7 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:      const MachineInstr &SecondMI) {
 // CHECK-PREDICATOR-NEXT:    {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} auto &MRI = SecondMI.getMF()->getRegInfo();
 // CHECK-PREDICATOR-NEXT:    {
-// CHECK-PREDICATOR-NEXT:      const MachineInstr *MI = &SecondMI;
+// CHECK-PREDICATOR-NEXT:      {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} const MachineInstr *MI = &SecondMI;
 // CHECK-PREDICATOR-NEXT:      if (!(
 // CHECK-PREDICATOR-NEXT:          ( MI->getOpcode() == Test::Inst2 )
 // CHECK-PREDICATOR-NEXT:          && MI->getOperand(0).getReg() == Test::X0
@@ -225,7 +252,7 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:    if (!FirstMI)
 // CHECK-PREDICATOR-NEXT:      return true;
 // CHECK-PREDICATOR-NEXT:    {
-// CHECK-PREDICATOR-NEXT:      const MachineInstr *MI = FirstMI;
+// CHECK-PREDICATOR-NEXT:      {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} const MachineInstr *MI = FirstMI;
 // CHECK-PREDICATOR-NEXT:      if (!(
 // CHECK-PREDICATOR-NEXT:          ( MI->getOpcode() == Test::Inst0 )
 // CHECK-PREDICATOR-NEXT:          && true
@@ -257,6 +284,10 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:        if (FirstMI->getOperand(0).getReg() != SecondMI.getOperand(SrcOpIdx2).getReg())
 // CHECK-PREDICATOR-NEXT:          return false;
 // CHECK-PREDICATOR-NEXT:    }
+// CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
+// CHECK-PREDICATOR-NEXT:      ++NumTestSingleFusionPostRA;
+// CHECK-PREDICATOR-NEXT:    else
+// CHECK-PREDICATOR-NEXT:      ++NumTestSingleFusionPreRA;
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
 // CHECK-PREDICATOR-EMPTY:

diff  --git a/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp b/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
index 0ff0098407c65..1a446408e9a7f 100644
--- a/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
+++ b/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
@@ -19,15 +19,25 @@
 // `GET_<TargetName>_MACRO_FUSION_PRED_IMPL` and then including the generated
 // header file.
 //
+// Each predicator also maintains `Statistic`s that count how often the fusion
+// is matched, split into pre-RA and post-RA counters because both schedulers
+// run MacroFusion.
+//
 // The generated predicator will be like:
 //
 // ```
+// STATISTIC(NumNAMEPreRA, "Times NAME Triggered (pre-ra)");
+// STATISTIC(NumNAMEPostRA, "Times NAME Triggered (post-ra)");
 // bool isNAME(const TargetInstrInfo &TII,
 //             const TargetSubtargetInfo &STI,
 //             const MachineInstr *FirstMI,
 //             const MachineInstr &SecondMI) {
 //   auto &MRI = SecondMI.getMF()->getRegInfo();
 //   /* Predicates */
+//   if (SecondMI.getMF()->getProperties().hasNoVRegs())
+//     ++NumNAMEPostRA;
+//   else
+//     ++NumNAMEPreRA;
 //   return true;
 // }
 // ```
@@ -100,6 +110,15 @@ void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
         Fusion->getValueAsListOfDefs("Predicates");
     bool IsCommutable = Fusion->getValueAsBit("IsCommutable");
 
+    // Emit the statistics that count how often this fusion is matched. The
+    // pre-RA and post-RA schedulers both run MacroFusion, so they are split
+    // into separate counters (distinguished below via the `NoVRegs` property)
+    // to avoid conflating the two.
+    OS << "STATISTIC(Num" << Fusion->getName() << "PreRA, \"Times "
+       << Fusion->getName() << " Triggered (pre-ra)\");\n";
+    OS << "STATISTIC(Num" << Fusion->getName() << "PostRA, \"Times "
+       << Fusion->getName() << " Triggered (post-ra)\");\n";
+
     OS << "bool is" << Fusion->getName() << "(\n";
     OS.indent(4) << "const TargetInstrInfo &TII,\n";
     OS.indent(4) << "const TargetSubtargetInfo &STI,\n";
@@ -110,6 +129,11 @@ void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
 
     emitPredicates(Predicates, IsCommutable, PE, OS);
 
+    OS.indent(2) << "if (SecondMI.getMF()->getProperties().hasNoVRegs())\n";
+    OS.indent(4) << "++Num" << Fusion->getName() << "PostRA;\n";
+    OS.indent(2) << "else\n";
+    OS.indent(4) << "++Num" << Fusion->getName() << "PreRA;\n";
+
     OS.indent(2) << "return true;\n";
     OS << "}\n";
   }


        


More information about the llvm-commits mailing list