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

Pengcheng Wang via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 15 02:30:27 PDT 2026


https://github.com/wangpc-pp updated https://github.com/llvm/llvm-project/pull/209394

>From 25af0cf0f330985bf69d06c57ea28061ddba5138 Mon Sep 17 00:00:00 2001
From: Pengcheng Wang <wangpengcheng.pp at bytedance.com>
Date: Tue, 14 Jul 2026 15:22:06 +0800
Subject: [PATCH 1/4] [TableGen][RISCV] Generate pre/post-RA statistics for
 macro fusions

Emit a per-fusion Statistic from MacroFusionPredicatorEmitter that counts
how often each fusion is matched. Following the design suggested in
llvm/llvm-project#186499:

 * Add `bit GenerateStatistic = 1` to class Fusion so a fusion can opt out.
 * Emit a new `GET_<Target>_MACRO_FUSION_STATISTICS` section that defines
   ENABLE_STATISTIC and the Statistic counters, and guard the increments
   in each predicator behind ENABLE_STATISTIC.

Because both the pre-RA and post-RA schedulers run MacroFusion, the counts
are split into pre-ra/post-ra variants (distinguished via the NoVRegs
property) to avoid conflating the two.

Wire the new section into RISCVSubtarget.cpp and update the generic
TableGen test.

Co-authored-by: Sam Elliot <aelliott at qti.qualcomm.com>
Assisted-by: TRAE CLI (DeepSeek V4 Pro)
---
 llvm/include/llvm/Target/TargetMacroFusion.td |  6 ++
 llvm/lib/Target/RISCV/RISCVSubtarget.cpp      | 13 +++
 llvm/test/TableGen/MacroFusion.td             | 96 +++++++++++++++++++
 .../TableGen/MacroFusionPredicatorEmitter.cpp | 61 +++++++++++-
 4 files changed, 172 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/Target/TargetMacroFusion.td b/llvm/include/llvm/Target/TargetMacroFusion.td
index 1f070853c3084..02df4c4b52f99 100644
--- a/llvm/include/llvm/Target/TargetMacroFusion.td
+++ b/llvm/include/llvm/Target/TargetMacroFusion.td
@@ -107,6 +107,12 @@ class Fusion<string name, string fieldName, string desc, list<FusionPredicate> p
   : SubtargetFeature<"fusion-" # name, fieldName, "true", desc> {
   list<FusionPredicate> Predicates = predicates;
   bit IsCommutable = 0;
+
+  // When true, the MacroFusionPredicatorEmitter generates `Statistic`s that
+  // count how often this fusion is matched (see the
+  // `GET_<TargetName>_MACRO_FUSION_STATISTICS` section of the generated file).
+  // Set this to 0 to opt out, for fusions whose statistics are not interesting.
+  bit GenerateStatistic = 1;
 }
 
 // The generated predicator will be like:
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
index 524a48750e985..fa432da77f2ae 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/TargetRegistry.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -30,9 +31,21 @@ using namespace llvm;
 #define GET_SUBTARGETINFO_CTOR
 #include "RISCVGenSubtargetInfo.inc"
 
+#undef DEBUG_TYPE
+#define DEBUG_TYPE "riscv-macro-fusion"
+
+// The statistics section defines `ENABLE_STATISTIC` and the `Statistic`
+// counters, so it must be included before the predicator implementations that
+// reference them.
+#define GET_RISCV_MACRO_FUSION_STATISTICS
+#include "RISCVGenMacroFusion.inc"
+
 #define GET_RISCV_MACRO_FUSION_PRED_IMPL
 #include "RISCVGenMacroFusion.inc"
 
+#undef DEBUG_TYPE
+#define DEBUG_TYPE "riscv-subtarget"
+
 namespace llvm::RISCVTuneInfoTable {
 
 #define GET_RISCVTuneInfoTable_IMPL
diff --git a/llvm/test/TableGen/MacroFusion.td b/llvm/test/TableGen/MacroFusion.td
index 8fd823184a2b1..a597d10704760 100644
--- a/llvm/test/TableGen/MacroFusion.td
+++ b/llvm/test/TableGen/MacroFusion.td
@@ -68,6 +68,14 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
   bit IsCommutable = 1;
 }
 
+// A fusion that opts out of statistics collection.
+def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatisticFusion",
+                                        "Test Fusion Without Statistic",
+                                        CheckOpcode<[Inst0]>,
+                                        CheckOpcode<[Inst1]>> {
+  let GenerateStatistic = 0;
+}
+
 // CHECK-PREDICATOR:       #ifdef GET_Test_MACRO_FUSION_PRED_DECL
 // CHECK-PREDICATOR-NEXT:  #undef GET_Test_MACRO_FUSION_PRED_DECL
 // CHECK-PREDICATOR-EMPTY:
@@ -77,6 +85,7 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:  bool isTestCommutableFusion(const TargetInstrInfo &, const TargetSubtargetInfo &, const MachineInstr *, const MachineInstr &);
 // CHECK-PREDICATOR-NEXT:  bool isTestFirstSameRegFusion(const TargetInstrInfo &, const TargetSubtargetInfo &, const MachineInstr *, const MachineInstr &);
 // CHECK-PREDICATOR-NEXT:  bool isTestFusion(const TargetInstrInfo &, const TargetSubtargetInfo &, const MachineInstr *, const MachineInstr &);
