[llvm] [SelectionDAG] Omit redundant two-child scope offsets (PR #202639)

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


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

DAGISel matcher scopes and switches encode a size before every child
and terminate the child list with a zero sentinel. For exactly two
children, only the first child size is needed: the remaining bytes are
the final child, and the interpreter knows there are no further
alternatives.

Add OPC_Scope2, OPC_SwitchOpcode2, and OPC_SwitchType2. These forms omit
the final child size and zero sentinel while retaining the existing
encodings for scopes and switches with other child counts.

The generated AArch64, AMDGPU, ARM, Mips, WebAssembly, and X86 matcher
tables shrink by 53,192 bytes. In the LLVM 22 Bazel build, stripped llc
shrinks from 78,629,632 to 78,530,560 bytes, saving 99,072 bytes
(0.126%). The stripped multicall binary shrinks from 143,039,720 to
142,924,144 bytes, saving 115,576 bytes.

A 12-run llc -O0 compile of tramp3d measured median wall time of 854.2
ms before the change and 846.0 ms after it. Mean user CPU decreased from
828.9 ms to 825.3 ms, with no measured runtime regression. Both binaries
produced byte-identical Mach-O objects.

Validate the specialized encodings with CPtrWildcard.td,
RegClassByHwMode.td, dag-isel-regclass-emit-enum.td, and
multiple-type-casts-patfrags.td. Build llvm-tblgen and LLVMSelectionDAG
with assertions enabled.

Work towards #202616

>From 25db52682cc7f91115ce70dfba801b1f4e68f68b Mon Sep 17 00:00:00 2001
From: David Zbarsky <dzbarsky at gmail.com>
Date: Mon, 8 Jun 2026 22:02:31 -0400
Subject: [PATCH] [SelectionDAG] Omit redundant two-child scope offsets

DAGISel matcher scopes and switches encode a size before every child
and terminate the child list with a zero sentinel. For exactly two
children, only the first child size is needed: the remaining bytes are
the final child, and the interpreter knows there are no further
alternatives.

Add OPC_Scope2, OPC_SwitchOpcode2, and OPC_SwitchType2. These forms omit
the final child size and zero sentinel while retaining the existing
encodings for scopes and switches with other child counts.

The generated AArch64, AMDGPU, ARM, Mips, WebAssembly, and X86 matcher
tables shrink by 53,192 bytes. In the LLVM 22 Bazel build, stripped llc
shrinks from 78,629,632 to 78,530,560 bytes, saving 99,072 bytes
(0.126%). The stripped multicall binary shrinks from 143,039,720 to
142,924,144 bytes, saving 115,576 bytes.

A 12-run llc -O0 compile of tramp3d measured median wall time of 854.2
ms before the change and 846.0 ms after it. Mean user CPU decreased from
828.9 ms to 825.3 ms, with no measured runtime regression. Both binaries
produced byte-identical Mach-O objects.

Validate the specialized encodings with CPtrWildcard.td,
RegClassByHwMode.td, dag-isel-regclass-emit-enum.td, and
multiple-type-casts-patfrags.td. Build llvm-tblgen and LLVMSelectionDAG
with assertions enabled.
---
 llvm/include/llvm/CodeGen/SelectionDAGISel.h  |   6 +
 .../CodeGen/SelectionDAG/SelectionDAGISel.cpp | 130 ++++++++++++++++--
 llvm/test/TableGen/CPtrWildcard.td            |  10 +-
 llvm/test/TableGen/RegClassByHwMode.td        |   4 +-
 .../TableGen/dag-isel-regclass-emit-enum.td   |   2 +-
 .../TableGen/multiple-type-casts-patfrags.td  |   4 +-
 llvm/utils/TableGen/DAGISelMatcherEmitter.cpp |  96 ++++++++-----
 7 files changed, 196 insertions(+), 56 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/SelectionDAGISel.h b/llvm/include/llvm/CodeGen/SelectionDAGISel.h
index 7b406ef4b4cb0..7f9a0a75ae1aa 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAGISel.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAGISel.h
@@ -145,6 +145,8 @@ class LLVM_ABI SelectionDAGISel {
   // Opcodes used by the DAG state machine:
   enum BuiltinOpcodes {
     OPC_Scope,
+    // Two-child scope with no second child size or zero sentinel.
+    OPC_Scope2,
     OPC_RecordNode,
     OPC_RecordChild0,
     OPC_RecordChild1,
@@ -203,6 +205,8 @@ class LLVM_ABI SelectionDAGISel {
     OPC_CheckPredicateWithOperands,
     OPC_CheckOpcode,
     OPC_SwitchOpcode,
+    // Two-case opcode switch with no second case size or zero sentinel.
+    OPC_SwitchOpcode2,
     OPC_CheckType,
     // Space-optimized forms that implicitly encode VT.
     OPC_CheckTypeI32,
@@ -213,6 +217,8 @@ class LLVM_ABI SelectionDAGISel {
     OPC_CheckTypeRes,
     OPC_CheckTypeResByHwMode,
     OPC_SwitchType,
+    // Two-case type switch with no second case size or zero sentinel.
+    OPC_SwitchType2,
     OPC_CheckChild0Type,
     OPC_CheckChild1Type,
     OPC_CheckChild2Type,
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
index 5ae52cae771fb..14cc5f09726d9 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -3306,6 +3306,9 @@ struct MatchScope {
 
   /// HasChainNodesMatched - True if the ChainNodesMatched list is non-empty.
   bool HasChainNodesMatched;
+
+  /// For OPC_Scope2, a nonzero fail index points directly at the final child.
+  bool IsTwoChildScope;
 };
 
 /// \A DAG update listener to keep the matching state
@@ -3488,17 +3491,23 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
       MatcherIndex = OpcodeOffset[N.getOpcode()];
     LLVM_DEBUG(dbgs() << "  Initial Opcode index to " << MatcherIndex << "\n");
 
-  } else if (MatcherTable[0] == OPC_SwitchOpcode) {
+  } else if (MatcherTable[0] == OPC_SwitchOpcode ||
+             MatcherTable[0] == OPC_SwitchOpcode2) {
     // Otherwise, the table isn't computed, but the state machine does start
-    // with an OPC_SwitchOpcode instruction.  Populate the table now, since this
-    // is the first time we're selecting an instruction.
+    // with an opcode switch. Populate the table now, since this is the first
+    // time we're selecting an instruction.
+    bool IsTwoCase = MatcherTable[0] == OPC_SwitchOpcode2;
     size_t Idx = 1;
-    while (true) {
+    for (unsigned Case = 0;; ++Case) {
       // Get the size of this case.
-      unsigned CaseSize = MatcherTable[Idx++];
-      if (CaseSize & 128)
-        CaseSize = GetVBR(CaseSize, MatcherTable, Idx);
-      if (CaseSize == 0) break;
+      unsigned CaseSize = 0;
+      if (!IsTwoCase || Case == 0) {
+        CaseSize = MatcherTable[Idx++];
+        if (CaseSize & 128)
+          CaseSize = GetVBR(CaseSize, MatcherTable, Idx);
+        if (CaseSize == 0)
+          break;
+      }
 
       // Get the opcode, add the index to the table.
       uint16_t Opc = MatcherTable[Idx++];
@@ -3506,6 +3515,9 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
       if (Opc >= OpcodeOffset.size())
         OpcodeOffset.resize((Opc+1)*2);
       OpcodeOffset[Opc] = Idx;
+
+      if (IsTwoCase && Case == 1)
+        break;
       Idx += CaseSize;
     }
 
@@ -3522,6 +3534,50 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
     BuiltinOpcodes Opcode =
         static_cast<BuiltinOpcodes>(MatcherTable[MatcherIndex++]);
     switch (Opcode) {
+    case OPC_Scope2: {
+      unsigned NumToSkip = MatcherTable[MatcherIndex++];
+      if (NumToSkip & 128)
+        NumToSkip = GetVBR(NumToSkip, MatcherTable, MatcherIndex);
+      size_t FinalChildIndex = MatcherIndex + NumToSkip;
+
+      size_t MatcherIndexOfPredicate = MatcherIndex;
+      (void)MatcherIndexOfPredicate;
+      bool Result;
+      MatcherIndex = IsPredicateKnownToFail(MatcherTable, MatcherIndex, N,
+                                            Result, *this, RecordedNodes);
+      bool MatchingFinalChild = false;
+      if (Result) {
+        LLVM_DEBUG(
+            dbgs() << "  Skipped scope entry (due to false predicate) at "
+                   << "index " << MatcherIndexOfPredicate << ", continuing at "
+                   << FinalChildIndex << "\n");
+        ++NumDAGIselRetries;
+
+        MatcherIndex = FinalChildIndex;
+        MatcherIndexOfPredicate = MatcherIndex;
+        MatcherIndex = IsPredicateKnownToFail(MatcherTable, MatcherIndex, N,
+                                              Result, *this, RecordedNodes);
+        if (Result) {
+          LLVM_DEBUG(dbgs()
+                     << "  Skipped final scope entry (due to false predicate) "
+                     << "at index " << MatcherIndexOfPredicate << "\n");
+          ++NumDAGIselRetries;
+          break;
+        }
+        MatchingFinalChild = true;
+      }
+
+      MatchScope &NewEntry = MatchScopes.emplace_back();
+      NewEntry.FailIndex = MatchingFinalChild ? 0 : FinalChildIndex;
+      NewEntry.NodeStack.append(NodeStack.begin(), NodeStack.end());
+      NewEntry.NumRecordedNodes = RecordedNodes.size();
+      NewEntry.NumMatchedMemRefs = MatchedMemRefs.size();
+      NewEntry.InputChain = InputChain;
+      NewEntry.InputGlue = InputGlue;
+      NewEntry.HasChainNodesMatched = !ChainNodesMatched.empty();
+      NewEntry.IsTwoChildScope = true;
+      continue;
+    }
     case OPC_Scope: {
       // Okay, the semantics of this operation are that we should push a scope
       // then evaluate the first child.  However, pushing a scope only to have
@@ -3578,6 +3634,7 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
       NewEntry.InputChain = InputChain;
       NewEntry.InputGlue = InputGlue;
       NewEntry.HasChainNodesMatched = !ChainNodesMatched.empty();
+      NewEntry.IsTwoChildScope = false;
       continue;
     }
     case OPC_RecordNode: {
@@ -3797,6 +3854,29 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
       continue;
     }
 
+    case OPC_SwitchOpcode2: {
+      unsigned CurNodeOpcode = N.getOpcode();
+      unsigned SwitchStart = MatcherIndex - 1;
+      (void)SwitchStart;
+
+      unsigned CaseSize = MatcherTable[MatcherIndex++];
+      if (CaseSize & 128)
+        CaseSize = GetVBR(CaseSize, MatcherTable, MatcherIndex);
+      uint16_t Opc = MatcherTable[MatcherIndex++];
+      Opc |= static_cast<uint16_t>(MatcherTable[MatcherIndex++]) << 8;
+      if (CurNodeOpcode != Opc) {
+        MatcherIndex += CaseSize;
+        Opc = MatcherTable[MatcherIndex++];
+        Opc |= static_cast<uint16_t>(MatcherTable[MatcherIndex++]) << 8;
+        if (CurNodeOpcode != Opc)
+          break;
+      }
+
+      LLVM_DEBUG(dbgs() << "  OpcodeSwitch from " << SwitchStart << " to "
+                        << MatcherIndex << "\n");
+      continue;
+    }
+
     case OPC_SwitchOpcode: {
       unsigned CurNodeOpcode = N.getOpcode();
       unsigned SwitchStart = MatcherIndex-1; (void)SwitchStart;
@@ -3828,6 +3908,31 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
       continue;
     }
 
+    case OPC_SwitchType2: {
+      MVT CurNodeVT = N.getSimpleValueType();
+      unsigned SwitchStart = MatcherIndex - 1;
+      (void)SwitchStart;
+
+      unsigned CaseSize = MatcherTable[MatcherIndex++];
+      if (CaseSize & 128)
+        CaseSize = GetVBR(CaseSize, MatcherTable, MatcherIndex);
+      MVT CaseVT = getSimpleVT(MatcherTable, MatcherIndex);
+      if (CaseVT == MVT::iPTR)
+        CaseVT = TLI->getPointerTy(CurDAG->getDataLayout());
+      if (CurNodeVT != CaseVT) {
+        MatcherIndex += CaseSize;
+        CaseVT = getSimpleVT(MatcherTable, MatcherIndex);
+        if (CaseVT == MVT::iPTR)
+          CaseVT = TLI->getPointerTy(CurDAG->getDataLayout());
+        if (CurNodeVT != CaseVT)
+          break;
+      }
+
+      LLVM_DEBUG(dbgs() << "  TypeSwitch[" << CurNodeVT << "] from "
+                        << SwitchStart << " to " << MatcherIndex << '\n');
+      continue;
+    }
+
     case OPC_SwitchType: {
       MVT CurNodeVT = N.getSimpleValueType();
       unsigned SwitchStart = MatcherIndex-1; (void)SwitchStart;
@@ -4558,6 +4663,15 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
       if (!LastScope.HasChainNodesMatched)
         ChainNodesMatched.clear();
 
+      if (LastScope.IsTwoChildScope) {
+        if (LastScope.FailIndex == 0) {
+          MatchScopes.pop_back();
+          continue;
+        }
+        LastScope.FailIndex = 0;
+        break;
+      }
+
       // Check to see what the offset is at the new MatcherIndex.  If it is zero
       // we have reached the end of this scope, otherwise we have another child
       // in the current scope to try.
diff --git a/llvm/test/TableGen/CPtrWildcard.td b/llvm/test/TableGen/CPtrWildcard.td
index 6b1312b6a1fe2..1943ef7bc71c0 100644
--- a/llvm/test/TableGen/CPtrWildcard.td
+++ b/llvm/test/TableGen/CPtrWildcard.td
@@ -7,20 +7,18 @@
 // CHECK-NEXT: /*     0*/ OPC_CheckOpcode, TARGET_VAL(ISD::INTRINSIC_WO_CHAIN),
 // CHECK-NEXT:/*     3*/ OPC_CheckChild0Integer, [[#]],
 // CHECK-NEXT:/*     5*/ OPC_RecordChild1, // #0 = $src
-// CHECK-NEXT:/*     6*/ OPC_Scope /*2 children */, 9, // ->17
+// CHECK-NEXT:/*     6*/ OPC_Scope2 /*2 children */, 9, // ->17
 // CHECK-NEXT:/*     8*/  OPC_CheckChild1Type, /*MVT::c64*/5|128,2/*261*/,
 // CHECK-NEXT:/*    11*/  OPC_MorphNodeTo1None, TARGET_VAL(MyTarget::C64_TO_I64),
 // CHECK-NEXT:                MVT::i64, 1/*#Ops*/, /*OperandList*/0, // Ops = #0
 // CHECK-NEXT:            // Src: (intrinsic_wo_chain:{ *:[i64] } [[#]]:{ *:[iPTR] }, c64:{ *:[c64] }:$src) - Complexity = 8
 // CHECK-NEXT:            // Dst: (C64_TO_I64:{ *:[i64] } ?:{ *:[c64] }:$src)
-// CHECK-NEXT:/*    17*/ /*Scope*/ 9, // ->27
-// CHECK-NEXT:/*    18*/  OPC_CheckChild1Type, /*MVT::c128*/6|128,2/*262*/,
-// CHECK-NEXT:/*    21*/  OPC_MorphNodeTo1None, TARGET_VAL(MyTarget::C128_TO_I64),
+// CHECK-NEXT:/*    17*/  OPC_CheckChild1Type, /*MVT::c128*/6|128,2/*262*/,
+// CHECK-NEXT:/*    20*/  OPC_MorphNodeTo1None, TARGET_VAL(MyTarget::C128_TO_I64),
 // CHECK-NEXT:                MVT::i64, 1/*#Ops*/, /*OperandList*/0, // Ops = #0
 // CHECK-NEXT:            // Src: (intrinsic_wo_chain:{ *:[i64] } [[#]]:{ *:[iPTR] }, c128:{ *:[c128] }:$src) - Complexity = 8
 // CHECK-NEXT:            // Dst: (C128_TO_I64:{ *:[i64] } ?:{ *:[c128] }:$src)
-// CHECK-NEXT:/*    27*/ 0, // End of Scope
-// CHECK-NEXT:  }; // Total Array size is 28 bytes
+// CHECK-NEXT:  }; // Total Array size is 26 bytes
 
 // CHECK:        static const uint8_t OperandLists[] = {
 // CHECK-NEXT:     /* 0 */ 0,
diff --git a/llvm/test/TableGen/RegClassByHwMode.td b/llvm/test/TableGen/RegClassByHwMode.td
index 46aef05ebc9c2..9d6887a1af429 100644
--- a/llvm/test/TableGen/RegClassByHwMode.td
+++ b/llvm/test/TableGen/RegClassByHwMode.td
@@ -201,7 +201,7 @@ include "Common/RegClassByHwModeCommon.td"
 
 
 // ISEL-SDAG: MatcherTable
-// ISEL-SDAG: OPC_SwitchOpcode /*2 cases */, {{[0-9]+}}, TARGET_VAL(ISD::STORE),
+// ISEL-SDAG: OPC_SwitchOpcode2 /*2 cases */, {{[0-9]+}}, TARGET_VAL(ISD::STORE),
 // ISEL-SDAG-NEXT: OPC_RecordMemRef,
 // ISEL-SDAG-NEXT: OPC_RecordNode, // #0 = 'st' chained node
 // ISEL-SDAG-NEXT: OPC_RecordChild1, // #1 = $val
@@ -214,7 +214,7 @@ include "Common/RegClassByHwModeCommon.td"
 // ISEL-SDAG-NEXT: OPC_MorphNodeTo0, TARGET_VAL(MyTarget::MY_STORE), 0|OPFL_Chain|OPFL_MemRefs,
 // ISEL-SDAG-NEXT:     2/*#Ops*/, /*OperandList*/1, // Ops = #1 #2
 
-// ISEL-SDAG: /*SwitchOpcode*/ {{[0-9]+}}, TARGET_VAL(ISD::LOAD),
+// ISEL-SDAG: /*SwitchOpcode*/ TARGET_VAL(ISD::LOAD),
 // ISEL-SDAG-NEXT: OPC_RecordMemRef,
 // ISEL-SDAG-NEXT: OPC_RecordNode, // #0 = 'ld' chained node
 // ISEL-SDAG-NEXT: OPC_RecordChild1, // #1 = $src
diff --git a/llvm/test/TableGen/dag-isel-regclass-emit-enum.td b/llvm/test/TableGen/dag-isel-regclass-emit-enum.td
index 5a3b4a47953ca..3a5eac015930b 100644
--- a/llvm/test/TableGen/dag-isel-regclass-emit-enum.td
+++ b/llvm/test/TableGen/dag-isel-regclass-emit-enum.td
@@ -23,7 +23,7 @@ def GPRAbove127 : RegisterClass<"TestTarget", [i32], 32,
 
 // CHECK:      OPC_CheckOpcode, TARGET_VAL(ISD::ADD),
 // CHECK-NEXT: OPC_RecordChild0, // #0 = $src
-// CHECK-NEXT: OPC_Scope /*2 children */, 11, // ->17
+// CHECK-NEXT: OPC_Scope2 /*2 children */, 11, // ->17
 // CHECK-NEXT: OPC_CheckChild1Integer, 0,
 // CHECK-NEXT: OPC_EmitIntegerI32, 0|128,1/*128*/, // #1 = TestNamespace::GPRAbove127RegClassID
 // CHECK-NEXT: OPC_MorphNodeTo1None, TARGET_VAL(TargetOpcode::COPY_TO_REGCLASS),
diff --git a/llvm/test/TableGen/multiple-type-casts-patfrags.td b/llvm/test/TableGen/multiple-type-casts-patfrags.td
index 39a5b042eaa66..fd3bf93550479 100644
--- a/llvm/test/TableGen/multiple-type-casts-patfrags.td
+++ b/llvm/test/TableGen/multiple-type-casts-patfrags.td
@@ -18,7 +18,7 @@ def INSTR_FOO_I32_I16 : Instruction {
   let InOperandList = (ins);
 }
 
-// SDAG: 7*/ OPC_SwitchType {{.*}}, 10, MVT::i16
+// SDAG: 7*/ OPC_SwitchType2 {{.*}}, 10, MVT::i16
 // SDAG: OPC_CheckTypeRes, 1, MVT::i32
 // SDAG: OPC_MorphNodeTo2Chain, TARGET_VAL(::INSTR_FOO_I16_I32)
 
@@ -27,7 +27,7 @@ def INSTR_FOO_I32_I16 : Instruction {
 // GISEL: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(::INSTR_FOO_I16_I32)
 def : Pat<([i16, i32] (int_foo)), ([i16, i32] (INSTR_FOO_I16_I32))>;
 
-// SDAG: 20*/ /*SwitchType*/ {{.*}} MVT::i32
+// SDAG: 20*/ /*SwitchType*/ MVT::i32
 // SDAG: OPC_CheckTypeRes, 1, MVT::i16
 // SDAG: OPC_MorphNodeTo2Chain, TARGET_VAL(::INSTR_FOO_I32_I16)
 
diff --git a/llvm/utils/TableGen/DAGISelMatcherEmitter.cpp b/llvm/utils/TableGen/DAGISelMatcherEmitter.cpp
index 3d69c96ebd900..f5870d616640c 100644
--- a/llvm/utils/TableGen/DAGISelMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/DAGISelMatcherEmitter.cpp
@@ -390,23 +390,28 @@ unsigned MatcherTableEmitter::SizeMatcher(Matcher *N, raw_ostream &OS) {
 
   ++OpcodeCounts[N->getKind()];
   switch (N->getKind()) {
-  // The Scope matcher has its kind, a series of child size + child,
-  // and a trailing zero.
+  // The Scope matcher has its kind, a series of child size + child, and a
+  // trailing zero. The two-child form omits the second size and trailing zero.
   case Matcher::Scope: {
     ScopeMatcher *SM = cast<ScopeMatcher>(N);
+    bool IsTwoChild = SM->getNumChildren() == 2;
     unsigned Size = 1; // Count the kind.
     for (unsigned i = 0, e = SM->getNumChildren(); i != e; ++i) {
       const unsigned ChildSize = SizeMatcherList(SM->getChild(i), OS);
       assert(ChildSize != 0 && "Matcher cannot have child of size 0");
       SM->getChild(i).setSize(ChildSize);
-      Size += GetVBRSize(ChildSize) + ChildSize; // Count VBR and child size.
+      if (!IsTwoChild || i == 0)
+        Size += GetVBRSize(ChildSize);
+      Size += ChildSize;
     }
-    ++Size; // Count the zero sentinel.
+    if (!IsTwoChild)
+      ++Size; // Count the zero sentinel.
     return Size;
   }
 
   // SwitchOpcode and SwitchType have their kind, a series of child size +
-  // opcode/type + child, and a trailing zero.
+  // opcode/type + child, and a trailing zero. The two-case forms omit the
+  // second size and trailing zero.
   case Matcher::SwitchOpcode:
   case Matcher::SwitchType: {
     unsigned Size = 1; // Count the kind.
@@ -415,6 +420,7 @@ unsigned MatcherTableEmitter::SizeMatcher(Matcher *N, raw_ostream &OS) {
       NumCases = SOM->getNumCases();
     else
       NumCases = cast<SwitchTypeMatcher>(N)->getNumCases();
+    bool IsTwoCase = NumCases == 2;
     for (unsigned i = 0, e = NumCases; i != e; ++i) {
       MatcherList *Child;
       if (SwitchOpcodeMatcher *SOM = dyn_cast<SwitchOpcodeMatcher>(N)) {
@@ -429,9 +435,12 @@ unsigned MatcherTableEmitter::SizeMatcher(Matcher *N, raw_ostream &OS) {
       const unsigned ChildSize = SizeMatcherList(*Child, OS);
       assert(ChildSize != 0 && "Matcher cannot have child of size 0");
       Child->setSize(ChildSize);
-      Size += GetVBRSize(ChildSize) + ChildSize; // Count VBR and child size.
+      if (!IsTwoCase || i == 0)
+        Size += GetVBRSize(ChildSize);
+      Size += ChildSize;
     }
-    ++Size; // Count the zero sentinel.
+    if (!IsTwoCase)
+      ++Size; // Count the zero sentinel.
     return Size;
   }
 
@@ -532,9 +541,10 @@ unsigned MatcherTableEmitter::EmitMatcher(const Matcher *N,
   switch (N->getKind()) {
   case Matcher::Scope: {
     const ScopeMatcher *SM = cast<ScopeMatcher>(N);
+    bool IsTwoChild = SM->getNumChildren() == 2;
     unsigned StartIdx = CurrentIdx;
 
-    OS << "OPC_Scope";
+    OS << (IsTwoChild ? "OPC_Scope2" : "OPC_Scope");
     if (!OmitComments)
       OS << " /*" << SM->getNumChildren() << " children */";
     OS << ", ";
@@ -542,7 +552,7 @@ unsigned MatcherTableEmitter::EmitMatcher(const Matcher *N,
 
     // Emit all of the children.
     for (unsigned i = 0, e = SM->getNumChildren(); i != e; ++i) {
-      if (i != 0) {
+      if ((!IsTwoChild || i == 0) && i != 0) {
         if (!OmitComments) {
           OS << "/*" << format_decimal(CurrentIdx, IndexWidth) << "*/";
           OS.indent(Indent) << "/*Scope*/ ";
@@ -553,10 +563,12 @@ unsigned MatcherTableEmitter::EmitMatcher(const Matcher *N,
 
       const MatcherList &Child = SM->getChild(i);
       unsigned ChildSize = Child.getSize();
-      CurrentIdx += EmitVBRValue(ChildSize, OS);
-      if (!OmitComments)
-        OS << " // ->" << CurrentIdx + ChildSize;
-      OS << '\n';
+      if (!IsTwoChild || i == 0) {
+        CurrentIdx += EmitVBRValue(ChildSize, OS);
+        if (!OmitComments)
+          OS << " // ->" << CurrentIdx + ChildSize;
+        OS << '\n';
+      }
 
       ChildSize = EmitMatcherList(Child, Indent + 1, CurrentIdx, OS);
       assert(ChildSize == Child.getSize() &&
@@ -564,14 +576,17 @@ unsigned MatcherTableEmitter::EmitMatcher(const Matcher *N,
       CurrentIdx += ChildSize;
     }
 
-    // Emit a zero as a sentinel indicating end of 'Scope'.
-    if (!OmitComments)
-      OS << "/*" << format_decimal(CurrentIdx, IndexWidth) << "*/";
-    OS.indent(Indent) << "0,";
-    if (!OmitComments)
-      OS << " // End of Scope";
-    OS << '\n';
-    return CurrentIdx - StartIdx + 1;
+    if (!IsTwoChild) {
+      // Emit a zero as a sentinel indicating end of 'Scope'.
+      if (!OmitComments)
+        OS << "/*" << format_decimal(CurrentIdx, IndexWidth) << "*/";
+      OS.indent(Indent) << "0,";
+      if (!OmitComments)
+        OS << " // End of Scope";
+      OS << '\n';
+      ++CurrentIdx;
+    }
+    return CurrentIdx - StartIdx;
   }
 
   case Matcher::RecordNode:
@@ -687,19 +702,21 @@ unsigned MatcherTableEmitter::EmitMatcher(const Matcher *N,
 
     unsigned NumCases;
     if (const SwitchOpcodeMatcher *SOM = dyn_cast<SwitchOpcodeMatcher>(N)) {
-      OS << "OPC_SwitchOpcode ";
       NumCases = SOM->getNumCases();
+      OS << (NumCases == 2 ? "OPC_SwitchOpcode2 " : "OPC_SwitchOpcode ");
     } else {
-      OS << "OPC_SwitchType ";
       NumCases = cast<SwitchTypeMatcher>(N)->getNumCases();
+      OS << (NumCases == 2 ? "OPC_SwitchType2 " : "OPC_SwitchType ");
     }
+    bool IsTwoCase = NumCases == 2;
 
     if (!OmitComments)
       OS << "/*" << NumCases << " cases */";
     OS << ", ";
     ++CurrentIdx;
 
-    // For each case we emit the size, then the opcode, then the matcher.
+    // For each case we emit the size, then the opcode/type and matcher. The
+    // final case of the two-case form omits its size.
     for (unsigned i = 0, e = NumCases; i != e; ++i) {
       const MatcherList *Child;
       unsigned IdxSize;
@@ -724,13 +741,16 @@ unsigned MatcherTableEmitter::EmitMatcher(const Matcher *N,
       }
 
       unsigned ChildSize = Child->getSize();
-      CurrentIdx += EmitVBRValue(ChildSize, OS) + IdxSize;
-      OS << ' ';
+      if (!IsTwoCase || i == 0) {
+        CurrentIdx += EmitVBRValue(ChildSize, OS);
+        OS << ' ';
+      }
+      CurrentIdx += IdxSize;
       if (const SwitchOpcodeMatcher *SOM = dyn_cast<SwitchOpcodeMatcher>(N))
         OS << "TARGET_VAL(" << SOM->getCaseOpcode(i).getEnumName() << "),";
       else
         emitMVT(cast<SwitchTypeMatcher>(N)->getCaseType(i), OS);
-      if (!OmitComments)
+      if (!OmitComments && (!IsTwoCase || i == 0))
         OS << " // ->" << CurrentIdx + ChildSize;
       OS << '\n';
 
@@ -740,16 +760,18 @@ unsigned MatcherTableEmitter::EmitMatcher(const Matcher *N,
       CurrentIdx += ChildSize;
     }
 
-    // Emit the final zero to terminate the switch.
-    if (!OmitComments)
-      OS << "/*" << format_decimal(CurrentIdx, IndexWidth) << "*/";
-    OS.indent(Indent) << "0,";
-    if (!OmitComments)
-      OS << (isa<SwitchOpcodeMatcher>(N) ? " // EndSwitchOpcode"
-                                         : " // EndSwitchType");
-
-    OS << '\n';
-    return CurrentIdx - StartIdx + 1;
+    if (!IsTwoCase) {
+      // Emit the final zero to terminate the switch.
+      if (!OmitComments)
+        OS << "/*" << format_decimal(CurrentIdx, IndexWidth) << "*/";
+      OS.indent(Indent) << "0,";
+      if (!OmitComments)
+        OS << (isa<SwitchOpcodeMatcher>(N) ? " // EndSwitchOpcode"
+                                           : " // EndSwitchType");
+      OS << '\n';
+      ++CurrentIdx;
+    }
+    return CurrentIdx - StartIdx;
   }
 
   case Matcher::CheckType: {



More information about the llvm-commits mailing list