ugBASIC 1.18
An isomorphic BASIC language compiler for retrocomputers
Loading...
Searching...
No Matches
_optimizer.c
Go to the documentation of this file.
1/*****************************************************************************
2 * ugBASIC - an isomorphic BASIC language compiler for retrocomputers *
3 *****************************************************************************
4 * Copyright 2021-2026 Marco Spedaletti (asimov@mclink.it)
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *----------------------------------------------------------------------------
18 * Concesso in licenza secondo i termini della Licenza Apache, versione 2.0
19 * (la "Licenza"); è proibito usare questo file se non in conformità alla
20 * Licenza. Una copia della Licenza è disponibile all'indirizzo:
21 *
22 * http://www.apache.org/licenses/LICENSE-2.0
23 *
24 * Se non richiesto dalla legislazione vigente o concordato per iscritto,
25 * il software distribuito nei termini della Licenza è distribuito
26 * "COSÌ COM'È", SENZA GARANZIE O CONDIZIONI DI ALCUN TIPO, esplicite o
27 * implicite. Consultare la Licenza per il testo specifico che regola le
28 * autorizzazioni e le limitazioni previste dalla medesima.
29 ****************************************************************************/
30
31 /* Zilog Z80 optimizations for ugBasic by
32 * Based on Motorola 6809 optimizations for ugBasic by S.Devulder
33 *
34 * The idea here is to look for specific patterns over consecutive (1 to 4)
35 * lines of code generated by ugBasic compiler, and reorganize it locally
36 * by another pattern that contains a more efficent code achieving the same
37 * result.
38 *
39 * It does multiple passes over the source as local rewrites can be
40 * applied on previous rewrites, resulting in a avalanche effects that
41 * optimizes the source far away from one can guess from the basic patterns.
42 *
43 * The optimizer can also perform some data flow analysis. It can for
44 * instance see that one of the accumulator is $00 to simplify code. It can
45 * also detect that some data in memory are written but never read. These
46 * are called dead-data. It will then remove all reference to these data in
47 * the code making it smaller and faster (as a useless write is not
48 * performed anymore).
49 *
50 * Real full data-flow analysis should normally be able to detect data which
51 * is written two times in a row whithout being read in-between. These are
52 * also called dead-data since the first written value is also lost. These
53 * dead-data typically appear during the avalanche effect occuring during
54 * previous optimizations passes. Write operation to these dead-data can
55 * also be removed harmlessly.
56 *
57 * Unfortunately this version of the optimized doesn't have yet a complete
58 * data-flow analyzer. Instead, heuristics is used to guess whether a memory
59 * operation accesses a dead data. In that case the optimizer will indicate
60 * in the commented-out code that the data is *presumed dead*. The
61 * heuristics are carefully choosen from the patterns generated by ugBasic
62 * and make good guesses. But if you consider these as too dangerous you
63 * can disable all of them with the "ALLOW_UNSAFE" flag below.
64 *
65 * Last the optimizer will also attempt to reorganize the data to get a
66 * faster & shorter code. Some data will be "inlined" in the code, making
67 * the code auto-modifiable which is no problem for the mc6809. And some
68 * "heavily used" data will be moved into the direct-page location for
69 * faster accesses.
70 */
71
72/****************************************************************************
73 * INCLUDE SECTION
74 ****************************************************************************/
75
76#include "../../ugbc.h"
77#include <stdarg.h>
78
79/****************************************************************************
80 * CODE SECTION
81 ****************************************************************************/
82
83#define DIRECT_PAGE 0x2100
84#define LOOK_AHEAD 5
85#define ALLOW_UNSAFE 1
86#define KEEP_COMMENTS 1
87
88#define DO_DIRECT_PAGE 1
89#define DO_INLINE 1
90#define DO_UNREAD 1
91
92/* returns an UPPER-cased char */
93static inline char _toUpper(char a) {
94 return (a>='a' && a<='z') ? a-'a'+'A' : a;
95}
96
97/* returns true if char is end of line ? */
98static inline int _eol(char c) {
99 return c=='\0' || c=='\n';
100}
101
102/* returns true if both char matches */
103static inline int _eq(char pat, char txt) {
104 return (pat<=' ') ? (txt<=' ') : (_toUpper(pat)==_toUpper(txt));
105}
106
107/* returns true if the POBuffer matches a comment or and empty line */
109 char * _buffer = buf->str;
110
111 if ( ! *_buffer ) {
112 return 1;
113 }
114 if ( *_buffer == '\r' || *_buffer == '\n' ) {
115 return 1;
116 }
117 while( * _buffer ) {
118 if ( *_buffer == ' ' || *_buffer == '\t' ) {
119 ++_buffer;
120 } else if ( *_buffer == ';' ) {
121 return 1;
122 } else {
123 return 0;
124 }
125 }
126 return 0;
127}
128
129/* number of lines changed */
130static int change = 0;
131static int peephole_pass = 0;
132static int num_dp = 0; /* number of variables relocated to direct-page */
133static int num_inlined = 0; /* number of variables inlined */
134static int num_unread = 0; /* number of variables not read */
135
136#ifdef __GNUC__
137static void optim(POBuffer buf, const char *rule, const char *repl, ...)
138 __attribute__ ((format (printf, 3, 4)));
139#endif
140
141#define R__(X) #X
142#define R_(X) R__(X)
143#define RULE "r" R_(__LINE__) " "
144
145/* replaces the POBuffer with an optimized code */
146/* original POBuffer is kept as comment */
147static void optim(POBuffer buf, const char *rule, const char *repl, ...) {
148 va_list ap;
149 POBuffer tmp = TMP_BUF;
150 char *s;
151
152 va_start(ap, repl);
153 po_buf_cpy(tmp, "");
154
155 /* add our own comment if any */
156 if(rule) po_buf_printf(tmp, "; peephole(%d): %s\n", peephole_pass, rule);
157
158 /* comment out line */
159 po_buf_cat(tmp, ";");
160
161 /* copy upto the end of string or upto end of string */
162 if ( (s = strchr(buf->str, '\n')) != NULL) *s = '\0'; /* cut at \n */
163 po_buf_cat(tmp, buf->str);
164 if( s != NULL ) po_buf_add(tmp, *s++ = '\n'); /* restore \n */
165
166 /* insert replacement if provided */
167 if(repl) {
168 po_buf_vprintf(tmp, repl, ap);
169 po_buf_cat(tmp, "\n");
170 }
171
172 /* copy remaining comments */
173 if(s) po_buf_cat(tmp, s);
174
175 /* write result back into input POBuffer */
176 po_buf_cpy(buf, tmp->str);
177
178 /* one more change */
179 ++change;
180
181 va_end(ap);
182}
183
184/* returns true if the POBuffer matches a zero value */
185static int isZero(char *s) {
186 if(*s == '$') ++s;
187 while(*s == '0') ++s;
188 return _eq(' ', *s);
189}
190static int _isZero(POBuffer buf) {
191 return buf!=NULL && isZero(buf->str);
192}
193
194/* perform basic peephole optimization with a length-4 look-ahead */
195static void basic_peephole(POBuffer buf[LOOK_AHEAD], int zA, int zB) {
196 /* allows presumably safe operations */
197 int unsafe = ALLOW_UNSAFE;
198
199 /* various local buffers */
200 POBuffer v1 = TMP_BUF;
201 POBuffer v2 = TMP_BUF;
202 POBuffer v3 = TMP_BUF;
203 POBuffer v4 = TMP_BUF;
204
205 /* a bunch of rules */
206
207 // ;Instead of:
208 // ld a,0
209 // ;Try this:
210 // xor a ;disadvantages: changes flags
211 // ;or
212 // sub a ;disadvantages: changes flags
213 // ; -> save 1 byte and 3 T-states
214 if( po_buf_match( buf[0], " LD *, 0", v1) && strchr( "A", _toUpper(*v1->str) ) != NULL ) {
215 optim( buf[0], RULE "(LD A, 0)->(XOR A)", "\tXOR %c", _toUpper(*v1->str) );
216 }
217
218 // ;Instead of
219 // ld b,$20
220 // ld c,$30
221 // ;try this
222 // ld bc,$2030
223 // ;or this
224 // ld bc,(b_num * 256) + c_num ;where b_num goes to b register and c_num to c register
225 // ; -> save 1 byte and 4 T-states
226 if( po_buf_match( buf[0], " LD B, $*", v1) && po_buf_match( buf[1], " LD C, $*", v2) ) {
227 optim( buf[0], RULE "(LD B, x; LD C, x)->(LD BC, xx)", "\tLD BC, ($%s * 256) + $%s", v1->str, v2->str );
228 optim( buf[1], "", NULL );
229 }
230
231 //
232 if( po_buf_match( buf[0], " LD A, *", v1) &&
233 po_buf_match( buf[1], " LD (*), A", v2) &&
234 po_buf_match( buf[2], " LD A, *", v3) &&
235 po_buf_strcmp(v1,v3)==0
236 ) {
237 optim( buf[2], RULE "(LD A, x; LD (x), A; LD A, x)->(LD A, x; LD (x), A)", NULL );
238 }
239
240 if( po_buf_match(buf[0], " LD HL, $*", v1)
241 && po_buf_match(buf[1], " LD A, $*", v2)
242 && (strtol(v1->str, NULL, 16) == strtol(v2->str, NULL, 16))
243 ) {
244 optim(buf[0], RULE "(LD HL, LD A, same value)->(LD A)", NULL );
245 }
246
247 // ;Instead of
248 // ld a,$42
249 // ld (hl),a
250 // ;try this
251 // ld (hl),$42
252 // ; -> save 1 byte and 4 T-states
253 // if( po_buf_match( buf[0], " LD A, $*", v1) &&
254 // po_buf_match( buf[1], " LD (HL), A")
255 // ) {
256 // optim( buf[0], RULE "(LD A, x; LD (HL), A)->(LD (HL), x)", "\tLD HL, $%s", v1->str );
257 // optim( buf[1], NULL, NULL );
258 // }
259
260 // ;Instead of
261 // ld A, (var)
262 // inc a
263 // ld (var),a
264 // ;try this ;Note: if hl is not tied up, use indirection:
265 // ld hl,var
266 // inc (hl)
267 // ld A, (hl) ;if you don't need (hl) in a, delete this line
268 // ; -> save 2 bytes and 2 T-states
269 // if( po_buf_match( buf[0], " LD A, (*)", v1) &&
270 // po_buf_match( buf[1], " INC A") &&
271 // po_buf_match( buf[2], " LD (*), A", v2 ) &&
272 // po_buf_strcmp( v1, v2 ) == 0
273 // ) {
274 // optim( buf[0], RULE "(LD A, (x); INC A; LD (x), A)->(LD HL, x; INC (HL); LD A, (HL)", "\tLD HL, %s", v1->str );
275 // optim( buf[1], NULL, "\tINC (HL)" );
276 // optim( buf[2], NULL, "\tLD A, (HL)" );
277 // }
278
279 // ; Instead of :
280 // ld a, (hl)
281 // ld (de), a
282 // inc hl
283 // inc de
284 // ; Use :
285 // ldi
286 // inc bc
287 // ; -> save 1 byte and 4 T-states
288 // if( po_buf_match( buf[0], " LD A, (HL)") &&
289 // po_buf_match( buf[1], " LD (DE), A" ) &&
290 // po_buf_match( buf[2], " INC HL" ) &&
291 // po_buf_match( buf[3], " INC DE" )
292 // ) {
293 // optim( buf[0], RULE "(LD A, (HL); LD (DE), A; INC HL; INC DE)->(LDI; INC BC)", "\tLDI" );
294 // optim( buf[1], NULL, "\tINC BC" );
295 // optim( buf[2], NULL, NULL );
296 // optim( buf[3], NULL, NULL );
297 // }
298
299 // ;Instead of:
300 // cp 0
301 // ;Use
302 // or a
303 // ; -> save 1 byte and 3 T-states
304 // if( po_buf_match( buf[0], " CP 0") ) {
305 // optim( buf[0], RULE "(CP 0)->(OR A)", "\tOR A" );
306 // }
307
308 // xor %11111111
309 // ; >
310 // cpl
311 // ; -> save 1 byte and 3 T-states
312 if( po_buf_match( buf[0], " XOR $FF") ) {
313 optim( buf[0], RULE "(XOR $FF)->(CPL)", "\tCPL" );
314 }
315
316 // ;Instead of
317 // ld de,767
318 // or a ;reset carry so sbc works as a sub
319 // sbc hl,de
320 // ;try this
321 // ld de,-767 ;negation of de
322 // add hl,de
323 // ; -> 2 bytes and 8 T-states !
324 // if(
325 // po_buf_match( buf[0], " LD DE, $*", v1 ) &&
326 // po_buf_match( buf[1], " OR A" ) &&
327 // po_buf_match( buf[2], " SBC HL, DE" )
328 // ) {
329 // optim( buf[0], RULE "(LD DE, x; OR A; SBC HL, DE)->(LD DE, -x; ADD HL, DE)", "\tLD DE, -$%s", v1->str );
330 // optim( buf[1], NULL, "\tADD HL, DE" );
331 // }
332
333 // ;Instead of
334 // sla l
335 // rl h ; I've actually seen this!
336 // ; >
337 // add hl,hl
338 // ; -> save 3 bytes and 5 T-states
339 if(
340 po_buf_match( buf[0], " SLA L" ) &&
341 po_buf_match( buf[1], " RL H" )
342 ) {
343 optim( buf[0], RULE "(SLA+RL)->(ADD)", "\tADD HL, HL" );
344 optim( buf[1], NULL, NULL );
345 }
346
347 // ; Instead of
348 // and 1
349 // cp 1
350 // jr z,foo
351 // ; >
352 // and 1 ;and sets zero flag, no need for cp
353 // jr nz,foo
354 // ; -> save 2 bytes and 7 T-states
355 // if(
356 // po_buf_match( buf[0], " AND $*", v1 ) &&
357 // po_buf_match( buf[1], " CP $*", v2 ) &&
358 // po_buf_match( buf[2], " JR Z, *", v3 ) &&
359 // po_buf_strcmp( v1, v2 ) == 0
360 // ) {
361 // optim( buf[1], NULL, NULL );
362 // optim( buf[2], RULE "(AND+CP+JZ)->(AND+JNZ)", " JR NZ, %s", v3->str );
363 // }
364
365 // ;Instead of
366 // call xxxx
367 // ret
368 // ;try this
369 // jp xxxx
370 // ;only do this if the pushed pc to stack is not passed to the call. Example: some kind of inline vputs.
371 // ; -> save 1 byte and 17 T-states
372 if(
373 po_buf_match( buf[0], " CALL *", v1 ) &&
374 po_buf_match( buf[1], " RET" )
375 ) {
376 optim( buf[0], RULE "(CALL+RET)->(JP)", "\tJP %s", v1->str );
377 optim( buf[1], NULL, NULL );
378 }
379
380 // ;Never use:
381 // dec B
382 // jr NZ,loop ;I have seen this...
383 // ;Use:
384 // djnz loop
385 // ; save 1 byte and 3 T-states
386 if(
387 po_buf_match( buf[0], " DEC B" ) &&
388 po_buf_match( buf[1], " JR NZ, *", v1 )
389 ) {
390 optim( buf[0], RULE "(DEC B+JR NZ)->(DJNZ)", "\tDJNZ %s", v1->str );
391 optim( buf[1], NULL, NULL );
392 }
393
394 if(
395 po_buf_match( buf[0], " LD A, *", v1 ) &&
396 po_buf_match( buf[2], " LD A, *", v3 ) &&
397 po_buf_strcmp( v1, v3 ) == 0
398 ) {
399 optim( buf[2], RULE "(LD A, *; LD A, *)->(LD, A)", NULL );
400 }
401
402}
403
404/* optimizations related to variables */
405
406/* variables database */
407static struct {
408 struct var {
409 char *name;
410#define NO_REORG 1
411#define NO_DP 2
412#define NO_INLINE 4
413#define NO_REMOVE 8
414 int flags;
415 int size;
416 int nb_rd;
417 int nb_wr;
418 int offset; /* 0=unchanged, >0 offset to page 0; -1 = candidate for inlining, -2 = inlined */
419 char *init;
420 } *tab;
422 int size;
424} vars;
425
426/* clears the database */
427static void vars_clear(void) {
428 int i;
429 for(i=0; i<vars.size; ++i) {
430 struct var *v = &vars.tab[i];
431 free(v->name);
432 if(v->init) free(v->init);
433 }
434 vars.size = 0;
435 vars.page0_max = 0;
436}
437
438/* gets (or creates) an entry for a variable from the data-base */
439struct var *vars_get(POBuffer _name) {
440 char *name = _name->str;
441 struct var *ret = NULL;
442 int i;
443
444 char *s=strchr(name,'+');
445 if(s) *s='\0';
446
447 for(i=0; i<vars.size ; ++i) {
448 if(strcmp(vars.tab[i].name, name)==0) {
449 ret = &vars.tab[i];
450 }
451 }
452 if(ret == NULL) {
453 if(vars.size == vars.capacity) {
454 vars.capacity += 16;
455 vars.tab = realloc(vars.tab, sizeof(*vars.tab)*vars.capacity);
456 }
457 ret = &vars.tab[vars.size++];
458 ret->name = strdup(name);
459 ret->flags = 0;
460 ret->size = 0;
461 ret->nb_rd = 0;
462 ret->nb_wr = 0;
463 ret->offset = 0;
464 ret->init = NULL;
465 }
466 if(s) *s='+';
467
468 return ret;
469}
470
471static int vars_ok(POBuffer name) {
472
473 if(po_buf_match(name, "^_Tstr")) return 0;
474 if(po_buf_match(name, "_^_Tstr")) return 0;
475 if(po_buf_match(name, "_label")) return 0;
476
477 if(name->str[0]=='_') return 1;
478 if(po_buf_match(name, "CLIP")) return 1;
479 if(po_buf_match(name, "XCUR")) return 1;
480 if(po_buf_match(name, "YCUR")) return 1;
481 if(po_buf_match(name, "CURRENT")) return 1;
482 if(po_buf_match(name, "FONT")) return 1;
483 if(po_buf_match(name, "TEXT")) return 1;
484 if(po_buf_match(name, "LAST")) return 1;
485 if(po_buf_match(name, "XGR")) return 1;
486 if(po_buf_match(name, "YGR")) return 1;
487 if(po_buf_match(name, "FREE_")) return 1;
488
489 return 0;
490}
491
492/* look for variable uses and collect data about he variables */
493static void vars_scan(POBuffer buf[LOOK_AHEAD]) {
494 POBuffer tmp = TMP_BUF;
495 POBuffer arg = TMP_BUF;
496 POBuffer ofs = TMP_BUF;
497
498 // if( match( buf[0], " * _*+", NULL, buf) ) {
499 // struct var *v = vars_get(buf);
500 // v->flags |= NO_INLINE;
501 // }
502
503 if( po_buf_match( buf[0], " LD *, (*+*)", tmp, arg, ofs ) ) { if(vars_ok(arg)) {
504 struct var *v = vars_get(arg);
505 v->nb_rd++;
506 } } else if( po_buf_match( buf[0], " LD *, (*)", tmp, arg ) ) { if(vars_ok(arg)) {
507 struct var *v = vars_get(arg);
508 v->nb_rd++;
509 } }
510
511 if( po_buf_match( buf[0], " LD *, *", tmp, arg ) &&
512 strstr("A B C D E AD BC DE HL IX IY", tmp->str)!=NULL
513 ) if(vars_ok(arg)) {
514 struct var *v = vars_get(arg);
515 v->nb_rd++;
516 }
517
518 if (po_buf_match( buf[0], " LD (*+*), *", arg, ofs, tmp) ) { if(vars_ok(arg)) {
519 struct var *v = vars_get(arg);
520 v->nb_wr++;
521 } } else if (po_buf_match( buf[0], " LD (*), *", arg, tmp) ) { if(vars_ok(arg)) {
522 struct var *v = vars_get(arg);
523 v->nb_wr++;
524 } }
525
526 if (po_buf_match( buf[0], " LD *, *", arg, tmp) &&
527 strstr("A B C D E AD BC DE HL IX IY", tmp->str)!=NULL
528 ) if(vars_ok(arg)) {
529 struct var *v = vars_get(arg);
530 v->nb_wr++;
531 }
532
533 if( po_buf_match( buf[0], " *: defs *", tmp, arg) && vars_ok(tmp)) {
534 struct var *v = vars_get(tmp);
535 v->size = atoi(arg->str);
536 v->init = strdup("1-1");
537 }
538
539 if( po_buf_match(buf[0], " *: defb *", tmp, arg) && vars_ok(tmp) && strchr(buf[0]->str,',')==NULL) {
540 struct var *v = vars_get(tmp);
541 v->size = 1;
542 v->init = strdup(isZero(arg->str) ? "1-1" : arg->str);
543 }
544
545 if( po_buf_match(buf[0], " *: defw *", tmp, arg) && vars_ok(tmp) && strchr(buf[0]->str,',')==NULL) {
546 struct var *v = vars_get(tmp);
547 v->size = 2;
548 v->init = strdup(arg->str);
549 }
550
551}
552
553/* compares two variables according to their access-count */
554static int vars_cmp(const void *_a, const void *_b) {
555 const struct var *a = _a;
556 const struct var *b = _b;
557
558 int diff = ((a->nb_rd + a->nb_wr) - (b->nb_rd + b->nb_wr));
559
560 return -(diff!=0 ? diff : strcmp(a->name, b->name)); // Ttmp < Tstr
561}
562
563/* removes unread variables */
564static void vars_remove(Environment * _environment, POBuffer buf[LOOK_AHEAD]) {
565 POBuffer var = TMP_BUF;
566 POBuffer op = TMP_BUF;
567 POBuffer ofs = TMP_BUF;
568
569 if(!DO_UNREAD) return;
570
571 /* unread */
572 if(po_buf_match( buf[0], " LD (*+*), *", var, ofs, op) && vars_ok(var)) {
573 struct var *v = vars_get(var);
574 if(v->nb_rd == 0 && v->offset!=-2) {
575 v->offset = 0;
576 optim(buf[0], "unread", NULL);
577 ++_environment->removedAssemblyLines;
578 }
579 } else if(po_buf_match( buf[0], " LD (*), *", var, op) && vars_ok(var)) {
580 struct var *v = vars_get(var);
581 if(v->nb_rd == 0 && v->offset!=-2) {
582 v->offset = 0;
583 optim(buf[0], "unread", NULL);
584 ++_environment->removedAssemblyLines;
585 }
586 }
587
588 /* remove changed variables */
589 if(po_buf_match( buf[0], " *: DEFS ", var)
590 || po_buf_match( buf[0], " *: DEFB ", var)
591 || po_buf_match( buf[0], " *: DEFW ", var) ) if(vars_ok(var)) {
592 struct var *v = vars_get(var);
593 if(v->nb_rd==0 && 0<v->size && v->size<=4 && 0==(v->flags & NO_REMOVE) && v->offset!=-2) {
594 optim(buf[0], "unread",NULL);
595 ++num_unread;
596 ++_environment->removedAssemblyLines;
597 }
598 }
599}
600
601/* collapse all heading spaces into a single tabulation */
602static void out(FILE *f, POBuffer _buf) {
603 char *s = _buf->str;
604 int tab = 0;
605 while(*s==' ' || *s=='\t') {tab = 1; ++s;}
606 if(tab) fputs("\t", f);
607 fputs(s, f);
608}
609
610/* remove space that is sometimes used in indexing mode and makes the optimized produce bad dcode */
611static void fixes_indexed_syntax(POBuffer buf) {
612 char *s = buf->str;
613
614 /* not an instruction */
615 if(!_eq(' ', *s)) return;
616
617 /* skip over spaces */
618 do ++s; while(*s && _eq(' ', *s));
619
620 /* comment */
621 if(*s==';') return;
622
623 /* skip over instruction */
624 while(*s && !_eq(' ', *s)) ++s;
625 if(!*s) return;
626
627 /* skip over spaces */
628 do ++s; while(*s && _eq(' ', *s));
629 if(!*s) return;
630
631 /* process argment */
632 do ++s; while(*s && !_eq(' ', *s));
633 if(!*s) return;
634
635}
636
637/* various kind of optimization */
638static int optim_pass( Environment * _environment, POBuffer buf[LOOK_AHEAD], PeepHoleOptimizationKind kind) {
639 char fileNameOptimized[MAX_TEMPORARY_STORAGE];
640 FILE * fileAsm;
641 FILE * fileOptimized;
642 int i;
643 int still_to_go = LOOK_AHEAD;
644
645 int line = 0;
646 int zA = 0, zB = 0;
647
648 int sourceLine = -1;
649
650 _environment->currentSourceLineAnalyzed = 0;
651 _environment->removedAssemblyLines = 0;
652
653 adiline2( "POP:0:%d:%d", peephole_pass, kind );
654
655 sprintf( fileNameOptimized, "%s.asm", get_temporary_filename( _environment ) );
656
657 /* prepare for phase */
658 switch(kind) {
659 case DEADVARS:
660 ++peephole_pass;
661 num_unread = 0;
662 break;
663
664 case RELOCATION1:
665 ++peephole_pass;
666 // vars_prepare_relocation();
667 break;
668
669 case RELOCATION2:
670 break;
671
672 case PEEPHOLE:
673 ++peephole_pass;
674 // vars_clear();
675 break;
676 }
677
678 fileAsm = fopen( _environment->asmFileName, "rt" );
679 if(fileAsm == NULL) {
680 perror(_environment->asmFileName);
681 exit(-1);
682 }
683
684 fileOptimized = fopen( fileNameOptimized, "wt" );
685 if(fileOptimized == NULL) {
686 perror(fileNameOptimized);
687 exit(-1);
688 }
689
690 /* clears our look-ahead buffers */
691 for(i = 0; i<LOOK_AHEAD; ++i) po_buf_cpy(buf[i], "");
692
693 /* global change flag */
694 change = 0;
695
696 while( still_to_go ) {
697 /* print out oldest POBuffer */
698 if ( line >= LOOK_AHEAD ) out(fileOptimized, buf[0]);
699
700 /* shift the buffers */
701 for(i=0; i<LOOK_AHEAD-1; ++i) po_buf_cpy(buf[i], buf[i+1]->str);
702
703 /* read next line, merging adjacent comments */
704 if(feof(fileAsm)) {
705 --still_to_go;
706 po_buf_cpy(buf[LOOK_AHEAD-1], "");
707 } else do {
708 /* read next line */
709 po_buf_fgets( buf[LOOK_AHEAD-1], fileAsm );
710 fixes_indexed_syntax(buf[LOOK_AHEAD-1]);
711 /* merge comment with previous line if we do not overflow the POBuffer */
712 if(isAComment(buf[LOOK_AHEAD-1])) {
713 POBuffer ln = TMP_BUF;
714 if (po_buf_match( buf[LOOK_AHEAD-1], " ; L:*", ln ) ) {
715 sourceLine = atoi( ln->str );
716 if ( ( sourceLine != _environment->currentSourceLineAnalyzed ) ) {
717 adiline3( "POL:0:%d:%d:%d",
718 peephole_pass, _environment->currentSourceLineAnalyzed, _environment->removedAssemblyLines );
719 _environment->currentSourceLineAnalyzed = sourceLine;
720 _environment->removedAssemblyLines = 0;
721 }
722 }
723 if(KEEP_COMMENTS) po_buf_cat(buf[LOOK_AHEAD-2], buf[LOOK_AHEAD-1]->str);
724 po_buf_cpy(buf[LOOK_AHEAD-1], "");
725 } else break;
726 } while(!feof(fileAsm));
727
728 switch(kind) {
729 case PEEPHOLE:
730 basic_peephole(buf, zA, zB);
731
732 /* only look fo variable when no peephole has been performed */
733 if(change == 0) vars_scan(buf);
734 break;
735
736 case DEADVARS:
737 vars_remove(_environment, buf);
738 break;
739
740 case RELOCATION1:
741 case RELOCATION2:
742 // vars_relocate(buf);
743 break;
744 }
745
746 ++line;
747 }
748
749 adiline3( "POL:0:%d:%d:%d",
750 peephole_pass, _environment->currentSourceLineAnalyzed, _environment->removedAssemblyLines );
751
752 /* log info at the end of the file */
753 switch(kind) {
754 case PEEPHOLE:
755 fprintf(fileOptimized, "; peephole: pass %d, %d change%s.\n", peephole_pass,
756 change, change>1 ?"s":"");
757 break;
758
759 case DEADVARS:
760 fprintf(fileOptimized, "; peephole: pass %d, %d var%s removed.\n", peephole_pass,
761 num_unread, num_unread>1 ?"s":"");
762 break;
763
764 case RELOCATION2:
765 fprintf(fileOptimized, "; peephole: pass %d, %d var%s moved to dp, %d var%s inlined.\n", peephole_pass,
766 num_dp, num_dp>1 ?"s":"",
767 num_inlined, num_inlined>1 ? "s":"");
768 break;
769
770 default:
771 break;
772 }
773
774 (void)fclose(fileAsm);
775 (void)fclose(fileOptimized);
776
777 /* makes our generated file the new asm file */
778 remove(_environment->asmFileName);
779 BUILD_SAFE_MOVE( _environment, fileNameOptimized, _environment->asmFileName );
780
781 return change;
782}
783
784/* main entry-point for this service */
786 if ( _environment->peepholeOptimizationLimit > 0 ) {
787 POBuffer buf[LOOK_AHEAD];
788 int i;
789
790 for(i=0; i<LOOK_AHEAD; ++i) buf[i] = po_buf_new(0);
791
792 int optimization_limit_count = _environment->peepholeOptimizationLimit;
793
794 do {
795 while(optim_pass(_environment, buf, PEEPHOLE)&&optimization_limit_count) {
796 --optimization_limit_count;
797 };
798 optim_pass(_environment, buf, DEADVARS);
799 } while(change&&optimization_limit_count);
800 optim_pass(_environment, buf, RELOCATION1);
801 optim_pass(_environment, buf, RELOCATION2);
802
803 for(i=0; i<LOOK_AHEAD; ++i) buf[i] = po_buf_del(buf[i]);
805 }
806}
807
808void target_finalize( Environment * _environment ) {
809
810 if ( _environment->additionalInfoFile ) {
811
812 char fileNameOptimized[MAX_TEMPORARY_STORAGE];
813 FILE * fileAsm;
814 FILE * fileListing;
815 POBuffer bufferAsm = TMP_BUF;
816 POBuffer bufferListing = TMP_BUF;
817 POBuffer bufferLine = TMP_BUF;
818 POBuffer bufferAddress = TMP_BUF;
819 POBuffer bufferBytes = TMP_BUF;
820
821 int sourceLine = -1;
822
823 _environment->currentSourceLineAnalyzed = 0;
824 _environment->bytesProduced = 0;
825
826 adiline0( "A:0" );
827
828 fileAsm = fopen( _environment->asmFileName, "rb" );
829 if(fileAsm == NULL) {
830 perror(_environment->asmFileName);
831 exit(-1);
832 }
833
834 fileListing = fopen( _environment->listingFileName, "rb" );
835 if(fileListing == NULL) {
836 perror(_environment->listingFileName);
837 exit(-1);
838 }
839
840 while( !feof(fileAsm) && !feof(fileListing)) {
841
842 po_buf_fgets( bufferAsm, fileAsm );
843 int leftPadding = po_buf_trim( bufferAsm );
844
845 if ( isAComment( bufferAsm ) ) {
846 POBuffer ln = TMP_BUF;
847 if (po_buf_match( bufferAsm, "; L:*", ln ) ) {
848 sourceLine = atoi( ln->str );
849 if ( ( sourceLine != _environment->currentSourceLineAnalyzed ) ) {
850 adiline2( "AB:0:%d:%d",
851 _environment->currentSourceLineAnalyzed, _environment->bytesProduced );
852 _environment->currentSourceLineAnalyzed = sourceLine;
853 _environment->bytesProduced = 0;
854 }
855 }
856 continue;
857 }
858
859 *bufferListing->str = 0;
860 int pos = ftell( fileListing );
861 po_buf_trim( bufferAsm );
862 while( !feof(fileListing) && (strstr( bufferListing->str, bufferAsm->str ) == NULL) ) {
863 po_buf_fgets( bufferListing, fileListing );
864 po_buf_trim( bufferListing );
865 }
866
867 if ( feof(fileListing) ) {
868
869 } else {
870 pos = ftell( fileListing );
871 char * bufferAsmEscaped = strdup( bufferAsm->str );
872 for( int i=0, c=strlen(bufferAsmEscaped); i<c; ++i ) {
873 if ( bufferAsmEscaped[i] == ':' ) {
874 bufferAsmEscaped[i] = 6;
875 }
876 if ( bufferAsmEscaped[i] == 9 ) {
877 bufferAsmEscaped[i] = ' ';
878 }
879 }
880 if ( po_buf_match( bufferListing, "* * * ", bufferLine, bufferAddress, bufferBytes ) ) {
881 _environment->bytesProduced += bufferBytes->len >> 1;
882 }
883 adiline4( "AL:0:%d:%*s%s",
884 _environment->currentSourceLineAnalyzed, leftPadding, "", bufferAsmEscaped );
885 }
886
887 fseek( fileListing, pos, SEEK_SET );
888
889 }
890
891 if ( _environment->currentSourceLineAnalyzed ) {
892 adiline1( "AF:0:%d",
893 _environment->bytesProduced );
894 }
895
896 (void)fclose(fileListing);
897 (void)fclose(fileAsm);
898
899 }
900
901}
char * get_temporary_filename(Environment *_environment)
int size
Definition _optimizer.c:678
struct @110344003176124301103124235173106120065272160135::var * tab
void target_finalize(Environment *_environment)
void target_peephole_optimizer(Environment *_environment)
struct var * vars_get(POBuffer _name)
Definition _optimizer.c:702
char * name
Definition _optimizer.c:672
#define ALLOW_UNSAFE
Definition _optimizer.c:86
int page0_max
Definition _optimizer.c:686
#define DO_UNREAD
Definition _optimizer.c:91
char * init
Definition _optimizer.c:682
int nb_rd
Definition _optimizer.c:679
int nb_wr
Definition _optimizer.c:680
#define RULE
Definition _optimizer.c:144
int flags
Definition _optimizer.c:677
#define NO_REMOVE
Definition _optimizer.c:676
int isAComment(POBuffer buf)
Definition _optimizer.c:109
int capacity
Definition _optimizer.c:684
#define KEEP_COMMENTS
Definition _optimizer.c:87
int offset
Definition _optimizer.c:681
#define LOOK_AHEAD
Definition _optimizer.c:85
POBuffer po_buf_match(POBuffer _buf, const char *_pattern,...)
Definition _optimizer.c:271
POBuffer po_buf_new(int size)
Definition _optimizer.c:62
POBuffer po_buf_printf(POBuffer buf, const char *fmt,...)
Definition _optimizer.c:129
int po_buf_strcmp(POBuffer _s, POBuffer _t)
Definition _optimizer.c:251
POBuffer po_buf_cpy(POBuffer buf, char *string)
Definition _optimizer.c:94
POBuffer po_buf_del(POBuffer buf)
Definition _optimizer.c:49
POBuffer po_buf_add(POBuffer buf, char c)
Definition _optimizer.c:100
int po_buf_trim(POBuffer buf)
Definition _optimizer.c:157
POBuffer po_buf_vprintf(POBuffer buf, const char *fmt, va_list ap)
Definition _optimizer.c:111
POBuffer po_buf_cat(POBuffer buf, char *string)
Definition _optimizer.c:83
POBuffer po_buf_fgets(POBuffer buf, FILE *f)
Definition _optimizer.c:138
int currentSourceLineAnalyzed
Definition ugbc.h:3025
char * listingFileName
Definition ugbc.h:2305
FILE * additionalInfoFile
Definition ugbc.h:3330
int peepholeOptimizationLimit
Definition ugbc.h:2370
int removedAssemblyLines
Definition ugbc.h:3030
int bytesProduced
Definition ugbc.h:3035
char * asmFileName
Definition ugbc.h:2285
char * str
Definition ugbc.h:293
int len
Definition ugbc.h:294
void free(void *)
#define adiline4(s, a, b, c, d)
Definition ugbc.h:4202
#define adiline0(s)
Definition ugbc.h:4182
#define MAX_TEMPORARY_STORAGE
Definition ugbc.h:563
#define adiline1(s, a)
Definition ugbc.h:4187
enum _PeepHoleOptimizationKind PeepHoleOptimizationKind
struct _POBuffer * POBuffer
Definition ugbc.h:298
struct _Environment Environment
Structure of compilation environment.
#define adiline3(s, a, b, c)
Definition ugbc.h:4197
#define TMP_BUF_CLR
Definition ugbc.h:5171
#define BUILD_SAFE_MOVE(_environment, source, destination)
Definition ugbc.h:4751
#define TMP_BUF
Definition ugbc.h:5170
@ RELOCATION2
Definition ugbc.h:282
@ DEADVARS
Definition ugbc.h:280
@ RELOCATION1
Definition ugbc.h:281
@ PEEPHOLE
Definition ugbc.h:279
#define adiline2(s, a, b)
Definition ugbc.h:4192