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