Commit d5e3300deb for perl

commit d5e3300deb9e41357fffaa5f30ac04b81a93a6ba
Author: Richard Leach <rich+perl@hyphen-dash-hyphen.info>
Date:   Sun Oct 25 23:50:21 2026 +0000

    Regex: Pre-calculate any start class length for re_intuit_start

    This replaces the following per-execution calculation:

        /* XXX this value could be pre-computed */
        const SSize_t cl_l = (REGNODE_TYPE(OP(progi->regstclass)) == EXACT
                    ?  (reginfo->is_utf8_pat
                        ? (SSize_t)utf8_distance(str + STR_LEN(progi->regstclass), str)
                        : (SSize_t)STR_LEN(progi->regstclass))
                    : 1);

diff --git a/regcomp.c b/regcomp.c
index 90efbddd08..a817b14852 100644
--- a/regcomp.c
+++ b/regcomp.c
@@ -2343,6 +2343,19 @@ Perl_re_op_compile(pTHX_ SV ** const patternp, int pat_count,
     if (RExC_rx->minlen < minlen)
         RExC_rx->minlen = minlen;

+    /* Pre-compute the number of characters matched by a start class.
+     * For (potential) use by Perl_re_intuit_start. */
+    if (RExC_rxi->regstclass
+        && REGNODE_TYPE(OP(RExC_rxi->regstclass)) == EXACT) {
+        const regnode * const stclass = RExC_rxi->regstclass;
+        const U8 * const str = (U8 *) STRING(stclass);
+        RExC_rxi->regstclass_len = RExC_utf8
+            ? (U8) utf8_distance(str + STR_LEN(stclass), str)
+            : (U8) STR_LEN(stclass);
+    } else {
+        RExC_rxi->regstclass_len = 1;
+    }
+
     if (RExC_seen & REG_RECURSE_SEEN ) {
         RExC_rx->intflags |= PREGf_RECURSE_SEEN;
         Newx(RExC_rx->recurse_locinput, RExC_rx->nparens + 1, char *);
@@ -14300,6 +14313,7 @@ Perl_regdupe_internal(pTHX_ REGEXP * const rx, CLONE_PARAMS *param)


     reti->name_list_idx = ri->name_list_idx;
+    reti->regstclass_len = ri->regstclass_len;
     reti->slc_whilem_seen = ri->slc_whilem_seen;
     reti->slc             = NULL; /* will be alloced if/when needed */
     reti->depth           = 0;
diff --git a/regcomp.h b/regcomp.h
index fac66c7fbc..47c38e7077 100644
--- a/regcomp.h
+++ b/regcomp.h
@@ -144,6 +144,9 @@ typedef struct regexp_internal {
                                    is true, 0 means "no value" like any other
                                    index into the data array.*/
         U32 depth;              /* 1 = executing; 2+ = recursing */
+        U8 regstclass_len;      /* How many chars regstclass matches.
+                                 *     variable for an EXACTish start class,
+                                 *     otherwise set to 1. */
         U8 slc_whilem_seen;     /* Num of WHILEMs using super-linear cache.
                                    Same type as FLAGS() */
         struct slc_cache_item *slc; /* permanent array of super-linear cache
diff --git a/regexec.c b/regexec.c
index 89d3b19b1e..083c044dcb 100644
--- a/regexec.c
+++ b/regexec.c
@@ -1673,14 +1673,8 @@ Perl_re_intuit_start(pTHX_
      * leave it to regmatch itself) */

     if (progi->regstclass && REGNODE_TYPE(OP(progi->regstclass))!=TRIE) {
-        const U8* const str = (U8*)STRING(progi->regstclass);
-
-        /* XXX this value could be pre-computed */
-        const SSize_t cl_l = (REGNODE_TYPE(OP(progi->regstclass)) == EXACT
-                    ?  (reginfo->is_utf8_pat
-                        ? (SSize_t)utf8_distance(str + STR_LEN(progi->regstclass), str)
-                        : (SSize_t)STR_LEN(progi->regstclass))
-                    : 1);
+        /* number of characters the start class matches */
+        const SSize_t cl_l = (SSize_t) progi->regstclass_len;
         char * endpos;
         char *s;
         /* latest pos that a matching float substr constrains rx start to */