Browse Source

Optimize memory for Switch instructions.

Doug Ilijev 8 years ago
parent
commit
4f935e2576
4 changed files with 259 additions and 338 deletions
  1. 129 20
      lib/Parser/RegexCompileTime.cpp
  2. 10 4
      lib/Parser/RegexOpCodes.h
  3. 91 279
      lib/Parser/RegexRuntime.cpp
  4. 29 35
      lib/Parser/RegexRuntime.h

+ 129 - 20
lib/Parser/RegexCompileTime.cpp

@@ -2301,9 +2301,13 @@ namespace UnifiedRegex
                 }
                 numItems++;
                 if (!curr->head->firstSet->IsCompact())
+                {
                     allCompact = false;
+                }
                 if (!curr->head->IsSimpleOneChar())
+                {
                     allSimpleOneChar = false;
+                }
                 totalChars += curr->head->firstSet->Count();
             }
 
@@ -2317,25 +2321,28 @@ namespace UnifiedRegex
                 {
                     // **COMMIT**
                     if (allSimpleOneChar)
+                    {
                         // This will probably never fire since the parser has already converted alts-of-chars/sets
                         // to sets. We include it for symmetry with below.
                         scheme = Set;
-                    else if (allCompact && totalChars <= Switch20Inst::MaxCases)
+                    }
+                    else if (allCompact && totalChars <= Switch24Inst::MaxCases)
                     {
                         // Can use a switch instruction to jump to item
                         scheme = Switch;
                         switchSize = totalChars;
                     }
                     else
+                    {
                         // Must use a chain of jump instructions to jump to item
                         scheme = Chain;
+                    }
                     isOptional = false;
                     return;
                 }
             }
         }
 
-
         //
         // Compilation scheme: None/Switch/Chain/Set, isOptional
         //
@@ -2368,7 +2375,9 @@ namespace UnifiedRegex
             for (AltNode* curr = this; curr != 0; curr = curr->tail)
             {
                 if (curr->head->IsEmptyOnly())
+                {
                     fires = true;
+                }
                 else if (curr->head->thisConsumes.CouldMatchEmpty())
                 {
                     fires = false;
@@ -2378,9 +2387,13 @@ namespace UnifiedRegex
                 {
                     numNonEmpty++;
                     if (!curr->head->IsSimpleOneChar())
+                    {
                         allSimpleOneChar = false;
+                    }
                     if (!curr->head->firstSet->IsCompact())
+                    {
                         allCompact = false;
+                    }
                     totalChars += curr->head->firstSet->Count();
                 }
             }
@@ -2403,23 +2416,31 @@ namespace UnifiedRegex
                     unionSet.UnionInPlace(compiler.ctAllocator, *firstSet);
                     unionSet.UnionInPlace(compiler.ctAllocator, *followSet);
                     if (totalChars + followSet->Count() == unionSet.Count())
+                    {
                         fires = true;
+                    }
                 }
 
                 if (fires)
                 {
                     // **COMMIT**
                     if (numNonEmpty == 0)
+                    {
                         scheme = None;
+                    }
                     else if (allSimpleOneChar)
+                    {
                         scheme = Set;
-                    else if (numNonEmpty > 1 && allCompact && totalChars <= Switch20Inst::MaxCases)
+                    }
+                    else if (numNonEmpty > 1 && allCompact && totalChars <= Switch24Inst::MaxCases)
                     {
                         switchSize = totalChars;
                         scheme = Switch;
                     }
                     else
+                    {
                         scheme = Chain;
+                    }
                     isOptional = true;
                     return;
                 }
@@ -2677,7 +2698,7 @@ namespace UnifiedRegex
                     //
                     // Compilation scheme:
                     //
-                    //            Switch(AndConsume)?(10|20)(<dispatch to each arm>)
+                    //            Switch(AndConsume)?(2|4|8|16|24)(<dispatch to each arm>)
                     //            Fail                                (if non-optional)
                     //            Jump Lexit                          (if optional)
                     //     L1:    <item1>
@@ -2687,7 +2708,7 @@ namespace UnifiedRegex
                     //     L3:    <item3>
                     //     Lexit:
                     //
-                    Assert(switchSize <= Switch20Inst::MaxCases);
+                    Assert(switchSize <= Switch24Inst::MaxCases);
                     int numItems = 0;
                     bool allCanSkip = true;
                     for (AltNode* curr = this; curr != 0; curr = curr->tail)
