1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 package ssacompile
34
35 import (
36 "fmt"
37 "reflect"
38 "testing"
39
40 "cmd/compile/internal/ssa"
41 "cmd/compile/internal/ssa/block"
42 "cmd/compile/internal/ssa/ssaop"
43 "cmd/compile/internal/types"
44 "cmd/internal/obj"
45 "cmd/internal/src"
46 )
47
48
49
50
51
52
53
54
55
56
57
58 func Equiv(f, g *ssa.Func) bool {
59 valcor := make(map[*ssa.Value]*ssa.Value)
60 var checkVal func(fv, gv *ssa.Value) bool
61 checkVal = func(fv, gv *ssa.Value) bool {
62 if fv == nil && gv == nil {
63 return true
64 }
65 if valcor[fv] == nil && valcor[gv] == nil {
66 valcor[fv] = gv
67 valcor[gv] = fv
68
69
70
71 if fv.Op != gv.Op || fv.Type != gv.Type || fv.AuxInt != gv.AuxInt {
72 return false
73 }
74 if !reflect.DeepEqual(fv.Aux, gv.Aux) {
75
76
77
78
79 return false
80 }
81 if len(fv.Args) != len(gv.Args) {
82 return false
83 }
84 for i := range fv.Args {
85 if !checkVal(fv.Args[i], gv.Args[i]) {
86 return false
87 }
88 }
89 }
90 return valcor[fv] == gv && valcor[gv] == fv
91 }
92 blkcor := make(map[*ssa.Block]*ssa.Block)
93 var checkBlk func(fb, gb *ssa.Block) bool
94 checkBlk = func(fb, gb *ssa.Block) bool {
95 if blkcor[fb] == nil && blkcor[gb] == nil {
96 blkcor[fb] = gb
97 blkcor[gb] = fb
98
99 if fb.Kind != gb.Kind {
100 return false
101 }
102 if len(fb.Values) != len(gb.Values) {
103 return false
104 }
105 for i := range fb.Values {
106 if !checkVal(fb.Values[i], gb.Values[i]) {
107 return false
108 }
109 }
110 if len(fb.Succs) != len(gb.Succs) {
111 return false
112 }
113 for i := range fb.Succs {
114 if !checkBlk(fb.Succs[i].B, gb.Succs[i].B) {
115 return false
116 }
117 }
118 if len(fb.Preds) != len(gb.Preds) {
119 return false
120 }
121 for i := range fb.Preds {
122 if !checkBlk(fb.Preds[i].B, gb.Preds[i].B) {
123 return false
124 }
125 }
126 return true
127
128 }
129 return blkcor[fb] == gb && blkcor[gb] == fb
130 }
131
132 return checkBlk(f.Entry, g.Entry)
133 }
134
135
136
137
138 type fun struct {
139 f *ssa.Func
140 blocks map[string]*ssa.Block
141 values map[string]*ssa.Value
142 }
143
144 var emptyPass ssa.Pass = ssa.Pass{
145 Name: "empty pass",
146 }
147
148
149
150 func AuxCallLSym(name string) *ssa.AuxCall {
151 return &ssa.AuxCall{Fn: &obj.LSym{}}
152 }
153
154
155
156
157
158 func (c *Conf) Fun(entry string, blocs ...bloc) fun {
159
160
161
162 f := c.config.NewFunc(c.Frontend(), new(ssa.Cache))
163 f.Pass = &emptyPass
164 f.CachedLineStarts = ssa.NewXPosMap(map[int]ssa.LineRange{0: {First: 0, Last: 100}, 1: {First: 0, Last: 100}, 2: {First: 0, Last: 100}, 3: {First: 0, Last: 100}, 4: {First: 0, Last: 100}})
165
166 blocks := make(map[string]*ssa.Block)
167 values := make(map[string]*ssa.Value)
168
169 for _, bloc := range blocs {
170 b := f.NewBlock(bloc.control.kind)
171 blocks[bloc.name] = b
172 for _, valu := range bloc.valus {
173
174 values[valu.name] = b.NewValue0IA(src.NoXPos, valu.op, valu.t, valu.auxint, valu.aux)
175 }
176 }
177
178 f.Entry = blocks[entry]
179 for _, bloc := range blocs {
180 b := blocks[bloc.name]
181 c := bloc.control
182
183 if c.control != "" {
184 cval, ok := values[c.control]
185 if !ok {
186 f.Fatalf("control value for block %s missing", bloc.name)
187 }
188 b.SetControl(cval)
189 }
190
191 for _, valu := range bloc.valus {
192 v := values[valu.name]
193 for _, arg := range valu.args {
194 a, ok := values[arg]
195 if !ok {
196 b.Fatalf("arg %s missing for value %s in block %s",
197 arg, valu.name, bloc.name)
198 }
199 v.AddArg(a)
200 }
201 }
202
203 for _, succ := range c.succs {
204 b.AddEdgeTo(blocks[succ])
205 }
206 }
207 return fun{f, blocks, values}
208 }
209
210
211
212
213 func Bloc(name string, entries ...any) bloc {
214 b := bloc{}
215 b.name = name
216 seenCtrl := false
217 for _, e := range entries {
218 switch v := e.(type) {
219 case ctrl:
220
221 if seenCtrl {
222 panic(fmt.Sprintf("already seen control for block %s", name))
223 }
224 b.control = v
225 seenCtrl = true
226 case valu:
227 b.valus = append(b.valus, v)
228 }
229 }
230 if !seenCtrl {
231 panic(fmt.Sprintf("block %s doesn't have control", b.name))
232 }
233 return b
234 }
235
236
237 func Valu(name string, op ssaop.Op, t *types.Type, auxint int64, aux ssa.Aux, args ...string) valu {
238 return valu{name, op, t, auxint, aux, args}
239 }
240
241
242
243 func Goto(succ string) ctrl {
244 return ctrl{block.BlockPlain, "", []string{succ}}
245 }
246
247
248 func If(cond, sub, alt string) ctrl {
249 return ctrl{block.BlockIf, cond, []string{sub, alt}}
250 }
251
252
253 func Exit(arg string) ctrl {
254 return ctrl{block.BlockExit, arg, []string{}}
255 }
256
257
258 func Ret(arg string) ctrl {
259 return ctrl{block.BlockRet, arg, []string{}}
260 }
261
262
263 func Eq(cond, sub, alt string) ctrl {
264 return ctrl{block.BlockAMD64EQ, cond, []string{sub, alt}}
265 }
266
267
268 func Lt(cond, yes, no string) ctrl {
269 return ctrl{block.BlockAMD64LT, cond, []string{yes, no}}
270 }
271
272
273
274
275 type bloc struct {
276 name string
277 control ctrl
278 valus []valu
279 }
280
281 type ctrl struct {
282 kind block.BlockKind
283 control string
284 succs []string
285 }
286
287 type valu struct {
288 name string
289 op ssaop.Op
290 t *types.Type
291 auxint int64
292 aux ssa.Aux
293 args []string
294 }
295
296 func TestArgs(t *testing.T) {
297 c := testConfig(t)
298 fun := c.Fun("entry",
299 Bloc("entry",
300 Valu("a", ssaop.OpConst64, c.config.Types.Int64, 14, nil),
301 Valu("b", ssaop.OpConst64, c.config.Types.Int64, 26, nil),
302 Valu("sum", ssaop.OpAdd64, c.config.Types.Int64, 0, nil, "a", "b"),
303 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
304 Goto("exit")),
305 Bloc("exit",
306 Exit("mem")))
307 sum := fun.values["sum"]
308 for i, name := range []string{"a", "b"} {
309 if sum.Args[i] != fun.values[name] {
310 t.Errorf("arg %d for sum is incorrect: want %s, got %s",
311 i, sum.Args[i], fun.values[name])
312 }
313 }
314 }
315
316 func TestEquiv(t *testing.T) {
317 cfg := testConfig(t)
318 equivalentCases := []struct{ f, g fun }{
319
320 {
321 cfg.Fun("entry",
322 Bloc("entry",
323 Valu("a", ssaop.OpConst64, cfg.config.Types.Int64, 14, nil),
324 Valu("b", ssaop.OpConst64, cfg.config.Types.Int64, 26, nil),
325 Valu("sum", ssaop.OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
326 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
327 Goto("exit")),
328 Bloc("exit",
329 Exit("mem"))),
330 cfg.Fun("entry",
331 Bloc("entry",
332 Valu("a", ssaop.OpConst64, cfg.config.Types.Int64, 14, nil),
333 Valu("b", ssaop.OpConst64, cfg.config.Types.Int64, 26, nil),
334 Valu("sum", ssaop.OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
335 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
336 Goto("exit")),
337 Bloc("exit",
338 Exit("mem"))),
339 },
340
341 {
342 cfg.Fun("entry",
343 Bloc("entry",
344 Valu("a", ssaop.OpConst64, cfg.config.Types.Int64, 14, nil),
345 Valu("b", ssaop.OpConst64, cfg.config.Types.Int64, 26, nil),
346 Valu("sum", ssaop.OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
347 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
348 Goto("exit")),
349 Bloc("exit",
350 Exit("mem"))),
351 cfg.Fun("entry",
352 Bloc("exit",
353 Exit("mem")),
354 Bloc("entry",
355 Valu("a", ssaop.OpConst64, cfg.config.Types.Int64, 14, nil),
356 Valu("b", ssaop.OpConst64, cfg.config.Types.Int64, 26, nil),
357 Valu("sum", ssaop.OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
358 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
359 Goto("exit"))),
360 },
361 }
362 for _, c := range equivalentCases {
363 if !Equiv(c.f.f, c.g.f) {
364 t.Error("expected equivalence. Func definitions:")
365 t.Error(c.f.f)
366 t.Error(c.g.f)
367 }
368 }
369
370 differentCases := []struct{ f, g fun }{
371
372 {
373 cfg.Fun("entry",
374 Bloc("entry",
375 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
376 Goto("exit")),
377 Bloc("exit",
378 Exit("mem"))),
379 cfg.Fun("entry",
380 Bloc("entry",
381 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
382 Exit("mem"))),
383 },
384
385 {
386 cfg.Fun("entry",
387 Bloc("entry",
388 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
389 Valu("b", ssaop.OpConst64, cfg.config.Types.Int64, 26, nil),
390 Valu("a", ssaop.OpConst64, cfg.config.Types.Int64, 14, nil),
391 Exit("mem"))),
392 cfg.Fun("entry",
393 Bloc("entry",
394 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
395 Valu("a", ssaop.OpConst64, cfg.config.Types.Int64, 14, nil),
396 Valu("b", ssaop.OpConst64, cfg.config.Types.Int64, 26, nil),
397 Exit("mem"))),
398 },
399
400 {
401 cfg.Fun("entry",
402 Bloc("entry",
403 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
404 Valu("a", ssaop.OpConst64, cfg.config.Types.Int64, 14, nil),
405 Exit("mem"))),
406 cfg.Fun("entry",
407 Bloc("entry",
408 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
409 Valu("a", ssaop.OpConst64, cfg.config.Types.Int64, 26, nil),
410 Exit("mem"))),
411 },
412
413 {
414 cfg.Fun("entry",
415 Bloc("entry",
416 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
417 Valu("a", ssaop.OpConstString, cfg.config.Types.String, 0, ssa.StringToAux("foo")),
418 Exit("mem"))),
419 cfg.Fun("entry",
420 Bloc("entry",
421 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
422 Valu("a", ssaop.OpConstString, cfg.config.Types.String, 0, ssa.StringToAux("bar")),
423 Exit("mem"))),
424 },
425
426 {
427 cfg.Fun("entry",
428 Bloc("entry",
429 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
430 Valu("a", ssaop.OpConst64, cfg.config.Types.Int64, 14, nil),
431 Valu("b", ssaop.OpConst64, cfg.config.Types.Int64, 26, nil),
432 Valu("sum", ssaop.OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
433 Exit("mem"))),
434 cfg.Fun("entry",
435 Bloc("entry",
436 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
437 Valu("a", ssaop.OpConst64, cfg.config.Types.Int64, 0, nil),
438 Valu("b", ssaop.OpConst64, cfg.config.Types.Int64, 14, nil),
439 Valu("sum", ssaop.OpAdd64, cfg.config.Types.Int64, 0, nil, "b", "a"),
440 Exit("mem"))),
441 },
442 }
443 for _, c := range differentCases {
444 if Equiv(c.f.f, c.g.f) {
445 t.Error("expected difference. Func definitions:")
446 t.Error(c.f.f)
447 t.Error(c.g.f)
448 }
449 }
450 }
451
452
453
454 func TestConstCache(t *testing.T) {
455 c := testConfig(t)
456 f := c.Fun("entry",
457 Bloc("entry",
458 Valu("mem", ssaop.OpInitMem, types.TypeMem, 0, nil),
459 Exit("mem")))
460 v1 := f.f.ConstBool(c.config.Types.Bool, false)
461 v2 := f.f.ConstBool(c.config.Types.Bool, true)
462 f.f.FreeValue(v1)
463 f.f.FreeValue(v2)
464 v3 := f.f.ConstBool(c.config.Types.Bool, false)
465 v4 := f.f.ConstBool(c.config.Types.Bool, true)
466 if v3.AuxInt != 0 {
467 t.Errorf("expected %s to have auxint of 0\n", v3.LongString())
468 }
469 if v4.AuxInt != 1 {
470 t.Errorf("expected %s to have auxint of 1\n", v4.LongString())
471 }
472
473 }
474
475
476
477 func opcodeMap(f *ssa.Func) map[ssaop.Op]int {
478 m := map[ssaop.Op]int{}
479 for _, b := range f.Blocks {
480 for _, v := range b.Values {
481 m[v.Op]++
482 }
483 }
484 return m
485 }
486
487
488
489 func checkOpcodeCounts(t *testing.T, f *ssa.Func, m map[ssaop.Op]int) {
490 n := opcodeMap(f)
491 for op, cnt := range m {
492 if n[op] != cnt {
493 t.Errorf("%s appears %d times, want %d times", op, n[op], cnt)
494 }
495 }
496 }
497
View as plain text