+// CHECK-PREDICATOR-NEXT:  bool isTestNoStatisticFusion(const TargetInstrInfo &, const TargetSubtargetInfo &, const MachineInstr *, const MachineInstr &);
 // CHECK-PREDICATOR-NEXT:  bool isTestSingleFusion(const TargetInstrInfo &, const TargetSubtargetInfo &, const MachineInstr *, const MachineInstr &);
 // CHECK-PREDICATOR-EMPTY:
 // CHECK-PREDICATOR-NEXT:  } // namespace llvm
@@ -104,6 +113,12 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:      if (MI->getOperand(0).getReg() != Test::X0)
 // CHECK-PREDICATOR-NEXT:        return false;
 // CHECK-PREDICATOR-NEXT:    }
+// CHECK-PREDICATOR-NEXT:  #ifdef ENABLE_STATISTIC
+// CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
+// CHECK-PREDICATOR-NEXT:      ++NumTestBothFusionPredicatePostRA;
+// CHECK-PREDICATOR-NEXT:    else
+// CHECK-PREDICATOR-NEXT:      ++NumTestBothFusionPredicatePreRA;
+// CHECK-PREDICATOR-NEXT:  #endif // ENABLE_STATISTIC
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
 // CHECK-PREDICATOR-NEXT:  bool isTestCommutableFusion(
@@ -152,6 +167,12 @@ 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:  #ifdef ENABLE_STATISTIC
+// CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
+// CHECK-PREDICATOR-NEXT:      ++NumTestCommutableFusionPostRA;
+// CHECK-PREDICATOR-NEXT:    else
+// CHECK-PREDICATOR-NEXT:      ++NumTestCommutableFusionPreRA;
+// CHECK-PREDICATOR-NEXT:  #endif // ENABLE_STATISTIC
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
 // CHECK-PREDICATOR-NEXT:  bool isTestFirstSameRegFusion(
@@ -170,6 +191,12 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-NEXT:            return false;
 // CHECK-PREDICATOR-NEXT:      }
 // CHECK-PREDICATOR-NEXT:    }
+// CHECK-PREDICATOR-NEXT:  #ifdef ENABLE_STATISTIC
+// CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
+// CHECK-PREDICATOR-NEXT:      ++NumTestFirstSameRegFusionPostRA;
+// CHECK-PREDICATOR-NEXT:    else
+// CHECK-PREDICATOR-NEXT:      ++NumTestFirstSameRegFusionPreRA;
+// CHECK-PREDICATOR-NEXT:  #endif // ENABLE_STATISTIC
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
 // CHECK-PREDICATOR-NEXT:  bool isTestFusion(
@@ -206,6 +233,45 @@ 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:  #ifdef ENABLE_STATISTIC
+// CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
+// CHECK-PREDICATOR-NEXT:      ++NumTestFusionPostRA;
+// CHECK-PREDICATOR-NEXT:    else
+// CHECK-PREDICATOR-NEXT:      ++NumTestFusionPreRA;
+// CHECK-PREDICATOR-NEXT:  #endif // ENABLE_STATISTIC
+// CHECK-PREDICATOR-NEXT:    return true;
+// CHECK-PREDICATOR-NEXT:  }
+// CHECK-PREDICATOR-NEXT:  bool isTestNoStatisticFusion(
+// CHECK-PREDICATOR-NEXT:      const TargetInstrInfo &TII,
+// CHECK-PREDICATOR-NEXT:      const TargetSubtargetInfo &STI,
+// CHECK-PREDICATOR-NEXT:      const MachineInstr *FirstMI,
+// 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:      if (( MI->getOpcode() != Test::Inst1 ))
+// CHECK-PREDICATOR-NEXT:        return false;
+// CHECK-PREDICATOR-NEXT:    }
+// CHECK-PREDICATOR-NEXT:    if (!FirstMI)
+// CHECK-PREDICATOR-NEXT:      return true;
+// CHECK-PREDICATOR-NEXT:    {
+// CHECK-PREDICATOR-NEXT:      const MachineInstr *MI = FirstMI;
+// CHECK-PREDICATOR-NEXT:      if (( MI->getOpcode() != Test::Inst0 ))
+// CHECK-PREDICATOR-NEXT:        return false;
+// CHECK-PREDICATOR-NEXT:    }
+// CHECK-PREDICATOR-NEXT:    if (!SecondMI.getOperand(0).getReg().isVirtual()) {
+// CHECK-PREDICATOR-NEXT:      if (SecondMI.getOperand(0).getReg() != SecondMI.getOperand(1).getReg())
+// CHECK-PREDICATOR-NEXT:        return false;
+// CHECK-PREDICATOR-NEXT:    }
+// CHECK-PREDICATOR-NEXT:    {
+// CHECK-PREDICATOR-NEXT:      Register FirstDest = FirstMI->getOperand(0).getReg();
+// CHECK-PREDICATOR-NEXT:      if (FirstDest.isVirtual() && !MRI.hasOneNonDBGUse(FirstDest))
+// CHECK-PREDICATOR-NEXT:        return false;
+// CHECK-PREDICATOR-NEXT:    }
+// CHECK-PREDICATOR-NEXT:    if (!(FirstMI->getOperand(0).isReg() &&
+// 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:    return true;
 // CHECK-PREDICATOR-NEXT:  }
 // CHECK-PREDICATOR-NEXT:  bool isTestSingleFusion(
@@ -257,6 +323,12 @@ 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:  #ifdef ENABLE_STATISTIC
+// CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
+// CHECK-PREDICATOR-NEXT:      ++NumTestSingleFusionPostRA;
+// CHECK-PREDICATOR-NEXT:    else
+// CHECK-PREDICATOR-NEXT:      ++NumTestSingleFusionPreRA;
+// CHECK-PREDICATOR-NEXT:  #endif // ENABLE_STATISTIC
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
 // CHECK-PREDICATOR-EMPTY:
