[Pcre-svn] [1279] code/trunk: Experimental support of (*THEN…

Startseite
Nachricht löschen
Autor: Subversion repository
Datum:  
To: pcre-svn
Betreff: [Pcre-svn] [1279] code/trunk: Experimental support of (*THEN) backtracking verb in the JIT compiler.
Revision: 1279
          http://vcs.pcre.org/viewvc?view=rev&revision=1279
Author:   zherczeg
Date:     2013-03-12 17:27:34 +0000 (Tue, 12 Mar 2013)


Log Message:
-----------
Experimental support of (*THEN) backtracking verb in the JIT compiler.

Modified Paths:
--------------
    code/trunk/ChangeLog
    code/trunk/pcre_jit_compile.c


Modified: code/trunk/ChangeLog
===================================================================
--- code/trunk/ChangeLog    2013-03-12 06:15:04 UTC (rev 1278)
+++ code/trunk/ChangeLog    2013-03-12 17:27:34 UTC (rev 1279)
@@ -106,7 +106,9 @@


28. Experimental support of (*SKIP) backtracking verb in the JIT compiler.

+29. Experimental support of (*THEN) backtracking verb in the JIT compiler.

+
Version 8.32 30-November-2012
-----------------------------


Modified: code/trunk/pcre_jit_compile.c
===================================================================
--- code/trunk/pcre_jit_compile.c    2013-03-12 06:15:04 UTC (rev 1278)
+++ code/trunk/pcre_jit_compile.c    2013-03-12 17:27:34 UTC (rev 1279)
@@ -196,6 +196,11 @@
   struct stub_list *next;
 } stub_list;


+enum bytecode_flag_types {
+ flag_optimized_cbracket = 1,
+ flag_then_start = 2,
+};
+
enum frame_types {
no_frame = -1,
no_stack = -2
@@ -206,7 +211,8 @@
type_prune = 1,
type_skip = 2,
type_skip_arg = 3,
- type_mark = 4
+ type_mark = 4,
+ type_then_trap = 5
};

typedef int (SLJIT_CALL *jit_function)(jit_arguments *args);
@@ -293,6 +299,13 @@
BOOL inlined_pattern;
} recurse_backtrack;

+typedef struct then_trap_backtrack {
+ backtrack_common common;
+ struct then_trap_backtrack *then_trap;
+ jump_list *quit;
+ int framesize;
+} then_trap_backtrack;
+
#define MAX_RANGE_SIZE 6

typedef struct compiler_common {
@@ -304,6 +317,10 @@
int *private_data_ptrs;
/* Tells whether the capturing bracket is optimized. */
pcre_uint8 *optimized_cbracket;
+ /* Tells whether the starting offset is a target of then. */
+ pcre_uint8 *then_offsets;
+ /* Current position where a THEN must jump. */
+ then_trap_backtrack *then_trap;
/* Starting offset of private data for capturing brackets. */
int cbra_ptr;
/* Output vector starting point. Must be divisible by 2. */
@@ -332,10 +349,12 @@
sljit_sw lcc;
/* Mode can be PCRE_STUDY_JIT_COMPILE and others. */
int mode;
- /* \K is in the pattern. */
+ /* \K is found in the pattern. */
BOOL has_set_som;
- /* (*SKIP:arg) is in the pattern. */
+ /* (*SKIP:arg) is found in the pattern. */
BOOL has_skip_arg;
+ /* (*THEN) is found in the pattern. */
+ BOOL has_then;
/* Needs to know the start position anytime. */
BOOL needs_start_ptr;
/* Currently in recurse or assert. */
@@ -601,6 +620,7 @@
case OP_BRAPOSZERO:
case OP_PRUNE:
case OP_SKIP:
+ case OP_THEN:
case OP_COMMIT:
case OP_FAIL:
case OP_ACCEPT:
@@ -701,6 +721,7 @@
case OP_MARK:
case OP_PRUNE_ARG:
case OP_SKIP_ARG:
+ case OP_THEN_ARG:
return cc + 1 + 2 + cc[1];

   default:
@@ -952,6 +973,10 @@
     cc += 2 + 2 * LINK_SIZE;
     break;


+    case OP_THEN_ARG:
+    common->has_then = TRUE;
+    /* Fall through. */
+
     case OP_PRUNE_ARG:
     common->needs_start_ptr = TRUE;
     common->control_head_ptr = 1;
@@ -966,6 +991,10 @@
     cc += 1 + 2 + cc[1];
     break;


+    case OP_THEN:
+    common->has_then = TRUE;
+    /* Fall through. */
+
     case OP_PRUNE:
     case OP_SKIP:
     common->needs_start_ptr = TRUE;
@@ -1156,9 +1185,8 @@
 }


/* Returns with a frame_types (always < 0) if no need for frame. */
-static int get_framesize(compiler_common *common, pcre_uchar *cc, BOOL recursive, BOOL* needs_control_head)
+static int get_framesize(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, BOOL recursive, BOOL* needs_control_head)
{
-pcre_uchar *ccend = bracketend(cc) - (1 + LINK_SIZE);
int length = 0;
int possessive = 0;
BOOL stack_restore = FALSE;
@@ -1174,14 +1202,18 @@
*needs_control_head = FALSE;
#endif

-if (!recursive && (*cc == OP_CBRAPOS || *cc == OP_SCBRAPOS))
+if (ccend == NULL)
   {
-  possessive = length = (common->capture_last_ptr != 0) ? 5 : 3;
-  /* This is correct regardless of common->capture_last_ptr. */
-  capture_last_found = TRUE;
+  ccend = bracketend(cc) - (1 + LINK_SIZE);
+  if (!recursive && (*cc == OP_CBRAPOS || *cc == OP_SCBRAPOS))
+    {
+    possessive = length = (common->capture_last_ptr != 0) ? 5 : 3;
+    /* This is correct regardless of common->capture_last_ptr. */
+    capture_last_found = TRUE;
+    }
+  cc = next_opcode(common, cc);
   }


-cc = next_opcode(common, cc);
SLJIT_ASSERT(cc != NULL);
while (cc < ccend)
switch(*cc)
@@ -1199,6 +1231,7 @@

     case OP_MARK:
     case OP_PRUNE_ARG:
+    case OP_THEN_ARG:
     SLJIT_ASSERT(common->mark_ptr != 0);
     stack_restore = TRUE;
     if (!setmark_found)
@@ -1335,10 +1368,9 @@
 return stack_restore ? no_frame : no_stack;
 }


-static void init_frame(compiler_common *common, pcre_uchar *cc, int stackpos, int stacktop, BOOL recursive)
+static void init_frame(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, int stackpos, int stacktop, BOOL recursive)
{
DEFINE_COMPILER;
-pcre_uchar *ccend = bracketend(cc) - (1 + LINK_SIZE);
BOOL setsom_found = recursive;
BOOL setmark_found = recursive;
/* The last capture is a local variable even for recursions. */
@@ -1350,8 +1382,13 @@
SLJIT_ASSERT(stackpos >= stacktop + 2);

 stackpos = STACK(stackpos);
-if (recursive || (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS))
-  cc = next_opcode(common, cc);
+if (ccend == NULL)
+  {
+  ccend = bracketend(cc) - (1 + LINK_SIZE);
+  if (recursive || (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS))
+    cc = next_opcode(common, cc);
+  }
+
 SLJIT_ASSERT(cc != NULL);
 while (cc < ccend)
   switch(*cc)
@@ -1372,6 +1409,7 @@


     case OP_MARK:
     case OP_PRUNE_ARG:
+    case OP_THEN_ARG:
     SLJIT_ASSERT(common->mark_ptr != 0);
     if (!setmark_found)
       {
@@ -1877,6 +1915,39 @@
 SLJIT_ASSERT(cc == ccend && stackptr == stacktop && (save || (tmp1empty && tmp2empty)));
 }


+static SLJIT_INLINE pcre_uchar *set_then_offsets(compiler_common *common, pcre_uchar *cc, pcre_uint8 *current_offset)
+{
+pcre_uchar *end = bracketend(cc);
+BOOL has_alternatives = cc[GET(cc, 1)] == OP_ALT;
+
+/* Assert captures then. */
+if (*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NOT)
+  current_offset = NULL;
+/* Conditional block does not. */
+if (*cc == OP_COND || *cc == OP_SCOND)
+  has_alternatives = FALSE;
+
+cc = next_opcode(common, cc);
+if (has_alternatives)
+  current_offset = common->then_offsets + (cc - common->start);
+
+while (cc < end)
+  {
+  if ((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NOT) || (*cc >= OP_ONCE && *cc <= OP_SCOND))
+    cc = set_then_offsets(common, cc, current_offset);
+  else
+    {
+    if (*cc == OP_ALT && has_alternatives)
+      current_offset = common->then_offsets + (cc + 1 + LINK_SIZE - common->start);
+    if (*cc >= OP_THEN && *cc <= OP_THEN_ARG && current_offset != NULL)
+      *current_offset = 1;
+    cc = next_opcode(common, cc);
+    }
+  }
+
+return end;
+}
+
 #undef CASE_ITERATOR_PRIVATE_DATA_1
 #undef CASE_ITERATOR_PRIVATE_DATA_2A
 #undef CASE_ITERATOR_PRIVATE_DATA_2B
@@ -2046,6 +2117,7 @@
     return -1;


     case type_prune:
+    case type_then_trap:
     break;


     case type_skip:
@@ -2075,6 +2147,35 @@
 return (return_value != 0 || skip_arg == NULL) ? return_value : -2;
 }


