/* Copyright 1982, 1983 Alcyon Corporation 8716 Production Ave. San Diego, Ca. 92121 @(#)canon.c 1.15 12/30/83 */ #include "cgen.h" #include "cskel.h" /* * canon - expression canonicalization * Top level tree canonicalization. This fixes any bit field * accesses, then loops on commute and optim until no more * optimizations are done. */ char *canon(tp) /* returns pointer to tree*/ struct tnode *tp; /* pointer to tree to canonicalize*/ { tp = fixbfield(tp); do { sucomp(tp,0,1); /*need Sethy-Ullman's for commute*/ tp = commute(tp); /*commute the tree*/ } while( optim(&tp) ); /*keep trying for optimizations*/ PUTEXPR((cflag>1),"canon return",tp); return(tp); } /* fixbfield - fix bit field operators*/ /* Fixes bit field assignment and normal usage*/ char *fixbfield(tp) /* returns pointer to fixed tree*/ struct tnode *tp; /* pointer to tree*/ { register struct tnode *btp, *stp; struct tnode *rhstp, *asgtp; register short foff, fmask, op, flen; if( LEAFOP(op=tp->t_op) ) return(tp); if( ISASGOP(op) && tp->t_left->t_op == BFIELD ) { tp->t_right = fixbfield(tp->t_right); /* [vlh] 4.0 */ btp = tp->t_left; /*pointer to BFIELD node*/ stp = btp->t_left; /*pointer to son of BFIELD node*/ foff = (btp->t_su>>8) & 0377; flen = btp->t_su & 0377; fmask = (1<t_right->t_op == CINT && (op == ASSIGN || op == EQXOR) ) { if( op == EQXOR ) { tp->t_left = stp; tp->t_right->t_value <<= foff; return(tp); } if( !tp->t_right->t_value ) { tp->t_op = EQAND; tp->t_left = stp; tp->t_right->t_value = ~ (fmask << foff); return(tp); } if( (tp->t_right->t_value & fmask) == fmask ) { tp->t_op = EQOR; tp->t_left = stp; tp->t_right->t_value = fmask << foff; return(tp); } } if( fmask == -1 ) tp->t_left = stp; else { asgtp = tcopy(stp,A_DOPOST); /*for lhs of assign*/ op = tp->t_op; tp = tp->t_right; /*pointer to expression*/ if( op != ASSIGN ) { /*=op operator?*/ rhstp = tcopy(stp,0); btp = fixbfield(btp); tp = tnalloc(op-(EQADD-ADD),INT,0,0,tcopy(btp,A_DOPRE),tp); } else rhstp = tcopy(stp,A_DOPRE); /*for rhs of assign*/ tp = tnalloc(AND,INT,0,0,tp,cnalloc(INT,fmask)); tp = tnalloc(LSH,INT,0,0,tp,cnalloc(INT,foff)); btp = tnalloc(AND,INT,0,0,rhstp,cnalloc(INT,~(fmask<t_su>>8) & 0377; fmask = (1<<(tp->t_su&0377))-1; tp = tnalloc(RSH,INT,0,0,tp->t_left,cnalloc(INT,foff)); tp = tnalloc(AND,INT,0,0,tp,cnalloc(INT,fmask)); } else { tp->t_left = fixbfield(tp->t_left); if( BINOP(op) ) tp->t_right = fixbfield(tp->t_right); } return(tp); } /* * optim - optimize expression tree * This takes expression tree and performs the following * translations: folds auto names to accesses off the local * environment pointer, performs mostly "machine-independent" * optimizations, such as multiply by zero and one, etc., * turns field accesses into and's and or's, etc. * [vlh] 4.0 added checks for divide/modulos long zero... * [vlh] 4.0 optimize multiply/divide 1L, add/sub 0L... * [vlh] 4.1 optimize all long constant expressions which * previously had integer constant equivilents. */ optim(tpp) /* returns pointer to new tree*/ struct tnode **tpp; { register struct tnode *ltp, *rtp, *tp; register char *p; register short i, changes, op, tmp; short lconst, tlc; long cval; /* [vlh] 4.1 handle all integer constants as longs */ tp = *tpp; if (tp->t_op != STASSIGN && (tp->t_type & ~TYPE)) { /*[vlh]4.2 STASSIGN*/ tp->t_type &= TYPE; tp->t_type |= POINTER; } if( LEAFOP(op=tp->t_op) ) return(0); lconst = changes = 0; if( BINOP(op) ) changes += optim(&tp->t_right); changes += optim(&tp->t_left); for( ; NOTLEAFOP(op=tp->t_op); changes++ ) { #ifdef DEBUG if (oflag>1) printf("optim: op = %d, changes %d\n",op,changes); #endif PUTEXPR(oflag,"optim",tp); *tpp = tp; ltp = tp->t_left; if( BINOP(op) ) { rtp = tp->t_right; if( tp->t_type == CHAR ) tp->t_type = INT; p = constant(rtp,&lconst); } else p = constant(ltp,&lconst); if( p ) cval = (lconst) ? p->t_lvalue : p->t_value; /* [vlh] 4.1 */ switch( op ) { case ADD: if( p ) { if( !cval ) { tp = ltp; continue; } if( ltp->t_op == ADDR ) { ltp->t_left->t_offset += cval; tp = ltp; continue; } } break; case ASSIGN: if( rtp->t_op == ADD && indexreg(rtp->t_left) && rtp->t_right->t_op == CINT ) { ltp = rtp->t_left; ltp->t_sc = REGOFF; ltp->t_offset += rtp->t_right->t_value; tp->t_right = ltp; tp->t_op = EQADDR; continue; } if (rtp->t_op==LONG2I && (rtp->t_left->t_op==LDIV || rtp->t_left->t_op==LMOD)) { rtp = rtp->t_left; if(rtp->t_right->t_op == DCLONG) { if(rtp->t_right->t_lvalue < 0) break; if(rtp->t_right->t_lvalue > 0xffff) break; rtp->t_right->t_op = CINT; rtp->t_right->t_type = INT; } else if(rtp->t_right->t_op == INT2L) rtp->t_right = rtp->t_right->t_left; rtp->t_op -= (LDIV - DIV); tp->t_right = rtp; continue; } case EQSUB: /* [vlh] 4.2 */ case EQADD: /* [vlh] 4.2 */ case EQAND: /* [vlh] 4.2 */ case EQOR: /* [vlh] 4.2 */ if(p && ltp->t_type==CHAR) { /* [vlh] 4.2 */ ctrunc: if (lconst) { tmp = rtp->t_lvalue; rtp->t_op = CINT; } else tmp = rtp->t_value; if (tmp > 255) { warning("value assigned to char truncated"); rtp->t_value = tmp & 0xff; } else if ( tmp < -128) { warning("value assigned to char truncated"); rtp->t_value = ((char)(tmp & 0xff)); } break; } break; case SUB: if( p ) { if( !cval ) { tp = ltp; continue; } tp->t_op = ADD; SETVAL(lconst,p,-cval); continue; } if( (p=constant(ltp,&tlc)) && CONSTZERO(tlc,p)) { tp->t_op = UMINUS; tp->t_left = ltp = rtp; continue; } /*0-x = -x*/ break; case DIV: case EQDIV: if( p ) { if( !cval ) { /*X/0->error*/ error("divide by zero"); tp = rtp; continue; } if( cval == 1 ) { tp = ltp; continue; } if( cval == -1 ) { tp->t_op = (tp->t_op == DIV ? UMINUS : EQNEG ); continue; } } if( (p=constant(ltp,&tlc)) && CONSTZERO(lconst,p)) { tp = ltp; continue; } if( multop(tpp) ) { tp = *tpp; continue; } break; case EQMOD: case MOD: if( p ) { if( !cval ) { /*X%0->error*/ error("modulus by zero"); tp = rtp; continue; } if( cval == 1 ) { /*X%1->0*/ SETVAL(lconst,p,0); if (op == EQMOD) /*[vlh] 4.0*/ tp->t_op = ASSIGN; else tp = rtp; continue; } } if( (p=constant(ltp,&tlc)) && CONSTZERO(lconst,p)) { tp = ltp; continue; } if( multop(tpp) ) { tp = *tpp; continue; } break; case MULT: case EQMULT: if( p ) { /* Don't do this optimization !!! case: 0 * f() if( !cval ) { if (op == EQMULT) tp->t_op = ASSIGN; else tp = rtp; continue; } Problem: side effects of object multiplied with !!! */ if( cval == 1L ) { tp = ltp; continue; } if( cval == -1L ) { tp->t_op = (tp->t_op == MULT ? UMINUS : EQNEG); continue; } } if( multop(tpp) ) { tp = *tpp; continue; } break; case EQUALS: case NEQUALS: if( p && ((i=onebit(cval)) >= 0) && ltp->t_op == AND && (rtp=constant(ltp->t_right,&tlc))) if ((!tlc && i == onebit((long)rtp->t_value)) || (tlc && i == onebit(rtp->t_lvalue))) { tp->t_op = invrel[tp->t_op-EQUALS]; p->t_value = 0; p->t_op = CINT; /* assure const short on bit value... */ continue; } break; case GREATEQ: if( p && !cval && UNSIGN(ltp->t_type) ) { SETVAL(lconst,p,1); tp = p; continue; } break; case LESS: if( p && !cval && UNSIGN(ltp->t_type) ) { tp = p; continue; } break; case AND: if( p ) { /* Don't do this optimization !!! case: 0 & f() if( !cval ) { tp = rtp; continue; } Problem: side effects of object anded with !!! */ if( cval == -1L ) { tp = ltp; continue; } } break; case OR: if( p ) { if( !cval ) { tp = ltp; continue; } if( cval == -1 ) { tp = rtp; continue; } } break; case EQXOR: case XOR: if( p ) { if( !cval ) { tp = ltp; continue; } if( cval == -1 ) { tp->t_op = (tp->t_op == XOR ? COMPL : EQNOT); continue; } if(ltp->t_type==CHAR) /* [vlh] 4.2 */ goto ctrunc; } break; case LSH: case EQLSH: if(tp->t_type==LONG && rtp->t_op==INT2L && UNSIGN(rtp->t_left->t_type)) { tp->t_right = rtp->t_left; continue; } case RSH: case EQRSH: if( p ) { if( !cval ) { tp = ltp; continue; } if( p != rtp ) { tp->t_right = p; continue; } } break; case ADDR: if( ltp->t_op == INDR ) { tp = ltp->t_left; continue; } if( ltp->t_op == SYMBOL && ltp->t_sc == REGOFF ) { tp->t_op = ADD; tp->t_right = cnalloc(INT,(short)ltp->t_offset); ltp->t_type = tp->t_type; ltp->t_sc = REGISTER; ltp->t_offset = 0; continue; } break; case INDR: p = ltp->t_left; switch( ltp->t_op ) { case ADDR: tp = p; continue; case CINT: tp = snalloc(tp->t_type,CINDR,(long)ltp->t_value,0,0); continue; case CLONG: tp = snalloc(tp->t_type,CLINDR,ltp->t_lvalue,0, ltp->t_lvalue.loword); continue; case CFLOAT: /* [vlh] 3.4 */ tp = snalloc(tp->t_type,CFINDR,ltp->t_lvalue,0, ltp->t_lvalue.loword); continue; case SYMBOL: if( indexreg(ltp) ) { ltp->t_sc = REGOFF; ltp->t_type = tp->t_type; ltp->t_offset = 0; tp = ltp; continue; } break; case ADD: if (p->t_op==SYMBOL && p->t_sc==REGOFF && (rtp=constant(ltp->t_right,&tlc)) && !tlc && NOTPOINTER(p->t_type) ) { p->t_offset += rtp->t_value; tp = p; continue; } /* [vlh] 4.2 long constant not valid offset !!!! */ if( indexreg(p) ) { if((rtp=constant(ltp->t_right,&tlc)) && !tlc){ /* * This combines an address register and a constant into a register * offset. This relies on 68000 Addressing scheme somewhat. */ p->t_sc = REGOFF; p->t_type = tp->t_type; p->t_offset += rtp->t_value; tp = p; continue; } if( !lflag && ltp->t_right->t_op == ADDR ) { /* * We can fold *(An+&expr) into *(&expr(An)), but note that &expr * must be 16 bits for 68000, hence we can only do this if short * addresses are enabled. Note that the storage classes are mapped: * EXTERNAL->EXTOFF, STATIC->STATOFF, REGISTER->REGOFF */ ltp = ltp->t_right->t_left; ltp->t_sc += (EXTOFF-EXTERNAL); ltp->t_type = tp->t_type; tp = ltp; continue; } } break; /* * --X and X++ can be folded into -(An) and (An)+ 68000 instructions */ case PREDEC: case POSTINC: if( indexreg(p) && ltp->t_type == p->t_type ) { p->t_op = (ltp->t_op == PREDEC ? AUTODEC : AUTOINC); p->t_type = tp->t_type; tp = p; continue; } } /* INDR switch... */ break; case NOT: if( RELOP(ltp->t_op) ) { /*!(X>Y)->X<=Y*/ tp = ltp; tp->t_op = invrel[tp->t_op-EQUALS]; continue; } break; case UMINUS: case COMPL: if( tp->t_type == CHAR ) tp->t_type = INT; if( tp->t_op == ltp->t_op ) { tp = ltp->t_left; continue; } if( ltp->t_op == INT2L ) { /*~(INT2L X)->INT2L (~X)*/ ltp->t_op = tp->t_op; tp->t_op = INT2L; ltp->t_type = INT; continue; } if(ltp->t_op==LONG2I) { /*[vlh] 4.2 ~(LONG2I X)->LONG2I (~X)*/ ltp->t_op = tp->t_op; tp->t_op = LONG2I; ltp->t_type = LONG; continue; } break; case INT2L: if( ltp->t_op == MULT ) { /*INT2L (X*Y)->X*Y*/ ltp->t_type = (ltp->t_type&(~TYPE))|LONG; tp = ltp; continue; } break; } /* end of switch */ if(!ccexpr(tpp)) break; tp = *tpp; } PUTEXPR(oflag,"optim exit",tp); *tpp = tp; return(changes); } /* * commute - commutes expression tree to canonical form * This sorts commutable expression trees so that the most * difficult expression is the left-most operand. Note that * canon assumes that commute has placed constant operands in * the right sub-tree. This also swaps relationals so that the * most difficult expression is on the left. */ char *commute(tp) /* returns commuted expression tree*/ struct tnode *tp; { struct tnode *clist[20], *plist[19]; register struct tnode **p, **q, *s; struct tnode **plp, **clp; register short op; if( RELOP(op=tp->t_op) ) { s = tp->t_left; if( harder(tp->t_right,s) ) { PUTEXPR(oflag,"swaprel",tp); tp->t_op = swaprel[op-EQUALS]; tp->t_left = tp->t_right; tp->t_right = s; } } if( COMMOP(op) ) { PUTEXPR(oflag,"commute",tp); clp = clist; plp = plist; addtree(tp,&clp,&plp); /*collect comm. expressions*/ /* * see if any sub-trees can also be commuted (with different operator) */ clp--; plp--; for( p = clist; p <= clp; p++ ) *p = commute(*p); /* * this sorts the expressions in decreasing order of Sethy-Ullman * values. */ for( p = clist; p <= clp; p++ ) { for( q = p; q <= clp; q++ ) { if( harder(*q,*p) ) { s = *q; *q = *p; *p = s; } } } /* * Now, we start at the end of the list and collect any constants * if possible. */ for( q = clp, p = plp; p > plist; p-- ) { s = *p; s->t_right = *q; s->t_left = *--q; if( ccexpr(p) ) { clp--; plp--; *q = *p; /*[vlh]*/ } } /* * this takes the sorted sub-expression pointers and the pointers * to the commutable operator nodes (plist) and structures the * tree so that the left sub-expression is the most complex. The * code generation scheme is very sensitive to this... */ q = clist; p = plist; s = *q++; while( p <= plp ) { (*p)->t_left = s; s = *p++; s->t_right = *q++; if(!LONGORPTR(s->t_type)) { if( LONGORPTR(s->t_right->t_type) ) s->t_type = s->t_right->t_type; else if( LONGORPTR(s->t_left->t_type) ) s->t_type = s->t_left->t_type; } } tp = s; PUTEXPR(oflag,"after commute",tp); } else if( BINOP(op) ) { tp->t_left = commute(tp->t_left); tp->t_right = commute(tp->t_right); } else if( UNARYOP(op) ) tp->t_left = commute(tp->t_left); return(tp); } /* * harder - test one sub-expression for being "harder" than another * This requires some special finagling for registers. The reason * for this is that the code skeletons produce better code if the * register is on the left. Also note that allowing an address * register on the right can have disastrous effects for AND and OR. * The basic poshort is: don't mess with this routine unless you're * 100% sure you understand the ramifications... */ harder(tp,ntp) /* returns 1 if tp > ntp, else 0*/ struct tnode *tp; struct tnode *ntp; { short lconst; if( ntp->t_su == SU_VHARD ) return(0); if( tp->t_su == SU_VHARD ) return(1); if( ISREG(ntp) ) return(0); if( ISREG(tp) ) return(1); if( constant(ntp,&lconst) ) /* [vlh] */ return(1); if( constant(tp,&lconst) ) /* [vlh] */ return(0); return( tp->t_su > ntp->t_su ); } /* * addtree - collect commutable sub-trees for commute * This recurses down the sub-trees looking for groups of * commutable operators. It collects the sub-trees and their * parent nodes for commute. */ addtree(tp,clist,plist) /* returns pointer to clist*/ struct tnode *tp; /* pointer to tree*/ struct tnode ***clist; /* commutable sub-trees*/ struct tnode ***plist; /* parent nodes of sub-trees*/ { register struct tnode ***p, ***c; c = clist; p = plist; if( tp->t_op == tp->t_left->t_op ) addtree(tp->t_left,c,p); else *(*c)++ = tp->t_left; if( tp->t_op == tp->t_right->t_op ) addtree(tp->t_right,c,p); else *(*c)++ = tp->t_right; *(*p)++ = tp; } /* constant - test for tree being a constant node*/ char * constant(tp,lconst) /* returns 0 or ptr to const node*/ struct tnode *tp; /* pointer to tree*/ short *lconst; { if (tp->t_op == CLONG) { /* [vlh] */ *lconst = 1; return(tp); } else *lconst = 0; if( tp->t_op == CINT ) return(tp); if(tp->t_op==INT2L && (tp->t_left->t_op==CINT||tp->t_left->t_op==CLONG)) { tp->t_op = CLONG; tp->t_type = LONG; if (tp->t_left->t_op == CLONG) tp->t_lvalue = tp->t_left->t_lvalue; else tp->t_lvalue = tp->t_left->t_value; *lconst = 1; return(tp); } if (tp->t_op==LONG2I && (tp->t_left->t_op==CINT||tp->t_left->t_op==CLONG)) { tp->t_op = CINT; tp->t_type = INT; if (tp->t_left->t_op == CLONG) tp->t_value = tp->t_left->t_lvalue; else tp->t_value = tp->t_left->t_value; return(tp); } return(0); } /* indexreg - returns whether node is an address register*/ /* For 68000, must be an A register*/ indexreg(tp) /* returns whether node is A reg*/ struct tnode *tp; /* pointer to tree*/ { if( tp->t_op == SYMBOL && tp->t_sc == REGISTER && !(ISDREG(tp->t_reg)) ) return(1); return(0); } /* ccexpr - compute constant expression*/ /* Evaluates constant expressions, including ?: and relationals*/ ccexpr(tpp) /* returns 1 if changes, 0 otherwise*/ struct tnode **tpp; /* pointer to tree*/ { register struct tnode *ltp, *rtp, *tp; register short op, anyfloat; /*[vlh] 3.4 anyfloat*/ register long rval, lval; short anylong, lconst; tp = *tpp; op = tp->t_op; if( LEAFOP(op) ) return(0); anylong = anyfloat = 0; ltp = tp->t_left; if ( ltp->t_op == CFLOAT) { /* [vlh] 3.4 */ lval = ltp->t_lvalue; anyfloat++; } else if( ltp = constant(ltp,&anylong) ) /* [vlh] 4.1 */ lval = (anylong) ? ltp->t_lvalue : ltp->t_value; else return(0); if( BINOP(op) ) { rtp = tp->t_right; if( op == QMARK ) { ltp = rtp->t_left; rtp = rtp->t_right; if( lconst && (ltp->t_op != CLONG || rtp->t_op != CLONG)) return(0); /* [vlh] 4.1 */ if( !lconst && (ltp->t_op != CINT || rtp->t_op != CINT )) return(0); if (lconst) /* [vlh] 4.1 */ ltp->t_lvalue = (lval) ? ltp->t_lvalue : rtp->t_lvalue; else ltp->t_value = (lval) ? ltp->t_value : rtp->t_value; *tpp = ltp; return(1); } if (rtp->t_op == CFLOAT) { /* [vlh] 3.4 */ anyfloat++; rval = rtp->t_lvalue; switch (op) { case MULT: case DIV: case MOD: case LSH: case RSH: case XOR: return(0); } } else if( rtp = constant(rtp,&lconst) ) { rval = (lconst) ? rtp->t_lvalue : rtp->t_value; anylong += lconst; } else return(0); } switch( op ) { case ADD: lval += rval; break; case SUB: lval -= rval; break; case MULT: lval *= rval; break; case DIV: lval /= rval; break; case MOD: lval %= rval; break; case RSH: lval >>= rval; break; case LSH: lval <<= rval; break; case XOR: lval ^= rval; break; case GREAT: lval = (lval>rval); break; case GREATEQ: lval = (lval>=rval); break; case LESS: lval = (lvalt_value = lval; *tpp = ltp; return(1); } /** * power2 - do multiply and divide by powers of two * This changes multiplies and divides by constant powers of two * to shifts. **/ power2(tpp) /* returns 0 if not power of two*/ struct tnode **tpp; /* pointer to expression tree*/ { register char *p; register short op; register struct tnode *tp; short lconst; short value; tp = *tpp; if( p = constant(tp->t_right,&lconst) ) { if(!lconst && ((value=onebit((long)p->t_value)) < 0) ) return(0); if(lconst && ((value=onebit(p->t_lvalue)) < 0) ) return(0); op = tp->t_op; /* THIS FIX MUST WAIT FOR UNSIGNED LONG IMPLEMENTATION...........*/ if (op!=MULT && op!=EQMULT && !UNSIGN(tp->t_left->t_type)) return(0); switch( op ) { case MULT: op = LSH; break; case EQMULT: op = EQLSH; break; case DIV: op = RSH; break; case EQDIV: op = EQRSH; break; case MOD: op = AND; if (!lconst) value = p->t_value - 1; else value = p->t_lvalue - 1; break; case EQMOD: op = EQAND; if (!lconst) value = p->t_value - 1; else value = p->t_lvalue - 1; break; default: return(0); } /* end of case */ tp->t_op = op; if (lconst) p->t_op = CINT; /* [vlh] 4.1 assure constant int.... */ p->t_value = value; #ifdef DEBUG if (oflag>1) printf("lconst %d op = %d value = %d\n",lconst,op,value); #endif return(1); } return(0); } /* chklong - check for tree being a long*/ chklong(tp) /* returns 1 if long, 0 otherwise*/ struct tnode *tp; /* pointer to expression tree*/ { if( tp->t_op == CLONG ) return(1); return( LONGORPTR(tp->t_type) ); } /** * multop - handle multiplicative operators * This checks for powers of two optimizations, then for a hard * long operation. **/ multop(tpp) /* returns ptr to expression tree*/ struct tnode **tpp; /* pointer to expression tree*/ { register struct tnode *tp, *rtp; register short change; tp = *tpp; PUTEXPR(oflag,"multop",*tpp); if (change = power2(tpp)) tp = *tpp; if( chklong(rtp=tp->t_right) || chklong(tp->t_left) ) switch( tp->t_op ) { case MULT: tp->t_op = LMULT; break; case DIV: case MOD: if( chklong(tp->t_left) ) { /*only hard if divisor is long*/ if(rtp->t_type == INT) /*only occurs if changed !!*/ break; tp->t_op += (LDIV-DIV); if( rtp->t_op == CLONG ) rtp->t_op = DCLONG; } break; case EQDIV: case EQMOD: if( !chklong(rtp) ) break; if( rtp->t_op == CLONG ) rtp->t_op = DCLONG; case EQMULT: tp->t_op += (LMULT-EQMULT); *tpp = tnalloc(ASSIGN,tp->t_type,0,0,tcopy(tp->t_left,A_DOPOST), tcopy(tp,A_DOPRE)); change++; break; } /* end of case... */ PUTEXPR(oflag,"after multop ",tp); return(change); } /* onebit - returns whether constant is power of two (one bit on)*/ onebit(val) /* returns bit number or -1*/ long val; /* [vlh] int->long, constant value to check*/ { register short i; for( i = 31; val != 0; val <<= 1, i-- ) if( val & 0x80000000 ) break; if (val != 0x80000000) return(-1); return(i); }