@@ -264,6 +336,29 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-PREDICATOR-EMPTY:
 // CHECK-PREDICATOR-NEXT:  #endif // GET_Test_MACRO_FUSION_PRED_IMPL
 
+// CHECK-PREDICATOR:       #ifdef GET_Test_MACRO_FUSION_STATISTICS
+// CHECK-PREDICATOR-NEXT:  #undef GET_Test_MACRO_FUSION_STATISTICS
+// CHECK-PREDICATOR-EMPTY:
+// CHECK-PREDICATOR-NEXT:  #ifndef ENABLE_STATISTIC
+// CHECK-PREDICATOR-NEXT:  #define ENABLE_STATISTIC
+// CHECK-PREDICATOR-NEXT:  #endif
+// 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:  STATISTIC(NumTestCommutableFusionPreRA, "Times TestCommutableFusion Triggered (pre-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestCommutableFusionPostRA, "Times TestCommutableFusion Triggered (post-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFirstSameRegFusionPreRA, "Times TestFirstSameRegFusion Triggered (pre-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFirstSameRegFusionPostRA, "Times TestFirstSameRegFusion Triggered (post-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFusionPreRA, "Times TestFusion Triggered (pre-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFusionPostRA, "Times TestFusion Triggered (post-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestSingleFusionPreRA, "Times TestSingleFusion Triggered (pre-ra)");
+// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestSingleFusionPostRA, "Times TestSingleFusion Triggered (post-ra)");
+// CHECK-PREDICATOR-EMPTY:
+// CHECK-PREDICATOR-NEXT:  } // namespace llvm
+// CHECK-PREDICATOR-EMPTY:
+// CHECK-PREDICATOR-NEXT:  #endif // GET_Test_MACRO_FUSION_STATISTICS
+
 // Check that we have generated `getMacroFusions()` function.
 // CHECK-SUBTARGET:      std::vector<MacroFusionPredTy> getMacroFusions() const final;
 
@@ -273,6 +368,7 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
 // CHECK-SUBTARGET-NEXT:   if (hasFeature(Test::TestCommutableFusion)) Fusions.push_back(llvm::isTestCommutableFusion);
 // CHECK-SUBTARGET-NEXT:   if (hasFeature(Test::TestFirstSameRegFusion)) Fusions.push_back(llvm::isTestFirstSameRegFusion);
 // CHECK-SUBTARGET-NEXT:   if (hasFeature(Test::TestFusion)) Fusions.push_back(llvm::isTestFusion);
+// CHECK-SUBTARGET-NEXT:   if (hasFeature(Test::TestNoStatisticFusion)) Fusions.push_back(llvm::isTestNoStatisticFusion);
 // CHECK-SUBTARGET-NEXT:   if (hasFeature(Test::TestSingleFusion)) Fusions.push_back(llvm::isTestSingleFusion);
 // CHECK-SUBTARGET-NEXT:   return Fusions;
 // CHECK-SUBTARGET-NEXT: }
diff --git a/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp b/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
index 0ff0098407c65..37b173b2c4723 100644
--- a/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
+++ b/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
@@ -13,12 +13,23 @@
 // predicators for checking if input instructions can be fused. These
 // predicators can used in `MacroFusion` DAG mutation.
 //
-// The generated header file contains two parts: one for predicator
-// declarations and one for predicator implementations. The user can get them
-// by defining macro `GET_<TargetName>_MACRO_FUSION_PRED_DECL` or
-// `GET_<TargetName>_MACRO_FUSION_PRED_IMPL` and then including the generated
+// The generated header file contains three parts: one for predicator
+// declarations, one for predicator implementations, and one for the
+// definitions of the `Statistic`s that count how often each fusion is matched.
+// The user can get them by defining macro
+// `GET_<TargetName>_MACRO_FUSION_PRED_DECL`,
+// `GET_<TargetName>_MACRO_FUSION_PRED_IMPL` or
+// `GET_<TargetName>_MACRO_FUSION_STATISTICS` and then including the generated
 // header file.
 //
+// The statistics are opt-in: the predicator implementations only increment the
+// counters when `ENABLE_STATISTIC` is defined, and the
+// `GET_<TargetName>_MACRO_FUSION_STATISTICS` section both defines
+// `ENABLE_STATISTIC` and emits the `Statistic` definitions. So a target that
+// wants the statistics should include the `..._STATISTICS` section *before* the
+// `..._PRED_IMPL` section. Per-fusion, the statistics can be disabled by
+// setting `GenerateStatistic = 0` on the `Fusion` definition.
+//
 // The generated predicator will be like:
 //
 // ```
@@ -60,6 +71,8 @@ class MacroFusionPredicatorEmitter {
                            PredicateExpander &PE, raw_ostream &OS);
   void emitMacroFusionImpl(ArrayRef<const Record *> Fusions,
                            PredicateExpander &PE, raw_ostream &OS);
+  void emitMacroFusionStatistics(ArrayRef<const Record *> Fusions,
+                                 raw_ostream &OS);
   void emitPredicates(ArrayRef<const Record *> FirstPredicate,
                       bool IsCommutable, PredicateExpander &PE,
                       raw_ostream &OS);
@@ -99,6 +112,7 @@ void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
     std::vector<const Record *> Predicates =
         Fusion->getValueAsListOfDefs("Predicates");
     bool IsCommutable = Fusion->getValueAsBit("IsCommutable");
