[llvm] [TableGen] Pack generated MCOperandInfo sequences (PR #202648)

David Zbarsky via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 9 07:01:18 PDT 2026


https://github.com/dzbarsky created https://github.com/llvm/llvm-project/pull/202648

InstrInfoEmitter currently deduplicates only identical complete operand sequences. Target descriptions also contain many sequences that are substrings or overlapping suffixes of longer sequences, leaving redundant MCOperandInfo records in every consumer.

Sort unique sequences longest-first, reuse sequences already present in the packed table, and overlap new sequences with the table suffix. This changes only the generated table layout; MCInstrDesc, MCOperandInfo, and runtime lookups are unchanged. Add focused coverage for suffix reuse.

In a stripped Darwin arm64 all-target all-tools build, the binary decreases from 142,989,664 to 142,874,088 bytes, saving 115,576 bytes (0.081%). Representative object reductions are 48,864 bytes for AMDGPU, 13,496 bytes for X86, and 4,928 bytes for RISCV; six sampled target objects save 77,928 bytes total.

Generated code and disassembly were byte-identical for X86, AArch64, RISCV, ARM, AMDGPU, BPF, SystemZ, WebAssembly, and NVPTX. The tramp3d-v4 and miniGMG objects were also byte-identical.

Existing performance workloads were neutral: AArch64 llvm-mca was +0.6%, tramp3d-v4 was -1.2%, and miniGMG was within timer granularity. TableGen generation time changed by +0.5% for X86 and +0.3% for AMDGPU.

Work towards #202616

>From 6b40e6fc5ac857438bcd3992c7b1bb19c3fc79a6 Mon Sep 17 00:00:00 2001
From: David Zbarsky <dzbarsky at gmail.com>
Date: Mon, 8 Jun 2026 16:02:20 -0400
Subject: [PATCH] [TableGen] Pack generated MCOperandInfo sequences

InstrInfoEmitter currently deduplicates only identical complete operand sequences. Target descriptions also contain many sequences that are substrings or overlapping suffixes of longer sequences, leaving redundant MCOperandInfo records in every consumer.

Sort unique sequences longest-first, reuse sequences already present in the packed table, and overlap new sequences with the table suffix. This changes only the generated table layout; MCInstrDesc, MCOperandInfo, and runtime lookups are unchanged. Add focused coverage for suffix reuse.

In a stripped Darwin arm64 all-target all-tools build, the binary decreases from 142,989,664 to 142,874,088 bytes, saving 115,576 bytes (0.081%). Representative object reductions are 48,864 bytes for AMDGPU, 13,496 bytes for X86, and 4,928 bytes for RISCV; six sampled target objects save 77,928 bytes total.

Generated code and disassembly were byte-identical for X86, AArch64, RISCV, ARM, AMDGPU, BPF, SystemZ, WebAssembly, and NVPTX. The tramp3d-v4 and miniGMG objects were also byte-identical.

Existing performance workloads were neutral: AArch64 llvm-mca was +0.6%, tramp3d-v4 was -1.2%, and miniGMG was within timer granularity. TableGen generation time changed by +0.5% for X86 and +0.3% for AMDGPU.
---
 .../TableGen/instr-info-operand-packing.td    | 45 ++++++++++
 llvm/utils/TableGen/InstrInfoEmitter.cpp      | 85 ++++++++++++++-----
 2 files changed, 109 insertions(+), 21 deletions(-)
 create mode 100644 llvm/test/TableGen/instr-info-operand-packing.td

diff --git a/llvm/test/TableGen/instr-info-operand-packing.td b/llvm/test/TableGen/instr-info-operand-packing.td
new file mode 100644
index 0000000000000..83b6c76ad5467
--- /dev/null
+++ b/llvm/test/TableGen/instr-info-operand-packing.td
@@ -0,0 +1,45 @@
+// RUN: llvm-tblgen -gen-instr-info -I %p/../../include %s | FileCheck %s
+
+include "llvm/Target/Target.td"
+
+def TestInstrInfo : InstrInfo {}
+
+def TestTarget : Target {
+  let InstructionSet = TestInstrInfo;
+}
+
+def R0 : Register<"r0">;
+def R1 : Register<"r1">;
+def R2 : Register<"r2">;
+def R3 : Register<"r3">;
+def R4 : Register<"r4">;
+def R5 : Register<"r5">;
+def R6 : Register<"r6">;
+def R7 : Register<"r7">;
+
+def RC0 : RegisterClass<"Test", [i32], 0, (add R0)>;
+def RC1 : RegisterClass<"Test", [i32], 0, (add R1)>;
+def RC2 : RegisterClass<"Test", [i32], 0, (add R2)>;
+def RC3 : RegisterClass<"Test", [i32], 0, (add R3)>;
+def RC4 : RegisterClass<"Test", [i32], 0, (add R4)>;
+def RC5 : RegisterClass<"Test", [i32], 0, (add R5)>;
+def RC6 : RegisterClass<"Test", [i32], 0, (add R6)>;
+def RC7 : RegisterClass<"Test", [i32], 0, (add R7)>;
+
+class TestInstruction<dag Operands> : Instruction {
+  let Namespace = "Test";
+  let OutOperandList = (outs);
+  let InOperandList = Operands;
+  let hasSideEffects = false;
+}
+
+def Long : TestInstruction<
+  (ins RC0:$r0, RC1:$r1, RC2:$r2, RC3:$r3,
+       RC4:$r4, RC5:$r5, RC6:$r6, RC7:$r7)>;
+def Suffix : TestInstruction<
+  (ins RC3:$r3, RC4:$r4, RC5:$r5, RC6:$r6, RC7:$r7)>;
+
+defm : RemapAllTargetPseudoPointerOperands<RC0>;
+
+// CHECK: { {{[0-9]+}}, 5, 0, {{.*}}TestTargetOpInfoBase {{[+]}} [[#SUFFIX_OFFSET:]], {{.*}} },  // Suffix
+// CHECK: { {{[0-9]+}}, 8, 0, {{.*}}TestTargetOpInfoBase {{[+]}} [[#SUFFIX_OFFSET-3]], {{.*}} },  // Long
diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp
index 08526cc2a72bf..b4a8dc942a9c3 100644
--- a/llvm/utils/TableGen/InstrInfoEmitter.cpp
+++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp
@@ -25,6 +25,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/SourceMgr.h"
@@ -34,6 +35,7 @@
 #include "llvm/TableGen/Record.h"
 #include "llvm/TableGen/TGTimer.h"
 #include "llvm/TableGen/TableGenBackend.h"
+#include <algorithm>
 #include <cassert>
 #include <cstdint>
 #include <iterator>
@@ -69,7 +71,6 @@ class InstrInfoEmitter {
                  ArrayRef<const CodeGenInstruction *> NumberedInstructions);
 
   using OperandInfoTy = std::vector<std::string>;
-  using OperandInfoListTy = std::vector<OperandInfoTy>;
   using OperandInfoMapTy = std::map<OperandInfoTy, unsigned>;
 
   DenseMap<const CodeGenInstruction *, const CodeGenInstruction *>
@@ -108,9 +109,9 @@ class InstrInfoEmitter {
       ArrayRef<const CodeGenInstruction *> TargetInstructions);
 
   // Operand information.
-  unsigned CollectOperandInfo(OperandInfoListTy &OperandInfoList,
+  unsigned CollectOperandInfo(OperandInfoTy &PackedOperandInfo,
                               OperandInfoMapTy &OperandInfoMap);
-  void EmitOperandInfo(raw_ostream &OS, OperandInfoListTy &OperandInfoList);
+  void EmitOperandInfo(raw_ostream &OS, const OperandInfoTy &PackedOperandInfo);
   OperandInfoTy GetOperandInfo(const CodeGenInstruction &Inst);
 };
 
@@ -226,33 +227,75 @@ InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) {
 }
 
 unsigned
