summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-05-27 15:46:15 +0000
committerLibravatar GitHub <noreply@github.com>2024-05-27 17:46:15 +0200
commit1e7b32490dfdccddd04f46d4b0416b48d749d51b (patch)
tree62a11365933a5a11e0800af64cbdf9172e5e6e7a /vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi
parent[chore] Small styling + link issues (#2933) (diff)
downloadgotosocial-1e7b32490dfdccddd04f46d4b0416b48d749d51b.tar.xz
[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/debug_options.go196
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/exitcode.go109
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/offsetdata.go216
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap.go96
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_disabled.go5
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_enabled.go5
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/pool.go215
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/ptr.go15
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/queue.go26
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/resetmap.go13
10 files changed, 896 insertions, 0 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/debug_options.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/debug_options.go
new file mode 100644
index 000000000..2db61e219
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/debug_options.go
@@ -0,0 +1,196 @@
+package wazevoapi
+
+import (
+ "context"
+ "encoding/hex"
+ "fmt"
+ "math/rand"
+ "os"
+ "time"
+)
+
+// These consts are used various places in the wazevo implementations.
+// Instead of defining them in each file, we define them here so that we can quickly iterate on
+// debugging without spending "where do we have debug logging?" time.
+
+// ----- Debug logging -----
+// These consts must be disabled by default. Enable them only when debugging.
+
+const (
+ FrontEndLoggingEnabled = false
+ SSALoggingEnabled = false
+ RegAllocLoggingEnabled = false
+)
+
+// ----- Output prints -----
+// These consts must be disabled by default. Enable them only when debugging.
+
+const (
+ PrintSSA = false
+ PrintOptimizedSSA = false
+ PrintSSAToBackendIRLowering = false
+ PrintRegisterAllocated = false
+ PrintFinalizedMachineCode = false
+ PrintMachineCodeHexPerFunction = printMachineCodeHexPerFunctionUnmodified || PrintMachineCodeHexPerFunctionDisassemblable //nolint
+ printMachineCodeHexPerFunctionUnmodified = false
+ // PrintMachineCodeHexPerFunctionDisassemblable prints the machine code while modifying the actual result
+ // to make it disassemblable. This is useful when debugging the final machine code. See the places where this is used for detail.
+ // When this is enabled, functions must not be called.
+ PrintMachineCodeHexPerFunctionDisassemblable = false
+)
+
+// printTarget is the function index to print the machine code. This is used for debugging to print the machine code
+// of a specific function.
+const printTarget = -1
+
+// PrintEnabledIndex returns true if the current function index is the print target.
+func PrintEnabledIndex(ctx context.Context) bool {
+ if printTarget == -1 {
+ return true
+ }
+ return GetCurrentFunctionIndex(ctx) == printTarget
+}
+
+// ----- Validations -----
+const (
+ // SSAValidationEnabled enables the SSA validation. This is disabled by default since the operation is expensive.
+ SSAValidationEnabled = false
+)
+
+// ----- Stack Guard Check -----
+const (
+ // StackGuardCheckEnabled enables the stack guard check to ensure that our stack bounds check works correctly.
+ StackGuardCheckEnabled = false
+ StackGuardCheckGuardPageSize = 8096
+)
+
+// CheckStackGuardPage checks the given stack guard page is not corrupted.
+func CheckStackGuardPage(s []byte) {
+ for i := 0; i < StackGuardCheckGuardPageSize; i++ {
+ if s[i] != 0 {
+ panic(
+ fmt.Sprintf("BUG: stack guard page is corrupted:\n\tguard_page=%s\n\tstack=%s",
+ hex.EncodeToString(s[:StackGuardCheckGuardPageSize]),
+ hex.EncodeToString(s[StackGuardCheckGuardPageSize:]),
+ ))
+ }
+ }
+}
+
+// ----- Deterministic compilation verifier -----
+
+const (
+ // DeterministicCompilationVerifierEnabled enables the deterministic compilation verifier. This is disabled by default
+ // since the operation is expensive. But when in doubt, enable this to make sure the compilation is deterministic.
+ DeterministicCompilationVerifierEnabled = false
+ DeterministicCompilationVerifyingIter = 5
+)
+
+type (
+ verifierState struct {
+ initialCompilationDone bool
+ maybeRandomizedIndexes []int
+ r *rand.Rand
+ values map[string]string
+ }
+ verifierStateContextKey struct{}
+ currentFunctionNameKey struct{}
+ currentFunctionIndexKey struct{}
+)
+
+// NewDeterministicCompilationVerifierContext creates a new context with the deterministic compilation verifier used per wasm.Module.
+func NewDeterministicCompilationVerifierContext(ctx context.Context, localFunctions int) context.Context {
+ maybeRandomizedIndexes := make([]int, localFunctions)
+ for i := range maybeRandomizedIndexes {
+ maybeRandomizedIndexes[i] = i
+ }
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
+ return context.WithValue(ctx, verifierStateContextKey{}, &verifierState{
+ r: r, maybeRandomizedIndexes: maybeRandomizedIndexes, values: map[string]string{},
+ })
+}
+
+// DeterministicCompilationVerifierRandomizeIndexes randomizes the indexes for the deterministic compilation verifier.
+// To get the randomized index, use DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex.
+func DeterministicCompilationVerifierRandomizeIndexes(ctx context.Context) {
+ state := ctx.Value(verifierStateContextKey{}).(*verifierState)
+ if !state.initialCompilationDone {
+ // If this is the first attempt, we use the index as-is order.
+ state.initialCompilationDone = true
+ return
+ }
+ r := state.r
+ r.Shuffle(len(state.maybeRandomizedIndexes), func(i, j int) {
+ state.maybeRandomizedIndexes[i], state.maybeRandomizedIndexes[j] = state.maybeRandomizedIndexes[j], state.maybeRandomizedIndexes[i]
+ })
+}
+
+// DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex returns the randomized index for the given `index`
+// which is assigned by DeterministicCompilationVerifierRandomizeIndexes.
+func DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex(ctx context.Context, index int) int {
+ state := ctx.Value(verifierStateContextKey{}).(*verifierState)
+ ret := state.maybeRandomizedIndexes[index]
+ return ret
+}
+
+// VerifyOrSetDeterministicCompilationContextValue verifies that the `newValue` is the same as the previous value for the given `scope`
+// and the current function name. If the previous value doesn't exist, it sets the value to the given `newValue`.
+//
+// If the verification fails, this prints the diff and exits the process.
+func VerifyOrSetDeterministicCompilationContextValue(ctx context.Context, scope string, newValue string) {
+ fn := ctx.Value(currentFunctionNameKey{}).(string)
+ key := fn + ": " + scope
+ verifierCtx := ctx.Value(verifierStateContextKey{}).(*verifierState)
+ oldValue, ok := verifierCtx.values[key]
+ if !ok {
+ verifierCtx.values[key] = newValue
+ return
+ }
+ if oldValue != newValue {
+ fmt.Printf(
+ `BUG: Deterministic compilation failed for function%s at scope="%s".
+
+This is mostly due to (but might not be limited to):
+ * Resetting ssa.Builder, backend.Compiler or frontend.Compiler, etc doens't work as expected, and the compilation has been affected by the previous iterations.
+ * Using a map with non-deterministic iteration order.
+
+---------- [old] ----------
+%s
+
+---------- [new] ----------
+%s
+`,
+ fn, scope, oldValue, newValue,
+ )
+ os.Exit(1)
+ }
+}
+
+// nolint
+const NeedFunctionNameInContext = PrintSSA ||
+ PrintOptimizedSSA ||
+ PrintSSAToBackendIRLowering ||
+ PrintRegisterAllocated ||
+ PrintFinalizedMachineCode ||
+ PrintMachineCodeHexPerFunction ||
+ DeterministicCompilationVerifierEnabled ||
+ PerfMapEnabled
+
+// SetCurrentFunctionName sets the current function name to the given `functionName`.
+func SetCurrentFunctionName(ctx context.Context, index int, functionName string) context.Context {
+ ctx = context.WithValue(ctx, currentFunctionNameKey{}, functionName)
+ ctx = context.WithValue(ctx, currentFunctionIndexKey{}, index)
+ return ctx
+}
+
+// GetCurrentFunctionName returns the current function name.
+func GetCurrentFunctionName(ctx context.Context) string {
+ ret, _ := ctx.Value(currentFunctionNameKey{}).(string)
+ return ret
+}
+
+// GetCurrentFunctionIndex returns the current function index.
+func GetCurrentFunctionIndex(ctx context.Context) int {
+ ret, _ := ctx.Value(currentFunctionIndexKey{}).(int)
+ return ret
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/exitcode.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/exitcode.go
new file mode 100644
index 000000000..5ad594982
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/exitcode.go
@@ -0,0 +1,109 @@
+package wazevoapi
+
+// ExitCode is an exit code of an execution of a function.
+type ExitCode uint32
+
+const (
+ ExitCodeOK ExitCode = iota
+ ExitCodeGrowStack
+ ExitCodeGrowMemory
+ ExitCodeUnreachable
+ ExitCodeMemoryOutOfBounds
+ // ExitCodeCallGoModuleFunction is an exit code for a call to an api.GoModuleFunction.
+ ExitCodeCallGoModuleFunction
+ // ExitCodeCallGoFunction is an exit code for a call to an api.GoFunction.
+ ExitCodeCallGoFunction
+ ExitCodeTableOutOfBounds
+ ExitCodeIndirectCallNullPointer
+ ExitCodeIndirectCallTypeMismatch
+ ExitCodeIntegerDivisionByZero
+ ExitCodeIntegerOverflow
+ ExitCodeInvalidConversionToInteger
+ ExitCodeCheckModuleExitCode
+ ExitCodeCallListenerBefore
+ ExitCodeCallListenerAfter
+ ExitCodeCallGoModuleFunctionWithListener
+ ExitCodeCallGoFunctionWithListener
+ ExitCodeTableGrow
+ ExitCodeRefFunc
+ ExitCodeMemoryWait32
+ ExitCodeMemoryWait64
+ ExitCodeMemoryNotify
+ ExitCodeUnalignedAtomic
+ exitCodeMax
+)
+
+const ExitCodeMask = 0xff
+
+// String implements fmt.Stringer.
+func (e ExitCode) String() string {
+ switch e {
+ case ExitCodeOK:
+ return "ok"
+ case ExitCodeGrowStack:
+ return "grow_stack"
+ case ExitCodeCallGoModuleFunction:
+ return "call_go_module_function"
+ case ExitCodeCallGoFunction:
+ return "call_go_function"
+ case ExitCodeUnreachable:
+ return "unreachable"
+ case ExitCodeMemoryOutOfBounds:
+ return "memory_out_of_bounds"
+ case ExitCodeUnalignedAtomic:
+ return "unaligned_atomic"
+ case ExitCodeTableOutOfBounds:
+ return "table_out_of_bounds"
+ case ExitCodeIndirectCallNullPointer:
+ return "indirect_call_null_pointer"
+ case ExitCodeIndirectCallTypeMismatch:
+ return "indirect_call_type_mismatch"
+ case ExitCodeIntegerDivisionByZero:
+ return "integer_division_by_zero"
+ case ExitCodeIntegerOverflow:
+ return "integer_overflow"
+ case ExitCodeInvalidConversionToInteger:
+ return "invalid_conversion_to_integer"
+ case ExitCodeCheckModuleExitCode:
+ return "check_module_exit_code"
+ case ExitCodeCallListenerBefore:
+ return "call_listener_before"
+ case ExitCodeCallListenerAfter:
+ return "call_listener_after"
+ case ExitCodeCallGoModuleFunctionWithListener:
+ return "call_go_module_function_with_listener"
+ case ExitCodeCallGoFunctionWithListener:
+ return "call_go_function_with_listener"
+ case ExitCodeGrowMemory:
+ return "grow_memory"
+ case ExitCodeTableGrow:
+ return "table_grow"
+ case ExitCodeRefFunc:
+ return "ref_func"
+ case ExitCodeMemoryWait32:
+ return "memory_wait32"
+ case ExitCodeMemoryWait64:
+ return "memory_wait64"
+ case ExitCodeMemoryNotify:
+ return "memory_notify"
+ }
+ panic("TODO")
+}
+
+func ExitCodeCallGoModuleFunctionWithIndex(index int, withListener bool) ExitCode {
+ if withListener {
+ return ExitCodeCallGoModuleFunctionWithListener | ExitCode(index<<8)
+ }
+ return ExitCodeCallGoModuleFunction | ExitCode(index<<8)
+}
+
+func ExitCodeCallGoFunctionWithIndex(index int, withListener bool) ExitCode {
+ if withListener {
+ return ExitCodeCallGoFunctionWithListener | ExitCode(index<<8)
+ }
+ return ExitCodeCallGoFunction | ExitCode(index<<8)
+}
+
+func GoFunctionIndexFromExitCode(exitCode ExitCode) int {
+ return int(exitCode >> 8)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/offsetdata.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/offsetdata.go
new file mode 100644
index 000000000..fe6161b04
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/offsetdata.go
@@ -0,0 +1,216 @@
+package wazevoapi
+
+import (
+ "github.com/tetratelabs/wazero/internal/wasm"
+)
+
+const (
+ // FunctionInstanceSize is the size of wazevo.functionInstance.
+ FunctionInstanceSize = 24
+ // FunctionInstanceExecutableOffset is an offset of `executable` field in wazevo.functionInstance
+ FunctionInstanceExecutableOffset = 0
+ // FunctionInstanceModuleContextOpaquePtrOffset is an offset of `moduleContextOpaquePtr` field in wazevo.functionInstance
+ FunctionInstanceModuleContextOpaquePtrOffset = 8
+ // FunctionInstanceTypeIDOffset is an offset of `typeID` field in wazevo.functionInstance
+ FunctionInstanceTypeIDOffset = 16
+)
+
+const (
+ // ExecutionContextOffsetExitCodeOffset is an offset of `exitCode` field in wazevo.executionContext
+ ExecutionContextOffsetExitCodeOffset Offset = 0
+ // ExecutionContextOffsetCallerModuleContextPtr is an offset of `callerModuleContextPtr` field in wazevo.executionContext
+ ExecutionContextOffsetCallerModuleContextPtr Offset = 8
+ // ExecutionContextOffsetOriginalFramePointer is an offset of `originalFramePointer` field in wazevo.executionContext
+ ExecutionContextOffsetOriginalFramePointer Offset = 16
+ // ExecutionContextOffsetOriginalStackPointer is an offset of `originalStackPointer` field in wazevo.executionContext
+ ExecutionContextOffsetOriginalStackPointer Offset = 24
+ // ExecutionContextOffsetGoReturnAddress is an offset of `goReturnAddress` field in wazevo.executionContext
+ ExecutionContextOffsetGoReturnAddress Offset = 32
+ // ExecutionContextOffsetStackBottomPtr is an offset of `stackBottomPtr` field in wazevo.executionContext
+ ExecutionContextOffsetStackBottomPtr Offset = 40
+ // ExecutionContextOffsetGoCallReturnAddress is an offset of `goCallReturnAddress` field in wazevo.executionContext
+ ExecutionContextOffsetGoCallReturnAddress Offset = 48
+ // ExecutionContextOffsetStackPointerBeforeGoCall is an offset of `StackPointerBeforeGoCall` field in wazevo.executionContext
+ ExecutionContextOffsetStackPointerBeforeGoCall Offset = 56
+ // ExecutionContextOffsetStackGrowRequiredSize is an offset of `stackGrowRequiredSize` field in wazevo.executionContext
+ ExecutionContextOffsetStackGrowRequiredSize Offset = 64
+ // ExecutionContextOffsetMemoryGrowTrampolineAddress is an offset of `memoryGrowTrampolineAddress` field in wazevo.executionContext
+ ExecutionContextOffsetMemoryGrowTrampolineAddress Offset = 72
+ // ExecutionContextOffsetStackGrowCallTrampolineAddress is an offset of `stackGrowCallTrampolineAddress` field in wazevo.executionContext.
+ ExecutionContextOffsetStackGrowCallTrampolineAddress Offset = 80
+ // ExecutionContextOffsetCheckModuleExitCodeTrampolineAddress is an offset of `checkModuleExitCodeTrampolineAddress` field in wazevo.executionContext.
+ ExecutionContextOffsetCheckModuleExitCodeTrampolineAddress Offset = 88
+ // ExecutionContextOffsetSavedRegistersBegin is an offset of the first element of `savedRegisters` field in wazevo.executionContext
+ ExecutionContextOffsetSavedRegistersBegin Offset = 96
+ // ExecutionContextOffsetGoFunctionCallCalleeModuleContextOpaque is an offset of `goFunctionCallCalleeModuleContextOpaque` field in wazevo.executionContext
+ ExecutionContextOffsetGoFunctionCallCalleeModuleContextOpaque Offset = 1120
+ // ExecutionContextOffsetTableGrowTrampolineAddress is an offset of `tableGrowTrampolineAddress` field in wazevo.executionContext
+ ExecutionContextOffsetTableGrowTrampolineAddress Offset = 1128
+ // ExecutionContextOffsetRefFuncTrampolineAddress is an offset of `refFuncTrampolineAddress` field in wazevo.executionContext
+ ExecutionContextOffsetRefFuncTrampolineAddress Offset = 1136
+ ExecutionContextOffsetMemmoveAddress Offset = 1144
+ ExecutionContextOffsetFramePointerBeforeGoCall Offset = 1152
+ ExecutionContextOffsetMemoryWait32TrampolineAddress Offset = 1160
+ ExecutionContextOffsetMemoryWait64TrampolineAddress Offset = 1168
+ ExecutionContextOffsetMemoryNotifyTrampolineAddress Offset = 1176
+)
+
+// ModuleContextOffsetData allows the compilers to get the information about offsets to the fields of wazevo.moduleContextOpaque,
+// This is unique per module.
+type ModuleContextOffsetData struct {
+ TotalSize int
+ ModuleInstanceOffset,
+ LocalMemoryBegin,
+ ImportedMemoryBegin,
+ ImportedFunctionsBegin,
+ GlobalsBegin,
+ TypeIDs1stElement,
+ TablesBegin,
+ BeforeListenerTrampolines1stElement,
+ AfterListenerTrampolines1stElement,
+ DataInstances1stElement,
+ ElementInstances1stElement Offset
+}
+
+// ImportedFunctionOffset returns an offset of the i-th imported function.
+// Each item is stored as wazevo.functionInstance whose size matches FunctionInstanceSize.
+func (m *ModuleContextOffsetData) ImportedFunctionOffset(i wasm.Index) (
+ executableOffset, moduleCtxOffset, typeIDOffset Offset,
+) {
+ base := m.ImportedFunctionsBegin + Offset(i)*FunctionInstanceSize
+ return base, base + 8, base + 16
+}
+
+// GlobalInstanceOffset returns an offset of the i-th global instance.
+func (m *ModuleContextOffsetData) GlobalInstanceOffset(i wasm.Index) Offset {
+ return m.GlobalsBegin + Offset(i)*16
+}
+
+// Offset represents an offset of a field of a struct.
+type Offset int32
+
+// U32 encodes an Offset as uint32 for convenience.
+func (o Offset) U32() uint32 {
+ return uint32(o)
+}
+
+// I64 encodes an Offset as int64 for convenience.
+func (o Offset) I64() int64 {
+ return int64(o)
+}
+
+// U64 encodes an Offset as int64 for convenience.
+func (o Offset) U64() uint64 {
+ return uint64(o)
+}
+
+// LocalMemoryBase returns an offset of the first byte of the local memory.
+func (m *ModuleContextOffsetData) LocalMemoryBase() Offset {
+ return m.LocalMemoryBegin
+}
+
+// LocalMemoryLen returns an offset of the length of the local memory buffer.
+func (m *ModuleContextOffsetData) LocalMemoryLen() Offset {
+ if l := m.LocalMemoryBegin; l >= 0 {
+ return l + 8
+ }
+ return -1
+}
+
+// TableOffset returns an offset of the i-th table instance.
+func (m *ModuleContextOffsetData) TableOffset(tableIndex int) Offset {
+ return m.TablesBegin + Offset(tableIndex)*8
+}
+
+// NewModuleContextOffsetData creates a ModuleContextOffsetData determining the structure of moduleContextOpaque for the given Module.
+// The structure is described in the comment of wazevo.moduleContextOpaque.
+func NewModuleContextOffsetData(m *wasm.Module, withListener bool) ModuleContextOffsetData {
+ ret := ModuleContextOffsetData{}
+ var offset Offset
+
+ ret.ModuleInstanceOffset = 0
+ offset += 8
+
+ if m.MemorySection != nil {
+ ret.LocalMemoryBegin = offset
+ // buffer base + memory size.
+ const localMemorySizeInOpaqueModuleContext = 16
+ offset += localMemorySizeInOpaqueModuleContext
+ } else {
+ // Indicates that there's no local memory
+ ret.LocalMemoryBegin = -1
+ }
+
+ if m.ImportMemoryCount > 0 {
+ offset = align8(offset)
+ // *wasm.MemoryInstance + imported memory's owner (moduleContextOpaque)
+ const importedMemorySizeInOpaqueModuleContext = 16
+ ret.ImportedMemoryBegin = offset
+ offset += importedMemorySizeInOpaqueModuleContext
+ } else {
+ // Indicates that there's no imported memory
+ ret.ImportedMemoryBegin = -1
+ }
+
+ if m.ImportFunctionCount > 0 {
+ offset = align8(offset)
+ ret.ImportedFunctionsBegin = offset
+ // Each function is stored wazevo.functionInstance.
+ size := int(m.ImportFunctionCount) * FunctionInstanceSize
+ offset += Offset(size)
+ } else {
+ ret.ImportedFunctionsBegin = -1
+ }
+
+ if globals := int(m.ImportGlobalCount) + len(m.GlobalSection); globals > 0 {
+ // Align to 16 bytes for globals, as f32/f64/v128 might be loaded via SIMD instructions.
+ offset = align16(offset)
+ ret.GlobalsBegin = offset
+ // Pointers to *wasm.GlobalInstance.
+ offset += Offset(globals) * 16
+ } else {
+ ret.GlobalsBegin = -1
+ }
+
+ if tables := len(m.TableSection) + int(m.ImportTableCount); tables > 0 {
+ offset = align8(offset)
+ ret.TypeIDs1stElement = offset
+ offset += 8 // First element of TypeIDs.
+
+ ret.TablesBegin = offset
+ // Pointers to *wasm.TableInstance.
+ offset += Offset(tables) * 8
+ } else {
+ ret.TypeIDs1stElement = -1
+ ret.TablesBegin = -1
+ }
+
+ if withListener {
+ offset = align8(offset)
+ ret.BeforeListenerTrampolines1stElement = offset
+ offset += 8 // First element of BeforeListenerTrampolines.
+
+ ret.AfterListenerTrampolines1stElement = offset
+ offset += 8 // First element of AfterListenerTrampolines.
+ } else {
+ ret.BeforeListenerTrampolines1stElement = -1
+ ret.AfterListenerTrampolines1stElement = -1
+ }
+
+ ret.DataInstances1stElement = offset
+ offset += 8 // First element of DataInstances.
+
+ ret.ElementInstances1stElement = offset
+ offset += 8 // First element of ElementInstances.
+
+ ret.TotalSize = int(align16(offset))
+ return ret
+}
+
+func align16(o Offset) Offset {
+ return (o + 15) &^ 15
+}
+
+func align8(o Offset) Offset {
+ return (o + 7) &^ 7
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap.go
new file mode 100644
index 000000000..642c7f75d
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap.go
@@ -0,0 +1,96 @@
+package wazevoapi
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+ "sync"
+)
+
+var PerfMap *Perfmap
+
+func init() {
+ if PerfMapEnabled {
+ pid := os.Getpid()
+ filename := "/tmp/perf-" + strconv.Itoa(pid) + ".map"
+
+ fh, err := os.OpenFile(filename, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0o644)
+ if err != nil {
+ panic(err)
+ }
+
+ PerfMap = &Perfmap{fh: fh}
+ }
+}
+
+// Perfmap holds perfmap entries to be flushed into a perfmap file.
+type Perfmap struct {
+ entries []entry
+ mux sync.Mutex
+ fh *os.File
+}
+
+type entry struct {
+ index int
+ offset int64
+ size uint64
+ name string
+}
+
+func (f *Perfmap) Lock() {
+ f.mux.Lock()
+}
+
+func (f *Perfmap) Unlock() {
+ f.mux.Unlock()
+}
+
+// AddModuleEntry adds a perfmap entry into the perfmap file.
+// index is the index of the function in the module, offset is the offset of the function in the module,
+// size is the size of the function, and name is the name of the function.
+//
+// Note that the entries are not flushed into the perfmap file until Flush is called,
+// and the entries are module-scoped; Perfmap must be locked until Flush is called.
+func (f *Perfmap) AddModuleEntry(index int, offset int64, size uint64, name string) {
+ e := entry{index: index, offset: offset, size: size, name: name}
+ if f.entries == nil {
+ f.entries = []entry{e}
+ return
+ }
+ f.entries = append(f.entries, e)
+}
+
+// Flush writes the perfmap entries into the perfmap file where the entries are adjusted by the given `addr` and `functionOffsets`.
+func (f *Perfmap) Flush(addr uintptr, functionOffsets []int) {
+ defer func() {
+ _ = f.fh.Sync()
+ }()
+
+ for _, e := range f.entries {
+ if _, err := f.fh.WriteString(fmt.Sprintf("%x %s %s\n",
+ uintptr(e.offset)+addr+uintptr(functionOffsets[e.index]),
+ strconv.FormatUint(e.size, 16),
+ e.name,
+ )); err != nil {
+ panic(err)
+ }
+ }
+ f.entries = f.entries[:0]
+}
+
+// Clear clears the perfmap entries not yet flushed.
+func (f *Perfmap) Clear() {
+ f.entries = f.entries[:0]
+}
+
+// AddEntry writes a perfmap entry directly into the perfmap file, not using the entries.
+func (f *Perfmap) AddEntry(addr uintptr, size uint64, name string) {
+ _, err := f.fh.WriteString(fmt.Sprintf("%x %s %s\n",
+ addr,
+ strconv.FormatUint(size, 16),
+ name,
+ ))
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_disabled.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_disabled.go
new file mode 100644
index 000000000..bcc4e545c
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_disabled.go
@@ -0,0 +1,5 @@
+//go:build !perfmap
+
+package wazevoapi
+
+const PerfMapEnabled = false
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_enabled.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_enabled.go
new file mode 100644
index 000000000..2a39879ec
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_enabled.go
@@ -0,0 +1,5 @@
+//go:build perfmap
+
+package wazevoapi
+
+const PerfMapEnabled = true
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/pool.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/pool.go
new file mode 100644
index 000000000..3149fdc9e
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/pool.go
@@ -0,0 +1,215 @@
+package wazevoapi
+
+const poolPageSize = 128
+
+// Pool is a pool of T that can be allocated and reset.
+// This is useful to avoid unnecessary allocations.
+type Pool[T any] struct {
+ pages []*[poolPageSize]T
+ resetFn func(*T)
+ allocated, index int
+}
+
+// NewPool returns a new Pool.
+// resetFn is called when a new T is allocated in Pool.Allocate.
+func NewPool[T any](resetFn func(*T)) Pool[T] {
+ var ret Pool[T]
+ ret.resetFn = resetFn
+ ret.Reset()
+ return ret
+}
+
+// Allocated returns the number of allocated T currently in the pool.
+func (p *Pool[T]) Allocated() int {
+ return p.allocated
+}
+
+// Allocate allocates a new T from the pool.
+func (p *Pool[T]) Allocate() *T {
+ if p.index == poolPageSize {
+ if len(p.pages) == cap(p.pages) {
+ p.pages = append(p.pages, new([poolPageSize]T))
+ } else {
+ i := len(p.pages)
+ p.pages = p.pages[:i+1]
+ if p.pages[i] == nil {
+ p.pages[i] = new([poolPageSize]T)
+ }
+ }
+ p.index = 0
+ }
+ ret := &p.pages[len(p.pages)-1][p.index]
+ if p.resetFn != nil {
+ p.resetFn(ret)
+ }
+ p.index++
+ p.allocated++
+ return ret
+}
+
+// View returns the pointer to i-th item from the pool.
+func (p *Pool[T]) View(i int) *T {
+ page, index := i/poolPageSize, i%poolPageSize
+ return &p.pages[page][index]
+}
+
+// Reset resets the pool.
+func (p *Pool[T]) Reset() {
+ p.pages = p.pages[:0]
+ p.index = poolPageSize
+ p.allocated = 0
+}
+
+// IDedPool is a pool of T that can be allocated and reset, with a way to get T by an ID.
+type IDedPool[T any] struct {
+ pool Pool[T]
+ idToItems []*T
+ maxIDEncountered int
+}
+
+// NewIDedPool returns a new IDedPool.
+func NewIDedPool[T any](resetFn func(*T)) IDedPool[T] {
+ return IDedPool[T]{pool: NewPool[T](resetFn)}
+}
+
+// GetOrAllocate returns the T with the given id.
+func (p *IDedPool[T]) GetOrAllocate(id int) *T {
+ if p.maxIDEncountered < id {
+ p.maxIDEncountered = id
+ }
+ if id >= len(p.idToItems) {
+ p.idToItems = append(p.idToItems, make([]*T, id-len(p.idToItems)+1)...)
+ }
+ if p.idToItems[id] == nil {
+ p.idToItems[id] = p.pool.Allocate()
+ }
+ return p.idToItems[id]
+}
+
+// Get returns the T with the given id, or nil if it's not allocated.
+func (p *IDedPool[T]) Get(id int) *T {
+ if id >= len(p.idToItems) {
+ return nil
+ }
+ return p.idToItems[id]
+}
+
+// Reset resets the pool.
+func (p *IDedPool[T]) Reset() {
+ p.pool.Reset()
+ for i := range p.idToItems {
+ p.idToItems[i] = nil
+ }
+ p.maxIDEncountered = -1
+}
+
+// MaxIDEncountered returns the maximum id encountered so far.
+func (p *IDedPool[T]) MaxIDEncountered() int {
+ return p.maxIDEncountered
+}
+
+// arraySize is the size of the array used in VarLengthPool's arrayPool.
+// This is chosen to be 8, which is empirically a good number among 8, 12, 16 and 20.
+const arraySize = 8
+
+// VarLengthPool is a pool of VarLength[T] that can be allocated and reset.
+type (
+ VarLengthPool[T any] struct {
+ arrayPool Pool[varLengthPoolArray[T]]
+ slicePool Pool[[]T]
+ }
+ // varLengthPoolArray wraps an array and keeps track of the next index to be used to avoid the heap allocation.
+ varLengthPoolArray[T any] struct {
+ arr [arraySize]T
+ next int
+ }
+)
+
+// VarLength is a variable length array that can be reused via a pool.
+type VarLength[T any] struct {
+ arr *varLengthPoolArray[T]
+ slc *[]T
+}
+
+// NewVarLengthPool returns a new VarLengthPool.
+func NewVarLengthPool[T any]() VarLengthPool[T] {
+ return VarLengthPool[T]{
+ arrayPool: NewPool[varLengthPoolArray[T]](func(v *varLengthPoolArray[T]) {
+ v.next = 0
+ }),
+ slicePool: NewPool[[]T](func(i *[]T) {
+ *i = (*i)[:0]
+ }),
+ }
+}
+
+// NewNilVarLength returns a new VarLength[T] with a nil backing.
+func NewNilVarLength[T any]() VarLength[T] {
+ return VarLength[T]{}
+}
+
+// Allocate allocates a new VarLength[T] from the pool.
+func (p *VarLengthPool[T]) Allocate(knownMin int) VarLength[T] {
+ if knownMin <= arraySize {
+ arr := p.arrayPool.Allocate()
+ return VarLength[T]{arr: arr}
+ }
+ slc := p.slicePool.Allocate()
+ return VarLength[T]{slc: slc}
+}
+
+// Reset resets the pool.
+func (p *VarLengthPool[T]) Reset() {
+ p.arrayPool.Reset()
+ p.slicePool.Reset()
+}
+
+// Append appends items to the backing slice just like the `append` builtin function in Go.
+func (i VarLength[T]) Append(p *VarLengthPool[T], items ...T) VarLength[T] {
+ if i.slc != nil {
+ *i.slc = append(*i.slc, items...)
+ return i
+ }
+
+ if i.arr == nil {
+ i.arr = p.arrayPool.Allocate()
+ }
+
+ arr := i.arr
+ if arr.next+len(items) <= arraySize {
+ for _, item := range items {
+ arr.arr[arr.next] = item
+ arr.next++
+ }
+ } else {
+ slc := p.slicePool.Allocate()
+ // Copy the array to the slice.
+ for ptr := 0; ptr < arr.next; ptr++ {
+ *slc = append(*slc, arr.arr[ptr])
+ }
+ i.slc = slc
+ *i.slc = append(*i.slc, items...)
+ }
+ return i
+}
+
+// View returns the backing slice.
+func (i VarLength[T]) View() []T {
+ if i.slc != nil {
+ return *i.slc
+ } else if i.arr != nil {
+ arr := i.arr
+ return arr.arr[:arr.next]
+ }
+ return nil
+}
+
+// Cut cuts the backing slice to the given length.
+// Precondition: n <= len(i.backing).
+func (i VarLength[T]) Cut(n int) {
+ if i.slc != nil {
+ *i.slc = (*i.slc)[:n]
+ } else if i.arr != nil {
+ i.arr.next = n
+ }
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/ptr.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/ptr.go
new file mode 100644
index 000000000..f21e1a5d8
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/ptr.go
@@ -0,0 +1,15 @@
+package wazevoapi
+
+import "unsafe"
+
+// PtrFromUintptr resurrects the original *T from the given uintptr.
+// The caller of this function MUST be sure that ptr is valid.
+func PtrFromUintptr[T any](ptr uintptr) *T {
+ // Wraps ptrs as the double pointer in order to avoid the unsafe access as detected by race detector.
+ //
+ // For example, if we have (*function)(unsafe.Pointer(ptr)) instead, then the race detector's "checkptr"
+ // subroutine wanrs as "checkptr: pointer arithmetic result points to invalid allocation"
+ // https://github.com/golang/go/blob/1ce7fcf139417d618c2730010ede2afb41664211/src/runtime/checkptr.go#L69
+ var wrapped *uintptr = &ptr
+ return *(**T)(unsafe.Pointer(wrapped))
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/queue.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/queue.go
new file mode 100644
index 000000000..e3118fa69
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/queue.go
@@ -0,0 +1,26 @@
+package wazevoapi
+
+// Queue is the resettable queue where the underlying slice is reused.
+type Queue[T any] struct {
+ index int
+ Data []T
+}
+
+func (q *Queue[T]) Enqueue(v T) {
+ q.Data = append(q.Data, v)
+}
+
+func (q *Queue[T]) Dequeue() (ret T) {
+ ret = q.Data[q.index]
+ q.index++
+ return
+}
+
+func (q *Queue[T]) Empty() bool {
+ return q.index >= len(q.Data)
+}
+
+func (q *Queue[T]) Reset() {
+ q.index = 0
+ q.Data = q.Data[:0]
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/resetmap.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/resetmap.go
new file mode 100644
index 000000000..7177fbb4b
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/resetmap.go
@@ -0,0 +1,13 @@
+package wazevoapi
+
+// ResetMap resets the map to an empty state, or creates a new map if it is nil.
+func ResetMap[K comparable, V any](m map[K]V) map[K]V {
+ if m == nil {
+ m = make(map[K]V)
+ } else {
+ for v := range m {
+ delete(m, v)
+ }
+ }
+ return m
+}