Skip to content

simd: bridge.LoadFloat32sPart cannot be used in variable initialization #80689

Open

Description

@rschio

Go version

go version go1.27rc2 darwin/arm64

Output of go env in your module/workspace:

AR='ar'
CC='clang'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='clang++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN='/Users/rschio/go/bin'
GOCACHE='/Users/rschio/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/rschio/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT='simd'
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/rq/0fx96ptn7w5357fdm_qzflhh0000gn/T/go-build2766455738=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/tmp/report_bug/go.mod'
GOMODCACHE='/Users/rschio/go/pkg/mod'
GONOPROXY='github.com/rschio/*,buf.build/gen/go'
GONOSUMDB='github.com/rschio/*,buf.build/gen/go'
GOOS='darwin'
GOPACKAGESDRIVER=''
GOPATH='/Users/rschio/go'
GOPRIVATE='github.com/rschio/*,buf.build/gen/go'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/Users/rschio/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.27rc2.darwin-arm64'
GOSUMDB='sum.golang.org'
GOTELEMETRY='on'
GOTELEMETRYDIR='/Users/rschio/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='go1.27rc2'
GOTOOLDIR='/Users/rschio/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.27rc2.darwin-arm64/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.27rc2'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

I run this program:

package main

import (
	"math"
	"simd"
)

func main() {
	XYToRGB_SIMD(0, 1, 0)
}

// XY represents a XY color in CIE color space.
// This type is adapted to work Philips Hue bulbs color gamut.
type XY struct {
	X, Y float32
	Bri  uint8
}

var (
	coeR, _ = simd.LoadFloat32sPart([]float32{1.6564920, -0.354851, -0.255038})
	coeG, _ = simd.LoadFloat32sPart([]float32{-0.707196, 1.6553970, 0.0361520})
	coeB, _ = simd.LoadFloat32sPart([]float32{0.0517130, -0.121364, 1.0115300})
)

func XYToRGB_SIMD(x, y float32, bri uint8) (uint8, uint8, uint8) {
	if y == 0 {
		return 0, 0, 0
	}

	z := 1 - x - y
	Y := float32(bri) / 254
	X := (Y / y) * x
	Z := (Y / y) * z

	xyz, _ := simd.LoadFloat32sPart([]float32{X, Y, Z})

	var (
		rs [3]float32
		gs [3]float32
		bs [3]float32
	)
	coeR.Mul(xyz).StorePart(rs[:])
	coeG.Mul(xyz).StorePart(gs[:])
	coeB.Mul(xyz).StorePart(bs[:])

	r := sum(rs[:])
	g := sum(gs[:])
	b := sum(bs[:])

	r = xyToRGBGamma(r)
	g = xyToRGBGamma(g)
	b = xyToRGBGamma(b)

	return to8(r), to8(g), to8(b)
}

func sum(fs []float32) float32 {
	var total float32
	for _, f := range fs {
		total += f
	}
	return total
}

func xyToRGBGamma(c float32) float32 {
	if c <= 0.0031308 {
		return 12.92 * c
	}
	return float32(1.055*math.Pow(float64(c), 1/2.4) - 0.055)
}

func to8(v float32) uint8 {
	v = max(v, 0)
	v = min(v, 1)
	return uint8(v*0xff + 0.5)
}

What did you see happen?

I got this output:

report_bug

/Users/rschio/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.27rc2.darwin-arm64/src/simd/internal/bridge/simd_emulated.go:2467:6: internal compiler error: bridge.LoadFloat32sPart (type func([]float32) (bridge.Float32s, int)) and func([]float32) (simd.Float32s, int) are not shape types

Please file a bug report including a short program that triggers the error.
https://go.dev/issue/new

What did you expect to see?

I expected to see no bug.