+    bool GenerateStatistic = Fusion->getValueAsBit("GenerateStatistic");
 
     OS << "bool is" << Fusion->getName() << "(\n";
     OS.indent(4) << "const TargetInstrInfo &TII,\n";
@@ -110,11 +124,48 @@ void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
 
     emitPredicates(Predicates, IsCommutable, PE, OS);
 
+    // Bump the statistics that count how often this fusion is matched. The
+    // pre-RA and post-RA schedulers both run MacroFusion, so we distinguish
+    // them (using the `NoVRegs` property) to avoid conflating the two.
+    if (GenerateStatistic) {
+      OS << "#ifdef ENABLE_STATISTIC\n";
+      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 << "#endif // ENABLE_STATISTIC\n";
+    }
+
     OS.indent(2) << "return true;\n";
     OS << "}\n";
   }
 }
 
+void MacroFusionPredicatorEmitter::emitMacroFusionStatistics(
+    ArrayRef<const Record *> Fusions, raw_ostream &OS) {
+  IfDefEmitter IfDef(
+      OS, ("GET_" + Target.getName() + "_MACRO_FUSION_STATISTICS").str());
+
+  // Requesting the statistics implies collecting them, so make sure the
+  // increments emitted in the predicators (guarded by `ENABLE_STATISTIC`) are
+  // compiled in when this section is included.
+  OS << "#ifndef ENABLE_STATISTIC\n";
+  OS << "#define ENABLE_STATISTIC\n";
+  OS << "#endif\n";
+
+  NamespaceEmitter LlvmNS(OS, "llvm");
+
+  for (const Record *Fusion : Fusions) {
+    if (!Fusion->getValueAsBit("GenerateStatistic"))
+      continue;
+
+    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";
+  }
+}
+
 void MacroFusionPredicatorEmitter::emitPredicates(
     ArrayRef<const Record *> Predicates, bool IsCommutable,
     PredicateExpander &PE, raw_ostream &OS) {
@@ -305,6 +356,8 @@ void MacroFusionPredicatorEmitter::run(raw_ostream &OS) {
   emitMacroFusionDecl(Fusions, PE, OS);
   OS << "\n";
   emitMacroFusionImpl(Fusions, PE, OS);
+  OS << "\n";
+  emitMacroFusionStatistics(Fusions, OS);
 }
 
 static TableGen::Emitter::OptClass<MacroFusionPredicatorEmitter>

>From 54f88be9f78a5e9ef1c4d45c5625866385540c77 Mon Sep 17 00:00:00 2001
From: Pengcheng Wang <wangpengcheng.pp at bytedance.com>
Date: Wed, 15 Jul 2026 16:41:03 +0800
Subject: [PATCH 2/4] [TableGen] Use `true` for Fusion.GenerateStatistic
 default

Prefer the boolean literal `true` over `1` for the `GenerateStatistic`
bit and update the doc comment accordingly. NFC.

Assisted-by: TRAE CLI (DeepSeek V4 Pro)
---
 llvm/include/llvm/Target/TargetMacroFusion.td | 5 +++--
 llvm/test/TableGen/MacroFusion.td             | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/Target/TargetMacroFusion.td b/llvm/include/llvm/Target/TargetMacroFusion.td
index 02df4c4b52f99..e89483b2c2598 100644
--- a/llvm/include/llvm/Target/TargetMacroFusion.td
+++ b/llvm/include/llvm/Target/TargetMacroFusion.td
@@ -111,8 +111,9 @@ class Fusion<string name, string fieldName, string desc, list<FusionPredicate> p
   // When true, the MacroFusionPredicatorEmitter generates `Statistic`s that
   // count how often this fusion is matched (see the
   // `GET_<TargetName>_MACRO_FUSION_STATISTICS` section of the generated file).
-  // Set this to 0 to opt out, for fusions whose statistics are not interesting.
-  bit GenerateStatistic = 1;
+  // Set this to false to opt out, for fusions whose statistics are not
+  // interesting.
+  bit GenerateStatistic = true;
 }
 
 // The generated predicator will be like:
diff --git a/llvm/test/TableGen/MacroFusion.td b/llvm/test/TableGen/MacroFusion.td
index a597d10704760..de88ab10f1d08 100644
--- a/llvm/test/TableGen/MacroFusion.td
+++ b/llvm/test/TableGen/MacroFusion.td
@@ -73,7 +73,7 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
                                         "Test Fusion Without Statistic",
                                         CheckOpcode<[Inst0]>,
                                         CheckOpcode<[Inst1]>> {
-  let GenerateStatistic = 0;
+  let GenerateStatistic = false;
 }
 
 // CHECK-PREDICATOR:       #ifdef GET_Test_MACRO_FUSION_PRED_DECL

>From 97547794b96702461897a0693b58203723aba5d6 Mon Sep 17 00:00:00 2001
From: Pengcheng Wang <wangpengcheng.pp at bytedance.com>
Date: Wed, 15 Jul 2026 17:04:09 +0800
Subject: [PATCH 3/4] [TableGen][RISCV] Drop ENABLE_STATISTIC, emit fusion
 statistics inline

Simplify the macro fusion statistics to a single level of control. Instead
of a separate `GET_<Target>_MACRO_FUSION_STATISTICS` section that defines
`ENABLE_STATISTIC` and guards the counters, emit the `STATISTIC` definitions
and increments directly in the predicator implementations.

Untriggered `Statistic`s never appear in `-stats` output and are no-ops when
`LLVM_ENABLE_STATS` is off, so there is no cost to always emitting them. A
target that does not want the counters for a specific fusion can still opt
out via `GenerateStatistic = false`.

