summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/module_engine.go
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/module_engine.go
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/module_engine.go')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/module_engine.go344
1 files changed, 344 insertions, 0 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/module_engine.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/module_engine.go
new file mode 100644
index 000000000..ba8f546c0
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/module_engine.go
@@ -0,0 +1,344 @@
+package wazevo
+
+import (
+ "encoding/binary"
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/api"
+ "github.com/tetratelabs/wazero/experimental"
+ "github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
+ "github.com/tetratelabs/wazero/internal/wasm"
+ "github.com/tetratelabs/wazero/internal/wasmruntime"
+)
+
+type (
+ // moduleEngine implements wasm.ModuleEngine.
+ moduleEngine struct {
+ // opaquePtr equals &opaque[0].
+ opaquePtr *byte
+ parent *compiledModule
+ module *wasm.ModuleInstance
+ opaque moduleContextOpaque
+ localFunctionInstances []*functionInstance
+ importedFunctions []importedFunction
+ listeners []experimental.FunctionListener
+ }
+
+ functionInstance struct {
+ executable *byte
+ moduleContextOpaquePtr *byte
+ typeID wasm.FunctionTypeID
+ indexInModule wasm.Index
+ }
+
+ importedFunction struct {
+ me *moduleEngine
+ indexInModule wasm.Index
+ }
+
+ // moduleContextOpaque is the opaque byte slice of Module instance specific contents whose size
+ // is only Wasm-compile-time known, hence dynamic. Its contents are basically the pointers to the module instance,
+ // specific objects as well as functions. This is sometimes called "VMContext" in other Wasm runtimes.
+ //
+ // Internally, the buffer is structured as follows:
+ //
+ // type moduleContextOpaque struct {
+ // moduleInstance *wasm.ModuleInstance
+ // localMemoryBufferPtr *byte (optional)
+ // localMemoryLength uint64 (optional)
+ // importedMemoryInstance *wasm.MemoryInstance (optional)
+ // importedMemoryOwnerOpaqueCtx *byte (optional)
+ // importedFunctions [# of importedFunctions]functionInstance
+ // importedGlobals []ImportedGlobal (optional)
+ // localGlobals []Global (optional)
+ // typeIDsBegin &wasm.ModuleInstance.TypeIDs[0] (optional)
+ // tables []*wasm.TableInstance (optional)
+ // beforeListenerTrampolines1stElement **byte (optional)
+ // afterListenerTrampolines1stElement **byte (optional)
+ // dataInstances1stElement []wasm.DataInstance (optional)
+ // elementInstances1stElement []wasm.ElementInstance (optional)
+ // }
+ //
+ // type ImportedGlobal struct {
+ // *Global
+ // _ uint64 // padding
+ // }
+ //
+ // type Global struct {
+ // Val, ValHi uint64
+ // }
+ //
+ // See wazevoapi.NewModuleContextOffsetData for the details of the offsets.
+ //
+ // Note that for host modules, the structure is entirely different. See buildHostModuleOpaque.
+ moduleContextOpaque []byte
+)
+
+func newAlignedOpaque(size int) moduleContextOpaque {
+ // Check if the size is a multiple of 16.
+ if size%16 != 0 {
+ panic("size must be a multiple of 16")
+ }
+ buf := make([]byte, size+16)
+ // Align the buffer to 16 bytes.
+ rem := uintptr(unsafe.Pointer(&buf[0])) % 16
+ buf = buf[16-rem:]
+ return buf
+}
+
+func putLocalMemory(opaque []byte, offset wazevoapi.Offset, mem *wasm.MemoryInstance) {
+ s := uint64(len(mem.Buffer))
+ var b uint64
+ if len(mem.Buffer) > 0 {
+ b = uint64(uintptr(unsafe.Pointer(&mem.Buffer[0])))
+ }
+ binary.LittleEndian.PutUint64(opaque[offset:], b)
+ binary.LittleEndian.PutUint64(opaque[offset+8:], s)
+}
+
+func (m *moduleEngine) setupOpaque() {
+ inst := m.module
+ offsets := &m.parent.offsets
+ opaque := m.opaque
+
+ binary.LittleEndian.PutUint64(opaque[offsets.ModuleInstanceOffset:],
+ uint64(uintptr(unsafe.Pointer(m.module))),
+ )
+
+ if lm := offsets.LocalMemoryBegin; lm >= 0 {
+ putLocalMemory(opaque, lm, inst.MemoryInstance)
+ }
+
+ // Note: imported memory is resolved in ResolveImportedFunction.
+
+ // Note: imported functions are resolved in ResolveImportedFunction.
+
+ if globalOffset := offsets.GlobalsBegin; globalOffset >= 0 {
+ for i, g := range inst.Globals {
+ if i < int(inst.Source.ImportGlobalCount) {
+ importedME := g.Me.(*moduleEngine)
+ offset := importedME.parent.offsets.GlobalInstanceOffset(g.Index)
+ importedMEOpaque := importedME.opaque
+ binary.LittleEndian.PutUint64(opaque[globalOffset:],
+ uint64(uintptr(unsafe.Pointer(&importedMEOpaque[offset]))))
+ } else {
+ binary.LittleEndian.PutUint64(opaque[globalOffset:], g.Val)
+ binary.LittleEndian.PutUint64(opaque[globalOffset+8:], g.ValHi)
+ }
+ globalOffset += 16
+ }
+ }
+
+ if tableOffset := offsets.TablesBegin; tableOffset >= 0 {
+ // First we write the first element's address of typeIDs.
+ if len(inst.TypeIDs) > 0 {
+ binary.LittleEndian.PutUint64(opaque[offsets.TypeIDs1stElement:], uint64(uintptr(unsafe.Pointer(&inst.TypeIDs[0]))))
+ }
+
+ // Then we write the table addresses.
+ for _, table := range inst.Tables {
+ binary.LittleEndian.PutUint64(opaque[tableOffset:], uint64(uintptr(unsafe.Pointer(table))))
+ tableOffset += 8
+ }
+ }
+
+ if beforeListenerOffset := offsets.BeforeListenerTrampolines1stElement; beforeListenerOffset >= 0 {
+ binary.LittleEndian.PutUint64(opaque[beforeListenerOffset:], uint64(uintptr(unsafe.Pointer(&m.parent.listenerBeforeTrampolines[0]))))
+ }
+ if afterListenerOffset := offsets.AfterListenerTrampolines1stElement; afterListenerOffset >= 0 {
+ binary.LittleEndian.PutUint64(opaque[afterListenerOffset:], uint64(uintptr(unsafe.Pointer(&m.parent.listenerAfterTrampolines[0]))))
+ }
+ if len(inst.DataInstances) > 0 {
+ binary.LittleEndian.PutUint64(opaque[offsets.DataInstances1stElement:], uint64(uintptr(unsafe.Pointer(&inst.DataInstances[0]))))
+ }
+ if len(inst.ElementInstances) > 0 {
+ binary.LittleEndian.PutUint64(opaque[offsets.ElementInstances1stElement:], uint64(uintptr(unsafe.Pointer(&inst.ElementInstances[0]))))
+ }
+}
+
+// NewFunction implements wasm.ModuleEngine.
+func (m *moduleEngine) NewFunction(index wasm.Index) api.Function {
+ if wazevoapi.PrintMachineCodeHexPerFunctionDisassemblable {
+ panic("When PrintMachineCodeHexPerFunctionDisassemblable enabled, functions must not be called")
+ }
+
+ localIndex := index
+ if importedFnCount := m.module.Source.ImportFunctionCount; index < importedFnCount {
+ imported := &m.importedFunctions[index]
+ return imported.me.NewFunction(imported.indexInModule)
+ } else {
+ localIndex -= importedFnCount
+ }
+
+ src := m.module.Source
+ typIndex := src.FunctionSection[localIndex]
+ typ := src.TypeSection[typIndex]
+ sizeOfParamResultSlice := typ.ResultNumInUint64
+ if ps := typ.ParamNumInUint64; ps > sizeOfParamResultSlice {
+ sizeOfParamResultSlice = ps
+ }
+ p := m.parent
+ offset := p.functionOffsets[localIndex]
+
+ ce := &callEngine{
+ indexInModule: index,
+ executable: &p.executable[offset],
+ parent: m,
+ preambleExecutable: &m.parent.entryPreambles[typIndex][0],
+ sizeOfParamResultSlice: sizeOfParamResultSlice,
+ requiredParams: typ.ParamNumInUint64,
+ numberOfResults: typ.ResultNumInUint64,
+ }
+
+ ce.execCtx.memoryGrowTrampolineAddress = &m.parent.sharedFunctions.memoryGrowExecutable[0]
+ ce.execCtx.stackGrowCallTrampolineAddress = &m.parent.sharedFunctions.stackGrowExecutable[0]
+ ce.execCtx.checkModuleExitCodeTrampolineAddress = &m.parent.sharedFunctions.checkModuleExitCode[0]
+ ce.execCtx.tableGrowTrampolineAddress = &m.parent.sharedFunctions.tableGrowExecutable[0]
+ ce.execCtx.refFuncTrampolineAddress = &m.parent.sharedFunctions.refFuncExecutable[0]
+ ce.execCtx.memoryWait32TrampolineAddress = &m.parent.sharedFunctions.memoryWait32Executable[0]
+ ce.execCtx.memoryWait64TrampolineAddress = &m.parent.sharedFunctions.memoryWait64Executable[0]
+ ce.execCtx.memoryNotifyTrampolineAddress = &m.parent.sharedFunctions.memoryNotifyExecutable[0]
+ ce.execCtx.memmoveAddress = memmovPtr
+ ce.init()
+ return ce
+}
+
+// GetGlobalValue implements the same method as documented on wasm.ModuleEngine.
+func (m *moduleEngine) GetGlobalValue(i wasm.Index) (lo, hi uint64) {
+ offset := m.parent.offsets.GlobalInstanceOffset(i)
+ buf := m.opaque[offset:]
+ if i < m.module.Source.ImportGlobalCount {
+ panic("GetGlobalValue should not be called for imported globals")
+ }
+ return binary.LittleEndian.Uint64(buf), binary.LittleEndian.Uint64(buf[8:])
+}
+
+// SetGlobalValue implements the same method as documented on wasm.ModuleEngine.
+func (m *moduleEngine) SetGlobalValue(i wasm.Index, lo, hi uint64) {
+ offset := m.parent.offsets.GlobalInstanceOffset(i)
+ buf := m.opaque[offset:]
+ if i < m.module.Source.ImportGlobalCount {
+ panic("GetGlobalValue should not be called for imported globals")
+ }
+ binary.LittleEndian.PutUint64(buf, lo)
+ binary.LittleEndian.PutUint64(buf[8:], hi)
+}
+
+// OwnsGlobals implements the same method as documented on wasm.ModuleEngine.
+func (m *moduleEngine) OwnsGlobals() bool { return true }
+
+// ResolveImportedFunction implements wasm.ModuleEngine.
+func (m *moduleEngine) ResolveImportedFunction(index, indexInImportedModule wasm.Index, importedModuleEngine wasm.ModuleEngine) {
+ executableOffset, moduleCtxOffset, typeIDOffset := m.parent.offsets.ImportedFunctionOffset(index)
+ importedME := importedModuleEngine.(*moduleEngine)
+
+ if int(indexInImportedModule) >= len(importedME.importedFunctions) {
+ indexInImportedModule -= wasm.Index(len(importedME.importedFunctions))
+ } else {
+ imported := &importedME.importedFunctions[indexInImportedModule]
+ m.ResolveImportedFunction(index, imported.indexInModule, imported.me)
+ return // Recursively resolve the imported function.
+ }
+
+ offset := importedME.parent.functionOffsets[indexInImportedModule]
+ typeID := getTypeIDOf(indexInImportedModule, importedME.module)
+ executable := &importedME.parent.executable[offset]
+ // Write functionInstance.
+ binary.LittleEndian.PutUint64(m.opaque[executableOffset:], uint64(uintptr(unsafe.Pointer(executable))))
+ binary.LittleEndian.PutUint64(m.opaque[moduleCtxOffset:], uint64(uintptr(unsafe.Pointer(importedME.opaquePtr))))
+ binary.LittleEndian.PutUint64(m.opaque[typeIDOffset:], uint64(typeID))
+
+ // Write importedFunction so that it can be used by NewFunction.
+ m.importedFunctions[index] = importedFunction{me: importedME, indexInModule: indexInImportedModule}
+}
+
+func getTypeIDOf(funcIndex wasm.Index, m *wasm.ModuleInstance) wasm.FunctionTypeID {
+ source := m.Source
+
+ var typeIndex wasm.Index
+ if funcIndex >= source.ImportFunctionCount {
+ funcIndex -= source.ImportFunctionCount
+ typeIndex = source.FunctionSection[funcIndex]
+ } else {
+ var cnt wasm.Index
+ for i := range source.ImportSection {
+ if source.ImportSection[i].Type == wasm.ExternTypeFunc {
+ if cnt == funcIndex {
+ typeIndex = source.ImportSection[i].DescFunc
+ break
+ }
+ cnt++
+ }
+ }
+ }
+ return m.TypeIDs[typeIndex]
+}
+
+// ResolveImportedMemory implements wasm.ModuleEngine.
+func (m *moduleEngine) ResolveImportedMemory(importedModuleEngine wasm.ModuleEngine) {
+ importedME := importedModuleEngine.(*moduleEngine)
+ inst := importedME.module
+
+ var memInstPtr uint64
+ var memOwnerOpaquePtr uint64
+ if offs := importedME.parent.offsets; offs.ImportedMemoryBegin >= 0 {
+ offset := offs.ImportedMemoryBegin
+ memInstPtr = binary.LittleEndian.Uint64(importedME.opaque[offset:])
+ memOwnerOpaquePtr = binary.LittleEndian.Uint64(importedME.opaque[offset+8:])
+ } else {
+ memInstPtr = uint64(uintptr(unsafe.Pointer(inst.MemoryInstance)))
+ memOwnerOpaquePtr = uint64(uintptr(unsafe.Pointer(importedME.opaquePtr)))
+ }
+ offset := m.parent.offsets.ImportedMemoryBegin
+ binary.LittleEndian.PutUint64(m.opaque[offset:], memInstPtr)
+ binary.LittleEndian.PutUint64(m.opaque[offset+8:], memOwnerOpaquePtr)
+}
+
+// DoneInstantiation implements wasm.ModuleEngine.
+func (m *moduleEngine) DoneInstantiation() {
+ if !m.module.Source.IsHostModule {
+ m.setupOpaque()
+ }
+}
+
+// FunctionInstanceReference implements wasm.ModuleEngine.
+func (m *moduleEngine) FunctionInstanceReference(funcIndex wasm.Index) wasm.Reference {
+ if funcIndex < m.module.Source.ImportFunctionCount {
+ begin, _, _ := m.parent.offsets.ImportedFunctionOffset(funcIndex)
+ return uintptr(unsafe.Pointer(&m.opaque[begin]))
+ }
+ localIndex := funcIndex - m.module.Source.ImportFunctionCount
+ p := m.parent
+ executable := &p.executable[p.functionOffsets[localIndex]]
+ typeID := m.module.TypeIDs[m.module.Source.FunctionSection[localIndex]]
+
+ lf := &functionInstance{
+ executable: executable,
+ moduleContextOpaquePtr: m.opaquePtr,
+ typeID: typeID,
+ indexInModule: funcIndex,
+ }
+ m.localFunctionInstances = append(m.localFunctionInstances, lf)
+ return uintptr(unsafe.Pointer(lf))
+}
+
+// LookupFunction implements wasm.ModuleEngine.
+func (m *moduleEngine) LookupFunction(t *wasm.TableInstance, typeId wasm.FunctionTypeID, tableOffset wasm.Index) (*wasm.ModuleInstance, wasm.Index) {
+ if tableOffset >= uint32(len(t.References)) || t.Type != wasm.RefTypeFuncref {
+ panic(wasmruntime.ErrRuntimeInvalidTableAccess)
+ }
+ rawPtr := t.References[tableOffset]
+ if rawPtr == 0 {
+ panic(wasmruntime.ErrRuntimeInvalidTableAccess)
+ }
+
+ tf := wazevoapi.PtrFromUintptr[functionInstance](rawPtr)
+ if tf.typeID != typeId {
+ panic(wasmruntime.ErrRuntimeIndirectCallTypeMismatch)
+ }
+ return moduleInstanceFromOpaquePtr(tf.moduleContextOpaquePtr), tf.indexInModule
+}
+
+func moduleInstanceFromOpaquePtr(ptr *byte) *wasm.ModuleInstance {
+ return *(**wasm.ModuleInstance)(unsafe.Pointer(ptr))
+}