[llvm] [TableGen] Split large printInstruction() for NVPTX to avoid compiler limits (PR #218530)

Kazu Hirata via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 23 19:54:04 PDT 2026


================
@@ -575,33 +600,125 @@ void AsmWriterEmitter::EmitPrintInstruction(
     BitsLeft -= NumBits;
   }
 
-  // Okay, delete instructions with no operand info left.
-  llvm::erase_if(Instructions,
-                 [](AsmWriterInst &Inst) { return Inst.Operands.empty(); });
-
-  // Because this is a vector, we want to emit from the end.  Reverse all of the
-  // elements in the vector.
-  std::reverse(Instructions.begin(), Instructions.end());
-
-  std::vector<InstructionGroup> Groups = groupInstructions(Instructions);
-
   // Now that we've emitted all of the operand info that fit into 64 bits, emit
   // information for those instructions that are left.  This is a less dense
   // encoding, but we expect the main 64-bit table to handle the majority of
   // instructions.
-  if (!Groups.empty()) {
-    // Find the opcode # of inline asm.
-    O << "  switch (MI->getOpcode()) {\n";
-    O << "  default: llvm_unreachable(\"Unexpected opcode.\");\n";
-    for (const InstructionGroup &G : Groups)
-      emitInstructions(G, O, PassSubtarget);
+  if (!Instructions.empty()) {
+    if (UseFnPtrTable) {
+      // Single unconditional indirect call; the function-pointer table was
+      // emitted above, before printInstruction().
+      EmitOpcodePatternDispatch(O, Target.getName(), ClassName, PassSubtarget);
+    } else {
+      // Because this is a vector, we want to emit from the end.  Reverse all
+      // of the elements in the vector.
+      std::reverse(Instructions.begin(), Instructions.end());
 
-    O << "  }\n";
+      std::vector<InstructionGroup> Groups = groupInstructions(Instructions);
+
+      O << "  switch (MI->getOpcode()) {\n";
+      O << "  default: llvm_unreachable(\"Unexpected opcode.\");\n";
+      for (const InstructionGroup &G : Groups)
+        emitInstructions(G, O, PassSubtarget);
+
+      O << "  }\n";
+    }
   }
 
   O << "}\n";
 }
 
+void AsmWriterEmitter::EmitOpcodePatternTable(raw_ostream &O,
+                                              StringRef TargetName,
+                                              StringRef ClassName,
+                                              bool PassSubtarget) {
+  assert(!Instructions.empty() && "caller should have checked HasOverflow");
+
+  // Serialize each instruction's complete operand sequence into a string key.
+  // Instructions with identical keys share one printPattern_N function.
+  // getCode() with Receiver="P->" turns member calls like printOperand(...)
+  // into P->printOperand(...), which is correct for a static free function.
+  using PatternBody = std::vector<std::string>;
+  std::map<PatternBody, unsigned> PatternMap;
+  SmallVector<PatternBody, 64> Patterns;
+
+  // Table: index by CGIIndex, value = pattern index (~0U for non-overflow).
+  std::vector<unsigned> OpcodeToPattern(NumberedInstructions.size(), ~0U);
+
+  for (const AsmWriterInst &AWI : Instructions) {
+    PatternBody Body;
+    for (const AsmWriterOperand &Op : AWI.Operands)
+      Body.push_back(Op.getCode(PassSubtarget, "P->"));
+    auto [It, Inserted] = PatternMap.emplace(Body, Patterns.size());
+    if (Inserted)
+      Patterns.push_back(Body);
+    OpcodeToPattern[AWI.CGIIndex] = It->second;
+  }
+
+  std::string FullClassName = (TargetName + ClassName).str();
+  // Include ClassName in the table name so multiple AsmWriter variants in the
+  // same TU (e.g. AArch64GenAsmWriter.inc + AArch64GenAsmWriter1.inc) don't
+  // produce conflicting variable declarations.
+  std::string PtrTableName = FullClassName + "Printers";
+  std::string ParamList = "    " + FullClassName +
+                          " *, const MCInst *,\n"
+                          "    uint64_t," +
+                          (PassSubtarget ? " const MCSubtargetInfo &," : "") +
+                          " raw_ostream &";
+
+  // printPattern_None: no-op for instructions fully handled by table-driven
+  // path.
+  O << "static void printPattern_None(\n" << ParamList << ") {}\n\n";
+
+  // One printPattern_N per unique operand sequence.
+  for (unsigned PIdx = 0, E = Patterns.size(); PIdx < E; ++PIdx) {
+    O << "static void printPattern_" << PIdx << "(\n"
+      << "    " << FullClassName << " *P, const MCInst *MI,\n"
+      << "    uint64_t Address,";
+    if (PassSubtarget)
+      O << " const MCSubtargetInfo &STI,";
+    O << " raw_ostream &O) {\n";
+    for (const std::string &Line : Patterns[PIdx])
+      O << "  " << Line << "\n";
+    O << "}\n\n";
+  }
+
+  // Function-pointer table indexed by opcode, covering the full opcode range.
+  // Non-overflow opcodes → printPattern_None.
+  // Overflow opcodes     → their deduplicated printPattern_N.
+  O << "static void (*const " << PtrTableName << "[])(\n"
+    << ParamList << ") = {\n";
+  for (unsigned i = 0, E = NumberedInstructions.size(); i < E; ++i) {
+    unsigned PIdx = OpcodeToPattern[i];
+    if (PIdx == ~0U)
+      O << "  &printPattern_None";
+    else
+      O << "  &printPattern_" << PIdx;
+    if (i + 1 < E)
+      O << ",";
+    O << "\t// " << NumberedInstructions[i]->getName() << "\n";
+  }
+  O << "};\n\n";
+
+  LLVM_DEBUG(dbgs() << "[AsmWriter] " << TargetName << ": "
+                    << OpcodeInsts.size() << " overflow instructions -> "
----------------
kazutakahirata wrote:

`OpcodeInsts` is still referenced inside `LLVM_DEBUG`, which causes assertion-enabled (`-DLLVM_ENABLE_ASSERTIONS=ON`) CI builds to fail:

```suggestion
                    << Instructions.size() << " overflow instructions -> "
```

https://github.com/llvm/llvm-project/pull/218530


More information about the llvm-commits mailing list