-InstrInfoEmitter::CollectOperandInfo(OperandInfoListTy &OperandInfoList,
+InstrInfoEmitter::CollectOperandInfo(OperandInfoTy &PackedOperandInfo,
                                      OperandInfoMapTy &OperandInfoMap) {
   const CodeGenTarget &Target = CDP.getTargetInfo();
-  unsigned Offset = 0;
   for (const CodeGenInstruction *Inst : Target.getInstructions()) {
     auto OverrideEntry = TargetSpecializedPseudoInsts.find(Inst);
     if (OverrideEntry != TargetSpecializedPseudoInsts.end())
       Inst = OverrideEntry->second;
 
-    OperandInfoTy OperandInfo = GetOperandInfo(*Inst);
-    if (OperandInfoMap.try_emplace(OperandInfo, Offset).second) {
-      OperandInfoList.push_back(OperandInfo);
-      Offset += OperandInfo.size();
+    OperandInfoMap.try_emplace(GetOperandInfo(*Inst), 0);
+  }
+
+  SmallVector<const OperandInfoTy *> OperandInfos;
+  OperandInfos.reserve(OperandInfoMap.size());
+  for (const auto &Entry : OperandInfoMap)
+    OperandInfos.push_back(&Entry.first);
+  llvm::sort(OperandInfos,
+             [](const OperandInfoTy *LHS, const OperandInfoTy *RHS) {
+               if (LHS->size() != RHS->size())
+                 return LHS->size() > RHS->size();
+               return *LHS < *RHS;
+             });
+
+  StringMap<SmallVector<unsigned>> ElementPositions;
+  for (const OperandInfoTy *OperandInfo : OperandInfos) {
+    if (OperandInfo->empty()) {
+      OperandInfoMap.find(*OperandInfo)->second = 0;
+      continue;
+    }
+
+    std::optional<unsigned> ExistingOffset;
+    for (unsigned Offset : ElementPositions.lookup(OperandInfo->front())) {
+      if (Offset + OperandInfo->size() <= PackedOperandInfo.size() &&
+          std::equal(OperandInfo->begin(), OperandInfo->end(),
+                     PackedOperandInfo.begin() + Offset)) {
+        ExistingOffset = Offset;
+        break;
+      }
+    }
+    if (ExistingOffset) {
+      OperandInfoMap.find(*OperandInfo)->second = *ExistingOffset;
+      continue;
     }
+
+    unsigned Overlap = std::min(PackedOperandInfo.size(), OperandInfo->size());
+    while (Overlap &&
+           !std::equal(PackedOperandInfo.end() - Overlap,
+                       PackedOperandInfo.end(), OperandInfo->begin()))
+      --Overlap;
+
+    OperandInfoMap.find(*OperandInfo)->second =
+        PackedOperandInfo.size() - Overlap;
+    unsigned OldSize = PackedOperandInfo.size();
+    PackedOperandInfo.insert(PackedOperandInfo.end(),
+                             OperandInfo->begin() + Overlap,
+                             OperandInfo->end());
+    for (unsigned I = OldSize; I != PackedOperandInfo.size(); ++I)
+      ElementPositions[PackedOperandInfo[I]].push_back(I);
   }
-  return Offset;
+  return PackedOperandInfo.size();
 }
 
 void InstrInfoEmitter::EmitOperandInfo(raw_ostream &OS,
-                                       OperandInfoListTy &OperandInfoList) {
-  unsigned Offset = 0;
-  for (auto &OperandInfo : OperandInfoList) {
-    OS << "    /* " << Offset << " */";
-    for (auto &Info : OperandInfo)
-      OS << " { " << Info << " },";
-    OS << '\n';
-    Offset += OperandInfo.size();
+                                       const OperandInfoTy &PackedOperandInfo) {
+  for (auto [Index, Info] : enumerate(PackedOperandInfo)) {
+    if (Index % 4 == 0)
+      OS << "    /* " << Index << " */";
+    OS << " { " << Info << " },";
+    if (Index % 4 == 3 || Index + 1 == PackedOperandInfo.size())
+      OS << '\n';
   }
 }
 
@@ -921,10 +964,10 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
   Timer.startTimer("Collect operand info");
   buildTargetSpecializedPseudoInstsMap();
 
-  OperandInfoListTy OperandInfoList;
+  OperandInfoTy PackedOperandInfo;
   OperandInfoMapTy OperandInfoMap;
   unsigned OperandInfoSize =
-      CollectOperandInfo(OperandInfoList, OperandInfoMap);
+      CollectOperandInfo(PackedOperandInfo, OperandInfoMap);
 
   // Collect all of the instruction's implicit uses and defs.
   // Also collect which features are enabled by instructions to control
@@ -1040,7 +1083,7 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
 
     // Emit all of the operand info records.
     Timer.startTimer("Emit operand info");
-    EmitOperandInfo(OS, OperandInfoList);
+    EmitOperandInfo(OS, PackedOperandInfo);
 
     OS << "  }\n};\n\n";
 



More information about the llvm-commits mailing list