[llvm] [TableGen] Diagnose sub-register indices that overflow their register (PR #206346)
Rahul Joshi via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 6 09:51:38 PDT 2026
================
@@ -388,6 +388,77 @@ CodeGenRegister::computeSubRegs(CodeGenRegBank &RegBank) {
return SubRegs;
}
+// Verify that a register's explicit sub-registers fit within it. A SubRegIndex
+// whose offset+size runs past the register's own size gives that sub-register
+// an oversized lane mask, which silently corrupts sub-register liveness and
+// spilling and is not otherwise diagnosed. (This was found via a real
+// wrong-code bug in a downstream target, where a 64-bit exponent sub-register
+// index was mistakenly declared with size 576.) The check is limited to
+// registers whose explicit sub-registers tile a contiguous bit range:
+// strided/spaced register tuples (e.g. ARM's Tuples3DSpc,
+// [dsub_0, dsub_2, dsub_4]) deliberately place sub-registers at non-adjacent
+// offsets that extend past the nominal size, and are not bugs.
+void CodeGenRegister::checkSubRegIndexSizes(CodeGenRegBank &RegBank) const {
+ // Only registers that are covered by their sub-registers promise to be fully
+ // tiled by them; for those an explicit SubRegIndex extending past the
+ // register is a genuine bug. A register that is not covered by its
+ // sub-registers makes no such promise, so don't second-guess its layout.
+ if (!CoveredBySubRegs)
+ return;
+
+ // This register's own bit size is the largest register class containing it.
+ unsigned ParentSize = 0;
+ for (const auto &RC : RegBank.getRegClasses()) {
+ if (!RC.contains(this) || !RC.RSI.hasDefault())
+ continue;
+ // Only trust a register-class size that is backed by a real value type.
+ // An 'untyped' class carries a placeholder size, not the register's bit
+ // width, so its size cannot be compared against sub-register extents.
+ ArrayRef<ValueTypeByHwMode> VTs = RC.getValueTypes();
+ if (VTs.empty() || !VTs[0].isSimple() || VTs[0].getSimple() == MVT::Untyped)
+ continue;
+ ParentSize = std::max(ParentSize, RC.RSI.get(DefaultMode).RegSize);
+ }
+ if (!ParentSize)
+ return;
+
+ // Collect the explicit sub-register (offset, size) ranges. Bail on any
+ // register with incomplete or non-contiguous range info.
+ SmallVector<std::pair<unsigned, unsigned>> Ranges;
+ for (const CodeGenSubRegIndex *Idx : ExplicitSubRegIndices) {
+ if (!Idx->Range.hasDefault())
+ return;
+ const SubRegRange &R = Idx->Range.get(DefaultMode);
+ if (R.Size == static_cast<uint32_t>(-1) ||
+ R.Offset == static_cast<uint32_t>(-1))
+ return; // tuple sub-index with no contiguous bit range (e.g. X86 KPAIRS,
+ // ARM strided NEON tuples)
+ Ranges.emplace_back(R.Offset, R.Size);
+ }
+ if (Ranges.empty())
+ return;
+
+ // ExplicitSubRegIndices is in .td declaration order, not offset order, so
+ // sort by offset before scanning. Only a contiguous tiling is a dense bit
+ // container; anything left with a gap or overlap is a strided/spaced tuple
+ // (also covered-by-subregs), which is allowed to exceed the nominal size.
+ llvm::sort(Ranges);
+ unsigned Pos = 0;
+ for (auto [Off, Sz] : Ranges) {
+ if (Off != Pos)
+ return; // gap or overlap -> strided/sparse, skip
+ Pos += Sz;
+ }
+
+ if (Pos > ParentSize)
+ PrintFatalError(TheDef->getLoc(),
+ Twine("register '") + getName() + "' has size " +
+ Twine(ParentSize) +
+ " but its explicit sub-registers cover " + Twine(Pos) +
+ " bits; a SubRegIndex 'Size' is larger than the "
----------------
jurahul wrote:
nit: a SubRegIndex 'Size' is larger than the sub-register it describes.
Is that the only reason why this error may occur? If not, we can drop this part of the error message?
https://github.com/llvm/llvm-project/pull/206346
More information about the llvm-commits
mailing list