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/internal/src"
9	"fmt"
10)
11
12// fuseEarly runs fuse(f, fuseTypePlain|fuseTypeIntInRange).
13func fuseEarly(f *Func) { fuse(f, fuseTypePlain|fuseTypeIntInRange) }
14
15// fuseLate runs fuse(f, fuseTypePlain|fuseTypeIf|fuseTypeBranchRedirect).
16func fuseLate(f *Func) { fuse(f, fuseTypePlain|fuseTypeIf|fuseTypeBranchRedirect) }
17
18type fuseType uint8
19
20const (
21	fuseTypePlain fuseType = 1 << iota
22	fuseTypeIf
23	fuseTypeIntInRange
24	fuseTypeBranchRedirect
25	fuseTypeShortCircuit
26)
27
28// fuse simplifies control flow by joining basic blocks.
29func fuse(f *Func, typ fuseType) {
30	for changed := true; changed; {
31		changed = false
32		// Be sure to avoid quadratic behavior in fuseBlockPlain. See issue 13554.
33		// Previously this was dealt with using backwards iteration, now fuseBlockPlain
34		// handles large runs of blocks.
35		for i := len(f.Blocks) - 1; i >= 0; i-- {
36			b := f.Blocks[i]
37			if typ&fuseTypeIf != 0 {
38				changed = fuseBlockIf(b) || changed
39			}
40			if typ&fuseTypeIntInRange != 0 {
41				changed = fuseIntegerComparisons(b) || changed
42			}
43			if typ&fuseTypePlain != 0 {
44				changed = fuseBlockPlain(b) || changed
45			}
46			if typ&fuseTypeShortCircuit != 0 {
47				changed = shortcircuitBlock(b) || changed
48			}
49		}
50
51		if typ&fuseTypeBranchRedirect != 0 {
52			changed = fuseBranchRedirect(f) || changed
53		}
54		if changed {
55			f.invalidateCFG()
56		}
57	}
58}
59
60// fuseBlockIf handles the following cases where s0 and s1 are empty blocks.
61//
62//	   b        b           b       b
63//	\ / \ /    | \  /    \ / |     | |
64//	 s0  s1    |  s1      s0 |     | |
65//	  \ /      | /         \ |     | |
66//	   ss      ss           ss      ss
67//
68// If all Phi ops in ss have identical variables for slots corresponding to
69// s0, s1 and b then the branch can be dropped.
70// This optimization often comes up in switch statements with multiple
71// expressions in a case clause:
72//
73//	switch n {
74//	  case 1,2,3: return 4
75//	}
76//
77// TODO: If ss doesn't contain any OpPhis, are s0 and s1 dead code anyway.
78func fuseBlockIf(b *Block) bool {
79	if b.Kind != BlockIf {
80		return false
81	}
82	// It doesn't matter how much Preds does s0 or s1 have.
83	var ss0, ss1 *Block
84	s0 := b.Succs[0].b
85	i0 := b.Succs[0].i
86	if s0.Kind != BlockPlain || !isEmpty(s0) {
87		s0, ss0 = b, s0
88	} else {
89		ss0 = s0.Succs[0].b
90		i0 = s0.Succs[0].i
91	}
92	s1 := b.Succs[1].b
93	i1 := b.Succs[1].i
94	if s1.Kind != BlockPlain || !isEmpty(s1) {
95		s1, ss1 = b, s1
96	} else {
97		ss1 = s1.Succs[0].b
98		i1 = s1.Succs[0].i
99	}
100	if ss0 != ss1 {
101		if s0.Kind == BlockPlain && isEmpty(s0) && s1.Kind == BlockPlain && isEmpty(s1) {
102			// Two special cases where both s0, s1 and ss are empty blocks.
103			if s0 == ss1 {
104				s0, ss0 = b, ss1
105			} else if ss0 == s1 {
106				s1, ss1 = b, ss0
107			} else {
108				return false
109			}
110		} else {
111			return false
112		}
113	}
114	ss := ss0
115
116	// s0 and s1 are equal with b if the corresponding block is missing
117	// (2nd, 3rd and 4th case in the figure).
118
119	for _, v := range ss.Values {
120		if v.Op == OpPhi && v.Uses > 0 && v.Args[i0] != v.Args[i1] {
121			return false
122		}
123	}
124
125	// We do not need to redirect the Preds of s0 and s1 to ss,
126	// the following optimization will do this.
127	b.removeEdge(0)
128	if s0 != b && len(s0.Preds) == 0 {
129		s0.removeEdge(0)
130		// Move any (dead) values in s0 to b,
131		// where they will be eliminated by the next deadcode pass.
132		for _, v := range s0.Values {
133			v.Block = b
134		}
135		b.Values = append(b.Values, s0.Values...)
136		// Clear s0.
137		s0.Kind = BlockInvalid
138		s0.Values = nil
139		s0.Succs = nil
140		s0.Preds = nil
141	}
142
143	b.Kind = BlockPlain
144	b.Likely = BranchUnknown
145	b.ResetControls()
146	// The values in b may be dead codes, and clearing them in time may
147	// obtain new optimization opportunities.
148	// First put dead values that can be deleted into a slice walkValues.
149	// Then put their arguments in walkValues before resetting the dead values
150	// in walkValues, because the arguments may also become dead values.
151	walkValues := []*Value{}
152	for _, v := range b.Values {
153		if v.Uses == 0 && v.removeable() {
154			walkValues = append(walkValues, v)
155		}
156	}
157	for len(walkValues) != 0 {
158		v := walkValues[len(walkValues)-1]
159		walkValues = walkValues[:len(walkValues)-1]
160		if v.Uses == 0 && v.removeable() {
161			walkValues = append(walkValues, v.Args...)
162			v.reset(OpInvalid)
163		}
164	}
165	return true
166}
167
168// isEmpty reports whether b contains any live values.
169// There may be false positives.
170func isEmpty(b *Block) bool {
171	for _, v := range b.Values {
172		if v.Uses > 0 || v.Op.IsCall() || v.Op.HasSideEffects() || v.Type.IsVoid() || opcodeTable[v.Op].nilCheck {
173			return false
174		}
175	}
176	return true
177}
178
179// fuseBlockPlain handles a run of blocks with length >= 2,
180// whose interior has single predecessors and successors,
181// b must be BlockPlain, allowing it to be any node except the
182// last (multiple successors means not BlockPlain).
183// Cycles are handled and merged into b's successor.
184func fuseBlockPlain(b *Block) bool {
185	if b.Kind != BlockPlain {
186		return false
187	}
188
189	c := b.Succs[0].b
190	if len(c.Preds) != 1 || c == b { // At least 2 distinct blocks.
191		return false
192	}
193
194	// find earliest block in run.  Avoid simple cycles.
195	for len(b.Preds) == 1 && b.Preds[0].b != c && b.Preds[0].b.Kind == BlockPlain {
196		b = b.Preds[0].b
197	}
198
199	// find latest block in run.  Still beware of simple cycles.
200	for {
201		if c.Kind != BlockPlain {
202			break
203		} // Has exactly 1 successor
204		cNext := c.Succs[0].b
205		if cNext == b {
206			break
207		} // not a cycle
208		if len(cNext.Preds) != 1 {
209			break
210		} // no other incoming edge
211		c = cNext
212	}
213
214	// Try to preserve any statement marks on the ends of blocks; move values to C
215	var b_next *Block
216	for bx := b; bx != c; bx = b_next {
217		// For each bx with an end-of-block statement marker,
218		// try to move it to a value in the next block,
219		// or to the next block's end, if possible.
220		b_next = bx.Succs[0].b
221		if bx.Pos.IsStmt() == src.PosIsStmt {
222			l := bx.Pos.Line() // looking for another place to mark for line l
223			outOfOrder := false
224			for _, v := range b_next.Values {
225				if v.Pos.IsStmt() == src.PosNotStmt {
226					continue
227				}
228				if l == v.Pos.Line() { // Found a Value with same line, therefore done.
229					v.Pos = v.Pos.WithIsStmt()
230					l = 0
231					break
232				}
233				if l < v.Pos.Line() {
234					// The order of values in a block is not specified so OOO in a block is not interesting,
235					// but they do all come before the end of the block, so this disqualifies attaching to end of b_next.
236					outOfOrder = true
237				}
238			}
239			if l != 0 && !outOfOrder && (b_next.Pos.Line() == l || b_next.Pos.IsStmt() != src.PosIsStmt) {
240				b_next.Pos = bx.Pos.WithIsStmt()
241			}
242		}
243		// move all of bx's values to c (note containing loop excludes c)
244		for _, v := range bx.Values {
245			v.Block = c
246		}
247	}
248
249	// Compute the total number of values and find the largest value slice in the run, to maximize chance of storage reuse.
250	total := 0
251	totalBeforeMax := 0 // number of elements preceding the maximum block (i.e. its position in the result).
252	max_b := b          // block with maximum capacity
253
254	for bx := b; ; bx = bx.Succs[0].b {
255		if cap(bx.Values) > cap(max_b.Values) {
256			totalBeforeMax = total
257			max_b = bx
258		}
259		total += len(bx.Values)
260		if bx == c {
261			break
262		}
263	}
264
265	// Use c's storage if fused blocks will fit, else use the max if that will fit, else allocate new storage.
266
267	// Take care to avoid c.Values pointing to b.valstorage.
268	// See golang.org/issue/18602.
269
270	// It's important to keep the elements in the same order; maintenance of
271	// debugging information depends on the order of *Values in Blocks.
272	// This can also cause changes in the order (which may affect other
273	// optimizations and possibly compiler output) for 32-vs-64 bit compilation
274	// platforms (word size affects allocation bucket size affects slice capacity).
275
276	// figure out what slice will hold the values,
277	// preposition the destination elements if not allocating new storage
278	var t []*Value
279	if total <= len(c.valstorage) {
280		t = c.valstorage[:total]
281		max_b = c
282		totalBeforeMax = total - len(c.Values)
283		copy(t[totalBeforeMax:], c.Values)
284	} else if total <= cap(max_b.Values) { // in place, somewhere
285		t = max_b.Values[0:total]
286		copy(t[totalBeforeMax:], max_b.Values)
287	} else {
288		t = make([]*Value, total)
289		max_b = nil
290	}
291
292	// copy the values
293	copyTo := 0
294	for bx := b; ; bx = bx.Succs[0].b {
295		if bx != max_b {
296			copy(t[copyTo:], bx.Values)
297		} else if copyTo != totalBeforeMax { // trust but verify.
298			panic(fmt.Errorf("totalBeforeMax (%d) != copyTo (%d), max_b=%v, b=%v, c=%v", totalBeforeMax, copyTo, max_b, b, c))
299		}
300		if bx == c {
301			break
302		}
303		copyTo += len(bx.Values)
304	}
305	c.Values = t
306
307	// replace b->c edge with preds(b) -> c
308	c.predstorage[0] = Edge{}
309	if len(b.Preds) > len(b.predstorage) {
310		c.Preds = b.Preds
311	} else {
312		c.Preds = append(c.predstorage[:0], b.Preds...)
313	}
314	for i, e := range c.Preds {
315		p := e.b
316		p.Succs[e.i] = Edge{c, i}
317	}
318	f := b.Func
319	if f.Entry == b {
320		f.Entry = c
321	}
322
323	// trash b's fields, just in case
324	for bx := b; bx != c; bx = b_next {
325		b_next = bx.Succs[0].b
326
327		bx.Kind = BlockInvalid
328		bx.Values = nil
329		bx.Preds = nil
330		bx.Succs = nil
331	}
332	return true
333}
334