| // errorcheck -0 -d=ssa/critical/debug=1 | |
| //go:build goexperiment.simd && amd64 | |
| // Copyright 2026 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Test that blocks created by the critical pass to split critical | |
| // edges inherit the CPU features of the edge they sit on, so that | |
| // later consumers (e.g. regalloc-inserted shuffle copies) can use | |
| // feature-dependent instruction encodings there. There are three | |
| // paths through the pass, each exercised below: | |
| // | |
| // 1. a fresh split block for an edge into a block with a single phi | |
| // 2. a fresh split block for an edge into a block with several phis | |
| // (or none) | |
| // 3. a split block reused for several predecessor edges carrying the | |
| // same phi argument, which keeps only the features all of its | |
| // predecessors guarantee | |
| package foo | |
| import "simd/archsimd" | |
| var cond bool | |
| // Case 1: the merge block has a single phi (x), so the split block for | |
| // the critical edge is created on the single-phi path. It inherits | |
| // avx from its predecessor and successor. | |
| func singlePhi(a, b archsimd.Int64x4) archsimd.Int64x4 { | |
| x := a.Add(b) | |
| if cond { // ERROR "split critical edge" "split-edge block b[0-9]+ has features avx$" | |
| x = x.Add(a) | |
| } | |
| return x | |
| } | |
| // Case 2: the merge block has two phis (x and y), so the split block | |
| // for the critical edge is created on the no-single-phi path. | |
| func multiPhi(a, b archsimd.Int64x4) (archsimd.Int64x4, archsimd.Int64x4) { | |
| x := a.Add(b) | |
| y := b.Sub(a) | |
| if cond { // ERROR "split critical edge" "split-edge block b[0-9]+ has features avx$" | |
| x = x.Add(a) | |
| y = y.Sub(b) | |
| } | |
| return x, y | |
| } | |
| // Case 3: the short-circuit && evaluates each operand in its own | |
| // block, and both false edges jump to the single-phi merge block with | |
| // the same phi argument (the zero value of x). The split block is | |
| // created for the edge from the second operand's block, whose features | |
| // are avx+avx2+avx512 (it is dominated by the first operand's block | |
| // and holds the 512-bit ops); when it is reused for the edge from the | |
| // first operand's block, which only guarantees avx, its features must | |
| // drop to the common subset rather than keep avx512. | |
| func reuseIntersect(s []int64, s8 []int64) { | |
| var x archsimd.Int64x4 | |
| if archsimd.LoadInt64x4(s).IsZero() && // ERROR "split critical edge" "reused split-edge block b[0-9]+ has features avx$" "split-edge block b[0-9]+ has features avx$" | |
| archsimd.LoadInt64x8(s8).Equal(archsimd.LoadInt64x8(s8)).ToBits() != 0 { // ERROR "split critical edge" "split-edge block b[0-9]+ has features avx[+]avx2[+]avx512$" | |
| x = archsimd.LoadInt64x4(s) | |
| } | |
| x.Store(s) | |
| } |