emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master e61a0398433 2/5: regex.c: Consolidate the two analysis functions


From: Stefan Monnier
Subject: master e61a0398433 2/5: regex.c: Consolidate the two analysis functions
Date: Sat, 30 Sep 2023 09:42:19 -0400 (EDT)

branch: master
commit e61a03984335b4ffb164280b2df80668b2a92c23
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>

    regex.c: Consolidate the two analysis functions
    
    We currently have two functions that analyze the bytecode
    to try and apply optimizations: `analyze_first` and `mutually_exclusive_p`.
    Extract the common code between them into a new function `forall_firstchar`,
    and then rewrite the old ones on top of that one.
    
    Along the way, we get slightly better analyses that reverts
    the recent de-optimizations but without re-introducing the
    corresponding bugs.
    
    * src/regex-emacs.c (forall_firstchar_1, forall_firstchar): New functions.
    (analyze_first_old): Rename from `analyze_first`.
    (struct anafirst_data): New struct.
    (analyze_first_fastmap, analyze_first_null): New functions.
    (analyze_first): Rewrite to use `forall_firstchar` with those two functions.
    Take a `bufp` rather than a `multibyte` arg.
    (regex_compile, re_compile_fastmap): Adjust calls accordingly.
    (struct mutexcl_data): New struct.
    (mutually_exclusive_one): New function.
    (mutually_exclusive_p): Rewrite to use `forall_firstchar` with that 
function.
---
 src/regex-emacs.c | 591 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 580 insertions(+), 11 deletions(-)

diff --git a/src/regex-emacs.c b/src/regex-emacs.c
index 33bdad5c46b..dbd5bcb8953 100644
--- a/src/regex-emacs.c
+++ b/src/regex-emacs.c
@@ -1179,8 +1179,8 @@ static void insert_op2 (re_opcode_t op, unsigned char 
*loc,
 static bool at_begline_loc_p (re_char *pattern, re_char *p);
 static bool at_endline_loc_p (re_char *p, re_char *pend);
 static re_char *skip_one_char (re_char *p);
-static bool analyze_first (re_char *p, re_char *pend,
-                           char *fastmap, bool multibyte);
+static bool analyze_first (struct re_pattern_buffer *bufp,
+                           re_char *p, re_char *pend, char *fastmap);
 
 /* Fetch the next character in the uncompiled pattern, with no
    translation.  */
@@ -1930,7 +1930,7 @@ regex_compile (re_char *pattern, ptrdiff_t size,
                    ptrdiff_t startoffset = 0;
                    re_opcode_t ofj =
                      /* Check if the loop can match the empty string.  */
-                     (simple || !analyze_first (laststart, b, NULL, false))
+                     (simple || !analyze_first (bufp, laststart, b, NULL))
                      ? on_failure_jump : on_failure_jump_loop;
                    eassert (skip_one_char (laststart) <= b);
 
@@ -1987,7 +1987,7 @@ regex_compile (re_char *pattern, ptrdiff_t size,
                GET_BUFFER_SPACE (7); /* We might use less.  */
                if (many_times_ok)
                  {
-                   bool emptyp = analyze_first (laststart, b, NULL, false);
+                   bool emptyp = analyze_first (bufp, laststart, b, NULL);
 
                    /* The non-greedy multiple match looks like
                       a repeat..until: we only need a conditional jump
@@ -2822,7 +2822,229 @@ group_in_compile_stack (compile_stack_type 
compile_stack, regnum_t regnum)
   return false;
 }
 
-/* analyze_first.
+/* Iterate through all the char-matching operations directly reachable from P.
+   This is the inner loop of `forall_firstchar`, which see.
+   LOOP_BEG..LOOP_END delimit the currentl "block" of code (we assume
+   the code is made of syntactically nested loops).
+   LOOP_END is blindly assumed to be "safe".
+   To guarantee termination, at each iteration, either LOOP_BEG should
+   get bigger, or it should stay the same and P should get bigger.  */
+static bool
+forall_firstchar_1 (re_char *p, re_char *pend,
+                    re_char *loop_beg, re_char *loop_end,
+                    bool f (const re_char *p, void *arg), void *arg)
+{
+  eassert (p >= loop_beg);
+  eassert (p <= loop_end);
+
+  while (true)
+    {
+      re_char *newp1, *newp2, *tmp;
+      re_char *p_orig = p;
+
+      if (p == pend)
+        return false;
+      else if (p == loop_end)
+        return true;
+      else if (p > loop_end)
+        {
+#if ENABLE_CHECKING
+          fprintf (stderr, "FORALL_FIRSTCHAR: Broken assumption1!!\n");
+#endif
+          return false; /* FIXME: Broken assumption about the code shape.  */
+        }
+      else
+        switch (*p)
+          {
+          /* Cases which stop the iteration.  */
+          case succeed:
+          case exactn:
+          case charset:
+          case charset_not:
+          case anychar:
+          case syntaxspec:
+          case notsyntaxspec:
+          case categoryspec:
+          case notcategoryspec:
+            return f (p, arg);
+
+          /* Cases which may match the empty string.  */
+          case at_dot:
+          case begbuf:
+          case no_op:
+          case wordbound:
+          case notwordbound:
+          case begline:
+            p++;
+            continue;
+
+          /* Cases which may match the empty string and may
+             tell us something about the next char.  */
+          case endline:
+          case endbuf:
+          case wordbeg:
+          case wordend:
+          case symbeg:
+          case symend:
+            if (f (p, arg))
+              return true;
+            p++;
+            continue;
+
+          case jump:
+          case jump_n:
+           newp1 = extract_address (p + 1);
+           if (newp1 > p)
+             { /* Forward jump, boring.  */
+               p = newp1;
+               continue;
+             }
+           switch (*newp1)
+             {
+             case on_failure_jump:
+             case on_failure_keep_string_jump:
+             case on_failure_jump_nastyloop:
+             case on_failure_jump_loop:
+             case on_failure_jump_smart:
+             case succeed_n:
+               newp2 = extract_address (newp1 + 1);
+               goto do_twoway_jump;
+             default:
+               newp2 = loop_end; /* "Safe" choice.  */
+               goto do_jump;
+             }
+
+         case on_failure_jump:
+         case on_failure_keep_string_jump:
+         case on_failure_jump_nastyloop:
+         case on_failure_jump_loop:
+         case on_failure_jump_smart:
+           newp1 = extract_address (p + 1);
+           newp2 = p + 3;
+           /* For `+` loops, we often have an `on_failure_jump` that skips
+               forward over a subsequent `jump`.  Recognize this pattern
+               since that subsequent `jump` is the one that jumps to the
+               loop-entry.  */
+           newp2 = ((re_opcode_t) *newp2 == jump)
+                   ? extract_address (newp2 + 1) : newp2;
+
+         do_twoway_jump:
+           /* We have to check that both destinations are safe.
+              Arrange for `newp1` to be the smaller of the two.  */
+           if (newp1 > newp2)
+             (tmp = newp1, newp1 = newp2, newp2 = tmp);
+
+           if (newp2 <= p_orig) /* Both destinations go backward!  */
+             {
+#if ENABLE_CHECKING
+               fprintf (stderr, "FORALL_FIRSTCHAR: Broken assumption2!!\n");
+#endif
+               return false;
+              }
+
+            if (!forall_firstchar_1 (newp2, pend, loop_beg, loop_end, f, arg))
+              return false;
+
+         do_jump:
+           eassert (newp2 <= loop_end);
+            if (newp1 <= p_orig)
+             {
+               if (newp1 < loop_beg)
+                 {
+#if ENABLE_CHECKING
+                   fprintf (stderr, "FORALL_FIRSTCHAR: Broken 
assumption3!!\n");
+#endif
+                   return false;
+                 }
+               else if (newp1 == loop_beg)
+                 /* If we jump backward to the entry point of the current loop
+                    it means it's a zero-length cycle through that loop;
+                    this cycle itself does not break safety.  */
+                 return true;
+               else
+                 /* We jump backward to a new loop, nested within the current
+                    one. `newp1` is the entry point and `newp2` the exit of
+                    that inner loop.  */
+                 /* `p` gets smaller, but termination is still ensured because
+                    `loop_beg` gets bigger. */
+                 (loop_beg = newp1, loop_end = newp2);
+             }
+           p = newp1;
+           continue;
+
+          case succeed_n:
+           newp1 = extract_address (p + 1);
+           newp2 = p + 5;      /* Skip the two bytes containing the count.  */
+           goto do_twoway_jump;
+
+          case set_number_at:
+            int offset = extract_number (p + 1);
+            DEBUG_STATEMENT (eassert (extract_number (p + 3)));
+            /* If we're setting the counter of an immediately following
+               `succeed_n`, then this next execution of `succeed_n` will do
+               nothing but decrement its counter and "fall through".
+               So we do the fall through here to avoid considering the
+               "on failure" part of the `succeed_n` which should only be
+               considered when coming from the `jump(_n)` at the end of
+               the loop.  */
+            p += (offset == 5 && p[5] == succeed_n) ? 10 : 5;
+            continue;
+
+          case start_memory:
+          case stop_memory:
+            p += 2;
+            continue;
+
+          /* This could match the empty string, so we may need to continue,
+             but in most cases, this can match "anything", so we should
+             return `false` unless told otherwise.  */
+          case duplicate:
+            if (!f (p, arg))
+              return false;
+            p += 2;
+            continue;
+
+          default:
+            abort (); /* We have listed all the cases.  */
+          }
+      }
+}
+
+/* Iterate through all the char-matching operations directly reachable from P.
+   Return true if P is "safe", meaning that PEND cannot be reached directly
+   from P and all calls to F returned true.
+   Return false if PEND *may* be directly reachable from P or if one of
+   the calls to F returned false.
+   PEND can be NULL (and hence never reachable).
+
+   Call `F (POS, ARG)` for every POS directly reachable from P,
+   before reaching PEND, where POS is the position of a char-matching
+   operation (`exactn`, `charset`, ...).
+
+   For operations that match the empty string (`wordbeg`, ...), if F
+   returns true we stop going down that path immediately but if it returns
+   false we don't consider it as a failure and we simply look for the
+   next char-matching operations on that path.
+   For `duplicate`, it is the reverse: a false is an immediate failure
+   whereas a true just lets the analysis continue with the rest of the path.
+
+   This function can be used while building the bytecode (in which case
+   you should pass NULL for bufp), but if so, P and PEND need to delimit
+   a valid block such that there is not jump to a location outside
+   of [P...PEND].  */
+static bool
+forall_firstchar (struct re_pattern_buffer *bufp, re_char *p, re_char *pend,
+                  bool f (re_char *p, void *arg), void *arg)
+{
+  eassert (!bufp || bufp->used);
+  eassert (pend || bufp->used);
+  return forall_firstchar_1 (p, pend,
+                             bufp ? bufp->buffer - 1 : p,
+                             bufp ? bufp->buffer + bufp->used + 1 : pend,
+                             f, arg);
+}
+
+/* analyze_first_old.
    If fastmap is non-NULL, go through the pattern and fill fastmap
    with all the possible leading chars.  If fastmap is NULL, don't
    bother filling it up (obviously) and only return whether the
@@ -2833,7 +3055,7 @@ group_in_compile_stack (compile_stack_type compile_stack, 
regnum_t regnum)
                 or if fastmap was not updated accurately.  */
 
 static bool
-analyze_first (re_char *p, re_char *pend, char *fastmap, bool multibyte)
+analyze_first_old (re_char *p, re_char *pend, char *fastmap, bool multibyte)
 {
   int j, k;
   int nbits;
@@ -3079,7 +3301,7 @@ analyze_first (re_char *p, re_char *pend, char *fastmap, 
bool multibyte)
            { /* We have to look down both arms.
                 We first go down the "straight" path so as to minimize
                 stack usage when going through alternatives.  */
-             bool r = analyze_first (p, pend, fastmap, multibyte);
+             bool r = analyze_first_old (p, pend, fastmap, multibyte);
              if (r) return r;
              p += j;
            }
@@ -3133,7 +3355,263 @@ analyze_first (re_char *p, re_char *pend, char 
*fastmap, bool multibyte)
   /* We reached the end without matching anything.  */
   return true;
 
-} /* analyze_first */
+} /* analyze_first_old */
+
+struct anafirst_data {
+  bool multibyte;
+  char *fastmap;
+  bool match_any_multibyte_characters;
+};
+
+static bool
+analyze_first_fastmap (const re_char *p, void *arg)
+{
+  struct anafirst_data *data = arg;
+
+  int j, k;
+  int nbits;
+  bool not;
+
+  switch (*p)
+    {
+    case succeed:
+      return false;
+
+    case duplicate:
+      /* If the first character has to match a backreference, that means
+         that the group was empty (since it already matched).  Since this
+         is the only case that interests us here, we can assume that the
+         backreference must match the empty string and we need to
+         build the fastmap from the rest of the path.  */
+      return true;
+
+    /* Following are the cases which match a character.  These end
+       with 'break'.  */
+
+    case exactn:
+      p++;
+      /* If multibyte is nonzero, the first byte of each
+        character is an ASCII or a leading code.  Otherwise,
+        each byte is a character.  Thus, this works in both
+        cases. */
+      data->fastmap[p[1]] = 1;
+      if (data->multibyte)
+       {
+         /* Cover the case of matching a raw char in a
+            multibyte regexp against unibyte.  */
+         if (CHAR_BYTE8_HEAD_P (p[1]))
+           data->fastmap[CHAR_TO_BYTE8 (STRING_CHAR (p + 1))] = 1;
+       }
+      else
+       {
+         /* For the case of matching this unibyte regex
+            against multibyte, we must set a leading code of
+            the corresponding multibyte character.  */
+         int c = RE_CHAR_TO_MULTIBYTE (p[1]);
+
+         data->fastmap[CHAR_LEADING_CODE (c)] = 1;
+       }
+      return true;
+
+    case anychar:
+      /* We could put all the chars except for \n (and maybe \0)
+        but we don't bother since it is generally not worth it.  */
+      return false;
+
+    case charset_not:
+      {
+       /* Chars beyond end of bitmap are possible matches.  */
+       for (j = CHARSET_BITMAP_SIZE (p) * BYTEWIDTH;
+             j < (1 << BYTEWIDTH); j++)
+         data->fastmap[j] = 1;
+      }
+      FALLTHROUGH;
+    case charset:
+      not = (re_opcode_t) *(p) == charset_not;
+      nbits = CHARSET_BITMAP_SIZE (p) * BYTEWIDTH;
+      p += 2;
+      for (j = 0; j < nbits; j++)
+       if (!!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) ^ not)
+         data->fastmap[j] = 1;
+
+      /* To match raw bytes (in the 80..ff range) against multibyte
+        strings, add their leading bytes to the fastmap.  */
+      for (j = 0x80; j < nbits; j++)
+       if (!!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) ^ not)
+         data->fastmap[CHAR_LEADING_CODE (BYTE8_TO_CHAR (j))] = 1;
+
+      if (/* Any leading code can possibly start a character
+            which doesn't match the specified set of characters.  */
+         not
+         ||
+         /* If we can match a character class, we can match any
+            multibyte characters.  */
+         (CHARSET_RANGE_TABLE_EXISTS_P (&p[-2])
+          && CHARSET_RANGE_TABLE_BITS (&p[-2]) != 0))
+
+       {
+         if (!data->match_any_multibyte_characters)
+           {
+             for (j = MIN_MULTIBYTE_LEADING_CODE;
+                   j <= MAX_MULTIBYTE_LEADING_CODE; j++)
+               data->fastmap[j] = 1;
+             data->match_any_multibyte_characters = true;
+           }
+       }
+
+      else if (!not && CHARSET_RANGE_TABLE_EXISTS_P (&p[-2])
+              && data->match_any_multibyte_characters == false)
+       {
+         /* Set fastmap[I] to 1 where I is a leading code of each
+            multibyte character in the range table. */
+         int c, count;
+         unsigned char lc1, lc2;
+
+         /* Make P points the range table.  '+ 2' is to skip flag
+            bits for a character class.  */
+         p += CHARSET_BITMAP_SIZE (&p[-2]) + 2;
+
+         /* Extract the number of ranges in range table into COUNT.  */
+         EXTRACT_NUMBER_AND_INCR (count, p);
+         for (; count > 0; count--, p += 3)
+           {
+             /* Extract the start and end of each range.  */
+             EXTRACT_CHARACTER (c, p);
+             lc1 = CHAR_LEADING_CODE (c);
+             p += 3;
+             EXTRACT_CHARACTER (c, p);
+             lc2 = CHAR_LEADING_CODE (c);
+             for (j = lc1; j <= lc2; j++)
+               data->fastmap[j] = 1;
+           }
+       }
+      return true;
+
+    case syntaxspec:
+    case notsyntaxspec:
+      /* This match depends on text properties.  These end with
+        aborting optimizations.  */
+      return false;
+
+    case categoryspec:
+    case notcategoryspec:
+      not = (re_opcode_t)p[0] == notcategoryspec;
+      p++;
+      k = *p++;
+      for (j = (1 << BYTEWIDTH); j >= 0; j--)
+       if ((CHAR_HAS_CATEGORY (j, k)) ^ not)
+         data->fastmap[j] = 1;
+
+      /* Any leading code can possibly start a character which
+        has or doesn't has the_malloc_fn specified category.  */
+      if (!data->match_any_multibyte_characters)
+       {
+         for (j = MIN_MULTIBYTE_LEADING_CODE;
+               j <= MAX_MULTIBYTE_LEADING_CODE; j++)
+           data->fastmap[j] = 1;
+         data->match_any_multibyte_characters = true;
+       }
+      return true;
+
+    case endline:
+    case endbuf:
+    case wordbeg:
+    case wordend:
+    case symbeg:
+    case symend:
+      /* This false doesn't mean failure but rather "not succeeded yet".  */
+      return false;
+
+    default:
+#if ENABLE_CHECKING
+      abort (); /* We have listed all the cases.  */
+#endif
+      return false;
+    }
+}
+
+static bool
+analyze_first_null (const re_char *p, void *arg)
+{
+  switch (*p)
+    {
+    case succeed:
+      /* This is safe: we can't reach `pend` at all from here.  */
+      return true;
+
+    case duplicate:
+      /* Either `duplicate` ends up matching a non-empty string, in which
+         case we're good, or it matches the empty string, in which case we
+         need to continue checking the rest of this path, which is exactly
+         what returning `true` does, here.  */
+      return true;
+
+    case exactn:
+    case anychar:
+    case charset_not:
+    case charset:
+    case syntaxspec:
+    case notsyntaxspec:
+    case categoryspec:
+    case notcategoryspec:
+      return true;
+
+    case endline:
+    case endbuf:
+    case wordbeg:
+    case wordend:
+    case symbeg:
+    case symend:
+      /* This false doesn't mean failure but rather "not succeeded yet".  */
+      return false;
+
+    default:
+#if ENABLE_CHECKING
+      abort (); /* We have listed all the cases.  */
+#endif
+      return false;
+    }
+}
+
+/* analyze_first.
+   If fastmap is non-NULL, go through the pattern and fill fastmap
+   with all the possible leading chars.  If fastmap is NULL, don't
+   bother filling it up (obviously) and only return whether the
+   pattern could potentially match the empty string.
+
+   Return false if p matches at least one char before reaching pend.
+   Return true  if p..pend might match the empty string
+                or if fastmap was not updated accurately.  */
+
+static bool
+analyze_first (struct re_pattern_buffer *bufp,
+               re_char *p, re_char *pend, char *fastmap)
+{
+  eassert (pend);
+  struct anafirst_data data = { bufp ? bufp->multibyte : false,
+                                fastmap, false };
+  bool safe = forall_firstchar (bufp->used ? bufp : NULL, p, pend,
+                                fastmap ? analyze_first_fastmap
+                                : analyze_first_null,
+                                &data);
+#if ENABLE_CHECKING
+  bool old = !!analyze_first_old (p, pend, fastmap, data.multibyte);
+  if (old && safe)
+    {
+      fprintf (stderr, "ANALYZE_FIRST: New optimization (fastmap=%d)!\n",
+               fastmap ? 1 : 0);
+      print_partial_compiled_pattern (stderr, p, pend);
+    }
+  else if (!old && !safe)
+    {
+      fprintf (stderr, "ANALYZE_FIRST: Lost an optimization (fastmap=%d)!\n",
+               fastmap ? 1 : 0);
+      print_partial_compiled_pattern (stderr, p, pend);
+    }
+#endif
+  return !safe;
+}
+
 
 /* Compute a fastmap for the compiled pattern in BUFP.
    A fastmap records which of the (1 << BYTEWIDTH) possible
@@ -3162,8 +3640,8 @@ re_compile_fastmap (struct re_pattern_buffer *bufp)
   /* FIXME: Is the following assignment correct even when ANALYSIS < 0?  */
   bufp->fastmap_accurate = 1;      /* It will be when we're done.  */
 
-  bufp->can_be_null = analyze_first (bufp->buffer, bufp->buffer + bufp->used,
-                                    fastmap, RE_MULTIBYTE_P (bufp));
+  bufp->can_be_null = analyze_first (bufp, bufp->buffer,
+                                    bufp->buffer + bufp->used, fastmap);
 } /* re_compile_fastmap */
 
 /* Set REGS to hold NUM_REGS registers, storing them in STARTS and
@@ -3962,11 +4440,102 @@ mutually_exclusive_aux (struct re_pattern_buffer 
*bufp, re_char *p1,
   return false;
 }
 
+struct mutexcl_data {
+  struct re_pattern_buffer *bufp;
+  re_char *p1;
+};
+
+static bool
+mutually_exclusive_one (re_char *p2, void *arg)
+{
+  struct mutexcl_data *data = arg;
+  switch (*p2)
+    {
+    case endline:
+    case exactn:
+      return mutually_exclusive_exactn (data->bufp, data->p1, p2);
+    case charset:
+      {
+       if (*data->p1 == exactn)
+         return mutually_exclusive_exactn (data->bufp, p2, data->p1);
+       else
+         return mutually_exclusive_charset (data->bufp, data->p1, p2);
+      }
+
+    case charset_not:
+      switch (*data->p1)
+       {
+       case exactn:
+         return mutually_exclusive_exactn (data->bufp, p2, data->p1);
+       case charset:
+         return mutually_exclusive_charset (data->bufp, p2, data->p1);
+       case charset_not:
+         /* When we have two charset_not, it's very unlikely that
+            they don't overlap.  The union of the two sets of excluded
+            chars should cover all possible chars, which, as a matter of
+            fact, is virtually impossible in multibyte buffers.  */
+         return false;
+       }
+      return false;
+    case anychar:
+      return false;             /* FIXME: exactn \n ? */
+    case syntaxspec:
+      return (*data->p1 == notsyntaxspec && data->p1[1] == p2[1]);
+    case notsyntaxspec:
+      return (*data->p1 == syntaxspec && data->p1[1] == p2[1]);
+    case categoryspec:
+      return (*data->p1 == notcategoryspec && data->p1[1] == p2[1]);
+    case notcategoryspec:
+      return (*data->p1 == categoryspec && data->p1[1] == p2[1]);
+
+    case endbuf:
+    case succeed:
+      return true;
+    case wordbeg:
+      return (*data->p1 == notsyntaxspec && data->p1[1] == Sword);
+    case wordend:
+      return (*data->p1 == syntaxspec && data->p1[1] == Sword);
+    case symbeg:
+      return (*data->p1 == notsyntaxspec
+              && (data->p1[1] == Ssymbol || data->p1[1] == Sword));
+    case symend:
+      return (*data->p1 == syntaxspec
+              && (data->p1[1] == Ssymbol || data->p1[1] == Sword));
+
+    case duplicate:
+      /* At this point, we know nothing about what this can match, sadly.  */
+      return false;
+
+    default:
+#if ENABLE_CHECKING
+      abort (); /* We have listed all the cases.  */
+#endif
+      return false;
+    }
+}
+
 static bool
 mutually_exclusive_p (struct re_pattern_buffer *bufp, re_char *p1,
                      re_char *p2)
 {
-  return mutually_exclusive_aux (bufp, p1, p2, bufp->buffer - 1, NULL);
+  struct mutexcl_data data = { bufp, p1 };
+  bool new = forall_firstchar (bufp, p2, NULL, mutually_exclusive_one, &data);
+#if ENABLE_CHECKING
+  bool old = mutually_exclusive_aux (bufp, p1, p2, bufp->buffer - 1, NULL);
+  if (old && !new)
+    {
+      fprintf (stderr, "MUTUALLY_EXCLUSIVE_P: Lost an optimization between %d 
and %d!\n",
+               (int)(p1 - bufp->buffer), (int)(p2 - bufp->buffer));
+      print_partial_compiled_pattern (stderr, bufp->buffer, bufp->buffer + 
bufp->used);
+    }
+  else if (!old && new)
+    {
+      fprintf (stderr, "MUTUALLY_EXCLUSIVE_P: New optimization between %d and 
%d!\n",
+               (int)(p1 - bufp->buffer), (int)(p2 - bufp->buffer));
+      print_partial_compiled_pattern (stderr, bufp->buffer, bufp->buffer + 
bufp->used);
+    }
+#endif
+  return new;
 }
 
 /* Matching routines.  */



reply via email to

[Prev in Thread] Current Thread [Next in Thread]