wazevo: adds support for context cancelation (#1709)

Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
This commit is contained in:
Takeshi Yoneda
2023-09-14 13:22:30 +09:00
committed by GitHub
parent 69c15b10ca
commit 173fae7b81
13 changed files with 165 additions and 38 deletions

View File

@@ -3100,7 +3100,7 @@ L9 (SSA Block: blk6):
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
ssab := ssa.NewBuilder() ssab := ssa.NewBuilder()
offset := wazevoapi.NewModuleContextOffsetData(tc.m) offset := wazevoapi.NewModuleContextOffsetData(tc.m)
fc := frontend.NewFrontendCompiler(tc.m, ssab, &offset) fc := frontend.NewFrontendCompiler(tc.m, ssab, &offset, false)
machine := newMachine() machine := newMachine()
machine.DisableStackCheck() machine.DisableStackCheck()
be := backend.NewCompiler(context.Background(), machine, ssab) be := backend.NewCompiler(context.Background(), machine, ssab)

View File

@@ -376,7 +376,7 @@ func (m *machine) insertStackBoundsCheck(requiredStackSize int64, cur *instructi
ldrAddress.asULoad(operandNR(tmpRegVReg), addressMode{ ldrAddress.asULoad(operandNR(tmpRegVReg), addressMode{
kind: addressModeKindRegUnsignedImm12, kind: addressModeKindRegUnsignedImm12,
rn: x0VReg, // execution context is always the first argument rn: x0VReg, // execution context is always the first argument
imm: wazevoapi.ExecutionContextOffsets.StackGrowCallSequenceAddress.I64(), imm: wazevoapi.ExecutionContextOffsets.StackGrowCallTrampolineAddress.I64(),
}, 64) }, 64)
cur = linkInstr(cur, ldrAddress) cur = linkInstr(cur, ldrAddress)

View File

@@ -63,11 +63,12 @@ type (
stackGrowRequiredSize uintptr stackGrowRequiredSize uintptr
// memoryGrowTrampolineAddress holds the address of memory grow trampoline function. // memoryGrowTrampolineAddress holds the address of memory grow trampoline function.
memoryGrowTrampolineAddress *byte memoryGrowTrampolineAddress *byte
// stackGrowCallSequenceAddress holds the address of stack grow call sequence function. // stackGrowCallTrampolineAddress holds the address of stack grow trampoline function.
stackGrowCallSequenceAddress *byte stackGrowCallTrampolineAddress *byte
// checkModuleExitCodeTrampolineAddress holds the address of check-module-exit-code function.
checkModuleExitCodeTrampolineAddress *byte
// savedRegisters is the opaque spaces for save/restore registers. // savedRegisters is the opaque spaces for save/restore registers.
// We want to align 16 bytes for each register, so we use [64][2]uint64. // We want to align 16 bytes for each register, so we use [64][2]uint64.
_ uint64
savedRegisters [64][2]uint64 savedRegisters [64][2]uint64
// goFunctionCallCalleeModuleContextOpaque is the pointer to the target Go function's moduleContextOpaque. // goFunctionCallCalleeModuleContextOpaque is the pointer to the target Go function's moduleContextOpaque.
goFunctionCallCalleeModuleContextOpaque uintptr goFunctionCallCalleeModuleContextOpaque uintptr
@@ -138,6 +139,19 @@ func (c *callEngine) CallWithStack(ctx context.Context, paramResultStack []uint6
// CallWithStack implements api.Function. // CallWithStack implements api.Function.
func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint64) (err error) { func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint64) (err error) {
p := c.parent
ensureTermination := p.parent.ensureTermination
m := p.module
if ensureTermination {
select {
case <-ctx.Done():
// If the provided context is already done, close the module and return the error.
m.CloseWithCtxErr(ctx)
return m.FailIfClosed()
default:
}
}
var paramResultPtr *uint64 var paramResultPtr *uint64
if len(paramResultStack) > 0 { if len(paramResultStack) > 0 {
paramResultPtr = &paramResultStack[0] paramResultPtr = &paramResultStack[0]
@@ -165,6 +179,11 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
} }
}() }()
if ensureTermination {
done := m.CloseModuleOnCanceledOrTimeout(ctx)
defer done()
}
entrypoint(c.preambleExecutable, c.executable, c.execCtxPtr, c.parent.opaquePtr, paramResultPtr, c.stackTop) entrypoint(c.preambleExecutable, c.executable, c.execCtxPtr, c.parent.opaquePtr, paramResultPtr, c.stackTop)
for { for {
switch ec := c.execCtx.exitCode; ec & wazevoapi.ExitCodeMask { switch ec := c.execCtx.exitCode; ec & wazevoapi.ExitCodeMask {
@@ -210,6 +229,15 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
f.Call(ctx, mod, c.execCtx.goFunctionCallStack[:]) f.Call(ctx, mod, c.execCtx.goFunctionCallStack[:])
c.execCtx.exitCode = wazevoapi.ExitCodeOK c.execCtx.exitCode = wazevoapi.ExitCodeOK
afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, c.execCtx.stackPointerBeforeGoCall) afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, c.execCtx.stackPointerBeforeGoCall)
case wazevoapi.ExitCodeCheckModuleExitCode:
// Note: this operation must be done in Go, not native code. The reason is that
// native code cannot be preempted and that means it can block forever if there are not
// enough OS threads (which we don't have control over).
if err := m.FailIfClosed(); err != nil {
panic(err)
}
c.execCtx.exitCode = wazevoapi.ExitCodeOK
afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, c.execCtx.stackPointerBeforeGoCall)
case wazevoapi.ExitCodeUnreachable: case wazevoapi.ExitCodeUnreachable:
panic(wasmruntime.ErrRuntimeUnreachable) panic(wasmruntime.ErrRuntimeUnreachable)
case wazevoapi.ExitCodeMemoryOutOfBounds: case wazevoapi.ExitCodeMemoryOutOfBounds:

View File

@@ -45,6 +45,9 @@ type (
sharedFunctions struct { sharedFunctions struct {
// memoryGrowExecutable is a compiled executable for memory.grow builtin function. // memoryGrowExecutable is a compiled executable for memory.grow builtin function.
memoryGrowExecutable []byte memoryGrowExecutable []byte
// checkModuleExitCode is a compiled executable for checking module instance exit code. This
// is used when ensureTermination is true.
checkModuleExitCode []byte
// stackGrowExecutable is a compiled executable for growing stack builtin function. // stackGrowExecutable is a compiled executable for growing stack builtin function.
stackGrowExecutable []byte stackGrowExecutable []byte
entryPreambles map[*wasm.FunctionType][]byte entryPreambles map[*wasm.FunctionType][]byte
@@ -54,10 +57,11 @@ type (
compiledModule struct { compiledModule struct {
executable []byte executable []byte
// functionOffsets maps a local function index to the offset in the executable. // functionOffsets maps a local function index to the offset in the executable.
functionOffsets []int functionOffsets []int
parent *engine parent *engine
module *wasm.Module module *wasm.Module
entryPreambles []*byte // indexed-correlated with the type index. entryPreambles []*byte // indexed-correlated with the type index.
ensureTermination bool
// The followings are only available for non host modules. // The followings are only available for non host modules.
@@ -78,7 +82,7 @@ func NewEngine(ctx context.Context, _ api.CoreFeatures, _ filecache.Cache) wasm.
machine: machine, machine: machine,
be: be, be: be,
} }
e.compileBuiltinFunctions() e.compileSharedFunctions()
return e return e
} }
@@ -108,6 +112,7 @@ func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listene
e.rels = e.rels[:0] e.rels = e.rels[:0]
cm := &compiledModule{ cm := &compiledModule{
offsets: wazevoapi.NewModuleContextOffsetData(module), parent: e, module: module, offsets: wazevoapi.NewModuleContextOffsetData(module), parent: e, module: module,
ensureTermination: ensureTermination,
} }
if module.IsHostModule { if module.IsHostModule {
@@ -131,7 +136,7 @@ func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listene
// Creates new compiler instances which are reused for each function. // Creates new compiler instances which are reused for each function.
ssaBuilder := ssa.NewBuilder() ssaBuilder := ssa.NewBuilder()
fe := frontend.NewFrontendCompiler(module, ssaBuilder, &cm.offsets) fe := frontend.NewFrontendCompiler(module, ssaBuilder, &cm.offsets, ensureTermination)
machine := newMachine() machine := newMachine()
be := backend.NewCompiler(ctx, machine, ssaBuilder) be := backend.NewCompiler(ctx, machine, ssaBuilder)
@@ -154,7 +159,7 @@ func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listene
ctx = wazevoapi.SetCurrentFunctionName(ctx, fmt.Sprintf("[%d/%d] \"%s\"", i, len(module.CodeSection)-1, name)) ctx = wazevoapi.SetCurrentFunctionName(ctx, fmt.Sprintf("[%d/%d] \"%s\"", i, len(module.CodeSection)-1, name))
} }
body, rels, err := e.compileLocalWasmFunction(ctx, module, wasm.Index(i), fe, ssaBuilder, be, listeners, ensureTermination) body, rels, err := e.compileLocalWasmFunction(ctx, module, wasm.Index(i), fe, ssaBuilder, be, listeners)
if err != nil { if err != nil {
return nil, fmt.Errorf("compile function %d/%d: %v", i, len(module.CodeSection)-1, err) return nil, fmt.Errorf("compile function %d/%d: %v", i, len(module.CodeSection)-1, err)
} }
@@ -214,7 +219,7 @@ func (e *engine) compileLocalWasmFunction(
fe *frontend.Compiler, fe *frontend.Compiler,
ssaBuilder ssa.Builder, ssaBuilder ssa.Builder,
be backend.Compiler, be backend.Compiler,
_ []experimental.FunctionListener, _ bool, _ []experimental.FunctionListener,
) (body []byte, rels []backend.RelocationInfo, err error) { ) (body []byte, rels []backend.RelocationInfo, err error) {
typ := &module.TypeSection[module.FunctionSection[localFunctionIndex]] typ := &module.TypeSection[module.FunctionSection[localFunctionIndex]]
codeSeg := &module.CodeSection[localFunctionIndex] codeSeg := &module.CodeSection[localFunctionIndex]
@@ -468,7 +473,7 @@ func (e *engine) NewModuleEngine(m *wasm.Module, mi *wasm.ModuleInstance) (wasm.
return me, nil return me, nil
} }
func (e *engine) compileBuiltinFunctions() { func (e *engine) compileSharedFunctions() {
e.sharedFunctions = &sharedFunctions{entryPreambles: make(map[*wasm.FunctionType][]byte)} e.sharedFunctions = &sharedFunctions{entryPreambles: make(map[*wasm.FunctionType][]byte)}
e.be.Init() e.be.Init()
@@ -480,6 +485,15 @@ func (e *engine) compileBuiltinFunctions() {
e.sharedFunctions.memoryGrowExecutable = mmapExecutable(src) e.sharedFunctions.memoryGrowExecutable = mmapExecutable(src)
} }
e.be.Init()
{
src := e.machine.CompileGoFunctionTrampoline(wazevoapi.ExitCodeCheckModuleExitCode, &ssa.Signature{
Params: []ssa.Type{ssa.TypeI32 /* exec context */},
Results: []ssa.Type{ssa.TypeI32},
}, false)
e.sharedFunctions.checkModuleExitCode = mmapExecutable(src)
}
// TODO: table grow, etc. // TODO: table grow, etc.
e.be.Init() e.be.Init()
@@ -491,21 +505,26 @@ func (e *engine) compileBuiltinFunctions() {
e.setFinalizer(e.sharedFunctions, sharedFunctionsFinalizer) e.setFinalizer(e.sharedFunctions, sharedFunctionsFinalizer)
} }
func sharedFunctionsFinalizer(bf *sharedFunctions) { func sharedFunctionsFinalizer(sf *sharedFunctions) {
if err := platform.MunmapCodeSegment(bf.memoryGrowExecutable); err != nil { if err := platform.MunmapCodeSegment(sf.memoryGrowExecutable); err != nil {
panic(err) panic(err)
} }
if err := platform.MunmapCodeSegment(bf.stackGrowExecutable); err != nil { if err := platform.MunmapCodeSegment(sf.checkModuleExitCode); err != nil {
panic(err) panic(err)
} }
for _, f := range bf.entryPreambles { if err := platform.MunmapCodeSegment(sf.stackGrowExecutable); err != nil {
panic(err)
}
for _, f := range sf.entryPreambles {
if err := platform.MunmapCodeSegment(f); err != nil { if err := platform.MunmapCodeSegment(f); err != nil {
panic(err) panic(err)
} }
} }
bf.memoryGrowExecutable = nil sf.memoryGrowExecutable = nil
bf.stackGrowExecutable = nil sf.checkModuleExitCode = nil
sf.stackGrowExecutable = nil
sf.entryPreambles = nil
} }
func compiledModuleFinalizer(cm *compiledModule) { func compiledModuleFinalizer(cm *compiledModule) {

View File

@@ -13,19 +13,37 @@ import (
) )
func Test_sharedFunctionsFinalizer(t *testing.T) { func Test_sharedFunctionsFinalizer(t *testing.T) {
bf := &sharedFunctions{} sf := &sharedFunctions{}
b1, err := platform.MmapCodeSegment(100) b1, err := platform.MmapCodeSegment(100)
require.NoError(t, err) require.NoError(t, err)
b2, err := platform.MmapCodeSegment(100) b2, err := platform.MmapCodeSegment(100)
require.NoError(t, err) require.NoError(t, err)
bf.memoryGrowExecutable = b1
bf.stackGrowExecutable = b2
sharedFunctionsFinalizer(bf) b3, err := platform.MmapCodeSegment(100)
require.Nil(t, bf.memoryGrowExecutable) require.NoError(t, err)
require.Nil(t, bf.stackGrowExecutable)
b4, err := platform.MmapCodeSegment(100)
require.NoError(t, err)
b5, err := platform.MmapCodeSegment(100)
require.NoError(t, err)
preabmles := map[*wasm.FunctionType][]byte{
{Params: []wasm.ValueType{}}: b4,
{Params: []wasm.ValueType{wasm.ValueTypeI32}}: b5,
}
sf.memoryGrowExecutable = b1
sf.stackGrowExecutable = b2
sf.checkModuleExitCode = b3
sf.entryPreambles = preabmles
sharedFunctionsFinalizer(sf)
require.Nil(t, sf.memoryGrowExecutable)
require.Nil(t, sf.stackGrowExecutable)
require.Nil(t, sf.checkModuleExitCode)
require.Nil(t, sf.entryPreambles)
} }
func Test_compiledModuleFinalizer(t *testing.T) { func Test_compiledModuleFinalizer(t *testing.T) {

View File

@@ -17,9 +17,12 @@ type Compiler struct {
m *wasm.Module m *wasm.Module
offset *wazevoapi.ModuleContextOffsetData offset *wazevoapi.ModuleContextOffsetData
// ssaBuilder is a ssa.Builder used by this frontend. // ssaBuilder is a ssa.Builder used by this frontend.
ssaBuilder ssa.Builder ssaBuilder ssa.Builder
signatures map[*wasm.FunctionType]*ssa.Signature signatures map[*wasm.FunctionType]*ssa.Signature
memoryGrowSig ssa.Signature memoryGrowSig ssa.Signature
checkModuleExitCodeSig ssa.Signature
checkModuleExitCodeArg [1]ssa.Value
ensureTermination bool
// Followings are reset by per function. // Followings are reset by per function.
@@ -43,13 +46,14 @@ type Compiler struct {
} }
// NewFrontendCompiler returns a frontend Compiler. // NewFrontendCompiler returns a frontend Compiler.
func NewFrontendCompiler(m *wasm.Module, ssaBuilder ssa.Builder, offset *wazevoapi.ModuleContextOffsetData) *Compiler { func NewFrontendCompiler(m *wasm.Module, ssaBuilder ssa.Builder, offset *wazevoapi.ModuleContextOffsetData, ensureTermination bool) *Compiler {
c := &Compiler{ c := &Compiler{
m: m, m: m,
ssaBuilder: ssaBuilder, ssaBuilder: ssaBuilder,
br: bytes.NewReader(nil), br: bytes.NewReader(nil),
wasmLocalToVariable: make(map[wasm.Index]ssa.Variable), wasmLocalToVariable: make(map[wasm.Index]ssa.Variable),
offset: offset, offset: offset,
ensureTermination: ensureTermination,
} }
c.signatures = make(map[*wasm.FunctionType]*ssa.Signature, len(m.TypeSection)+1) c.signatures = make(map[*wasm.FunctionType]*ssa.Signature, len(m.TypeSection)+1)
@@ -70,6 +74,12 @@ func NewFrontendCompiler(m *wasm.Module, ssaBuilder ssa.Builder, offset *wazevoa
} }
c.ssaBuilder.DeclareSignature(&c.memoryGrowSig) c.ssaBuilder.DeclareSignature(&c.memoryGrowSig)
c.checkModuleExitCodeSig = ssa.Signature{
ID: c.memoryGrowSig.ID + 1,
// Only takes execution context.
Params: []ssa.Type{ssa.TypeI64},
}
c.ssaBuilder.DeclareSignature(&c.checkModuleExitCodeSig)
return c return c
} }

View File

@@ -17,7 +17,8 @@ func TestCompiler_LowerToSSA(t *testing.T) {
// what output should look like, you can run: // what output should look like, you can run:
// `~/wasmtime/target/debug/clif-util wasm --target aarch64-apple-darwin testcase.wat -p -t` // `~/wasmtime/target/debug/clif-util wasm --target aarch64-apple-darwin testcase.wat -p -t`
for _, tc := range []struct { for _, tc := range []struct {
name string name string
ensureTermination bool
// m is the *wasm.Module to be compiled in this test. // m is the *wasm.Module to be compiled in this test.
m *wasm.Module m *wasm.Module
// targetIndex is the index of a local function to be compiled in this test. // targetIndex is the index of a local function to be compiled in this test.
@@ -219,6 +220,36 @@ blk0: (exec_ctx:i64, module_ctx:i64)
blk1: () <-- (blk0,blk1) blk1: () <-- (blk0,blk1)
Jump blk1 Jump blk1
`,
},
{
name: "loop - br / ensure termination", m: testcases.LoopBr.Module,
ensureTermination: true,
exp: `
signatures:
sig2: i64_v
blk0: (exec_ctx:i64, module_ctx:i64)
Jump blk1
blk1: () <-- (blk0,blk1)
v2:i64 = Load exec_ctx, 0x58
CallIndirect v2:sig2, exec_ctx
Jump blk1
blk2: ()
`,
expAfterOpt: `
signatures:
sig2: i64_v
blk0: (exec_ctx:i64, module_ctx:i64)
Jump blk1
blk1: () <-- (blk0,blk1)
v2:i64 = Load exec_ctx, 0x58
CallIndirect v2:sig2, exec_ctx
Jump blk1
`, `,
}, },
{ {
@@ -1736,7 +1767,7 @@ blk4: () <-- (blk2,blk3)
b := ssa.NewBuilder() b := ssa.NewBuilder()
offset := wazevoapi.NewModuleContextOffsetData(tc.m) offset := wazevoapi.NewModuleContextOffsetData(tc.m)
fc := NewFrontendCompiler(tc.m, b, &offset) fc := NewFrontendCompiler(tc.m, b, &offset, tc.ensureTermination)
typeIndex := tc.m.FunctionSection[tc.targetIndex] typeIndex := tc.m.FunctionSection[tc.targetIndex]
code := &tc.m.CodeSection[tc.targetIndex] code := &tc.m.CodeSection[tc.targetIndex]
fc.Init(tc.targetIndex, &tc.m.TypeSection[typeIndex], code.LocalTypes, code.Body) fc.Init(tc.targetIndex, &tc.m.TypeSection[typeIndex], code.LocalTypes, code.Body)

View File

@@ -1012,6 +1012,19 @@ func (c *Compiler) lowerCurrentOpcode() {
c.switchTo(originalLen, loopHeader) c.switchTo(originalLen, loopHeader)
if c.ensureTermination {
checkModuleExitCodePtr := builder.AllocateInstruction().
AsLoad(c.execCtxPtrValue,
wazevoapi.ExecutionContextOffsets.CheckModuleExitCodeTrampolineAddress.U32(),
ssa.TypeI64,
).Insert(builder).Return()
c.checkModuleExitCodeArg[0] = c.execCtxPtrValue
builder.AllocateInstruction().
AsCallIndirect(checkModuleExitCodePtr, &c.checkModuleExitCodeSig, c.checkModuleExitCodeArg[:]).
Insert(builder)
}
case wasm.OpcodeIf: case wasm.OpcodeIf:
bt := c.readBlockType() bt := c.readBlockType()

View File

@@ -142,7 +142,8 @@ func (m *moduleEngine) NewFunction(index wasm.Index) api.Function {
} }
ce.execCtx.memoryGrowTrampolineAddress = &m.parent.sharedFunctions.memoryGrowExecutable[0] ce.execCtx.memoryGrowTrampolineAddress = &m.parent.sharedFunctions.memoryGrowExecutable[0]
ce.execCtx.stackGrowCallSequenceAddress = &m.parent.sharedFunctions.stackGrowExecutable[0] ce.execCtx.stackGrowCallTrampolineAddress = &m.parent.sharedFunctions.stackGrowExecutable[0]
ce.execCtx.checkModuleExitCodeTrampolineAddress = &m.parent.sharedFunctions.checkModuleExitCode[0]
ce.init() ce.init()
return ce return ce
} }

View File

@@ -58,7 +58,8 @@ func Test_ExecutionContextOffsets(t *testing.T) {
require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackPointerBeforeGoCall)), offsets.StackPointerBeforeGoCall) require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackPointerBeforeGoCall)), offsets.StackPointerBeforeGoCall)
require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackGrowRequiredSize)), offsets.StackGrowRequiredSize) require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackGrowRequiredSize)), offsets.StackGrowRequiredSize)
require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.memoryGrowTrampolineAddress)), offsets.MemoryGrowTrampolineAddress) require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.memoryGrowTrampolineAddress)), offsets.MemoryGrowTrampolineAddress)
require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackGrowCallSequenceAddress)), offsets.StackGrowCallSequenceAddress) require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackGrowCallTrampolineAddress)), offsets.StackGrowCallTrampolineAddress)
require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.checkModuleExitCodeTrampolineAddress)), offsets.CheckModuleExitCodeTrampolineAddress)
require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.savedRegisters))%16, wazevoapi.Offset(0), require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.savedRegisters))%16, wazevoapi.Offset(0),
"SavedRegistersBegin must be aligned to 16 bytes") "SavedRegistersBegin must be aligned to 16 bytes")
require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.savedRegisters)), offsets.SavedRegistersBegin) require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.savedRegisters)), offsets.SavedRegistersBegin)

