Files
chains/mpcvm/backend.go
T
zeekay 6981a3cd6c mpcvm: M-Chain to 98.6%
The lane that did this was killed before it could commit; the tree it left is
green and is preserved here rather than lost. Root package 51.5% at the start
of the day, 93.3% at its last checkpoint, 98.6% now, with cert, fhe, types and
cmd/plugin all passing.

Includes the fault-injection suite it was building when it stopped: a node that
cannot read its state does not start, and one that cannot enumerate its
registry does not either.

Not reviewed by me beyond build, vet, gofmt and the full suite under -count=1.
The lane's own report is owed and did not arrive, so its mutation count and its
verdict on the mpcvm/fhe duplication are unrecorded.
2026-08-28 13:25:30 -07:00

121 lines
3.7 KiB
Go

// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//go:build cgo
package mpcvm
// dlopen probe for the MPCVM GPU plugin. Runs once at package init() and
// pins the resolved GPUBackend into the gpuBackend singleton. Probe order
// matches the LP-186 substrate priority: native (CUDA, HIP, Metal) over
// portable (Vulkan, WebGPU), discrete over UMA.
//
// cuda → hip → metal → vulkan → webgpu
//
// Each backend has a small set of candidate dylib names. We try the
// LUXCPP_PREFIX install path first, then the lux-gpu-kernels build-tree
// path, then the bare leaf-name (relying on the dynamic linker's default
// search path: $DYLD_LIBRARY_PATH, $LD_LIBRARY_PATH, system dirs).
//
// The probe is intentionally silent on failure: a host with no GPU still
// boots; mpcvm just runs on the CPU reference (the existing
// protocol/, factory.go, executor.go state machine, unchanged by this
// bridge).
import (
"os"
"path/filepath"
)
// init runs the dlopen probe exactly once at package load. The size
// assertions run first so any wire-struct drift fails fast at process
// start instead of producing silently-wrong kernel results.
func init() {
gpuBackendOnce.Do(func() {
assertSizes()
gpuBackend = probeGPUBackend()
})
}
// probeGPUBackend walks the canonical backend priority list and returns the
// first one that dlopens AND resolves the required symbol pair. Returns
// nil when nothing matches (CPU-only mode).
func probeGPUBackend() *GPUBackend {
order := []GPUBackendKind{
GPUBackendCUDA,
GPUBackendHIP,
GPUBackendMetal,
GPUBackendVulkan,
GPUBackendWebGPU,
}
for _, kind := range order {
if b := tryLoadPlugin(kind, candidatesFor(kind)...); b != nil {
return b
}
}
return nil
}
// candidatesFor returns up to three dylib filename candidates for the given
// backend, ordered: LUXCPP_PREFIX/lib install path, lux-gpu-kernels build-tree
// path under LUX_GPU_PLUGIN_DIR, then bare leaf-name for the
// dynamic-linker default search path.
//
// On darwin the convention is libluxgpu_backend_<X>.dylib; on linux it's
// .so. WebGPU has both .dylib (when built locally on macOS) and .so
// (linux), so we include both extensions in its candidate list to maximize
// dlopen hit rate without per-platform compile-time branching.
func candidatesFor(kind GPUBackendKind) []string {
leaves := backendDylibLeaves(kind)
out := make([]string, 0, len(leaves)*3)
prefix := os.Getenv("LUXCPP_PREFIX")
if prefix == "" {
if home := os.Getenv("HOME"); home != "" {
prefix = filepath.Join(home, "work", "luxcpp", "install")
}
}
for _, leaf := range leaves {
if prefix != "" {
out = append(out, filepath.Join(prefix, "lib", leaf))
}
}
if dir := os.Getenv("LUX_GPU_PLUGIN_DIR"); dir != "" {
for _, leaf := range leaves {
out = append(out, filepath.Join(dir, leaf))
}
}
// Bare leaf names — dynamic-linker default search picks them up if
// LD_LIBRARY_PATH / DYLD_LIBRARY_PATH or /usr/local/lib has them.
out = append(out, leaves...)
return out
}
// backendDylibLeaves returns the OS-appropriate leaf filenames for one
// backend. WebGPU has two valid extensions across platforms — we include
// both so the same probe works on macOS and linux.
func backendDylibLeaves(kind GPUBackendKind) []string {
switch kind {
case GPUBackendCUDA:
return []string{"libluxgpu_backend_cuda.so"}
case GPUBackendHIP:
return []string{"libluxgpu_backend_hip.so"}
case GPUBackendMetal:
return []string{"libluxgpu_backend_metal.dylib"}
case GPUBackendVulkan:
return []string{
"libluxgpu_backend_vulkan.so",
"libluxgpu_backend_vulkan.dylib",
}
case GPUBackendWebGPU:
return []string{
"libluxgpu_backend_webgpu.dylib",
"libluxgpu_backend_webgpu.so",
}
default:
return nil
}
}