+static sljit_sw SLJIT_CALL do_search_then_trap(sljit_sw *current)
+{
+do
+  {
+  switch (current[-2])
+    {
+    case type_commit:
+    /* Commit overwrites all. */
+    return 0;
+
+    case type_then_trap:
+    return (sljit_sw)current;
+
+    case type_prune:
+    case type_skip:
+    case type_skip_arg:
+    case type_mark:
+    break;
+
+    default:
+    SLJIT_ASSERT_STOP();
+    break;
+    }
+  current = (sljit_sw*)current[-1];
+  SLJIT_ASSERT(current != NULL);
+  }
+while (TRUE);
+}
+
 static SLJIT_INLINE void copy_ovector(compiler_common *common, int topbracket)
 {
 DEFINE_COMPILER;
@@ -5287,7 +5388,7 @@
 PUSH_BACKTRACK(sizeof(recurse_backtrack), cc, NULL);


/* Inlining simple patterns. */
-if (get_framesize(common, common->start + start, TRUE, &needs_control_head) == no_stack)
+if (get_framesize(common, common->start + start, NULL, TRUE, &needs_control_head) == no_stack)
{
start_cc = common->start + start;
compile_matchingpath(common, next_opcode(common, start_cc), bracketend(start_cc) - (1 + LINK_SIZE), backtrack);
@@ -5457,14 +5558,18 @@
jump_list **target = (conditional) ? &backtrack->condfailed : &backtrack->common.topbacktracks;
jump_list **found;
/* Saving previous accept variables. */
+BOOL save_local_exit = common->local_exit;
+then_trap_backtrack *save_then_trap = common->then_trap;
struct sljit_label *save_quit_label = common->quit_label;
struct sljit_label *save_accept_label = common->accept_label;
jump_list *save_quit = common->quit;
jump_list *save_accept = common->accept;
-BOOL save_local_exit = common->local_exit;
struct sljit_jump *jump;
struct sljit_jump *brajump = NULL;

+/* Assert captures then. */
+common->then_trap = NULL;
+
 if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO)
   {
   SLJIT_ASSERT(!conditional);
@@ -5473,7 +5578,7 @@
   }
 private_data_ptr = PRIVATE_DATA(cc);
 SLJIT_ASSERT(private_data_ptr != 0);
-framesize = get_framesize(common, cc, FALSE, &needs_control_head);
+framesize = get_framesize(common, cc, NULL, FALSE, &needs_control_head);
 backtrack->framesize = framesize;
 backtrack->private_data_ptr = private_data_ptr;
 opcode = *cc;
@@ -5523,7 +5628,7 @@
     }
   else
     OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
-  init_frame(common, ccbegin, framesize + extrasize - 1, extrasize, FALSE);
+  init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize, FALSE);
   }


 memset(&altbacktrack, 0, sizeof(backtrack_common));
@@ -5545,6 +5650,7 @@
   if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
     {
     common->local_exit = save_local_exit;
+    common->then_trap = save_then_trap;
     common->quit_label = save_quit_label;
     common->accept_label = save_accept_label;
     common->quit = save_quit;
@@ -5613,6 +5719,7 @@
   if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
     {
     common->local_exit = save_local_exit;
+    common->then_trap = save_then_trap;
     common->quit_label = save_quit_label;
     common->accept_label = save_accept_label;
     common->quit = save_quit;
@@ -5793,6 +5900,7 @@
   }


 common->local_exit = save_local_exit;
+common->then_trap = save_then_trap;
 common->quit_label = save_quit_label;
 common->accept_label = save_accept_label;
 common->quit = save_quit;
@@ -6125,7 +6233,7 @@
   SLJIT_ASSERT(private_data_ptr != 0);
   BACKTRACK_AS(bracket_backtrack)->private_data_ptr = private_data_ptr;
   if (opcode == OP_ONCE)
-    BACKTRACK_AS(bracket_backtrack)->u.framesize = get_framesize(common, ccbegin, FALSE, &needs_control_head);
+    BACKTRACK_AS(bracket_backtrack)->u.framesize = get_framesize(common, ccbegin, NULL, FALSE, &needs_control_head);
   }


 /* Instructions before the first alternative. */
@@ -6275,7 +6383,7 @@
       OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), private_data_ptr, TMP2, 0);
       OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0);
       }
-    init_frame(common, ccbegin, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1, FALSE);
+    init_frame(common, ccbegin, NULL, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1, FALSE);
     }
   }
 else if (opcode == OP_CBRA || opcode == OP_SCBRA)
@@ -6569,7 +6677,7 @@
   break;
   }


-framesize = get_framesize(common, cc, FALSE, &needs_control_head);
+framesize = get_framesize(common, cc, NULL, FALSE, &needs_control_head);
 BACKTRACK_AS(bracketpos_backtrack)->framesize = framesize;
 if (framesize < 0)
   {
@@ -6662,7 +6770,7 @@
     stack++;
     }
   OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP1, 0);
-  init_frame(common, cc, stacksize - 1, stacksize - framesize, FALSE);
+  init_frame(common, cc, NULL, stacksize - 1, stacksize - framesize, FALSE);
   stack -= 1 + (offset == 0);
   }


@@ -7169,11 +7277,100 @@
return cc + 1 + IMM2_SIZE;
}

+static SLJIT_INLINE pcre_uchar *compile_control_verb_matchingpath(compiler_common *common, pcre_uchar *cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+pcre_uchar opcode = *cc;
+pcre_uchar *ccend = cc + 1;
+
+SLJIT_ASSERT(common->control_head_ptr != 0 || *cc == OP_COMMIT);
+
+if (opcode == OP_PRUNE_ARG || opcode == OP_SKIP_ARG || opcode == OP_THEN_ARG)
+ ccend += 2 + cc[1];
+
+PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL);
+
+if (opcode == OP_SKIP || opcode == OP_SKIP_ARG)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr);
+ allocate_stack(common, 3);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, STACK_TOP, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, *cc == OP_SKIP ? type_skip : type_skip_arg);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), (opcode == OP_SKIP) ? STR_PTR : SLJIT_IMM, (opcode == OP_SKIP) ? 0 : (sljit_sw)(cc + 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ return ccend;
+ }
+
+if (opcode == OP_PRUNE_ARG || opcode == OP_THEN_ARG)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr, TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr), TMP2, 0);
+ }
+
+if (common->control_head_ptr != 0 && ((opcode != OP_THEN && opcode != OP_THEN_ARG) || common->then_trap == NULL))
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr);
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, STACK_TOP, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, *cc == OP_COMMIT ? type_commit : type_prune);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ }
+
+return ccend;
+}
+
+static pcre_uchar then_trap_opcode[1] = { OP_TABLE_LENGTH };
+
+static SLJIT_INLINE void compile_then_trap_matchingpath(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+BOOL needs_control_head;
+int size;
+
+PUSH_BACKTRACK_NOVALUE(sizeof(then_trap_backtrack), cc);
+common->then_trap = BACKTRACK_AS(then_trap_backtrack);
+BACKTRACK_AS(then_trap_backtrack)->common.cc = then_trap_opcode;
+BACKTRACK_AS(then_trap_backtrack)->framesize = get_framesize(common, cc, ccend, FALSE, &needs_control_head);
+
+size = BACKTRACK_AS(then_trap_backtrack)->framesize;
+size = 2 + (size < 0 ? 0 : size);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr);
+allocate_stack(common, size);
+if (size > 2)
+ OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 2) * sizeof(sljit_sw));
+else
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, STACK_TOP, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 1), SLJIT_IMM, type_then_trap);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 2), TMP2, 0);
+
+size = BACKTRACK_AS(then_trap_backtrack)->framesize;
+if (size >= 0)
+ init_frame(common, cc, ccend, size - 1, 0, FALSE);
+}
+
static void compile_matchingpath(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, backtrack_common *parent)
{
DEFINE_COMPILER;
backtrack_common *backtrack;
+BOOL has_then_trap = FALSE;
+then_trap_backtrack *save_then_trap = NULL;

+SLJIT_ASSERT(*ccend == OP_END || (*ccend >= OP_ALT && *ccend <= OP_KETRPOS));
+
+if (common->has_then && common->then_offsets[cc - common->start] != 0)
+  {
+  SLJIT_ASSERT(*ccend != OP_END && common->control_head_ptr != 0);
+  has_then_trap = TRUE;
+  save_then_trap = common->then_trap;
+  /* Tail item on backtrack. */
+  compile_then_trap_matchingpath(common, cc, ccend, parent);
+  }
+
 while (cc < ccend)
   {
   switch(*cc)
@@ -7407,35 +7604,14 @@
     cc += 1 + 2 + cc[1];
     break;


+    case OP_PRUNE:
     case OP_PRUNE_ARG:
-    OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
-    OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2));
-    OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->mark_ptr, TMP2, 0);
-    OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr), TMP2, 0);
-    /* Fall through. */
-
-    case OP_PRUNE:
-    case OP_COMMIT:
-    PUSH_BACKTRACK_NOVALUE(sizeof(backtrack_common), cc);
-    SLJIT_ASSERT(common->control_head_ptr != 0);
-    OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr);
-    allocate_stack(common, 2);
-    OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, STACK_TOP, 0);
-    OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, *cc == OP_COMMIT ? type_commit : type_prune);
-    OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
-    cc += (*cc == OP_PRUNE_ARG) ? (1 + 2 + cc[1]) : 1;
-    break;
-
     case OP_SKIP:
     case OP_SKIP_ARG:
-    PUSH_BACKTRACK_NOVALUE(sizeof(backtrack_common), cc);
-    OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr);
-    allocate_stack(common, 3);
-    OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, STACK_TOP, 0);
-    OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, *cc == OP_SKIP ? type_skip : type_skip_arg);
-    OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), *cc == OP_SKIP ? STR_PTR : SLJIT_IMM, *cc == OP_SKIP ? 0 : (sljit_sw)(cc + 2));
-    OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
-    cc += (*cc == OP_SKIP_ARG) ? (1 + 2 + cc[1]) : 1;
+    case OP_THEN:
+    case OP_THEN_ARG:
+    case OP_COMMIT:
+    cc = compile_control_verb_matchingpath(common, cc, parent);
     break;


     case OP_FAIL:
@@ -7459,6 +7635,15 @@
   if (cc == NULL)
     return;
   }
+
+if (has_then_trap)
+  {
+  /* Head item on backtrack. */
+  PUSH_BACKTRACK_NOVALUE(sizeof(then_trap_backtrack), cc);
+  BACKTRACK_AS(then_trap_backtrack)->common.cc = then_trap_opcode;
+  BACKTRACK_AS(then_trap_backtrack)->then_trap = common->then_trap;
+  common->then_trap = save_then_trap;
+  }
 SLJIT_ASSERT(cc == ccend);
 }


@@ -7620,7 +7805,7 @@
}
}

-static void compile_ref_iterator_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+static SLJIT_INLINE void compile_ref_iterator_backtrackingpath(compiler_common *common, struct backtrack_common *current)
{
DEFINE_COMPILER;
pcre_uchar *cc = current->cc;
@@ -7642,7 +7827,7 @@
free_stack(common, 2);
}

-static void compile_recurse_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+static SLJIT_INLINE void compile_recurse_backtrackingpath(compiler_common *common, struct backtrack_common *current)
{
DEFINE_COMPILER;

@@ -8142,7 +8327,7 @@
}
}

