storename := array[Dend] of { Dtype => "type", Dfn => "function", Dglobal => "global", Darg => "argument", Dlocal => "local", Dconst => "con", Dfield => "field", Dtag => "pick tag", Dimport => "import", Dunbound => "unbound", Dundef => "undefined", Dwundef => "undefined", }; storeart := array[Dend] of { Dtype => "a ", Dfn => "a ", Dglobal => "a ", Darg => "an ", Dlocal => "a ", Dconst => "a ", Dfield => "a ", Dtag => "a ", Dimport => "an ", Dunbound => "", Dundef => "", Dwundef => "", }; storespace := array[Dend] of { Dtype => 0, Dfn => 0, Dglobal => 1, Darg => 1, Dlocal => 1, Dconst => 0, Dfield => 1, Dtag => 0, Dimport => 0, Dunbound => 0, Dundef => 0, Dwundef => 0, }; impdecl: ref Decl; impdecls: ref Dlist; scopes := array[MaxScope] of ref Decl; tails := array[MaxScope] of ref Decl; scopekind := array[MaxScope] of byte; scopenode := array[MaxScope] of ref Node; iota: ref Decl; zdecl: Decl; popscopes() { d: ref Decl; # # clear out any decls left in syms # while(scope >= ScopeBuiltin){ for(d = scopes[scope--]; d != nil; d = d.next){ if(d.sym != nil){ d.sym.decl = d.old; d.old = nil; } } } for(id := impdecls; id != nil; id = id.next){ for(d = id.d.ty.ids; d != nil; d = d.next){ d.sym.decl = nil; d.old = nil; } } impdecls = nil; scope = ScopeBuiltin; scopes[ScopeBuiltin] = nil; tails[ScopeBuiltin] = nil; } declstart() { iota = mkids(nosrc, enter("iota", 0), tint, nil); iota.init = mkconst(nosrc, big 0); scope = ScopeNils; scopes[ScopeNils] = nil; tails[ScopeNils] = nil; nildecl = mkdecl(nosrc, Dglobal, tany); nildecl.sym = enter("nil", 0); installids(Dglobal, nildecl); d := mkdecl(nosrc, Dglobal, tstring); d.sym = enterstring(""); installids(Dglobal, d); scope = ScopeGlobal; scopes[ScopeGlobal] = nil; tails[ScopeGlobal] = nil; } redecl(d: ref Decl) { old := d.sym.decl; if(old.store == Dwundef) return; error(d.src.start, "redeclaration of "+declconv(d)+", previously declared as "+storeconv(old)+" on line "+ lineconv(old.src.start)); } checkrefs(d: ref Decl) { id, m: ref Decl; refs: int; for(; d != nil; d = d.next){ if(d.das != byte 0) d.refs--; case d.store{ Dtype => refs = d.refs; if(d.ty.kind == Tadt){ for(id = d.ty.ids; id != nil; id = id.next){ d.refs += id.refs; if(id.store != Dfn) continue; if(id.init == nil && id.link == nil && d.importid == nil) error(d.src.start, "function "+d.sym.name+"."+id.sym.name+" not defined"); if(superwarn && !id.refs && d.importid == nil) warn(d.src.start, "function "+d.sym.name+"."+id.sym.name+" not referenced"); } } if(d.ty.kind == Tmodule){ for(id = d.ty.ids; id != nil; id = id.next){ refs += id.refs; if(id.iface != nil) id.iface.refs += id.refs; if(id.store == Dtype){ for(m = id.ty.ids; m != nil; m = m.next){ refs += m.refs; if(m.iface != nil) m.iface.refs += m.refs; } } } d.refs = refs; } if(superwarn && !refs && d.importid == nil) warn(d.src.start, declconv(d)+" not referenced"); Dglobal => if(superwarn && !d.refs && d.sym != nil && d.sym.name[0] != '.') warn(d.src.start, declconv(d)+" not referenced"); Dlocal or Darg => if(!d.refs && d.sym != nil && d.sym.name != nil && d.sym.name[0] != '.') warn(d.src.start, declconv(d)+" not referenced"); Dconst => if(superwarn && !d.refs && d.sym != nil) warn(d.src.start, declconv(d)+" not referenced"); Dfn => if(d.init == nil && d.importid == nil) error(d.src.start, declconv(d)+" not defined"); if(superwarn && !d.refs) warn(d.src.start, declconv(d)+" not referenced"); Dimport => if(superwarn && !d.refs) warn(d.src.start, declconv(d)+" not referenced"); } if(d.das != byte 0) d.refs++; } } vardecl(ids: ref Decl, t: ref Type): ref Node { n := mkn(Ovardecl, mkn(Oseq, nil, nil), nil); n.decl = ids; n.ty = t; return n; } vardecled(n: ref Node) { store := Dlocal; if(scope == ScopeGlobal) store = Dglobal; if(n.ty.kind == Texception && n.ty.cons == byte 1){ store = Dconst; fatal("Texception in vardecled"); } ids := n.decl; installids(store, ids); t := n.ty; for(last := ids; ids != nil; ids = ids.next){ ids.ty = t; last = ids; } n.left.decl = last; } condecl(ids: ref Decl, init: ref Node): ref Node { n := mkn(Ocondecl, mkn(Oseq, nil, nil), init); n.decl = ids; return n; } condecled(n: ref Node) { ids := n.decl; installids(Dconst, ids); for(last := ids; ids != nil; ids = ids.next){ ids.ty = tunknown; last = ids; } n.left.decl = last; } exdecl(ids: ref Decl, tids: ref Decl): ref Node { n: ref Node; t: ref Type; t = mktype(ids.src.start, ids.src.stop, Texception, nil, tids); t.cons = byte 1; n = mkn(Oexdecl, mkn(Oseq, nil, nil), nil); n.decl = ids; n.ty = t; return n; } exdecled(n: ref Node) { ids, last: ref Decl; t: ref Type; ids = n.decl; installids(Dconst, ids); t = n.ty; for(last = ids; ids != nil; ids = ids.next){ ids.ty = t; last = ids; } n.left.decl = last; } importdecl(m: ref Node, ids: ref Decl): ref Node { n := mkn(Oimport, mkn(Oseq, nil, nil), m); n.decl = ids; return n; } importdecled(n: ref Node) { ids := n.decl; installids(Dimport, ids); for(last := ids; ids != nil; ids = ids.next){ ids.ty = tunknown; last = ids; } n.left.decl = last; } mkscope(body: ref Node): ref Node { n := mkn(Oscope, nil, body); if(body != nil) n.src = body.src; return n; } fndecl(n: ref Node, t: ref Type, body: ref Node): ref Node { n = mkbin(Ofunc, n, body); n.ty = t; return n; } fndecled(n: ref Node) { left := n.left; if(left.op == Oname){ d := left.decl.sym.decl; if(d == nil || d.store == Dimport){ d = mkids(left.src, left.decl.sym, n.ty, nil); installids(Dfn, d); } left.decl = d; d.refs++; } if(left.op == Odot) pushscope(nil, Sother); if(n.ty.polys != nil){ pushscope(nil, Sother); installids(Dtype, n.ty.polys); } pushscope(nil, Sother); installids(Darg, n.ty.ids); n.ty.ids = popscope(); if(n.ty.val != nil) mergepolydecs(n.ty); if(n.ty.polys != nil) n.ty.polys = popscope(); if(left.op == Odot) popscope(); } # # check the function declaration only # the body will be type checked later by fncheck # fnchk(n: ref Node): ref Decl { bad := 0; d := n.left.decl; if(n.left.op == Odot) d = n.left.right.decl; if(d == nil) fatal("decl() fnchk nil"); n.left.decl = d; if(d.store == Dglobal || d.store == Dfield) d.store = Dfn; if(d.store != Dfn || d.init != nil){ nerror(n, "redeclaration of function "+dotconv(d)+", previously declared as " +storeconv(d)+" on line "+lineconv(d.src.start)); if(d.store == Dfn && d.init != nil) bad = 1; } d.init = n; t := n.ty; inadt := d.dot; if(inadt != nil && (inadt.store != Dtype || inadt.ty.kind != Tadt)) inadt = nil; if(n.left.op == Odot){ pushscope(nil, Sother); adtp := outerpolys(n.left); if(adtp != nil) installids(Dtype, adtp); if(!polyequal(adtp, n.decl)) nerror(n, "adt polymorphic type mismatch"); n.decl = nil; } t = validtype(t, inadt); if(n.left.op == Odot) popscope(); if(debug['d']) print("declare function %s ty %s newty %s\n", dotconv(d), typeconv(d.ty), typeconv(t)); t = usetype(t); if(!polyequal(d.ty.polys, t.polys)) nerror(n, "function polymorphic type mismatch"); if(!tcompat(d.ty, t, 0)) nerror(n, "type mismatch: "+dotconv(d)+" defined as " +typeconv(t)+" declared as "+typeconv(d.ty)+" on line "+lineconv(d.src.start)); else if(!raisescompat(d.ty.eraises, t.eraises)) nerror(n, "raises mismatch: " + dotconv(d)); if(t.varargs != byte 0) nerror(n, "cannot define functions with a '*' argument, such as "+dotconv(d)); t.eraises = d.ty.eraises; d.ty = t; d.offset = idoffsets(t.ids, MaxTemp, IBY2WD); d.src = n.src; d.locals = nil; n.ty = t; if(bad) return nil; return d; } globalas(dst: ref Node, v: ref Node, valok: int): ref Node { if(v == nil) return nil; if(v.op == Oas || v.op == Odas){ v = globalas(v.left, v.right, valok); if(v == nil) return nil; }else if(valok && !initable(dst, v, 0)) return nil; case dst.op{ Oname => if(dst.decl.init != nil) nerror(dst, "duplicate assignment to "+expconv(dst)+", previously assigned on line " +lineconv(dst.decl.init.src.start)); if(valok) dst.decl.init = v; return v; Otuple => if(valok && v.op != Otuple) fatal("can't deal with "+nodeconv(v)+" in tuple case of globalas"); tv := v.left; for(dst = dst.left; dst != nil; dst = dst.right){ globalas(dst.left, tv.left, valok); if(valok) tv = tv.right; } return v; } fatal("can't deal with "+nodeconv(dst)+" in globalas"); return nil; } needsstore(d: ref Decl): int { if(!d.refs) return 0; if(d.importid != nil) return 0; if(storespace[d.store]) return 1; return 0; } # # return the list of all referenced storage variables # vars(d: ref Decl): ref Decl { while(d != nil && !needsstore(d)) d = d.next; for(v := d; v != nil; v = v.next){ while(v.next != nil){ n := v.next; if(needsstore(n)) break; v.next = n.next; } } return d; } # # declare variables from the left side of a := statement # recdasdecl(n: ref Node, store: int, nid: int): (int, int) { r: int; case n.op{ Otuple => ok := 1; for(n = n.left; n != nil; n = n.right){ (r, nid) = recdasdecl(n.left, store, nid); ok &= r; } return (ok, nid); Oname => if(n.decl == nildecl) return (1, -1); d := mkids(n.src, n.decl.sym, nil, nil); installids(store, d); n.decl = d; old := d.old; if(old != nil && old.store != Dfn && old.store != Dwundef && old.store != Dundef) warn(d.src.start, "redeclaration of "+declconv(d)+", previously declared as " +storeconv(old)+" on line "+lineconv(old.src.start)); d.refs++; d.das = byte 1; if(nid >= 0) nid++; return (1, nid); } return (0, nid); } recmark(n: ref Node, nid: int): int { case(n.op){ Otuple => for(n = n.left; n != nil; n = n.right) nid = recmark(n.left, nid); Oname => n.decl.nid = byte nid; nid = 0; } return nid; } dasdecl(n: ref Node): int { ok: int; nid := 0; store := Dlocal; if(scope == ScopeGlobal) store = Dglobal; (ok, nid) = recdasdecl(n, store, nid); if(!ok) nerror(n, "illegal declaration expression "+expconv(n)); if(ok && store == Dlocal && nid > 1) recmark(n, nid); return ok; } # # declare global variables in nested := expressions # gdasdecl(n: ref Node) { if(n == nil) return; if(n.op == Odas){ gdasdecl(n.right); dasdecl(n.left); }else{ gdasdecl(n.left); gdasdecl(n.right); } } undefed(src: Src, s: ref Sym): ref Decl { d := mkids(src, s, tnone, nil); error(src.start, s.name+" is not declared"); installids(Dwundef, d); return d; } # inloop() : int # { # for (i := scope; i > 0; i--) # if (int scopekind[i] == Sloop) # return 1; # return 0; # } nested() : int { for (i := scope; i > 0; i--) if (int scopekind[i] == Sscope || int scopekind[i] == Sloop) return 1; return 0; } decltozero(n : ref Node) { if ((scop := scopenode[scope]) != nil) { if (n.right != nil && errors == 0) fatal("Ovardecl/Oname/Otuple has right field\n"); n.right = scop.left; scop.left = n; } } pushscope(scp : ref Node, kind : int) { if(scope >= MaxScope) fatal("scope too deep"); scope++; scopes[scope] = nil; tails[scope] = nil; scopenode[scope] = scp; scopekind[scope] = byte kind; } curscope(): ref Decl { return scopes[scope]; } # # revert to old declarations for each symbol in the currect scope. # remove the effects of any imported adt types # whenever the adt is imported from a module, # we record in the type's decl the module to use # when calling members. the process is reversed here. # popscope(): ref Decl { for(id := scopes[scope]; id != nil; id = id.next){ if(id.sym != nil){ id.sym.decl = id.old; id.old = nil; } if(id.importid != nil) id.importid.refs += id.refs; t := id.ty; if(id.store == Dtype && t.decl != nil && t.decl.timport == id) t.decl.timport = id.timport; if(id.store == Dlocal) freeloc(id); } return scopes[scope--]; } # # make a new scope, # preinstalled with some previously installed identifiers # don't add the identifiers to the scope chain, # so they remain separate from any newly installed ids # # these routines assume no ids are imports # repushids(ids: ref Decl) { if(scope >= MaxScope) fatal("scope too deep"); scope++; scopes[scope] = nil; tails[scope] = nil; scopenode[scope] = nil; scopekind[scope] = byte Sother; for(; ids != nil; ids = ids.next){ if(ids.scope != scope && (ids.dot == nil || !isimpmod(ids.dot.sym) || ids.scope != ScopeGlobal || scope != ScopeGlobal + 1)) fatal("repushids scope mismatch"); s := ids.sym; if(s != nil && ids.store != Dtag){ if(s.decl != nil && s.decl.scope >= scope) ids.old = s.decl.old; else ids.old = s.decl; s.decl = ids; } } } # # pop a scope which was started with repushids # return any newly installed ids # popids(ids: ref Decl): ref Decl { for(; ids != nil; ids = ids.next){ if(ids.sym != nil && ids.store != Dtag){ ids.sym.decl = ids.old; ids.old = nil; } } return popscope(); } installids(store: int, ids: ref Decl) { last : ref Decl = nil; for(d := ids; d != nil; d = d.next){ d.scope = scope; if(d.store == Dundef) d.store = store; s := d.sym; if(s != nil){ if(s.decl != nil && s.decl.scope >= scope){ redecl(d); d.old = s.decl.old; }else d.old = s.decl; s.decl = d; } last = d; } if(ids != nil){ d = tails[scope]; if(d == nil) scopes[scope] = ids; else d.next = ids; tails[scope] = last; } } lookup(sym: ref Sym): ref Decl { s: int; d: ref Decl; for(s = scope; s >= ScopeBuiltin; s--){ for(d = scopes[s]; d != nil; d = d.next){ if(d.sym == sym) return d; } } return nil; } mkids(src: Src, s: ref Sym, t: ref Type, next: ref Decl): ref Decl { d := ref zdecl; d.src = src; d.store = Dundef; d.ty = t; d.next = next; d.sym = s; d.nid = byte 1; return d; } mkdecl(src: Src, store: int, t: ref Type): ref Decl { d := ref zdecl; d.src = src; d.store = store; d.ty = t; d.nid = byte 1; return d; } dupdecl(old: ref Decl): ref Decl { d := ref *old; d.next = nil; return d; } dupdecls(old: ref Decl): ref Decl { d, nd, first, last: ref Decl; first = last = nil; for(d = old; d != nil; d = d.next){ nd = dupdecl(d); if(first == nil) first = nd; else last.next = nd; last = nd; } return first; } appdecls(d: ref Decl, dd: ref Decl): ref Decl { if(d == nil) return dd; for(t := d; t.next != nil; t = t.next) ; t.next = dd; return d; } revids(id: ref Decl): ref Decl { next : ref Decl; d : ref Decl = nil; for(; id != nil; id = next){ next = id.next; id.next = d; d = id; } return d; } idoffsets(id: ref Decl, offset: int, al: int): int { algn := 1; for(; id != nil; id = id.next){ if(storespace[id.store]){ usedty(id.ty); if(id.store == Dlocal && id.link != nil){ # id.nid always 1 id.offset = id.link.offset; continue; } a := id.ty.align; if(id.nid > byte 1){ for(d := id.next; d != nil && d.nid == byte 0; d = d.next) if(d.ty.align > a) a = d.ty.align; algn = a; } offset = align(offset, a); id.offset = offset; offset += id.ty.size; if(id.nid == byte 0 && (id.next == nil || id.next.nid != byte 0)) offset = align(offset, algn); } } return align(offset, al); } idindices(id: ref Decl): int { i := 0; for(; id != nil; id = id.next){ if(storespace[id.store]){ usedty(id.ty); id.offset = i++; } } return i; } declconv(d: ref Decl): string { if(d.sym == nil) return storename[d.store] + " " + ""; return storename[d.store] + " " + d.sym.name; } storeconv(d: ref Decl): string { return storeart[d.store] + storename[d.store]; } dotconv(d: ref Decl): string { s: string; if(d.dot != nil && !isimpmod(d.dot.sym)){ s = dotconv(d.dot); if(d.dot.ty != nil && d.dot.ty.kind == Tmodule) s += "."; else s += "."; } s += d.sym.name; return s; } # # merge together two sorted lists, yielding a sorted list # namemerge(e, f: ref Decl): ref Decl { d := rock := ref Decl; while(e != nil && f != nil){ if(e.sym.name <= f.sym.name){ d.next = e; e = e.next; }else{ d.next = f; f = f.next; } d = d.next; } if(e != nil) d.next = e; else d.next = f; return rock.next; } # # recursively split lists and remerge them after they are sorted # recnamesort(d: ref Decl, n: int): ref Decl { if(n <= 1) return d; m := n / 2 - 1; dd := d; for(i := 0; i < m; i++) dd = dd.next; r := dd.next; dd.next = nil; return namemerge(recnamesort(d, n / 2), recnamesort(r, (n + 1) / 2)); } # # sort the ids by name # namesort(d: ref Decl): ref Decl { n := 0; for(dd := d; dd != nil; dd = dd.next) n++; return recnamesort(d, n); } printdecls(d: ref Decl) { for(; d != nil; d = d.next) print("%d: %s %s ref %d\n", d.offset, declconv(d), typeconv(d.ty), d.refs); } mergepolydecs(t: ref Type) { n, nn: ref Node; id, ids, ids1: ref Decl; for(n = t.val; n != nil; n = n.right){ nn = n.left; for(ids = nn.decl; ids != nil; ids = ids.next){ id = ids.sym.decl; if(id == nil){ undefed(ids.src, ids.sym); break; } if(id.store != Dtype){ error(ids.src.start, declconv(id) + " is not a type"); break; } if(id.ty.kind != Tpoly){ error(ids.src.start, declconv(id) + " is not a polymorphic type"); break; } if(id.ty.ids != nil) error(ids.src.start, declconv(id) + " redefined"); pushscope(nil, Sother); fielddecled(nn.left); id.ty.ids = popscope(); for(ids1 = id.ty.ids; ids1 != nil; ids1 = ids1.next){ ids1.dot = id; bindtypes(ids1.ty); if(ids1.ty.kind != Tfn){ error(ids1.src.start, "only function types expected"); id.ty.ids = nil; } } } } t.val = nil; } adjfnptrs(d: ref Decl, polys1: ref Decl, polys2: ref Decl) { n: int; id, idt, idf, arg: ref Decl; n = 0; for(id = d.ty.ids; id != nil; id = id.next) n++; for(idt = polys1; idt != nil; idt = idt.next) for(idf = idt.ty.ids; idf != nil; idf = idf.next) n -= 2; for(idt = polys2; idt != nil; idt = idt.next) for(idf = idt.ty.ids; idf != nil; idf = idf.next) n -= 2; for(arg = d.ty.ids; --n >= 0; arg = arg.next) ; for(idt = polys1; idt != nil; idt = idt.next){ for(idf = idt.ty.ids; idf != nil; idf = idf.next){ idf.link = arg; arg = arg.next.next; } } for(idt = polys2; idt != nil; idt = idt.next){ for(idf = idt.ty.ids; idf != nil; idf = idf.next){ idf.link = arg; arg = arg.next.next; } } } addptrs(polys: ref Decl, fps: ref Decl, last: ref Decl, link: int, src: Src): (ref Decl, ref Decl) { for(idt := polys; idt != nil; idt = idt.next){ for(idf := idt.ty.ids; idf != nil; idf = idf.next){ fp := mkdecl(src, Darg, tany); fp.sym = idf.sym; if(link) idf.link = fp; if(fps == nil) fps = fp; else last.next = fp; last = fp; fp = mkdecl(src, Darg, tint); fp.sym = idf.sym; last.next = fp; last = fp; } } return (fps, last); } addfnptrs(d: ref Decl, link: int) { fps, last, polys: ref Decl; polys = encpolys(d); if(int(d.ty.flags&FULLARGS)){ if(link) adjfnptrs(d, d.ty.polys, polys); return; } d.ty.flags |= FULLARGS; fps = last = nil; (fps, last) = addptrs(d.ty.polys, fps, last, link, d.src); (fps, last) = addptrs(polys, fps, last, link, d.src); for(last = d.ty.ids; last != nil && last.next != nil; last = last.next) ; if(last != nil) last.next = fps; else d.ty.ids = fps; d.offset = idoffsets(d.ty.ids, MaxTemp, IBY2WD); } rmfnptrs(d: ref Decl) { n: int; id, idt, idf: ref Decl; if(int(d.ty.flags&FULLARGS)) d.ty.flags &= ~FULLARGS; else return; n = 0; for(id = d.ty.ids; id != nil; id = id.next) n++; for(idt = d.ty.polys; idt != nil; idt = idt.next) for(idf = idt.ty.ids; idf != nil; idf = idf.next) n -= 2; for(idt = encpolys(d); idt != nil; idt = idt.next) for(idf = idt.ty.ids; idf != nil; idf = idf.next) n -= 2; if(n == 0){ d.ty.ids = nil; return; } for(id = d.ty.ids; --n > 0; id = id.next) ; id.next = nil; d.offset = idoffsets(d.ty.ids, MaxTemp, IBY2WD); } local(d: ref Decl): int { for(d = d.dot; d != nil; d = d.dot) if(d.store == Dtype && d.ty.kind == Tmodule) return 0; return 1; } lmodule(d: ref Decl): ref Decl { for(d = d.dot; d != nil; d = d.dot) if(d.store == Dtype && d.ty.kind == Tmodule) return d; return nil; } outerpolys(n: ref Node): ref Decl { d: ref Decl; if(n.op == Odot){ d = n.right.decl; if(d == nil) fatal("decl() outeradt nil"); d = d.dot; if(d != nil && d.store == Dtype && d.ty.kind == Tadt) return d.ty.polys; } return nil; } encpolys(d: ref Decl): ref Decl { if((d = d.dot) == nil) return nil; return d.ty.polys; } fnlookup(s: ref Sym, t: ref Type): (ref Decl, ref Node) { id: ref Decl; mod: ref Node; id = nil; mod = nil; if(t.kind == Tpoly || t.kind == Tmodule) id = namedot(t.ids, s); else if(t.kind == Tref){ t = t.tof; if(t.kind == Tadt){ id = namedot(t.ids, s); if(t.decl != nil && t.decl.timport != nil) mod = t.decl.timport.eimport; } else if(t.kind == Tadtpick){ id = namedot(t.ids, s); if(t.decl != nil && t.decl.timport != nil) mod = t.decl.timport.eimport; t = t.decl.dot.ty; if(id == nil) id = namedot(t.ids, s); if(t.decl != nil && t.decl.timport != nil) mod = t.decl.timport.eimport; } } if(id == nil){ id = lookup(s); if(id != nil) mod = id.eimport; } return (id, mod); } isimpmod(s: ref Sym): int { d: ref Decl; for(d = impmods; d != nil; d = d.next) if(d.sym == s) return 1; return 0; } dequal(d1: ref Decl, d2: ref Decl, full: int): int { return d1.sym == d2.sym && d1.store == d2.store && d1.implicit == d2.implicit && d1.cyc == d2.cyc && (!full || tequal(d1.ty, d2.ty)) && (!full || d1.store == Dfn || sametree(d1.init, d2.init)); } tzero(t: ref Type): int { return t.kind == Texception || tmustzero(t); } isptr(t: ref Type): int { return t.kind == Texception || tattr[t.kind].isptr; } # can d share the same stack location as another local ? shareloc(d: ref Decl) { z: int; t, tt: ref Type; dd, res: ref Decl; if(d.store != Dlocal || d.nid != byte 1) return; t = d.ty; res = nil; for(dd = fndecls; dd != nil; dd = dd.next){ if(d == dd) fatal("d==dd in shareloc"); if(dd.store != Dlocal || dd.nid != byte 1 || dd.link != nil || dd.tref != 0) continue; tt = dd.ty; if(t.size != tt.size || t.align != tt.align) continue; z = tzero(t)+tzero(tt); if(z > 0) continue; # for now if(t == tt || tequal(t, tt)) res = dd; else{ if(z == 1) continue; if(z == 0 || isptr(t) || isptr(tt) || mktdesc(t) == mktdesc(tt)) res = dd; } if(res != nil){ d.link = res; res.tref = 1; return; } } return; } freeloc(d: ref Decl) { if(d.link != nil) d.link.tref = 0; }