1
2
3
4
5
6
7
8
9 package ir
10
11 import (
12 "crypto/sha256"
13 "encoding/hex"
14 "fmt"
15 "io"
16 "net/url"
17 "os"
18 "reflect"
19 "regexp"
20 "strings"
21 "sync"
22
23 "cmd/compile/internal/base"
24 "cmd/compile/internal/types"
25 "cmd/internal/src"
26 )
27
28
29 func DumpAny(root any, filter string, depth int) {
30 FDumpAny(os.Stderr, root, filter, depth)
31 }
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 func FDumpAny(w io.Writer, root any, filter string, depth int) {
51 if root == nil {
52 fmt.Fprintln(w, "nil")
53 return
54 }
55
56 if filter == "" {
57 filter = ".*"
58 }
59
60 p := dumper{
61 output: w,
62 fieldrx: regexp.MustCompile(filter),
63 ptrmap: make(map[uintptr]int),
64 last: '\n',
65 }
66
67 p.dump(reflect.ValueOf(root), depth)
68 p.printf("\n")
69 }
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 func MatchAstDump(fn *Func, where string) bool {
85 if len(base.Debug.AstDump) == 0 {
86 return false
87 }
88 return matchForDump(fn, base.Ctxt.Pkgpath, where)
89 }
90
91 var dbgRE *regexp.Regexp
92 var onceDbgRE sync.Once
93
94 func matchForDump(fn *Func, pkgPath, where string) bool {
95 dbg := false
96 flag := base.Debug.AstDump
97 if flag[0] == '~' {
98 onceDbgRE.Do(func() { dbgRE = regexp.MustCompile(flag[1:]) })
99 dbg = dbgRE.MatchString(pkgPath + "." + FuncName(fn))
100 } else {
101 dbg = matchPkgFn(pkgPath, FuncName(fn), flag)
102 }
103 return dbg
104 }
105
106
107
108
109
110 func matchPkgFn(pkgName, fnName, toMatch string) bool {
111 if fnName == toMatch {
112 return true
113 }
114 matchPkgDotName := func(pkg string) bool {
115
116 return len(toMatch) == len(pkg)+1+len(fnName) &&
117 strings.HasPrefix(toMatch, pkg) && toMatch[len(pkg)] == '.' && strings.HasSuffix(toMatch, fnName)
118 }
119 if matchPkgDotName(pkgName) {
120 return true
121 }
122 if l := strings.LastIndexByte(pkgName, '/'); l > 0 && matchPkgDotName(pkgName[l+1:]) {
123 return true
124 }
125
126 return false
127 }
128
129
130
131
132
133
134
135
136
137
138
139 func AstDump(fn *Func, why string) {
140 err := withLockAndFile(
141 fn,
142 func(w io.Writer) {
143 FDump(w, why, fn)
144 },
145 )
146
147 comma := strings.Index(why, ",")
148 if comma > 0 {
149 why = why[:comma]
150 }
151 DumpNodeHTML(fn, why, fn)
152 if err != nil {
153 fmt.Fprintf(os.Stderr, "Dump returned error %v\n", err)
154 }
155 }
156
157 var mu sync.Mutex
158 var astDumpFiles = make(map[string]bool)
159
160
161
162
163
164
165 func escapedFileName(fn *Func, suffix string) string {
166 name := url.PathEscape(PkgFuncName(fn))
167 if len(name) > 125 {
168 hash := sha256.Sum256([]byte(name))
169 name = hex.EncodeToString(hash[:8])
170 }
171 return name + suffix
172 }
173
174
175
176 func withLockAndFile(fn *Func, dump func(io.Writer)) (err error) {
177 name := escapedFileName(fn, ".ast")
178
179
180 mu.Lock()
181 defer mu.Unlock()
182 mode := os.O_APPEND | os.O_RDWR
183 if !astDumpFiles[name] {
184 astDumpFiles[name] = true
185 mode = os.O_CREATE | os.O_TRUNC | os.O_RDWR
186 fmt.Fprintf(os.Stderr, "Writing text ast output for %s to %s\n", PkgFuncName(fn), name)
187 }
188
189 fi, err := os.OpenFile(name, mode, 0777)
190 if err != nil {
191 return err
192 }
193 defer func() { err = fi.Close() }()
194 dump(fi)
195 return
196 }
197
198 var htmlWriters = make(map[*Func]*HTMLWriter)
199 var orderedFuncs = []*Func{}
200
201
202
203 func DumpNodeHTML(fn *Func, why string, n Node) {
204 mu.Lock()
205 defer mu.Unlock()
206 w, ok := htmlWriters[fn]
207 if !ok {
208 name := escapedFileName(fn, ".html")
209 w = NewHTMLWriter(name, fn, "")
210 htmlWriters[fn] = w
211 orderedFuncs = append(orderedFuncs, fn)
212 }
213 w.WritePhase(why, why)
214 }
215
216
217 func CloseHTMLWriters() {
218 mu.Lock()
219 defer mu.Unlock()
220 for _, fn := range orderedFuncs {
221 if w, ok := htmlWriters[fn]; ok {
222 w.Close()
223 delete(htmlWriters, fn)
224 }
225 }
226 orderedFuncs = nil
227 }
228
229 type dumper struct {
230 output io.Writer
231 fieldrx *regexp.Regexp
232 ptrmap map[uintptr]int
233 lastadr string
234
235
236 indent int
237 last byte
238 line int
239 }
240
241 var indentBytes = []byte(". ")
242
243 func (p *dumper) Write(data []byte) (n int, err error) {
244 var m int
245 for i, b := range data {
246
247 if b == '\n' {
248 m, err = p.output.Write(data[n : i+1])
249 n += m
250 if err != nil {
251 return
252 }
253 } else if p.last == '\n' {
254 p.line++
255 _, err = fmt.Fprintf(p.output, "%6d ", p.line)
256 if err != nil {
257 return
258 }
259 for j := p.indent; j > 0; j-- {
260 _, err = p.output.Write(indentBytes)
261 if err != nil {
262 return
263 }
264 }
265 }
266 p.last = b
267 }
268 if len(data) > n {
269 m, err = p.output.Write(data[n:])
270 n += m
271 }
272 return
273 }
274
275
276 func (p *dumper) printf(format string, args ...any) {
277 if _, err := fmt.Fprintf(p, format, args...); err != nil {
278 panic(err)
279 }
280 }
281
282
283
284
285
286 func (p *dumper) addr(x reflect.Value) string {
287 if !x.CanAddr() {
288 return "?"
289 }
290 adr := fmt.Sprintf("%p", x.Addr().Interface())
291 s := adr
292 if i := commonPrefixLen(p.lastadr, adr); i > 0 {
293 s = "0x…" + adr[i:]
294 }
295 p.lastadr = adr
296 return s
297 }
298
299
300 func (p *dumper) dump(x reflect.Value, depth int) {
301 if depth == 0 {
302 p.printf("…")
303 return
304 }
305
306 if pos, ok := x.Interface().(src.XPos); ok {
307 p.printf("%s", base.FmtPos(pos))
308 return
309 }
310
311 switch x.Kind() {
312 case reflect.String:
313 p.printf("%q", x.Interface())
314
315 case reflect.Interface:
316 if x.IsNil() {
317 p.printf("nil")
318 return
319 }
320 p.dump(x.Elem(), depth-1)
321
322 case reflect.Ptr:
323 if x.IsNil() {
324 p.printf("nil")
325 return
326 }
327
328 p.printf("*")
329 ptr := x.Pointer()
330 if line, exists := p.ptrmap[ptr]; exists {
331 p.printf("(@%d)", line)
332 return
333 }
334 p.ptrmap[ptr] = p.line
335 p.dump(x.Elem(), depth)
336
337 case reflect.Slice:
338 if x.IsNil() {
339 p.printf("nil")
340 return
341 }
342 p.printf("%s (%d entries) {", x.Type(), x.Len())
343 if x.Len() > 0 {
344 p.indent++
345 p.printf("\n")
346 for i, n := 0, x.Len(); i < n; i++ {
347 p.printf("%d: ", i)
348 p.dump(x.Index(i), depth-1)
349 p.printf("\n")
350 }
351 p.indent--
352 }
353 p.printf("}")
354
355 case reflect.Struct:
356 typ := x.Type()
357
358 isNode := false
359 if n, ok := x.Interface().(Node); ok {
360 isNode = true
361 p.printf("%s %s {", n.Op().String(), p.addr(x))
362 } else {
363 p.printf("%s {", typ)
364 }
365 p.indent++
366
367 first := true
368 omitted := false
369 for i, n := 0, typ.NumField(); i < n; i++ {
370
371
372 if name := typ.Field(i).Name; types.IsExported(name) {
373 if !p.fieldrx.MatchString(name) {
374 omitted = true
375 continue
376 }
377
378
379 if isNode && name == "Op" {
380 omitted = true
381 continue
382 }
383 x := x.Field(i)
384 if x.IsZero() {
385 omitted = true
386 continue
387 }
388 if n, ok := x.Interface().(Nodes); ok && len(n) == 0 {
389 omitted = true
390 continue
391 }
392
393 if first {
394 p.printf("\n")
395 first = false
396 }
397 p.printf("%s: ", name)
398 p.dump(x, depth-1)
399 p.printf("\n")
400 }
401 }
402 if omitted {
403 p.printf("…\n")
404 }
405
406 p.indent--
407 p.printf("}")
408
409 default:
410 p.printf("%v", x.Interface())
411 }
412 }
413
414 func commonPrefixLen(a, b string) (i int) {
415 for i < len(a) && i < len(b) && a[i] == b[i] {
416 i++
417 }
418 return
419 }
420
View as plain text