diff options
| author | 2025-11-10 07:29:48 +0100 | |
|---|---|---|
| committer | 2025-11-17 14:14:33 +0100 | |
| commit | 6a3b09a507aca0498845d9118a21a82bb5054301 (patch) | |
| tree | 5297960ecfe66f723179eb5a1a6f8d59504c3433 /vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go | |
| parent | [performance] add optional S3 object info caching (#4546) (diff) | |
| download | gotosocial-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/interpreter.go')
| -rw-r--r-- | vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go | 161 |
1 files changed, 115 insertions, 46 deletions
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 } |