Assisted-by: TRAE CLI (DeepSeek V4 Pro)
---
 llvm/include/llvm/Target/TargetMacroFusion.td |  7 +-
 llvm/lib/Target/RISCV/RISCVSubtarget.cpp      |  6 --
 llvm/test/TableGen/MacroFusion.td             | 68 ++++++-----------
 .../TableGen/MacroFusionPredicatorEmitter.cpp | 73 +++++++------------
 4 files changed, 51 insertions(+), 103 deletions(-)

diff --git a/llvm/include/llvm/Target/TargetMacroFusion.td b/llvm/include/llvm/Target/TargetMacroFusion.td
index e89483b2c2598..e7ca52b190690 100644
--- a/llvm/include/llvm/Target/TargetMacroFusion.td
+++ b/llvm/include/llvm/Target/TargetMacroFusion.td
@@ -109,10 +109,9 @@ class Fusion<string name, string fieldName, string desc, list<FusionPredicate> p
   bit IsCommutable = 0;
 
   // When true, the MacroFusionPredicatorEmitter generates `Statistic`s that
-  // count how often this fusion is matched (see the
-  // `GET_<TargetName>_MACRO_FUSION_STATISTICS` section of the generated file).
-  // Set this to false to opt out, for fusions whose statistics are not
-  // interesting.
+  // count how often this fusion is matched (split into pre-RA and post-RA
+  // counters). Set this to false to opt out, for fusions whose statistics are
+  // not interesting.
   bit GenerateStatistic = true;
 }
 
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
index fa432da77f2ae..eded8677eed32 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
@@ -34,12 +34,6 @@ using namespace llvm;
 #undef DEBUG_TYPE
 #define DEBUG_TYPE "riscv-macro-fusion"
 
-// The statistics section defines `ENABLE_STATISTIC` and the `Statistic`
-// counters, so it must be included before the predicator implementations that
-// reference them.
-#define GET_RISCV_MACRO_FUSION_STATISTICS
-#include "RISCVGenMacroFusion.inc"
-
 #define GET_RISCV_MACRO_FUSION_PRED_IMPL
 #include "RISCVGenMacroFusion.inc"
 
diff --git a/llvm/test/TableGen/MacroFusion.td b/llvm/test/TableGen/MacroFusion.td
index de88ab10f1d08..f606157523114 100644
--- a/llvm/test/TableGen/MacroFusion.td
+++ b/llvm/test/TableGen/MacroFusion.td
@@ -91,12 +91,15 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // 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,
@@ -104,23 +107,23 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // 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:  #ifdef ENABLE_STATISTIC
 // CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
 // CHECK-PREDICATOR-NEXT:      ++NumTestBothFusionPredicatePostRA;
 // CHECK-PREDICATOR-NEXT:    else
 // CHECK-PREDICATOR-NEXT:      ++NumTestBothFusionPredicatePreRA;
-// CHECK-PREDICATOR-NEXT:  #endif // ENABLE_STATISTIC
 // 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,
@@ -128,7 +131,7 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // 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
@@ -138,7 +141,7 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // 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:    }
@@ -167,14 +170,14 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // CHECK-PREDICATOR-NEXT:        if (FirstMI->getOperand(0).getReg() != SecondMI.getOperand(SrcOpIdx2).getReg())
 // CHECK-PREDICATOR-NEXT:          return false;
 // CHECK-PREDICATOR-NEXT:    }
-// CHECK-PREDICATOR-NEXT:  #ifdef ENABLE_STATISTIC
 // CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
 // CHECK-PREDICATOR-NEXT:      ++NumTestCommutableFusionPostRA;
 // CHECK-PREDICATOR-NEXT:    else
 // CHECK-PREDICATOR-NEXT:      ++NumTestCommutableFusionPreRA;
-// CHECK-PREDICATOR-NEXT:  #endif // ENABLE_STATISTIC
 // 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,
@@ -191,14 +194,14 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // CHECK-PREDICATOR-NEXT:            return false;
 // CHECK-PREDICATOR-NEXT:      }
 // CHECK-PREDICATOR-NEXT:    }
-// CHECK-PREDICATOR-NEXT:  #ifdef ENABLE_STATISTIC
 // CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
 // CHECK-PREDICATOR-NEXT:      ++NumTestFirstSameRegFusionPostRA;
 // CHECK-PREDICATOR-NEXT:    else
 // CHECK-PREDICATOR-NEXT:      ++NumTestFirstSameRegFusionPreRA;
-// CHECK-PREDICATOR-NEXT:  #endif // ENABLE_STATISTIC
 // 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,
@@ -206,7 +209,7 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // 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
@@ -216,7 +219,7 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // 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:    }
@@ -233,12 +236,10 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // 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:  #ifdef ENABLE_STATISTIC
 // CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
 // CHECK-PREDICATOR-NEXT:      ++NumTestFusionPostRA;
 // CHECK-PREDICATOR-NEXT:    else
 // CHECK-PREDICATOR-NEXT:      ++NumTestFusionPreRA;
-// CHECK-PREDICATOR-NEXT:  #endif // ENABLE_STATISTIC
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
 // CHECK-PREDICATOR-NEXT:  bool isTestNoStatisticFusion(
@@ -248,14 +249,14 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // 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 (( MI->getOpcode() != Test::Inst1 ))
 // CHECK-PREDICATOR-NEXT:        return false;
 // CHECK-PREDICATOR-NEXT:    }
 // 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:    }
@@ -274,6 +275,8 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // CHECK-PREDICATOR-NEXT:      return false;
 // 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,