-static void compile_bracketpos_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+static SLJIT_INLINE void compile_bracketpos_backtrackingpath(compiler_common *common, struct backtrack_common *current)
{
DEFINE_COMPILER;
int offset;
@@ -8181,7 +8366,7 @@
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracketpos_backtrack)->framesize * sizeof(sljit_sw));
}

-static void compile_braminzero_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+static SLJIT_INLINE void compile_braminzero_backtrackingpath(compiler_common *common, struct backtrack_common *current)
{
assert_backtrack backtrack;

@@ -8205,9 +8390,84 @@
SLJIT_ASSERT(!current->nextbacktracks && !current->topbacktracks);
}

+static SLJIT_INLINE void compile_control_verb_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+pcre_uchar opcode = *current->cc;
+
+SLJIT_ASSERT(common->control_head_ptr != 0);
+
+if ((opcode == OP_THEN || opcode == OP_THEN_ARG) && common->then_trap != NULL)
+  {
+  OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr);
+  OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, STACK_TOP, 0);
+  sljit_emit_ijump(compiler, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_search_then_trap));
+  OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0);
+
+  if (common->quit_label == NULL)
+    add_jump(compiler, &common->quit, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 0));
+  else
+    CMPTO(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 0, common->quit_label);
+
+  OP1(SLJIT_MOV, STACK_TOP, 0, TMP1, 0);
+  add_jump(compiler, &common->then_trap->quit, JUMP(SLJIT_JUMP));
+  return;
+  }
+
+if (!common->local_exit)
+  {
+  OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr);
+  OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, STACK_TOP, 0);
+  sljit_emit_ijump(compiler, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_check_control_chain));
+  OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0);
+
+  OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0);
+  add_jump(compiler, &common->reset_match, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, -1));
+
+  OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_NOMATCH);
+  }
+
+/* Commit or in recurse or accept. */
+if (common->quit_label == NULL)
+  add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP));
+else
+  JUMPTO(SLJIT_JUMP, common->quit_label);
+}
+
+static SLJIT_INLINE void compile_then_trap_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+int size;
+
+if (CURRENT_AS(then_trap_backtrack)->then_trap)
+  {
+  common->then_trap = CURRENT_AS(then_trap_backtrack)->then_trap;
+  return;
+  }
+
+size = CURRENT_AS(then_trap_backtrack)->framesize;
+size = 2 + (size < 0 ? 0 : size);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(size - 2));
+free_stack(common, size);
+jump = JUMP(SLJIT_JUMP);
+
+set_jumps(CURRENT_AS(then_trap_backtrack)->quit, LABEL());
+/* STACK_TOP is set by THEN. */
+if (CURRENT_AS(then_trap_backtrack)->framesize >= 0)
+  add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+free_stack(common, 2);
+
+JUMPHERE(jump);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, TMP1, 0);
+}
+
 static void compile_backtrackingpath(compiler_common *common, struct backtrack_common *current)
 {
 DEFINE_COMPILER;
+then_trap_backtrack *save_then_trap = common->then_trap;


 while (current)
   {
@@ -8350,28 +8610,12 @@
       OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, TMP2, 0);
     break;


+    case OP_THEN:
+    case OP_THEN_ARG:
     case OP_PRUNE:
     case OP_PRUNE_ARG:
     case OP_SKIP:
-    if (!common->local_exit)
-      {
-      SLJIT_ASSERT(common->control_head_ptr != 0);
-      OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr);
-      OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, STACK_TOP, 0);
-      sljit_emit_ijump(compiler, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_check_control_chain));
-      OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0);
-
-      OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0);
-      add_jump(compiler, &common->reset_match, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, -1));
-
-      OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_NOMATCH);
-      }
-
-    /* Commit or in recurse or accept. */
-    if (common->quit_label == NULL)
-      add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP));
-    else
-      JUMPTO(SLJIT_JUMP, common->quit_label);
+    compile_control_verb_backtrackingpath(common, current);
     break;


     case OP_SKIP_ARG:
@@ -8423,12 +8667,18 @@
     set_jumps(current->topbacktracks, LABEL());
     break;


+    case OP_TABLE_LENGTH:
+    /* A virtual opcode for then traps. */
+    compile_then_trap_backtrackingpath(common, current);
+    break;
+
     default:
     SLJIT_ASSERT_STOP();
     break;
     }
   current = current->prev;
   }