@@ -2700,7 +2721,9 @@ namespace UnifiedRegex
                         {
                             numItems++;
                             if (!curr->head->SupportsPrefixSkipping(compiler))
+                            {
                                 allCanSkip = false;
+                            }
                         }
                     }
                     Assert(numItems > 1);
@@ -2711,28 +2734,73 @@ namespace UnifiedRegex
                     Label* caseLabels = AnewArray(compiler.ctAllocator, Label, numItems);
                     // We must fixup the switch arms
                     Label switchLabel = compiler.CurrentLabel();
-                    Assert(switchSize <= Switch20Inst::MaxCases);
+
+                    Assert(switchSize <= Switch24Inst::MaxCases);
                     if (allCanSkip)
                     {
-                        if (switchSize > Switch10Inst::MaxCases)
-                            EMIT(compiler, SwitchAndConsume20Inst);
+                        if (switchSize <= Switch2Inst::MaxCases)
+                        {
+                            EMIT(compiler, SwitchAndConsume2Inst);
+                        }
+                        else if (switchSize <= Switch4Inst::MaxCases)
+                        {
+                            EMIT(compiler, SwitchAndConsume4Inst);
+                        }
+                        else if (switchSize <= Switch8Inst::MaxCases)
+                        {
+                            EMIT(compiler, SwitchAndConsume8Inst);
+                        }
+                        else if (switchSize <= Switch16Inst::MaxCases)
+                        {
+                            EMIT(compiler, SwitchAndConsume16Inst);
+                        }
+                        else if (switchSize <= Switch24Inst::MaxCases)
+                        {
+                            EMIT(compiler, SwitchAndConsume24Inst);
+                        }
                         else
-                            EMIT(compiler, SwitchAndConsume10Inst);
+                        {
+                            AssertOrFailFastMsg(false, "It should not be possible to reach here. This implies that we entered the Switch layout with greater than the max allowable cases.");
+                        }
                     }
                     else
                     {
-                        if (switchSize > Switch10Inst::MaxCases)
-                            EMIT(compiler, Switch20Inst);
+                        if (switchSize <= Switch2Inst::MaxCases)
+                        {
+                            EMIT(compiler, Switch2Inst);
+                        }
+                        else if (switchSize <= Switch4Inst::MaxCases)
+                        {
+                            EMIT(compiler, Switch4Inst);
+                        }
+                        else if (switchSize <= Switch8Inst::MaxCases)
+                        {
+                            EMIT(compiler, Switch8Inst);
+                        }
+                        else if (switchSize <= Switch16Inst::MaxCases)
+                        {
+                            EMIT(compiler, Switch16Inst);
+                        }
+                        else if (switchSize <= Switch24Inst::MaxCases)
+                        {
+                            EMIT(compiler, Switch24Inst);
+                        }
                         else
-                            EMIT(compiler, Switch10Inst);
+                        {
+                            AssertOrFailFastMsg(false, "It should not be possible to reach here. This implies that we entered the Switch layout with greater than the max allowable cases.");
+                        }
                     }
 
                     Label defaultJumpFixup = 0;
                     if (isOptional)
+                    {
                         // Must fixup default jump to exit
                         defaultJumpFixup = compiler.GetFixup(&EMIT(compiler, JumpInst)->targetLabel);
+                    }
                     else
+                    {
                         compiler.Emit<FailInst>();
+                    }
 
                     // Emit each item
                     int item = 0;
@@ -2741,20 +2809,29 @@ namespace UnifiedRegex
                         if (!curr->head->thisConsumes.CouldMatchEmpty())
                         {
                             if (allCanSkip)
+                            {
                                 skipped = 1;
+                            }
                             caseLabels[item] = compiler.CurrentLabel();
                             curr->head->Emit(compiler, skipped);
                             if (item < numItems - 1)
+                            {
                                 jumpFixups[item] = compiler.GetFixup(&EMIT(compiler, JumpInst)->targetLabel);
+                            }
                             item++;
                         }
                     }
 
                     // Fixup exit labels
                     if (isOptional)
+                    {
                         compiler.DoFixup(defaultJumpFixup, compiler.CurrentLabel());
+                    }
+
                     for (item = 0; item < numItems - 1; item++)
+                    {
                         compiler.DoFixup(jumpFixups[item], compiler.CurrentLabel());
+                    }
 
                     // Fixup the switch entries
                     item = 0;