@@ -281,7 +284,7 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // 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
@@ -291,7 +294,7 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // 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
@@ -323,12 +326,10 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // CHECK-PREDICATOR-NEXT:        if (FirstMI->getOperand(0).getReg() != SecondMI.getOperand(SrcOpIdx2).getReg())
 // CHECK-PREDICATOR-NEXT:          return false;
 // CHECK-PREDICATOR-NEXT:    }
-// CHECK-PREDICATOR-NEXT:  #ifdef ENABLE_STATISTIC
 // CHECK-PREDICATOR-NEXT:    if (SecondMI.getMF()->getProperties().hasNoVRegs())
 // CHECK-PREDICATOR-NEXT:      ++NumTestSingleFusionPostRA;
 // CHECK-PREDICATOR-NEXT:    else
 // CHECK-PREDICATOR-NEXT:      ++NumTestSingleFusionPreRA;
-// CHECK-PREDICATOR-NEXT:  #endif // ENABLE_STATISTIC
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
 // CHECK-PREDICATOR-EMPTY:
@@ -336,29 +337,6 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // CHECK-PREDICATOR-EMPTY:
 // CHECK-PREDICATOR-NEXT:  #endif // GET_Test_MACRO_FUSION_PRED_IMPL
 
-// CHECK-PREDICATOR:       #ifdef GET_Test_MACRO_FUSION_STATISTICS
-// CHECK-PREDICATOR-NEXT:  #undef GET_Test_MACRO_FUSION_STATISTICS
-// CHECK-PREDICATOR-EMPTY:
-// CHECK-PREDICATOR-NEXT:  #ifndef ENABLE_STATISTIC
-// CHECK-PREDICATOR-NEXT:  #define ENABLE_STATISTIC
-// CHECK-PREDICATOR-NEXT:  #endif
-// 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:  STATISTIC(NumTestCommutableFusionPreRA, "Times TestCommutableFusion Triggered (pre-ra)");
-// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestCommutableFusionPostRA, "Times TestCommutableFusion Triggered (post-ra)");
-// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFirstSameRegFusionPreRA, "Times TestFirstSameRegFusion Triggered (pre-ra)");
-// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFirstSameRegFusionPostRA, "Times TestFirstSameRegFusion Triggered (post-ra)");
-// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFusionPreRA, "Times TestFusion Triggered (pre-ra)");
-// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestFusionPostRA, "Times TestFusion Triggered (post-ra)");
-// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestSingleFusionPreRA, "Times TestSingleFusion Triggered (pre-ra)");
-// CHECK-PREDICATOR-NEXT:  STATISTIC(NumTestSingleFusionPostRA, "Times TestSingleFusion Triggered (post-ra)");
-// CHECK-PREDICATOR-EMPTY:
-// CHECK-PREDICATOR-NEXT:  } // namespace llvm
-// CHECK-PREDICATOR-EMPTY:
-// CHECK-PREDICATOR-NEXT:  #endif // GET_Test_MACRO_FUSION_STATISTICS
-
 // Check that we have generated `getMacroFusions()` function.
 // CHECK-SUBTARGET:      std::vector<MacroFusionPredTy> getMacroFusions() const final;
 
diff --git a/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp b/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
index 37b173b2c4723..da5763a8c8fbf 100644
--- a/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
+++ b/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
@@ -13,32 +13,32 @@
 // predicators for checking if input instructions can be fused. These
 // predicators can used in `MacroFusion` DAG mutation.
 //
-// The generated header file contains three parts: one for predicator
-// declarations, one for predicator implementations, and one for the
-// definitions of the `Statistic`s that count how often each fusion is matched.
-// The user can get them by defining macro
-// `GET_<TargetName>_MACRO_FUSION_PRED_DECL`,
-// `GET_<TargetName>_MACRO_FUSION_PRED_IMPL` or
-// `GET_<TargetName>_MACRO_FUSION_STATISTICS` and then including the generated
+// The generated header file contains two parts: one for predicator
+// declarations and one for predicator implementations. The user can get them
+// by defining macro `GET_<TargetName>_MACRO_FUSION_PRED_DECL` or
+// `GET_<TargetName>_MACRO_FUSION_PRED_IMPL` and then including the generated
 // header file.
 //
-// The statistics are opt-in: the predicator implementations only increment the
-// counters when `ENABLE_STATISTIC` is defined, and the
-// `GET_<TargetName>_MACRO_FUSION_STATISTICS` section both defines
-// `ENABLE_STATISTIC` and emits the `Statistic` definitions. So a target that
-// wants the statistics should include the `..._STATISTICS` section *before* the
-// `..._PRED_IMPL` section. Per-fusion, the statistics can be disabled by
-// setting `GenerateStatistic = 0` on the `Fusion` definition.
+// 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. Per-fusion, these can be disabled by setting
+// `GenerateStatistic = false` on the `Fusion` definition.
 //
 // 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;
 // }
 // ```
@@ -71,8 +71,6 @@ class MacroFusionPredicatorEmitter {
                            PredicateExpander &PE, raw_ostream &OS);
   void emitMacroFusionImpl(ArrayRef<const Record *> Fusions,
                            PredicateExpander &PE, raw_ostream &OS);
-  void emitMacroFusionStatistics(ArrayRef<const Record *> Fusions,
-                                 raw_ostream &OS);
   void emitPredicates(ArrayRef<const Record *> FirstPredicate,
                       bool IsCommutable, PredicateExpander &PE,
                       raw_ostream &OS);
@@ -114,6 +112,17 @@ void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
     bool IsCommutable = Fusion->getValueAsBit("IsCommutable");
     bool GenerateStatistic = Fusion->getValueAsBit("GenerateStatistic");
 
