This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Re: The performance problem of 30678
[perl5.git] / regcomp.c
index 8b108f5..bbff562 100644 (file)
--- a/regcomp.c
+++ b/regcomp.c
@@ -109,6 +109,7 @@ typedef struct RExC_state_t {
     char       *parse;                 /* Input-scan pointer. */
     I32                whilem_seen;            /* number of WHILEM in this expr */
     regnode    *emit_start;            /* Start of emitted-code area */
+    regnode    *emit_bound;            /* First regnode outside of the allocated space */
     regnode    *emit;                  /* Code-emit pointer; &regdummy = don't = compiling */
     I32                naughty;                /* How bad is this pattern? */
     I32                sawback;                /* Did we see \1, ...? */
@@ -123,7 +124,10 @@ typedef struct RExC_state_t {
     regnode    **open_parens;          /* pointers to open parens */
     regnode    **close_parens;         /* pointers to close parens */
     regnode    *opend;                 /* END node in program */
-    I32                utf8;
+    I32                utf8;           /* whether the pattern is utf8 or not */
+    I32                orig_utf8;      /* whether the pattern was originally in utf8 */
+                               /* XXX use this for future optimisation of case
+                                * where pattern must be upgraded to utf8. */
     HV         *charnames;             /* cache of named sequences */
     HV         *paren_names;           /* Paren names */
     
@@ -156,6 +160,7 @@ typedef struct RExC_state_t {
 #endif
 #define RExC_emit      (pRExC_state->emit)
 #define RExC_emit_start        (pRExC_state->emit_start)
+#define RExC_emit_bound        (pRExC_state->emit_bound)
 #define RExC_naughty   (pRExC_state->naughty)
 #define RExC_sawback   (pRExC_state->sawback)
 #define RExC_seen      (pRExC_state->seen)
@@ -166,6 +171,7 @@ typedef struct RExC_state_t {
 #define RExC_seen_zerolen      (pRExC_state->seen_zerolen)
 #define RExC_seen_evals        (pRExC_state->seen_evals)
 #define RExC_utf8      (pRExC_state->utf8)
+#define RExC_orig_utf8 (pRExC_state->orig_utf8)
 #define RExC_charnames  (pRExC_state->charnames)
 #define RExC_open_parens       (pRExC_state->open_parens)
 #define RExC_close_parens      (pRExC_state->close_parens)
@@ -1373,16 +1379,17 @@ S_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch, regnode *firs
         U8 foldbuf[ UTF8_MAXBYTES_CASE + 1 ];
         const U8 *scan = (U8*)NULL;
         U32 wordlen      = 0;         /* required init */
-        STRLEN chars=0;
+        STRLEN chars = 0;
+        bool set_bit = trie->bitmap ? 1 : 0; /*store the first char in the bitmap?*/
 
         if (OP(noper) == NOTHING) {
             trie->minlen= 0;
             continue;
         }
-        if (trie->bitmap) {
-            TRIE_BITMAP_SET(trie,*uc);
-            if ( folder ) TRIE_BITMAP_SET(trie,folder[ *uc ]);            
-        }
+        if ( set_bit ) /* bitmap only alloced when !(UTF&&Folding) */
+            TRIE_BITMAP_SET(trie,*uc); /* store the raw first byte
+                                          regardless of encoding */
+
         for ( ; uc < e ; uc += len ) {
             TRIE_CHARCOUNT(trie)++;
             TRIE_READ_CHAR;
@@ -1394,6 +1401,13 @@ S_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch, regnode *firs
                         trie->charmap[ folder[ uvc ] ] = trie->charmap[ uvc ];
                     TRIE_STORE_REVCHAR;
                 }
+                if ( set_bit ) {
+                    /* store the codepoint in the bitmap, and if its ascii
+                       also store its folded equivelent. */
+                    TRIE_BITMAP_SET(trie,uvc);
+                    if ( folder ) TRIE_BITMAP_SET(trie,folder[ uvc ]);
+                    set_bit = 0; /* We've done our bit :-) */
+                }
             } else {
                 SV** svpp;
                 if ( !widecharmap )
@@ -4050,16 +4064,18 @@ Perl_re_compile(pTHX_ char *exp, char *xend, PMOP *pm)
     if (exp == NULL)
        FAIL("NULL regexp argument");
 
-    RExC_utf8 = pm->op_pmdynflags & PMdf_CMP_UTF8;
+    RExC_utf8 = RExC_orig_utf8 = pm->op_pmdynflags & PMdf_CMP_UTF8;
 
-    RExC_precomp = exp;
     DEBUG_COMPILE_r({
         SV *dsv= sv_newmortal();
         RE_PV_QUOTED_DECL(s, RExC_utf8,
-            dsv, RExC_precomp, (xend - exp), 60);
+            dsv, exp, (xend - exp), 60);
         PerlIO_printf(Perl_debug_log, "%sCompiling REx%s %s\n",
                       PL_colors[4],PL_colors[5],s);
     });
+
+redo_first_pass:
+    RExC_precomp = exp;
     RExC_flags = pm->op_pmflags;
     RExC_sawback = 0;
 
@@ -4098,6 +4114,25 @@ Perl_re_compile(pTHX_ char *exp, char *xend, PMOP *pm)
        RExC_precomp = NULL;
        return(NULL);
     }
+    if (RExC_utf8 && !RExC_orig_utf8) {
+        /* It's possible to write a regexp in ascii that represents unicode
+        codepoints outside of the byte range, such as via \x{100}. If we
+        detect such a sequence we have to convert the entire pattern to utf8
+        and then recompile, as our sizing calculation will have been based
+        on 1 byte == 1 character, but we will need to use utf8 to encode
+        at least some part of the pattern, and therefore must convert the whole
+        thing.
+        XXX: somehow figure out how to make this less expensive...
+        -- dmq */
+        STRLEN len = xend-exp;
+        DEBUG_PARSE_r(PerlIO_printf(Perl_debug_log,
+           "UTF8 mismatch! Converting to utf8 for resizing and compile\n"));
+        exp = (char*)Perl_bytes_to_utf8(aTHX_ (U8*)exp, &len);
+        xend = exp + len;
+        RExC_orig_utf8 = RExC_utf8;
+        SAVEFREEPV(exp);
+        goto redo_first_pass;
+    }
     DEBUG_PARSE_r({
         PerlIO_printf(Perl_debug_log, 
             "Required size %"IVdf" nodes\n"
@@ -4115,11 +4150,6 @@ Perl_re_compile(pTHX_ char *exp, char *xend, PMOP *pm)
     if (RExC_whilem_seen > 15)
        RExC_whilem_seen = 15;
 
-#ifdef DEBUGGING
-    /* Make room for a sentinel value at the end of the program */
-    RExC_size++;
-#endif
-
     /* Allocate space and zero-initialize. Note, the two step process 
        of zeroing when in debug mode, thus anything assigned has to 
        happen after that */
@@ -4153,7 +4183,7 @@ Perl_re_compile(pTHX_ char *exp, char *xend, PMOP *pm)
             + (sizeof(STD_PAT_MODS) - 1)
             + (sizeof("(?:)") - 1);
 
-        Newx(r->wrapped, r->wraplen, char );
+        Newx(r->wrapped, r->wraplen + 1, char );
         p = r->wrapped;
         *p++='('; *p++='?';
         if (has_k)
@@ -4176,13 +4206,14 @@ Perl_re_compile(pTHX_ char *exp, char *xend, PMOP *pm)
             }
         }
 
-        *p++=':';
+        *p++ = ':';
         Copy(RExC_precomp, p, r->prelen, char);
         r->precomp = p;
         p += r->prelen;
         if (has_runon)
-            *p++='\n';
-        *p=')';
+            *p++ = '\n';
+        *p++ = ')';
+        *p = 0;
     }
 
     r->intflags = 0;
@@ -4215,11 +4246,8 @@ Perl_re_compile(pTHX_ char *exp, char *xend, PMOP *pm)
     RExC_npar = 1;
     RExC_emit_start = ri->program;
     RExC_emit = ri->program;
-#ifdef DEBUGGING
-    /* put a sentinal on the end of the program so we can check for
-       overwrites */
-    ri->program[RExC_size].type = 255;
-#endif
+    RExC_emit_bound = ri->program + RExC_size + 1;
+
     /* Store the count of eval-groups for security checks: */
     RExC_rx->seen_evals = RExC_seen_evals;
     REGC((U8)REG_MAGIC, (char*) RExC_emit++);
@@ -4667,8 +4695,8 @@ reStudy:
             ARG2L_SET( scan, RExC_open_parens[ARG(scan)-1] - scan );
         }
     }
