1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ssa
6
7import (
8	"fmt"
9	"io"
10	"strings"
11
12	"cmd/internal/notsha256"
13	"cmd/internal/src"
14)
15
16func printFunc(f *Func) {
17	f.Logf("%s", f)
18}
19
20func hashFunc(f *Func) []byte {
21	h := notsha256.New()
22	p := stringFuncPrinter{w: h, printDead: true}
23	fprintFunc(p, f)
24	return h.Sum(nil)
25}
26
27func (f *Func) String() string {
28	var buf strings.Builder
29	p := stringFuncPrinter{w: &buf, printDead: true}
30	fprintFunc(p, f)
31	return buf.String()
32}
33
34// rewriteHash returns a hash of f suitable for detecting rewrite cycles.
35func (f *Func) rewriteHash() string {
36	h := notsha256.New()
37	p := stringFuncPrinter{w: h, printDead: false}
38	fprintFunc(p, f)
39	return fmt.Sprintf("%x", h.Sum(nil))
40}
41
42type funcPrinter interface {
43	header(f *Func)
44	startBlock(b *Block, reachable bool)
45	endBlock(b *Block, reachable bool)
46	value(v *Value, live bool)
47	startDepCycle()
48	endDepCycle()
49	named(n LocalSlot, vals []*Value)
50}
51
52type stringFuncPrinter struct {
53	w         io.Writer
54	printDead bool
55}
56
57func (p stringFuncPrinter) header(f *Func) {
58	fmt.Fprint(p.w, f.Name)
59	fmt.Fprint(p.w, " ")
60	fmt.Fprintln(p.w, f.Type)
61}
62
63func (p stringFuncPrinter) startBlock(b *Block, reachable bool) {
64	if !p.printDead && !reachable {
65		return
66	}
67	fmt.Fprintf(p.w, "  b%d:", b.ID)
68	if len(b.Preds) > 0 {
69		io.WriteString(p.w, " <-")
70		for _, e := range b.Preds {
71			pred := e.b
72			fmt.Fprintf(p.w, " b%d", pred.ID)
73		}
74	}
75	if !reachable {
76		fmt.Fprint(p.w, " DEAD")
77	}
78	io.WriteString(p.w, "\n")
79}
80
81func (p stringFuncPrinter) endBlock(b *Block, reachable bool) {
82	if !p.printDead && !reachable {
83		return
84	}
85	fmt.Fprintln(p.w, "    "+b.LongString())
86}
87
88func StmtString(p src.XPos) string {
89	linenumber := "(?) "
90	if p.IsKnown() {
91		pfx := ""
92		if p.IsStmt() == src.PosIsStmt {
93			pfx = "+"
94		}
95		if p.IsStmt() == src.PosNotStmt {
96			pfx = "-"
97		}
98		linenumber = fmt.Sprintf("(%s%d) ", pfx, p.Line())
99	}
100	return linenumber
101}
102
103func (p stringFuncPrinter) value(v *Value, live bool) {
104	if !p.printDead && !live {
105		return
106	}
107	fmt.Fprintf(p.w, "    %s", StmtString(v.Pos))
108	fmt.Fprint(p.w, v.LongString())
109	if !live {
110		fmt.Fprint(p.w, " DEAD")
111	}
112	fmt.Fprintln(p.w)
113}
114
115func (p stringFuncPrinter) startDepCycle() {
116	fmt.Fprintln(p.w, "dependency cycle!")
117}
118
119func (p stringFuncPrinter) endDepCycle() {}
120
121func (p stringFuncPrinter) named(n LocalSlot, vals []*Value) {
122	fmt.Fprintf(p.w, "name %s: %v\n", n, vals)
123}
124
125func fprintFunc(p funcPrinter, f *Func) {
126	reachable, live := findlive(f)
127	defer f.Cache.freeBoolSlice(live)
128	p.header(f)
129	printed := make([]bool, f.NumValues())
130	for _, b := range f.Blocks {
131		p.startBlock(b, reachable[b.ID])
132
133		if f.scheduled {
134			// Order of Values has been decided - print in that order.
135			for _, v := range b.Values {
136				p.value(v, live[v.ID])
137				printed[v.ID] = true
138			}
139			p.endBlock(b, reachable[b.ID])
140			continue
141		}
142
143		// print phis first since all value cycles contain a phi
144		n := 0
145		for _, v := range b.Values {
146			if v.Op != OpPhi {
147				continue
148			}
149			p.value(v, live[v.ID])
150			printed[v.ID] = true
151			n++
152		}
153
154		// print rest of values in dependency order
155		for n < len(b.Values) {
156			m := n
157		outer:
158			for _, v := range b.Values {
159				if printed[v.ID] {
160					continue
161				}
162				for _, w := range v.Args {
163					// w == nil shouldn't happen, but if it does,
164					// don't panic; we'll get a better diagnosis later.
165					if w != nil && w.Block == b && !printed[w.ID] {
166						continue outer
167					}
168				}
169				p.value(v, live[v.ID])
170				printed[v.ID] = true
171				n++
172			}
173			if m == n {
174				p.startDepCycle()
175				for _, v := range b.Values {
176					if printed[v.ID] {
177						continue
178					}
179					p.value(v, live[v.ID])
180					printed[v.ID] = true
181					n++
182				}
183				p.endDepCycle()
184			}
185		}
186
187		p.endBlock(b, reachable[b.ID])
188	}
189	for _, name := range f.Names {
190		p.named(*name, f.NamedValues[*name])
191	}
192}
193