| // errorcheck -0 -d=ssa/cpufeatures/debug=1,ssa/rewrite_tern/debug=1 | |
| //go:build goexperiment.simd && amd64 | |
| // Copyright 2025 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. | |
| package foo | |
| import ( | |
| "fmt" | |
| "simd/archsimd" | |
| ) | |
| func f1(x archsimd.Int8x16) { | |
| return // ERROR "has features avx" | |
| } | |
| func g1() archsimd.Int8x16 { | |
| var x archsimd.Int8x16 | |
| return x // ERROR "has features avx$" | |
| } | |
| type T1 archsimd.Int8x16 | |
| func (x T1) h() { | |
| return // ERROR "has features avx$" | |
| } | |
| func f2(x archsimd.Int8x64) { | |
| return // ERROR "has features avx[+]avx2[+]avx512$" | |
| } | |
| func g2() archsimd.Int8x64 { | |
| var x archsimd.Int8x64 | |
| return x // ERROR "has features avx[+]avx2[+]avx512$" | |
| } | |
| type T2 archsimd.Int8x64 | |
| func (x T2) h() { | |
| return // ERROR "has features avx[+]avx2[+]avx512$" | |
| } | |
| var a int | |
| func f() { | |
| if a == 0 { | |
| if !archsimd.X86.AVX512() { | |
| return | |
| } | |
| println("has avx512") // ERROR "has features avx[+]avx2[+]avx512$" | |
| } else { | |
| if !archsimd.X86.AVX2() { | |
| return | |
| } | |
| println("has avx2") // ERROR "has features avx[+]avx2$" | |
| } | |
| println("has something") | |
| } // ERROR "has features avx[+]avx2$" | |
| func g() { | |
| if archsimd.X86.AVX2() { // ERROR "has features avx[+]avx2$" | |
| for range 5 { // ERROR "has features avx[+]avx2$" | |
| if a < 0 { // ERROR "has features avx[+]avx2$" | |
| a++ // ERROR "has features avx[+]avx2$" | |
| } | |
| } | |
| } | |
| println("ahoy!") // ERROR "has features avx[+]avx2$" // this is an artifact of flaky block numbering and why isn't it fused? | |
| if a > 0 { | |
| a-- | |
| } | |
| } | |
| //go:noinline | |
| func p() bool { | |
| return true | |
| } | |
| func hasIrreducibleLoop() { | |
| if archsimd.X86.AVX2() { | |
| goto a // ERROR "has features avx[+]avx2$" | |
| } else { | |
| goto b | |
| } | |
| a: | |
| println("a") | |
| if p() { | |
| goto c | |
| } | |
| b: | |
| println("b") | |
| if p() { | |
| goto a | |
| } | |
| c: | |
| println("c") | |
| } | |
| func ternRewrite(m, w, x, y, z archsimd.Int32x16) (t0, t1, t2 archsimd.Int32x16) { | |
| if !archsimd.X86.AVX512() { // ERROR "has features avx[+]avx2[+]avx512$" | |
| return // ERROR "has features avx[+]avx2[+]avx512$" // all blocks have it because of the vector size | |
| } | |
| t0 = w.Xor(y).Xor(z) // ERROR "Rewriting.*ternInt" | |
| t1 = m.And(w.Xor(y).Xor(z.Not())) // ERROR "Rewriting.*ternInt" | |
| t2 = x.Xor(y).Xor(z).And(x.Xor(y).Xor(z.Not())) // ERROR "Rewriting.*ternInt" | |
| return // ERROR "has features avx[+]avx2[+]avx512$" | |
| } | |
| func ternTricky1(x, y, z archsimd.Int32x8) archsimd.Int32x8 { | |
| // Int32x8 is a 256-bit vector and does not guarantee AVX-512 | |
| // a is a 3-variable logical expression occurring outside AVX-512 feature check | |
| a := x.Xor(y).Xor(z) | |
| var w archsimd.Int32x8 | |
| if !archsimd.X86.AVX512() { // ERROR "has features avx$" | |
| // do nothing | |
| } else { | |
| w = y.AndNot(a) // ERROR "has features avx[+]avx2[+]avx512" "Rewriting.*ternInt" | |
| } | |
| // a is a common subexpression | |
| return a.Or(w) // ERROR "has features avx$" | |
| } | |
| func ternTricky2(x, y, z archsimd.Int32x8) archsimd.Int32x8 { | |
| // Int32x8 is a 256-bit vector and does not guarantee AVX-512 | |
| var a, w archsimd.Int32x8 | |
| if !archsimd.X86.AVX512() { // ERROR "has features avx$" | |
| // do nothing | |
| } else { | |
| a = x.Xor(y).Xor(z) | |
| w = y.AndNot(a) // ERROR "has features avx[+]avx2[+]avx512" "Rewriting.*ternInt" | |
| } | |
| // a is a common subexpression | |
| return a.Or(w) // ERROR "has features avx$" | |
| } | |
| func ternTricky3(x, y, z archsimd.Int32x8) archsimd.Int32x8 { | |
| // Int32x8 is a 256-bit vector and does not guarantee AVX-512 | |
| a := x.Xor(y).Xor(z) | |
| w := y.AndNot(a) | |
| if !archsimd.X86.AVX512() { // ERROR "has features avx$" | |
| return a // ERROR "has features avx$" | |
| } | |
| // a is a common subexpression | |
| return a.Or(w) // ERROR "has features avx[+]avx2[+]avx512" // This does not rewrite, do we want it to? | |
| } | |
| func vpternlogdPanic() { | |
| resultsMask := archsimd.Mask64x8{} | |
| // The loop header holds only rematerializable SIMD zeros, which emit | |
| // no code, so it claims no features. | |
| for { | |
| resultsMask = archsimd.Mask64x8FromBits(0).Or( // ERROR "has features avx[+]avx2[+]avx512" | |
| archsimd.Float64x8{}.Less( | |
| archsimd.BroadcastFloat64x8(0))).Or(resultsMask) // ERROR "Rewriting.*ternInt" "Skipping rewrite" | |
| fmt.Print(resultsMask.And(resultsMask.And(archsimd.Mask64x8{}))) | |
| } | |
| } | |
| type notSIMD struct { | |
| v archsimd.Int8x16 | |
| n int | |
| } | |
| var cond bool | |
| // A SIMD-typed zero emits no code (it is just a reference to the fixed | |
| // all-zeros register), so it must imply no CPU features: this function | |
| // must stay compilable for machines without AVX and produce no feature | |
| // diagnostics at all. | |
| func zeroOnly(p *notSIMD) { | |
| p.v = archsimd.Int8x16{} | |
| p.n++ | |
| } | |
| // Only the block executing a real SIMD instruction claims features; | |
| // they do not leak into the unconditionally executed parts of the | |
| // function through the no-code zero or the merge. | |
| func zeroMerge(p *notSIMD, s []int8) { | |
| x := archsimd.Int8x16{} | |
| if cond { | |
| x = archsimd.LoadInt8x16(s) // ERROR "has features avx$" | |
| } | |
| p.v = x | |
| p.n++ | |
| } |