-    Newxz(r->startp, RExC_npar, I32);
-    Newxz(r->endp, RExC_npar, I32);
+    Newxz(r->startp, RExC_npar * 2, I32);
+    r->endp = r->startp + RExC_npar;
     /* assume we don't need to swap parens around before we match */
 
     DEBUG_DUMP_r({
@@ -4692,58 +4720,53 @@ reStudy:
     return(r);
 }
 
-#undef CORE_ONLY_BLOCK
 #undef RE_ENGINE_PTR
 
-#ifndef PERL_IN_XSUB_RE
+
 SV*
-Perl_reg_named_buff_get(pTHX_ SV* namesv, const REGEXP * const from_re, U32 flags)
+Perl_reg_named_buff_get(pTHX_ const REGEXP * const rx, SV* namesv, U32 flags)
 {
     AV *retarray = NULL;
     SV *ret;
     if (flags & 1) 
         retarray=newAV();
-    
-    if (from_re || PL_curpm) {
-        const REGEXP * const rx = from_re ? from_re : PM_GETRE(PL_curpm);
-        if (rx && rx->paren_names) {            
-            HE *he_str = hv_fetch_ent( rx->paren_names, namesv, 0, 0 );
-            if (he_str) {
-                IV i;
-                SV* sv_dat=HeVAL(he_str);
-                I32 *nums=(I32*)SvPVX(sv_dat);
-                for ( i=0; i<SvIVX(sv_dat); i++ ) {
-                    if ((I32)(rx->nparens) >= nums[i]
-                        && rx->startp[nums[i]] != -1
-                        && rx->endp[nums[i]] != -1)
-                    {
-                        ret = reg_numbered_buff_get(nums[i],rx,NULL,0);
-                        if (!retarray) 
-                            return ret;
-                    } else {
-                        ret = newSVsv(&PL_sv_undef);
-                    }
-                    if (retarray) {
-                        SvREFCNT_inc(ret); 
-                        av_push(retarray, ret);
-                    }
+
+    if (rx && rx->paren_names) {
+        HE *he_str = hv_fetch_ent( rx->paren_names, namesv, 0, 0 );
+        if (he_str) {
+            IV i;
+            SV* sv_dat=HeVAL(he_str);
+            I32 *nums=(I32*)SvPVX(sv_dat);
+            for ( i=0; i<SvIVX(sv_dat); i++ ) {
+               if ((I32)(rx->nparens) >= nums[i]
+                       && rx->startp[nums[i]] != -1
+                       && rx->endp[nums[i]] != -1)
+                {
+                    ret = CALLREG_NUMBUF(rx,nums[i],NULL);
+                    if (!retarray)
+                        return ret;
+                } else {
+                    ret = newSVsv(&PL_sv_undef);
+                }
+                if (retarray) {
+                    SvREFCNT_inc(ret);
+                    av_push(retarray, ret);
                 }
-                if (retarray)
-                    return (SV*)retarray;
             }
+            if (retarray)
+                return (SV*)retarray;
         }
     }
     return NULL;
 }
 
 SV*
-Perl_reg_numbered_buff_get(pTHX_ I32 paren, const REGEXP * const rx, SV* usesv, U32 flags)
+Perl_reg_numbered_buff_get(pTHX_ const REGEXP * const rx, I32 paren, SV* usesv)
 {
     char *s = NULL;
     I32 i = 0;
     I32 s1, t1;
     SV *sv = usesv ? usesv : newSVpvs("");
-    PERL_UNUSED_ARG(flags);
         
     if (!rx->subbeg) {
         sv_setsv(sv,&PL_sv_undef);
@@ -4812,7 +4835,7 @@ Perl_reg_numbered_buff_get(pTHX_ I32 paren, const REGEXP * const rx, SV* usesv,
     }
     return sv;
 }
-#endif
+
 
 /* Scans the name of a named buffer from the pattern.
  * If flags is REG_RSN_RETURN_NULL returns null.
@@ -4890,7 +4913,7 @@ S_reg_scan_name(pTHX_ RExC_state_t *pRExC_state, U32 flags) {
         PerlIO_printf(Perl_debug_log,"%16s","");                \
                                                                 \
     if (SIZE_ONLY)                                              \
-       num=RExC_size;                                           \
+       num = RExC_size + 1;                                     \
     else                                                        \
        num=REG_NODE_NUM(RExC_emit);                             \
     if (RExC_lastnum!=num)                                      \
@@ -4931,10 +4954,6 @@ S_reg_scan_name(pTHX_ RExC_state_t *pRExC_state, U32 flags) {
 #define REGTAIL_STUDY(x,y,z) regtail((x),(y),(z),depth+1)
 #endif
 
-/* this idea is borrowed from STR_WITH_LEN in handy.h */
-#define CHECK_WORD(s,v,l)  \
-    (((sizeof(s)-1)==(l)) && (strnEQ(start_verb, (s ""), (sizeof(s)-1))))
-
 STATIC regnode *
 S_reg(pTHX_ RExC_state_t *pRExC_state, I32 paren, I32 *flagp,U32 depth)
     /* paren: Parenthesized? 0=top, 1=(, inside: changed to letter. */
@@ -4967,7 +4986,6 @@ S_reg(pTHX_ RExC_state_t *pRExC_state, I32 paren, I32 *flagp,U32 depth)
     GET_RE_DEBUG_FLAGS_DECL;
     DEBUG_PARSE("reg ");
 
-
     *flagp = 0;                                /* Tentatively. */
 
 
@@ -5004,39 +5022,39 @@ S_reg(pTHX_ RExC_state_t *pRExC_state, I32 paren, I32 *flagp,U32 depth)
            
            switch ( *start_verb ) {
             case 'A':  /* (*ACCEPT) */
-                if ( CHECK_WORD("ACCEPT",start_verb,verb_len) ) {
+                if ( memEQs(start_verb,verb_len,"ACCEPT") ) {
                    op = ACCEPT;
                    internal_argval = RExC_nestroot;
                }
                break;
             case 'C':  /* (*COMMIT) */
-                if ( CHECK_WORD("COMMIT",start_verb,verb_len) )
+                if ( memEQs(start_verb,verb_len,"COMMIT") )
                     op = COMMIT;
                 break;
             case 'F':  /* (*FAIL) */
-                if ( verb_len==1 || CHECK_WORD("FAIL",start_verb,verb_len) ) {
+                if ( verb_len==1 || memEQs(start_verb,verb_len,"FAIL") ) {
                    op = OPFAIL;
                    argok = 0;
                }
                break;
             case ':':  /* (*:NAME) */
            case 'M':  /* (*MARK:NAME) */
-               if ( verb_len==0 || CHECK_WORD("MARK",start_verb,verb_len) ) {
+               if ( verb_len==0 || memEQs(start_verb,verb_len,"MARK") ) {
                     op = MARKPOINT;
                     argok = -1;
                 }
                 break;
             case 'P':  /* (*PRUNE) */
-                if ( CHECK_WORD("PRUNE",start_verb,verb_len) )
+                if ( memEQs(start_verb,verb_len,"PRUNE") )
                     op = PRUNE;
                 break;
             case 'S':   /* (*SKIP) */  
-                if ( CHECK_WORD("SKIP",start_verb,verb_len) ) 
+                if ( memEQs(start_verb,verb_len,"SKIP") ) 
                     op = SKIP;
                 break;
             case 'T':  /* (*THEN) */
                 /* [19:06] <TimToady> :: is then */
-                if ( CHECK_WORD("THEN",start_verb,verb_len) ) {
+                if ( memEQs(start_verb,verb_len,"THEN") ) {
                     op = CUTGROUP;
                     RExC_seen |= REG_SEEN_CUTGROUP;
                 }
@@ -5330,10 +5348,6 @@ S_reg(pTHX_ RExC_state_t *pRExC_state, I32 paren, I32 *flagp,U32 depth)
             } /* named and numeric backreferences */
             /* NOT REACHED */
 
-           case 'p':           /* (?p...) */
-               if (SIZE_ONLY && ckWARN2(WARN_DEPRECATED, WARN_REGEXP))
-                   vWARNdep(RExC_parse, "(?p{}) is deprecated - use (??{})");
-               /* FALL THROUGH*/
            case '?':           /* (??...) */
                is_logical = 1;
                if (*RExC_parse != '{') {
@@ -5524,6 +5538,9 @@ S_reg(pTHX_ RExC_state_t *pRExC_state, I32 paren, I32 *flagp,U32 depth)
                    }
                    else
                         REGTAIL(pRExC_state, ret, ender);
+                    RExC_size++; /* XXX WHY do we need this?!!
+                                    For large programs it seems to be required
+                                    but I can't figure out why. -- dmq*/
                    return ret;
                }
                else {
@@ -5674,7 +5691,7 @@ S_reg(pTHX_ RExC_state_t *pRExC_state, I32 paren, I32 *flagp,U32 depth)
     }
     else if (paren != '?')             /* Not Conditional */
        ret = br;
-    *flagp |= flags & (SPSTART | HASWIDTH);
+    *flagp |= flags & (SPSTART | HASWIDTH | POSTPONED);
     lastbr = br;
     while (*RExC_parse == '|') {
        if (!SIZE_ONLY && RExC_extralen) {
@@ -5695,9 +5712,7 @@ S_reg(pTHX_ RExC_state_t *pRExC_state, I32 paren, I32 *flagp,U32 depth)
            return(NULL);
         REGTAIL(pRExC_state, lastbr, br);               /* BRANCH -> BRANCH. */
        lastbr = br;
-       if (flags&HASWIDTH)
-           *flagp |= HASWIDTH;
-       *flagp |= flags&SPSTART;
+       *flagp |= flags & (SPSTART | HASWIDTH | POSTPONED);
     }
 
     if (have_branch || paren != ':') {
@@ -5810,6 +5825,7 @@ S_regbranch(pTHX_ RExC_state_t *pRExC_state, I32 *flagp, I32 first, U32 depth)
     I32 flags = 0, c = 0;
     GET_RE_DEBUG_FLAGS_DECL;
     DEBUG_PARSE("brnc");
+
     if (first)
        ret = NULL;
     else {
@@ -5838,7 +5854,7 @@ S_regbranch(pTHX_ RExC_state_t *pRExC_state, I32 *flagp, I32 first, U32 depth)
        }
        else if (ret == NULL)
            ret = latest;
-       *flagp |= flags&HASWIDTH;
+       *flagp |= flags&(HASWIDTH|POSTPONED);
        if (chain == NULL)      /* First piece. */
            *flagp |= flags&SPSTART;
        else {
@@ -7754,7 +7770,7 @@ parseit:
         return ret;
     /****** !SIZE_ONLY AFTER HERE *********/
 
-    if( stored == 1 && value < 256
+    if( stored == 1 && (value < 128 || (value < 256 && !UTF))
         && !( ANYOF_FLAGS(ret) & ( ANYOF_FLAGS_ALL ^ ANYOF_FOLD ) )
     ) {
         /* optimize single char class to an EXACT node
@@ -7904,11 +7920,9 @@ S_reg_node(pTHX_ RExC_state_t *pRExC_state, U8 op)
        RExC_size += 1;
        return(ret);
     }
-#ifdef DEBUGGING
-    if (OP(RExC_emit) == 255)
-        Perl_croak(aTHX_ "panic: reg_node overrun trying to emit %s: %d ",
-            reg_name[op], OP(RExC_emit));
-#endif  
+    if (RExC_emit >= RExC_emit_bound)
+        Perl_croak(aTHX_ "panic: reg_node overrun trying to emit %d", op);
+
     NODE_ALIGN_FILL(ret);
     ptr = ret;
     FILL_ADVANCE_NODE(ptr, op);
@@ -7916,7 +7930,7 @@ S_reg_node(pTHX_ RExC_state_t *pRExC_state, U8 op)
     if (RExC_offsets) {         /* MJD */
        MJD_OFFSET_DEBUG(("%s:%d: (op %s) %s %"UVuf" (len %"UVuf") (max %"UVuf").\n", 
               "reg_node", __LINE__, 
-              reg_name[op],
+              PL_reg_name[op],
               (UV)(RExC_emit - RExC_emit_start) > RExC_offsets[0] 
                ? "Overwriting end of array!\n" : "OK",
               (UV)(RExC_emit - RExC_emit_start),
@@ -7959,10 +7973,9 @@ S_reganode(pTHX_ RExC_state_t *pRExC_state, U8 op, U32 arg)
        */
        return(ret);
     }
-#ifdef DEBUGGING
-    if (OP(RExC_emit) == 255)
-        Perl_croak(aTHX_ "panic: reganode overwriting end of allocated program space");
-#endif 
+    if (RExC_emit >= RExC_emit_bound)
+        Perl_croak(aTHX_ "panic: reg_node overrun trying to emit %d", op);
+
     NODE_ALIGN_FILL(ret);
     ptr = ret;
     FILL_ADVANCE_NODE_ARG(ptr, op, arg);
@@ -7971,7 +7984,7 @@ S_reganode(pTHX_ RExC_state_t *pRExC_state, U8 op, U32 arg)
        MJD_OFFSET_DEBUG(("%s(%d): (op %s) %s %"UVuf" <- %"UVuf" (max %"UVuf").\n", 
               "reganode",
              __LINE__,
-             reg_name[op],
+             PL_reg_name[op],
               (UV)(RExC_emit - RExC_emit_start) > RExC_offsets[0] ? 
               "Overwriting end of array!\n" : "OK",
               (UV)(RExC_emit - RExC_emit_start),
@@ -8009,8 +8022,9 @@ S_reginsert(pTHX_ RExC_state_t *pRExC_state, U8 op, regnode *opnd, U32 depth)
     const int offset = regarglen[(U8)op];
     const int size = NODE_STEP_REGNODE + offset;
     GET_RE_DEBUG_FLAGS_DECL;
+    PERL_UNUSED_ARG(depth);
 /* (PL_regkind[(U8)op] == CURLY ? EXTRA_STEP_2ARGS : 0); */
-    DEBUG_PARSE_FMT("inst"," - %s",reg_name[op]);
+    DEBUG_PARSE_FMT("inst"," - %s",PL_reg_name[op]);
     if (SIZE_ONLY) {
        RExC_size += size;
        return;
@@ -8021,19 +8035,19 @@ S_reginsert(pTHX_ RExC_state_t *pRExC_state, U8 op, regnode *opnd, U32 depth)
     dst = RExC_emit;
     if (RExC_open_parens) {
         int paren;
-        DEBUG_PARSE_FMT("inst"," - %"IVdf, (IV)RExC_npar);
+        /*DEBUG_PARSE_FMT("inst"," - %"IVdf, (IV)RExC_npar);*/
         for ( paren=0 ; paren < RExC_npar ; paren++ ) {
             if ( RExC_open_parens[paren] >= opnd ) {
-                DEBUG_PARSE_FMT("open"," - %d",size);
+                /*DEBUG_PARSE_FMT("open"," - %d",size);*/
                 RExC_open_parens[paren] += size;
             } else {
-                DEBUG_PARSE_FMT("open"," - %s","ok");
+                /*DEBUG_PARSE_FMT("open"," - %s","ok");*/
             }
             if ( RExC_close_parens[paren] >= opnd ) {
-                DEBUG_PARSE_FMT("close"," - %d",size);
+                /*DEBUG_PARSE_FMT("close"," - %d",size);*/
                 RExC_close_parens[paren] += size;
             } else {
-                DEBUG_PARSE_FMT("close"," - %s","ok");
+                /*DEBUG_PARSE_FMT("close"," - %s","ok");*/
             }
         }
     }
@@ -8045,7 +8059,7 @@ S_reginsert(pTHX_ RExC_state_t *pRExC_state, U8 op, regnode *opnd, U32 depth)
            MJD_OFFSET_DEBUG(("%s(%d): (op %s) %s copy %"UVuf" -> %"UVuf" (max %"UVuf").\n",
                   "reg_insert",
                  __LINE__,
-                 reg_name[op],
+                 PL_reg_name[op],
                   (UV)(dst - RExC_emit_start) > RExC_offsets[0] 
                    ? "Overwriting end of array!\n" : "OK",
                   (UV)(src - RExC_emit_start),
@@ -8064,7 +8078,7 @@ S_reginsert(pTHX_ RExC_state_t *pRExC_state, U8 op, regnode *opnd, U32 depth)
        MJD_OFFSET_DEBUG(("%s(%d): (op %s) %s %"UVuf" <- %"UVuf" (max %"UVuf").\n", 
               "reginsert",
              __LINE__,
-             reg_name[op],
+             PL_reg_name[op],
               (UV)(place - RExC_emit_start) > RExC_offsets[0] 
               ? "Overwriting end of array!\n" : "OK",
               (UV)(place - RExC_emit_start),
@@ -8108,7 +8122,7 @@ S_regtail(pTHX_ RExC_state_t *pRExC_state, regnode *p, const regnode *val,U32 de
             PerlIO_printf(Perl_debug_log, "~ %s (%d) %s %s\n",
                 SvPV_nolen_const(mysv), REG_NODE_NUM(scan),
                     (temp == NULL ? "->" : ""),
-                    (temp == NULL ? reg_name[OP(val)] : "")
+                    (temp == NULL ? PL_reg_name[OP(val)] : "")
             );
         });
         if (temp == NULL)
@@ -8189,7 +8203,7 @@ S_regtail_study(pTHX_ RExC_state_t *pRExC_state, regnode *p, const regnode *val,
             PerlIO_printf(Perl_debug_log, "~ %s (%d) -> %s\n",
                 SvPV_nolen_const(mysv),
                 REG_NODE_NUM(scan),
-                reg_name[exact]);
+                PL_reg_name[exact]);
         });
        if (temp == NULL)
            break;
@@ -8347,7 +8361,7 @@ Perl_regprop(pTHX_ const regexp *prog, SV *sv, const regnode *o)
        /* It would be nice to FAIL() here, but this may be called from
           regexec.c, and it would be hard to supply pRExC_state. */
        Perl_croak(aTHX_ "Corrupted regexp opcode %d > %d", (int)OP(o), (int)REGNODE_MAX);
-    sv_catpv(sv, reg_name[OP(o)]); /* Take off const! */
+    sv_catpv(sv, PL_reg_name[OP(o)]); /* Take off const! */
 
     k = PL_regkind[OP(o)];
 
@@ -8376,7 +8390,7 @@ Perl_regprop(pTHX_ const regexp *prog, SV *sv, const regnode *o)
         const reg_trie_data * const trie
            = (reg_trie_data*)progi->data->data[!IS_TRIE_AC(op) ? n : ac->trie];
         
-        Perl_sv_catpvf(aTHX_ sv, "-%s",reg_name[o->flags]);
+        Perl_sv_catpvf(aTHX_ sv, "-%s",PL_reg_name[o->flags]);
         DEBUG_TRIE_COMPILE_r(
             Perl_sv_catpvf(aTHX_ sv,
                 "<S:%"UVuf"/%"IVdf" W:%"UVuf" L:%"UVuf"/%"UVuf" C:%"UVuf"/%"UVuf">",
@@ -8648,31 +8662,108 @@ Perl_pregfree(pTHX_ struct regexp *r)
 
     if (!r || (--r->refcnt > 0))
        return;
-       
-    CALLREGFREE_PVT(r); /* free the private data */
+    if (r->mother_re) {
+        ReREFCNT_dec(r->mother_re);
+    } else {
+        CALLREGFREE_PVT(r); /* free the private data */
+        if (r->paren_names)
+            SvREFCNT_dec(r->paren_names);
+        Safefree(r->wrapped);
+    }        
+    if (r->substrs) {
+        if (r->anchored_substr)
+            SvREFCNT_dec(r->anchored_substr);
+        if (r->anchored_utf8)
+            SvREFCNT_dec(r->anchored_utf8);
+        if (r->float_substr)
+            SvREFCNT_dec(r->float_substr);
+        if (r->float_utf8)
+            SvREFCNT_dec(r->float_utf8);
+       Safefree(r->substrs);
+    }
     RX_MATCH_COPY_FREE(r);
 #ifdef PERL_OLD_COPY_ON_WRITE
     if (r->saved_copy)
-       SvREFCNT_dec(r->saved_copy);
+        SvREFCNT_dec(r->saved_copy);
 #endif
-    if (r->substrs) {
-       if (r->anchored_substr)
-           SvREFCNT_dec(r->anchored_substr);
-       if (r->anchored_utf8)
-           SvREFCNT_dec(r->anchored_utf8);
-       if (r->float_substr)
-           SvREFCNT_dec(r->float_substr);
-       if (r->float_utf8)
-           SvREFCNT_dec(r->float_utf8);
-       Safefree(r->substrs);
+    if (r->swap) {
+        Safefree(r->swap->startp);
+        Safefree(r->swap);
     }
-    if (r->paren_names)
-        SvREFCNT_dec(r->paren_names);
-    Safefree(r->wrapped);
     Safefree(r->startp);
-    Safefree(r->endp);
     Safefree(r);
 }
+
+/*  reg_temp_copy()
+    
+    This is a hacky workaround to the structural issue of match results
+    being stored in the regexp structure which is in turn stored in
+    PL_curpm/PL_reg_curpm. The problem is that due to qr// the pattern
+    could be PL_curpm in multiple contexts, and could require multiple
+    result sets being associated with the pattern simultaneously, such
+    as when doing a recursive match with (??{$qr})
+    
+    The solution is to make a lightweight copy of the regexp structure 
+    when a qr// is returned from the code executed by (??{$qr}) this
+    lightweight copy doesnt actually own any of its data except for
+    the starp/end and the actual regexp structure itself. 
+    
+*/    
+    
+    
+regexp *
+Perl_reg_temp_copy (pTHX_ struct regexp *r) {
+    regexp *ret;
+    register const I32 npar = r->nparens+1;
+    (void)ReREFCNT_inc(r);
+    Newx(ret, 1, regexp);
+    StructCopy(r, ret, regexp);
+    Newx(ret->startp, npar * 2, I32);
+    Copy(r->startp, ret->startp, npar * 2, I32);
+    ret->endp = ret->startp + npar;
+    ret->refcnt = 1;
+    if (r->substrs) {
+        struct reg_substr_datum *s;
+        I32 i;
+        Newx(ret->substrs, 1, struct reg_substr_data);
+        for (s = ret->substrs->data, i = 0; i < 3; i++, s++) {
+            s->min_offset = r->substrs->data[i].min_offset;
+            s->max_offset = r->substrs->data[i].max_offset;
+            s->end_shift  = r->substrs->data[i].end_shift;
+            if (i < 2) {
+               s->substr      = SvREFCNT_inc(r->substrs->data[i].substr);
+               s->utf8_substr = SvREFCNT_inc(r->substrs->data[i].utf8_substr);
+           }
+        }
+       if (r->check_substr == r->anchored_substr)
+           ret->check_substr = ret->anchored_substr;
+       else if (r->check_substr == r->float_substr)
+           ret->check_substr = ret->float_substr;
+       else {
+           assert(!r->check_substr);
+           ret->check_substr = NULL;
+       }
+       if (r->check_utf8 == r->anchored_utf8)
+           ret->check_utf8 = ret->anchored_utf8;
+       else if (r->check_utf8 == r->float_utf8)
+           ret->check_utf8 = ret->float_utf8;
+       else {
+           assert(!r->check_utf8);
+           ret->check_utf8 = NULL;
+       }
+    }
+    RX_MATCH_COPIED_off(ret);
+#ifdef PERL_OLD_COPY_ON_WRITE
+    /* this is broken. */
+    assert(0); 
+    if (ret->saved_copy)
+        ret->saved_copy=NULL;
+#endif
+    ret->mother_re = r; 
+    ret->swap = NULL;
+    
+    return ret;
+}
 #endif
 
 /* regfree_internal() 
@@ -8797,11 +8888,7 @@ Perl_regfree_internal(pTHX_ struct regexp *r)
        Safefree(ri->data->what);
        Safefree(ri->data);
     }
-    if (ri->swap) {
-        Safefree(ri->swap->startp);
-        Safefree(ri->swap->endp);
-        Safefree(ri->swap);
-    }
+
     Safefree(ri);
 }
 
@@ -8831,8 +8918,7 @@ Perl_re_dup(pTHX_ const regexp *r, CLONE_PARAMS *param)
 {
     dVAR;
     regexp *ret;
-    int i, npar;
-    struct reg_substr_datum *s;
+    I32 npar;
 
     if (!r)
        return (REGEXP *)NULL;
@@ -8843,28 +8929,42 @@ Perl_re_dup(pTHX_ const regexp *r, CLONE_PARAMS *param)
     
     npar = r->nparens+1;
     Newxz(ret, 1, regexp);
-    Newx(ret->startp, npar, I32);
-    Copy(r->startp, ret->startp, npar, I32);
-    Newx(ret->endp, npar, I32);
-    Copy(r->endp, ret->endp, npar, I32);
+    Newx(ret->startp, npar * 2, I32);
+    Copy(r->startp, ret->startp, npar * 2, I32);
+    ret->endp = ret->startp + npar;
+    if(r->swap) {
+        Newx(ret->swap, 1, regexp_paren_ofs);
+        /* no need to copy these */
+        Newx(ret->swap->startp, npar * 2, I32);
+       ret->swap->endp = ret->swap->startp + npar;
+    } else {
+        ret->swap = NULL;
+    }
 
     if (r->substrs) {
+       struct reg_substr_datum *s;
+       const struct reg_substr_datum *s_end;
+
         Newx(ret->substrs, 1, struct reg_substr_data);
-        for (s = ret->substrs->data, i = 0; i < 3; i++, s++) {
-            s->min_offset = r->substrs->data[i].min_offset;
-            s->max_offset = r->substrs->data[i].max_offset;
-            s->end_shift  = r->substrs->data[i].end_shift;
-            s->substr     = sv_dup_inc(r->substrs->data[i].substr, param);
-            s->utf8_substr = sv_dup_inc(r->substrs->data[i].utf8_substr, param);
-        }
+       StructCopy(r->substrs, ret->substrs, struct reg_substr_data);
+
+       s = ret->substrs->data;
+       s_end
+           = s + sizeof(ret->substrs->data) / sizeof(struct reg_substr_datum);
+
+       do {
+            s->substr      = sv_dup_inc(s->substr, param);
+            s->utf8_substr = sv_dup_inc(s->utf8_substr, param);
+        } while (++s < s_end);
     } else 
         ret->substrs = NULL;    
 
-    ret->wrapped        = SAVEPVN(r->wrapped, r->wraplen);
+    ret->wrapped        = SAVEPVN(r->wrapped, r->wraplen+1);
     ret->precomp        = ret->wrapped + (r->precomp - r->wrapped);
     ret->prelen         = r->prelen;
     ret->wraplen        = r->wraplen;
 
+    ret->mother_re      = NULL;
     ret->refcnt         = r->refcnt;
     ret->minlen         = r->minlen;
     ret->minlenret      = r->minlenret;
@@ -8925,14 +9025,6 @@ Perl_regdupe_internal(pTHX_ const regexp *r, CLONE_PARAMS *param)
     Newxc(reti, sizeof(regexp_internal) + (len+1)*sizeof(regnode), char, regexp_internal);
     Copy(ri->program, reti->program, len+1, regnode);
     
-    if(ri->swap) {
-        Newx(reti->swap, 1, regexp_paren_ofs);
-        /* no need to copy these */
-        Newx(reti->swap->startp, npar, I32);
-        Newx(reti->swap->endp, npar, I32);
-    } else {
-        reti->swap = NULL;
-    }
 
     reti->regstclass = NULL;