1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ssa
6
7import (
8	"cmd/compile/internal/types"
9	"testing"
10)
11
12func TestSchedule(t *testing.T) {
13	c := testConfig(t)
14	cases := []fun{
15		c.Fun("entry",
16			Bloc("entry",
17				Valu("mem0", OpInitMem, types.TypeMem, 0, nil),
18				Valu("ptr", OpConst64, c.config.Types.Int64, 0xABCD, nil),
19				Valu("v", OpConst64, c.config.Types.Int64, 12, nil),
20				Valu("mem1", OpStore, types.TypeMem, 0, c.config.Types.Int64, "ptr", "v", "mem0"),
21				Valu("mem2", OpStore, types.TypeMem, 0, c.config.Types.Int64, "ptr", "v", "mem1"),
22				Valu("mem3", OpStore, types.TypeMem, 0, c.config.Types.Int64, "ptr", "sum", "mem2"),
23				Valu("l1", OpLoad, c.config.Types.Int64, 0, nil, "ptr", "mem1"),
24				Valu("l2", OpLoad, c.config.Types.Int64, 0, nil, "ptr", "mem2"),
25				Valu("sum", OpAdd64, c.config.Types.Int64, 0, nil, "l1", "l2"),
26				Goto("exit")),
27			Bloc("exit",
28				Exit("mem3"))),
29	}
30	for _, c := range cases {
31		schedule(c.f)
32		if !isSingleLiveMem(c.f) {
33			t.Error("single-live-mem restriction not enforced by schedule for func:")
34			printFunc(c.f)
35		}
36	}
37}
38
39func isSingleLiveMem(f *Func) bool {
40	for _, b := range f.Blocks {
41		var liveMem *Value
42		for _, v := range b.Values {
43			for _, w := range v.Args {
44				if w.Type.IsMemory() {
45					if liveMem == nil {
46						liveMem = w
47						continue
48					}
49					if w != liveMem {
50						return false
51					}
52				}
53			}
54			if v.Type.IsMemory() {
55				liveMem = v
56			}
57		}
58	}
59	return true
60}
61
62func TestStoreOrder(t *testing.T) {
63	// In the function below, v2 depends on v3 and v4, v4 depends on v3, and v3 depends on store v5.
64	// storeOrder did not handle this case correctly.
65	c := testConfig(t)
66	fun := c.Fun("entry",
67		Bloc("entry",
68			Valu("mem0", OpInitMem, types.TypeMem, 0, nil),
69			Valu("a", OpAdd64, c.config.Types.Int64, 0, nil, "b", "c"),                        // v2
70			Valu("b", OpLoad, c.config.Types.Int64, 0, nil, "ptr", "mem1"),                    // v3
71			Valu("c", OpNeg64, c.config.Types.Int64, 0, nil, "b"),                             // v4
72			Valu("mem1", OpStore, types.TypeMem, 0, c.config.Types.Int64, "ptr", "v", "mem0"), // v5
73			Valu("mem2", OpStore, types.TypeMem, 0, c.config.Types.Int64, "ptr", "a", "mem1"),
74			Valu("ptr", OpConst64, c.config.Types.Int64, 0xABCD, nil),
75			Valu("v", OpConst64, c.config.Types.Int64, 12, nil),
76			Goto("exit")),
77		Bloc("exit",
78			Exit("mem2")))
79
80	CheckFunc(fun.f)
81	order := storeOrder(fun.f.Blocks[0].Values, fun.f.newSparseSet(fun.f.NumValues()), make([]int32, fun.f.NumValues()))
82
83	// check that v2, v3, v4 is sorted after v5
84	var ai, bi, ci, si int
85	for i, v := range order {
86		switch v.ID {
87		case 2:
88			ai = i
89		case 3:
90			bi = i
91		case 4:
92			ci = i
93		case 5:
94			si = i
95		}
96	}
97	if ai < si || bi < si || ci < si {
98		t.Logf("Func: %s", fun.f)
99		t.Errorf("store order is wrong: got %v, want v2 v3 v4 after v5", order)
100	}
101}
102