func gclean() { for _, r := range Thearch.ReservedRegs { reg[r-Thearch.REGMIN]-- } for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { n := reg[r-Thearch.REGMIN] if n != 0 { if Debug['v'] != 0 { Regdump() } Yyerror("reg %v left allocated", obj.Rconv(r)) } } for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ { n := reg[r-Thearch.REGMIN] if n != 0 { if Debug['v'] != 0 { Regdump() } Yyerror("reg %v left allocated", obj.Rconv(r)) } } }
func Regdump() { if Debug['v'] == 0 { fmt.Printf("run compiler with -v for register allocation sites\n") return } dump := func(r int) { stk := regstk[r-Thearch.REGMIN] if len(stk) == 0 { return } fmt.Printf("reg %v allocated at:\n", obj.Rconv(r)) fmt.Printf("\t%s\n", strings.Replace(strings.TrimSpace(string(stk)), "\n", "\n\t", -1)) } for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { if reg[r-Thearch.REGMIN] != 0 { dump(r) } } for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ { if reg[r-Thearch.REGMIN] == 0 { dump(r) } } }
// If s==nil, copyu returns the set/use of v in p; otherwise, it // modifies p to replace reads of v with reads of s and returns 0 for // success or non-zero for failure. // // If s==nil, copy returns one of the following values: // 1 if v only used // 2 if v is set and used in one address (read-alter-rewrite; // can't substitute) // 3 if v is only set // 4 if v is set in one address and used in another (so addresses // can be rewritten independently) // 0 otherwise (not touched) func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { if p.From3Type() != obj.TYPE_NONE { // 7g never generates a from3 fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3)) } if p.RegTo2 != obj.REG_NONE { // 7g never generates a to2 fmt.Printf("copyu: RegTo2 (%v) not implemented\n", obj.Rconv(int(p.RegTo2))) } switch p.As { default: fmt.Printf("copyu: can't find %v\n", obj.Aconv(p.As)) return 2 case obj.ANOP, /* read p->from, write p->to */ arm64.ANEG, arm64.AFNEGD, arm64.AFNEGS, arm64.AFSQRTD, arm64.AFCVTZSD, arm64.AFCVTZSS, arm64.AFCVTZSDW, arm64.AFCVTZSSW, arm64.AFCVTZUD, arm64.AFCVTZUS, arm64.AFCVTZUDW, arm64.AFCVTZUSW, arm64.AFCVTSD, arm64.AFCVTDS, arm64.ASCVTFD, arm64.ASCVTFS, arm64.ASCVTFWD, arm64.ASCVTFWS, arm64.AUCVTFD, arm64.AUCVTFS, arm64.AUCVTFWD, arm64.AUCVTFWS, arm64.AMOVB, arm64.AMOVBU, arm64.AMOVH, arm64.AMOVHU, arm64.AMOVW, arm64.AMOVWU, arm64.AMOVD, arm64.AFMOVS, arm64.AFMOVD: if p.Scond == 0 { if s != nil { if copysub(&p.From, v, s, true) { return 1 } // Update only indirect uses of v in p->to if !copyas(&p.To, v) { if copysub(&p.To, v, s, true) { return 1 } } return 0 } if copyas(&p.To, v) { // Fix up implicit from if p.From.Type == obj.TYPE_NONE { p.From = p.To } if copyau(&p.From, v) { return 4 } return 3 } if copyau(&p.From, v) { return 1 } if copyau(&p.To, v) { // p->to only indirectly uses v return 1 } return 0 } /* rar p->from, write p->to or read p->from, rar p->to */ if p.From.Type == obj.TYPE_MEM { if copyas(&p.From, v) { // No s!=nil check; need to fail // anyway in that case return 2 } if s != nil { if copysub(&p.To, v, s, true) { return 1 } return 0 } if copyas(&p.To, v) { return 3 } } else if p.To.Type == obj.TYPE_MEM { if copyas(&p.To, v) { return 2 } if s != nil { if copysub(&p.From, v, s, true) { return 1 } return 0 } if copyau(&p.From, v) { return 1 } } else { fmt.Printf("copyu: bad %v\n", p) } return 0 case arm64.AADD, /* read p->from, read p->reg, write p->to */ arm64.AADDS, arm64.ASUB, arm64.AADC, arm64.AAND, arm64.AORR, arm64.AEOR, arm64.AROR, arm64.AMUL, arm64.ASMULL, arm64.AUMULL, arm64.ASMULH, arm64.AUMULH, arm64.ASDIV, arm64.AUDIV, arm64.ALSL, arm64.ALSR, arm64.AASR, arm64.AFADDD, arm64.AFADDS, arm64.AFSUBD, arm64.AFSUBS, arm64.AFMULD, arm64.AFMULS, arm64.AFDIVD, arm64.AFDIVS: if s != nil { if copysub(&p.From, v, s, true) { return 1 } if copysub1(p, v, s, true) { return 1 } // Update only indirect uses of v in p->to if !copyas(&p.To, v) { if copysub(&p.To, v, s, true) { return 1 } } return 0 } if copyas(&p.To, v) { if p.Reg == 0 { // Fix up implicit reg (e.g., ADD // R3,R4 -> ADD R3,R4,R4) so we can // update reg and to separately. p.Reg = p.To.Reg } if copyau(&p.From, v) { return 4 } if copyau1(p, v) { return 4 } return 3 } if copyau(&p.From, v) { return 1 } if copyau1(p, v) { return 1 } if copyau(&p.To, v) { return 1 } return 0 case arm64.ABEQ, arm64.ABNE, arm64.ABGE, arm64.ABLT, arm64.ABGT, arm64.ABLE, arm64.ABLO, arm64.ABLS, arm64.ABHI, arm64.ABHS: return 0 case obj.ACHECKNIL, /* read p->from */ arm64.ACMP, /* read p->from, read p->reg */ arm64.AFCMPD, arm64.AFCMPS: if s != nil { if copysub(&p.From, v, s, true) { return 1 } if copysub1(p, v, s, true) { return 1 } return 0 } if copyau(&p.From, v) { return 1 } if copyau1(p, v) { return 1 } return 0 case arm64.AB: /* read p->to */ if s != nil { if copysub(&p.To, v, s, true) { return 1 } return 0 } if copyau(&p.To, v) { return 1 } return 0 case obj.ARET: /* funny */ if s != nil { return 0 } // All registers die at this point, so claim // everything is set (and not used). return 3 case arm64.ABL: /* funny */ if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { return 2 } if s != nil { if copysub(&p.To, v, s, true) { return 1 } return 0 } if copyau(&p.To, v) { return 4 } return 3 // R31 is zero, used by DUFFZERO, cannot be substituted. // R16 is ptr to memory, used and set, cannot be substituted. case obj.ADUFFZERO: if v.Type == obj.TYPE_REG { if v.Reg == 31 { return 1 } if v.Reg == 16 { return 2 } } return 0 // R16, R17 are ptr to src, dst, used and set, cannot be substituted. // R27 is scratch, set by DUFFCOPY, cannot be substituted. case obj.ADUFFCOPY: if v.Type == obj.TYPE_REG { if v.Reg == 16 || v.Reg == 17 { return 2 } if v.Reg == 27 { return 3 } } return 0 case arm64.AHINT, obj.ATEXT, obj.APCDATA, obj.AFUNCDATA, obj.AVARDEF, obj.AVARKILL, obj.AVARLIVE, obj.AUSEFIELD: return 0 } }
func regopt(firstp *obj.Prog) { mergetemp(firstp) // control flow is more complicated in generated go code // than in generated c code. define pseudo-variables for // registers, so we have complete register usage information. var nreg int regnames := Thearch.Regnames(&nreg) nvar = nreg for i := 0; i < nreg; i++ { vars[i] = Var{} } for i := 0; i < nreg; i++ { if regnodes[i] == nil { regnodes[i] = newname(Lookup(regnames[i])) } vars[i].node = regnodes[i] } regbits = Thearch.Excludedregs() externs = zbits params = zbits consts = zbits addrs = zbits ivar = zbits ovar = zbits // pass 1 // build aux data structure // allocate pcs // find use and set of variables g := Flowstart(firstp, func() interface{} { return new(Reg) }) if g == nil { for i := 0; i < nvar; i++ { vars[i].node.SetOpt(nil) } return } firstf := g.Start for f := firstf; f != nil; f = f.Link { p := f.Prog // AVARLIVE must be considered a use, do not skip it. // Otherwise the variable will be optimized away, // and the whole point of AVARLIVE is to keep it on the stack. if p.As == obj.AVARDEF || p.As == obj.AVARKILL { continue } // Avoid making variables for direct-called functions. if p.As == obj.ACALL && p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_EXTERN { continue } // from vs to doesn't matter for registers. r := f.Data.(*Reg) r.use1.b[0] |= p.Info.Reguse | p.Info.Regindex r.set.b[0] |= p.Info.Regset bit := mkvar(f, &p.From) if bany(&bit) { if p.Info.Flags&LeftAddr != 0 { setaddrs(bit) } if p.Info.Flags&LeftRead != 0 { for z := 0; z < BITS; z++ { r.use1.b[z] |= bit.b[z] } } if p.Info.Flags&LeftWrite != 0 { for z := 0; z < BITS; z++ { r.set.b[z] |= bit.b[z] } } } // Compute used register for reg if p.Info.Flags&RegRead != 0 { r.use1.b[0] |= Thearch.RtoB(int(p.Reg)) } // Currently we never generate three register forms. // If we do, this will need to change. if p.From3Type() != obj.TYPE_NONE && p.From3Type() != obj.TYPE_CONST { Fatalf("regopt not implemented for from3") } bit = mkvar(f, &p.To) if bany(&bit) { if p.Info.Flags&RightAddr != 0 { setaddrs(bit) } if p.Info.Flags&RightRead != 0 { for z := 0; z < BITS; z++ { r.use2.b[z] |= bit.b[z] } } if p.Info.Flags&RightWrite != 0 { for z := 0; z < BITS; z++ { r.set.b[z] |= bit.b[z] } } } } for i := 0; i < nvar; i++ { v := &vars[i] if v.addr != 0 { bit := blsh(uint(i)) for z := 0; z < BITS; z++ { addrs.b[z] |= bit.b[z] } } if Debug['R'] != 0 && Debug['v'] != 0 { fmt.Printf("bit=%2d addr=%d et=%v w=%-2d s=%v + %d\n", i, v.addr, v.etype, v.width, v.node, v.offset) } } if Debug['R'] != 0 && Debug['v'] != 0 { Dumpit("pass1", firstf, 1) } // pass 2 // find looping structure flowrpo(g) if Debug['R'] != 0 && Debug['v'] != 0 { Dumpit("pass2", firstf, 1) } // pass 2.5 // iterate propagating fat vardef covering forward // r->act records vars with a VARDEF since the last CALL. // (r->act will be reused in pass 5 for something else, // but we'll be done with it by then.) active := 0 for f := firstf; f != nil; f = f.Link { f.Active = 0 r := f.Data.(*Reg) r.act = zbits } for f := firstf; f != nil; f = f.Link { p := f.Prog if p.As == obj.AVARDEF && Isfat(((p.To.Node).(*Node)).Type) && ((p.To.Node).(*Node)).Opt() != nil { active++ walkvardef(p.To.Node.(*Node), f, active) } } // pass 3 // iterate propagating usage // back until flow graph is complete var f1 *Flow var i int var f *Flow loop1: change = 0 for f = firstf; f != nil; f = f.Link { f.Active = 0 } for f = firstf; f != nil; f = f.Link { if f.Prog.As == obj.ARET { prop(f, zbits, zbits) } } // pick up unreachable code loop11: i = 0 for f = firstf; f != nil; f = f1 { f1 = f.Link if f1 != nil && f1.Active != 0 && f.Active == 0 { prop(f, zbits, zbits) i = 1 } } if i != 0 { goto loop11 } if change != 0 { goto loop1 } if Debug['R'] != 0 && Debug['v'] != 0 { Dumpit("pass3", firstf, 1) } // pass 4 // iterate propagating register/variable synchrony // forward until graph is complete loop2: change = 0 for f = firstf; f != nil; f = f.Link { f.Active = 0 } synch(firstf, zbits) if change != 0 { goto loop2 } if Debug['R'] != 0 && Debug['v'] != 0 { Dumpit("pass4", firstf, 1) } // pass 4.5 // move register pseudo-variables into regu. mask := uint64((1 << uint(nreg)) - 1) for f := firstf; f != nil; f = f.Link { r := f.Data.(*Reg) r.regu = (r.refbehind.b[0] | r.set.b[0]) & mask r.set.b[0] &^= mask r.use1.b[0] &^= mask r.use2.b[0] &^= mask r.refbehind.b[0] &^= mask r.refahead.b[0] &^= mask r.calbehind.b[0] &^= mask r.calahead.b[0] &^= mask r.regdiff.b[0] &^= mask r.act.b[0] &^= mask } if Debug['R'] != 0 && Debug['v'] != 0 { Dumpit("pass4.5", firstf, 1) } // pass 5 // isolate regions // calculate costs (paint1) var bit Bits if f := firstf; f != nil { r := f.Data.(*Reg) for z := 0; z < BITS; z++ { bit.b[z] = (r.refahead.b[z] | r.calahead.b[z]) &^ (externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]) } if bany(&bit) && !f.Refset { // should never happen - all variables are preset if Debug['w'] != 0 { fmt.Printf("%v: used and not set: %v\n", f.Prog.Line(), &bit) } f.Refset = true } } for f := firstf; f != nil; f = f.Link { (f.Data.(*Reg)).act = zbits } nregion = 0 region = region[:0] for f := firstf; f != nil; f = f.Link { r := f.Data.(*Reg) for z := 0; z < BITS; z++ { bit.b[z] = r.set.b[z] &^ (r.refahead.b[z] | r.calahead.b[z] | addrs.b[z]) } if bany(&bit) && !f.Refset { if Debug['w'] != 0 { fmt.Printf("%v: set and not used: %v\n", f.Prog.Line(), &bit) } f.Refset = true Thearch.Excise(f) } for z := 0; z < BITS; z++ { bit.b[z] = LOAD(r, z) &^ (r.act.b[z] | addrs.b[z]) } for bany(&bit) { i = bnum(&bit) change = 0 paint1(f, i) biclr(&bit, uint(i)) if change <= 0 { continue } if nregion >= MaxRgn { nregion++ continue } region = append(region, Rgn{ enter: f, cost: int16(change), varno: int16(i), }) nregion++ } } if false && Debug['v'] != 0 && strings.Contains(Curfn.Func.Nname.Sym.Name, "Parse") { Warn("regions: %d\n", nregion) } if nregion >= MaxRgn { if Debug['v'] != 0 { Warn("too many regions: %d\n", nregion) } nregion = MaxRgn } sort.Sort(rcmp(region[:nregion])) if Debug['R'] != 0 && Debug['v'] != 0 { Dumpit("pass5", firstf, 1) } // pass 6 // determine used registers (paint2) // replace code (paint3) if Debug['R'] != 0 && Debug['v'] != 0 { fmt.Printf("\nregisterizing\n") } for i := 0; i < nregion; i++ { rgp := ®ion[i] if Debug['R'] != 0 && Debug['v'] != 0 { fmt.Printf("region %d: cost %d varno %d enter %d\n", i, rgp.cost, rgp.varno, rgp.enter.Prog.Pc) } bit = blsh(uint(rgp.varno)) usedreg := paint2(rgp.enter, int(rgp.varno), 0) vreg := allreg(usedreg, rgp) if rgp.regno != 0 { if Debug['R'] != 0 && Debug['v'] != 0 { v := &vars[rgp.varno] fmt.Printf("registerize %v+%d (bit=%2d et=%v) in %v usedreg=%#x vreg=%#x\n", v.node, v.offset, rgp.varno, v.etype, obj.Rconv(int(rgp.regno)), usedreg, vreg) } paint3(rgp.enter, int(rgp.varno), vreg, int(rgp.regno)) } } // free aux structures. peep allocates new ones. for i := 0; i < nvar; i++ { vars[i].node.SetOpt(nil) } Flowend(g) firstf = nil if Debug['R'] != 0 && Debug['v'] != 0 { // Rebuild flow graph, since we inserted instructions g := Flowstart(firstp, nil) firstf = g.Start Dumpit("pass6", firstf, 0) Flowend(g) firstf = nil } // pass 7 // peep-hole on basic block if Debug['R'] == 0 || Debug['P'] != 0 { Thearch.Peep(firstp) } // eliminate nops for p := firstp; p != nil; p = p.Link { for p.Link != nil && p.Link.As == obj.ANOP { p.Link = p.Link.Link } if p.To.Type == obj.TYPE_BRANCH { for p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.ANOP { p.To.Val = p.To.Val.(*obj.Prog).Link } } } if Debug['R'] != 0 { if Ostats.Ncvtreg != 0 || Ostats.Nspill != 0 || Ostats.Nreload != 0 || Ostats.Ndelmov != 0 || Ostats.Nvar != 0 || Ostats.Naddr != 0 || false { fmt.Printf("\nstats\n") } if Ostats.Ncvtreg != 0 { fmt.Printf("\t%4d cvtreg\n", Ostats.Ncvtreg) } if Ostats.Nspill != 0 { fmt.Printf("\t%4d spill\n", Ostats.Nspill) } if Ostats.Nreload != 0 { fmt.Printf("\t%4d reload\n", Ostats.Nreload) } if Ostats.Ndelmov != 0 { fmt.Printf("\t%4d delmov\n", Ostats.Ndelmov) } if Ostats.Nvar != 0 { fmt.Printf("\t%4d var\n", Ostats.Nvar) } if Ostats.Naddr != 0 { fmt.Printf("\t%4d addr\n", Ostats.Naddr) } Ostats = OptStats{} } }
func clearfat(nl *gc.Node) { /* clear a fat object */ if gc.Debug['g'] != 0 { fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) } w := uint64(nl.Type.Width) // Avoid taking the address for simple enough types. if gc.Componentgen(nil, nl) { return } c := w % 8 // bytes q := w / 8 // dwords if gc.Reginuse(mips.REGRT1) { gc.Fatalf("%v in use during clearfat", obj.Rconv(mips.REGRT1)) } var r0 gc.Node gc.Nodreg(&r0, gc.Types[gc.TUINT64], mips.REGZERO) var dst gc.Node gc.Nodreg(&dst, gc.Types[gc.Tptr], mips.REGRT1) gc.Regrealloc(&dst) gc.Agen(nl, &dst) var boff uint64 if q > 128 { p := gins(mips.ASUBV, nil, &dst) p.From.Type = obj.TYPE_CONST p.From.Offset = 8 var end gc.Node gc.Regalloc(&end, gc.Types[gc.Tptr], nil) p = gins(mips.AMOVV, &dst, &end) p.From.Type = obj.TYPE_ADDR p.From.Offset = int64(q * 8) p = gins(mips.AMOVV, &r0, &dst) p.To.Type = obj.TYPE_MEM p.To.Offset = 8 pl := p p = gins(mips.AADDV, nil, &dst) p.From.Type = obj.TYPE_CONST p.From.Offset = 8 gc.Patch(ginsbranch(mips.ABNE, nil, &dst, &end, 0), pl) gc.Regfree(&end) // The loop leaves R1 on the last zeroed dword boff = 8 // TODO(dfc): https://golang.org/issue/12108 // If DUFFZERO is used inside a tail call (see genwrapper) it will // overwrite the link register. } else if false && q >= 4 { p := gins(mips.ASUBV, nil, &dst) p.From.Type = obj.TYPE_CONST p.From.Offset = 8 f := gc.Sysfunc("duffzero") p = gins(obj.ADUFFZERO, nil, f) gc.Afunclit(&p.To, f) // 8 and 128 = magic constants: see ../../runtime/asm_mips64x.s p.To.Offset = int64(8 * (128 - q)) // duffzero leaves R1 on the last zeroed dword boff = 8 } else { var p *obj.Prog for t := uint64(0); t < q; t++ { p = gins(mips.AMOVV, &r0, &dst) p.To.Type = obj.TYPE_MEM p.To.Offset = int64(8 * t) } boff = 8 * q } var p *obj.Prog for t := uint64(0); t < c; t++ { p = gins(mips.AMOVB, &r0, &dst) p.To.Type = obj.TYPE_MEM p.To.Offset = int64(t + boff) } gc.Regfree(&dst) }
func clearfat(nl *gc.Node) { /* clear a fat object */ if gc.Debug['g'] != 0 { fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) } w := uint64(nl.Type.Width) // Avoid taking the address for simple enough types. if gc.Componentgen(nil, nl) { return } c := w % 8 // bytes q := w / 8 // dwords if gc.Reginuse(ppc64.REGRT1) { gc.Fatalf("%v in use during clearfat", obj.Rconv(ppc64.REGRT1)) } var r0 gc.Node gc.Nodreg(&r0, gc.Types[gc.TUINT64], ppc64.REGZERO) var dst gc.Node gc.Nodreg(&dst, gc.Types[gc.Tptr], ppc64.REGRT1) gc.Regrealloc(&dst) gc.Agen(nl, &dst) var boff uint64 if q > 128 { p := gins(ppc64.ASUB, nil, &dst) p.From.Type = obj.TYPE_CONST p.From.Offset = 8 var end gc.Node gc.Regalloc(&end, gc.Types[gc.Tptr], nil) p = gins(ppc64.AMOVD, &dst, &end) p.From.Type = obj.TYPE_ADDR p.From.Offset = int64(q * 8) p = gins(ppc64.AMOVDU, &r0, &dst) p.To.Type = obj.TYPE_MEM p.To.Offset = 8 pl := p p = gins(ppc64.ACMP, &dst, &end) gc.Patch(gc.Gbranch(ppc64.ABNE, nil, 0), pl) gc.Regfree(&end) // The loop leaves R3 on the last zeroed dword boff = 8 } else if q >= 4 { p := gins(ppc64.ASUB, nil, &dst) p.From.Type = obj.TYPE_CONST p.From.Offset = 8 f := gc.Sysfunc("duffzero") p = gins(obj.ADUFFZERO, nil, f) gc.Afunclit(&p.To, f) // 4 and 128 = magic constants: see ../../runtime/asm_ppc64x.s p.To.Offset = int64(4 * (128 - q)) // duffzero leaves R3 on the last zeroed dword boff = 8 } else { var p *obj.Prog for t := uint64(0); t < q; t++ { p = gins(ppc64.AMOVD, &r0, &dst) p.To.Type = obj.TYPE_MEM p.To.Offset = int64(8 * t) } boff = 8 * q } var p *obj.Prog for t := uint64(0); t < c; t++ { p = gins(ppc64.AMOVB, &r0, &dst) p.To.Type = obj.TYPE_MEM p.To.Offset = int64(t + boff) } gc.Regfree(&dst) }
func nodedump(n *Node, flag FmtFlag) string { if n == nil { return "" } recur := flag&FmtShort == 0 var buf bytes.Buffer if recur { indent(&buf) if dumpdepth > 10 { buf.WriteString("...") return buf.String() } if n.Ninit.Len() != 0 { fmt.Fprintf(&buf, "%v-init%v", n.Op, n.Ninit) indent(&buf) } } switch n.Op { default: fmt.Fprintf(&buf, "%v%v", n.Op, jconv(n, 0)) case OREGISTER, OINDREG: fmt.Fprintf(&buf, "%v-%v%v", n.Op, obj.Rconv(int(n.Reg)), jconv(n, 0)) case OLITERAL: fmt.Fprintf(&buf, "%v-%v%v", n.Op, vconv(n.Val(), 0), jconv(n, 0)) case ONAME, ONONAME: if n.Sym != nil { fmt.Fprintf(&buf, "%v-%v%v", n.Op, n.Sym, jconv(n, 0)) } else { fmt.Fprintf(&buf, "%v%v", n.Op, jconv(n, 0)) } if recur && n.Type == nil && n.Name != nil && n.Name.Param != nil && n.Name.Param.Ntype != nil { indent(&buf) fmt.Fprintf(&buf, "%v-ntype%v", n.Op, n.Name.Param.Ntype) } case OASOP: fmt.Fprintf(&buf, "%v-%v%v", n.Op, Op(n.Etype), jconv(n, 0)) case OTYPE: fmt.Fprintf(&buf, "%v %v%v type=%v", n.Op, n.Sym, jconv(n, 0), n.Type) if recur && n.Type == nil && n.Name.Param.Ntype != nil { indent(&buf) fmt.Fprintf(&buf, "%v-ntype%v", n.Op, n.Name.Param.Ntype) } } if n.Sym != nil && n.Op != ONAME { fmt.Fprintf(&buf, " %v", n.Sym) } if n.Type != nil { fmt.Fprintf(&buf, " %v", n.Type) } if recur { if n.Left != nil { buf.WriteString(Nconv(n.Left, 0)) } if n.Right != nil { buf.WriteString(Nconv(n.Right, 0)) } if n.List.Len() != 0 { indent(&buf) fmt.Fprintf(&buf, "%v-list%v", n.Op, n.List) } if n.Rlist.Len() != 0 { indent(&buf) fmt.Fprintf(&buf, "%v-rlist%v", n.Op, n.Rlist) } if n.Nbody.Len() != 0 { indent(&buf) fmt.Fprintf(&buf, "%v-body%v", n.Op, n.Nbody) } } return buf.String() }
func exprfmt(n *Node, prec int) string { for n != nil && n.Implicit && (n.Op == OIND || n.Op == OADDR) { n = n.Left } if n == nil { return "<N>" } nprec := opprec[n.Op] if n.Op == OTYPE && n.Sym != nil { nprec = 8 } if prec > nprec { return fmt.Sprintf("(%v)", n) } switch n.Op { case OPAREN: return fmt.Sprintf("(%v)", n.Left) case ODDDARG: return "... argument" case OREGISTER: return obj.Rconv(int(n.Reg)) case OLITERAL: // this is a bit of a mess if fmtmode == FErr { if n.Orig != nil && n.Orig != n { return exprfmt(n.Orig, prec) } if n.Sym != nil { return sconv(n.Sym, 0) } } if n.Val().Ctype() == CTNIL && n.Orig != nil && n.Orig != n { return exprfmt(n.Orig, prec) } if n.Type != nil && n.Type.Etype != TIDEAL && n.Type.Etype != TNIL && n.Type != idealbool && n.Type != idealstring { // Need parens when type begins with what might // be misinterpreted as a unary operator: * or <-. if n.Type.IsPtr() || (n.Type.IsChan() && n.Type.ChanDir() == Crecv) { return fmt.Sprintf("(%v)(%v)", n.Type, vconv(n.Val(), 0)) } else { return fmt.Sprintf("%v(%v)", n.Type, vconv(n.Val(), 0)) } } return vconv(n.Val(), 0) // Special case: name used as local variable in export. // _ becomes ~b%d internally; print as _ for export case ONAME: if (fmtmode == FExp || fmtmode == FErr) && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' { return "_" } if fmtmode == FExp && n.Sym != nil && !isblank(n) && n.Name.Vargen > 0 { return fmt.Sprintf("%v·%d", n.Sym, n.Name.Vargen) } // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method, // but for export, this should be rendered as (*pkg.T).meth. // These nodes have the special property that they are names with a left OTYPE and a right ONAME. if fmtmode == FExp && n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME { if n.Left.Type.IsPtr() { return fmt.Sprintf("(%v).%v", n.Left.Type, sconv(n.Right.Sym, FmtShort|FmtByte)) } else { return fmt.Sprintf("%v.%v", n.Left.Type, sconv(n.Right.Sym, FmtShort|FmtByte)) } } fallthrough case OPACK, ONONAME: return sconv(n.Sym, 0) case OTYPE: if n.Type == nil && n.Sym != nil { return sconv(n.Sym, 0) } return Tconv(n.Type, 0) case OTARRAY: if n.Left != nil { return fmt.Sprintf("[]%v", n.Left) } return fmt.Sprintf("[]%v", n.Right) // happens before typecheck case OTMAP: return fmt.Sprintf("map[%v]%v", n.Left, n.Right) case OTCHAN: switch ChanDir(n.Etype) { case Crecv: return fmt.Sprintf("<-chan %v", n.Left) case Csend: return fmt.Sprintf("chan<- %v", n.Left) default: if n.Left != nil && n.Left.Op == OTCHAN && n.Left.Sym == nil && ChanDir(n.Left.Etype) == Crecv { return fmt.Sprintf("chan (%v)", n.Left) } else { return fmt.Sprintf("chan %v", n.Left) } } case OTSTRUCT: return "<struct>" case OTINTER: return "<inter>" case OTFUNC: return "<func>" case OCLOSURE: if fmtmode == FErr { return "func literal" } if n.Nbody.Len() != 0 { return fmt.Sprintf("%v { %v }", n.Type, n.Nbody) } return fmt.Sprintf("%v { %v }", n.Type, n.Func.Closure.Nbody) case OCOMPLIT: ptrlit := n.Right != nil && n.Right.Implicit && n.Right.Type != nil && n.Right.Type.IsPtr() if fmtmode == FErr { if n.Right != nil && n.Right.Type != nil && !n.Implicit { if ptrlit { return fmt.Sprintf("&%v literal", n.Right.Type.Elem()) } else { return fmt.Sprintf("%v literal", n.Right.Type) } } return "composite literal" } if fmtmode == FExp && ptrlit { // typecheck has overwritten OIND by OTYPE with pointer type. return fmt.Sprintf("(&%v{ %v })", n.Right.Type.Elem(), hconv(n.List, FmtComma)) } return fmt.Sprintf("(%v{ %v })", n.Right, hconv(n.List, FmtComma)) case OPTRLIT: if fmtmode == FExp && n.Left.Implicit { return Nconv(n.Left, 0) } return fmt.Sprintf("&%v", n.Left) case OSTRUCTLIT: if fmtmode == FExp { // requires special handling of field names var f string if n.Implicit { f += "{" } else { f += fmt.Sprintf("(%v{", n.Type) } for i1, n1 := range n.List.Slice() { f += fmt.Sprintf(" %v:%v", sconv(n1.Left.Sym, FmtShort|FmtByte), n1.Right) if i1+1 < n.List.Len() { f += "," } else { f += " " } } if !n.Implicit { f += "})" return f } f += "}" return f } fallthrough case OARRAYLIT, OMAPLIT: if fmtmode == FErr { return fmt.Sprintf("%v literal", n.Type) } if fmtmode == FExp && n.Implicit { return fmt.Sprintf("{ %v }", hconv(n.List, FmtComma)) } return fmt.Sprintf("(%v{ %v })", n.Type, hconv(n.List, FmtComma)) case OKEY: if n.Left != nil && n.Right != nil { if fmtmode == FExp && n.Left.Type == structkey { // requires special handling of field names return fmt.Sprintf("%v:%v", sconv(n.Left.Sym, FmtShort|FmtByte), n.Right) } else { return fmt.Sprintf("%v:%v", n.Left, n.Right) } } if n.Left == nil && n.Right != nil { return fmt.Sprintf(":%v", n.Right) } if n.Left != nil && n.Right == nil { return fmt.Sprintf("%v:", n.Left) } return ":" case OCALLPART: var f string f += exprfmt(n.Left, nprec) if n.Right == nil || n.Right.Sym == nil { f += ".<nil>" return f } f += fmt.Sprintf(".%v", sconv(n.Right.Sym, FmtShort|FmtByte)) return f case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH: var f string f += exprfmt(n.Left, nprec) if n.Sym == nil { f += ".<nil>" return f } f += fmt.Sprintf(".%v", sconv(n.Sym, FmtShort|FmtByte)) return f case ODOTTYPE, ODOTTYPE2: var f string f += exprfmt(n.Left, nprec) if n.Right != nil { f += fmt.Sprintf(".(%v)", n.Right) return f } f += fmt.Sprintf(".(%v)", n.Type) return f case OINDEX, OINDEXMAP: return fmt.Sprintf("%s[%v]", exprfmt(n.Left, nprec), n.Right) case OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR: var buf bytes.Buffer buf.WriteString(exprfmt(n.Left, nprec)) buf.WriteString("[") low, high, max := n.SliceBounds() if low != nil { buf.WriteString(low.String()) } buf.WriteString(":") if high != nil { buf.WriteString(high.String()) } if n.Op.IsSlice3() { buf.WriteString(":") if max != nil { buf.WriteString(max.String()) } } buf.WriteString("]") return buf.String() case OCOPY, OCOMPLEX: return fmt.Sprintf("%#v(%v, %v)", n.Op, n.Left, n.Right) case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR: if n.Type == nil || n.Type.Sym == nil { return fmt.Sprintf("(%v)(%v)", n.Type, n.Left) } if n.Left != nil { return fmt.Sprintf("%v(%v)", n.Type, n.Left) } return fmt.Sprintf("%v(%v)", n.Type, hconv(n.List, FmtComma)) case OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN: if n.Left != nil { return fmt.Sprintf("%#v(%v)", n.Op, n.Left) } if n.Isddd { return fmt.Sprintf("%#v(%v...)", n.Op, hconv(n.List, FmtComma)) } return fmt.Sprintf("%#v(%v)", n.Op, hconv(n.List, FmtComma)) case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG: var f string f += exprfmt(n.Left, nprec) if n.Isddd { f += fmt.Sprintf("(%v...)", hconv(n.List, FmtComma)) return f } f += fmt.Sprintf("(%v)", hconv(n.List, FmtComma)) return f case OMAKEMAP, OMAKECHAN, OMAKESLICE: if n.List.Len() != 0 { // pre-typecheck return fmt.Sprintf("make(%v, %v)", n.Type, hconv(n.List, FmtComma)) } if n.Right != nil { return fmt.Sprintf("make(%v, %v, %v)", n.Type, n.Left, n.Right) } if n.Left != nil && (n.Op == OMAKESLICE || !n.Left.Type.IsUntyped()) { return fmt.Sprintf("make(%v, %v)", n.Type, n.Left) } return fmt.Sprintf("make(%v)", n.Type) // Unary case OPLUS, OMINUS, OADDR, OCOM, OIND, ONOT, ORECV: f := n.Op.GoString() // %#v if n.Left.Op == n.Op { f += " " } f += exprfmt(n.Left, nprec+1) return f // Binary case OADD, OAND, OANDAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLT, OLSH, OMOD, OMUL, ONE, OOR, OOROR, ORSH, OSEND, OSUB, OXOR: var f string f += exprfmt(n.Left, nprec) f += fmt.Sprintf(" %#v ", n.Op) f += exprfmt(n.Right, nprec+1) return f case OADDSTR: var f string i := 0 for _, n1 := range n.List.Slice() { if i != 0 { f += " + " } f += exprfmt(n1, nprec) i++ } return f case OCMPSTR, OCMPIFACE: var f string f += exprfmt(n.Left, nprec) // TODO(marvin): Fix Node.EType type union. f += fmt.Sprintf(" %#v ", Op(n.Etype)) f += exprfmt(n.Right, nprec+1) return f case ODCLCONST: // if exporting, DCLCONST should just be removed as its usage // has already been replaced with literals if fmtbody { return "" } } return fmt.Sprintf("<node %v>", n.Op) }