chr                *search_start;       /* search start of string */
        chr                *stop;                       /* just past end of string */
        int                     err;                    /* error code if any (0 none) */
+       struct dfa **subdfas;           /* per-subre DFAs */
        struct smalldfa dfa1;
        struct smalldfa dfa2;
 };
  * forward declarations
  */
 /* === regexec.c === */
+static struct dfa *getsubdfa(struct vars *, struct subre *);
 static int     find(struct vars *, struct cnfa *, struct colormap *);
 static int     cfind(struct vars *, struct cnfa *, struct colormap *);
 static int     cfindloop(struct vars *, struct cnfa *, struct colormap *, struct dfa *, struct dfa *, chr **);
        register struct vars *v = &var;
        int                     st;
        size_t          n;
+       size_t          i;
        int                     backref;
 
 #define  LOCALMAT       20
        regmatch_t      mat[LOCALMAT];
 
+#define  LOCALDFAS      40
+       struct dfa *subdfas[LOCALDFAS];
+
        /* sanity checks */
        if (re == NULL || string == NULL || re->re_magic != REMAGIC)
                return REG_INVARG;
        v->search_start = (chr *) string + search_start;
        v->stop = (chr *) string + len;
        v->err = 0;
+       assert(v->g->ntree >= 0);
+       n = (size_t) v->g->ntree;
+       if (n <= LOCALDFAS)
+               v->subdfas = subdfas;
+       else
+               v->subdfas = (struct dfa **) MALLOC(n * sizeof(struct dfa *));
+       if (v->subdfas == NULL)
+       {
+               if (v->pmatch != pmatch && v->pmatch != mat)
+                       FREE(v->pmatch);
+               return REG_ESPACE;
+       }
+       for (i = 0; i < n; i++)
+               v->subdfas[i] = NULL;
 
        /* do it */
        assert(v->g->tree != NULL);
        /* clean up */
        if (v->pmatch != pmatch && v->pmatch != mat)
                FREE(v->pmatch);
+       for (i = 0; i < n; i++)
+       {
+               if (v->subdfas[i] != NULL)
+                       freedfa(v->subdfas[i]);
+       }
+       if (v->subdfas != subdfas)
+               FREE(v->subdfas);
+
        return st;
 }
 
+/*
+ * getsubdfa - create or re-fetch the DFA for a subre node
+ *
+ * We only need to create the DFA once per overall regex execution.
+ * The DFA will be freed by the cleanup step in pg_regexec().
+ */
+static struct dfa *
+getsubdfa(struct vars * v,
+                 struct subre * t)
+{
+       if (v->subdfas[t->id] == NULL)
+       {
+               v->subdfas[t->id] = newdfa(v, &t->cnfa, &v->g->cmap, DOMALLOC);
+               if (ISERR())
+                       return NULL;
+       }
+       return v->subdfas[t->id];
+}
+
 /*
  * find - find a match for the main NFA (no-complications case)
  */
        assert(t->left != NULL && t->left->cnfa.nstates > 0);
        assert(t->right != NULL && t->right->cnfa.nstates > 0);
 
-       d = newdfa(v, &t->left->cnfa, &v->g->cmap, &v->dfa1);
+       d = getsubdfa(v, t->left);
+       NOERR();
+       d2 = getsubdfa(v, t->right);
        NOERR();
-       d2 = newdfa(v, &t->right->cnfa, &v->g->cmap, &v->dfa2);
-       if (ISERR())
-       {
-               assert(d2 == NULL);
-               freedfa(d);
-               return v->err;
-       }
 
        /* pick a tentative midpoint */
        if (shorter)
        else
                mid = longest(v, d, begin, end, (int *) NULL);
        if (mid == NULL)
-       {
-               freedfa(d);
-               freedfa(d2);
                return REG_ASSERT;
-       }
        MDEBUG(("tentative midpoint %ld\n", LOFF(mid)));
 
        /* iterate until satisfaction or failure */
                {
                        /* all possibilities exhausted! */
                        MDEBUG(("no midpoint!\n"));
-                       freedfa(d);
-                       freedfa(d2);
                        return REG_ASSERT;
                }
                if (shorter)
                {
                        /* failed to find a new one! */
                        MDEBUG(("failed midpoint!\n"));
-                       freedfa(d);
-                       freedfa(d2);
                        return REG_ASSERT;
                }
                MDEBUG(("new midpoint %ld\n", LOFF(mid)));
 
        /* satisfaction */
        MDEBUG(("successful\n"));
