| // 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]) |
| } |
| } |