sourcepawn/compiler/sc7.c
changeset 1952 b7aa2b626807
child 3069 b2656266b6d9
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/sourcepawn/compiler/sc7.c	Sun Mar 30 07:00:22 2008 +0000
     1.3 @@ -0,0 +1,703 @@
     1.4 +/*  Pawn compiler - Staging buffer and optimizer
     1.5 + *
     1.6 + *  The staging buffer
     1.7 + *  ------------------
     1.8 + *  The staging buffer allows buffered output of generated code, deletion
     1.9 + *  of redundant code, optimization by a tinkering process and reversing
    1.10 + *  the ouput of evaluated expressions (which is used for the reversed
    1.11 + *  evaluation of arguments in functions).
    1.12 + *  Initially, stgwrite() writes to the file directly, but after a call to
    1.13 + *  stgset(TRUE), output is redirected to the buffer. After a call to
    1.14 + *  stgset(FALSE), stgwrite()'s output is directed to the file again. Thus
    1.15 + *  only one routine is used for writing to the output, which can be
    1.16 + *  buffered output or direct output.
    1.17 + *
    1.18 + *  staging buffer variables:   stgbuf  - the buffer
    1.19 + *                              stgidx  - current index in the staging buffer
    1.20 + *                              staging - if true, write to the staging buffer;
    1.21 + *                                        if false, write to file directly.
    1.22 + *
    1.23 + * The peephole optimizer uses a dual "pipeline". The staging buffer (described
    1.24 + * above) gets optimized for each expression or sub-expression in a function
    1.25 + * call. The peephole optimizer is recursive, but it does not span multiple
    1.26 + * sub-expressions. However, the data gets written to a second buffer that
    1.27 + * behaves much like the staging buffer. This second buffer gathers all
    1.28 + * optimized strings from the staging buffer for a complete expression. The
    1.29 + * peephole optmizer then runs over this second buffer to find optimzations
    1.30 + * across function parameter boundaries.
    1.31 + *
    1.32 + *
    1.33 + *  Copyright (c) ITB CompuPhase, 1997-2006
    1.34 + *
    1.35 + *  This software is provided "as-is", without any express or implied warranty.
    1.36 + *  In no event will the authors be held liable for any damages arising from
    1.37 + *  the use of this software.
    1.38 + *
    1.39 + *  Permission is granted to anyone to use this software for any purpose,
    1.40 + *  including commercial applications, and to alter it and redistribute it
    1.41 + *  freely, subject to the following restrictions:
    1.42 + *
    1.43 + *  1.  The origin of this software must not be misrepresented; you must not
    1.44 + *      claim that you wrote the original software. If you use this software in
    1.45 + *      a product, an acknowledgment in the product documentation would be
    1.46 + *      appreciated but is not required.
    1.47 + *  2.  Altered source versions must be plainly marked as such, and must not be
    1.48 + *      misrepresented as being the original software.
    1.49 + *  3.  This notice may not be removed or altered from any source distribution.
    1.50 + *
    1.51 + *  Version: $Id$
    1.52 + */
    1.53 +#include <assert.h>
    1.54 +#include <stdio.h>
    1.55 +#include <stdlib.h>     /* for atoi() */
    1.56 +#include <string.h>
    1.57 +#include <ctype.h>
    1.58 +#if defined FORTIFY
    1.59 +  #include <alloc/fortify.h>
    1.60 +#endif
    1.61 +#include "sc.h"
    1.62 +
    1.63 +#if defined _MSC_VER
    1.64 +  #pragma warning(push)
    1.65 +  #pragma warning(disable:4125)  /* decimal digit terminates octal escape sequence */
    1.66 +#endif
    1.67 +
    1.68 +#include "sc7.scp"
    1.69 +
    1.70 +#if defined _MSC_VER
    1.71 +  #pragma warning(pop)
    1.72 +#endif
    1.73 +
    1.74 +static int stgstring(char *start,char *end);
    1.75 +static void stgopt(char *start,char *end,int (*outputfunc)(char *str));
    1.76 +
    1.77 +
    1.78 +#define sSTG_GROW   512
    1.79 +#define sSTG_MAX    20480
    1.80 +
    1.81 +static char *stgbuf=NULL;
    1.82 +static int stgmax=0;    /* current size of the staging buffer */
    1.83 +
    1.84 +static char *stgpipe=NULL;
    1.85 +static int pipemax=0;   /* current size of the stage pipe, a second staging buffer */
    1.86 +static int pipeidx=0;
    1.87 +
    1.88 +#define CHECK_STGBUFFER(index) if ((int)(index)>=stgmax)  grow_stgbuffer(&stgbuf, stgmax, (index)+1)
    1.89 +#define CHECK_STGPIPE(index)   if ((int)(index)>=pipemax) grow_stgbuffer(&stgpipe, pipemax, (index)+1)
    1.90 +
    1.91 +static void grow_stgbuffer(char **buffer, int curmax, int requiredsize)
    1.92 +{
    1.93 +  char *p;
    1.94 +  int clear= (*buffer==NULL); /* if previously none, empty buffer explicitly */
    1.95 +
    1.96 +  assert(curmax<requiredsize);
    1.97 +  /* if the staging buffer (holding intermediate code for one line) grows
    1.98 +   * over a few kBytes, there is probably a run-away expression
    1.99 +   */
   1.100 +  if (requiredsize>sSTG_MAX)
   1.101 +    error(102,"staging buffer");    /* staging buffer overflow (fatal error) */
   1.102 +  curmax=requiredsize+sSTG_GROW;
   1.103 +  if (*buffer!=NULL)
   1.104 +    p=(char *)realloc(*buffer,curmax*sizeof(char));
   1.105 +  else
   1.106 +    p=(char *)malloc(curmax*sizeof(char));
   1.107 +  if (p==NULL)
   1.108 +    error(102,"staging buffer");    /* staging buffer overflow (fatal error) */
   1.109 +  *buffer=p;
   1.110 +  if (clear)
   1.111 +    **buffer='\0';
   1.112 +}
   1.113 +
   1.114 +SC_FUNC void stgbuffer_cleanup(void)
   1.115 +{
   1.116 +  if (stgbuf!=NULL) {
   1.117 +    free(stgbuf);
   1.118 +    stgbuf=NULL;
   1.119 +    stgmax=0;
   1.120 +  } /* if */
   1.121 +  if (stgpipe!=NULL) {
   1.122 +    free(stgpipe);
   1.123 +    stgpipe=NULL;
   1.124 +    pipemax=0;
   1.125 +    pipeidx=0;
   1.126 +  } /* if */
   1.127 +}
   1.128 +
   1.129 +/* the variables "stgidx" and "staging" are declared in "scvars.c" */
   1.130 +
   1.131 +/*  stgmark
   1.132 + *
   1.133 + *  Copies a mark into the staging buffer. At this moment there are three
   1.134 + *  possible marks:
   1.135 + *     sSTARTREORDER    identifies the beginning of a series of expression
   1.136 + *                      strings that must be written to the output file in
   1.137 + *                      reordered order
   1.138 + *    sENDREORDER       identifies the end of 'reverse evaluation'
   1.139 + *    sEXPRSTART + idx  only valid within a block that is evaluated in
   1.140 + *                      reordered order, it identifies the start of an
   1.141 + *                      expression; the "idx" value is the argument position
   1.142 + *
   1.143 + *  Global references: stgidx  (altered)
   1.144 + *                     stgbuf  (altered)
   1.145 + *                     staging (referred to only)
   1.146 + */
   1.147 +SC_FUNC void stgmark(char mark)
   1.148 +{
   1.149 +  if (staging) {
   1.150 +    CHECK_STGBUFFER(stgidx);
   1.151 +    stgbuf[stgidx++]=mark;
   1.152 +  } /* if */
   1.153 +}
   1.154 +
   1.155 +static int rebuffer(char *str)
   1.156 +{
   1.157 +  if (sc_status==statWRITE) {
   1.158 +    if (pipeidx>=2 && stgpipe[pipeidx-1]=='\0' && stgpipe[pipeidx-2]!='\n')
   1.159 +      pipeidx-=1;                      /* overwrite last '\0' */
   1.160 +    while (*str!='\0') {               /* copy to staging buffer */
   1.161 +      CHECK_STGPIPE(pipeidx);
   1.162 +      stgpipe[pipeidx++]=*str++;
   1.163 +    } /* while */
   1.164 +    CHECK_STGPIPE(pipeidx);
   1.165 +    stgpipe[pipeidx++]='\0';
   1.166 +  } /* if */
   1.167 +  return TRUE;
   1.168 +}
   1.169 +
   1.170 +static int filewrite(char *str)
   1.171 +{
   1.172 +  if (sc_status==statWRITE)
   1.173 +    return pc_writeasm(outf,str);
   1.174 +  return TRUE;
   1.175 +}
   1.176 +
   1.177 +/*  stgwrite
   1.178 + *
   1.179 + *  Writes the string "st" to the staging buffer or to the output file. In the
   1.180 + *  case of writing to the staging buffer, the terminating byte of zero is
   1.181 + *  copied too, but... the optimizer can only work on complete lines (not on
   1.182 + *  fractions of it. Therefore if the string is staged, if the last character
   1.183 + *  written to the buffer is a '\0' and the previous-to-last is not a '\n',
   1.184 + *  the string is concatenated to the last string in the buffer (the '\0' is
   1.185 + *  overwritten). This also means an '\n' used in the middle of a string isn't
   1.186 + *  recognized and could give wrong results with the optimizer.
   1.187 + *  Even when writing to the output file directly, all strings are buffered
   1.188 + *  until a whole line is complete.
   1.189 + *
   1.190 + *  Global references: stgidx  (altered)
   1.191 + *                     stgbuf  (altered)
   1.192 + *                     staging (referred to only)
   1.193 + */
   1.194 +SC_FUNC void stgwrite(const char *st)
   1.195 +{
   1.196 +  int len;
   1.197 +
   1.198 +  if (staging) {
   1.199 +    assert(stgidx==0 || stgbuf!=NULL);  /* staging buffer must be valid if there is (apparently) something in it */
   1.200 +    if (stgidx>=2 && stgbuf[stgidx-1]=='\0' && stgbuf[stgidx-2]!='\n')
   1.201 +      stgidx-=1;                       /* overwrite last '\0' */
   1.202 +    while (*st!='\0') {                /* copy to staging buffer */
   1.203 +      CHECK_STGBUFFER(stgidx);
   1.204 +      stgbuf[stgidx++]=*st++;
   1.205 +    } /* while */
   1.206 +    CHECK_STGBUFFER(stgidx);
   1.207 +    stgbuf[stgidx++]='\0';
   1.208 +  } else {
   1.209 +    len=(stgbuf!=NULL) ? strlen(stgbuf) : 0;
   1.210 +    CHECK_STGBUFFER(len+strlen(st)+1);
   1.211 +    strcat(stgbuf,st);
   1.212 +    len=strlen(stgbuf);
   1.213 +    if (len>0 && stgbuf[len-1]=='\n') {
   1.214 +      filewrite(stgbuf);
   1.215 +      stgbuf[0]='\0';
   1.216 +    } /* if */
   1.217 +  } /* if */
   1.218 +}
   1.219 +
   1.220 +/*  stgout
   1.221 + *
   1.222 + *  Writes the staging buffer to the output file via stgstring() (for
   1.223 + *  reversing expressions in the buffer) and stgopt() (for optimizing). It
   1.224 + *  resets "stgidx".
   1.225 + *
   1.226 + *  Global references: stgidx  (altered)
   1.227 + *                     stgbuf  (referred to only)
   1.228 + *                     staging (referred to only)
   1.229 + */
   1.230 +SC_FUNC void stgout(int index)
   1.231 +{
   1.232 +  int reordered=0;
   1.233 +  int idx;
   1.234 +
   1.235 +  if (!staging)
   1.236 +    return;
   1.237 +  assert(pipeidx==0);
   1.238 +
   1.239 +  /* first pass: sub-expressions */
   1.240 +  if (sc_status==statWRITE)
   1.241 +    reordered=stgstring(&stgbuf[index],&stgbuf[stgidx]);
   1.242 +  stgidx=index;
   1.243 +
   1.244 +  /* second pass: optimize the buffer created in the first pass */
   1.245 +  if (sc_status==statWRITE) {
   1.246 +    if (reordered) {
   1.247 +      stgopt(stgpipe,stgpipe+pipeidx,filewrite);
   1.248 +    } else {
   1.249 +      /* there is no sense in re-optimizing if the order of the sub-expressions
   1.250 +       * did not change; so output directly
   1.251 +       */
   1.252 +      for (idx=0; idx<pipeidx; idx+=strlen(stgpipe+idx)+1)
   1.253 +        filewrite(stgpipe+idx);
   1.254 +    } /* if */
   1.255 +  } /* if */
   1.256 +  pipeidx=0;  /* reset second pipe */
   1.257 +}
   1.258 +
   1.259 +typedef struct {
   1.260 +  char *start,*end;
   1.261 +} argstack;
   1.262 +
   1.263 +/*  stgstring
   1.264 + *
   1.265 + *  Analyses whether code strings should be output to the file as they appear
   1.266 + *  in the staging buffer or whether portions of it should be re-ordered.
   1.267 + *  Re-ordering takes place in function argument lists; Pawn passes arguments
   1.268 + *  to functions from right to left. When arguments are "named" rather than
   1.269 + *  positional, the order in the source stream is indeterminate.
   1.270 + *  This function calls itself recursively in case it needs to re-order code
   1.271 + *  strings, and it uses a private stack (or list) to mark the start and the
   1.272 + *  end of expressions in their correct (reversed) order.
   1.273 + *  In any case, stgstring() sends a block as large as possible to the
   1.274 + *  optimizer stgopt().
   1.275 + *
   1.276 + *  In "reorder" mode, each set of code strings must start with the token
   1.277 + *  sEXPRSTART, even the first. If the token sSTARTREORDER is represented
   1.278 + *  by '[', sENDREORDER by ']' and sEXPRSTART by '|' the following applies:
   1.279 + *     '[]...'     valid, but useless; no output
   1.280 + *     '[|...]     valid, but useless; only one string
   1.281 + *     '[|...|...] valid and usefull
   1.282 + *     '[...|...]  invalid, first string doesn't start with '|'
   1.283 + *     '[|...|]    invalid
   1.284 + */
   1.285 +static int stgstring(char *start,char *end)
   1.286 +{
   1.287 +  char *ptr;
   1.288 +  int nest,argc,arg;
   1.289 +  argstack *stack;
   1.290 +  int reordered=0;
   1.291 +
   1.292 +  while (start<end) {
   1.293 +    if (*start==sSTARTREORDER) {
   1.294 +      start+=1;         /* skip token */
   1.295 +      /* allocate a argstack with sMAXARGS items */
   1.296 +      stack=(argstack *)malloc(sMAXARGS*sizeof(argstack));
   1.297 +      if (stack==NULL)
   1.298 +        error(103);     /* insufficient memory */
   1.299 +      reordered=1;      /* mark that the expression is reordered */
   1.300 +      nest=1;           /* nesting counter */
   1.301 +      argc=0;           /* argument counter */
   1.302 +      arg=-1;           /* argument index; no valid argument yet */
   1.303 +      do {
   1.304 +        switch (*start) {
   1.305 +        case sSTARTREORDER:
   1.306 +          nest++;
   1.307 +          start++;
   1.308 +          break;
   1.309 +        case sENDREORDER:
   1.310 +          nest--;
   1.311 +          start++;
   1.312 +          break;
   1.313 +        default:
   1.314 +          if ((*start & sEXPRSTART)==sEXPRSTART) {
   1.315 +            if (nest==1) {
   1.316 +              if (arg>=0)
   1.317 +                stack[arg].end=start-1; /* finish previous argument */
   1.318 +              arg=(unsigned char)*start - sEXPRSTART;
   1.319 +              stack[arg].start=start+1;
   1.320 +              if (arg>=argc)
   1.321 +                argc=arg+1;
   1.322 +            } /* if */
   1.323 +            start++;
   1.324 +          } else {
   1.325 +            start+=strlen(start)+1;
   1.326 +          } /* if */
   1.327 +        } /* switch */
   1.328 +      } while (nest); /* enddo */
   1.329 +      if (arg>=0)
   1.330 +        stack[arg].end=start-1;   /* finish previous argument */
   1.331 +      while (argc>0) {
   1.332 +        argc--;
   1.333 +        stgstring(stack[argc].start,stack[argc].end);
   1.334 +      } /* while */
   1.335 +      free(stack);
   1.336 +    } else {
   1.337 +      ptr=start;
   1.338 +      while (ptr<end && *ptr!=sSTARTREORDER)
   1.339 +        ptr+=strlen(ptr)+1;
   1.340 +      stgopt(start,ptr,rebuffer);
   1.341 +      start=ptr;
   1.342 +    } /* if */
   1.343 +  } /* while */
   1.344 +  return reordered;
   1.345 +}
   1.346 +
   1.347 +/*  stgdel
   1.348 + *
   1.349 + *  Scraps code from the staging buffer by resetting "stgidx" to "index".
   1.350 + *
   1.351 + *  Global references: stgidx (altered)
   1.352 + *                     staging (reffered to only)
   1.353 + */
   1.354 +SC_FUNC void stgdel(int index,cell code_index)
   1.355 +{
   1.356 +  if (staging) {
   1.357 +    stgidx=index;
   1.358 +    code_idx=code_index;
   1.359 +  } /* if */
   1.360 +}
   1.361 +
   1.362 +SC_FUNC int stgget(int *index,cell *code_index)
   1.363 +{
   1.364 +  if (staging) {
   1.365 +    *index=stgidx;
   1.366 +    *code_index=code_idx;
   1.367 +  } /* if */
   1.368 +  return staging;
   1.369 +}
   1.370 +
   1.371 +/*  stgset
   1.372 + *
   1.373 + *  Sets staging on or off. If it's turned off, the staging buffer must be
   1.374 + *  initialized to an empty string. If it's turned on, the routine makes sure
   1.375 + *  the index ("stgidx") is set to 0 (it should already be 0).
   1.376 + *
   1.377 + *  Global references: staging  (altered)
   1.378 + *                     stgidx   (altered)
   1.379 + *                     stgbuf   (contents altered)
   1.380 + */
   1.381 +SC_FUNC void stgset(int onoff)
   1.382 +{
   1.383 +  staging=onoff;
   1.384 +  if (staging){
   1.385 +    assert(stgidx==0);
   1.386 +    stgidx=0;
   1.387 +    CHECK_STGBUFFER(stgidx);
   1.388 +    /* write any contents that may be put in the buffer by stgwrite()
   1.389 +     * when "staging" was 0
   1.390 +     */
   1.391 +    if (strlen(stgbuf)>0)
   1.392 +      filewrite(stgbuf);
   1.393 +  } /* if */
   1.394 +  stgbuf[0]='\0';
   1.395 +}
   1.396 +
   1.397 +/* phopt_init
   1.398 + * Initialize all sequence strings of the peehole optimizer. The strings
   1.399 + * are embedded in the .EXE file in compressed format, here we expand
   1.400 + * them (and allocate memory for the sequences).
   1.401 + */
   1.402 +static SEQUENCE *sequences;
   1.403 +
   1.404 +SC_FUNC int phopt_init(void)
   1.405 +{
   1.406 +  int number, i, len;
   1.407 +  char str[160];
   1.408 +
   1.409 +  /* count number of sequences */
   1.410 +  for (number=0; sequences_cmp[number].find!=NULL; number++)
   1.411 +    /* nothing */;
   1.412 +  number++;             /* include an item for the NULL terminator */
   1.413 +
   1.414 +  if ((sequences=(SEQUENCE*)malloc(number * sizeof(SEQUENCE)))==NULL)
   1.415 +    return FALSE;
   1.416 +
   1.417 +  /* pre-initialize all to NULL (in case of failure) */
   1.418 +  for (i=0; i<number; i++) {
   1.419 +    sequences[i].find=NULL;
   1.420 +    sequences[i].replace=NULL;
   1.421 +    sequences[i].savesize=0;
   1.422 +  } /* for */
   1.423 +
   1.424 +  /* expand all strings */
   1.425 +  for (i=0; i<number-1; i++) {
   1.426 +    len = strexpand(str,(unsigned char*)sequences_cmp[i].find,sizeof str,SCPACK_TABLE);
   1.427 +    assert(len<=sizeof str);
   1.428 +    assert(len==(int)strlen(str)+1);
   1.429 +    sequences[i].find=(char*)malloc(len);
   1.430 +    if (sequences[i].find!=NULL)
   1.431 +      strcpy(sequences[i].find,str);
   1.432 +    len = strexpand(str,(unsigned char*)sequences_cmp[i].replace,sizeof str,SCPACK_TABLE);
   1.433 +    assert(len<=sizeof str);
   1.434 +    assert(len==(int)strlen(str)+1);
   1.435 +    sequences[i].replace=(char*)malloc(len);
   1.436 +    if (sequences[i].replace!=NULL)
   1.437 +      strcpy(sequences[i].replace,str);
   1.438 +    sequences[i].savesize=sequences_cmp[i].savesize;
   1.439 +    if (sequences[i].find==NULL || sequences[i].replace==NULL)
   1.440 +      return phopt_cleanup();
   1.441 +  } /* for */
   1.442 +
   1.443 +  return TRUE;
   1.444 +}
   1.445 +
   1.446 +SC_FUNC int phopt_cleanup(void)
   1.447 +{
   1.448 +  int i;
   1.449 +  if (sequences!=NULL) {
   1.450 +    i=0;
   1.451 +    while (sequences[i].find!=NULL || sequences[i].replace!=NULL) {
   1.452 +      if (sequences[i].find!=NULL)
   1.453 +        free(sequences[i].find);
   1.454 +      if (sequences[i].replace!=NULL)
   1.455 +        free(sequences[i].replace);
   1.456 +      i++;
   1.457 +    } /* while */
   1.458 +    free(sequences);
   1.459 +    sequences=NULL;
   1.460 +  } /* if */
   1.461 +  return FALSE;
   1.462 +}
   1.463 +
   1.464 +#define MAX_OPT_VARS    5
   1.465 +#define MAX_OPT_CAT     5       /* max. values that are concatenated */
   1.466 +#if sNAMEMAX > (PAWN_CELL_SIZE/4) * MAX_OPT_CAT
   1.467 +  #define MAX_ALIAS       sNAMEMAX
   1.468 +#else
   1.469 +  #define MAX_ALIAS       (PAWN_CELL_SIZE/4) * MAX_OPT_CAT
   1.470 +#endif
   1.471 +
   1.472 +static int matchsequence(char *start,char *end,char *pattern,
   1.473 +                         char symbols[MAX_OPT_VARS][MAX_ALIAS+1],
   1.474 +                         int *match_length)
   1.475 +{
   1.476 +  int var,i;
   1.477 +  char str[MAX_ALIAS+1];
   1.478 +  char *start_org=start;
   1.479 +  cell value;
   1.480 +  char *ptr;
   1.481 +
   1.482 +  *match_length=0;
   1.483 +  for (var=0; var<MAX_OPT_VARS; var++)
   1.484 +    symbols[var][0]='\0';
   1.485 +
   1.486 +  while (*start=='\t' || *start==' ')
   1.487 +    start++;
   1.488 +  while (*pattern) {
   1.489 +    if (start>=end)
   1.490 +      return FALSE;
   1.491 +    switch (*pattern) {
   1.492 +    case '%':   /* new "symbol" */
   1.493 +      pattern++;
   1.494 +      assert(isdigit(*pattern));
   1.495 +      var=atoi(pattern) - 1;
   1.496 +      assert(var>=0 && var<MAX_OPT_VARS);
   1.497 +      assert(*start=='-' || alphanum(*start));
   1.498 +      for (i=0; start<end && (*start=='-' || *start=='+' || alphanum(*start)); i++,start++) {
   1.499 +        assert(i<=MAX_ALIAS);
   1.500 +        str[i]=*start;
   1.501 +      } /* for */
   1.502 +      str[i]='\0';
   1.503 +      if (symbols[var][0]!='\0') {
   1.504 +        if (strcmp(symbols[var],str)!=0)
   1.505 +          return FALSE; /* symbols should be identical */
   1.506 +      } else {
   1.507 +        strcpy(symbols[var],str);
   1.508 +      } /* if */
   1.509 +      break;
   1.510 +    case '-':
   1.511 +      value=-strtol(pattern+1,&pattern,16);
   1.512 +      ptr=itoh((ucell)value);
   1.513 +      while (*ptr!='\0') {
   1.514 +        if (tolower(*start) != tolower(*ptr))
   1.515 +          return FALSE;
   1.516 +        start++;
   1.517 +        ptr++;
   1.518 +      } /* while */
   1.519 +      pattern--;  /* there is an increment following at the end of the loop */
   1.520 +      break;
   1.521 +    case ' ':
   1.522 +      if (*start!='\t' && *start!=' ')
   1.523 +        return FALSE;
   1.524 +      while (start<end && (*start=='\t' || *start==' '))
   1.525 +        start++;
   1.526 +      break;
   1.527 +    case '!':
   1.528 +      while (start<end && (*start=='\t' || *start==' '))
   1.529 +        start++;                /* skip trailing white space */
   1.530 +      if (*start==';')
   1.531 +        while (start<end && *start!='\n')
   1.532 +          start++;              /* skip trailing comment */
   1.533 +      if (*start!='\n')
   1.534 +        return FALSE;
   1.535 +      assert(*(start+1)=='\0');
   1.536 +      start+=2;                 /* skip '\n' and '\0' */
   1.537 +      if (*(pattern+1)!='\0')
   1.538 +        while (start<end && *start=='\t' || *start==' ')
   1.539 +          start++;              /* skip leading white space of next instruction */
   1.540 +      break;
   1.541 +    default:
   1.542 +      if (tolower(*start) != tolower(*pattern))
   1.543 +        return FALSE;
   1.544 +      start++;
   1.545 +    } /* switch */
   1.546 +    pattern++;
   1.547 +  } /* while */
   1.548 +
   1.549 +  *match_length=(int)(start-start_org);
   1.550 +  return TRUE;
   1.551 +}
   1.552 +
   1.553 +static char *replacesequence(char *pattern,char symbols[MAX_OPT_VARS][MAX_ALIAS+1],int *repl_length)
   1.554 +{
   1.555 +  char *lptr;
   1.556 +  int var;
   1.557 +  char *buffer;
   1.558 +
   1.559 +  /* calculate the length of the new buffer
   1.560 +   * this is the length of the pattern plus the length of all symbols (note
   1.561 +   * that the same symbol may occur multiple times in the pattern) plus
   1.562 +   * line endings and startings ('\t' to start a line and '\n\0' to end one)
   1.563 +   */
   1.564 +  assert(repl_length!=NULL);
   1.565 +  *repl_length=0;
   1.566 +  lptr=pattern;
   1.567 +  while (*lptr) {
   1.568 +    switch (*lptr) {
   1.569 +    case '%':
   1.570 +      lptr++;           /* skip '%' */
   1.571 +      assert(isdigit(*lptr));
   1.572 +      var=atoi(lptr) - 1;
   1.573 +      assert(var>=0 && var<MAX_OPT_VARS);
   1.574 +      assert(symbols[var][0]!='\0');    /* variable should be defined */
   1.575 +      *repl_length+=strlen(symbols[var]);
   1.576 +      break;
   1.577 +    case '!':
   1.578 +      *repl_length+=3;  /* '\t', '\n' & '\0' */
   1.579 +      break;
   1.580 +    default:
   1.581 +      *repl_length+=1;
   1.582 +    } /* switch */
   1.583 +    lptr++;
   1.584 +  } /* while */
   1.585 +
   1.586 +  /* allocate a buffer to replace the sequence in */
   1.587 +  if ((buffer=(char*)malloc(*repl_length))==NULL)
   1.588 +    return (char*)error(103);
   1.589 +
   1.590 +  /* replace the pattern into this temporary buffer */
   1.591 +  lptr=buffer;
   1.592 +  *lptr++='\t';         /* the "replace" patterns do not have tabs */
   1.593 +  while (*pattern) {
   1.594 +    assert((int)(lptr-buffer)<*repl_length);
   1.595 +    switch (*pattern) {
   1.596 +    case '%':
   1.597 +      /* write out the symbol */
   1.598 +      pattern++;
   1.599 +      assert(isdigit(*pattern));
   1.600 +      var=atoi(pattern) - 1;
   1.601 +      assert(var>=0 && var<MAX_OPT_VARS);
   1.602 +      assert(symbols[var][0]!='\0');    /* variable should be defined */
   1.603 +      strcpy(lptr,symbols[var]);
   1.604 +      lptr+=strlen(symbols[var]);
   1.605 +      break;
   1.606 +    case '!':
   1.607 +      /* finish the line, optionally start the next line with an indent */
   1.608 +      *lptr++='\n';
   1.609 +      *lptr++='\0';
   1.610 +      if (*(pattern+1)!='\0')
   1.611 +        *lptr++='\t';
   1.612 +      break;
   1.613 +    default:
   1.614 +      *lptr++=*pattern;
   1.615 +    } /* switch */
   1.616 +    pattern++;
   1.617 +  } /* while */
   1.618 +
   1.619 +  assert((int)(lptr-buffer)==*repl_length);
   1.620 +  return buffer;
   1.621 +}
   1.622 +
   1.623 +static void strreplace(char *dest,char *replace,int sub_length,int repl_length,int dest_length)
   1.624 +{
   1.625 +  int offset=sub_length-repl_length;
   1.626 +  if (offset>0) {               /* delete a section */
   1.627 +    memmove(dest,dest+offset,dest_length-offset);
   1.628 +    memset(dest+dest_length-offset,0xcc,offset); /* not needed, but for cleanlyness */
   1.629 +  } else if (offset<0) {        /* insert a section */
   1.630 +    memmove(dest-offset, dest, dest_length);
   1.631 +  } /* if */
   1.632 +  memcpy(dest, replace, repl_length);
   1.633 +}
   1.634 +
   1.635 +/*  stgopt
   1.636 + *
   1.637 + *  Optimizes the staging buffer by checking for series of instructions that
   1.638 + *  can be coded more compact. The routine expects the lines in the staging
   1.639 + *  buffer to be separated with '\n' and '\0' characters.
   1.640 + *
   1.641 + *  The longest sequences should probably be checked first.
   1.642 + */
   1.643 +
   1.644 +static void stgopt(char *start,char *end,int (*outputfunc)(char *str))
   1.645 +{
   1.646 +  char symbols[MAX_OPT_VARS][MAX_ALIAS+1];
   1.647 +  int seq,match_length,repl_length;
   1.648 +  int matches;
   1.649 +  char *debut=start;  /* save original start of the buffer */
   1.650 +
   1.651 +  assert(sequences!=NULL);
   1.652 +  /* do not match anything if debug-level is maximum */
   1.653 +  if (pc_optimize>sOPTIMIZE_NONE && sc_status==statWRITE) {
   1.654 +    do {
   1.655 +      matches=0;
   1.656 +      start=debut;
   1.657 +      while (start<end) {
   1.658 +        seq=0;
   1.659 +        while (sequences[seq].find!=NULL) {
   1.660 +          assert(seq>=0);
   1.661 +          if (*sequences[seq].find=='\0') {
   1.662 +            if (pc_optimize==sOPTIMIZE_NOMACRO) {
   1.663 +              break;    /* don't look further */
   1.664 +            } else {
   1.665 +              seq++;    /* continue with next string */
   1.666 +              continue;
   1.667 +            } /* if */
   1.668 +          } /* if */
   1.669 +          if (matchsequence(start,end,sequences[seq].find,symbols,&match_length)) {
   1.670 +            char *replace=replacesequence(sequences[seq].replace,symbols,&repl_length);
   1.671 +            /* If the replacement is bigger than the original section, we may need
   1.672 +             * to "grow" the staging buffer. This is quite complex, due to the
   1.673 +             * re-ordering of expressions that can also happen in the staging
   1.674 +             * buffer. In addition, it should not happen: the peephole optimizer
   1.675 +             * must replace sequences with *shorter* sequences, not longer ones.
   1.676 +             * So, I simply forbid sequences that are longer than the ones they
   1.677 +             * are meant to replace.
   1.678 +             */
   1.679 +            assert(match_length>=repl_length);
   1.680 +            if (match_length>=repl_length) {
   1.681 +              strreplace(start,replace,match_length,repl_length,(int)(end-start));
   1.682 +              end-=match_length-repl_length;
   1.683 +              free(replace);
   1.684 +              code_idx-=sequences[seq].savesize;
   1.685 +              seq=0;                      /* restart search for matches */
   1.686 +              matches++;
   1.687 +            } else {
   1.688 +              /* actually, we should never get here (match_length<repl_length) */
   1.689 +              assert(0);
   1.690 +              seq++;
   1.691 +            } /* if */
   1.692 +          } else {
   1.693 +            seq++;
   1.694 +          } /* if */
   1.695 +        } /* while */
   1.696 +        assert(sequences[seq].find==NULL || *sequences[seq].find=='\0' && pc_optimize==sOPTIMIZE_NOMACRO);
   1.697 +        start += strlen(start) + 1;       /* to next string */
   1.698 +      } /* while (start<end) */
   1.699 +    } while (matches>0);
   1.700 +  } /* if (pc_optimize>sOPTIMIZE_NONE && sc_status==statWRITE) */
   1.701 +
   1.702 +  for (start=debut; start<end; start+=strlen(start)+1)
   1.703 +    outputfunc(start);
   1.704 +}
   1.705 +
   1.706 +#undef SCPACK_TABLE