-       freedfa(d);
-       freedfa(d2);
        i = dissect(v, t->left, begin, mid);
        if (i != REG_OKAY)
                return i;
        {
                MDEBUG(("trying %dth\n", i));
                assert(t->left != NULL && t->left->cnfa.nstates > 0);
-               d = newdfa(v, &t->left->cnfa, &v->g->cmap, &v->dfa1);
-               if (ISERR())
-                       return v->err;
+               d = getsubdfa(v, t->left);
+               NOERR();
                if (longest(v, d, begin, end, (int *) NULL) == end)
                {
                        MDEBUG(("success\n"));
-                       freedfa(d);
                        return dissect(v, t->left, begin, end);
                }
-               freedfa(d);
        }
        return REG_ASSERT;                      /* none of them matched?!? */
 }
                return REG_ESPACE;
        endpts[0] = begin;
 
-       d = newdfa(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
+       d = getsubdfa(v, t->left);
        if (ISERR())
        {
                FREE(endpts);
                        if (er == REG_NOMATCH)
                                break;
                        /* oops, something failed */
-                       freedfa(d);
                        FREE(endpts);
                        return er;
                }
                {
                        /* satisfaction */
                        MDEBUG(("%d successful\n", t->id));
-                       freedfa(d);
                        FREE(endpts);
                        return REG_OKAY;
                }
 
        /* all possibilities exhausted - shouldn't happen in uncomplicated mode */
        MDEBUG(("%d failed\n", t->id));
-       freedfa(d);
        FREE(endpts);
        return REG_ASSERT;
 }
                return REG_ESPACE;
        endpts[0] = begin;
 
-       d = newdfa(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
+       d = getsubdfa(v, t->left);
        if (ISERR())
        {
                FREE(endpts);
                        if (er == REG_NOMATCH)
                                break;
                        /* oops, something failed */
-                       freedfa(d);
                        FREE(endpts);
                        return er;
                }
                {
                        /* satisfaction */
                        MDEBUG(("%d successful\n", t->id));
-                       freedfa(d);
                        FREE(endpts);
                        return REG_OKAY;
                }
 
        /* all possibilities exhausted - shouldn't happen in uncomplicated mode */
        MDEBUG(("%d failed\n", t->id));
-       freedfa(d);
        FREE(endpts);
        return REG_ASSERT;
 }
        if (t->left->flags & SHORTER)           /* reverse scan */
                return crevdissect(v, t, begin, end);
 
-       d = newdfa(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
-       if (ISERR())
-               return v->err;
-       d2 = newdfa(v, &t->right->cnfa, &v->g->cmap, DOMALLOC);
-       if (ISERR())
-       {
-               freedfa(d);
-               return v->err;
-       }
+       d = getsubdfa(v, t->left);
+       NOERR();
+       d2 = getsubdfa(v, t->right);
+       NOERR();
        MDEBUG(("cconcat %d\n", t->id));
 
        /* pick a tentative midpoint */
        mid = longest(v, d, begin, end, (int *) NULL);
        if (mid == NULL)
-       {
-               freedfa(d);
-               freedfa(d2);
                return REG_NOMATCH;
-       }
        MDEBUG(("tentative midpoint %ld\n", LOFF(mid)));
 
        /* iterate until satisfaction or failure */
                                {
                                        /* satisfaction */
                                        MDEBUG(("successful\n"));
-                                       freedfa(d);
-                                       freedfa(d2);
                                        return REG_OKAY;
                                }
                        }
                        if (er != REG_OKAY && er != REG_NOMATCH)
-                       {
-                               freedfa(d);
-                               freedfa(d2);
                                return er;
-                       }
                }
 
                /* that midpoint didn't work, find a new one */
                {
                        /* all possibilities exhausted */
                        MDEBUG(("%d no midpoint\n", t->id));
-                       freedfa(d);
-                       freedfa(d2);
                        return REG_NOMATCH;
                }
                mid = longest(v, d, begin, mid - 1, (int *) NULL);
                {
                        /* failed to find a new one */
                        MDEBUG(("%d failed midpoint\n", t->id));
-                       freedfa(d);
-                       freedfa(d2);
                        return REG_NOMATCH;
                }
                MDEBUG(("%d: new midpoint %ld\n", t->id, LOFF(mid)));
        assert(t->left->flags & SHORTER);
 
        /* concatenation -- need to split the substring between parts */
