blob: ed7f1542497cf317621d3291b225b82b90b28e14 [file] [log] [blame] [edit]
// 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])
}
}