ref: 83246e296ea433b65b9d295b5e08fedd39ff1ab7
dir: /appl/cmd/limbo/gen.b/
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 Rnoff => Anoff, int Rdesc => Adesc, int Rdescp => Adesc, int Rconst => Aimm, int Radr => Afpind, int Rmadr => Ampind, int Rpc => Apc, int Rldt => Aldt, * => Aerr, }; wtemp: ref Decl; bigtemp: ref Decl; ntemp: int; retnode: ref Node; nilnode: ref Node; blockstack: array of int; blockdep: int; nblocks: int; ntoz: ref Node; #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.ty.align != t.align) 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.ty.align == t.align){ 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.ty.align == t.align){ 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); if (--d.tref == 1) zcom1(n, nil); # # 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){ # or tmustzero() # nilnode.decl.refs++; # genmove(lastinst.src, Mas, d.ty, nilnode, n); # } # }else{ # if(d.desc.nmap != 0){ # tmustzero() is better # 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); # } # } } tfreelater(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); if (--d.tref == 1){ nn := mkn(Oname, nil, nil); *nn = *n; nn.left = ntoz; ntoz = nn; d.tref++; } } tfreenow() { nn: ref Node; for(n := ntoz; n != nil; n = nn){ nn = n.left; n.left = nil; if(n.decl.tref != 2) fatal(sprint("bad free of temporary %s", n.decl.sym.name)); --n.decl.tref; zcom1(n, nil); } ntoz = nil; } # # 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"); if(iop == IMULX || iop == IDIVX) return genfixop(src, iop, s, m, d); 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, sz: ref Node, 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(sz != nil){ in.m = genaddr(sz); in.mm = addrmode[int sz.addable]; } 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 or Texception => 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]; } if(s.addable == Rpc) in.op = IMOVPC; return in; } patch(b, dst: ref Inst) { n: ref Inst; for(; b != nil; b = n){ n = b.branch; b.branch = dst; } } getpc(i: ref Inst): int { if(i.pc == 0 && i != firstinst && (firstinst.op != INOOP || i != firstinst.next)){ do i = i.next; while(i != nil && i.pc == 0); if(i == nil || i.pc == 0) fatal("bad instruction in getpc"); } return i.pc; } # # 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 ICASEL or ICASEC or IEXC => 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); if(in.op == IEXC) in.op = INOOP; return; IEXC0 => foldbranch(in.branch); in.op = INOOP; break; IRET or IEXIT or IRAISE => return; IJMP => b := in.branch; case b.op{ ICASE or ICASEL or ICASEC or IRET or IEXIT => next := in.next; *in = *b; in.next = next; 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 or int Rnoff => 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; int Rldt => a.decl = n.decl; int Rdescp or int Rpc => a.decl = n.decl; * => 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; } } } } # for addiface for(id = m.ty.tof.ids; id != nil; id = id.next){ if(id.store == Dfn){ if(id.pc == nil) id.pc = id.iface.pc; if(id.desc == nil) id.desc = id.iface.desc; } } return int m.ty.tof.decl.init.c.val; } # # place the Tiface decs in another list # resolveldts(d: ref Decl): (ref Decl, ref Decl) { d1, ld1, d2, ld2, n: ref Decl; d1 = d2 = nil; ld1 = ld2 = nil; for( ; d != nil; d = n){ n = d.next; d.next = nil; if(d.ty.kind == Tiface){ if(d2 == nil) d2 = d; else ld2.next = d; ld2 = d; } else{ if(d1 == nil) d1 = d; else ld1.next = d; ld1 = d; } } return (d1, d2); } # # 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)); if(in.op == INOOP){ in.pc = pc; continue; } 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 or int Anoff => v = in.m.decl.iface.offset; int Afp or int Amp or int Aldt => 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.s.decl; if(d != nil && in.sm == Apc) in.s.offset = d.pc.pc; 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 Anoff => 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))"; int Aldt => s = "$" + string a.reg; * => s = string a.offset + "(" + string a.reg + "(?" + string am + "?))"; } return s; } genstore(src: Src, n: ref Node, offset: int) { de := mkdecl(nosrc, Dlocal, tint); de.sym = nil; de.offset = offset; d := ref znode; d.op = Oname; d.addable = Rreg; d.decl = de; d.ty = tint; genrawop(src, IMOVW, n, nil, d); } genfixop(src: Src, op: int, s, m, d: ref Node): ref Inst { p, a: int; mm: ref Node; if(m == nil) mm = d; else mm = m; (op, p, a) = fixop(op, mm.ty, s.ty, d.ty); if(op == IMOVW){ # just zero d s = sumark(mkconst(src, big 0)); return genrawop(src, op, s, nil, d); } if(op != IMULX && op != IDIVX) genstore(src, sumark(mkconst(src, big a)), STemp); genstore(src, sumark(mkconst(src, big p)), DTemp); i := genrawop(src, op, s, m, d); return i; } genfixcastop(src: Src, op: int, s, d: ref Node): ref Inst { p, a: int; m: ref Node; (op, p, a) = fixop(op, s.ty, tint, d.ty); if(op == IMOVW){ # just zero d s = sumark(mkconst(src, big 0)); return genrawop(src, op, s, nil, d); } m = sumark(mkconst(src, big p)); if(op != ICVTXX) genstore(src, sumark(mkconst(src, big a)), STemp); return genrawop(src, op, s, m, d); }