1
2
3
4
5 package vta
6
7 import (
8 "go/token"
9 "go/types"
10 "reflect"
11 "sort"
12 "strings"
13 "testing"
14 "unsafe"
15
16 "golang.org/x/tools/go/ssa"
17
18 "golang.org/x/tools/go/types/typeutil"
19 )
20
21
22
23
24 type val struct {
25 name string
26 typ types.Type
27 }
28
29 func (v val) String() string {
30 return v.name
31 }
32
33 func (v val) Name() string {
34 return v.name
35 }
36
37 func (v val) Type() types.Type {
38 return v.typ
39 }
40
41 func (v val) Parent() *ssa.Function {
42 return nil
43 }
44
45 func (v val) Referrers() *[]ssa.Instruction {
46 return nil
47 }
48
49 func (v val) Pos() token.Pos {
50 return token.NoPos
51 }
52
53
54
55 func newLocal(name string, t types.Type) local {
56 return local{val: val{name: name, typ: t}}
57 }
58
59
60 func newNamedType(name string) *types.Named {
61 return types.NewNamed(types.NewTypeName(token.NoPos, nil, name, nil), types.Universe.Lookup("int").Type(), nil)
62 }
63
64
65
66
67 func sccString(nodeToScc map[node]int) []string {
68 sccs := make(map[int][]node)
69 for n, id := range nodeToScc {
70 sccs[id] = append(sccs[id], n)
71 }
72
73 var sccsStr []string
74 for _, scc := range sccs {
75 var nodesStr []string
76 for _, node := range scc {
77 nodesStr = append(nodesStr, node.String())
78 }
79 sort.Strings(nodesStr)
80 sccsStr = append(sccsStr, strings.Join(nodesStr, ";"))
81 }
82 return sccsStr
83 }
84
85
86
87
88
89
90 func nodeToTypeString(pMap propTypeMap) map[string]string {
91
92
93
94 propTypeString := func(p propType) string {
95 if p.f != nil {
96 return p.f.Name()
97 }
98 return p.typ.String()
99 }
100
101 nodeToTypeStr := make(map[string]string)
102 for node := range pMap.nodeToScc {
103 var propStrings []string
104 for _, prop := range pMap.propTypes(node) {
105 propStrings = append(propStrings, propTypeString(prop))
106 }
107 sort.Strings(propStrings)
108 nodeToTypeStr[node.String()] = strings.Join(propStrings, ";")
109 }
110
111 return nodeToTypeStr
112 }
113
114
115 func sccEqual(sccs1 []string, sccs2 []string) bool {
116 if len(sccs1) != len(sccs2) {
117 return false
118 }
119 sort.Strings(sccs1)
120 sort.Strings(sccs2)
121 return reflect.DeepEqual(sccs1, sccs2)
122 }
123
124
125
126
127
128 func isRevTopSorted(g vtaGraph, nodeToScc map[node]int) bool {
129 for n, succs := range g {
130 for s := range succs {
131 if nodeToScc[n] < nodeToScc[s] {
132 return false
133 }
134 }
135 }
136 return true
137 }
138
139
140
141
142 func setName(f *ssa.Function, name string) {
143 fi := reflect.ValueOf(f).Elem().FieldByName("name")
144 fi = reflect.NewAt(fi.Type(), unsafe.Pointer(fi.UnsafeAddr())).Elem()
145 fi.SetString(name)
146 }
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185 func testSuite() map[string]vtaGraph {
186 a := newNamedType("A")
187 b := newNamedType("B")
188 c := newNamedType("C")
189 sig := types.NewSignature(nil, types.NewTuple(), types.NewTuple(), false)
190
191 f1 := &ssa.Function{Signature: sig}
192 setName(f1, "F1")
193 f2 := &ssa.Function{Signature: sig}
194 setName(f2, "F2")
195 f3 := &ssa.Function{Signature: sig}
196 setName(f3, "F3")
197 f4 := &ssa.Function{Signature: sig}
198 setName(f4, "F4")
199
200 graphs := make(map[string]vtaGraph)
201 graphs["no-cycles"] = map[node]map[node]bool{
202 newLocal("t0", a): {newLocal("t1", b): true},
203 newLocal("t1", b): {newLocal("t2", c): true},
204 }
205
206 graphs["trivial-cycle"] = map[node]map[node]bool{
207 newLocal("t0", a): {newLocal("t0", a): true},
208 newLocal("t1", b): {newLocal("t1", b): true},
209 }
210
211 graphs["circle-cycle"] = map[node]map[node]bool{
212 newLocal("t0", a): {newLocal("t1", a): true},
213 newLocal("t1", a): {newLocal("t2", b): true},
214 newLocal("t2", b): {newLocal("t0", a): true},
215 }
216
217 graphs["fully-connected"] = map[node]map[node]bool{
218 newLocal("t0", a): {newLocal("t1", b): true, newLocal("t2", c): true},
219 newLocal("t1", b): {newLocal("t0", a): true, newLocal("t2", c): true},
220 newLocal("t2", c): {newLocal("t0", a): true, newLocal("t1", b): true},
221 }
222
223 graphs["subsumed-scc"] = map[node]map[node]bool{
224 newLocal("t0", a): {newLocal("t1", b): true},
225 newLocal("t1", b): {newLocal("t2", b): true},
226 newLocal("t2", b): {newLocal("t1", b): true, newLocal("t3", a): true},
227 newLocal("t3", a): {newLocal("t0", a): true},
228 }
229
230 graphs["more-realistic"] = map[node]map[node]bool{
231 newLocal("t0", a): {newLocal("t0", a): true},
232 newLocal("t1", a): {newLocal("t2", b): true},
233 newLocal("t2", b): {newLocal("t1", a): true, function{f1}: true},
234 function{f1}: {function{f2}: true, function{f3}: true},
235 function{f2}: {function{f3}: true},
236 function{f3}: {function{f1}: true, function{f4}: true},
237 }
238
239 return graphs
240 }
241
242 func TestSCC(t *testing.T) {
243 suite := testSuite()
244 for _, test := range []struct {
245 name string
246 graph vtaGraph
247 want []string
248 }{
249
250 {name: "no-cycles", graph: suite["no-cycles"], want: []string{"Local(t0)", "Local(t1)", "Local(t2)"}},
251
252 {name: "trivial-cycle", graph: suite["trivial-cycle"], want: []string{"Local(t0)", "Local(t1)"}},
253
254 {name: "circle-cycle", graph: suite["circle-cycle"], want: []string{"Local(t0);Local(t1);Local(t2)"}},
255
256 {name: "fully-connected", graph: suite["fully-connected"], want: []string{"Local(t0);Local(t1);Local(t2)"}},
257
258 {name: "subsumed-scc", graph: suite["subsumed-scc"], want: []string{"Local(t0);Local(t1);Local(t2);Local(t3)"}},
259
260 {name: "more-realistic", graph: suite["more-realistic"], want: []string{"Local(t0)", "Local(t1);Local(t2)", "Function(F1);Function(F2);Function(F3)", "Function(F4)"}},
261 } {
262 sccs, _ := scc(test.graph)
263 if got := sccString(sccs); !sccEqual(test.want, got) {
264 t.Errorf("want %v for graph %v; got %v", test.want, test.name, got)
265 }
266 if !isRevTopSorted(test.graph, sccs) {
267 t.Errorf("%v not topologically sorted", test.name)
268 }
269 }
270 }
271
272 func TestPropagation(t *testing.T) {
273 suite := testSuite()
274 var canon typeutil.Map
275 for _, test := range []struct {
276 name string
277 graph vtaGraph
278 want map[string]string
279 }{
280
281 {name: "no-cycles", graph: suite["no-cycles"],
282 want: map[string]string{
283 "Local(t0)": "A",
284 "Local(t1)": "A;B",
285 "Local(t2)": "A;B;C",
286 },
287 },
288
289 {name: "trivial-cycle", graph: suite["trivial-cycle"],
290 want: map[string]string{
291 "Local(t0)": "A",
292 "Local(t1)": "B",
293 },
294 },
295
296 {name: "circle-cycle", graph: suite["circle-cycle"],
297 want: map[string]string{
298 "Local(t0)": "A;B",
299 "Local(t1)": "A;B",
300 "Local(t2)": "A;B",
301 },
302 },
303
304 {name: "fully-connected", graph: suite["fully-connected"],
305 want: map[string]string{
306 "Local(t0)": "A;B;C",
307 "Local(t1)": "A;B;C",
308 "Local(t2)": "A;B;C",
309 },
310 },
311
312 {name: "subsumed-scc", graph: suite["subsumed-scc"],
313 want: map[string]string{
314 "Local(t0)": "A;B",
315 "Local(t1)": "A;B",
316 "Local(t2)": "A;B",
317 "Local(t3)": "A;B",
318 },
319 },
320
321 {name: "more-realistic", graph: suite["more-realistic"],
322 want: map[string]string{
323 "Local(t0)": "A",
324 "Local(t1)": "A;B",
325 "Local(t2)": "A;B",
326 "Function(F1)": "A;B;F1;F2;F3",
327 "Function(F2)": "A;B;F1;F2;F3",
328 "Function(F3)": "A;B;F1;F2;F3",
329 "Function(F4)": "A;B;F1;F2;F3;F4",
330 },
331 },
332 } {
333 if got := nodeToTypeString(propagate(test.graph, &canon)); !reflect.DeepEqual(got, test.want) {
334 t.Errorf("want %v for graph %v; got %v", test.want, test.name, got)
335 }
336 }
337 }
338
View as plain text