/* * C compiler, part 2 * * (long)btodb(l) produced 'no code table error for op: >>(17) type: 6' * allow both long and ulong at line ~341. 1996/6/19 */ #if !defined(lint) && defined(DOSCCS) static char sccsid[] = "@(#)c10.c 2.1 (2.11BSD GTE) 10/4/94"; #endif #include "c1.h" #ifdef DEBUG #define dbprint(op) printf(" / %s", opntab[op]) #else #define dbprint(op) /* */ #endif static int debug = 0; char maprel[] = { EQUAL, NEQUAL, GREATEQ, GREAT, LESSEQ, LESS, GREATQP, GREATP, LESSEQP, LESSP }; char notrel[] = { NEQUAL, EQUAL, GREAT, GREATEQ, LESS, LESSEQ, GREATP, GREATQP, LESSP, LESSEQP }; struct tconst czero = { CON, INT, 0}; struct tconst cone = { CON, INT, 1}; struct tname sfuncr = { NAME, STRUCT, STATIC, 0, 0, 0 }; struct table *cregtab; int nreg = 3; int isn = 10000; main(argc, argv) int argc; char *argv[]; { char buf1[BUFSIZ], buf2[BUFSIZ]; if (argc<4) { error("Arg count"); exit(1); } if (freopen(argv[1], "r", stdin)==NULL) { error("Missing temp file"); exit(1); } setbuf(stdin,buf1); /* sbrk problems */ if ((freopen(argv[3], "w", stdout)) == NULL) { error("Can't create %s", argv[3]); exit(1); } setbuf(stdout,buf2); /* sbrk problems */ funcbase = curbase = coremax = sbrk(0); getree(); /* * If any floating-point instructions * were used, generate a reference that * pulls in the floating-point part of printf. */ if (nfloat) printf(".globl fltused\n"); /* * tack on the string file. */ printf(".globl\n.data\n"); if (*argv[2] != '-') { if (freopen(argv[2], "r", stdin)==NULL) { error("Missing temp file"); exit(1); } setbuf(stdin,buf1); /* sbrk problems */ getree(); } if (totspace >= (unsigned)56000) werror("possibly too much data"); exit(nerror!=0); } /* * Given a tree, a code table, and a * count of available registers, find the code table * for the appropriate operator such that the operands * are of the right type and the number of registers * required is not too large. * Return a ptr to the table entry or 0 if none found. */ struct optab * match(tree, table, nrleft, nocvt) union tree *tree; struct table *table; { #define NOCVL 1 #define NOCVR 2 int op, d1, d2, dope; union tree *p2; register union tree *p1; register struct optab *opt; if (tree==NULL) return(NULL); if (table==lsptab) table = sptab; if ((op = tree->t.op)==0) return(0); dope = opdope[op]; if ((dope&LEAF) == 0) p1 = tree->t.tr1; else p1 = tree; d1 = dcalc(p1, nrleft); if ((dope&BINARY)!=0) { p2 = tree->t.tr2; /* * If a subtree starts off with a conversion operator, * try for a match with the conversion eliminated. * E.g. int = double can be done without generating * the converted int in a register by * movf double,fr0; movfi fr0,int . */ if (opdope[p2->t.op]&CNVRT && (nocvt&NOCVR)==0 && (opdope[p2->t.tr1->t.op]&CNVRT)==0) { tree->t.tr2 = p2->t.tr1; if (opt = match(tree, table, nrleft, NOCVL)) return(opt); tree->t.tr2 = p2; } else if (opdope[p1->t.op]&CNVRT && (nocvt&NOCVL)==0 && (opdope[p1->t.tr1->t.op]&CNVRT)==0) { tree->t.tr1 = p1->t.tr1; if (opt = match(tree, table, nrleft, NOCVR)) return(opt); tree->t.tr1 = p1; } d2 = dcalc(p2, nrleft); } for (; table->tabop!=op; table++) if (table->tabop==0) return(0); for (opt = table->tabp; opt->tabdeg1!=0; opt++) { if (d1 > (opt->tabdeg1&077) || (opt->tabdeg1 >= 0100 && (p1->t.op != STAR))) continue; if (notcompat(p1, opt->tabtyp1, opt->tabdeg1, op)) continue; if ((opdope[op]&BINARY)!=0 && p2!=0) { if (d2 > (opt->tabdeg2&077) || (opt->tabdeg2 >= 0100) && (p2->t.op != STAR) ) continue; if (notcompat(p2,opt->tabtyp2, opt->tabdeg2, 0)) continue; if ((opt->tabdeg2&077)==20 && xdcalc(p2,nrleft)>20) continue; } return(opt); } return(0); } /* * Given a tree, a code table, and a register, * produce code to evaluate the tree with the appropriate table. * Registers reg and upcan be used. * If there is a value, it is desired that it appear in reg. * The routine returns the register in which the value actually appears. * This routine must work or there is an error. * If the table called for is cctab, sptab, or efftab, * and tree can't be done using the called-for table, * another try is made. * If the tree can't be compiled using cctab, regtab is * used and a "tst" instruction is produced. * If the tree can't be compiled using sptab, * regtab is used and the register is pushed on the stack. * If the tree can't be compiled using efftab, * just use regtab. * Regtab must succeed or an "op not found" error results. * * A number of special cases are recognized, and * there is an interaction with the optimizer routines. */ rcexpr(atree, atable, reg) union tree *atree; struct table *atable; { register r; int modf, nargs, recurf; register union tree *tree; register struct table *table; table = atable; recurf = 0; if (reg<0) { recurf++; reg = ~reg; if (reg>=020) { reg -= 020; recurf++; } } again: if((tree=atree)==0) return(0); if (tree->t.type==VOID) { if (table!=efftab) error("Illegal use of void"); tree->t.type = INT; } if (opdope[tree->t.op]&RELAT && tree->t.tr2->t.op==CON && tree->t.tr2->c.value==0 && table==cctab) tree = atree = tree->t.tr1; /* * fieldselect(...) : in efftab mode, * ignore the select, otherwise * do the shift and mask. */ if (tree->t.op == FSELT) { if (table==efftab) atree = tree = tree->t.tr1; else { tree->t.op = FSEL; atree = tree = optim(tree); } } switch (tree->t.op) { /* * Structure assignments */ case STRASG: strasg(tree); return(0); /* * An initializing expression */ case INIT: tree = optim(tree); doinit(tree->t.type, tree->t.tr1); return(0); /* * Put the value of an expression in r0, * for a switch or a return */ case RFORCE: tree = tree->t.tr1; if((r=rcexpr(tree, regtab, reg)) != 0) movreg(r, 0, tree); return(0); /* * sequential execution */ case SEQNC: r = nstack; rcexpr(tree->t.tr1, efftab, reg); nstack = r; atree = tree = tree->t.tr2; goto again; /* * In the generated &~ operator, * fiddle things so a PDP-11 "bit" * instruction will be produced when cctab is used. */ case ANDN: if (table==cctab) { tree->t.op = TAND; tree->t.tr2 = optim(tnode(COMPL, tree->t.type, tree->t.tr2, TNULL)); } break; /* * Handle a subroutine call. It has to be done * here because if cexpr got called twice, the * arguments might be compiled twice. * There is also some fiddling so the * first argument, in favorable circumstances, * goes to (sp) instead of -(sp), reducing * the amount of stack-popping. */ case CALL: r = 0; nargs = 0; modf = 0; #ifdef notdef /* * The following code would catch instances of foo(...) where * "foo" was anything other than a simple name. In particular * f(...), (fp(...))(...) and (ffp(...))(...) where "f" is a * pointer to a function, "fp" is a function returning a * pointer to a function and "ffp" is a pointer to a function * returning a pointer to a function. The catch would among * other things cause the (sp)/-(sp) stack optimization to * stop working. The compiler has been tested in all these * different cases with the catch commented out and all the * code generated was correct. So what was it here for? * If a strange error crops up, uncommenting the catch might * be tried ... */ if (tree->t.tr1->t.op!=NAME || tree->t.tr1->n.class!=EXTERN) { nargs++; nstack++; } #endif tree = tree->t.tr2; if(tree->t.op) { while (tree->t.op==COMMA) { r += comarg(tree->t.tr2, &modf); tree = tree->t.tr1; nargs++; } r += comarg(tree, &modf); nargs++; } tree = atree; tree->t.op = CALL2; if (modf && tree->t.tr1->t.op==NAME && tree->t.tr1->n.class==EXTERN) tree->t.op = CALL1; if (cexpr(tree, regtab, reg)<0) error("compiler botch: call"); popstk(r); nstack -= nargs; if (table==efftab || table==regtab) return(0); r = 0; goto fixup; /* * Longs need special treatment. */ case ASULSH: /* 18 */ case ULSH: /* 17 */ if (tree->t.type != LONG && tree->t.type != UNLONG) break; if (tree->t.tr2->t.op==ITOL) tree->t.tr2 = tree->t.tr2->t.tr1; else tree->t.tr2 = optim(tnode(LTOI,INT,tree->t.tr2,TNULL)); if (tree->t.op==ASULSH) { tree->t.op = UASLSHL; tree->t.tr1 = tnode(AMPER, LONG+PTR, tree->t.tr1, TNULL); } else tree->t.op = ULLSHIFT; break; case ASLSH: case LSHIFT: if (tree->t.type==LONG || tree->t.type==UNLONG) { if (tree->t.tr2->t.op==ITOL) tree->t.tr2 = tree->t.tr2->t.tr1; else tree->t.tr2 = optim(tnode(LTOI,INT,tree->t.tr2,TNULL)); if (tree->t.op==ASLSH) tree->t.op = ASLSHL; else tree->t.op = LLSHIFT; } break; /* * Try to change * to shift. */ case TIMES: case ASTIMES: tree = pow2(tree); } /* * Try to find postfix ++ and -- operators that can be * pulled out and done after the rest of the expression */ if (table!=cctab && table!=cregtab && recurf<2 && (opdope[tree->t.op]&LEAF)==0) { if (r=delay(&atree, table, reg)) { tree = atree; table = efftab; reg = r-1; } } /* * Basically, try to reorder the computation * so reg = x+y is done as reg = x; reg += y */ if (recurf==0 && reorder(&atree, table, reg)) { if (table==cctab && atree->t.op==NAME) return(reg); } tree = atree; if (table==efftab && tree->t.op==NAME) return(reg); if ((r=cexpr(tree, table, reg))>=0) { if (table==cregtab && (tree->t.op==INCAFT || tree->t.op==DECAFT || tree->t.op==TIMES)) goto fixup; return(r); } if (table!=regtab && (table!=cctab||(opdope[tree->t.op]&RELAT)==0)) { if((r=cexpr(tree, regtab, reg))>=0) { fixup: modf = isfloat(tree); dbprint(tree->t.op); if (table==sptab || table==lsptab) { if (tree->t.type==LONG || tree->t.type==UNLONG){ printf("mov\tr%d,-(sp)\n",r+1); nstack++; } printf("mov%s r%d,%s(sp)\n", modf=='f'?"f":"", r, table==sptab? "-":""); nstack++; } if (table==cctab || table==cregtab) printf("tst%s r%d\n", modf=='f'?"f":"", r); return(r); } } /* * Special grace for unsigned chars as right operands */ if (opdope[tree->t.op]&BINARY && tree->t.tr2->t.type==UNCHAR) { tree->t.tr2 = tnode(LOAD, UNSIGN, tree->t.tr2, TNULL); return(rcexpr(tree, table, reg)); } /* * There's a last chance for this operator */ if (tree->t.op==LTOI) { r = rcexpr(tree->t.tr1, regtab, reg); if (r >= 0) { r++; goto fixup; } } r = tree->t.op; if (tree->t.type == STRUCT) error("Illegal operation on structure"); else if (r > 0 && r < UASLSHL && opntab[r]) error("No code table for op: %s(%d) type: %d", opntab[r], r, tree->t.type); else error("No code table for op %d", r); return(reg); } /* * Try to compile the tree with the code table using * registers areg and up. If successful, * return the register where the value actually ended up. * If unsuccessful, return -1. * * Most of the work is the macro-expansion of the * code table. */ cexpr(tree, table, areg) register union tree *tree; struct table *table; { int c, r; register union tree *p, *p1; struct table *ctable; union tree *p2; char *string; int reg, reg1, rreg, flag, opd; struct optab *opt; reg = areg; p1 = tree->t.tr2; c = tree->t.op; opd = opdope[c]; /* * When the value of a relational or a logical expression is * desired, more work must be done. */ if ((opd&RELAT||c==LOGAND||c==LOGOR||c==EXCLA) && table!=cctab) { cbranch(tree, c=isn++, 1, reg); rcexpr((union tree *)&czero, table, reg); branch(isn, 0, 0); label(c); rcexpr((union tree *)&cone, table, reg); label(isn++); return(reg); } if(c==QUEST) { if (table==cctab) return(-1); cbranch(tree->t.tr1, c=isn++, 0, reg); flag = nstack; rreg = rcexpr(p1->t.tr1, table, reg); nstack = flag; branch(r=isn++, 0, 0); label(c); reg = rcexpr(p1->t.tr2, table, rreg); if (rreg!=reg) movreg(reg, rreg, tree->t.tr2); label(r); return(rreg); } reg = oddreg(tree, reg); reg1 = reg+1; /* * long values take 2 registers. */ if ((tree->t.type==LONG||tree->t.type==UNLONG||opd&RELAT&&(tree->t.tr1->t.type==LONG||tree->t.tr1->t.type==UNLONG)) && tree->t.op!=ITOL) reg1++; /* * Leaves of the expression tree */ if ((r = chkleaf(tree, table, reg)) >= 0) return(r); /* * x + (-1) is better done as x-1. */ if (tree->t.op==PLUS||tree->t.op==ASPLUS) { if ((p1=tree->t.tr2)->t.op==CON && p1->c.value==-1) { p1->c.value = -p1->c.value; tree->t.op += (MINUS-PLUS); } } /* * Because of a peculiarity of the PDP11 table * char = *intreg++ and *--intreg cannot go through. */ if (tree->t.tr2 && (tree->t.tr2->t.op==AUTOI||tree->t.tr2->t.op==AUTOD) && (tree->t.tr1->t.type==CHAR || tree->t.tr1->t.type==UNCHAR) && tree->t.tr2->t.type!=CHAR && tree->t.tr2->t.type!=UNCHAR) tree->t.tr2 = tnode(LOAD, tree->t.tr2->t.type, tree->t.tr2, TNULL); /* * Another peculiarity of the PDP11 table manifested itself when * amplifying the move3: table. The same case which optimizes * u_char to char moves is used to move a u_char to a register. This * is wrong, leading to sign extension. Rather than lose the ability * to generate better code when moving a u_char to a char, a check * is made here to prevent sign extension. * * If the opcode is assign, the destination is a register and the * source is u_char then do a conversion. * * u_char handling in the compiler is a bit awkward, it would be nice * if %aub in the tables had a more unique meaning. */ if (tree->t.tr2 && tree->t.tr1->t.op == NAME && tree->t.tr1->n.class == REG && tree->t.op == ASSIGN && tree->t.tr2->t.type == UNCHAR) tree->t.tr2 = tnode(LOAD, UNSIGN, tree->t.tr2, TNULL); if (table==cregtab) table = regtab; /* * The following peculiar code depends on the fact that * if you just want the codition codes set, efftab * will generate the right code unless the operator is * a shift or * postfix ++ or --. Unravelled, if the table is * cctab and the operator is not special, try first * for efftab; if the table isn't, if the operator is, * or the first match fails, try to match * with the table actually asked for. */ /* * Account for longs and oddregs; below is really * r = nreg - reg - (reg-areg) - (reg1-reg-1); */ r = nreg - reg + areg - reg1 + 1; if (table!=cctab || c==INCAFT || c==DECAFT || tree->t.type==LONG || tree->t.type==UNLONG /* || c==ASRSH || c==ASLSH || c==ASULSH || tree->t.tr1->t.type==UNCHAR */ || c==ASRSH || c==ASLSH || c==ASULSH || (opt = match(tree, efftab, r, 0)) == 0) if ((opt=match(tree, table, r, 0))==0) return(-1); string = opt->tabstring; p1 = tree->t.tr1; if (p1->t.op==FCON && p1->f.value>0) { /* nonportable */ printf(".data\nL%d:%o;%o;%o;%o\n.text\n", p1->f.value, ((unsigned short *)&(p1->f.fvalue))[0], ((unsigned short *)&(p1->f.fvalue))[1], ((unsigned short *)&(p1->f.fvalue))[2], ((unsigned short *)&(p1->f.fvalue))[3] ); p1->c.value = -p1->c.value; } p2 = 0; if (opdope[tree->t.op]&BINARY) { p2 = tree->t.tr2; if (p2->t.op==FCON && p2->f.value>0) { /* nonportable */ printf(".data\nL%d:%o;%o;%o;%o\n.text\n", p2->f.value, ((unsigned short *)&(p2->f.fvalue))[0], ((unsigned short *)&(p2->f.fvalue))[1], ((unsigned short *)&(p2->f.fvalue))[2], ((unsigned short *)&(p2->f.fvalue))[3] ); p2->f.value = -p2->f.value; } } loop: /* * The 0200 bit asks for a tab. */ if ((c = *string++) & 0200) { c &= 0177; putchar('\t'); } switch (c) { case '\n': dbprint(tree->t.op); break; case '\0': if (!isfloat(tree)) if (tree->t.op==DIVIDE||tree->t.op==ASDIV) reg--; if (table==regtab && (opdope[tree->t.op]&ASSGOP)) { if (tree->t.tr1->t.type==CHAR) printf("movb r%d,r%d\n", reg, reg); } return(reg); /* A1 */ case 'A': p = p1; goto adr; /* A2 */ case 'B': p = p2; goto adr; adr: c = 0; while (*string=='\'') { c++; string++; } if (*string=='+') { c = 100; string++; } pname(p, c); goto loop; /* I */ case 'M': if ((c = *string)=='\'') string++; else c = 0; prins(tree->t.op, c, instab, 0); goto loop; /* B1 */ case 'C': if ((opd&LEAF) != 0) p = tree; else p = p1; goto pbyte; /* BF */ case 'P': p = tree; goto pb1; /* B2 */ case 'D': p = p2; pbyte: if (p->t.type==CHAR || p->t.type==UNCHAR) putchar('b'); pb1: if (isfloat(p)) putchar('f'); goto loop; /* BE */ case 'L': if (p1->t.type==CHAR || p2->t.type==CHAR || p1->t.type==UNCHAR || p2->t.type==UNCHAR) putchar('b'); p = tree; goto pb1; /* F */ case 'G': p = p1; flag = 01; goto subtre; /* S */ case 'K': p = p2; flag = 02; goto subtre; /* H */ case 'H': p = tree; flag = 04; subtre: ctable = regtab; if (flag&04) ctable = cregtab; c = *string++ - 'A'; if (*string=='!') { string++; c |= 020; /* force right register */ } if (*string=='?') { string++; c |= 040; /* force condition codes */ } if ((c&02)!=0) ctable = sptab; if ((c&04)!=0) ctable = cctab; if ((flag&01) && ctable==regtab && (c&01)==0 && ((c&040)||tree->t.op==DIVIDE||tree->t.op==MOD || tree->t.op==ASDIV||tree->t.op==ASMOD||tree->t.op==ITOL)) ctable = cregtab; if ((c&01)!=0) { p = p->t.tr1; if(collcon(p) && ctable!=sptab) { if (p->t.op==STAR) p = p->t.tr1; p = p->t.tr1; } } if (table==lsptab && ctable==sptab) ctable = lsptab; if (c&010) r = reg1; else if (opdope[p->t.op]&LEAF || p->t.degree < 2) r = reg; else r = areg; rreg = rcexpr(p, ctable, r); if (ctable!=regtab && ctable!=cregtab) goto loop; if (c&010) { if (c&020 && rreg!=reg1) movreg(rreg, reg1, p); else reg1 = rreg; } else if (rreg!=reg) if ((c&020)==0 && oddreg(tree, 0)==0 && tree->t.type!=LONG && tree->t.type!=UNLONG && (flag&04 || flag&01&&xdcalc(p2,nreg-rreg-1)<=(opt->tabdeg2&077) || flag&02&&xdcalc(p1,nreg-rreg-1)<=(opt->tabdeg1&077))) { reg = rreg; reg1 = rreg+1; } else movreg(rreg, reg, p); goto loop; /* R */ case 'I': r = reg; if (*string=='-') { string++; r--; } goto preg; /* R1 */ case 'J': r = reg1; preg: if (*string=='+') { string++; r++; } if (r>nreg || r>=4 && tree->t.type==DOUBLE) { if (regpanic) error("Register overflow: simplify expression"); else longjmp(jmpbuf, 1); } printf("r%d", r); goto loop; case '-': /* check -(sp) */ if (*string=='(') { nstack++; if (table!=lsptab) putchar('-'); goto loop; } break; case ')': /* check (sp)+ */ putchar(')'); if (*string=='+') nstack--; goto loop; /* #1 */ case '#': p = p1->t.tr1; goto nmbr; /* #2 */ case '"': p = p2->t.tr1; nmbr: if(collcon(p)) { if (p->t.op==STAR) { printf("*"); p = p->t.tr1; } if ((p = p->t.tr2)->t.op == CON) { if (p->c.value) psoct(p->c.value); } else if (p->t.op==AMPER) pname(p->t.tr1, 0); } goto loop; /* * Certain adjustments for / % */ case 'T': c = reg-1; if (uns(p1) || uns(p2)) { printf("clr r%d\n", c); goto loop; } if (dcalc(p1, 5)>12 && !match(p1, cctab, 10, 0)) printf("tst r%d\n", reg); printf("sxt r%d\n", c); goto loop; case 'V': /* adc sbc, clr, or sxt as required for longs */ switch(tree->t.op) { case PLUS: case ASPLUS: case INCBEF: case INCAFT: printf("adc"); break; case MINUS: case ASMINUS: case NEG: case DECBEF: case DECAFT: printf("sbc"); break; case ASSIGN: p = tree->t.tr2; goto lcasev; case ASDIV: case ASMOD: case ASULSH: p = tree->t.tr1; lcasev: if (p->t.type!=LONG && p->t.type!=UNLONG) { if (uns(p) || uns(tree->t.tr2)) printf("clr"); else printf("sxt"); goto loop; } default: while ((c = *string++)!='\n' && c!='\0'); break; } goto loop; /* * Mask used in field assignments */ case 'Z': printf("$%o", UNS(tree->F.mask)); goto loop; /* * Relational on long values. * Might bug out early. E.g., * (long<0) can be determined with only 1 test. */ case 'X': if (xlongrel(*string++ - '0')) return(reg); goto loop; } putchar(c); goto loop; } /* * This routine just calls sreorder (below) * on the subtrees and then on the tree itself. * It returns non-zero if anything changed. */ reorder(treep, table, reg) union tree **treep; struct table *table; { register r, o; register union tree *p; p = *treep; o = p->t.op; if (opdope[o]&LEAF||o==LOGOR||o==LOGAND||o==SEQNC||o==QUEST||o==COLON) return(0); while(sreorder(&p->t.tr1, regtab, reg, 1)) ; if (opdope[o]&BINARY) while(sreorder(&p->t.tr2, regtab, reg, 1)) ; r = 0; if (table!=cctab) while (sreorder(treep, table, reg, 0)) r++; *treep = optim(*treep); return(r); } /* * Basically this routine carries out two kinds of optimization. * First, it observes that "x + (reg = y)" where actually * the = is any assignment op is better done as "reg=y; x+reg". * In this case rcexpr is called to do the first part and the * tree is modified so the name of the register * replaces the assignment. * Moreover, expressions like "reg = x+y" are best done as * "reg = x; reg += y" (so long as "reg" and "y" are not the same!). */ sreorder(treep, table, reg, recurf) union tree **treep; struct table *table; { register union tree *p, *p1; p = *treep; if (opdope[p->t.op]&LEAF) return(0); if (p->t.op==PLUS && recurf) if (reorder(&p->t.tr2, table, reg)) *treep = p = optim(p); if ((p1 = p->t.tr1)==TNULL) return(0); if (p->t.op==STAR || p->t.op==PLUS) { if (recurf && reorder(&p->t.tr1, table, reg)) { *treep = p = optim(p); if (opdope[p->t.op]&LEAF) return(0); } p1 = p->t.tr1; } if (p1->t.op==NAME) switch(p->t.op) { case ASLSH: case ASRSH: case ASSIGN: if (p1->n.class != REG || p1->n.type==CHAR || isfloat(p->t.tr2)) return(0); if (p->t.op==ASSIGN) switch (p->t.tr2->t.op) { case RSHIFT: if (p->t.type==UNSIGN) return(0); goto caseGEN; case TIMES: if (!ispow2(p->t.tr2)) break; p->t.tr2 = pow2(p->t.tr2); case PLUS: case MINUS: case AND: case ANDN: case OR: case EXOR: case LSHIFT: caseGEN: p1 = p->t.tr2->t.tr2; if (xdcalc(p1, 16) > 12 || p1->t.op==NAME &&(p1->n.nloc==p->t.tr1->n.nloc || p1->n.regno==p->t.tr1->n.nloc)) return(0); p1 = p->t.tr2; p->t.tr2 = p1->t.tr1; if (p1->t.tr1->t.op!=NAME || p1->t.tr1->n.class!=REG || p1->t.tr1->n.nloc!=p->t.tr1->n.nloc) rcexpr(p, efftab, reg); p->t.tr2 = p1->t.tr2; p->t.op = p1->t.op + ASPLUS - PLUS; *treep = p; return(1); } goto OK; case ASTIMES: if (!ispow2(p)) return(0); case ASPLUS: case ASMINUS: case ASAND: case ASANDN: case ASOR: case ASXOR: case INCBEF: case DECBEF: OK: if (table==cctab||table==cregtab) reg += 020; rcexpr(optim(p), efftab, ~reg); *treep = p1; return(1); } return(0); } /* * Delay handles postfix ++ and -- * It observes that "x + y++" is better * treated as "x + y; y++". * If the operator is ++ or -- itself, * it calls rcexpr to load the operand, letting * the calling instance of rcexpr to do the * ++ using efftab. * Otherwise it uses sdelay to search for inc/dec * among the operands. */ delay(treep, table, reg) union tree **treep; struct table *table; { register union tree *p, *p1; register r; p = *treep; if ((p->t.op==INCAFT||p->t.op==DECAFT) && p->t.tr1->t.op==NAME) { r = p->t.tr1->n.class; if (r == EXTERN || r == OFFS || r == STATIC && p->t.tr1->t.type == UNCHAR) return(1+rcexpr(p->t.tr1, table, reg)); else return(1+rcexpr(paint(p->t.tr1, p->t.type), table,reg)); } p1 = 0; /* * typo fix, original code. * if (opdope[p->t.op]&BINARY) { * if (p->t.op==LOGAND || p->t.op==LOGOR * || p->t.op==QUEST || p->t.op==COLON || p->t.op==SEQNC) * return(0); * } * p1 = sdelay(&p->t.tr2); * if (p1==0) * p1 = sdelay(&p->t.tr1); */ if (opdope[p->t.op]&BINARY) { if (p->t.op==LOGAND || p->t.op==LOGOR || p->t.op==QUEST || p->t.op==COLON || p->t.op==SEQNC) return(0); p1 = sdelay(&p->t.tr2); } if (p1==0) p1 = sdelay(&p->t.tr1); if (p1) { r = rcexpr(optim(p), table, reg); *treep = p1; return(r+1); } return(0); } union tree * sdelay(ap) union tree **ap; { register union tree *p, *p1; if ((p = *ap)==TNULL) return(TNULL); if ((p->t.op==INCAFT||p->t.op==DECAFT) && p->t.tr1->t.op==NAME) { *ap = paint(ncopy(p->t.tr1), p->t.type); return(p); } if (p->t.op==STAR || p->t.op==PLUS) if (p1=sdelay(&p->t.tr1)) return(p1); if (p->t.op==PLUS) return(sdelay(&p->t.tr2)); return(0); } /* * Propagate possible implicit type-changing operation */ union tree * paint(tp, type) register union tree *tp; register type; { if (tp->t.type==type) return(tp); if (tp->t.type==CHAR && type==INT) return(tp); if (tp->t.type==CHAR || tp->t.type==UNCHAR) return(optim(tnode(LOAD, type, tp, TNULL))); tp->t.type = type; if (tp->t.op==AMPER && type&XTYPE) tp->t.tr1 = paint(tp->t.tr1, decref(type)); else if (tp->t.op==STAR) tp->t.tr1 = paint(tp->t.tr1, incref(type)); else if (tp->t.op==ASSIGN) { paint(tp->t.tr1, type); paint(tp->t.tr2, type); } return(tp); } /* * Copy a tree node for a register variable. * Used by sdelay because if *reg-- is turned * into *reg; reg-- the *reg will in turn * be changed to some offset class, accidentally * modifying the reg--. */ union tree * ncopy(p) register union tree *p; { register union tree *q; q = getblk(sizeof(struct xtname)); q->n.op = p->n.op; q->n.type = p->n.type; q->n.class = p->n.class; q->n.regno = p->n.regno; q->n.offset = p->n.offset; if (q->n.class==EXTERN || q->n.class==XOFFS) q->x.name = p->x.name; else q->n.nloc = p->n.nloc; return(q); } /* * If the tree can be immediately loaded into a register, * produce code to do so and return success. */ chkleaf(tree, table, reg) register union tree *tree; struct table *table; { struct tnode lbuf; if (tree->t.op!=STAR && dcalc(tree, nreg-reg) > 12) return(-1); lbuf.op = LOAD; lbuf.type = tree->t.type; lbuf.degree = tree->t.degree; lbuf.tr1 = tree; return(rcexpr((union tree *)&lbuf, table, reg)); } /* * Compile a function argument. * If the stack is currently empty, put it in (sp) * rather than -(sp); this will save a pop. * Return the number of bytes pushed, * for future popping. */ comarg(tree, flagp) register union tree *tree; int *flagp; { register retval; int i; int size; if (tree->t.op==STRASG) { size = tree->F.mask; tree = tree->t.tr1; tree = strfunc(tree); if (size <= sizeof(short)) { paint(tree, INT); goto normal; } if (size <= sizeof(long)) { paint(tree, LONG); goto normal; } if (tree->t.op!=NAME && tree->t.op!=STAR) { error("Unimplemented structure assignment"); return(0); } tree = tnode(AMPER, STRUCT+PTR, tree, TNULL); tree = tnode(PLUS, STRUCT+PTR, tree, tconst(size, INT)); tree = optim(tree); retval = rcexpr(tree, regtab, 0); size >>= 1; if (size <= 5) { for (i=0; it.type==LONG || tree->t.type==UNLONG) { rcexpr(tree, sptab, 0); retval = arlength(tree->t.type); } else { (*flagp)++; rcexpr(tree, lsptab, 0); retval = 0; } return(retval); } union tree * strfunc(tp) register union tree *tp; { if (tp->t.op != CALL) return(tp); paint(tp, STRUCT+PTR); return(tnode(STAR, STRUCT, tp, TNULL)); } /* * Compile an initializing expression */ doinit(type, tree) register type; register union tree *tree; { float sfval; double fval; long lval; if (type==CHAR || type==UNCHAR) { printf(".byte "); if (tree->t.type&XTYPE) goto illinit; type = INT; } if (type&XTYPE) type = INT; switch (type) { case INT: case UNSIGN: if (tree->t.op==FTOI) { if (tree->t.tr1->t.op!=FCON && tree->t.tr1->t.op!=SFCON) goto illinit; tree = tree->t.tr1; tree->c.value = tree->f.fvalue; tree->t.op = CON; } else if (tree->t.op==LTOI) { if (tree->t.tr1->t.op!=LCON) goto illinit; tree = tree->t.tr1; lval = tree->l.lvalue; tree->t.op = CON; tree->c.value = lval; } if (tree->t.op == CON) printf("%o\n", UNS(tree->c.value)); else if (tree->t.op==AMPER) { pname(tree->t.tr1, 0); putchar('\n'); } else goto illinit; return; case DOUBLE: case FLOAT: if (tree->t.op==ITOF) { if (tree->t.tr1->t.op==CON) { fval = tree->t.tr1->c.value; } else goto illinit; } else if (tree->t.op==FCON || tree->t.op==SFCON) fval = tree->f.fvalue; else if (tree->t.op==LTOF) { if (tree->t.tr1->t.op!=LCON) goto illinit; fval = tree->t.tr1->l.lvalue; } else goto illinit; if (type==FLOAT) { sfval = fval; /* nonportable */ printf("%o; %o\n", ((unsigned short *)&sfval)[0], ((unsigned short *)&sfval)[1] ); } else printf("%o; %o; %o; %o\n", ((unsigned short *)&fval)[0], ((unsigned short *)&fval)[1], ((unsigned short *)&fval)[2], ((unsigned short *)&fval)[3] ); return; case UNLONG: case LONG: if (tree->t.op==FTOL) { tree = tree->t.tr1; if (tree->t.op==SFCON) tree->t.op = FCON; if (tree->t.op!= FCON) goto illinit; lval = tree->f.fvalue; } else if (tree->t.op==ITOL) { if (tree->t.tr1->t.op != CON) goto illinit; if (uns(tree->t.tr1)) lval = (unsigned)tree->t.tr1->c.value; else lval = tree->t.tr1->c.value; } else if (tree->t.op==LCON) lval = tree->l.lvalue; else goto illinit; /* nonportable */ printf("%o; %o\n", UNS((lval>>16)), UNS(lval)); return; } illinit: error("Illegal initialization"); } movreg(r0, r1, tree) union tree *tree; { register char *s; char c; if (r0==r1) return; if (tree->t.type==LONG || tree->t.type == UNLONG) { if (r0>=nreg || r1>=nreg) { error("register overflow: compiler error"); } s = "mov r%d,r%d\nmov r%d,r%d\n"; if (r0 < r1) printf(s, r0+1,r1+1,r0,r1); else printf(s, r0,r1,r0+1,r1+1); return; } c = isfloat(tree); printf("mov%.1s r%d,r%d\n", &c, r0, r1); }