sourcepawn/compiler/sc7.c
author Asher Baker <asherkin@gmail.com>
Wed Aug 22 23:23:31 2012 +0100 (2012-08-22)
changeset 3623 3fd87cb487f8
parent 3069 b2656266b6d9
child 3951 c3f26df2183f
permissions -rw-r--r--
Updated cstrike gamedata.
     1 /*  Pawn compiler - Staging buffer and optimizer
     2  *
     3  *  The staging buffer
     4  *  ------------------
     5  *  The staging buffer allows buffered output of generated code, deletion
     6  *  of redundant code, optimization by a tinkering process and reversing
     7  *  the ouput of evaluated expressions (which is used for the reversed
     8  *  evaluation of arguments in functions).
     9  *  Initially, stgwrite() writes to the file directly, but after a call to
    10  *  stgset(TRUE), output is redirected to the buffer. After a call to
    11  *  stgset(FALSE), stgwrite()'s output is directed to the file again. Thus
    12  *  only one routine is used for writing to the output, which can be
    13  *  buffered output or direct output.
    14  *
    15  *  staging buffer variables:   stgbuf  - the buffer
    16  *                              stgidx  - current index in the staging buffer
    17  *                              staging - if true, write to the staging buffer;
    18  *                                        if false, write to file directly.
    19  *
    20  * The peephole optimizer uses a dual "pipeline". The staging buffer (described
    21  * above) gets optimized for each expression or sub-expression in a function
    22  * call. The peephole optimizer is recursive, but it does not span multiple
    23  * sub-expressions. However, the data gets written to a second buffer that
    24  * behaves much like the staging buffer. This second buffer gathers all
    25  * optimized strings from the staging buffer for a complete expression. The
    26  * peephole optmizer then runs over this second buffer to find optimzations
    27  * across function parameter boundaries.
    28  *
    29  *
    30  *  Copyright (c) ITB CompuPhase, 1997-2006
    31  *
    32  *  This software is provided "as-is", without any express or implied warranty.
    33  *  In no event will the authors be held liable for any damages arising from
    34  *  the use of this software.
    35  *
    36  *  Permission is granted to anyone to use this software for any purpose,
    37  *  including commercial applications, and to alter it and redistribute it
    38  *  freely, subject to the following restrictions:
    39  *
    40  *  1.  The origin of this software must not be misrepresented; you must not
    41  *      claim that you wrote the original software. If you use this software in
    42  *      a product, an acknowledgment in the product documentation would be
    43  *      appreciated but is not required.
    44  *  2.  Altered source versions must be plainly marked as such, and must not be
    45  *      misrepresented as being the original software.
    46  *  3.  This notice may not be removed or altered from any source distribution.
    47  *
    48  *  Version: $Id$
    49  */
    50 #include <assert.h>
    51 #include <stdio.h>
    52 #include <stdlib.h>     /* for atoi() */
    53 #include <string.h>
    54 #include <ctype.h>
    55 #if defined FORTIFY
    56   #include <alloc/fortify.h>
    57 #endif
    58 #include "sc.h"
    59 
    60 #if defined _MSC_VER
    61   #pragma warning(push)
    62   #pragma warning(disable:4125)  /* decimal digit terminates octal escape sequence */
    63 #endif
    64 
    65 #include "sc7.scp"
    66 
    67 #if defined _MSC_VER
    68   #pragma warning(pop)
    69 #endif
    70 
    71 static int stgstring(char *start,char *end);
    72 static void stgopt(char *start,char *end,int (*outputfunc)(char *str));
    73 
    74 
    75 #define sSTG_GROW   512
    76 #define sSTG_MAX    20480
    77 
    78 static char *stgbuf=NULL;
    79 static int stgmax=0;    /* current size of the staging buffer */
    80 
    81 static char *stgpipe=NULL;
    82 static int pipemax=0;   /* current size of the stage pipe, a second staging buffer */
    83 static int pipeidx=0;
    84 
    85 #define CHECK_STGBUFFER(index) if ((int)(index)>=stgmax)  grow_stgbuffer(&stgbuf, &stgmax, (index)+1)
    86 #define CHECK_STGPIPE(index)   if ((int)(index)>=pipemax) grow_stgbuffer(&stgpipe, &pipemax, (index)+1)
    87 
    88 static void grow_stgbuffer(char **buffer, int *curmax, int requiredsize)
    89 {
    90   char *p;
    91   int clear= (*buffer==NULL); /* if previously none, empty buffer explicitly */
    92 
    93   assert(*curmax<requiredsize);
    94   /* if the staging buffer (holding intermediate code for one line) grows
    95    * over a few kBytes, there is probably a run-away expression
    96    */
    97   if (requiredsize>sSTG_MAX)
    98     error(102,"staging buffer");    /* staging buffer overflow (fatal error) */
    99   *curmax=requiredsize+sSTG_GROW;
   100   if (*buffer!=NULL)
   101     p=(char *)realloc(*buffer,*curmax*sizeof(char));
   102   else
   103     p=(char *)malloc(*curmax*sizeof(char));
   104   if (p==NULL)
   105     error(102,"staging buffer");    /* staging buffer overflow (fatal error) */
   106   *buffer=p;
   107   if (clear)
   108     **buffer='\0';
   109 }
   110 
   111 SC_FUNC void stgbuffer_cleanup(void)
   112 {
   113   if (stgbuf!=NULL) {
   114     free(stgbuf);
   115     stgbuf=NULL;
   116     stgmax=0;
   117   } /* if */
   118   if (stgpipe!=NULL) {
   119     free(stgpipe);
   120     stgpipe=NULL;
   121     pipemax=0;
   122     pipeidx=0;
   123   } /* if */
   124 }
   125 
   126 /* the variables "stgidx" and "staging" are declared in "scvars.c" */
   127 
   128 /*  stgmark
   129  *
   130  *  Copies a mark into the staging buffer. At this moment there are three
   131  *  possible marks:
   132  *     sSTARTREORDER    identifies the beginning of a series of expression
   133  *                      strings that must be written to the output file in
   134  *                      reordered order
   135  *    sENDREORDER       identifies the end of 'reverse evaluation'
   136  *    sEXPRSTART + idx  only valid within a block that is evaluated in
   137  *                      reordered order, it identifies the start of an
   138  *                      expression; the "idx" value is the argument position
   139  *
   140  *  Global references: stgidx  (altered)
   141  *                     stgbuf  (altered)
   142  *                     staging (referred to only)
   143  */
   144 SC_FUNC void stgmark(char mark)
   145 {
   146   if (staging) {
   147     CHECK_STGBUFFER(stgidx);
   148     stgbuf[stgidx++]=mark;
   149   } /* if */
   150 }
   151 
   152 static int rebuffer(char *str)
   153 {
   154   if (sc_status==statWRITE) {
   155     if (pipeidx>=2 && stgpipe[pipeidx-1]=='\0' && stgpipe[pipeidx-2]!='\n')
   156       pipeidx-=1;                      /* overwrite last '\0' */
   157     while (*str!='\0') {               /* copy to staging buffer */
   158       CHECK_STGPIPE(pipeidx);
   159       stgpipe[pipeidx++]=*str++;
   160     } /* while */
   161     CHECK_STGPIPE(pipeidx);
   162     stgpipe[pipeidx++]='\0';
   163   } /* if */
   164   return TRUE;
   165 }
   166 
   167 static int filewrite(char *str)
   168 {
   169   if (sc_status==statWRITE)
   170     return pc_writeasm(outf,str);
   171   return TRUE;
   172 }
   173 
   174 /*  stgwrite
   175  *
   176  *  Writes the string "st" to the staging buffer or to the output file. In the
   177  *  case of writing to the staging buffer, the terminating byte of zero is
   178  *  copied too, but... the optimizer can only work on complete lines (not on
   179  *  fractions of it. Therefore if the string is staged, if the last character
   180  *  written to the buffer is a '\0' and the previous-to-last is not a '\n',
   181  *  the string is concatenated to the last string in the buffer (the '\0' is
   182  *  overwritten). This also means an '\n' used in the middle of a string isn't
   183  *  recognized and could give wrong results with the optimizer.
   184  *  Even when writing to the output file directly, all strings are buffered
   185  *  until a whole line is complete.
   186  *
   187  *  Global references: stgidx  (altered)
   188  *                     stgbuf  (altered)
   189  *                     staging (referred to only)
   190  */
   191 SC_FUNC void stgwrite(const char *st)
   192 {
   193   int len;
   194 
   195   if (staging) {
   196     assert(stgidx==0 || stgbuf!=NULL);  /* staging buffer must be valid if there is (apparently) something in it */
   197     if (stgidx>=2 && stgbuf[stgidx-1]=='\0' && stgbuf[stgidx-2]!='\n')
   198       stgidx-=1;                       /* overwrite last '\0' */
   199     while (*st!='\0') {                /* copy to staging buffer */
   200       CHECK_STGBUFFER(stgidx);
   201       stgbuf[stgidx++]=*st++;
   202     } /* while */
   203     CHECK_STGBUFFER(stgidx);
   204     stgbuf[stgidx++]='\0';
   205   } else {
   206     len=(stgbuf!=NULL) ? strlen(stgbuf) : 0;
   207     CHECK_STGBUFFER(len+strlen(st)+1);
   208     strcat(stgbuf,st);
   209     len=strlen(stgbuf);
   210     if (len>0 && stgbuf[len-1]=='\n') {
   211       filewrite(stgbuf);
   212       stgbuf[0]='\0';
   213     } /* if */
   214   } /* if */
   215 }
   216 
   217 /*  stgout
   218  *
   219  *  Writes the staging buffer to the output file via stgstring() (for
   220  *  reversing expressions in the buffer) and stgopt() (for optimizing). It
   221  *  resets "stgidx".
   222  *
   223  *  Global references: stgidx  (altered)
   224  *                     stgbuf  (referred to only)
   225  *                     staging (referred to only)
   226  */
   227 SC_FUNC void stgout(int index)
   228 {
   229   int reordered=0;
   230   int idx;
   231 
   232   if (!staging)
   233     return;
   234   assert(pipeidx==0);
   235 
   236   /* first pass: sub-expressions */
   237   if (sc_status==statWRITE)
   238     reordered=stgstring(&stgbuf[index],&stgbuf[stgidx]);
   239   stgidx=index;
   240 
   241   /* second pass: optimize the buffer created in the first pass */
   242   if (sc_status==statWRITE) {
   243     if (reordered) {
   244       stgopt(stgpipe,stgpipe+pipeidx,filewrite);
   245     } else {
   246       /* there is no sense in re-optimizing if the order of the sub-expressions
   247        * did not change; so output directly
   248        */
   249       for (idx=0; idx<pipeidx; idx+=strlen(stgpipe+idx)+1)
   250         filewrite(stgpipe+idx);
   251     } /* if */
   252   } /* if */
   253   pipeidx=0;  /* reset second pipe */
   254 }
   255 
   256 typedef struct {
   257   char *start,*end;
   258 } argstack;
   259 
   260 /*  stgstring
   261  *
   262  *  Analyses whether code strings should be output to the file as they appear
   263  *  in the staging buffer or whether portions of it should be re-ordered.
   264  *  Re-ordering takes place in function argument lists; Pawn passes arguments
   265  *  to functions from right to left. When arguments are "named" rather than
   266  *  positional, the order in the source stream is indeterminate.
   267  *  This function calls itself recursively in case it needs to re-order code
   268  *  strings, and it uses a private stack (or list) to mark the start and the
   269  *  end of expressions in their correct (reversed) order.
   270  *  In any case, stgstring() sends a block as large as possible to the
   271  *  optimizer stgopt().
   272  *
   273  *  In "reorder" mode, each set of code strings must start with the token
   274  *  sEXPRSTART, even the first. If the token sSTARTREORDER is represented
   275  *  by '[', sENDREORDER by ']' and sEXPRSTART by '|' the following applies:
   276  *     '[]...'     valid, but useless; no output
   277  *     '[|...]     valid, but useless; only one string
   278  *     '[|...|...] valid and usefull
   279  *     '[...|...]  invalid, first string doesn't start with '|'
   280  *     '[|...|]    invalid
   281  */
   282 static int stgstring(char *start,char *end)
   283 {
   284   char *ptr;
   285   int nest,argc,arg;
   286   argstack *stack;
   287   int reordered=0;
   288 
   289   while (start<end) {
   290     if (*start==sSTARTREORDER) {
   291       start+=1;         /* skip token */
   292       /* allocate a argstack with sMAXARGS items */
   293       stack=(argstack *)malloc(sMAXARGS*sizeof(argstack));
   294       if (stack==NULL)
   295         error(103);     /* insufficient memory */
   296       reordered=1;      /* mark that the expression is reordered */
   297       nest=1;           /* nesting counter */
   298       argc=0;           /* argument counter */
   299       arg=-1;           /* argument index; no valid argument yet */
   300       do {
   301         switch (*start) {
   302         case sSTARTREORDER:
   303           nest++;
   304           start++;
   305           break;
   306         case sENDREORDER:
   307           nest--;
   308           start++;
   309           break;
   310         default:
   311           if ((*start & sEXPRSTART)==sEXPRSTART) {
   312             if (nest==1) {
   313               if (arg>=0)
   314                 stack[arg].end=start-1; /* finish previous argument */
   315               arg=(unsigned char)*start - sEXPRSTART;
   316               stack[arg].start=start+1;
   317               if (arg>=argc)
   318                 argc=arg+1;
   319             } /* if */
   320             start++;
   321           } else {
   322             start+=strlen(start)+1;
   323           } /* if */
   324         } /* switch */
   325       } while (nest); /* enddo */
   326       if (arg>=0)
   327         stack[arg].end=start-1;   /* finish previous argument */
   328       while (argc>0) {
   329         argc--;
   330         stgstring(stack[argc].start,stack[argc].end);
   331       } /* while */
   332       free(stack);
   333     } else {
   334       ptr=start;
   335       while (ptr<end && *ptr!=sSTARTREORDER)
   336         ptr+=strlen(ptr)+1;
   337       stgopt(start,ptr,rebuffer);
   338       start=ptr;
   339     } /* if */
   340   } /* while */
   341   return reordered;
   342 }
   343 
   344 /*  stgdel
   345  *
   346  *  Scraps code from the staging buffer by resetting "stgidx" to "index".
   347  *
   348  *  Global references: stgidx (altered)
   349  *                     staging (reffered to only)
   350  */
   351 SC_FUNC void stgdel(int index,cell code_index)
   352 {
   353   if (staging) {
   354     stgidx=index;
   355     code_idx=code_index;
   356   } /* if */
   357 }
   358 
   359 SC_FUNC int stgget(int *index,cell *code_index)
   360 {
   361   if (staging) {
   362     *index=stgidx;
   363     *code_index=code_idx;
   364   } /* if */
   365   return staging;
   366 }
   367 
   368 /*  stgset
   369  *
   370  *  Sets staging on or off. If it's turned off, the staging buffer must be
   371  *  initialized to an empty string. If it's turned on, the routine makes sure
   372  *  the index ("stgidx") is set to 0 (it should already be 0).
   373  *
   374  *  Global references: staging  (altered)
   375  *                     stgidx   (altered)
   376  *                     stgbuf   (contents altered)
   377  */
   378 SC_FUNC void stgset(int onoff)
   379 {
   380   staging=onoff;
   381   if (staging){
   382     assert(stgidx==0);
   383     stgidx=0;
   384     CHECK_STGBUFFER(stgidx);
   385     /* write any contents that may be put in the buffer by stgwrite()
   386      * when "staging" was 0
   387      */
   388     if (strlen(stgbuf)>0)
   389       filewrite(stgbuf);
   390   } /* if */
   391   stgbuf[0]='\0';
   392 }
   393 
   394 /* phopt_init
   395  * Initialize all sequence strings of the peehole optimizer. The strings
   396  * are embedded in the .EXE file in compressed format, here we expand
   397  * them (and allocate memory for the sequences).
   398  */
   399 static SEQUENCE *sequences;
   400 
   401 SC_FUNC int phopt_init(void)
   402 {
   403   int number, i, len;
   404   char str[160];
   405 
   406   /* count number of sequences */
   407   for (number=0; sequences_cmp[number].find!=NULL; number++)
   408     /* nothing */;
   409   number++;             /* include an item for the NULL terminator */
   410 
   411   if ((sequences=(SEQUENCE*)malloc(number * sizeof(SEQUENCE)))==NULL)
   412     return FALSE;
   413 
   414   /* pre-initialize all to NULL (in case of failure) */
   415   for (i=0; i<number; i++) {
   416     sequences[i].find=NULL;
   417     sequences[i].replace=NULL;
   418     sequences[i].savesize=0;
   419   } /* for */
   420 
   421   /* expand all strings */
   422   for (i=0; i<number-1; i++) {
   423     len = strexpand(str,(unsigned char*)sequences_cmp[i].find,sizeof str,SCPACK_TABLE);
   424     assert(len<=sizeof str);
   425     assert(len==(int)strlen(str)+1);
   426     sequences[i].find=(char*)malloc(len);
   427     if (sequences[i].find!=NULL)
   428       strcpy(sequences[i].find,str);
   429     len = strexpand(str,(unsigned char*)sequences_cmp[i].replace,sizeof str,SCPACK_TABLE);
   430     assert(len<=sizeof str);
   431     assert(len==(int)strlen(str)+1);
   432     sequences[i].replace=(char*)malloc(len);
   433     if (sequences[i].replace!=NULL)
   434       strcpy(sequences[i].replace,str);
   435     sequences[i].savesize=sequences_cmp[i].savesize;
   436     if (sequences[i].find==NULL || sequences[i].replace==NULL)
   437       return phopt_cleanup();
   438   } /* for */
   439 
   440   return TRUE;
   441 }
   442 
   443 SC_FUNC int phopt_cleanup(void)
   444 {
   445   int i;
   446   if (sequences!=NULL) {
   447     i=0;
   448     while (sequences[i].find!=NULL || sequences[i].replace!=NULL) {
   449       if (sequences[i].find!=NULL)
   450         free(sequences[i].find);
   451       if (sequences[i].replace!=NULL)
   452         free(sequences[i].replace);
   453       i++;
   454     } /* while */
   455     free(sequences);
   456     sequences=NULL;
   457   } /* if */
   458   return FALSE;
   459 }
   460 
   461 #define MAX_OPT_VARS    5
   462 #define MAX_OPT_CAT     5       /* max. values that are concatenated */
   463 #if sNAMEMAX > (PAWN_CELL_SIZE/4) * MAX_OPT_CAT
   464   #define MAX_ALIAS       sNAMEMAX
   465 #else
   466   #define MAX_ALIAS       (PAWN_CELL_SIZE/4) * MAX_OPT_CAT
   467 #endif
   468 
   469 static int matchsequence(char *start,char *end,char *pattern,
   470                          char symbols[MAX_OPT_VARS][MAX_ALIAS+1],
   471                          int *match_length)
   472 {
   473   int var,i;
   474   char str[MAX_ALIAS+1];
   475   char *start_org=start;
   476   cell value;
   477   char *ptr;
   478 
   479   *match_length=0;
   480   for (var=0; var<MAX_OPT_VARS; var++)
   481     symbols[var][0]='\0';
   482 
   483   while (*start=='\t' || *start==' ')
   484     start++;
   485   while (*pattern) {
   486     if (start>=end)
   487       return FALSE;
   488     switch (*pattern) {
   489     case '%':   /* new "symbol" */
   490       pattern++;
   491       assert(isdigit(*pattern));
   492       var=atoi(pattern) - 1;
   493       assert(var>=0 && var<MAX_OPT_VARS);
   494       assert(*start=='-' || alphanum(*start));
   495       for (i=0; start<end && (*start=='-' || *start=='+' || alphanum(*start)); i++,start++) {
   496         assert(i<=MAX_ALIAS);
   497         str[i]=*start;
   498       } /* for */
   499       str[i]='\0';
   500       if (symbols[var][0]!='\0') {
   501         if (strcmp(symbols[var],str)!=0)
   502           return FALSE; /* symbols should be identical */
   503       } else {
   504         strcpy(symbols[var],str);
   505       } /* if */
   506       break;
   507     case '-':
   508       value=-strtol(pattern+1,&pattern,16);
   509       ptr=itoh((ucell)value);
   510       while (*ptr!='\0') {
   511         if (tolower(*start) != tolower(*ptr))
   512           return FALSE;
   513         start++;
   514         ptr++;
   515       } /* while */
   516       pattern--;  /* there is an increment following at the end of the loop */
   517       break;
   518     case ' ':
   519       if (*start!='\t' && *start!=' ')
   520         return FALSE;
   521       while (start<end && (*start=='\t' || *start==' '))
   522         start++;
   523       break;
   524     case '!':
   525       while (start<end && (*start=='\t' || *start==' '))
   526         start++;                /* skip trailing white space */
   527       if (*start==';')
   528         while (start<end && *start!='\n')
   529           start++;              /* skip trailing comment */
   530       if (*start!='\n')
   531         return FALSE;
   532       assert(*(start+1)=='\0');
   533       start+=2;                 /* skip '\n' and '\0' */
   534       if (*(pattern+1)!='\0')
   535         while (start<end && *start=='\t' || *start==' ')
   536           start++;              /* skip leading white space of next instruction */
   537       break;
   538     default:
   539       if (tolower(*start) != tolower(*pattern))
   540         return FALSE;
   541       start++;
   542     } /* switch */
   543     pattern++;
   544   } /* while */
   545 
   546   *match_length=(int)(start-start_org);
   547   return TRUE;
   548 }
   549 
   550 static char *replacesequence(char *pattern,char symbols[MAX_OPT_VARS][MAX_ALIAS+1],int *repl_length)
   551 {
   552   char *lptr;
   553   int var;
   554   char *buffer;
   555 
   556   /* calculate the length of the new buffer
   557    * this is the length of the pattern plus the length of all symbols (note
   558    * that the same symbol may occur multiple times in the pattern) plus
   559    * line endings and startings ('\t' to start a line and '\n\0' to end one)
   560    */
   561   assert(repl_length!=NULL);
   562   *repl_length=0;
   563   lptr=pattern;
   564   while (*lptr) {
   565     switch (*lptr) {
   566     case '%':
   567       lptr++;           /* skip '%' */
   568       assert(isdigit(*lptr));
   569       var=atoi(lptr) - 1;
   570       assert(var>=0 && var<MAX_OPT_VARS);
   571       assert(symbols[var][0]!='\0');    /* variable should be defined */
   572       *repl_length+=strlen(symbols[var]);
   573       break;
   574     case '!':
   575       *repl_length+=3;  /* '\t', '\n' & '\0' */
   576       break;
   577     default:
   578       *repl_length+=1;
   579     } /* switch */
   580     lptr++;
   581   } /* while */
   582 
   583   /* allocate a buffer to replace the sequence in */
   584   if ((buffer=(char*)malloc(*repl_length))==NULL)
   585     return (char*)error(103);
   586 
   587   /* replace the pattern into this temporary buffer */
   588   lptr=buffer;
   589   *lptr++='\t';         /* the "replace" patterns do not have tabs */
   590   while (*pattern) {
   591     assert((int)(lptr-buffer)<*repl_length);
   592     switch (*pattern) {
   593     case '%':
   594       /* write out the symbol */
   595       pattern++;
   596       assert(isdigit(*pattern));
   597       var=atoi(pattern) - 1;
   598       assert(var>=0 && var<MAX_OPT_VARS);
   599       assert(symbols[var][0]!='\0');    /* variable should be defined */
   600       strcpy(lptr,symbols[var]);
   601       lptr+=strlen(symbols[var]);
   602       break;
   603     case '!':
   604       /* finish the line, optionally start the next line with an indent */
   605       *lptr++='\n';
   606       *lptr++='\0';
   607       if (*(pattern+1)!='\0')
   608         *lptr++='\t';
   609       break;
   610     default:
   611       *lptr++=*pattern;
   612     } /* switch */
   613     pattern++;
   614   } /* while */
   615 
   616   assert((int)(lptr-buffer)==*repl_length);
   617   return buffer;
   618 }
   619 
   620 static void strreplace(char *dest,char *replace,int sub_length,int repl_length,int dest_length)
   621 {
   622   int offset=sub_length-repl_length;
   623   if (offset>0) {               /* delete a section */
   624     memmove(dest,dest+offset,dest_length-offset);
   625     memset(dest+dest_length-offset,0xcc,offset); /* not needed, but for cleanlyness */
   626   } else if (offset<0) {        /* insert a section */
   627     memmove(dest-offset, dest, dest_length);
   628   } /* if */
   629   memcpy(dest, replace, repl_length);
   630 }
   631 
   632 /*  stgopt
   633  *
   634  *  Optimizes the staging buffer by checking for series of instructions that
   635  *  can be coded more compact. The routine expects the lines in the staging
   636  *  buffer to be separated with '\n' and '\0' characters.
   637  *
   638  *  The longest sequences should probably be checked first.
   639  */
   640 
   641 static void stgopt(char *start,char *end,int (*outputfunc)(char *str))
   642 {
   643   char symbols[MAX_OPT_VARS][MAX_ALIAS+1];
   644   int seq,match_length,repl_length;
   645   int matches;
   646   char *debut=start;  /* save original start of the buffer */
   647 
   648   assert(sequences!=NULL);
   649   /* do not match anything if debug-level is maximum */
   650   if (pc_optimize>sOPTIMIZE_NONE && sc_status==statWRITE) {
   651     do {
   652       matches=0;
   653       start=debut;
   654       while (start<end) {
   655         seq=0;
   656         while (sequences[seq].find!=NULL) {
   657           assert(seq>=0);
   658           if (*sequences[seq].find=='\0') {
   659             if (pc_optimize==sOPTIMIZE_NOMACRO) {
   660               break;    /* don't look further */
   661             } else {
   662               seq++;    /* continue with next string */
   663               continue;
   664             } /* if */
   665           } /* if */
   666           if (matchsequence(start,end,sequences[seq].find,symbols,&match_length)) {
   667             char *replace=replacesequence(sequences[seq].replace,symbols,&repl_length);
   668             /* If the replacement is bigger than the original section, we may need
   669              * to "grow" the staging buffer. This is quite complex, due to the
   670              * re-ordering of expressions that can also happen in the staging
   671              * buffer. In addition, it should not happen: the peephole optimizer
   672              * must replace sequences with *shorter* sequences, not longer ones.
   673              * So, I simply forbid sequences that are longer than the ones they
   674              * are meant to replace.
   675              */
   676             assert(match_length>=repl_length);
   677             if (match_length>=repl_length) {
   678               strreplace(start,replace,match_length,repl_length,(int)(end-start));
   679               end-=match_length-repl_length;
   680               free(replace);
   681               code_idx-=sequences[seq].savesize;
   682               seq=0;                      /* restart search for matches */
   683               matches++;
   684             } else {
   685               /* actually, we should never get here (match_length<repl_length) */
   686               assert(0);
   687               seq++;
   688             } /* if */
   689           } else {
   690             seq++;
   691           } /* if */
   692         } /* while */
   693         assert(sequences[seq].find==NULL || *sequences[seq].find=='\0' && pc_optimize==sOPTIMIZE_NOMACRO);
   694         start += strlen(start) + 1;       /* to next string */
   695       } /* while (start<end) */
   696     } while (matches>0);
   697   } /* if (pc_optimize>sOPTIMIZE_NONE && sc_status==statWRITE) */
   698 
   699   for (start=debut; start<end; start+=strlen(start)+1)
   700     outputfunc(start);
   701 }
   702 
   703 #undef SCPACK_TABLE