@@ -2769,17 +2846,49 @@ namespace UnifiedRegex
                             {
                                 if (allCanSkip)
                                 {
-                                    if (switchSize > Switch10Inst::MaxCases)
-                                        compiler.L2I(SwitchAndConsume20, switchLabel)->AddCase(entries[i], caseLabels[item]);
-                                    else
-                                        compiler.L2I(SwitchAndConsume10, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    if (switchSize <= Switch2Inst::MaxCases)
+                                    {
+                                        compiler.L2I(SwitchAndConsume2, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    }
+                                    else if (switchSize <= Switch4Inst::MaxCases)
+                                    {
+                                        compiler.L2I(SwitchAndConsume4, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    }
+                                    else if (switchSize <= Switch8Inst::MaxCases)
+                                    {
+                                        compiler.L2I(SwitchAndConsume8, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    }
+                                    else if (switchSize <= Switch16Inst::MaxCases)
+                                    {
+                                        compiler.L2I(SwitchAndConsume16, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    }
+                                    else if (switchSize <= Switch24Inst::MaxCases)
+                                    {
+                                        compiler.L2I(SwitchAndConsume24, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    }
                                 }
                                 else
                                 {
-                                    if (switchSize > Switch10Inst::MaxCases)
-                                        compiler.L2I(Switch20, switchLabel)->AddCase(entries[i], caseLabels[item]);
-                                    else
-                                        compiler.L2I(Switch10, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    if (switchSize <= Switch2Inst::MaxCases)
+                                    {
+                                        compiler.L2I(Switch2, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    }
+                                    else if (switchSize <= Switch4Inst::MaxCases)
+                                    {
+                                        compiler.L2I(Switch4, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    }
+                                    else if (switchSize <= Switch8Inst::MaxCases)
+                                    {
+                                        compiler.L2I(Switch8, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    }
+                                    else if (switchSize <= Switch16Inst::MaxCases)
+                                    {
+                                        compiler.L2I(Switch16, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    }
+                                    else if (switchSize <= Switch24Inst::MaxCases)
+                                    {
+                                        compiler.L2I(Switch24, switchLabel)->AddCase(entries[i], caseLabels[item]);
+                                    }
                                 }
                             }
                             item++;

+ 10 - 4
lib/Parser/RegexOpCodes.h

@@ -16,10 +16,16 @@ M(MatchCharOrJump)
 M(JumpIfNotSet)
 M(MatchSetOrJump)
 // 0x08
-M(Switch10)
-M(Switch20)
-M(SwitchAndConsume10)
-M(SwitchAndConsume20)
+M(Switch2)
+M(Switch4)
+M(Switch8)
+M(Switch16) // 16 = 0x10
+M(Switch24) // 24 = 0x18 (support at least 20 slots from previous iteration, less fragmentation (<= 7 empty)
+M(SwitchAndConsume2)
+M(SwitchAndConsume4)
+M(SwitchAndConsume8)
+M(SwitchAndConsume16)
+M(SwitchAndConsume24)
 MTemplate(BOIHardFailTest, template<bool canHardFail>, BOITestInst, BOITestInst<true>)
 MTemplate(BOITest, template<bool canHardFail>, BOITestInst, BOITestInst<false>)
 MTemplate(EOIHardFailTest, template<bool canHardFail>, EOITestInst, EOITestInst<true>)

+ 91 - 279
lib/Parser/RegexRuntime.cpp

@@ -901,12 +901,11 @@ namespace UnifiedRegex
     }
 #endif
 
-    template <int n>
+    template <uint8 n>
     void SwitchMixin<n>::AddCase(char16 c, Label targetLabel)
     {
-        Assert(numCases < MaxCases);
-        int i;
-        __analysis_assume(numCases < MaxCases);
+        AnalysisAssert(numCases < MaxCases);
+        uint8 i;
         for (i = 0; i < numCases; i++)
         {
             Assert(cases[i].c != c);
@@ -916,7 +915,7 @@ namespace UnifiedRegex
             }
         }
         __analysis_assume(numCases < MaxCases);
-        for (int j = numCases; j > i; j--)
+        for (uint8 j = numCases; j > i; j--)
         {
             cases[j] = cases[j - 1];
         }
@@ -927,29 +926,36 @@ namespace UnifiedRegex
 
     void UnifiedRegexSwitchMixinForceAllInstantiations()
     {
-        {
-            SwitchMixin<10> x;
-            x.AddCase(0, 0);
-#if ENABLE_REGEX_CONFIG_OPTIONS
-            x.Print(0, 0);
-#endif
-        }
-        {
-            SwitchMixin<20> x;
-            x.AddCase(0, 0);
 #if ENABLE_REGEX_CONFIG_OPTIONS
-            x.Print(0, 0);
+#define SWITCH_FORCE_INSTANTIATION_PRINT x.Print(0, 0)
+#else
+#define SWITCH_FORCE_INSTANTIATION_PRINT
 #endif
+
+#define SWITCH_FORCE_INSTANTIATION(n)                       \
+        {                                                   \
+            SwitchMixin<n> x;                               \
+            x.AddCase(0, 0);                                \
+            SWITCH_FORCE_INSTANTIATION_PRINT;               \
         }
+
+        SWITCH_FORCE_INSTANTIATION(2);
+        SWITCH_FORCE_INSTANTIATION(4);
+        SWITCH_FORCE_INSTANTIATION(8);
+        SWITCH_FORCE_INSTANTIATION(16);
+        SWITCH_FORCE_INSTANTIATION(24);
+
+#undef SWITCH_FORCE_INSTANTIATION_PRINT
+#undef SWITCH_FORCE_INSTANTIATION
     }
 
 #if ENABLE_REGEX_CONFIG_OPTIONS
-    template <int n>
+    template <uint8 n>
     void SwitchMixin<n>::Print(DebugWriter* w, const char16* litbuf) const
     {
         w->EOL();
         w->Indent();
-        for (int i = 0; i < numCases; i++)
+        for (uint8 i = 0; i < numCases; i++)
         {
             cases[i].Print(w);
         }
@@ -1173,270 +1179,73 @@ namespace UnifiedRegex
 #endif
 
     // ----------------------------------------------------------------------
-    // Switch10Inst (optimized instruction)
+    // Switch(AndConsume)Inst (optimized instructions)
     // ----------------------------------------------------------------------
 
-    inline bool Switch10Inst::Exec(REGEX_INST_EXEC_PARAMETERS) const
-    {
-        if (inputOffset >= inputLength)
-        {
-            return matcher.Fail(FAIL_PARAMETERS);
-        }
-#if 0
-        int l = 0;
-        int h = numCases - 1;
-        while (l <= h)
-        {
-            int m = (l + h) / 2;
-#if ENABLE_REGEX_CONFIG_OPTIONS
-            matcher.CompStats();
-#endif
-            if (cases[m].c == input[inputOffset])
-            {
-                instPointer = matcher.LabelToInstPointer(cases[m].targetLabel);
-                return false;
-            }
-            else if (cases[m].c < input[inputOffset])
-            {
-                l = m + 1;
-            }
-            else
-            {
-                h = m - 1;
-            }
-        }
-#else
-        const int localNumCases = numCases;
-        for (int i = 0; i < localNumCases; i++)
-        {
 #if ENABLE_REGEX_CONFIG_OPTIONS
-            matcher.CompStats();
-#endif
-            if (cases[i].c == input[inputOffset])
-            {
-                instPointer = matcher.LabelToInstPointer(cases[i].targetLabel);
-                return false;
-            }
-            else if (cases[i].c > input[inputOffset])
-            {
-                break;
-            }
-        }
-#endif
-
-        instPointer += sizeof(*this);
-        return false;
+#define COMP_STATS matcher.CompStats()
+#define SwitchAndConsumeInstPrintImpl(BaseName, n)                                              \
+    int BaseName##n##Inst::Print(DebugWriter* w, Label label, const Char* litbuf) const         \
+    {                                                                                           \
+        PRINT_RE_BYTECODE_BEGIN("SwitchAndConsume"#n);                                          \
+        PRINT_MIXIN(SwitchMixin<n>);                                                            \
+        PRINT_RE_BYTECODE_MID();                                                                \
+        PRINT_BYTES(SwitchMixin<n>);                                                            \
+        PRINT_RE_BYTECODE_END();                                                                \
     }
-
-#if ENABLE_REGEX_CONFIG_OPTIONS
-    int Switch10Inst::Print(DebugWriter* w, Label label, const Char* litbuf) const
-    {
-        PRINT_RE_BYTECODE_BEGIN("Switch10");
-        PRINT_MIXIN(SwitchMixin<10>);
-        PRINT_RE_BYTECODE_MID();
-        PRINT_BYTES(SwitchMixin<10>);
-        PRINT_RE_BYTECODE_END();
-    }
-#endif
-
-    // ----------------------------------------------------------------------
-    // Switch20Inst (optimized instruction)
-    // ----------------------------------------------------------------------
-
-    inline bool Switch20Inst::Exec(REGEX_INST_EXEC_PARAMETERS) const
-    {
-        if (inputOffset >= inputLength)
-            return matcher.Fail(FAIL_PARAMETERS);
-#if 0
-        int l = 0;
-        int h = numCases - 1;
-        while (l <= h)
-        {
-            int m = (l + h) / 2;
-#if ENABLE_REGEX_CONFIG_OPTIONS
-            matcher.CompStats();
-#endif
-            if (cases[m].c == input[inputOffset])
-            {
-                instPointer = matcher.LabelToInstPointer(cases[m].targetLabel);
-                return false;
-            }
-            else if (cases[m].c < input[inputOffset])
-            {
-                l = m + 1;
-            }
-            else
-            {
-                h = m - 1;
-            }
-        }
 #else
-        const int localNumCases = numCases;
-        for (int i = 0; i < localNumCases; i++)
-        {
-#if ENABLE_REGEX_CONFIG_OPTIONS
-            matcher.CompStats();
-#endif
-            if (cases[i].c == input[inputOffset])
-            {
-                instPointer = matcher.LabelToInstPointer(cases[i].targetLabel);
-                return false;
-            }
-            else if (cases[i].c > input[inputOffset])
-            {
-                break;
-            }
-        }
-#endif
-
-        instPointer += sizeof(*this);
-        return false;
-    }
-
-#if ENABLE_REGEX_CONFIG_OPTIONS
-    int Switch20Inst::Print(DebugWriter* w, Label label, const Char* litbuf) const
-    {
-        PRINT_RE_BYTECODE_BEGIN("Switch20");
-        PRINT_MIXIN(SwitchMixin<20>);
-        PRINT_RE_BYTECODE_MID();
-        PRINT_BYTES(SwitchMixin<20>);
-        PRINT_RE_BYTECODE_END();
-    }
-#endif
-
-    // ----------------------------------------------------------------------
-    // SwitchAndConsume10Inst (optimized instruction)
-    // ----------------------------------------------------------------------
-
-    inline bool SwitchAndConsume10Inst::Exec(REGEX_INST_EXEC_PARAMETERS) const
-    {
-        if (inputOffset >= inputLength)
-            return matcher.Fail(FAIL_PARAMETERS);
-#if 0
-        int l = 0;
-        int h = numCases - 1;
-        while (l <= h)
-        {
-            int m = (l + h) / 2;
-#if ENABLE_REGEX_CONFIG_OPTIONS
-            matcher.CompStats();
-#endif
-            if (cases[m].c == input[inputOffset])
-            {
-                inputOffset++;
-                instPointer = matcher.LabelToInstPointer(cases[m].targetLabel);
-                return false;
-            }
-            else if (cases[m].c < input[inputOffset])
-            {
-                l = m + 1;
-            }
-            else
-            {
-                h = m - 1;
-            }
-        }
-#else
-        const int localNumCases = numCases;
-        for (int i = 0; i < localNumCases; i++)
-        {
-#if ENABLE_REGEX_CONFIG_OPTIONS
-            matcher.CompStats();
-#endif
-            if (cases[i].c == input[inputOffset])
-            {
-                inputOffset++;
-                instPointer = matcher.LabelToInstPointer(cases[i].targetLabel);
-                return false;
-            }
-            else if (cases[i].c > input[inputOffset])
-            {
-                break;
-            }
-        }
-#endif
-
-        instPointer += sizeof(*this);
-        return false;
-    }
-
-#if ENABLE_REGEX_CONFIG_OPTIONS
-    int SwitchAndConsume10Inst::Print(DebugWriter* w, Label label, const Char* litbuf) const
-    {
-        PRINT_RE_BYTECODE_BEGIN("SwitchAndConsume10");
-        PRINT_MIXIN(SwitchMixin<10>);
-        PRINT_RE_BYTECODE_MID();
-        PRINT_BYTES(SwitchMixin<10>);
-        PRINT_RE_BYTECODE_END();
-    }
-#endif
-
-    // ----------------------------------------------------------------------
-    // SwitchAndConsume20Inst (optimized instruction)
-    // ----------------------------------------------------------------------
-
-    inline bool SwitchAndConsume20Inst::Exec(REGEX_INST_EXEC_PARAMETERS) const
-    {
-        if (inputOffset >= inputLength)
-            return matcher.Fail(FAIL_PARAMETERS);
-#if 0
-        int l = 0;
-        int h = numCases - 1;
-        while (l <= h)
-        {
-            int m = (l + h) / 2;
-#if ENABLE_REGEX_CONFIG_OPTIONS
-            matcher.CompStats();
-#endif
-            if (cases[m].c == input[inputOffset])
-            {
-                inputOffset++;
-                instPointer = matcher.LabelToInstPointer(cases[m].targetLabel);
-                return false;
-            }
-            else if (cases[m].c < input[inputOffset])
-            {
-                l = m + 1;
-            }
-            else
-            {
-                h = m - 1;
-            }
-        }
-#else
-        const int localNumCases = numCases;
-        for (int i = 0; i < localNumCases; i++)
-        {
-#if ENABLE_REGEX_CONFIG_OPTIONS
-            matcher.CompStats();
-#endif
-            if (cases[i].c == input[inputOffset])
-            {
-                inputOffset++;
-                instPointer = matcher.LabelToInstPointer(cases[i].targetLabel);
-                return false;
-            }
-            else if (cases[i].c > input[inputOffset])
-            {
-                break;
-            }
-        }
-#endif
-
-        instPointer += sizeof(*this);
-        return false;
-    }
-
-#if ENABLE_REGEX_CONFIG_OPTIONS
-    int SwitchAndConsume20Inst::Print(DebugWriter* w, Label label, const Char* litbuf) const
-    {
-        PRINT_RE_BYTECODE_BEGIN("SwitchAndConsume20");
-        PRINT_MIXIN(SwitchMixin<20>);
-        PRINT_RE_BYTECODE_MID();
-        PRINT_BYTES(SwitchMixin<20>);
-        PRINT_RE_BYTECODE_END();
-    }
-#endif
+#define COMP_STATS
+#define SwitchAndConsumeInstPrintImpl(BaseName, n)
+#endif
+
+#define SwitchAndConsumeInstImpl(BaseName, n) \
+    inline bool BaseName##n##Inst::Exec(REGEX_INST_EXEC_PARAMETERS) const                       \
+    {                                                                                           \
+        if (inputOffset >= inputLength)                                                         \
+        {                                                                                       \
+            return matcher.Fail(FAIL_PARAMETERS);                                               \
+        }                                                                                       \
+                                                                                                \
+        const uint8 localNumCases = numCases;                                                     \
+        for (int i = 0; i < localNumCases; i++)                                                 \
+        {                                                                                       \
+            COMP_STATS;                                                                         \
+            if (cases[i].c == input[inputOffset])                                               \
+            {                                                                                   \
+                CONSUME;                                                                        \
+                instPointer = matcher.LabelToInstPointer(cases[i].targetLabel);                 \
+                return false;                                                                   \
+            }                                                                                   \
+            else if (cases[i].c > input[inputOffset])                                           \
+            {                                                                                   \
+                break;                                                                          \
+            }                                                                                   \
+        }                                                                                       \
+                                                                                                \
+        instPointer += sizeof(*this);                                                           \
+        return false;                                                                           \
+    }                                                                                           \
+    SwitchAndConsumeInstPrintImpl(BaseName, n);
+
+#define CONSUME
+    SwitchAndConsumeInstImpl(Switch, 2);
+    SwitchAndConsumeInstImpl(Switch, 4);
+    SwitchAndConsumeInstImpl(Switch, 8);
+    SwitchAndConsumeInstImpl(Switch, 16);
+    SwitchAndConsumeInstImpl(Switch, 24);
+#undef CONSUME
+
+#define CONSUME inputOffset++
+    SwitchAndConsumeInstImpl(SwitchAndConsume, 2);
+    SwitchAndConsumeInstImpl(SwitchAndConsume, 4);
+    SwitchAndConsumeInstImpl(SwitchAndConsume, 8);
+    SwitchAndConsumeInstImpl(SwitchAndConsume, 16);
+    SwitchAndConsumeInstImpl(SwitchAndConsume, 24);
+#undef CONSUME
+
+#undef COMP_STATS
+#undef SwitchAndConsumeInstPrintImpl
+#undef SwitchAndConsumeInstImpl
 
     // ----------------------------------------------------------------------
     // BOITestInst
@@ -6018,8 +5827,11 @@ namespace UnifiedRegex
 #endif
 
     // Template parameter here is the max number of cases
-    template void UnifiedRegex::SwitchMixin<10>::AddCase(char16, unsigned int);
-    template void UnifiedRegex::SwitchMixin<20>::AddCase(char16, unsigned int);
+    template void UnifiedRegex::SwitchMixin<2>::AddCase(char16, Label);
+    template void UnifiedRegex::SwitchMixin<4>::AddCase(char16, Label);
+    template void UnifiedRegex::SwitchMixin<8>::AddCase(char16, Label);
+    template void UnifiedRegex::SwitchMixin<16>::AddCase(char16, Label);
+    template void UnifiedRegex::SwitchMixin<24>::AddCase(char16, Label);
 
 #define M(...)
 #define MTemplate(TagName, TemplateDeclaration, GenericClassName, SpecializedClassName) template struct SpecializedClassName;

+ 29 - 35
lib/Parser/RegexRuntime.h

@@ -660,12 +660,12 @@ namespace UnifiedRegex
 #endif
     };
 
-    template <int n>
+    template <uint8 n>
     struct SwitchMixin
     {
-        static const int MaxCases = n;
+        static constexpr uint8 MaxCases = n;
 
-        int numCases;
+        uint8 numCases;
         // numCases cases, in increasing character order
         SwitchCase cases[MaxCases];
 
@@ -673,7 +673,7 @@ namespace UnifiedRegex
         inline SwitchMixin() : numCases(0)
         {
 #if DBG
-            for (int i = 0; i < MaxCases; i++)
+            for (uint8 i = 0; i < MaxCases; i++)
             {
                 cases[i].c = (char16)-1;
                 cases[i].targetLabel = (Label)-1;
@@ -804,37 +804,31 @@ namespace UnifiedRegex
         INST_BODY_FREE(SetMixin<false>)
     };
 
-    struct Switch10Inst : Inst, SwitchMixin<10>
-    {
-        // Cases must always be added
-        inline Switch10Inst() : Inst(Switch10), SwitchMixin() {}
-
-        INST_BODY
-    };
-
-    struct Switch20Inst : Inst, SwitchMixin<20>
-    {
-        // Cases must always be added
-        inline Switch20Inst() : Inst(Switch20), SwitchMixin() {}
-
-        INST_BODY
-    };
-
-    struct SwitchAndConsume10Inst : Inst, SwitchMixin<10>
-    {
-        // Cases must always be added
-        inline SwitchAndConsume10Inst() : Inst(SwitchAndConsume10), SwitchMixin() {}
-
-        INST_BODY
-    };
-
-    struct SwitchAndConsume20Inst : Inst, SwitchMixin<20>
-    {
-        // Cases must always be added
-        inline SwitchAndConsume20Inst() : Inst(SwitchAndConsume20), SwitchMixin() {}
-
-        INST_BODY
-    };
+#define SwitchInstActual(n)                                                 \
+    struct Switch##n##Inst : Inst, SwitchMixin<n>                           \
+    {                                                                       \
+        inline Switch##n##Inst() : Inst(Switch##n), SwitchMixin() {}        \
+        INST_BODY                                                           \
+    };
+    SwitchInstActual(2);
+    SwitchInstActual(4);
+    SwitchInstActual(8);
+    SwitchInstActual(16);
+    SwitchInstActual(24);
+#undef SwitchInstActual
+
+#define SwitchAndConsumeInstActual(n)                                                            \
+    struct SwitchAndConsume##n##Inst : Inst, SwitchMixin<n>                                     \
+    {                                                                                           \
+        inline SwitchAndConsume##n##Inst() : Inst(SwitchAndConsume##n), SwitchMixin() {}        \
+        INST_BODY                                                                               \
+    };
+    SwitchAndConsumeInstActual(2);
+    SwitchAndConsumeInstActual(4);
+    SwitchAndConsumeInstActual(8);
+    SwitchAndConsumeInstActual(16);
+    SwitchAndConsumeInstActual(24);
+#undef SwitchAndConsumeInstActual
 
     //
     // Built-in assertions