@@ -2,16 +2,21 @@ package btf
2
2
3
3
import (
4
4
"fmt"
5
+ "iter"
5
6
)
6
7
7
8
// Functions to traverse a cyclic graph of types. The below was very useful:
8
9
// https://eli.thegreenplace.net/2015/directed-graph-traversal-orderings-and-applications-to-data-flow-analysis/#post-order-and-reverse-post-order
9
10
10
- // Visit all types reachable from root in postorder.
11
- //
12
- // Traversal stops if yield returns false.
13
- //
14
- // Returns false if traversal was aborted.
11
+ // postorder yields all types reachable from root in post order.
12
+ func postorder (root Type , visited map [Type ]struct {}) iter.Seq [Type ] {
13
+ return func (yield func (Type ) bool ) {
14
+ visitInPostorder (root , visited , yield )
15
+ }
16
+ }
17
+
18
+ // visitInPostorder is a separate function to avoid arguments escaping
19
+ // to the heap. Don't change the setup without re-running the benchmarks.
15
20
func visitInPostorder (root Type , visited map [Type ]struct {}, yield func (typ Type ) bool ) bool {
16
21
if _ , ok := visited [root ]; ok {
17
22
return true
@@ -21,139 +26,134 @@ func visitInPostorder(root Type, visited map[Type]struct{}, yield func(typ Type)
21
26
}
22
27
visited [root ] = struct {}{}
23
28
24
- cont := children (root , func (child * Type ) bool {
25
- return visitInPostorder (* child , visited , yield )
26
- })
27
- if ! cont {
28
- return false
29
+ for child := range children (root ) {
30
+ if ! visitInPostorder (* child , visited , yield ) {
31
+ return false
32
+ }
29
33
}
30
34
31
35
return yield (root )
32
36
}
33
37
34
- // children calls yield on each child of typ.
35
- //
36
- // Traversal stops if yield returns false.
37
- //
38
- // Returns false if traversal was aborted.
39
- func children (typ Type , yield func (child * Type ) bool ) bool {
40
- // Explicitly type switch on the most common types to allow the inliner to
41
- // do its work. This avoids allocating intermediate slices from walk() on
42
- // the heap.
43
- var tags []string
44
- switch v := typ .(type ) {
45
- case * Void , * Int , * Enum , * Fwd , * Float , * declTag :
46
- // No children to traverse.
47
- // declTags is declared as a leaf type since it's parsed into .Tags fields of other types
48
- // during unmarshaling.
49
- case * Pointer :
50
- if ! yield (& v .Target ) {
51
- return false
52
- }
53
- case * Array :
54
- if ! yield (& v .Index ) {
55
- return false
56
- }
57
- if ! yield (& v .Type ) {
58
- return false
59
- }
60
- case * Struct :
61
- for i := range v .Members {
62
- if ! yield (& v .Members [i ].Type ) {
63
- return false
64
- }
65
- for _ , t := range v .Members [i ].Tags {
66
- var tag Type = & declTag {v , t , i }
67
- if ! yield (& tag ) {
68
- return false
69
- }
38
+ // children yields all direct descendants of typ.
39
+ func children (typ Type ) iter.Seq [* Type ] {
40
+ return func (yield func (* Type ) bool ) {
41
+ // Explicitly type switch on the most common types to allow the inliner to
42
+ // do its work. This avoids allocating intermediate slices from walk() on
43
+ // the heap.
44
+ var tags []string
45
+ switch v := typ .(type ) {
46
+ case * Void , * Int , * Enum , * Fwd , * Float , * declTag :
47
+ // No children to traverse.
48
+ // declTags is declared as a leaf type since it's parsed into .Tags fields of other types
49
+ // during unmarshaling.
50
+ case * Pointer :
51
+ if ! yield (& v .Target ) {
52
+ return
70
53
}
71
- }
72
- tags = v .Tags
73
- case * Union :
74
- for i := range v .Members {
75
- if ! yield (& v .Members [i ].Type ) {
76
- return false
77
- }
78
- for _ , t := range v .Members [i ].Tags {
79
- var tag Type = & declTag {v , t , i }
80
- if ! yield (& tag ) {
81
- return false
54
+ case * Array :
55
+ if ! yield (& v .Index ) {
56
+ return
57
+ }
58
+ if ! yield (& v .Type ) {
59
+ return
60
+ }
61
+ case * Struct :
62
+ for i := range v .Members {
63
+ if ! yield (& v .Members [i ].Type ) {
64
+ return
65
+ }
66
+ for _ , t := range v .Members [i ].Tags {
67
+ var tag Type = & declTag {v , t , i }
68
+ if ! yield (& tag ) {
69
+ return
70
+ }
82
71
}
83
72
}
84
- }
85
- tags = v .Tags
86
- case * Typedef :
87
- if ! yield (& v .Type ) {
88
- return false
89
- }
90
- tags = v .Tags
91
- case * Volatile :
92
- if ! yield (& v .Type ) {
93
- return false
94
- }
95
- case * Const :
96
- if ! yield (& v .Type ) {
97
- return false
98
- }
99
- case * Restrict :
100
- if ! yield (& v .Type ) {
101
- return false
102
- }
103
- case * Func :
104
- if ! yield (& v .Type ) {
105
- return false
106
- }
107
- if fp , ok := v .Type .(* FuncProto ); ok {
108
- for i := range fp .Params {
109
- if len (v .ParamTags ) <= i {
110
- continue
73
+ tags = v .Tags
74
+ case * Union :
75
+ for i := range v .Members {
76
+ if ! yield (& v .Members [i ].Type ) {
77
+ return
111
78
}
112
- for _ , t := range v .ParamTags [i ] {
79
+ for _ , t := range v .Members [i ]. Tags {
113
80
var tag Type = & declTag {v , t , i }
114
81
if ! yield (& tag ) {
115
- return false
82
+ return
116
83
}
117
84
}
118
85
}
119
- }
120
- tags = v .Tags
121
- case * FuncProto :
122
- if ! yield (& v .Return ) {
123
- return false
124
- }
125
- for i := range v .Params {
126
- if ! yield (& v .Params [i ].Type ) {
127
- return false
86
+ tags = v .Tags
87
+ case * Typedef :
88
+ if ! yield (& v .Type ) {
89
+ return
128
90
}
129
- }
130
- case * Var :
131
- if ! yield (& v .Type ) {
132
- return false
133
- }
134
- tags = v .Tags
135
- case * Datasec :
136
- for i := range v .Vars {
137
- if ! yield (& v .Vars [i ].Type ) {
138
- return false
91
+ tags = v .Tags
92
+ case * Volatile :
93
+ if ! yield (& v .Type ) {
94
+ return
139
95
}
96
+ case * Const :
97
+ if ! yield (& v .Type ) {
98
+ return
99
+ }
100
+ case * Restrict :
101
+ if ! yield (& v .Type ) {
102
+ return
103
+ }
104
+ case * Func :
105
+ if ! yield (& v .Type ) {
106
+ return
107
+ }
108
+ if fp , ok := v .Type .(* FuncProto ); ok {
109
+ for i := range fp .Params {
110
+ if len (v .ParamTags ) <= i {
111
+ continue
112
+ }
113
+ for _ , t := range v .ParamTags [i ] {
114
+ var tag Type = & declTag {v , t , i }
115
+ if ! yield (& tag ) {
116
+ return
117
+ }
118
+ }
119
+ }
120
+ }
121
+ tags = v .Tags
122
+ case * FuncProto :
123
+ if ! yield (& v .Return ) {
124
+ return
125
+ }
126
+ for i := range v .Params {
127
+ if ! yield (& v .Params [i ].Type ) {
128
+ return
129
+ }
130
+ }
131
+ case * Var :
132
+ if ! yield (& v .Type ) {
133
+ return
134
+ }
135
+ tags = v .Tags
136
+ case * Datasec :
137
+ for i := range v .Vars {
138
+ if ! yield (& v .Vars [i ].Type ) {
139
+ return
140
+ }
141
+ }
142
+ case * TypeTag :
143
+ if ! yield (& v .Type ) {
144
+ return
145
+ }
146
+ case * cycle :
147
+ // cycle has children, but we ignore them deliberately.
148
+ default :
149
+ panic (fmt .Sprintf ("don't know how to walk Type %T" , v ))
140
150
}
141
- case * TypeTag :
142
- if ! yield (& v .Type ) {
143
- return false
144
- }
145
- case * cycle :
146
- // cycle has children, but we ignore them deliberately.
147
- default :
148
- panic (fmt .Sprintf ("don't know how to walk Type %T" , v ))
149
- }
150
151
151
- for _ , t := range tags {
152
- var tag Type = & declTag {typ , t , - 1 }
153
- if ! yield (& tag ) {
154
- return false
152
+ for _ , t := range tags {
153
+ var tag Type = & declTag {typ , t , - 1 }
154
+ if ! yield (& tag ) {
155
+ return
156
+ }
155
157
}
156
158
}
157
-
158
- return true
159
159
}
0 commit comments