+    // 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.
+    if (GenerateStatistic) {
+      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";
@@ -124,16 +133,11 @@ void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
 
     emitPredicates(Predicates, IsCommutable, PE, OS);
 
-    // Bump the statistics that count how often this fusion is matched. The
-    // pre-RA and post-RA schedulers both run MacroFusion, so we distinguish
-    // them (using the `NoVRegs` property) to avoid conflating the two.
     if (GenerateStatistic) {
-      OS << "#ifdef ENABLE_STATISTIC\n";
       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 << "#endif // ENABLE_STATISTIC\n";
     }
 
     OS.indent(2) << "return true;\n";
@@ -141,31 +145,6 @@ void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
   }
 }
 
-void MacroFusionPredicatorEmitter::emitMacroFusionStatistics(
-    ArrayRef<const Record *> Fusions, raw_ostream &OS) {
-  IfDefEmitter IfDef(
-      OS, ("GET_" + Target.getName() + "_MACRO_FUSION_STATISTICS").str());
-
-  // Requesting the statistics implies collecting them, so make sure the
-  // increments emitted in the predicators (guarded by `ENABLE_STATISTIC`) are
-  // compiled in when this section is included.
-  OS << "#ifndef ENABLE_STATISTIC\n";
-  OS << "#define ENABLE_STATISTIC\n";
-  OS << "#endif\n";
-
-  NamespaceEmitter LlvmNS(OS, "llvm");
-
-  for (const Record *Fusion : Fusions) {
-    if (!Fusion->getValueAsBit("GenerateStatistic"))
-      continue;
-
-    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";
-  }
-}
-
 void MacroFusionPredicatorEmitter::emitPredicates(
     ArrayRef<const Record *> Predicates, bool IsCommutable,
     PredicateExpander &PE, raw_ostream &OS) {
@@ -356,8 +335,6 @@ void MacroFusionPredicatorEmitter::run(raw_ostream &OS) {
   emitMacroFusionDecl(Fusions, PE, OS);
   OS << "\n";
   emitMacroFusionImpl(Fusions, PE, OS);
-  OS << "\n";
-  emitMacroFusionStatistics(Fusions, OS);
 }
 
 static TableGen::Emitter::OptClass<MacroFusionPredicatorEmitter>

>From 12985dcd1e1be571011a3b804f1fc2ddd63146de Mon Sep 17 00:00:00 2001
From: Pengcheng Wang <wangpengcheng.pp at bytedance.com>
Date: Wed, 15 Jul 2026 17:27:11 +0800
Subject: [PATCH 4/4] [TableGen] Always emit macro fusion statistics, drop
 GenerateStatistic

Remove the per-fusion `GenerateStatistic` opt-out and unconditionally emit
the pre-RA/post-RA statistics for every fusion. Untriggered `Statistic`s
never show up in `-stats` output and are no-ops when `LLVM_ENABLE_STATS` is
off, so a control knob is unnecessary.

Assisted-by: TRAE CLI (DeepSeek V4 Pro)
---
 llvm/include/llvm/Target/TargetMacroFusion.td |  6 ---
 llvm/test/TableGen/MacroFusion.td             | 43 -------------------
 .../TableGen/MacroFusionPredicatorEmitter.cpp | 24 ++++-------
 3 files changed, 9 insertions(+), 64 deletions(-)

diff --git a/llvm/include/llvm/Target/TargetMacroFusion.td b/llvm/include/llvm/Target/TargetMacroFusion.td
index e7ca52b190690..1f070853c3084 100644
--- a/llvm/include/llvm/Target/TargetMacroFusion.td
+++ b/llvm/include/llvm/Target/TargetMacroFusion.td
@@ -107,12 +107,6 @@ class Fusion<string name, string fieldName, string desc, list<FusionPredicate> p
   : SubtargetFeature<"fusion-" # name, fieldName, "true", desc> {
   list<FusionPredicate> Predicates = predicates;
   bit IsCommutable = 0;
-
-  // When true, the MacroFusionPredicatorEmitter generates `Statistic`s that
-  // count how often this fusion is matched (split into pre-RA and post-RA
-  // counters). Set this to false to opt out, for fusions whose statistics are
-  // not interesting.
-  bit GenerateStatistic = true;
 }
 
 // The generated predicator will be like:
diff --git a/llvm/test/TableGen/MacroFusion.td b/llvm/test/TableGen/MacroFusion.td
index f606157523114..64c0885874e71 100644
--- a/llvm/test/TableGen/MacroFusion.td
+++ b/llvm/test/TableGen/MacroFusion.td
@@ -68,14 +68,6 @@ def TestFirstSameRegFusion: Fusion<"test-first-same-reg", "HasTestFirstSameRegFu
   bit IsCommutable = 1;
 }
 
-// A fusion that opts out of statistics collection.
-def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatisticFusion",
-                                        "Test Fusion Without Statistic",
-                                        CheckOpcode<[Inst0]>,
-                                        CheckOpcode<[Inst1]>> {
-  let GenerateStatistic = false;
-}
-
 // CHECK-PREDICATOR:       #ifdef GET_Test_MACRO_FUSION_PRED_DECL
 // CHECK-PREDICATOR-NEXT:  #undef GET_Test_MACRO_FUSION_PRED_DECL
 // CHECK-PREDICATOR-EMPTY:
