summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter
diff options
context:
space:
mode:
authorLibravatar kim <grufwub@gmail.com>2025-11-10 07:29:48 +0100
committerLibravatar tobi <tobi.smethurst@protonmail.com>2025-11-17 14:14:33 +0100
commit6a3b09a507aca0498845d9118a21a82bb5054301 (patch)
tree5297960ecfe66f723179eb5a1a6f8d59504c3433 /vendor/github.com/tetratelabs/wazero/internal/engine/interpreter
parent[performance] add optional S3 object info caching (#4546) (diff)
downloadgotosocial-6a3b09a507aca0498845d9118a21a82bb5054301.tar.xz
[chore] update dependencies (#4547)
- codeberg.org/gruf/go-ffmpreg: v0.6.12 -> v0.6.14 - github.com/ncruces/go-sqlite3: v0.30.0 -> v0.30.1 - github.com/wazero/wazero: v1.9.0 -> v1.10.0 Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4547 Co-authored-by: kim <grufwub@gmail.com> Co-committed-by: kim <grufwub@gmail.com>
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/engine/interpreter')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/compiler.go45
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go161
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/operations.go33
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/signature.go4
4 files changed, 194 insertions, 49 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/compiler.go b/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/compiler.go
index 4e20e4b2c..4269d237b 100644
--- a/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/compiler.go
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/compiler.go
@@ -814,6 +814,7 @@ operatorSwitch:
c.emit(
newOperationCallIndirect(typeIndex, tableIndex),
)
+
case wasm.OpcodeDrop:
r := inclusiveRange{Start: 0, End: 0}
if peekValueType == unsignedTypeV128 {
@@ -3423,6 +3424,45 @@ operatorSwitch:
default:
return fmt.Errorf("unsupported atomic instruction in interpreterir: %s", wasm.AtomicInstructionName(atomicOp))
}
+
+ case wasm.OpcodeTailCallReturnCall:
+ fdef := c.module.FunctionDefinition(index)
+ functionFrame := c.controlFrames.functionFrame()
+ // Currently we do not support imported functions, we treat them as regular calls.
+ // For details, see internal/engine/RATIONALE.md
+ if _, _, isImport := fdef.Import(); isImport {
+ c.emit(newOperationCall(index))
+ dropOp := newOperationDrop(c.getFrameDropRange(functionFrame, false))
+
+ // Cleanup the stack and then jmp to function frame's continuation (meaning return).
+ c.emit(dropOp)
+ c.emit(newOperationBr(functionFrame.asLabel()))
+ } else {
+ c.emit(newOperationTailCallReturnCall(index))
+ }
+
+ // Return operation is stack-polymorphic, and mark the state as unreachable.
+ // That means subsequent instructions in the current control frame are "unreachable"
+ // and can be safely removed.
+ c.markUnreachable()
+
+ case wasm.OpcodeTailCallReturnCallIndirect:
+ typeIndex := index
+ tableIndex, n, err := leb128.LoadUint32(c.body[c.pc+1:])
+ if err != nil {
+ return fmt.Errorf("read target for br_table: %w", err)
+ }
+ c.pc += n
+
+ functionFrame := c.controlFrames.functionFrame()
+ dropRange := c.getFrameDropRange(functionFrame, false)
+ c.emit(newOperationTailCallReturnCallIndirect(typeIndex, tableIndex, dropRange, functionFrame.asLabel()))
+
+ // Return operation is stack-polymorphic, and mark the state as unreachable.
+ // That means subsequent instructions in the current control frame are "unreachable"
+ // and can be safely removed.
+ c.markUnreachable()
+
default:
return fmt.Errorf("unsupported instruction in interpreterir: 0x%x", op)
}
@@ -3449,7 +3489,10 @@ func (c *compiler) applyToStack(opcode wasm.Opcode) (index uint32, err error) {
wasm.OpcodeLocalSet,
wasm.OpcodeLocalTee,
wasm.OpcodeGlobalGet,
- wasm.OpcodeGlobalSet:
+ wasm.OpcodeGlobalSet,
+ // tail-call proposal
+ wasm.OpcodeTailCallReturnCall,
+ wasm.OpcodeTailCallReturnCallIndirect:
// Assumes that we are at the opcode now so skip it before read immediates.
v, num, err := leb128.LoadUint32(c.body[c.pc+1:])
if err != nil {
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go b/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go
index 5b5e6e9d0..6f2fa949a 100644
--- a/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go
@@ -7,6 +7,7 @@ import (
"fmt"
"math"
"math/bits"
+ "slices"
"sync"
"unsafe"
@@ -27,27 +28,37 @@ import (
// The default value should suffice for most use cases. Those wishing to change this can via `go build -ldflags`.
var callStackCeiling = 2000
+type compiledFunctionWithCount struct {
+ funcs []compiledFunction
+ refCount int
+}
+
// engine is an interpreter implementation of wasm.Engine
type engine struct {
enabledFeatures api.CoreFeatures
- compiledFunctions map[wasm.ModuleID][]compiledFunction // guarded by mutex.
- mux sync.RWMutex
+ compiledFunctions map[wasm.ModuleID]*compiledFunctionWithCount // guarded by mutex.
+ mux sync.Mutex
}
func NewEngine(_ context.Context, enabledFeatures api.CoreFeatures, _ filecache.Cache) wasm.Engine {
return &engine{
enabledFeatures: enabledFeatures,
- compiledFunctions: map[wasm.ModuleID][]compiledFunction{},
+ compiledFunctions: map[wasm.ModuleID]*compiledFunctionWithCount{},
}
}
// Close implements the same method as documented on wasm.Engine.
func (e *engine) Close() (err error) {
+ e.mux.Lock()
+ defer e.mux.Unlock()
+ clear(e.compiledFunctions)
return
}
// CompiledModuleCount implements the same method as documented on wasm.Engine.
func (e *engine) CompiledModuleCount() uint32 {
+ e.mux.Lock()
+ defer e.mux.Unlock()
return uint32(len(e.compiledFunctions))
}
@@ -59,19 +70,33 @@ func (e *engine) DeleteCompiledModule(m *wasm.Module) {
func (e *engine) deleteCompiledFunctions(module *wasm.Module) {
e.mux.Lock()
defer e.mux.Unlock()
+ cf, ok := e.compiledFunctions[module.ID]
+ if !ok {
+ return
+ }
+ cf.refCount--
+ if cf.refCount > 0 {
+ return
+ }
delete(e.compiledFunctions, module.ID)
}
func (e *engine) addCompiledFunctions(module *wasm.Module, fs []compiledFunction) {
e.mux.Lock()
defer e.mux.Unlock()
- e.compiledFunctions[module.ID] = fs
+ e.compiledFunctions[module.ID] = &compiledFunctionWithCount{funcs: fs, refCount: 1}
}
-func (e *engine) getCompiledFunctions(module *wasm.Module) (fs []compiledFunction, ok bool) {
- e.mux.RLock()
- defer e.mux.RUnlock()
- fs, ok = e.compiledFunctions[module.ID]
+func (e *engine) getCompiledFunctions(module *wasm.Module, increaseRefCount bool) (fs []compiledFunction, ok bool) {
+ e.mux.Lock()
+ defer e.mux.Unlock()
+ cf, ok := e.compiledFunctions[module.ID]
+ if ok {
+ fs = cf.funcs
+ if increaseRefCount {
+ cf.refCount++
+ }
+ }
return
}
@@ -242,15 +267,9 @@ type snapshot struct {
// Snapshot implements the same method as documented on experimental.Snapshotter.
func (ce *callEngine) Snapshot() experimental.Snapshot {
- stack := make([]uint64, len(ce.stack))
- copy(stack, ce.stack)
-
- frames := make([]*callFrame, len(ce.frames))
- copy(frames, ce.frames)
-
return &snapshot{
- stack: stack,
- frames: frames,
+ stack: slices.Clone(ce.stack),
+ frames: slices.Clone(ce.frames),
ce: ce,
}
}
@@ -356,7 +375,7 @@ const callFrameStackSize = 0
// CompileModule implements the same method as documented on wasm.Engine.
func (e *engine) CompileModule(_ context.Context, module *wasm.Module, listeners []experimental.FunctionListener, ensureTermination bool) error {
- if _, ok := e.getCompiledFunctions(module); ok { // cache hit!
+ if _, ok := e.getCompiledFunctions(module, true); ok { // cache hit!
return nil
}
@@ -405,7 +424,7 @@ func (e *engine) NewModuleEngine(module *wasm.Module, instance *wasm.ModuleInsta
functions: make([]function, len(module.FunctionSection)+int(module.ImportFunctionCount)),
}
- codes, ok := e.getCompiledFunctions(module)
+ codes, ok := e.getCompiledFunctions(module, false)
if !ok {
return nil, errors.New("source module must be compiled before instantiation")
}
@@ -427,12 +446,10 @@ func (e *engine) NewModuleEngine(module *wasm.Module, instance *wasm.ModuleInsta
// lowerIR lowers the interpreterir operations to engine friendly struct.
func (e *engine) lowerIR(ir *compilationResult, ret *compiledFunction) error {
// Copy the body from the result.
- ret.body = make([]unionOperation, len(ir.Operations))
- copy(ret.body, ir.Operations)
+ ret.body = slices.Clone(ir.Operations)
// Also copy the offsets if necessary.
if offsets := ir.IROperationSourceOffsetsInWasmBinary; len(offsets) > 0 {
- ret.offsetsInWasmBinary = make([]uint64, len(offsets))
- copy(ret.offsetsInWasmBinary, offsets)
+ ret.offsetsInWasmBinary = slices.Clone(offsets)
}
labelAddressResolutions := [labelKindNum][]uint64{}
@@ -449,9 +466,7 @@ func (e *engine) lowerIR(ir *compilationResult, ret *compiledFunction) error {
frameToAddresses := labelAddressResolutions[label.Kind()]
// Expand the slice if necessary.
if diff := fid - len(frameToAddresses) + 1; diff > 0 {
- for j := 0; j < diff; j++ {
- frameToAddresses = append(frameToAddresses, 0)
- }
+ frameToAddresses = append(frameToAddresses, make([]uint64, diff)...)
}
frameToAddresses[fid] = address
labelAddressResolutions[kind] = frameToAddresses
@@ -472,6 +487,8 @@ func (e *engine) lowerIR(ir *compilationResult, ret *compiledFunction) error {
target := op.Us[j]
e.setLabelAddress(&op.Us[j], label(target), labelAddressResolutions)
}
+ case operationKindTailCallReturnCallIndirect:
+ e.setLabelAddress(&op.Us[1], label(op.Us[1]), labelAddressResolutions)
}
}
return nil
@@ -761,18 +778,7 @@ func (ce *callEngine) callNativeFunc(ctx context.Context, m *wasm.ModuleInstance
case operationKindCallIndirect:
offset := ce.popValue()
table := tables[op.U2]
- if offset >= uint64(len(table.References)) {
- panic(wasmruntime.ErrRuntimeInvalidTableAccess)
- }
- rawPtr := table.References[offset]
- if rawPtr == 0 {
- panic(wasmruntime.ErrRuntimeInvalidTableAccess)
- }
-
- tf := functionFromUintptr(rawPtr)
- if tf.typeID != typeIDs[op.U1] {
- panic(wasmruntime.ErrRuntimeIndirectCallTypeMismatch)
- }
+ tf := ce.functionForOffset(table, offset, typeIDs[op.U1])
ce.callFunction(ctx, f.moduleInstance, tf)
frame.pc++
@@ -1725,12 +1731,17 @@ func (ce *callEngine) callNativeFunc(ctx context.Context, m *wasm.ModuleInstance
if fillSize+offset > uint64(len(memoryInst.Buffer)) {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
} else if fillSize != 0 {
- // Uses the copy trick for faster filling buffer.
- // https://gist.github.com/taylorza/df2f89d5f9ab3ffd06865062a4cf015d
+ // Uses the copy trick for faster filling the buffer with the value.
+ // https://github.com/golang/go/blob/go1.24.0/src/bytes/bytes.go#L664-L673
buf := memoryInst.Buffer[offset : offset+fillSize]
- buf[0] = value
- for i := 1; i < len(buf); i *= 2 {
- copy(buf[i:], buf[:i])
+ if value == 0 {
+ clear(buf)
+ } else {
+ buf[0] = value
+ for i := 1; i < len(buf); {
+ chunk := min(i, 8192)
+ i += copy(buf[i:], buf[:chunk])
+ }
}
}
frame.pc++
@@ -1804,7 +1815,7 @@ func (ce *callEngine) callNativeFunc(ctx context.Context, m *wasm.ModuleInstance
panic(wasmruntime.ErrRuntimeInvalidTableAccess)
} else if num > 0 {
// Uses the copy trick for faster filling the region with the value.
- // https://gist.github.com/taylorza/df2f89d5f9ab3ffd06865062a4cf015d
+ // https://github.com/golang/go/blob/go1.24.0/src/slices/slices.go#L514-L517
targetRegion := table.References[offset : offset+num]
targetRegion[0] = ref
for i := 1; i < len(targetRegion); i *= 2 {
@@ -4331,6 +4342,32 @@ func (ce *callEngine) callNativeFunc(ctx context.Context, m *wasm.ModuleInstance
memoryInst.Mux.Unlock()
ce.pushValue(uint64(old))
frame.pc++
+ case operationKindTailCallReturnCall:
+ f := &functions[op.U1]
+ ce.dropForTailCall(frame, f)
+ body, bodyLen = ce.resetPc(frame, f)
+
+ case operationKindTailCallReturnCallIndirect:
+ offset := ce.popValue()
+ table := tables[op.U2]
+ tf := ce.functionForOffset(table, offset, typeIDs[op.U1])
+
+ // We are allowing proper tail calls only across functions that belong to the same
+ // module; for indirect calls, we have to enforce it at run-time.
+ // For details, see internal/engine/RATIONALE.md
+ if tf.moduleInstance != f.moduleInstance {
+ // Revert to a normal call.
+ ce.callFunction(ctx, f.moduleInstance, tf)
+ // Return
+ ce.drop(op.Us[0])
+ // Jump to the function frame (return)
+ frame.pc = op.Us[1]
+ continue
+ }
+
+ ce.dropForTailCall(frame, tf)
+ body, bodyLen = ce.resetPc(frame, tf)
+
default:
frame.pc++
}
@@ -4338,6 +4375,40 @@ func (ce *callEngine) callNativeFunc(ctx context.Context, m *wasm.ModuleInstance
ce.popFrame()
}
+func (ce *callEngine) dropForTailCall(frame *callFrame, f *function) {
+ base := frame.base - frame.f.funcType.ParamNumInUint64
+ paramCount := f.funcType.ParamNumInUint64
+ ce.stack = append(ce.stack[:base], ce.stack[len(ce.stack)-paramCount:]...)
+}
+
+func (ce *callEngine) resetPc(frame *callFrame, f *function) (body []unionOperation, bodyLen uint64) {
+ // The compiler is currently allowing proper tail call only across functions
+ // that belong to the same module; thus, we can overwrite the frame in-place.
+ // For details, see internal/engine/RATIONALE.md
+ frame.f = f
+ frame.base = len(ce.stack)
+ frame.pc = 0
+ body = frame.f.parent.body
+ bodyLen = uint64(len(body))
+ return body, bodyLen
+}
+
+func (ce *callEngine) functionForOffset(table *wasm.TableInstance, offset uint64, expectedTypeID wasm.FunctionTypeID) *function {
+ if offset >= uint64(len(table.References)) {
+ panic(wasmruntime.ErrRuntimeInvalidTableAccess)
+ }
+ rawPtr := table.References[offset]
+ if rawPtr == 0 {
+ panic(wasmruntime.ErrRuntimeInvalidTableAccess)
+ }
+
+ tf := functionFromUintptr(rawPtr)
+ if tf.typeID != expectedTypeID {
+ panic(wasmruntime.ErrRuntimeIndirectCallTypeMismatch)
+ }
+ return tf
+}
+
func wasmCompatMax32bits(v1, v2 uint32) uint64 {
return uint64(math.Float32bits(moremath.WasmCompatMax32(
math.Float32frombits(v1),
@@ -4564,9 +4635,7 @@ func (ce *callEngine) callGoFuncWithStack(ctx context.Context, m *wasm.ModuleIns
// In the interpreter engine, ce.stack may only have capacity to store
// parameters. Grow when there are more results than parameters.
if growLen := resultLen - paramLen; growLen > 0 {
- for i := 0; i < growLen; i++ {
- ce.stack = append(ce.stack, 0)
- }
+ ce.stack = append(ce.stack, make([]uint64, growLen)...)
stackLen += growLen
}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/operations.go b/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/operations.go
index 3087a718f..db3cfa250 100644
--- a/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/operations.go
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/operations.go
@@ -445,6 +445,10 @@ func (o operationKind) String() (ret string) {
ret = "operationKindAtomicRMW8Cmpxchg"
case operationKindAtomicRMW16Cmpxchg:
ret = "operationKindAtomicRMW16Cmpxchg"
+ case operationKindTailCallReturnCall:
+ ret = "operationKindTailCallReturnCall"
+ case operationKindTailCallReturnCallIndirect:
+ ret = "operationKindTailCallReturnCallIndirect"
default:
panic(fmt.Errorf("unknown operation %d", o))
}
@@ -768,6 +772,11 @@ const (
// operationKindAtomicRMW16Cmpxchg is the kind for NewOperationAtomicRMW16Cmpxchg.
operationKindAtomicRMW16Cmpxchg
+ // operationKindTailCallReturnCall is the Kind for newOperationTailCallReturnCall.
+ operationKindTailCallReturnCall
+ // operationKindTailCallReturnCallIndirect is the Kind for newOperationKindTailCallReturnCallIndirect.
+ operationKindTailCallReturnCallIndirect
+
// operationKindEnd is always placed at the bottom of this iota definition to be used in the test.
operationKindEnd
)
@@ -1097,6 +1106,12 @@ func (o unionOperation) String() string {
operationKindAtomicRMW16Cmpxchg:
return o.Kind.String()
+ case operationKindTailCallReturnCall:
+ return fmt.Sprintf("%s %d %s", o.Kind, o.U1, label(o.U2).String())
+
+ case operationKindTailCallReturnCallIndirect:
+ return fmt.Sprintf("%s %d %d", o.Kind, o.U1, o.U2)
+
default:
panic(fmt.Sprintf("TODO: %v", o.Kind))
}
@@ -2810,3 +2825,21 @@ func newOperationAtomicRMW8Cmpxchg(unsignedType unsignedType, arg memoryArg) uni
func newOperationAtomicRMW16Cmpxchg(unsignedType unsignedType, arg memoryArg) unionOperation {
return unionOperation{Kind: operationKindAtomicRMW16Cmpxchg, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
}
+
+// newOperationTailCallReturnCall is a constructor for unionOperation with operationKindTailCallReturnCall.
+//
+// This corresponds to
+//
+// wasm.OpcodeTailCallReturnCall.
+func newOperationTailCallReturnCall(functionIndex uint32) unionOperation {
+ return unionOperation{Kind: operationKindTailCallReturnCall, U1: uint64(functionIndex)}
+}
+
+// NewOperationCallIndirect is a constructor for unionOperation with operationKindTailCallReturnCallIndirect.
+//
+// This corresponds to
+//
+// wasm.OpcodeTailCallReturnCallIndirect.
+func newOperationTailCallReturnCallIndirect(typeIndex, tableIndex uint32, dropDepth inclusiveRange, l label) unionOperation {
+ return unionOperation{Kind: operationKindTailCallReturnCallIndirect, U1: uint64(typeIndex), U2: uint64(tableIndex), Us: []uint64{dropDepth.AsU64(), uint64(l)}}
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/signature.go b/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/signature.go
index 7b9d5602d..da5ca3c15 100644
--- a/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/signature.go
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/signature.go
@@ -272,9 +272,9 @@ func (c *compiler) wasmOpcodeSignature(op wasm.Opcode, index uint32) (*signature
return signature_I32_None, nil
case wasm.OpcodeReturn:
return signature_None_None, nil
- case wasm.OpcodeCall:
+ case wasm.OpcodeCall, wasm.OpcodeTailCallReturnCall:
return c.funcTypeToSigs.get(c.funcs[index], false /* direct */), nil
- case wasm.OpcodeCallIndirect:
+ case wasm.OpcodeCallIndirect, wasm.OpcodeTailCallReturnCallIndirect:
return c.funcTypeToSigs.get(index, true /* call_indirect */), nil
case wasm.OpcodeDrop:
return signature_Unknown_None, nil