View File

@@ -19,6 +19,7 @@ const (
ExitCodeIntegerDivisionByZero ExitCodeIntegerDivisionByZero
ExitCodeIntegerOverflow ExitCodeIntegerOverflow
ExitCodeInvalidConversionToInteger ExitCodeInvalidConversionToInteger
ExitCodeCheckModuleExitCode
exitCodeMax exitCodeMax
) )
@@ -51,6 +52,8 @@ func (e ExitCode) String() string {
return "integer_overflow" return "integer_overflow"
case ExitCodeInvalidConversionToInteger: case ExitCodeInvalidConversionToInteger:
return "invalid_conversion_to_integer" return "invalid_conversion_to_integer"
case ExitCodeCheckModuleExitCode:
return "check_module_exit_code"
} }
panic("TODO") panic("TODO")
} }

View File

@@ -24,7 +24,8 @@ var ExecutionContextOffsets = ExecutionContextOffsetData{
StackPointerBeforeGoCall: 56, StackPointerBeforeGoCall: 56,
StackGrowRequiredSize: 64, StackGrowRequiredSize: 64,
MemoryGrowTrampolineAddress: 72, MemoryGrowTrampolineAddress: 72,
StackGrowCallSequenceAddress: 80, StackGrowCallTrampolineAddress: 80,
CheckModuleExitCodeTrampolineAddress: 88,
SavedRegistersBegin: 96, SavedRegistersBegin: 96,
GoFunctionCallCalleeModuleContextOpaque: 1120, GoFunctionCallCalleeModuleContextOpaque: 1120,
GoFunctionCallStackBegin: 1128, GoFunctionCallStackBegin: 1128,
@@ -53,8 +54,10 @@ type ExecutionContextOffsetData struct {
StackGrowRequiredSize Offset StackGrowRequiredSize Offset
// MemoryGrowTrampolineAddress is an offset of `memoryGrowTrampolineAddress` field in wazevo.executionContext // MemoryGrowTrampolineAddress is an offset of `memoryGrowTrampolineAddress` field in wazevo.executionContext
MemoryGrowTrampolineAddress Offset MemoryGrowTrampolineAddress Offset
// StackGrowCallSequenceAddress is an offset of `stackGrowCallSequenceAddress` field in wazevo.executionContext // stackGrowCallTrampolineAddress is an offset of `stackGrowCallTrampolineAddress` field in wazevo.executionContext.
StackGrowCallSequenceAddress Offset StackGrowCallTrampolineAddress Offset
// CheckModuleExitCodeTrampolineAddress is an offset of `checkModuleExitCodeTrampolineAddress` field in wazevo.executionContext.
CheckModuleExitCodeTrampolineAddress Offset
// GoCallReturnAddress is an offset of the first element of `savedRegisters` field in wazevo.executionContext // GoCallReturnAddress is an offset of the first element of `savedRegisters` field in wazevo.executionContext
SavedRegistersBegin Offset SavedRegistersBegin Offset
// GoFunctionCallCalleeModuleContextOpaque is an offset of `goFunctionCallCalleeModuleContextOpaque` field in wazevo.executionContext // GoFunctionCallCalleeModuleContextOpaque is an offset of `goFunctionCallCalleeModuleContextOpaque` field in wazevo.executionContext

View File

@@ -51,7 +51,7 @@ var tests = map[string]testCase{
"overflow integer addition": {f: testOverflow}, "overflow integer addition": {f: testOverflow},
"un-signed extend global": {f: testGlobalExtend}, "un-signed extend global": {f: testGlobalExtend},
"user-defined primitive in host func": {f: testUserDefinedPrimitiveHostFunc}, "user-defined primitive in host func": {f: testUserDefinedPrimitiveHostFunc},
"ensures invocations terminate on module close": {f: testEnsureTerminationOnClose, wazevoSkip: true}, "ensures invocations terminate on module close": {f: testEnsureTerminationOnClose},
"call host function indirectly": {f: callHostFunctionIndirect, wazevoSkip: true}, "call host function indirectly": {f: callHostFunctionIndirect, wazevoSkip: true},
"lookup function": {f: testLookupFunction}, "lookup function": {f: testLookupFunction},
"memory grow in recursive call": {f: testMemoryGrowInRecursiveCall}, "memory grow in recursive call": {f: testMemoryGrowInRecursiveCall},