Files
evmgpu/core/parallel/gpu_evm.go
zooqueen 28942737fc fix(build): re-pin [email protected], restore core/vm/runtime, gate GPU-opcode cgo
Four orthogonal fixes so 'GOWORK=off CGO_ENABLED=1 go build ./...' compiles
the real slot-level Block-STM (core/parallel + state_processor) again:

1. upgrade: re-pin v1.0.1-0.20260603055252-f51810805436 -> v1.0.1. The pinned
   pseudo-version commit was force-pushed off origin (orphaned); the published
   v1.0.1 tag resolves to the same tree (bd889e9). params, which imports
   upgrade, now builds.

2. gpu: re-pin v1.0.2 -> v1.0.1 (matches sibling luxfi/evm). gpu v1.0.2's
   e2e_cgo.go calls lux_gpu_{mldsa,slhdsa}_verify_batch with a stale arg list
   that no longer matches the C header (cgo 'not enough arguments'); v1.0.1
   compiles clean against the installed header.

3. core/vm/runtime: restore the package. An UNANCHORED  entry
    silently excluded core/vm/runtime/, so cmd/evm/runner.go imported
   a package that was never committed. Anchored the ignore to /runtime (root
   build artifact) and added the package (ported from luxfi/evm, rewired to
   evmgpu's forked core/state.StateDB).

4. gpu_evm.go: add the missing  build tag (cgo && darwin && arm64 ->
   gpu && cgo && darwin && arm64), aligning the GPU-opcode dispatcher with its
   own test (gpu_evm_test.go) and doc comment. Without it the lux-cevm-linked
   cgo file was compiled on every darwin build, forcing an absent native dep
   on the default build. GPU-opcode dispatch is now opt-in behind -tags gpu;
   the pure-Go STM core and the luxfi/gpu ecrecover path stay default.

Verified: ./core/... ./params/... ./cmd/evm build (CGO=1, install pkg-config).
plugin/cmd/abigen link only against absent native archives (accel embeds a
broken relative -L; pqclean/lattice not install-complete) — pre-existing and
identical in luxfi/evm, not introduced here.
2026-06-21 22:39:06 -07:00

171 lines
4.4 KiB
Go

// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//go:build gpu && cgo && darwin && arm64
package parallel
// Linkage goes through the lux-cevm pkg-config bundle (libevm + libevm-gpu).
// The C header "go_bridge.h" ships under
// $LUXCPP_PREFIX/include/cevm/lib/evm/gpu/, which is in the .pc Cflags.
//
// Build + install once with:
//
// cmake -S ~/work/luxcpp/cevm -B ~/work/luxcpp/cevm/build \
// -DCMAKE_INSTALL_PREFIX=$HOME/work/luxcpp/install
// cmake --build ~/work/luxcpp/cevm/build
// cmake --install ~/work/luxcpp/cevm/build
//
// Then `export PKG_CONFIG_PATH=$HOME/work/luxcpp/install/lib/pkgconfig`.
/*
#cgo pkg-config: lux-cevm
#cgo darwin LDFLAGS: -framework Metal -framework Foundation -lstdc++
#include <stdlib.h>
#include "go_bridge.h"
*/
import "C"
import (
"fmt"
"unsafe"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/log"
)
// GPUEVMDispatcher dispatches GPU-eligible transactions to the C++
// Metal/CUDA EVM kernel via CGo. Transactions that contain CALL, CREATE,
// or DELEGATECALL opcodes are not eligible and fall back to Go EVM.
type GPUEVMDispatcher struct {
backend C.uint8_t
}
// NewGPUEVMDispatcher creates a dispatcher that routes eligible transactions
// to the C++ GPU kernel. Auto-detects the best available backend.
func NewGPUEVMDispatcher() *GPUEVMDispatcher {
backend := C.gpu_auto_detect_backend()
log.Info("GPU EVM dispatcher initialized",
"backend", gpuBackendName(uint8(backend)),
)
return &GPUEVMDispatcher{
backend: backend,
}
}
// Available returns true if a GPU backend was detected.
func (d *GPUEVMDispatcher) Available() bool {
return uint8(d.backend) >= 2 // Metal=2, CUDA=3
}
// Backend returns the name of the active backend.
func (d *GPUEVMDispatcher) Backend() string {
return gpuBackendName(uint8(d.backend))
}
// ExecuteBlock dispatches a batch of GPU-eligible transactions to the C++
// Metal/CUDA kernel. Returns per-transaction gas used.
//
// Caller must ensure all txs passed here are GPU-eligible (IsGPUEligible).
// The signer is needed to recover sender addresses.
func (d *GPUEVMDispatcher) ExecuteBlock(
signer types.Signer,
txs []*types.Transaction,
senders []common.Address,
) ([]GPUEVMResult, error) {
n := len(txs)
if n == 0 {
return nil, nil
}
// Pack transactions into C structs.
cTxs := make([]C.CGpuTx, n)
for i, tx := range txs {
// From (sender)
copy(cTxs[i].from[:], senders[i][:])
// To
if tx.To() != nil {
copy(cTxs[i].to[:], tx.To()[:])
cTxs[i].has_to = 1
}
// Data -- for GPU-eligible txs this is empty, but handle it.
if len(tx.Data()) > 0 {
cTxs[i].data = (*C.uint8_t)(C.CBytes(tx.Data()))
cTxs[i].data_len = C.uint32_t(len(tx.Data()))
}
cTxs[i].gas_limit = C.uint64_t(tx.Gas())
cTxs[i].value = C.uint64_t(tx.Value().Uint64())
cTxs[i].nonce = C.uint64_t(tx.Nonce())
if tx.GasPrice() != nil {
cTxs[i].gas_price = C.uint64_t(tx.GasPrice().Uint64())
}
}
// Call C++ via CGo.
result := C.gpu_execute_block(
(*C.CGpuTx)(unsafe.Pointer(&cTxs[0])),
C.uint32_t(n),
d.backend,
)
// Free any calldata we allocated.
for i := range cTxs {
if cTxs[i].data != nil {
C.free(unsafe.Pointer(cTxs[i].data))
}
}
if result.ok == 0 {
C.gpu_free_result(&result)
return nil, fmt.Errorf("gpu evm execute_block failed")
}
// Unpack results.
gasUsedSlice := unsafe.Slice((*C.uint64_t)(result.gas_used), n)
results := make([]GPUEVMResult, n)
for i := 0; i < n; i++ {
results[i] = GPUEVMResult{
GasUsed: uint64(gasUsedSlice[i]),
Success: true,
}
}
C.gpu_free_result(&result)
return results, nil
}
func gpuBackendName(b uint8) string {
switch b {
case 0:
return "CPU-Sequential"
case 1:
return "CPU-Parallel"
case 2:
return "Metal"
case 3:
return "CUDA"
default:
return "Unknown"
}
}
// WithGPUOpcodes returns an EngineOption that enables GPU EVM opcode dispatch
// for eligible transactions. Requires the gpu build tag and luxcpp/evm.
//
// Trie-node hashing stays on the CPU hasher: GPU keccak dispatch belongs in
// luxcpp (Metal/CUDA kernels) and is not implemented in Go. Once
// luxcpp/gpu exposes a batch keccak entry point, the dispatcher can be
// wired through the existing luxgpu cgo bridge in lux/gpu.
func WithGPUOpcodes() EngineOption {
return func(e *Engine) {
e.gpuEVM = NewGPUEVMDispatcher()
e.UseGPU = true
// e.hasher left as the default CPU hasher.
}
}