@@ -85,7 +77,6 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // CHECK-PREDICATOR-NEXT:  bool isTestCommutableFusion(const TargetInstrInfo &, const TargetSubtargetInfo &, const MachineInstr *, const MachineInstr &);
 // CHECK-PREDICATOR-NEXT:  bool isTestFirstSameRegFusion(const TargetInstrInfo &, const TargetSubtargetInfo &, const MachineInstr *, const MachineInstr &);
 // CHECK-PREDICATOR-NEXT:  bool isTestFusion(const TargetInstrInfo &, const TargetSubtargetInfo &, const MachineInstr *, const MachineInstr &);
-// CHECK-PREDICATOR-NEXT:  bool isTestNoStatisticFusion(const TargetInstrInfo &, const TargetSubtargetInfo &, const MachineInstr *, const MachineInstr &);
 // CHECK-PREDICATOR-NEXT:  bool isTestSingleFusion(const TargetInstrInfo &, const TargetSubtargetInfo &, const MachineInstr *, const MachineInstr &);
 // CHECK-PREDICATOR-EMPTY:
 // CHECK-PREDICATOR-NEXT:  } // namespace llvm
@@ -242,39 +233,6 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // CHECK-PREDICATOR-NEXT:      ++NumTestFusionPreRA;
 // CHECK-PREDICATOR-NEXT:    return true;
 // CHECK-PREDICATOR-NEXT:  }
-// CHECK-PREDICATOR-NEXT:  bool isTestNoStatisticFusion(
-// CHECK-PREDICATOR-NEXT:      const TargetInstrInfo &TII,
-// CHECK-PREDICATOR-NEXT:      const TargetSubtargetInfo &STI,
-// CHECK-PREDICATOR-NEXT:      const MachineInstr *FirstMI,
-// CHECK-PREDICATOR-NEXT:      const MachineInstr &SecondMI) {
-// CHECK-PREDICATOR-NEXT:    {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} auto &MRI = SecondMI.getMF()->getRegInfo();
-// CHECK-PREDICATOR-NEXT:    {
-// CHECK-PREDICATOR-NEXT:      {{[[]}}{{[[]}}maybe_unused{{[]]}}{{[]]}} const MachineInstr *MI = &SecondMI;
-// CHECK-PREDICATOR-NEXT:      if (( MI->getOpcode() != Test::Inst1 ))
-// CHECK-PREDICATOR-NEXT:        return false;
-// CHECK-PREDICATOR-NEXT:    }
-// CHECK-PREDICATOR-NEXT:    if (!FirstMI)
-// CHECK-PREDICATOR-NEXT:      return true;
-// CHECK-PREDICATOR-NEXT:    {
-// 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:    }
-// CHECK-PREDICATOR-NEXT:    if (!SecondMI.getOperand(0).getReg().isVirtual()) {
-// CHECK-PREDICATOR-NEXT:      if (SecondMI.getOperand(0).getReg() != SecondMI.getOperand(1).getReg())
-// CHECK-PREDICATOR-NEXT:        return false;
-// CHECK-PREDICATOR-NEXT:    }
-// CHECK-PREDICATOR-NEXT:    {
-// CHECK-PREDICATOR-NEXT:      Register FirstDest = FirstMI->getOperand(0).getReg();
-// CHECK-PREDICATOR-NEXT:      if (FirstDest.isVirtual() && !MRI.hasOneNonDBGUse(FirstDest))
-// CHECK-PREDICATOR-NEXT:        return false;
-// CHECK-PREDICATOR-NEXT:    }
-// CHECK-PREDICATOR-NEXT:    if (!(FirstMI->getOperand(0).isReg() &&
-// 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:    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(
@@ -346,7 +304,6 @@ def TestNoStatisticFusion: SimpleFusion<"test-no-statistic", "HasTestNoStatistic
 // CHECK-SUBTARGET-NEXT:   if (hasFeature(Test::TestCommutableFusion)) Fusions.push_back(llvm::isTestCommutableFusion);
 // CHECK-SUBTARGET-NEXT:   if (hasFeature(Test::TestFirstSameRegFusion)) Fusions.push_back(llvm::isTestFirstSameRegFusion);
 // CHECK-SUBTARGET-NEXT:   if (hasFeature(Test::TestFusion)) Fusions.push_back(llvm::isTestFusion);
-// CHECK-SUBTARGET-NEXT:   if (hasFeature(Test::TestNoStatisticFusion)) Fusions.push_back(llvm::isTestNoStatisticFusion);
 // CHECK-SUBTARGET-NEXT:   if (hasFeature(Test::TestSingleFusion)) Fusions.push_back(llvm::isTestSingleFusion);
 // CHECK-SUBTARGET-NEXT:   return Fusions;
 // CHECK-SUBTARGET-NEXT: }
diff --git a/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp b/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
index da5763a8c8fbf..1a446408e9a7f 100644
--- a/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
+++ b/llvm/utils/TableGen/MacroFusionPredicatorEmitter.cpp
@@ -21,8 +21,7 @@
 //
 // 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. Per-fusion, these can be disabled by setting
-// `GenerateStatistic = false` on the `Fusion` definition.
+// run MacroFusion.
 //
 // The generated predicator will be like:
 //
@@ -110,18 +109,15 @@ void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
     std::vector<const Record *> Predicates =
         Fusion->getValueAsListOfDefs("Predicates");
     bool IsCommutable = Fusion->getValueAsBit("IsCommutable");
-    bool GenerateStatistic = Fusion->getValueAsBit("GenerateStatistic");
 
     // 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.
-    if (GenerateStatistic) {
-      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 << "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";
@@ -133,12 +129,10 @@ void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
 
     emitPredicates(Predicates, IsCommutable, PE, OS);
 
-    if (GenerateStatistic) {
-      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) << "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