wazevo: passes ref_func spectest (#1751)

Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
This commit is contained in:
Takeshi Yoneda
2023-10-04 08:16:22 +09:00
committed by GitHub
parent c4d12c1720
commit f4324b17cd
11 changed files with 76 additions and 20 deletions

View File

@@ -76,6 +76,8 @@ type (
goFunctionCallCalleeModuleContextOpaque uintptr
// tableGrowTrampolineAddress holds the address of table grow trampoline function.
tableGrowTrampolineAddress *byte
// refFuncTrampolineAddress holds the address of ref-func trampoline function.
refFuncTrampolineAddress *byte
}
)
@@ -348,6 +350,14 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
}
c.execCtx.exitCode = wazevoapi.ExitCodeOK
afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
case wazevoapi.ExitCodeRefFunc:
mod := c.callerModuleInstance()
s := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
funcIndex := s[0]
ref := mod.Engine.FunctionInstanceReference(wasm.Index(funcIndex))
s[0] = uint64(ref)
c.execCtx.exitCode = wazevoapi.ExitCodeOK
afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
case wazevoapi.ExitCodeUnreachable:
panic(wasmruntime.ErrRuntimeUnreachable)
case wazevoapi.ExitCodeMemoryOutOfBounds:

View File

@@ -146,6 +146,7 @@ func TestSpectestV2(t *testing.T) {
{"loop"},
{"ref_null"},
{"ref_is_null"},
{"ref_func"},
{"table_get"},
{"table_set"},
{"table_size"},

View File

@@ -54,7 +54,9 @@ type (
// stackGrowExecutable is a compiled executable for growing stack builtin function.
stackGrowExecutable []byte
// tableGrowExecutable is a compiled trampoline executable for table.grow builtin function.
tableGrowExecutable []byte
tableGrowExecutable []byte
// refFuncExecutable is a compiled trampoline executable for ref.func builtin function.
refFuncExecutable []byte
entryPreambles map[*wasm.FunctionType][]byte
listenerBeforeTrampolines map[*wasm.FunctionType][]byte
listenerAfterTrampolines map[*wasm.FunctionType][]byte
@@ -561,7 +563,14 @@ func (e *engine) compileSharedFunctions() {
e.sharedFunctions.checkModuleExitCode = mmapExecutable(src)
}
// TODO: table grow, etc.
e.be.Init()
{
src := e.machine.CompileGoFunctionTrampoline(wazevoapi.ExitCodeRefFunc, &ssa.Signature{
Params: []ssa.Type{ssa.TypeI64 /* exec context */, ssa.TypeI32 /* function index */},
Results: []ssa.Type{ssa.TypeI64}, // returns the function reference.
}, false)
e.sharedFunctions.refFuncExecutable = mmapExecutable(src)
}
e.be.Init()
{
@@ -585,6 +594,9 @@ func sharedFunctionsFinalizer(sf *sharedFunctions) {
if err := platform.MunmapCodeSegment(sf.tableGrowExecutable); err != nil {
panic(err)
}
if err := platform.MunmapCodeSegment(sf.refFuncExecutable); err != nil {
panic(err)
}
for _, f := range sf.entryPreambles {
if err := platform.MunmapCodeSegment(f); err != nil {
panic(err)
@@ -605,6 +617,7 @@ func sharedFunctionsFinalizer(sf *sharedFunctions) {
sf.checkModuleExitCode = nil
sf.stackGrowExecutable = nil
sf.tableGrowExecutable = nil
sf.refFuncExecutable = nil
sf.entryPreambles = nil
sf.listenerBeforeTrampolines = nil
sf.listenerAfterTrampolines = nil

View File

@@ -17,19 +17,18 @@ func Test_sharedFunctionsFinalizer(t *testing.T) {
b1, err := platform.MmapCodeSegment(100)
require.NoError(t, err)
b2, err := platform.MmapCodeSegment(100)
require.NoError(t, err)
b3, err := platform.MmapCodeSegment(100)
require.NoError(t, err)
b4, err := platform.MmapCodeSegment(100)
require.NoError(t, err)
b5, err := platform.MmapCodeSegment(100)
require.NoError(t, err)
b6, err := platform.MmapCodeSegment(100)
require.NoError(t, err)
b7, err := platform.MmapCodeSegment(100)
require.NoError(t, err)
preabmles := map[*wasm.FunctionType][]byte{
{Params: []wasm.ValueType{}}: b4,
@@ -40,6 +39,7 @@ func Test_sharedFunctionsFinalizer(t *testing.T) {
sf.stackGrowExecutable = b2
sf.checkModuleExitCode = b3
sf.tableGrowExecutable = b6
sf.refFuncExecutable = b7
sf.entryPreambles = preabmles
sharedFunctionsFinalizer(sf)
@@ -48,6 +48,7 @@ func Test_sharedFunctionsFinalizer(t *testing.T) {
require.Nil(t, sf.checkModuleExitCode)
require.Nil(t, sf.tableGrowExecutable)
require.Nil(t, sf.entryPreambles)
require.Nil(t, sf.refFuncExecutable)
}
func Test_compiledModuleFinalizer(t *testing.T) {

View File

@@ -23,6 +23,7 @@ type Compiler struct {
memoryGrowSig ssa.Signature
checkModuleExitCodeSig ssa.Signature
tableGrowSig ssa.Signature
refFuncSig ssa.Signature
checkModuleExitCodeArg [1]ssa.Value
ensureTermination bool
@@ -110,13 +111,20 @@ func (c *Compiler) declareSignatures(listenerOn bool) {
c.ssaBuilder.DeclareSignature(&c.checkModuleExitCodeSig)
c.tableGrowSig = ssa.Signature{
ID: c.checkModuleExitCodeSig.ID + 1,
// Takes execution context and the page size to grow.
ID: c.checkModuleExitCodeSig.ID + 1,
Params: []ssa.Type{ssa.TypeI64 /* exec context */, ssa.TypeI32 /* table index */, ssa.TypeI32 /* num */, ssa.TypeI64 /* ref */},
// Returns the previous size.
Results: []ssa.Type{ssa.TypeI32},
}
c.ssaBuilder.DeclareSignature(&c.tableGrowSig)
c.refFuncSig = ssa.Signature{
ID: c.tableGrowSig.ID + 1,
Params: []ssa.Type{ssa.TypeI64 /* exec context */, ssa.TypeI32 /* func index */},
// Returns the function reference.
Results: []ssa.Type{ssa.TypeI64},
}
c.ssaBuilder.DeclareSignature(&c.refFuncSig)
}
// SignatureForWasmFunctionType returns the ssa.Signature for the given wasm.FunctionType.

View File

@@ -1875,12 +1875,6 @@ func TestCompiler_declareSignatures(t *testing.T) {
c.declareSignatures(false)
declaredSigs := builder.Signatures()
require.Equal(t,
4+
3, // memoryGrowSig and checkModuleExitCodeSig + tableGrowSig.
len(declaredSigs),
)
expected := []*ssa.Signature{
{ID: 0, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64}},
{ID: 1, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64, ssa.TypeI64, ssa.TypeI32}},
@@ -1889,9 +1883,11 @@ func TestCompiler_declareSignatures(t *testing.T) {
{ID: 4, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32}, Results: []ssa.Type{ssa.TypeI32}},
{ID: 5, Params: []ssa.Type{ssa.TypeI64}},
{ID: 6, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32, ssa.TypeI32, ssa.TypeI64}, Results: []ssa.Type{ssa.TypeI32}},
{ID: 7, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32}, Results: []ssa.Type{ssa.TypeI64}},
}
for i := 0; i < 7; i++ {
require.Equal(t, len(expected), len(declaredSigs))
for i := 0; i < len(expected); i++ {
require.Equal(t, expected[i].String(), declaredSigs[i].String(), i)
}
})
@@ -1902,11 +1898,6 @@ func TestCompiler_declareSignatures(t *testing.T) {
c.declareSignatures(true)
declaredSigs := builder.Signatures()
require.Equal(t,
4*3+
3, // memoryGrowSig and checkModuleExitCodeSig + tableGrowSig.
len(declaredSigs),
)
expected := []*ssa.Signature{
{ID: 0, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64}},
@@ -1927,8 +1918,9 @@ func TestCompiler_declareSignatures(t *testing.T) {
{ID: 12, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32}, Results: []ssa.Type{ssa.TypeI32}},
{ID: 13, Params: []ssa.Type{ssa.TypeI64}},
{ID: 14, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32, ssa.TypeI32, ssa.TypeI64}, Results: []ssa.Type{ssa.TypeI32}},
{ID: 15, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32}, Results: []ssa.Type{ssa.TypeI64}},
}
require.Equal(t, len(expected), len(declaredSigs))
for i := 0; i < len(declaredSigs); i++ {
require.Equal(t, expected[i].String(), declaredSigs[i].String(), i)
}

View File

@@ -2448,6 +2448,30 @@ func (c *Compiler) lowerCurrentOpcode() {
default:
panic("TODO: unsupported vector instruction: " + wasm.VectorInstructionName(vecOp))
}
case wasm.OpcodeRefFunc:
funcIndex := c.readI32u()
if state.unreachable {
break
}
c.storeCallerModuleContext()
funcIndexVal := builder.AllocateInstruction().AsIconst32(funcIndex).Insert(builder).Return()
refFuncPtr := builder.AllocateInstruction().
AsLoad(c.execCtxPtrValue,
wazevoapi.ExecutionContextOffsetRefFuncTrampolineAddress.U32(),
ssa.TypeI64,
).Insert(builder).Return()
// TODO: reuse the slice.
args := []ssa.Value{c.execCtxPtrValue, funcIndexVal}
refFuncRet := builder.
AllocateInstruction().
AsCallIndirect(refFuncPtr, &c.refFuncSig, args).
Insert(builder).Return()
state.push(refFuncRet)
case wasm.OpcodeRefNull:
c.loweringState.pc++ // skips the reference type as we treat both of them as i64(0).
if state.unreachable {

View File

@@ -154,6 +154,7 @@ func (m *moduleEngine) NewFunction(index wasm.Index) api.Function {
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.init()
return ce
}

View File

@@ -64,4 +64,5 @@ func Test_ExecutionContextOffsets(t *testing.T) {
require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.savedRegisters)), wazevoapi.ExecutionContextOffsetSavedRegistersBegin)
require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.goFunctionCallCalleeModuleContextOpaque)), wazevoapi.ExecutionContextOffsetGoFunctionCallCalleeModuleContextOpaque)
require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.tableGrowTrampolineAddress)), wazevoapi.ExecutionContextOffsetTableGrowTrampolineAddress)
require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.refFuncTrampolineAddress)), wazevoapi.ExecutionContextOffsetRefFuncTrampolineAddress)
}

View File

@@ -25,6 +25,7 @@ const (
ExitCodeCallGoModuleFunctionWithListener
ExitCodeCallGoFunctionWithListener
ExitCodeTableGrow
ExitCodeRefFunc
exitCodeMax
)
@@ -71,6 +72,8 @@ func (e ExitCode) String() string {
return "grow_memory"
case ExitCodeTableGrow:
return "table_grow"
case ExitCodeRefFunc:
return "ref_func"
}
panic("TODO")
}

View File

@@ -46,6 +46,8 @@ const (
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
)
// ModuleContextOffsetData allows the compilers to get the information about offsets to the fields of wazevo.moduleContextOpaque,