| // Copyright 2015 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package ssa | |
| import ( | |
| "fmt" | |
| "io" | |
| "strings" | |
| "cmd/internal/hash" | |
| "cmd/internal/src" | |
| ) | |
| func printFunc(f *Func) { | |
| f.Logf("%s", f) | |
| } | |
| func hashFunc(f *Func) []byte { | |
| h := hash.New32() | |
| p := stringFuncPrinter{w: h, printDead: true} | |
| fprintFunc(p, f) | |
| return h.Sum(nil) | |
| } | |
| func (f *Func) String() string { | |
| var buf strings.Builder | |
| p := stringFuncPrinter{w: &buf, printDead: true} | |
| fprintFunc(p, f) | |
| return buf.String() | |
| } | |
| // rewriteHash returns a hash of f suitable for detecting rewrite cycles. | |
| func (f *Func) rewriteHash() string { | |
| h := hash.New32() | |
| p := stringFuncPrinter{w: h, printDead: false} | |
| fprintFunc(p, f) | |
| return fmt.Sprintf("%x", h.Sum(nil)) | |
| } | |
| type funcPrinter interface { | |
| header(f *Func) | |
| startBlock(b *Block, reachable bool) | |
| endBlock(b *Block, reachable bool) | |
| value(v *Value, live bool) | |
| startDepCycle() | |
| endDepCycle() | |
| named(n LocalSlot, vals []*Value) | |
| } | |
| type stringFuncPrinter struct { | |
| w io.Writer | |
| printDead bool | |
| } | |
| func (p stringFuncPrinter) header(f *Func) { | |
| fmt.Fprint(p.w, f.Name) | |
| fmt.Fprint(p.w, " ") | |
| fmt.Fprintln(p.w, f.Type) | |
| } | |
| func (p stringFuncPrinter) startBlock(b *Block, reachable bool) { | |
| if !p.printDead && !reachable { | |
| return | |
| } | |
| fmt.Fprintf(p.w, " b%d:", b.ID) | |
| if len(b.Preds) > 0 { | |
| io.WriteString(p.w, " <-") | |
| for _, e := range b.Preds { | |
| pred := e.b | |
| fmt.Fprintf(p.w, " b%d", pred.ID) | |
| } | |
| } | |
| if !reachable { | |
| fmt.Fprint(p.w, " DEAD") | |
| } | |
| io.WriteString(p.w, "\n") | |
| } | |
| func (p stringFuncPrinter) endBlock(b *Block, reachable bool) { | |
| if !p.printDead && !reachable { | |
| return | |
| } | |
| fmt.Fprintln(p.w, " "+b.LongString()) | |
| } | |
| func StmtString(p src.XPos) string { | |
| linenumber := "(?) " | |
| if p.IsKnown() { | |
| pfx := "" | |
| if p.IsStmt() == src.PosIsStmt { | |
| pfx = "+" | |
| } | |
| if p.IsStmt() == src.PosNotStmt { | |
| pfx = "-" | |
| } | |
| linenumber = fmt.Sprintf("(%s%d) ", pfx, p.Line()) | |
| } | |
| return linenumber | |
| } | |
| func (p stringFuncPrinter) value(v *Value, live bool) { | |
| if !p.printDead && !live { | |
| return | |
| } | |
| fmt.Fprintf(p.w, " %s", StmtString(v.Pos)) | |
| fmt.Fprint(p.w, v.LongString()) | |
| if !live { | |
| fmt.Fprint(p.w, " DEAD") | |
| } | |
| fmt.Fprintln(p.w) | |
| } | |
| func (p stringFuncPrinter) startDepCycle() { | |
| fmt.Fprintln(p.w, "dependency cycle!") | |
| } | |
| func (p stringFuncPrinter) endDepCycle() {} | |
| func (p stringFuncPrinter) named(n LocalSlot, vals []*Value) { | |
| fmt.Fprintf(p.w, "name %s: %v\n", n, vals) | |
| } | |
| func fprintFunc(p funcPrinter, f *Func) { | |
| reachable, live := findlive(f) | |
| defer f.Cache.freeBoolSlice(live) | |
| p.header(f) | |
| printed := make([]bool, f.NumValues()) | |
| for _, b := range f.Blocks { | |
| p.startBlock(b, reachable[b.ID]) | |
| if f.scheduled { | |
| // Order of Values has been decided - print in that order. | |
| for _, v := range b.Values { | |
| p.value(v, live[v.ID]) | |
| printed[v.ID] = true | |
| } | |
| p.endBlock(b, reachable[b.ID]) | |
| continue | |
| } | |
| // print phis first since all value cycles contain a phi | |
| n := 0 | |
| for _, v := range b.Values { | |
| if v.Op != OpPhi { | |
| continue | |
| } | |
| p.value(v, live[v.ID]) | |
| printed[v.ID] = true | |
| n++ | |
| } | |
| // print rest of values in dependency order | |
| for n < len(b.Values) { | |
| m := n | |
| outer: | |
| for _, v := range b.Values { | |
| if printed[v.ID] { | |
| continue | |
| } | |
| for _, w := range v.Args { | |
| // w == nil shouldn't happen, but if it does, | |
| // don't panic; we'll get a better diagnosis later. | |
| if w != nil && w.Block == b && !printed[w.ID] { | |
| continue outer | |
| } | |
| } | |
| p.value(v, live[v.ID]) | |
| printed[v.ID] = true | |
| n++ | |
| } | |
| if m == n { | |
| p.startDepCycle() | |
| for _, v := range b.Values { | |
| if printed[v.ID] { | |
| continue | |
| } | |
| p.value(v, live[v.ID]) | |
| printed[v.ID] = true | |
| n++ | |
| } | |
| p.endDepCycle() | |
| } | |
| } | |
| p.endBlock(b, reachable[b.ID]) | |
| } | |
| for _, name := range f.Names { | |
| p.named(*name, f.NamedValues[*name]) | |
| } | |
| } |