-       d = newdfa(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
-       if (ISERR())
-               return v->err;
-       d2 = newdfa(v, &t->right->cnfa, &v->g->cmap, DOMALLOC);
-       if (ISERR())
-       {
-               freedfa(d);
-               return v->err;
-       }
+       d = getsubdfa(v, t->left);
+       NOERR();
+       d2 = getsubdfa(v, t->right);
+       NOERR();
        MDEBUG(("crev %d\n", t->id));
 
        /* pick a tentative midpoint */
        mid = shortest(v, d, begin, begin, end, (chr **) NULL, (int *) NULL);
        if (mid == NULL)
-       {
-               freedfa(d);
-               freedfa(d2);
                return REG_NOMATCH;
-       }
        MDEBUG(("tentative midpoint %ld\n", LOFF(mid)));
 
        /* iterate until satisfaction or failure */
                                {
                                        /* satisfaction */
                                        MDEBUG(("successful\n"));
-                                       freedfa(d);
-                                       freedfa(d2);
                                        return REG_OKAY;
                                }
                        }
                        if (er != REG_OKAY && er != REG_NOMATCH)
-                       {
-                               freedfa(d);
-                               freedfa(d2);
                                return er;
-                       }
                }
 
                /* that midpoint didn't work, find a new one */
                {
                        /* all possibilities exhausted */
                        MDEBUG(("%d no midpoint\n", t->id));
-                       freedfa(d);
-                       freedfa(d2);
                        return REG_NOMATCH;
                }
                mid = shortest(v, d, begin, mid + 1, end, (chr **) NULL, (int *) NULL);
                {
                        /* failed to find a new one */
                        MDEBUG(("%d failed midpoint\n", t->id));
-                       freedfa(d);
-                       freedfa(d2);
                        return REG_NOMATCH;
                }
                MDEBUG(("%d: new midpoint %ld\n", t->id, LOFF(mid)));
 
        MDEBUG(("calt n%d\n", t->id));
 
-       d = newdfa(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
-       if (ISERR())
-               return v->err;
+       d = getsubdfa(v, t->left);
+       NOERR();
        if (longest(v, d, begin, end, (int *) NULL) != end)
-       {
-               freedfa(d);
                return caltdissect(v, t->right, begin, end);
-       }
-       freedfa(d);
        MDEBUG(("calt matched\n"));
 
        er = cdissect(v, t->left, begin, end);
                return REG_ESPACE;
        endpts[0] = begin;
 
-       d = newdfa(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
+       d = getsubdfa(v, t->left);
        if (ISERR())
        {
                FREE(endpts);
                        if (er == REG_NOMATCH)
                                break;
                        /* oops, something failed */
-                       freedfa(d);
                        FREE(endpts);
                        return er;
                }
                {
                        /* satisfaction */
                        MDEBUG(("%d successful\n", t->id));
-                       freedfa(d);
                        FREE(endpts);
                        return REG_OKAY;
                }
 
        /* all possibilities exhausted */
        MDEBUG(("%d failed\n", t->id));
-       freedfa(d);
        FREE(endpts);
        return REG_NOMATCH;
 }
                return REG_ESPACE;
        endpts[0] = begin;
 
-       d = newdfa(v, &t->left->cnfa, &v->g->cmap, DOMALLOC);
+       d = getsubdfa(v, t->left);
        if (ISERR())
        {
                FREE(endpts);
                        if (er == REG_NOMATCH)
                                break;
                        /* oops, something failed */
-                       freedfa(d);
                        FREE(endpts);
                        return er;
                }
                {
                        /* satisfaction */
                        MDEBUG(("%d successful\n", t->id));
-                       freedfa(d);
                        FREE(endpts);
                        return REG_OKAY;
                }
 
        /* all possibilities exhausted */
        MDEBUG(("%d failed\n", t->id));
-       freedfa(d);
        FREE(endpts);
        return REG_NOMATCH;
 }