Activity

  1. rschio commented on Aug 2, 2026

    @rschio
    Author

    I ran in a M3 Pro.

  2. rschio commented on Aug 3, 2026

    @rschio
    Author

    Simplified:

    package main
    
    import (
    	"simd"
    )
    
    var v, _ = simd.LoadFloat32sPart([]float32{0, 0, 0})
    
    func main() {
    	_ = v
    }
  3. magical commented on Aug 4, 2026

    @magical
    Contributor

    I'm guessing this is a known limitation since the simd package currently has this note:

    Bugs:
    SIMD-dependent var initializers don't work.


    Edit:
    As a workaround, this appears to work:

    var v simd.Float32s
    
    func init() {
    	v, _ = simd.LoadFloat32sPart([]float32{0, 0, 0})
    }
    
  4. added
    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.
    on Aug 17, 2026
  5. cagedmantis commented on Aug 17, 2026

    @cagedmantis
    Contributor
  6. philpearl commented on Aug 28, 2026

    @philpearl
    Contributor

    I hit the same issue with a simd.Uint8s global variable initialised from simd.BroadcastUint8s(0x80). Moving the initialisation to an init() function also solved my problem

  7. changed the title [-]simd: bridge.LoadFloat32sPart ... are not shape types[/-] [+]simd: bridge.LoadFloat32sPart cannot be used in variable initialization[/+] on Sep 15, 2026
  8. added this to the Go1.28 milestone on Sep 15, 2026
  9. spa5k commented on Sep 20, 2026

    @spa5k
    Contributor

    Root cause and candidate fix for the ICE

    Scope

    I confirmed the failure on go1.27.0 darwin/arm64 and on gotip f043823a. Cross-builds to GOOS=linux GOARCH=amd64 and GOOS=js GOARCH=wasm fail in the same way. The emulation copy alone triggers the failure. No vector hardware is needed. -gcflags=-l does not change the result. BroadcastUint32s and LoadUint8s trigger the failure too. The package-scope initializer is the common factor. One bridge function is not the cause.

    If the program never reads the variable, the compiler reports a different error. The error is undefined: bridge at the initializer. The compiler does not report the ICE:

    package main
    
    import "simd"
    
    var b = simd.BroadcastUint8s(0x20) // never used
    
    func main() {}

    Root cause: the emulation copy shares one syntax node with the original

    The midway deep copier builds the emulation copy (VecLen == 0) with the original name node. The OnNameExpr and OnSelector branches put the original node into the new selector:

    // go1.27.1 src/cmd/compile/internal/midway/deepcopy.go:114 and :178
    newSel := &syntax.SelectorExpr{
    	X:   archsimdId,
    	Sel: se.Sel, // name is unchanged for emulation
    }

    The non-emulation branches build a fresh node with syntax.NewName. These two branches do not.

    The failure chain:

    1. The deep copier updates types2.Info in place. It executes c.info.Uses[newName] = oldObj.
    2. Type checking runs again after the rewrite. See goto recheck in noder/irgen.go.
    3. The re-check resolves the emulation copy bridge.BroadcastUint8s. It writes the bridge object into info.Uses[sharedSel].
    4. The original declaration uses the same node. The original simd.BroadcastUint8s now resolves to the bridge object. Its X operand still says simd.
    5. The cached TypeAndValue on that node still holds the simd signature. The writer takes the object from info.Uses. The writer takes the type from the cache. The two disagree.

    Writer trace from an instrumented compiler (-d=simd=2):

    main.go:9:13: WRITE-RESHAPE expr=simd.BroadcastUint8s   type=func(uint8) simd.Uint8s
    main.go:9:13: WRITE-RESHAPE expr=bridge.BroadcastUint8s type=func(x uint8) simd/internal/bridge.Uint8s
    

    The first line prints simd text. The syntax supplies the text. But the write uses the bridge object. info.Uses supplies the object. The reader then builds x with the bridge type. The reader takes the reshape target from the simd cache. Neither type carries the shape bit. An instrumented compiler confirms this. HasShape() returns false on both types and on both result types. The exprReshape assert in reader.expr fails.

    Only var initializers fail. The rewriter replaces function bodies with dispatchers. No corrupted original survives. The rewriter keeps var decls verbatim. See rewrite.go:240-243.

    Candidate fix

    Build a fresh name in both emulation branches:

    diff --git a/src/cmd/compile/internal/midway/deepcopy.go b/src/cmd/compile/internal/midway/deepcopy.go
    @@ -113,7 +113,7 @@ func (c *DeepCopier) OnNameExpr(id *syntax.Name) syntax.Expr {
     				// special case for emulation
     				newSel := &syntax.SelectorExpr{
     					X:   archsimdId,
    -					Sel: id, // name is unchanged for emulation
    +					Sel: syntax.NewName(id.Pos(), id.Value),
     				}
     				newSel.SetPos(id.Pos())
     				return newSel
    @@ -177,7 +177,7 @@ func (c *DeepCopier) OnSelector(se *syntax.SelectorExpr) syntax.Expr {
     					// emulated instead, name is unchanged
     					newSel := &syntax.SelectorExpr{
     						X:   archsimdId,
    -						Sel: se.Sel,
    +						Sel: syntax.NewName(se.Sel.Pos(), se.Sel.Value),
     					}
     					newSel.SetPos(se.Pos())
     					return newSel

    I verified this fix on gotip f043823a darwin/arm64. The test compiler contained only this change.

    Results:

    • The ICE is gone. The build now fails at link time. The error is main.init: relocation target simd.BroadcastUint8s not defined.
    • The undefined: bridge variant does not change.
    • Regular simd builds still work. Function-body use and init() assignment both build and run.

    The fix converts the ICE into the documented BUG(global initialization) failure. A full fix needs one more decision. See the TODO at rewrite.go:242: drop the kept declaration, or neutralize its initializer. I leave this decision to you. I can send a CL for the deepcopy.go part on request.

  10. gopherbot commented on Sep 29, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/841367 mentions this issue: cmd/compile: fix midway ICE on shared emulation selector node

  11. dr2chase commented on Oct 1, 2026

    @dr2chase
    Contributor

    Candidate CL for fixing this, straight LLM output so I still need to review it myself but it handles #81845 (@mauri870) and it did not crash compiling the test for this bug either. https://go.dev/cl/843307

    I'd love to hear if it works more generally, or doesn't.
    Fixes for the other simd-ast-rewriting bugs recently landed, as well as a few more mask-related operations (not iterators, those are still being discussed).

    There are some performance glitches that may take a little longer to land, we want to be sure that we have the simd API in good shape and SVE in good shape, and maybe SVE-support enabled in simd.

  12. spa5k commented on Oct 1, 2026

    @spa5k
    Contributor

    I tested CL 843307 with three cases.

    1. Package-scope var init (simd: bridge.LoadFloat32sPart cannot be used in variable initialization #80689): builds, links, and runs. Pass.
    2. The cmd/link: GOEXPERIMENT=simd calls left unresolved when inlined into a package initializer #81845 case: builds, links, and runs. Output is correct: 200 32. Pass.
    3. The simd: exporter compiles, but every importer fails with "could not import simd/internal/bridge" when a package API uses a simd type #81614 case: still fails. Same error: could not import simd/internal/bridge.

    So the CL fixes the init cases. It does not fix #81614. That is a separate bug.

    Built patchset 1 on master 6bbd163, GOEXPERIMENT=simd, darwin/arm64.

    EDIT: More testing, still patchset 1:

    • Both init cases also build for linux/amd64 and js/wasm.
    • The simd test suite fails the same way with and without the CL. The CL adds no new failures.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.compiler/runtimeIssues related to the Go compiler and/or runtime.

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions