Source file src/simd/internal/spec/bitmath.go
1 // Copyright 2026 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 5 //specgen:category Bit math 6 7 package spec 8 9 import "math/bits" 10 11 // OnesCount counts the number of one bits ("population count") in 12 // each element. 13 // 14 // z[i] = bits.OnesCount(x[i]) 15 func OnesCount[E Ints | Uints, W Width](x Vec[E, W]) (z Vec[E, W]) { 16 return map1[E, W, E, W](x, func(x E) E { 17 switch any(x).(type) { 18 case int8, uint8: 19 return E(bits.OnesCount8(uint8(x))) 20 case int16, uint16: 21 return E(bits.OnesCount16(uint16(x))) 22 case int32, uint32: 23 return E(bits.OnesCount32(uint32(x))) 24 case int64, uint64: 25 return E(bits.OnesCount64(uint64(x))) 26 } 27 panic("unreachable") 28 }) 29 } 30 31 // TODO: Should LeadingZeros be defined on signed types? 32 33 // LeadingZeros counts the leading zero bits of each element in x, 34 // starting from the most significant bit. 35 // If an element is 0, the result is {{.xN}}. 36 // 37 // z[i] = bits.LeadingZeros(x[i]) 38 func LeadingZeros[E Ints | Uints, W Width](x Vec[E, W]) (z Vec[E, W]) { 39 return map1[E, W, E, W](x, func(x E) E { 40 switch any(x).(type) { 41 case int8, uint8: 42 return E(bits.LeadingZeros8(uint8(x))) 43 case int16, uint16: 44 return E(bits.LeadingZeros16(uint16(x))) 45 case int32, uint32: 46 return E(bits.LeadingZeros32(uint32(x))) 47 case int64, uint64: 48 return E(bits.LeadingZeros64(uint64(x))) 49 } 50 panic("unreachable") 51 }) 52 } 53 54 // TODO: Right now, nothing supports TrailingZeros. 55 56 // TrailingZeros counts the trailing zero bits of each element in x, 57 // starting from the least significant bit. 58 // If an element is 0, the result is {{.xN}}. 59 // 60 // z[i] = bits.TrailingZeros(x[i]) 61 func TrailingZeros[E Uints, W Width](x Vec[E, W]) (z Vec[E, W]) { 62 return map1[E, W, E, W](x, func(x E) E { 63 switch x := any(x).(type) { 64 case uint8: 65 return E(bits.TrailingZeros8(x)) 66 case uint16: 67 return E(bits.TrailingZeros16(x)) 68 case uint32: 69 return E(bits.TrailingZeros32(x)) 70 case uint64: 71 return E(bits.TrailingZeros64(x)) 72 } 73 panic("unreachable") 74 }) 75 } 76 77 // TODO: Calling this "Trailing zeros" for masks breaks the abstraction boundary 78 // around the exact mask representation. E.g., what does it mean for a wide mask 79 // or a byte mask? 80 81 // TrailingZeros returns the number of low-order false (zero) elements in mask 82 // x. If the mask is entirely false, it returns {{lanes .x "x"}}. 83 // 84 //specgen:name TrailingZeros 85 func TrailingZerosMask[E MaskElt, W Width](x Vec[E, W]) int { 86 i := 0 87 for ; i < len(x); i++ { 88 if x[i] != 0 { 89 break 90 } 91 } 92 return i 93 } 94 95 // CarrylessMultiplyEven computes the elementwise carryless multiplication of 96 // even-indexed elements of x and y. The result of each carryless multiply is 97 // twice the width of the input elements. The high bits are stored in elements 98 // z[2*i+1] and the low bits are stored in elements z[2*i]. 99 // 100 // concat(z[2*i+1], z[2*i]) = clmul(x[2*i], y[2*i]) 101 // 102 // A carryless multiplication uses bitwise XOR instead of add-with-carry. 103 // For example, to compute the carryless multiply of 0b1110 and 0b1011: 104 // 105 // 1110 106 // ⊗ 1011 107 // ─────── 108 // 1110 109 // 1110 110 // 0000 111 // ⊕ 1110 112 // ─────── 113 // 1100010 114 // 115 // Carryless multiply can also be viewed as multiplying polynomials with 116 // coefficients from GF(2). For example, the above example can be represented as 117 // 118 // (x^3 + x^2 + x^1) * (x^3 + x^1 + x^0) 119 // = (x^6 + x^5 + (1^1)x^4 + (1^1)x^3 + (1^1)x^2 + x^1) 120 // = (x^6 + x^5 + x^1) 121 // 122 //specgen:commutative 123 func CarrylessMultiplyEven[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { 124 return carrylessMultiplyPair(x, y, false, false) 125 } 126 127 // CarrylessMultiplyEvenOdd computes the elementwise carryless multiplication of 128 // even-indexed elements of x and odd-indexed elements of y. The result of each 129 // carryless multiply is twice the width of the input elements. The high bits 130 // are stored in elements z[2*i+1] and the low bits are stored in elements 131 // z[2*i]. 132 // 133 // concat(z[2*i+1], z[2*i]) = clmul(x[2*i], y[2*i+1]) 134 // 135 // See [CarrylessMultiplyEven] for details about carryless multiply. 136 // 137 //specgen:commutative 138 func CarrylessMultiplyEvenOdd[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { 139 return carrylessMultiplyPair(x, y, false, true) 140 } 141 142 // CarrylessMultiplyOdd computes the elementwise carryless multiplication of 143 // odd-indexed elements of x and y. The result of each carryless multiply is 144 // twice the width of the input elements. The high bits are stored in elements 145 // z[2*i+1] and the low bits are stored in elements z[2*i]. 146 // 147 // concat(z[2*i+1], z[2*i]) = clmul(x[2*i+1], y[2*i+1]) 148 // 149 // See [CarrylessMultiplyEven] for details about carryless multiply. 150 // 151 //specgen:commutative 152 func CarrylessMultiplyOdd[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { 153 return carrylessMultiplyPair(x, y, true, true) 154 } 155 156 // CarrylessMultiplyOddEven computes the elementwise carryless multiplication of 157 // odd-indexed elements of x and even-indexed elements of y. The result of each 158 // carryless multiply is twice the width of the input elements. The high bits 159 // are stored in elements z[2*i+1] and the low bits are stored in elements 160 // z[2*i]. 161 // 162 // concat(z[2*i+1], z[2*i]) = clmul(x[2*i+1], y[2*i]) 163 // 164 // See [CarrylessMultiplyEven] for details about carryless multiply. 165 // 166 //specgen:commutative 167 func CarrylessMultiplyOddEven[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { 168 return carrylessMultiplyPair(x, y, true, false) 169 } 170 171 func carrylessMultiplyPair[E Uints, W Width](x, y Vec[E, W], xOdd, yOdd bool) (z Vec[E, W]) { 172 z = makeVec[E, W]() 173 pairs := z.len() / 2 174 for i := 0; i < pairs; i++ { 175 xi := x[2*i] 176 if xOdd { 177 xi = x[2*i+1] 178 } 179 yi := y[2*i] 180 if yOdd { 181 yi = y[2*i+1] 182 } 183 z[2*i+1], z[2*i] = clmul(xi, yi) 184 } 185 return z 186 } 187