+common->then_trap = save_then_trap;
 }


static SLJIT_INLINE void compile_recurse(compiler_common *common)
@@ -8438,13 +8688,16 @@
pcre_uchar *ccbegin = cc + 1 + LINK_SIZE + (*cc == OP_BRA ? 0 : IMM2_SIZE);
pcre_uchar *ccend = bracketend(cc);
BOOL needs_control_head;
-int framesize = get_framesize(common, cc, TRUE, &needs_control_head);
+int framesize = get_framesize(common, cc, NULL, TRUE, &needs_control_head);
int private_data_size = get_private_data_copy_length(common, ccbegin, ccend, needs_control_head);
int alternativesize;
BOOL needs_frame;
backtrack_common altbacktrack;
struct sljit_jump *jump;

+/* Recurse captures then. */
+common->then_trap = NULL;
+
SLJIT_ASSERT(*cc == OP_BRA || *cc == OP_CBRA || *cc == OP_CBRAPOS || *cc == OP_SCBRA || *cc == OP_SCBRAPOS);
needs_frame = framesize >= 0;
if (!needs_frame)
@@ -8463,7 +8716,7 @@
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->control_head_ptr, SLJIT_IMM, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), common->recursive_head_ptr, STACK_TOP, 0);
if (needs_frame)
- init_frame(common, cc, framesize + alternativesize - 1, alternativesize, TRUE);
+ init_frame(common, cc, NULL, framesize + alternativesize - 1, alternativesize, TRUE);

if (alternativesize > 0)
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
@@ -8727,6 +8980,7 @@
SLJIT_FREE(common->optimized_cbracket);
return;
}
+
common->private_data_ptrs = (int *)SLJIT_MALLOC((ccend - rootbacktrack.cc) * sizeof(int));
if (!common->private_data_ptrs)
{
@@ -8736,11 +8990,26 @@
memset(common->private_data_ptrs, 0, (ccend - rootbacktrack.cc) * sizeof(int));
set_private_data_ptrs(common, common->cbra_ptr + (re->top_bracket + 1) * sizeof(sljit_sw), ccend);

+if (common->has_then)
+  {
+  common->then_offsets = (pcre_uint8 *)SLJIT_MALLOC(ccend - rootbacktrack.cc);
+  if (!common->then_offsets)
+    {
+    SLJIT_FREE(common->optimized_cbracket);
+    SLJIT_FREE(common->private_data_ptrs);
+    return;
+    }
+  memset(common->then_offsets, 0, ccend - rootbacktrack.cc);
+  set_then_offsets(common, rootbacktrack.cc, NULL);
+  }
+
 compiler = sljit_create_compiler();
 if (!compiler)
   {
   SLJIT_FREE(common->optimized_cbracket);
   SLJIT_FREE(common->private_data_ptrs);
+  if (common->has_then)
+    SLJIT_FREE(common->then_offsets);
   return;
   }
 common->compiler = compiler;
@@ -8832,6 +9101,8 @@
   sljit_free_compiler(compiler);
   SLJIT_FREE(common->optimized_cbracket);
   SLJIT_FREE(common->private_data_ptrs);
+  if (common->has_then)
+    SLJIT_FREE(common->then_offsets);
   return;
   }


@@ -8867,6 +9138,8 @@
   sljit_free_compiler(compiler);
   SLJIT_FREE(common->optimized_cbracket);
   SLJIT_FREE(common->private_data_ptrs);
+  if (common->has_then)
+    SLJIT_FREE(common->then_offsets);
   return;
   }


@@ -8934,6 +9207,8 @@
     sljit_free_compiler(compiler);
     SLJIT_FREE(common->optimized_cbracket);
     SLJIT_FREE(common->private_data_ptrs);
+    if (common->has_then)
+      SLJIT_FREE(common->then_offsets);
     return;
     }
   flush_stubs(common);
@@ -9042,6 +9317,9 @@


SLJIT_FREE(common->optimized_cbracket);
SLJIT_FREE(common->private_data_ptrs);
+if (common->has_then)
+ SLJIT_FREE(common->then_offsets);
+
executable_func = sljit_generate_code(compiler);
executable_size = sljit_get_generated_code_size(compiler);
sljit_free_compiler(compiler);