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