/* * Print a warning if a definition/declaration does not match another * definition/declaration of the same name. For functions, only the * types of return values are tested. */ static void chkvtdi(hte_t *hte, sym_t *def, sym_t *decl) { sym_t *sym; type_t *tp1, *tp2; /* LINTED (automatic hides external declaration: warn) */ int eq, warn; char *pos1; if (def == NULL) def = decl; if (def == NULL) return; tp1 = TP(def->s_type); for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) { if (sym == def) continue; tp2 = TP(sym->s_type); warn = 0; if (tp1->t_tspec == FUNC && tp2->t_tspec == FUNC) { eq = eqtype(tp1->t_subt, tp2->t_subt, 1, 0, 0, &warn); } else { eq = eqtype(tp1, tp2, 0, 0, 0, &warn); } if (!eq || (sflag && warn)) { pos1 = xstrdup(mkpos(&def->s_pos)); /* %s value declared inconsistently\t%s :: %s */ msg(5, hte->h_name, pos1, mkpos(&sym->s_pos)); free(pos1); } } }
Tree condtree(Tree e, Tree l, Tree r) { Symbol t1; Type ty, xty = l->type, yty = r->type; Tree p; if (isarith(xty) && isarith(yty)) ty = binary(xty, yty); else if (eqtype(xty, yty, 1)) ty = unqual(xty); else if (isptr(xty) && isnullptr(r)) ty = xty; else if (isnullptr(l) && isptr(yty)) ty = yty; else if (isptr(xty) && !isfunc(xty->type) && isvoidptr(yty) || isptr(yty) && !isfunc(yty->type) && isvoidptr(xty)) ty = voidptype; else if ((isptr(xty) && isptr(yty) && eqtype(unqual(xty->type), unqual(yty->type), 1))) ty = xty; else { typeerror(COND, l, r); return consttree(0, inttype); } if (isptr(ty)) { ty = unqual(unqual(ty)->type); if (isptr(xty) && isconst(unqual(xty)->type) || isptr(yty) && isconst(unqual(yty)->type)) ty = qual(CONST, ty); if (isptr(xty) && isvolatile(unqual(xty)->type) || isptr(yty) && isvolatile(unqual(yty)->type)) ty = qual(VOLATILE, ty); ty = ptr(ty); } switch (e->op) { case CNST+I: return cast(e->u.v.i != 0 ? l : r, ty); case CNST+U: return cast(e->u.v.u != 0 ? l : r, ty); case CNST+P: return cast(e->u.v.p != 0 ? l : r, ty); case CNST+F: return cast(e->u.v.d != 0.0 ? l : r, ty); } if (ty != voidtype && ty->size > 0) { t1 = genident(REGISTER, unqual(ty), level); /* t1 = temporary(REGISTER, unqual(ty)); */ l = asgn(t1, l); r = asgn(t1, r); } else t1 = NULL; p = tree(COND, ty, cond(e), tree(RIGHT, ty, root(l), root(r))); p->u.sym = t1; return p; }
static int compatible(Type ty1, Type ty2) { ty1 = unqual(ty1); ty2 = unqual(ty2); return isptr(ty1) && !isfunc(ty1->type) && isptr(ty2) && !isfunc(ty2->type) && eqtype(unqual(ty1->type), unqual(ty2->type), 0); }
/* * Print a warning if the return value assumed for a function call * differs from the return value of the function definition or * function declaration. * * If no definition/declaration can be found, the assumed return values * are always int. So there is no need to compare with another function * call as it's done for function arguments. */ static void chkvtui(hte_t *hte, sym_t *def, sym_t *decl) { fcall_t *call; char *pos1; type_t *tp1, *tp2; /* LINTED (automatic hides external declaration: warn) */ int warn, eq; tspec_t t1; if (hte->h_calls == NULL) return; if (def == NULL) def = decl; if (def == NULL) return; t1 = (tp1 = TP(def->s_type)->t_subt)->t_tspec; for (call = hte->h_calls; call != NULL; call = call->f_nxt) { tp2 = TP(call->f_type)->t_subt; eq = eqtype(tp1, tp2, 1, 0, 0, (warn = 0, &warn)); if (!call->f_rused) { /* no return value used */ if ((t1 == STRUCT || t1 == UNION) && !eq) { /* * If a function returns a struct or union it * must be declared to return a struct or * union, also if the return value is ignored. * This is necessary because the caller must * allocate stack space for the return value. * If it does not, the return value would over- * write other data. * XXX Following massage may be confusing * because it appears also if the return value * was declared inconsistently. But this * behaviour matches pcc based lint, so it is * accepted for now. */ pos1 = xstrdup(mkpos(&def->s_pos)); /* %s value must be decl. before use %s :: %s */ msg(17, hte->h_name, pos1, mkpos(&call->f_pos)); free(pos1); } continue; } if (!eq || (sflag && warn)) { pos1 = xstrdup(mkpos(&def->s_pos)); /* %s value used inconsistenty\t%s :: %s */ msg(4, hte->h_name, pos1, mkpos(&call->f_pos)); free(pos1); } } }
Tree eqtree(int op, Tree l, Tree r) { Type xty = unqual(l->type), yty = unqual(r->type); if (isptr(xty) && isnullptr(r) || isptr(xty) && !isfunc(xty->type) && isvoidptr(yty) || (isptr(xty) && isptr(yty) && eqtype(unqual(xty->type), unqual(yty->type), 1))) { Type ty = unsignedptr; l = cast(l, ty); r = cast(r, ty); return simplify(mkop(op,ty), inttype, l, r); } if (isptr(yty) && isnullptr(l) || isptr(yty) && !isfunc(yty->type) && isvoidptr(xty)) return eqtree(op, r, l); return cmptree(op, l, r); }
static int exprcmp(Case *c1, Case *c2) { int ct, n; Node *n1, *n2; // sort non-constants last if(c1->type != Texprconst) return +1; if(c2->type != Texprconst) return -1; n1 = c1->node->left; n2 = c2->node->left; // sort by type (for switches on interface) ct = n1->val.ctype; if(ct != n2->val.ctype) return ct - n2->val.ctype; if(!eqtype(n1->type, n2->type)) { if(n1->type->vargen > n2->type->vargen) return +1; else return -1; } // sort by constant value n = 0; switch(ct) { case CTFLT: n = mpcmpfltflt(n1->val.u.fval, n2->val.u.fval); break; case CTINT: case CTRUNE: n = mpcmpfixfix(n1->val.u.xval, n2->val.u.xval); break; case CTSTR: n = cmpslit(n1, n2); break; } return n; }
int oaslit(Node *n, NodeList **init) { int ctxt; if(n->left == N || n->right == N) goto no; if(n->left->type == T || n->right->type == T) goto no; if(!simplename(n->left)) goto no; if(!eqtype(n->left->type, n->right->type)) goto no; // context is init() function. // implies generated data executed // exactly once and not subject to races. ctxt = 0; // if(n->dodata == 1) // ctxt = 1; switch(n->right->op) { default: goto no; case OSTRUCTLIT: case OARRAYLIT: case OMAPLIT: if(vmatch1(n->left, n->right)) goto no; anylit(ctxt, n->right, n->left, init); break; } n->op = OEMPTY; return 1; no: // not a special composit literal assignment return 0; }
Type assign(Type xty, Tree e) { Type yty = unqual(e->type); xty = unqual(xty); if (isenum(xty)) xty = xty->type; if (xty->size == 0 || yty->size == 0) return NULL; if ( isarith(xty) && isarith(yty) || isstruct(xty) && xty == yty) return xty; if (isptr(xty) && isnullptr(e)) return xty; if ((isvoidptr(xty) && isptr(yty) || isptr(xty) && isvoidptr(yty)) && ( (isconst(xty->type) || !isconst(yty->type)) && (isvolatile(xty->type) || !isvolatile(yty->type)))) return xty; if ((isptr(xty) && isptr(yty) && eqtype(unqual(xty->type), unqual(yty->type), 1)) && ( (isconst(xty->type) || !isconst(yty->type)) && (isvolatile(xty->type) || !isvolatile(yty->type)))) return xty; if (isptr(xty) && isptr(yty) && ( (isconst(xty->type) || !isconst(yty->type)) && (isvolatile(xty->type) || !isvolatile(yty->type)))) { Type lty = unqual(xty->type), rty = unqual(yty->type); if (isenum(lty) && rty == inttype || isenum(rty) && lty == inttype) { if (Aflag >= 1) warning("assignment between `%t' and `%t' is compiler-dependent\n", xty, yty); return xty; } } return NULL; }
/* * Compares arguments of two prototypes */ static int eqargs(type_t *tp1, type_t *tp2, int *warn) { type_t **a1, **a2; if (tp1->t_vararg != tp2->t_vararg) return (0); a1 = tp1->t_args; a2 = tp2->t_args; while (*a1 != NULL && *a2 != NULL) { if (eqtype(*a1, *a2, 1, 0, 0, warn) == 0) return (0); a1++; a2++; } return (*a1 == *a2); }
/* * Print warnings for inconsistent argument declarations. */ static void chkadecl(hte_t *hte, sym_t *def, sym_t *decl) { /* LINTED (automatic hides external declaration: warn) */ int osdef, eq, warn, n; sym_t *sym1, *sym; type_t **ap1, **ap2, *tp1, *tp2; char *pos1; const char *pos2; osdef = 0; if (def != NULL) { osdef = def->s_osdef; sym1 = def; } else if (decl != NULL && TP(decl->s_type)->t_proto) { sym1 = decl; } else { return; } if (TP(sym1->s_type)->t_tspec != FUNC) return; /* * XXX Prototypes should also be compared with old style function * declarations. */ for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) { if (sym == sym1 || !TP(sym->s_type)->t_proto) continue; ap1 = TP(sym1->s_type)->t_args; ap2 = TP(sym->s_type)->t_args; n = 0; while (*ap1 != NULL && *ap2 != NULL) { warn = 0; eq = eqtype(*ap1, *ap2, 1, osdef, 0, &warn); if (!eq || warn) { pos1 = xstrdup(mkpos(&sym1->s_pos)); pos2 = mkpos(&sym->s_pos); /* %s, arg %d declared inconsistently ... */ msg(11, hte->h_name, n + 1, pos1, pos2); free(pos1); } n++; ap1++; ap2++; } if (*ap1 == *ap2) { tp1 = TP(sym1->s_type); tp2 = TP(sym->s_type); if (tp1->t_vararg == tp2->t_vararg) continue; if (tp2->t_vararg && sym1->s_va && sym1->s_nva == n && !sflag) { continue; } } /* %s: variable # of args declared\t%s :: %s */ pos1 = xstrdup(mkpos(&sym1->s_pos)); msg(12, hte->h_name, pos1, mkpos(&sym->s_pos)); free(pos1); } }
static Case* mkcaselist(Node *sw, int arg) { Node *n; Case *c, *c1, *c2; NodeList *l; int ord; c = C; ord = 0; for(l=sw->list; l; l=l->next) { n = l->n; c1 = mal(sizeof(*c1)); c1->link = c; c = c1; ord++; if((uint16)ord != ord) fatal("too many cases in switch"); c->ordinal = ord; c->node = n; if(n->left == N) { c->type = Tdefault; continue; } switch(arg) { case Stype: c->hash = 0; if(n->left->op == OLITERAL) { c->type = Ttypenil; continue; } if(istype(n->left->type, TINTER)) { c->type = Ttypevar; continue; } c->hash = typehash(n->left->type); c->type = Ttypeconst; continue; case Snorm: case Strue: case Sfalse: c->type = Texprvar; c->hash = typehash(n->left->type); switch(consttype(n->left)) { case CTFLT: case CTINT: case CTRUNE: case CTSTR: c->type = Texprconst; } continue; } } if(c == C) return C; // sort by value and diagnose duplicate cases switch(arg) { case Stype: c = csort(c, typecmp); for(c1=c; c1!=C; c1=c1->link) { for(c2=c1->link; c2!=C && c2->hash==c1->hash; c2=c2->link) { if(c1->type == Ttypenil || c1->type == Tdefault) break; if(c2->type == Ttypenil || c2->type == Tdefault) break; if(!eqtype(c1->node->left->type, c2->node->left->type)) continue; yyerrorl(c2->node->lineno, "duplicate case %T in type switch\n\tprevious case at %L", c2->node->left->type, c1->node->lineno); } } break; case Snorm: case Strue: case Sfalse: c = csort(c, exprcmp); for(c1=c; c1->link!=C; c1=c1->link) { if(exprcmp(c1, c1->link) != 0) continue; setlineno(c1->link->node); yyerror("duplicate case %N in switch\n\tprevious case at %L", c1->node->left, c1->node->lineno); } break; } // put list back in processing order c = csort(c, ordlcmp); return c; }
void gen(Node *n, int retain) { int i; if(n==0) return; switch(n->t){ case NArrayref: arygen(n->l, n->r, 0, 0L); if(!retain) popgen(n->l->o.s->val->type->r); return; case NBecome: didbecome=1; if(n->l->t==NCall && !bflag){ callgen(n->l, Ibecome); return; } gen(n->l, 1); n=n->r; if(n->o.t==TID) n=typeoftid(n); switch(n->o.t){ case TInt: case TChar: emit(Istoreauto); emitconst(-WS*(4+length(formals))); break; case TArray: case TChan: case TProg: case TStruct: emit(Istoreptrauto); emitconst(-WS*(4+length(formals))); break; case TUnit: break; default: panic("can't compile %t become", n); } scopedecrefgen(); trlrgen(); return; case NBegin: callgen(n->l, Ibegin); return; case NBreak: if(breakloc==-1) lerror(n, "break not in loop"); if(breakloc) /* chain previous break to here */ patch(breakloc, (long)(here()-breakloc-1)*WS); emit(Ijmp); breakloc=here(); emitconst(0L); return; case NCall: callgen(n, Icall); if(!retain) popgen(etypeoft(n->l)->r); return; case NComplete: gen(n->l, retain); return; case NContinue: if(continueloc==-1) lerror(n, "continue not in loop"); if(continueloc) /* chain previous continue to here */ patch(continueloc, (long)(here()-continueloc-1)*WS); emit(Ijmp); continueloc=here(); emitconst(0L); return; case NDecl: case NDeclsc: declare(n, 0, 0, 1); return; case NExpr: switch(n->o.i){ case GE: i=Ige; Binop: gen(n->l, 1); gen(n->r, 1); if(eqtype(etypeof(n->l), &arychartype)){ emit(Istrcmp); constgen(0L); } emit(i); Popit: if(!retain) emit(Ipop); return; case LE: i=Ile; goto Binop; case NE: i=Ine; goto Binop; case EQ: i=Ieq; goto Binop; case '>': i=Igt; goto Binop; case '<': i=Ilt; goto Binop; case '+': i=Iadd; goto Binop; case '-': i=Isub; goto Binop; case '*': i=Imul; goto Binop; case '/': i=Idiv; goto Binop; case '%': i=Imod; goto Binop; case '&': i=Iand; goto Binop; case '|': i=Ior; goto Binop; case '^': i=Ixor; goto Binop; case LSH: i=Ilsh; goto Binop; case RSH: i=Irsh; goto Binop; case ANDAND: condgen(n->l, n->r, Ijmptrue, Ijmpfalse, 0L, 1L, retain); return; case OROR: condgen(n->l, n->r, Ijmpfalse, Ijmptrue, 1L, 0L, retain); return; case CAT: gen(n->l, 1); gen(n->r, 1); emit(Icat); return; case DEL: gen(n->l, 1); gen(n->r, 1); emit(Idel); return; case PRINT: gen(n->l, 1); printgen(n->l); emit(Isprnt); if(!retain) emit(Iprint); return; case SND: gen(n->l, 1); constgen((long)Cissnd); emit(Icommset1); emit(Icommcln1); gen(n->r, 1); if(isptrtype(etypeoft(n->l)->r)) emit(Isndptr); else emit(Isnd); if(!retain) popgen(etypeof(n)); return; case RCV: gen(n->l, 1); constgen(0L); /* not Cissnd */ emit(Icommset1); emit(Icommcln1); if(!retain) popgen(etypeof(n)); return; case '=': gen(n->r, 1); if(retain) dupgen(etypeof(n->r), 1); lgen(n->l); return; case LEN: gen(n->l, 1); emit(Ilen); goto Popit; case REF: if(isptrtype(etypeof(n->l))){ gen(n->l, 1); emit(Iref); }else constgen(1L); goto Popit; case DEF: if(retain && n->l->t==NID && isinttype(etypeof(n->l))){ constgen(1L); return; } /* * don't really need to call lgen1, which will uniquify our * array for us, but it does no harm, and it's easy. */ lgen1(n->l, Idefauto, Idef, Idefary); goto Popit; case UMINUS: gen(n->l, 1); emit(Ineg); goto Popit; case '~': gen(n->l, 1); emit(Inot); goto Popit; case '!': gen(n->l, 1); emit(Ilnot); goto Popit; case INC: lgen1(n->l, Iincauto, Iinc, Iincary); goto Popit; case DEC: lgen1(n->l, Idecauto, Idec, Idecary); goto Popit; default: panic("can't compile %e expression", n); } case NExprlist: /* * This is an arg or element list; first is pushed last */ gen(n->r, 1); gen(n->l, 1); return; case NID: if(!retain) return; switch(type_of(n)->o.t){ case TInt: case TChar: if(n->o.s->val->isauto){ emit(Ipushauto); emitconst(n->o.s->val->store.off); }else{ emit(Ipush); emitconst((long)&n->o.s->val->store.l); } return; case TProg: case TArray: case TChan: case TStruct: if(n->o.s->val->isauto){ emit(Ipushptrauto); emitconst(n->o.s->val->store.off); }else{ emit(Ipushptr); emitconst((long)&n->o.s->val->store.l); } return; case TUnit: if(retain) constgen(0L); return; case TType: lerror(n, "attempt to evaluate type variable %m", n); default: panic("can't compile type %t", n->o.s->val->type); } case NIf: ifgen(n); return; case NList: gen(n->l, 0); gen(n->r, 0); return; case NLoop: loopgen(n); return; case NMk: mkgen(n->l, n->r); return; case NNum: if(retain) constgen(n->o.l); return; case NProg: if(retain) proggen(n->l, n->r); return; case NResult: if(resultloc==0) lerror(n, "result not in val"); gen(n->l, 1); emit(Ijmp); emitconst((long)(resultloc-here()-1)*WS); return; case NScope: pushscope(); if(nscope==1){ int nauto; autooffset=0; emit(Ipushfp); nauto=here(); emitconst(0L); gen(n->l, 0); patch((int)nauto, autooffset); emit(Ipop); /* Ipopfp() */ }else gen(n->l, 0); scopedecrefgen(); popscope(); return; case NSelect: selgen(n->l); return; case NSmash:{ Value *vl, *vr; vl=n->l->o.s->val; vr=n->r->o.s->val; if(vr->type->o.t==TType){ freenode(vl->type); vl->type=dupnode(vr->type); return; } gen(n->r, 1); /* * Free old values; tricky: push as int, pop as ptr */ if(isptrtype(vl->type)){ if(vl->isauto){ emit(Ipushauto); emitconst(vl->store.off); }else{ emit(Ipush); emitconst((long)&vl->store.l); } emit(Ipopptr); } if(vl->isauto){ emit(Istoreauto); emitconst(vl->store.l); return; } emit(Istore); emitconst((long)&vl->store.l); return; } case NString: if(retain){ emit(Ipushdata); emitconst((long)n->o.st); emitstore(n->o.st); n->o.st->ref++; } return; case NStructref: arygen(n->l, n->r, 1, n->o.l); return; case NSwitch: switchgen(n->l, n->r); return; case NUnit: if(retain) constgen(0L); return; case NVal: valgen(n->l); if(!retain) popgen(n->o.n); return; } panic("can't compile node %n", n); return; }
void mkgen(Node *t, Node *v) { switch(t->o.t){ case TAggr: lerror(v, "can't derive type in mk %m", v); case TChar: case TInt: case TUnit: if(v) gen(v, 1); else constgen(0L); return; case TID: mkgen(typeoftid(t), v); return; case TChan: if(v) gen(v, 1); else{ constgen((long)(sizeof(Chan)-sizeof(Store))); mallocgen(t); } return; case TArray: if(v==0){ if(t->l==0) constgen(0L); else gen(t->l, 1); mallocgen(t); return; } gen(v, 1); if(v->t!=NExprlist && eqtype(t, etypeof(v))) return; if(v->t==NString && eqtype(t, etypeof(v))) constgen(v->o.st->len); else constgen(length(v)); emit(Idup); if(t->l) gen(t->l, 1); else constgen(0L); emit(Imax); mallocgen(t); if(t->r->o.t==TChar){ if(v->t==NString) emit(Imemcpychar); else emit(Imemcpycharint); }else emit(Imemcpy); return; case TProg: if(v==0){ v=new(NProg, dupnode(t), (Node *)0, (Node *)0); gen(v, 1); freenode(v); return; } gen(v, 1); return; case TStruct: if(v==0){ mallocgen(t); return; } gen(v, 1); if(v->t!=NExprlist && eqtype(t, etypeof(v))) return; constgen((long)length(v)); mallocgen(t); emit(Imemcpystruct); return; default: panic("mkgen: bad type %t", t); }
void walkrange(Node *n) { Node *ohv1, *hv1, *hv2; // hidden (old) val 1, 2 Node *ha, *hit; // hidden aggregate, iterator Node *a, *v1, *v2; // not hidden aggregate, val 1, 2 Node *fn; NodeList *body, *init; Type *th, *t; t = n->type; init = nil; a = n->right; if(t->etype == TSTRING && !eqtype(t, types[TSTRING])) { a = nod(OCONV, n->right, N); a->type = types[TSTRING]; } ha = nod(OXXX, N, N); tempname(ha, a->type); init = list(init, nod(OAS, ha, a)); v1 = n->list->n; hv1 = N; v2 = N; if(n->list->next) v2 = n->list->next->n; hv2 = N; switch(t->etype) { default: fatal("walkrange"); case TARRAY: hv1 = nod(OXXX, N, n); tempname(hv1, types[TINT]); init = list(init, nod(OAS, hv1, N)); n->ntest = nod(OLT, hv1, nod(OLEN, ha, N)); n->nincr = nod(OASOP, hv1, nodintconst(1)); n->nincr->etype = OADD; body = list1(nod(OAS, v1, hv1)); if(v2) body = list(body, nod(OAS, v2, nod(OINDEX, ha, hv1))); break; case TMAP: th = typ(TARRAY); th->type = ptrto(types[TUINT8]); th->bound = (sizeof(struct Hiter) + widthptr - 1) / widthptr; hit = nod(OXXX, N, N); tempname(hit, th); fn = syslook("mapiterinit", 1); argtype(fn, t->down); argtype(fn, t->type); argtype(fn, th); init = list(init, mkcall1(fn, T, nil, ha, nod(OADDR, hit, N))); n->ntest = nod(ONE, nod(OINDEX, hit, nodintconst(0)), nodnil()); fn = syslook("mapiternext", 1); argtype(fn, th); n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N)); if(v2 == N) { fn = syslook("mapiter1", 1); argtype(fn, th); argtype(fn, t->down); a = nod(OAS, v1, mkcall1(fn, t->down, nil, nod(OADDR, hit, N))); } else { fn = syslook("mapiter2", 1); argtype(fn, th); argtype(fn, t->down); argtype(fn, t->type); a = nod(OAS2, N, N); a->list = list(list1(v1), v2); a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, nod(OADDR, hit, N))); } body = list1(a); break; case TCHAN: hv1 = nod(OXXX, N, n); tempname(hv1, t->type); n->ntest = nod(ONOT, nod(OCLOSED, ha, N), N); n->ntest->ninit = list1(nod(OAS, hv1, nod(ORECV, ha, N))); body = list1(nod(OAS, v1, hv1)); break; case TSTRING: ohv1 = nod(OXXX, N, N); tempname(ohv1, types[TINT]); hv1 = nod(OXXX, N, N); tempname(hv1, types[TINT]); init = list(init, nod(OAS, hv1, N)); if(v2 == N) a = nod(OAS, hv1, mkcall("stringiter", types[TINT], nil, ha, hv1)); else { hv2 = nod(OXXX, N, N); tempname(hv2, types[TINT]); a = nod(OAS2, N, N); a->list = list(list1(hv1), hv2); fn = syslook("stringiter2", 0); a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, ha, hv1)); } n->ntest = nod(ONE, hv1, nodintconst(0)); n->ntest->ninit = list(list1(nod(OAS, ohv1, hv1)), a); body = list1(nod(OAS, v1, ohv1)); if(v2 != N) body = list(body, nod(OAS, v2, hv2)); break; } n->op = OFOR; typechecklist(init, Etop); n->ninit = concat(n->ninit, init); typechecklist(n->ntest->ninit, Etop); typecheck(&n->ntest, Erv); typecheck(&n->nincr, Etop); typechecklist(body, Etop); n->nbody = concat(body, n->nbody); walkstmt(&n); }
int gen_as_init(Node *n) { Node *nr, *nl; Node nam, nod1; if(n->dodata == 0) goto no; nr = n->right; nl = n->left; if(nr == N) { if(!stataddr(&nam, nl)) goto no; if(nam.class != PEXTERN) goto no; goto yes; } if(nr->type == T || !eqtype(nl->type, nr->type)) goto no; if(!stataddr(&nam, nl)) goto no; if(nam.class != PEXTERN) goto no; switch(nr->op) { default: goto no; case OCONVNOP: nr = nr->left; if(nr == N || nr->op != OSLICEARR) goto no; // fall through case OSLICEARR: if(nr->right->op == OKEY && nr->right->left == N && nr->right->right == N) { nr = nr->left; goto slice; } goto no; case OLITERAL: break; } switch(nr->type->etype) { default: goto no; case TBOOL: case TINT8: case TUINT8: case TINT16: case TUINT16: case TINT32: case TUINT32: case TINT64: case TUINT64: case TINT: case TUINT: case TUINTPTR: case TPTR32: case TPTR64: case TFLOAT32: case TFLOAT64: gused(N); // in case the data is the dest of a goto gdata(&nam, nr, nr->type->width); break; case TCOMPLEX64: case TCOMPLEX128: gused(N); // in case the data is the dest of a goto gdatacomplex(&nam, nr->val.u.cval); break; case TSTRING: gused(N); // in case the data is the dest of a goto gdatastring(&nam, nr->val.u.sval); break; } yes: return 1; slice: gused(N); // in case the data is the dest of a goto nl = nr; if(nr == N || nr->op != OADDR) goto no; nr = nr->left; if(nr == N || nr->op != ONAME) goto no; // nr is the array being converted to a slice if(nr->type == T || nr->type->etype != TARRAY || nr->type->bound < 0) goto no; nam.xoffset += Array_array; gdata(&nam, nl, types[tptr]->width); nam.xoffset += Array_nel-Array_array; nodconst(&nod1, types[TINT32], nr->type->bound); gdata(&nam, &nod1, types[TINT32]->width); nam.xoffset += Array_cap-Array_nel; gdata(&nam, &nod1, types[TINT32]->width); goto yes; no: if(n->dodata == 2) { dump("\ngen_as_init", n); fatal("gen_as_init couldnt make data statement"); } return 0; }
/* * type check switch statement */ void typecheckswitch(Node *n) { int top, lno; Type *t; NodeList *l, *ll; Node *ncase, *nvar; Node *def; lno = lineno; typechecklist(n->ninit, Etop); if(n->ntest != N && n->ntest->op == OTYPESW) { // type switch top = Etype; typecheck(&n->ntest->right, Erv); t = n->ntest->right->type; if(t != T && t->etype != TINTER) yyerror("cannot type switch on non-interface value %+N", n->ntest->right); } else { // value switch top = Erv; if(n->ntest) { typecheck(&n->ntest, Erv); defaultlit(&n->ntest, T); t = n->ntest->type; } else t = types[TBOOL]; } n->type = t; def = N; for(l=n->list; l; l=l->next) { ncase = l->n; setlineno(n); if(ncase->list == nil) { // default if(def != N) yyerror("multiple defaults in switch (first at %L)", def->lineno); else def = ncase; } else { for(ll=ncase->list; ll; ll=ll->next) { setlineno(ll->n); typecheck(&ll->n, Erv | Etype); if(ll->n->type == T || t == T) continue; switch(top) { case Erv: // expression switch defaultlit(&ll->n, t); if(ll->n->op == OTYPE) yyerror("type %T is not an expression", ll->n->type); else if(ll->n->type != T && !eqtype(ll->n->type, t)) yyerror("case %+N in %T switch", ll->n, t); break; case Etype: // type switch if(ll->n->op == OLITERAL && istype(ll->n->type, TNIL)) ; else if(ll->n->op != OTYPE && ll->n->type != T) yyerror("%#N is not a type", ll->n); break; } } } if(top == Etype && n->type != T) { ll = ncase->list; nvar = ncase->nname; if(nvar != N) { if(ll && ll->next == nil && ll->n->type != T && !istype(ll->n->type, TNIL)) { // single entry type switch nvar->ntype = typenod(ll->n->type); } else { // multiple entry type switch or default nvar->ntype = typenod(n->type); } } } typechecklist(ncase->nbody, Etop); } lineno = lno; }
void walkrange(Node *n) { Node *ohv1, *hv1, *hv2; // hidden (old) val 1, 2 Node *ha, *hit; // hidden aggregate, iterator Node *hn, *hp; // hidden len, pointer Node *hb; // hidden bool Node *a, *v1, *v2; // not hidden aggregate, val 1, 2 Node *fn, *tmp; NodeList *body, *init; Type *th, *t; int lno; t = n->type; init = nil; a = n->right; lno = setlineno(a); if(t->etype == TSTRING && !eqtype(t, types[TSTRING])) { a = nod(OCONV, n->right, N); a->type = types[TSTRING]; } v1 = n->list->n; v2 = N; if(n->list->next) v2 = n->list->next->n; hv2 = N; if(v2 == N && t->etype == TARRAY) { // will have just one reference to argument. // no need to make a potentially expensive copy. ha = a; } else { ha = temp(a->type); init = list(init, nod(OAS, ha, a)); } switch(t->etype) { default: fatal("walkrange"); case TARRAY: hv1 = temp(types[TINT]); hn = temp(types[TINT]); hp = nil; init = list(init, nod(OAS, hv1, N)); init = list(init, nod(OAS, hn, nod(OLEN, ha, N))); if(v2) { hp = temp(ptrto(n->type->type)); tmp = nod(OINDEX, ha, nodintconst(0)); tmp->etype = 1; // no bounds check init = list(init, nod(OAS, hp, nod(OADDR, tmp, N))); } n->ntest = nod(OLT, hv1, hn); n->nincr = nod(OASOP, hv1, nodintconst(1)); n->nincr->etype = OADD; if(v2 == N) body = list1(nod(OAS, v1, hv1)); else { a = nod(OAS2, N, N); a->list = list(list1(v1), v2); a->rlist = list(list1(hv1), nod(OIND, hp, N)); body = list1(a); tmp = nod(OADD, hp, nodintconst(t->type->width)); tmp->type = hp->type; tmp->typecheck = 1; tmp->right->type = types[tptr]; tmp->right->typecheck = 1; body = list(body, nod(OAS, hp, tmp)); } break; case TMAP: th = typ(TARRAY); th->type = ptrto(types[TUINT8]); // see ../../pkg/runtime/hashmap.h:/hash_iter // Size in words. th->bound = 5 + 4*3 + 4*4/widthptr; hit = temp(th); fn = syslook("mapiterinit", 1); argtype(fn, t->down); argtype(fn, t->type); argtype(fn, th); init = list(init, mkcall1(fn, T, nil, typename(t), ha, nod(OADDR, hit, N))); n->ntest = nod(ONE, nod(OINDEX, hit, nodintconst(0)), nodnil()); fn = syslook("mapiternext", 1); argtype(fn, th); n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N)); if(v2 == N) { fn = syslook("mapiter1", 1); argtype(fn, th); argtype(fn, t->down); a = nod(OAS, v1, mkcall1(fn, t->down, nil, nod(OADDR, hit, N))); } else { fn = syslook("mapiter2", 1); argtype(fn, th); argtype(fn, t->down); argtype(fn, t->type); a = nod(OAS2, N, N); a->list = list(list1(v1), v2); a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, nod(OADDR, hit, N))); } body = list1(a); break; case TCHAN: hv1 = temp(t->type); hb = temp(types[TBOOL]); n->ntest = nod(ONE, hb, nodbool(0)); a = nod(OAS2RECV, N, N); a->typecheck = 1; a->list = list(list1(hv1), hb); a->rlist = list1(nod(ORECV, ha, N)); n->ntest->ninit = list1(a); body = list1(nod(OAS, v1, hv1)); break; case TSTRING: ohv1 = temp(types[TINT]); hv1 = temp(types[TINT]); init = list(init, nod(OAS, hv1, N)); if(v2 == N) a = nod(OAS, hv1, mkcall("stringiter", types[TINT], nil, ha, hv1)); else { hv2 = temp(runetype); a = nod(OAS2, N, N); a->list = list(list1(hv1), hv2); fn = syslook("stringiter2", 0); a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, ha, hv1)); } n->ntest = nod(ONE, hv1, nodintconst(0)); n->ntest->ninit = list(list1(nod(OAS, ohv1, hv1)), a); body = list1(nod(OAS, v1, ohv1)); if(v2 != N) body = list(body, nod(OAS, v2, hv2)); break; } n->op = OFOR; typechecklist(init, Etop); n->ninit = concat(n->ninit, init); typechecklist(n->ntest->ninit, Etop); typecheck(&n->ntest, Erv); typecheck(&n->nincr, Etop); typechecklist(body, Etop); n->nbody = concat(body, n->nbody); walkstmt(&n); lineno = lno; }
/* * Check a single argument in a function call. * * hte a pointer to the hash table entry of the function * n the number of the argument (1..) * def the function definition or NULL * decl prototype declaration, old style declaration or NULL * pos1p position of definition, declaration of first call * call1 first call, if both def and decl are old style def/decl * call checked call * arg1 currently checked argument of def/decl/call1 * arg2 currently checked argument of call * */ static void chkau(hte_t *hte, int n, sym_t *def, sym_t *decl, pos_t *pos1p, fcall_t *call1, fcall_t *call, type_t *arg1, type_t *arg2) { /* LINTED (automatic hides external declaration: warn) */ int promote, asgn, warn; tspec_t t1, t2; arginf_t *ai, *ai1; char *pos1; /* * If a function definition is available (def != NULL), we compare the * function call (call) with the definition. Otherwise, if a function * definition is available and it is not an old style definition * (decl != NULL && TP(decl->s_type)->t_proto), we compare the call * with this declaration. Otherwise we compare it with the first * call we have found (call1). */ /* arg1 must be promoted if it stems from an old style definition */ promote = def != NULL && def->s_osdef; /* * If we compair with a definition or declaration, we must perform * the same checks for qualifiers in indirected types as in * assignments. */ asgn = def != NULL || (decl != NULL && TP(decl->s_type)->t_proto); warn = 0; if (eqtype(arg1, arg2, 1, promote, asgn, &warn) && (!sflag || !warn)) return; /* * Other lint implementations print warnings as soon as the type * of an argument does not match exactly the expected type. The * result are lots of warnings which are really not necessary. * We print a warning only if * (0) at least one type is not an integer type and types differ * (1) hflag is set and types differ * (2) types differ, except in signedness * If the argument is an integer constant whose msb is not set, * signedness is ignored (e.g. 0 matches both signed and unsigned * int). This is with and without hflag. * If the argument is an integer constant with value 0 and the * expected argument is of type pointer and the width of the * integer constant is the same as the width of the pointer, * no warning is printed. */ t1 = arg1->t_tspec; t2 = arg2->t_tspec; if (isityp(t1) && isityp(t2) && !arg1->t_isenum && !arg2->t_isenum) { if (promote) { /* * XXX Here is a problem: Although it is possible to * pass an int where a char/short it expected, there * may be loss in significant digits. We should first * check for const arguments if they can be converted * into the original parameter type. */ if (t1 == FLOAT) { t1 = DOUBLE; } else if (t1 == CHAR || t1 == SCHAR) { t1 = INT; } else if (t1 == UCHAR) { t1 = tflag ? UINT : INT; } else if (t1 == SHORT) { t1 = INT; } else if (t1 == USHORT) { /* CONSTCOND */ t1 = INT_MAX < USHRT_MAX || tflag ? UINT : INT; } } if (styp(t1) == styp(t2)) { /* * types differ only in signedness; get information * about arguments */ /* * treat a definition like a call with variable * arguments */ ai1 = call1 != NULL ? call1->f_args : NULL; /* * if two calls are compared, ai1 is set to the * information for the n-th argument, if this was * a constant, otherwise to NULL */ for ( ; ai1 != NULL; ai1 = ai1->a_nxt) { if (ai1->a_num == n) break; } /* * ai is set to the information of the n-th arg * of the (second) call, if this was a constant, * otherwise to NULL */ for (ai = call->f_args; ai != NULL; ai = ai->a_nxt) { if (ai->a_num == n) break; } if (ai1 == NULL && ai == NULL) { /* no constant at all */ if (!hflag) return; } else if (ai1 == NULL || ai == NULL) { /* one constant */ if (ai == NULL) ai = ai1; if (ai->a_zero || ai->a_pcon) /* same value in signed and unsigned */ return; /* value (not representation) differently */ } else { /* * two constants, one signed, one unsigned; * if the msb of one of the constants is set, * the argument is used inconsistently. */ if (!ai1->a_ncon && !ai->a_ncon) return; } } } else if (t1 == PTR && isityp(t2)) { for (ai = call->f_args; ai != NULL; ai = ai->a_nxt) { if (ai->a_num == n) break; } /* * Vendor implementations of lint (e.g. HP-UX, Digital UNIX) * don't care about the size of the integer argument, * only whether or not it is zero. We do the same. */ if (ai != NULL && ai->a_zero) return; } pos1 = xstrdup(mkpos(pos1p)); /* %s, arg %d used inconsistently\t%s :: %s */ msg(6, hte->h_name, n, pos1, mkpos(&call->f_pos)); free(pos1); }
void mergetemp(Prog *firstp) { int i, j, nvar, ninuse, nfree, nkill; TempVar *var, *v, *v1, **bystart, **inuse; TempFlow *r; NodeList *l, **lp; Node *n; Prog *p, *p1; Type *t; ProgInfo info, info1; int32 gen; Graph *g; enum { Debug = 0 }; g = flowstart(firstp, sizeof(TempFlow)); if(g == nil) return; // Build list of all mergeable variables. nvar = 0; for(l = curfn->dcl; l != nil; l = l->next) if(canmerge(l->n)) nvar++; var = calloc(nvar*sizeof var[0], 1); nvar = 0; for(l = curfn->dcl; l != nil; l = l->next) { n = l->n; if(canmerge(n)) { v = &var[nvar++]; n->opt = v; v->node = n; } } // Build list of uses. // We assume that the earliest reference to a temporary is its definition. // This is not true of variables in general but our temporaries are all // single-use (that's why we have so many!). for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) { p = r->f.prog; proginfo(&info, p); if(p->from.node != N && p->from.node->opt && p->to.node != N && p->to.node->opt) fatal("double node %P", p); if((n = p->from.node) != N && (v = n->opt) != nil || (n = p->to.node) != N && (v = n->opt) != nil) { if(v->def == nil) v->def = r; r->uselink = v->use; v->use = r; if(n == p->from.node && (info.flags & LeftAddr)) v->addr = 1; } } if(Debug > 1) dumpit("before", g->start, 0); nkill = 0; // Special case. for(v = var; v < var+nvar; v++) { if(v->addr) continue; // Used in only one instruction, which had better be a write. if((r = v->use) != nil && r->uselink == nil) { p = r->f.prog; proginfo(&info, p); if(p->to.node == v->node && (info.flags & RightWrite) && !(info.flags & RightRead)) { p->as = ANOP; p->to = zprog.to; v->removed = 1; if(Debug) print("drop write-only %S\n", v->node->sym); } else fatal("temp used and not set: %P", p); nkill++; continue; } // Written in one instruction, read in the next, otherwise unused, // no jumps to the next instruction. Happens mainly in 386 compiler. if((r = v->use) != nil && r->f.link == &r->uselink->f && r->uselink->uselink == nil && uniqp(r->f.link) == &r->f) { p = r->f.prog; proginfo(&info, p); p1 = r->f.link->prog; proginfo(&info1, p1); enum { SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD, }; if(p->from.node == v->node && p1->to.node == v->node && (info.flags & Move) && !((info.flags|info1.flags) & (LeftAddr|RightAddr)) && (info.flags & SizeAny) == (info1.flags & SizeAny)) { p1->from = p->from; excise(&r->f); v->removed = 1; if(Debug) print("drop immediate-use %S\n", v->node->sym); } nkill++; continue; } } // Traverse live range of each variable to set start, end. // Each flood uses a new value of gen so that we don't have // to clear all the r->f.active words after each variable. gen = 0; for(v = var; v < var+nvar; v++) { gen++; for(r = v->use; r != nil; r = r->uselink) mergewalk(v, r, gen); } // Sort variables by start. bystart = malloc(nvar*sizeof bystart[0]); for(i=0; i<nvar; i++) bystart[i] = &var[i]; qsort(bystart, nvar, sizeof bystart[0], startcmp); // List of in-use variables, sorted by end, so that the ones that // will last the longest are the earliest ones in the array. // The tail inuse[nfree:] holds no-longer-used variables. // In theory we should use a sorted tree so that insertions are // guaranteed O(log n) and then the loop is guaranteed O(n log n). // In practice, it doesn't really matter. inuse = malloc(nvar*sizeof inuse[0]); ninuse = 0; nfree = nvar; for(i=0; i<nvar; i++) { v = bystart[i]; if(v->addr || v->removed) continue; // Expire no longer in use. while(ninuse > 0 && inuse[ninuse-1]->end < v->start) { v1 = inuse[--ninuse]; inuse[--nfree] = v1; } // Find old temp to reuse if possible. t = v->node->type; for(j=nfree; j<nvar; j++) { v1 = inuse[j]; if(eqtype(t, v1->node->type)) { inuse[j] = inuse[nfree++]; if(v1->merge) v->merge = v1->merge; else v->merge = v1; nkill++; break; } } // Sort v into inuse. j = ninuse++; while(j > 0 && inuse[j-1]->end < v->end) { inuse[j] = inuse[j-1]; j--; } inuse[j] = v; } if(Debug) { print("%S [%d - %d]\n", curfn->nname->sym, nvar, nkill); for(v=var; v<var+nvar; v++) { print("var %#N %T %lld-%lld", v->node, v->node->type, v->start, v->end); if(v->addr) print(" addr=1"); if(v->removed) print(" dead=1"); if(v->merge) print(" merge %#N", v->merge->node); if(v->start == v->end) print(" %P", v->def->f.prog); print("\n"); } if(Debug > 1) dumpit("after", g->start, 0); } // Update node references to use merged temporaries. for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) { p = r->f.prog; if((n = p->from.node) != N && (v = n->opt) != nil && v->merge != nil) p->from.node = v->merge->node; if((n = p->to.node) != N && (v = n->opt) != nil && v->merge != nil) p->to.node = v->merge->node; } // Delete merged nodes from declaration list. for(lp = &curfn->dcl; (l = *lp); ) { curfn->dcl->end = l; n = l->n; v = n->opt; if(v && (v->merge || v->removed)) { *lp = l->next; continue; } lp = &l->next; } // Clear aux structures. for(v=var; v<var+nvar; v++) v->node->opt = nil; free(var); free(bystart); free(inuse); flowend(g); }