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