blocks: int; # nesting of blocks while generating code zinst: Inst; firstinst: ref Inst; lastinst: ref Inst; include "disoptab.m"; addrmode := array[int Rend] of { int Rreg => Afp, int Rmreg => Amp, int Roff => Aoff, int Rdesc => Adesc, int Rconst => Aimm, int Radr => Afpind, int Rmadr => Ampind, int Rpc => Apc, * => Aerr, }; wtemp: ref Decl; bigtemp: ref Decl; ntemp: int; retnode: ref Node; nilnode: ref Node; blockstack: array of int; blockdep: int; nblocks: int; #znode: Node; genstart() { d := mkdecl(nosrc, Dlocal, tint); d.sym = enter(".ret", 0); d.offset = IBY2WD * REGRET; retnode = ref znode; retnode.op = Oname; retnode.addable = Rreg; retnode.decl = d; retnode.ty = tint; zinst.op = INOP; zinst.sm = Anone; zinst.dm = Anone; zinst.mm = Anone; firstinst = ref zinst; lastinst = firstinst; nilnode = ref znode; nilnode.op = Oname; nilnode.addable = Rmreg; nilnode.decl = nildecl; nilnode.ty = nildecl.ty; blocks = -1; blockdep = 0; nblocks = 0; } # # manage nested control flow blocks # pushblock(): int { if(blockdep >= len blockstack){ bs := array[blockdep + 32] of int; bs[0:] = blockstack; blockstack = bs; } blockstack[blockdep++] = blocks; return blocks = nblocks++; } repushblock(b: int) { blockstack[blockdep++] = blocks; blocks = b; } popblock() { blocks = blockstack[blockdep -= 1]; } tinit() { wtemp = nil; bigtemp = nil; } tdecls(): ref Decl { for(d := wtemp; d != nil; d = d.next){ if(d.tref != 1) fatal("temporary "+d.sym.name+" has "+string(d.tref-1)+" references"); } for(d = bigtemp; d != nil; d = d.next){ if(d.tref != 1) fatal("temporary "+d.sym.name+" has "+string(d.tref-1)+" references"); } return appdecls(wtemp, bigtemp); } talloc(t: ref Type, nok: ref Node): ref Node { ok, d: ref Decl; ok = nil; if(nok != nil) ok = nok.decl; if(ok == nil || ok.tref == 0 || tattr[ok.ty.kind].isbig != tattr[t.kind].isbig) ok = nil; n := ref znode; n.op = Oname; n.addable = Rreg; n.ty = t; if(tattr[t.kind].isbig){ desc := mktdesc(t); if(ok != nil && ok.desc == desc){ ok.tref++; ok.refs++; n.decl = ok; return n; } for(d = bigtemp; d != nil; d = d.next){ if(d.tref == 1 && d.desc == desc){ d.tref++; d.refs++; n.decl = d; return n; } } d = mkdecl(nosrc, Dlocal, t); d.desc = desc; d.tref = 2; d.refs = 1; d.sym = enter(".b"+string ntemp++, 0); d.next = bigtemp; bigtemp = d; n.decl = d; return n; } if(ok != nil && tattr[ok.ty.kind].isptr == tattr[t.kind].isptr && ok.ty.size == t.size){ ok.tref++; n.decl = ok; return n; } for(d = wtemp; d != nil; d = d.next){ if(d.tref == 1 && tattr[d.ty.kind].isptr == tattr[t.kind].isptr && d.ty.size == t.size){ d.tref++; n.decl = d; return n; } } d = mkdecl(nosrc, Dlocal, t); d.tref = 2; d.refs = 1; d.sym = enter(".t"+string ntemp++, 0); d.next = wtemp; wtemp = d; n.decl = d; return n; } tfree(n: ref Node) { if(n == nil || n.decl == nil) return; d := n.decl; if(d.tref == 0) return; if(d.tref == 1) fatal("double free of temporary " + d.sym.name); d.tref--; # # nil out any pointers so we don't # hang onto references # # # costs ~7% in instruction count # if(d.tref != 1) # return; # if(!tattr[d.ty.kind].isbig){ # if(tattr[d.ty.kind].isptr){ # nilnode.decl.refs++; # genmove(lastinst.src, Mas, d.ty, nilnode, n); # } # }else{ # if(d.desc.nmap != 0){ # zn := ref znode; # zn.op = Oname; # zn.addable = Rmreg; # zn.decl = globalztup(d.ty); # zn.ty = d.ty; # genmove(lastinst.src, Mas, d.ty, zn, n); # } # } } # # realloc a temporary after it's been released # tacquire(n: ref Node): ref Node { if(n == nil || n.decl == nil) return n; d := n.decl; if(d.tref == 0) return n; if(d.tref != 1) fatal("tacquire ref != 1: "+string d.tref); d.tref++; return n; } trelease(n: ref Node) { if(n == nil || n.decl == nil) return; d := n.decl; if(d.tref == 0) return; if(d.tref == 1) fatal("double release of temporary " + d.sym.name); d.tref--; } mkinst(): ref Inst { in := lastinst.next; if(in == nil){ in = ref zinst; lastinst.next = in; } lastinst = in; in.block = blocks; if(blocks < 0) fatal("mkinst no block"); return in; } nextinst(): ref Inst { in := lastinst.next; if(in != nil) return in; in = ref zinst; lastinst.next = in; return in; } # # allocate a node for returning # retalloc(n, nn: ref Node): ref Node { if(nn.ty == tnone) return nil; n = ref znode; n.op = Oind; n.addable = Radr; n.left = dupn(1, n.src, retnode); n.ty = nn.ty; return n; } genrawop(src: Src, op: int, s, m, d: ref Node): ref Inst { in := mkinst(); in.op = op; in.src = src; if(s != nil){ in.s = genaddr(s); in.sm = addrmode[int s.addable]; } if(m != nil){ in.m = genaddr(m); in.mm = addrmode[int m.addable]; if(in.mm == Ampind || in.mm == Afpind) fatal("illegal addressing mode in register "+nodeconv(m)); } if(d != nil){ in.d = genaddr(d); in.dm = addrmode[int d.addable]; } return in; } genop(src: Src, op: int, s, m, d: ref Node): ref Inst { iop := disoptab[op][opind[d.ty.kind]]; if(iop == 0) fatal("can't deal with op "+opconv(op)+" on "+nodeconv(s)+" "+nodeconv(m)+" "+nodeconv(d)+" in genop"); in := mkinst(); in.op = iop; in.src = src; if(s != nil){ in.s = genaddr(s); in.sm = addrmode[int s.addable]; } if(m != nil){ in.m = genaddr(m); in.mm = addrmode[int m.addable]; if(in.mm == Ampind || in.mm == Afpind) fatal("illegal addressing mode in register "+nodeconv(m)); } if(d != nil){ in.d = genaddr(d); in.dm = addrmode[int d.addable]; } return in; } genbra(src: Src, op: int, s, m: ref Node): ref Inst { t := s.ty; if(t == tany) t = m.ty; iop := disoptab[op][opind[t.kind]]; if(iop == 0) fatal("can't deal with op "+opconv(op)+" on "+nodeconv(s)+" "+nodeconv(m)+" in genbra"); in := mkinst(); in.op = iop; in.src = src; if(s != nil){ in.s = genaddr(s); in.sm = addrmode[int s.addable]; } if(m != nil){ in.m = genaddr(m); in.mm = addrmode[int m.addable]; if(in.mm == Ampind || in.mm == Afpind) fatal("illegal addressing mode in register "+nodeconv(m)); } return in; } genchan(src: Src, mt: ref Type, d: ref Node): ref Inst { reg: Addr; regm := Anone; reg.decl = nil; reg.reg = 0; reg.offset = 0; op := chantab[mt.kind]; if(op == 0) fatal("can't deal with op "+string mt.kind+" in genchan"); case mt.kind{ Tadt or Tadtpick or Ttuple => td := mktdesc(mt); if(td.nmap != 0){ op++; # sleazy usedesc(td); regm = Adesc; reg.decl = mt.decl; }else{ regm = Aimm; reg.offset = mt.size; } } in := mkinst(); in.op = op; in.src = src; in.s = reg; in.sm = regm; if(d != nil){ in.d = genaddr(d); in.dm = addrmode[int d.addable]; } return in; } genmove(src: Src, how: int, mt: ref Type, s, d: ref Node): ref Inst { reg: Addr; regm := Anone; reg.decl = nil; reg.reg = 0; reg.offset = 0; op := movetab[how][mt.kind]; if(op == 0) fatal("can't deal with op "+string how+" on "+nodeconv(s)+" "+nodeconv(d)+" in genmove"); case mt.kind{ Tadt or Tadtpick or Ttuple => if(mt.size == 0 && how == Mas) return nil; td := mktdesc(mt); if(td.nmap != 0){ op++; # sleazy usedesc(td); regm = Adesc; reg.decl = mt.decl; }else{ regm = Aimm; reg.offset = mt.size; } } in := mkinst(); in.op = op; in.src = src; if(s != nil){ in.s = genaddr(s); in.sm = addrmode[int s.addable]; } in.m = reg; in.mm = regm; if(d != nil){ in.d = genaddr(d); in.dm = addrmode[int d.addable]; } return in; } patch(b, dst: ref Inst) { n: ref Inst; for(; b != nil; b = n){ n = b.branch; b.branch = dst; } } # # follow all possible paths from n, # marking reached code, compressing branches, and reclaiming unreached insts # reach(in: ref Inst) { foldbranch(in); last := in; for(in = in.next; in != nil; in = in.next){ if(in.reach == byte 0) last.next = in.next; else last = in; } lastinst = last; } foldbranch(in: ref Inst) { while(in != nil && in.reach != byte 1){ in.reach = byte 1; if(in.branch != nil) while(in.branch.op == IJMP){ if(in == in.branch || in.branch == in.branch.branch) break; in.branch = in.branch.branch; } case in.op{ IGOTO or ICASE or ICASEC => foldbranch(in.d.decl.ty.cse.iwild); lab := in.d.decl.ty.cse.labs; n := in.d.decl.ty.cse.nlab; for(i := 0; i < n; i++) foldbranch(lab[i].inst); return; IRET or IEXIT => return; IJMP => b := in.branch; case b.op{ ICASE or ICASEC or IRET or IEXIT => next := in.next; *in = *b; in.next = next; b.reach = byte 1; continue; } foldbranch(in.branch); return; * => if(in.branch != nil) foldbranch(in.branch); } in = in.next; } } # # convert the addressable node into an operand # see the comment for sumark # genaddr(n: ref Node): Addr { a: Addr; a.reg = 0; a.offset = 0; a.decl = nil; case int n.addable{ int Rreg => if(n.decl != nil) a.decl = n.decl; else a = genaddr(n.left); int Rmreg => if(n.decl != nil) a.decl = n.decl; else a = genaddr(n.left); int Rdesc => a.decl = n.ty.decl; int Roff => a.decl = n.decl; int Rconst => a.offset = int n.c.val; int Radr => a = genaddr(n.left); int Rmadr => a = genaddr(n.left); int Rareg or int Ramreg => a = genaddr(n.left); if(n.op == Oadd) a.reg += int n.right.c.val; int Raadr or int Ramadr => a = genaddr(n.left); if(n.op == Oadd) a.offset += int n.right.c.val; * => fatal("can't deal with "+nodeconv(n)+" in genaddr"); } return a; } sameaddr(n, m: ref Node): int { if(n.addable != m.addable) return 0; a := genaddr(n); b := genaddr(m); return a.offset == b.offset && a.reg == b.reg && a.decl == b.decl; } resolvedesc(mod: ref Decl, length: int, id: ref Decl): int { last: ref Desc; g := gendesc(mod, length, id); g.used = 0; last = nil; for(d := descriptors; d != nil; d = d.next){ if(!d.used){ if(last != nil) last.next = d.next; else descriptors = d.next; continue; } last = d; } g.next = descriptors; descriptors = g; descid := 0; for(d = descriptors; d != nil; d = d.next) d.id = descid++; if(g.id != 0) fatal("bad global descriptor id"); return descid; } resolvemod(m: ref Decl): int { for(id := m.ty.ids; id != nil; id = id.next){ case id.store{ Dfn => id.iface.pc = id.pc; id.iface.desc = id.desc; Dtype => if(id.ty.kind != Tadt) break; for(d := id.ty.ids; d != nil; d = d.next){ if(d.store == Dfn){ d.iface.pc = d.pc; d.iface.desc = d.desc; } } } } return int m.ty.tof.decl.init.c.val; } # # fix up all pc's # finalize all data offsets # fix up instructions with offsets too large # resolvepcs(inst: ref Inst): int { d: ref Decl; pc := 0; for(in := inst; in != nil; in = in.next){ if(in.reach == byte 0 || in.op == INOP) fatal("unreachable pc: "+instconv(in)); d = in.s.decl; if(d != nil){ if(in.sm == Adesc){ if(d.desc != nil) in.s.offset = d.desc.id; }else in.s.reg += d.offset; } r := in.s.reg; off := in.s.offset; if((in.sm == Afpind || in.sm == Ampind) && (r >= MaxReg || off >= MaxReg)) fatal("big offset in "+instconv(in)); d = in.m.decl; if(d != nil){ if(in.mm == Adesc){ if(d.desc != nil) in.m.offset = d.desc.id; }else in.m.reg += d.offset; } v := 0; case int in.mm{ int Anone => break; int Aimm or int Apc or int Adesc => v = in.m.offset; int Aoff => v = in.m.decl.iface.offset; int Afp or int Amp => v = in.m.reg; if(v < 0) v = 16r8000; * => fatal("can't deal with "+instconv(in)+"'s m mode"); } if(v > 16r7fff || v < -16r8000){ case in.op{ IALT or IINDX => rewritedestreg(in, IMOVW, RTemp); * => op := IMOVW; if(isbyteinst[in.op]) op = IMOVB; in = rewritesrcreg(in, op, RTemp, pc++); } } d = in.d.decl; if(d != nil){ if(in.dm == Apc) in.d.offset = d.pc.pc; else in.d.reg += d.offset; } r = in.d.reg; off = in.d.offset; if((in.dm == Afpind || in.dm == Ampind) && (r >= MaxReg || off >= MaxReg)) fatal("big offset in "+instconv(in)); in.pc = pc; pc++; } for(in = inst; in != nil; in = in.next){ d = in.d.decl; if(d != nil && in.dm == Apc) in.d.offset = d.pc.pc; if(in.branch != nil){ in.dm = Apc; in.d.offset = in.branch.pc; } } return pc; } # # fixp up a big register constant uses as a source # ugly: smashes the instruction # rewritesrcreg(in: ref Inst, op: int, treg: int, pc: int): ref Inst { a := in.m; am := in.mm; in.mm = Afp; in.m.reg = treg; in.m.decl = nil; new := ref *in; *in = zinst; in.src = new.src; in.next = new; in.op = op; in.s = a; in.sm = am; in.dm = Afp; in.d.reg = treg; in.pc = pc; in.reach = byte 1; in.block = new.block; return new; } # # fix up a big register constant by moving to the destination # after the instruction completes # rewritedestreg(in: ref Inst, op: int, treg: int): ref Inst { n := ref zinst; n.next = in.next; in.next = n; n.src = in.src; n.op = op; n.sm = Afp; n.s.reg = treg; n.d = in.m; n.dm = in.mm; n.reach = byte 1; n.block = in.block; in.mm = Afp; in.m.reg = treg; in.m.decl = nil; return n; } instconv(in: ref Inst): string { if(in.op == INOP) return "nop"; op := ""; if(in.op >= 0 && in.op < 256) op = instname[in.op]; if(op == nil) op = "?"+string in.op+"?"; s := "\t" + op + "\t"; comma := ""; if(in.sm != Anone){ s += addrconv(in.sm, in.s); comma = ","; } if(in.mm != Anone){ s += comma; s += addrconv(in.mm, in.m); comma = ","; } if(in.dm != Anone){ s += comma; s += addrconv(in.dm, in.d); } if(!asmsym) return s; if(in.s.decl != nil && in.sm == Adesc){ s += "\t#"; s += dotconv(in.s.decl); } if(0 && in.m.decl != nil){ s += "\t#"; s += dotconv(in.m.decl); } if(in.d.decl != nil && in.dm == Apc){ s += "\t#"; s += dotconv(in.d.decl); } s += "\t#"; s += srcconv(in.src); return s; } addrconv(am: byte, a: Addr): string { s := ""; case int am{ int Anone => break; int Aimm or int Apc or int Adesc => s = "$" + string a.offset; int Aoff => s = "$" + string a.decl.iface.offset; int Afp => s = string a.reg + "(fp)"; int Afpind => s = string a.offset + "(" + string a.reg + "(fp))"; int Amp => s = string a.reg + "(mp)"; int Ampind => s = string a.offset + "(" + string a.reg + "(mp))"; * => s = string a.offset + "(" + string a.reg + "(?" + string am + "?))"; } return s; }