|
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@365
|
48 |
* Version: $Id$ |
|
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 |