Skip to content

simd: expose portable mask bit extraction or iteration for Mask8s #80149

Description

@llxisdsh

Go version

go version go1.27rc1 windows/amd64

Output of go env in your module/workspace:

set AR=ar
set CC=gcc
set CGO_CFLAGS=-O2 -g
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-O2 -g
set CGO_ENABLED=1
set CGO_FFLAGS=-O2 -g
set CGO_LDFLAGS=-O2 -g
set CXX=g++
set GCCGO=gccgo
set GO111MODULE=on
set GOAMD64=v1
set GOARCH=amd64
set GOAUTH=netrc
set GOBIN=C:\Users\8\go\bin
set GOCACHE=C:\Users\8\AppData\Local\go-build
set GOCACHEPROG=
set GODEBUG=
set GOENV=C:\Users\8\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFIPS140=off
set GOFLAGS=
set GOGCCFLAGS=-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\8\AppData\Local\Temp\go-build1360496246=/tmp/go-build -gno-record-gcc-switches
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=E:\code\cc\go.mod
set GOMODCACHE=C:\Users\8\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOEXE=.exe
set GOEXPERIMENT=
set GOFIPS140=off
set GOFLAGS=
set GOGCCFLAGS=-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\8\AppData\Local\Temp\go-build1360496246=/tmp/go-build -gno-record-gcc-switches
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=E:\code\cc\go.mod
set GOMODCACHE=C:\Users\8\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOEXPERIMENT=
set GOFIPS140=off
set GOFLAGS=
set GOGCCFLAGS=-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\8\AppData\Local\Temp\go-build1360496246=/tmp/go-build -gno-record-gcc-switches
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=E:\code\cc\go.mod
set GOMODCACHE=C:\Users\8\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOGCCFLAGS=-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\8\AppData\Local\Temp\go-build1360496246=/tmp/go-build -gno-record-gcc-switches
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=E:\code\cc\go.mod
set GOMODCACHE=C:\Users\8\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=E:\code\cc\go.mod
set GOMODCACHE=C:\Users\8\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOMODCACHE=C:\Users\8\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPACKAGESDRIVER=
set GOTMPDIR=
set GOTOOLCHAIN=auto
set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.27rc1
set GOWORK=
set PKG_CONFIG=pkg-config

What did you do?

This is API usability feedback for the experimental Go 1.27 simd package, not a compiler crash report.

I am prototyping a vector-size-agnostic hash table using the new portable simd package.

The core probing operation is:

  1. Load a vector of tag bytes.
  2. Compare all lanes against a broadcast tag.
  3. Convert the resulting mask into lane indexes.
  4. Probe only matching lanes.

With simd/archsimd, this is direct:

words := archsimd.LoadUint8x32Array((*[32]uint8)(ptr))
bits := words.Equal(archsimd.BroadcastUint8x32(tag)).ToBits()

for bits != 0 {
    lane := bits.TrailingZeros()
    bits &= bits - 1
    // check entry at lane
}

With portable simd, I can produce a simd.Mask8s:

words := simd.LoadUint8s(tags)
mask := words.Equal(simd.BroadcastUint8s(tag))

But I could not find a portable way to extract set lane indexes from simd.Mask8s.
The only practical path seems to be mask.ToArch() plus architecture-specific type switches:

switch m := mask.ToArch().(type) {
case archsimd.Mask8x16:
    bits := uint64(m.ToBits())
case archsimd.Mask8x32:
    bits := uint64(m.ToBits())
case archsimd.Mask8x64:
    bits := m.ToBits()
}

What did you see happen?

The current portable simd.Mask8s API does not appear to provide a portable way to extract or iterate over the selected lanes.

For workloads such as hash-table probing, byte search, parsers, and scanners, the comparison mask is usually not the final result. The caller needs the lane indexes represented by the mask.

The portable simd.Mask8s API exposes operations such as And, Or, ToInt8s, and ToArch, but I could not find a direct portable way to enumerate or extract selected lanes.

The only practical workaround I found is to call mask.ToArch() and then switch on architecture-specific mask types such as archsimd.Mask8x16, archsimd.Mask8x32, and archsimd.Mask8x64, using their ToBits methods. That makes the probing loop architecture-specific again, which reduces the usefulness of the portable, vector-size-agnostic API for this class of workloads.

What did you expect to see?

I expected simd.Mask8s to provide a portable way to extract or enumerate selected lanes.

One possible API would be:

func (Mask8s) ToBits() uint64

This would be enough because current portable vectors are at least 128 bits and commonly 128/256/512 bits; for Mask8s, that is up to 64 lanes.
Alternatively, a width-independent iterator-style API would also work:

func (Mask8s) FirstSet() int
func (Mask8s) Clear(i int) Mask8s
func (Mask8s) NextSet(start int) int

The important part is not the exact API name, but that portable simd.Mask8s should let callers efficiently enumerate selected lanes without converting to simd/archsimd.

This came up while implementing a generic open-addressed hash table. The portable API can express the vector comparison, but without portable mask extraction the probing loop still has to be architecture-specific.

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions