mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[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/).
This commit is contained in:
196
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/debug_options.go
generated
vendored
Normal file
196
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/debug_options.go
generated
vendored
Normal file
@ -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
|
||||
}
|
109
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/exitcode.go
generated
vendored
Normal file
109
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/exitcode.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
216
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/offsetdata.go
generated
vendored
Normal file
216
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/offsetdata.go
generated
vendored
Normal file
@ -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
|
||||
}
|
96
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap.go
generated
vendored
Normal file
96
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
}
|
5
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_disabled.go
generated
vendored
Normal file
5
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_disabled.go
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
//go:build !perfmap
|
||||
|
||||
package wazevoapi
|
||||
|
||||
const PerfMapEnabled = false
|
5
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_enabled.go
generated
vendored
Normal file
5
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/perfmap_enabled.go
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
//go:build perfmap
|
||||
|
||||
package wazevoapi
|
||||
|
||||
const PerfMapEnabled = true
|
215
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/pool.go
generated
vendored
Normal file
215
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/pool.go
generated
vendored
Normal file
@ -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
|
||||
}
|
||||
}
|
15
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/ptr.go
generated
vendored
Normal file
15
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/ptr.go
generated
vendored
Normal file
@ -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))
|
||||
}
|
26
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/queue.go
generated
vendored
Normal file
26
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/queue.go
generated
vendored
Normal file
@ -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]
|
||||
}
|
13
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/resetmap.go
generated
vendored
Normal file
13
vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi/resetmap.go
generated
vendored
Normal file
@ -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
|
||||
}
|
Reference in New Issue
Block a user