#include #include "c.h" #include "expr.h" #include "gen.h" #include "cglbdec.h" /* * 68000 C compiler * * Copyright 1984, 1985, 1986 Matthew Brandt. * all commercial rights reserved. * * This compiler is intended as an instructive tool for personal use. Any * use for profit without the written consent of the author is prohibited. * * This compiler may be distributed freely for non-commercial use as long * as this notice stays intact. Please forward any enhancements or questions * to: * * Matthew Brandt * Box 920337 * Norcross, Ga 30092 */ extern struct amode push[], pop[]; /* * this module will step through the parse tree and find all * optimizable expressions. at present these expressions are * limited to expressions that are valid throughout the scope * of the function. the list of optimizable expressions is: * * constants * global and static addresses * auto addresses * contents of auto addresses. * * contents of auto addresses are valid only if the address is * never referred to without dereferencing. * * scan will build a list of optimizable expressions which * opt1 will replace during the second optimization pass. */ static struct cse *olist; /* list of optimizable expressions */ equalnode(node1, node2) /* * equalnode will return 1 if the expressions pointed to by * node1 and node2 are equivalent. */ struct enode *node1, *node2; { if( node1 == 0 || node2 == 0 ) return 0; if( node1->nodetype != node2->nodetype ) return 0; if( (node1->nodetype == en_icon || node1->nodetype == en_labcon || node1->nodetype == en_nacon || node1->nodetype == en_autocon) && node1->v.i == node2->v.i ) return 1; if( lvalue(node1) && equalnode(node1->v.p[0], node2->v.p[0]) ) return 1; return 0; } struct cse *searchnode(node) /* * searchnode will search the common expression table for an entry * that matches the node passed and return a pointer to it. */ struct enode *node; { struct cse *csp; csp = olist; while( csp != 0 ) { if( equalnode(node,csp->exp) ) return csp; csp = csp->next; } return 0; } struct enode *copynode(node) /* * copy the node passed into a new enode so it wont get * corrupted during substitution. */ struct enode *node; { struct enode *temp; if( node == 0 ) return 0; temp = xalloc(sizeof(struct enode)); temp->nodetype = node->nodetype; temp->v.p[0] = node->v.p[0]; temp->v.p[1] = node->v.p[1]; return temp; } enternode(node,duse) /* * enternode will enter a reference to an expression node into the * common expression table. duse is a flag indicating whether or not * this reference will be dereferenced. */ struct enode *node; int duse; { struct cse *csp; if( (csp = searchnode(node)) == 0 ) { /* add to tree */ csp = xalloc(sizeof(struct cse)); csp->next = olist; csp->uses = 1; csp->duses = (duse != 0); csp->exp = copynode(node); csp->voidf = 0; olist = csp; return csp; } ++(csp->uses); if( duse ) ++(csp->duses); return csp; } struct cse *voidauto(node) /* * voidauto will void an auto dereference node which points to * the same auto constant as node. */ struct enode *node; { struct cse *csp; csp = olist; while( csp != 0 ) { if( lvalue(csp->exp) && equalnode(node,csp->exp->v.p[0]) ) { if( csp->voidf ) return 0; csp->voidf = 1; return csp; } csp = csp->next; } return 0; } scanexpr(node,duse) /* * scanexpr will scan the expression pointed to by node for optimizable * subexpressions. when an optimizable expression is found it is entered * into the tree. if a reference to an autocon node is scanned the * corresponding auto dereferenced node will be voided. duse should be * set if the expression will be dereferenced. */ struct enode *node; { struct cse *csp, *csp1; int n; if( node == 0 ) return; switch( node->nodetype ) { case en_icon: case en_labcon: case en_nacon: enternode(node,duse); break; case en_autocon: if( (csp = voidauto(node)) != 0 ) { csp1 = enternode(node,duse); csp1->uses = (csp1->duses += csp->uses); } else enternode(node,duse); break; case en_b_ref: case en_w_ref: case en_l_ref: if( node->v.p[0]->nodetype == en_autocon ) { csp = enternode(node,duse); if( csp->voidf ) scanexpr(node->v.p[0],1); } else scanexpr(node->v.p[0],1); break; case en_cbl: case en_cwl: case en_cbw: case en_uminus: case en_compl: case en_ainc: case en_adec: case en_not: scanexpr(node->v.p[0],duse); break; case en_asadd: case en_assub: case en_add: case en_sub: scanexpr(node->v.p[0],duse); scanexpr(node->v.p[1],duse); break; case en_mul: case en_div: case en_lsh: case en_rsh: case en_mod: case en_and: case en_or: case en_xor: case en_lor: case en_land: case en_eq: case en_ne: case en_gt: case en_ge: case en_lt: case en_le: case en_asmul: case en_asdiv: case en_asmod: case en_aslsh: case en_asrsh: case en_asand: case en_asor: case en_cond: case en_void: case en_assign: scanexpr(node->v.p[0],0); scanexpr(node->v.p[1],0); break; case en_fcall: scanexpr(node->v.p[0],1); scanexpr(node->v.p[1],0); break; } } scan(block) /* * scan will gather all optimizable expressions into the expression * list for a block of statements. */ struct snode *block; { while( block != 0 ) { switch( block->stype ) { case st_return: case st_expr: opt4(&block->exp); scanexpr(block->exp,0); break; case st_while: case st_do: opt4(&block->exp); scanexpr(block->exp,0); scan(block->s1); break; case st_for: opt4(&block->label); scanexpr(block->label,0); opt4(&block->exp); scanexpr(block->exp,0); scan(block->s1); opt4(&block->s2); scanexpr(block->s2,0); break; case st_if: opt4(&block->exp); scanexpr(block->exp,0); scan(block->s1); scan(block->s2); break; case st_switch: opt4(&block->exp); scanexpr(block->exp,0); scan(block->s1); break; case st_case: scan(block->s1); break; } block = block->next; } } exchange(c1) /* * exchange will exchange the order of two expression entries * following c1 in the linked list. */ struct cse **c1; { struct cse *csp1, *csp2; csp1 = *c1; csp2 = csp1->next; csp1->next = csp2->next; csp2->next = csp1; *c1 = csp2; } int desire(csp) /* * returns the desirability of optimization for a subexpression. */ struct cse *csp; { if( csp->voidf || (csp->exp->nodetype == en_icon && csp->exp->v.i < 16 && csp->exp->v.i >= 0)) return 0; if( lvalue(csp->exp) ) return 2 * csp->uses; return csp->uses; } int bsort(list) /* * bsort implements a bubble sort on the expression list. */ struct cse **list; { struct cse *csp1, *csp2; int i; csp1 = *list; if( csp1 == 0 || csp1->next == 0 ) return 0; i = bsort( &(csp1->next)); csp2 = csp1->next; if( desire(csp1) < desire(csp2) ) { exchange(list); return 1; } return 0; } allocate() /* * allocate will allocate registers for the expressions that have * a high enough desirability. */ { struct cse *csp; struct enode *exptr; int datareg, addreg, mask, rmask, i; struct amode *ap, *ap2; datareg = 3; addreg = 10; mask = 0; rmask = 0; while( bsort(&olist) ); /* sort the expression list */ csp = olist; while( csp != 0 ) { if( desire(csp) < 3 ) csp->reg = -1; else if( csp->duses > csp->uses / 4 && addreg < 14 ) csp->reg = addreg++; else if( datareg < 8 ) csp->reg = datareg++; else csp->reg = -1; if( csp->reg != -1 ) { rmask = rmask | (1 << (15 - csp->reg)); mask = mask | (1 << csp->reg); } csp = csp->next; } if( mask != 0 ) gen_code(op_movem,4,make_mask(rmask),push); save_mask = mask; csp = olist; while( csp != 0 ) { if( csp->reg != -1 ) { /* see if preload needed */ exptr = csp->exp; if( !lvalue(exptr) || (exptr->v.p[0]->v.i > 0) ) { initstack(); ap = gen_expr(exptr,F_ALL,4); if( csp->reg < 8 ) ap2 = makedreg(csp->reg); else ap2 = makeareg(csp->reg - 8); gen_code(op_move,4,ap,ap2); freeop(ap); } } csp = csp->next; } } repexpr(node) /* * repexpr will replace all allocated references within an expression * with tempref nodes. */ struct enode *node; { struct cse *csp; if( node == 0 ) return; switch( node->nodetype ) { case en_icon: case en_nacon: case en_labcon: case en_autocon: if( (csp = searchnode(node)) != 0 ) if( csp->reg > 0 ) { node->nodetype = en_tempref; node->v.i = csp->reg; } break; case en_b_ref: case en_w_ref: case en_l_ref: if( (csp = searchnode(node)) != 0 ) { if( csp->reg > 0 ) { node->nodetype = en_tempref; node->v.i = csp->reg; } else repexpr(node->v.p[0]); } else repexpr(node->v.p[0]); break; case en_cbl: case en_cbw: case en_cwl: case en_uminus: case en_not: case en_compl: case en_ainc: case en_adec: repexpr(node->v.p[0]); break; case en_add: case en_sub: case en_mul: case en_div: case en_mod: case en_lsh: case en_rsh: case en_and: case en_or: case en_xor: case en_land: case en_lor: case en_eq: case en_ne: case en_lt: case en_le: case en_gt: case en_ge: case en_cond: case en_void: case en_asadd: case en_assub: case en_asmul: case en_asdiv: case en_asor: case en_asand: case en_asmod: case en_aslsh: case en_asrsh: case en_fcall: case en_assign: repexpr(node->v.p[0]); repexpr(node->v.p[1]); break; } } repcse(block) /* * repcse will scan through a block of statements replacing the * optimized expressions with their temporary references. */ struct snode *block; { while( block != 0 ) { switch( block->stype ) { case st_return: case st_expr: repexpr(block->exp); break; case st_while: case st_do: repexpr(block->exp); repcse(block->s1); break; case st_for: repexpr(block->label); repexpr(block->exp); repcse(block->s1); repexpr(block->s2); break; case st_if: repexpr(block->exp); repcse(block->s1); repcse(block->s2); break; case st_switch: repexpr(block->exp); repcse(block->s1); break; case st_case: repcse(block->s1); break; } block = block->next; } } opt1(block) /* * opt1 is the externally callable optimization routine. it will * collect and allocate common subexpressions and substitute the * tempref for all occurrances of the expression within the block. * * optimizer is currently turned off... */ struct snode *block; { olist = 0; scan(block); /* collect expressions */ allocate(); /* allocate registers */ repcse(block); /* replace allocated expressions */ }