[llvm] [BOLT] Add generic VLIW bundle emission infrastructure (PR #200916)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 1 12:46:06 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-bolt

Author: Brian Cain (androm3da)

<details>
<summary>Changes</summary>

Add an abstract BundleEmissionState interface to MCPlusBuilder and wire it into BinaryEmitter::emitFunctionBody(). This enables VLIW targets to accumulate instructions into bundles before emission without requiring any changes to per-instruction emitter paths.

* MCPlusBuilder::createBundleEmissionState() returns nullptr by default, making the new code paths zero-overhead no-ops for all existing non-VLIW targets.
* BundleEmissionState has four abstract methods: onBeginBasicBlock, canEmitInstructionLabel, processInstruction, and flush.
* BinaryEmitter tracks a PrevBB pointer to support cross-BB bundle carry detection in targets that split packets at branch boundaries.
* Mid-bundle labels are dropped when canEmitInstructionLabel() returns false, since addresses within an atomically-executing bundle are not meaningful.

---
Full diff: https://github.com/llvm/llvm-project/pull/200916.diff


2 Files Affected:

- (modified) bolt/include/bolt/Core/MCPlusBuilder.h (+51) 
- (modified) bolt/lib/Core/BinaryEmitter.cpp (+29-2) 


``````````diff
diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index 3a840615bec8f..79a020dbd6719 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -18,6 +18,7 @@
 #include "bolt/Core/Relocation.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/CodeGen/TargetOpcodes.h"
 #include "llvm/MC/MCAsmBackend.h"
@@ -36,6 +37,7 @@
 #include <cassert>
 #include <cstdint>
 #include <map>
+#include <memory>
 #include <optional>
 #include <system_error>
 #include <unordered_map>
@@ -2516,6 +2518,55 @@ class MCPlusBuilder {
     return 2;
   }
 
+  /// State object for targets that emit instructions as bundles (e.g.
+  /// Hexagon VLIW packets). Created per-function by
+  /// createBundleEmissionState().
+  class BundleEmissionState {
+  public:
+    virtual ~BundleEmissionState() = default;
+
+    /// Called before emitting a BB label. Returns true if the pending
+    /// bundle was flushed (or was empty). Returns false if the bundle
+    /// is being carried across the BB boundary.
+    virtual bool
+    onBeginBasicBlock(const BinaryBasicBlock &BB,
+                      const BinaryBasicBlock *PrevBB,
+                      function_ref<void(const MCInst &)> EmitBundle) = 0;
+
+    /// Returns true if a label can be emitted at this point (i.e. we
+    /// are NOT mid-bundle).
+    virtual bool canEmitInstructionLabel() const = 0;
+
+    /// Process one instruction. Returns true if consumed (emitter
+    /// should not emit it individually).
+    virtual bool
+    processInstruction(MCInst &Inst,
+                       function_ref<void(const MCInst &)> EmitBundle) = 0;
+
+    /// Flush remaining instructions at end of fragment.
+    virtual void flush(function_ref<void(const MCInst &)> EmitBundle) = 0;
+  };
+
+  /// Create a bundle emission state for VLIW targets. Returns nullptr
+  /// for targets that emit instructions individually.
+  virtual std::unique_ptr<BundleEmissionState>
+  createBundleEmissionState(MCContext &Ctx) const {
+    return nullptr;
+  }
+
+  /// Return true if the instruction is a .new value consumer that requires
+  /// its producer to be in the same VLIW packet (e.g. Hexagon .new value
+  /// stores and .new compare-and-jump instructions).
+  virtual bool isNewValueConsumer(const MCInst &Inst) const { return false; }
+
+  /// Create a BUNDLE MCInst from a sequence of individual instructions.
+  /// Used by targets that require bundle emission (e.g. Hexagon).
+  virtual MCInst createBundle(MCContext &Ctx, ArrayRef<MCInst> Instrs,
+                              bool InnerLoop = false,
+                              bool OuterLoop = false) const {
+    llvm_unreachable("not implemented");
+  }
+
   // AliasMap caches a mapping of registers to the set of registers that
   // alias (are sub or superregs of itself, including itself).
   std::vector<BitVector> AliasMap;
diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp
index 6affddafd3bf5..989f4f1f32f20 100644
--- a/bolt/lib/Core/BinaryEmitter.cpp
+++ b/bolt/lib/Core/BinaryEmitter.cpp
@@ -454,18 +454,29 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF,
     BF.duplicateConstantIslands();
   }
 
+  auto BundleState = BC.MIB->createBundleEmissionState(Streamer.getContext());
+  auto EmitViaStreamer = [&](const MCInst &I) {
+    Streamer.emitInstruction(I, *BC.STI);
+  };
+
   // Track the first emitted instruction with debug info.
   bool FirstInstr = true;
+  BinaryBasicBlock *PrevBB = nullptr;
   for (BinaryBasicBlock *const BB : FF) {
     if ((opts::AlignBlocks || opts::PreserveBlocksAlignment) &&
         BB->getAlignment() > 1)
       Streamer.emitCodeAlignment(BB->getAlign(), &*BC.STI,
                                  BB->getAlignmentMaxBytes());
+
+    if (BundleState)
+      BundleState->onBeginBasicBlock(*BB, PrevBB, EmitViaStreamer);
+
     Streamer.emitLabel(BB->getLabel());
     if (!EmitCodeOnly) {
       if (MCSymbol *EntrySymbol = BF.getSecondaryEntryPointSymbol(*BB))
         Streamer.emitLabel(EntrySymbol);
     }
+    PrevBB = BB;
 
     SMLoc LastLocSeen;
     for (auto I = BB->begin(), E = BB->end(); I != E; ++I) {
@@ -499,8 +510,17 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF,
           BB->getLocSyms().emplace_back(Offset, InstrLabel);
         }
 
-        if (InstrLabel)
-          Streamer.emitLabel(InstrLabel);
+        if (InstrLabel) {
+          if (BundleState && !BundleState->canEmitInstructionLabel()) {
+            // Mid-bundle labels are dropped because VLIW packets execute
+            // atomically. Addresses within a bundle are not meaningful.
+            LLVM_DEBUG(dbgs() << "BOLT-DEBUG: dropping mid-packet label "
+                              << InstrLabel->getName() << " in "
+                              << BF.getPrintName() << '\n');
+          } else {
+            Streamer.emitLabel(InstrLabel);
+          }
+        }
       }
 
       // Emit sized NOPs via MCAsmBackend::writeNopData() interface on x86.
@@ -515,10 +535,17 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF,
         }
       }
 
+      if (BundleState &&
+          BundleState->processInstruction(Instr, EmitViaStreamer))
+        continue;
+
       Streamer.emitInstruction(Instr, *BC.STI);
     }
   }
 
+  if (BundleState)
+    BundleState->flush(EmitViaStreamer);
+
   if (!EmitCodeOnly)
     emitConstantIslands(BF, FF.isSplitFragment());
 }

``````````

</details>


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


More information about the llvm-commits mailing list