• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 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
12// Test that a trivial 'if' is eliminated
13func TestBranchElimIf(t *testing.T) {
14	var testData = []struct {
15		arch    string
16		intType string
17		ok      bool
18	}{
19		{"arm64", "int32", true},
20		{"amd64", "int32", true},
21		{"amd64", "int8", false},
22	}
23
24	for _, data := range testData {
25		t.Run(data.arch+"/"+data.intType, func(t *testing.T) {
26			c := testConfigArch(t, data.arch)
27			boolType := c.config.Types.Bool
28			var intType *types.Type
29			switch data.intType {
30			case "int32":
31				intType = c.config.Types.Int32
32			case "int8":
33				intType = c.config.Types.Int8
34			default:
35				t.Fatal("invalid integer type:", data.intType)
36			}
37			fun := c.Fun("entry",
38				Bloc("entry",
39					Valu("start", OpInitMem, types.TypeMem, 0, nil),
40					Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
41					Valu("const1", OpConst32, intType, 1, nil),
42					Valu("const2", OpConst32, intType, 2, nil),
43					Valu("addr", OpAddr, boolType.PtrTo(), 0, nil, "sb"),
44					Valu("cond", OpLoad, boolType, 0, nil, "addr", "start"),
45					If("cond", "b2", "b3")),
46				Bloc("b2",
47					Goto("b3")),
48				Bloc("b3",
49					Valu("phi", OpPhi, intType, 0, nil, "const1", "const2"),
50					Valu("retstore", OpStore, types.TypeMem, 0, nil, "phi", "sb", "start"),
51					Exit("retstore")))
52
53			CheckFunc(fun.f)
54			branchelim(fun.f)
55			CheckFunc(fun.f)
56			Deadcode(fun.f)
57			CheckFunc(fun.f)
58
59			if data.ok {
60
61				if len(fun.f.Blocks) != 1 {
62					t.Fatalf("expected 1 block after branchelim and deadcode; found %d", len(fun.f.Blocks))
63				}
64				if fun.values["phi"].Op != OpCondSelect {
65					t.Fatalf("expected phi op to be CondSelect; found op %s", fun.values["phi"].Op)
66				}
67				if fun.values["phi"].Args[2] != fun.values["cond"] {
68					t.Errorf("expected CondSelect condition to be %s; found %s", fun.values["cond"], fun.values["phi"].Args[2])
69				}
70				if fun.blocks["entry"].Kind != BlockExit {
71					t.Errorf("expected entry to be BlockExit; found kind %s", fun.blocks["entry"].Kind.String())
72				}
73			} else {
74				if len(fun.f.Blocks) != 3 {
75					t.Fatalf("expected 3 block after branchelim and deadcode; found %d", len(fun.f.Blocks))
76				}
77			}
78		})
79	}
80}
81
82// Test that a trivial if/else is eliminated
83func TestBranchElimIfElse(t *testing.T) {
84	for _, arch := range []string{"arm64", "amd64"} {
85		t.Run(arch, func(t *testing.T) {
86			c := testConfigArch(t, arch)
87			boolType := c.config.Types.Bool
88			intType := c.config.Types.Int32
89			fun := c.Fun("entry",
90				Bloc("entry",
91					Valu("start", OpInitMem, types.TypeMem, 0, nil),
92					Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
93					Valu("const1", OpConst32, intType, 1, nil),
94					Valu("const2", OpConst32, intType, 2, nil),
95					Valu("addr", OpAddr, boolType.PtrTo(), 0, nil, "sb"),
96					Valu("cond", OpLoad, boolType, 0, nil, "addr", "start"),
97					If("cond", "b2", "b3")),
98				Bloc("b2",
99					Goto("b4")),
100				Bloc("b3",
101					Goto("b4")),
102				Bloc("b4",
103					Valu("phi", OpPhi, intType, 0, nil, "const1", "const2"),
104					Valu("retstore", OpStore, types.TypeMem, 0, nil, "phi", "sb", "start"),
105					Exit("retstore")))
106
107			CheckFunc(fun.f)
108			branchelim(fun.f)
109			CheckFunc(fun.f)
110			Deadcode(fun.f)
111			CheckFunc(fun.f)
112
113			if len(fun.f.Blocks) != 1 {
114				t.Fatalf("expected 1 block after branchelim; found %d", len(fun.f.Blocks))
115			}
116			if fun.values["phi"].Op != OpCondSelect {
117				t.Fatalf("expected phi op to be CondSelect; found op %s", fun.values["phi"].Op)
118			}
119			if fun.values["phi"].Args[2] != fun.values["cond"] {
120				t.Errorf("expected CondSelect condition to be %s; found %s", fun.values["cond"], fun.values["phi"].Args[2])
121			}
122			if fun.blocks["entry"].Kind != BlockExit {
123				t.Errorf("expected entry to be BlockExit; found kind %s", fun.blocks["entry"].Kind.String())
124			}
125		})
126	}
127}
128
129// Test that an if/else CFG that loops back
130// into itself does *not* get eliminated.
131func TestNoBranchElimLoop(t *testing.T) {
132	for _, arch := range []string{"arm64", "amd64"} {
133		t.Run(arch, func(t *testing.T) {
134			c := testConfigArch(t, arch)
135			boolType := c.config.Types.Bool
136			intType := c.config.Types.Int32
137
138			// The control flow here is totally bogus,
139			// but a dead cycle seems like the only plausible
140			// way to arrive at a diamond CFG that is also a loop.
141			fun := c.Fun("entry",
142				Bloc("entry",
143					Valu("start", OpInitMem, types.TypeMem, 0, nil),
144					Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
145					Valu("const2", OpConst32, intType, 2, nil),
146					Valu("const3", OpConst32, intType, 3, nil),
147					Goto("b5")),
148				Bloc("b2",
149					Valu("addr", OpAddr, boolType.PtrTo(), 0, nil, "sb"),
150					Valu("cond", OpLoad, boolType, 0, nil, "addr", "start"),
151					Valu("phi", OpPhi, intType, 0, nil, "const2", "const3"),
152					If("cond", "b3", "b4")),
153				Bloc("b3",
154					Goto("b2")),
155				Bloc("b4",
156					Goto("b2")),
157				Bloc("b5",
158					Exit("start")))
159
160			CheckFunc(fun.f)
161			branchelim(fun.f)
162			CheckFunc(fun.f)
163
164			if len(fun.f.Blocks) != 5 {
165				t.Errorf("expected 5 block after branchelim; found %d", len(fun.f.Blocks))
166			}
167			if fun.values["phi"].Op != OpPhi {
168				t.Errorf("expected phi op to be CondSelect; found op %s", fun.values["phi"].Op)
169			}
170		})
171	}
172}
173