| // 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. |
| |
| //specgen:category Bit math |
| |
| package spec |
| |
| import "math/bits" |
| |
| // OnesCount counts the number of one bits ("population count") in |
| // each element. |
| // |
| // z[i] = bits.OnesCount(x[i]) |
| func OnesCount[E Ints | Uints, W Width](x Vec[E, W]) (z Vec[E, W]) { |
| return map1[E, W, E, W](x, func(x E) E { |
| switch any(x).(type) { |
| case int8, uint8: |
| return E(bits.OnesCount8(uint8(x))) |
| case int16, uint16: |
| return E(bits.OnesCount16(uint16(x))) |
| case int32, uint32: |
| return E(bits.OnesCount32(uint32(x))) |
| case int64, uint64: |
| return E(bits.OnesCount64(uint64(x))) |
| } |
| panic("unreachable") |
| }) |
| } |
| |
| // TODO: Should LeadingZeros be defined on signed types? |
| |
| // LeadingZeros counts the leading zero bits of each element in x, |
| // starting from the most significant bit. |
| // If an element is 0, the result is {{.xN}}. |
| // |
| // z[i] = bits.LeadingZeros(x[i]) |
| func LeadingZeros[E Ints | Uints, W Width](x Vec[E, W]) (z Vec[E, W]) { |
| return map1[E, W, E, W](x, func(x E) E { |
| switch any(x).(type) { |
| case int8, uint8: |
| return E(bits.LeadingZeros8(uint8(x))) |
| case int16, uint16: |
| return E(bits.LeadingZeros16(uint16(x))) |
| case int32, uint32: |
| return E(bits.LeadingZeros32(uint32(x))) |
| case int64, uint64: |
| return E(bits.LeadingZeros64(uint64(x))) |
| } |
| panic("unreachable") |
| }) |
| } |
| |
| // TODO: Right now, nothing supports TrailingZeros. |
| |
| // TrailingZeros counts the trailing zero bits of each element in x, |
| // starting from the least significant bit. |
| // If an element is 0, the result is {{.xN}}. |
| // |
| // z[i] = bits.TrailingZeros(x[i]) |
| func TrailingZeros[E Uints, W Width](x Vec[E, W]) (z Vec[E, W]) { |
| return map1[E, W, E, W](x, func(x E) E { |
| switch x := any(x).(type) { |
| case uint8: |
| return E(bits.TrailingZeros8(x)) |
| case uint16: |
| return E(bits.TrailingZeros16(x)) |
| case uint32: |
| return E(bits.TrailingZeros32(x)) |
| case uint64: |
| return E(bits.TrailingZeros64(x)) |
| } |
| panic("unreachable") |
| }) |
| } |
| |
| // TODO: Calling this "Trailing zeros" for masks breaks the abstraction boundary |
| // around the exact mask representation. E.g., what does it mean for a wide mask |
| // or a byte mask? |
| |
| // TrailingZeros returns the number of low-order false (zero) elements in mask |
| // x. If the mask is entirely false, it returns {{lanes .x "x"}}. |
| // |
| //specgen:name TrailingZeros |
| func TrailingZerosMask[E MaskElt, W Width](x Vec[E, W]) int { |
| i := 0 |
| for ; i < len(x); i++ { |
| if x[i] != 0 { |
| break |
| } |
| } |
| return i |
| } |
| |
| // CarrylessMultiplyEven computes the elementwise carryless multiplication of |
| // even-indexed elements of x and y. The result of each carryless multiply is |
| // twice the width of the input elements. The high bits are stored in elements |
| // z[2*i+1] and the low bits are stored in elements z[2*i]. |
| // |
| // concat(z[2*i+1], z[2*i]) = clmul(x[2*i], y[2*i]) |
| // |
| // A carryless multiplication uses bitwise XOR instead of add-with-carry. |
| // For example, to compute the carryless multiply of 0b1110 and 0b1011: |
| // |
| // 1110 |
| // ⊗ 1011 |
| // ─────── |
| // 1110 |
| // 1110 |
| // 0000 |
| // ⊕ 1110 |
| // ─────── |
| // 1100010 |
| // |
| // Carryless multiply can also be viewed as multiplying polynomials with |
| // coefficients from GF(2). For example, the above example can be represented as |
| // |
| // (x^3 + x^2 + x^1) * (x^3 + x^1 + x^0) |
| // = (x^6 + x^5 + (1^1)x^4 + (1^1)x^3 + (1^1)x^2 + x^1) |
| // = (x^6 + x^5 + x^1) |
| // |
| //specgen:commutative |
| func CarrylessMultiplyEven[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { |
| return carrylessMultiplyPair(x, y, false, false) |
| } |
| |
| // CarrylessMultiplyEvenOdd computes the elementwise carryless multiplication of |
| // even-indexed elements of x and odd-indexed elements of y. The result of each |
| // carryless multiply is twice the width of the input elements. The high bits |
| // are stored in elements z[2*i+1] and the low bits are stored in elements |
| // z[2*i]. |
| // |
| // concat(z[2*i+1], z[2*i]) = clmul(x[2*i], y[2*i+1]) |
| // |
| // See [CarrylessMultiplyEven] for details about carryless multiply. |
| // |
| //specgen:commutative |
| func CarrylessMultiplyEvenOdd[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { |
| return carrylessMultiplyPair(x, y, false, true) |
| } |
| |
| // CarrylessMultiplyOdd computes the elementwise carryless multiplication of |
| // odd-indexed elements of x and y. The result of each carryless multiply is |
| // twice the width of the input elements. The high bits are stored in elements |
| // z[2*i+1] and the low bits are stored in elements z[2*i]. |
| // |
| // concat(z[2*i+1], z[2*i]) = clmul(x[2*i+1], y[2*i+1]) |
| // |
| // See [CarrylessMultiplyEven] for details about carryless multiply. |
| // |
| //specgen:commutative |
| func CarrylessMultiplyOdd[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { |
| return carrylessMultiplyPair(x, y, true, true) |
| } |
| |
| // CarrylessMultiplyOddEven computes the elementwise carryless multiplication of |
| // odd-indexed elements of x and even-indexed elements of y. The result of each |
| // carryless multiply is twice the width of the input elements. The high bits |
| // are stored in elements z[2*i+1] and the low bits are stored in elements |
| // z[2*i]. |
| // |
| // concat(z[2*i+1], z[2*i]) = clmul(x[2*i+1], y[2*i]) |
| // |
| // See [CarrylessMultiplyEven] for details about carryless multiply. |
| // |
| //specgen:commutative |
| func CarrylessMultiplyOddEven[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { |
| return carrylessMultiplyPair(x, y, true, false) |
| } |
| |
| func carrylessMultiplyPair[E Uints, W Width](x, y Vec[E, W], xOdd, yOdd bool) (z Vec[E, W]) { |
| z = makeVec[E, W]() |
| pairs := z.len() / 2 |
| for i := 0; i < pairs; i++ { |
| xi := x[2*i] |
| if xOdd { |
| xi = x[2*i+1] |
| } |
| yi := y[2*i] |
| if yOdd { |
| yi = y[2*i+1] |
| } |
| z[2*i+1], z[2*i] = clmul(xi, yi) |
| } |
| return z |
| } |