Files
wazero/internal/engine/compiler/compiler_global_test.go
2023-05-19 07:06:30 +02:00

222 lines
7.6 KiB
Go

package compiler
import (
"testing"
"github.com/tetratelabs/wazero/internal/asm"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/internal/wasm"
"github.com/tetratelabs/wazero/internal/wazeroir"
)
func TestCompiler_compileGlobalGet(t *testing.T) {
const globalValue uint64 = 12345
for _, tp := range []wasm.ValueType{
wasm.ValueTypeF32, wasm.ValueTypeF64, wasm.ValueTypeI32, wasm.ValueTypeI64, wasm.ValueTypeExternref, wasm.ValueTypeFuncref,
} {
tp := tp
t.Run(wasm.ValueTypeName(tp), func(t *testing.T) {
env := newCompilerEnvironment()
compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{
Globals: []wasm.GlobalType{{}, {ValType: tp}},
})
// Setup the global. (start with nil as a dummy so that global index can be non-trivial.)
globals := []*wasm.GlobalInstance{nil, {Val: globalValue, Type: wasm.GlobalType{ValType: tp}}}
env.addGlobals(globals...)
// Emit the code.
err := compiler.compilePreamble()
require.NoError(t, err)
op := operationPtr(wazeroir.NewOperationGlobalGet(1))
err = compiler.compileGlobalGet(op)
require.NoError(t, err)
// At this point, the top of stack must be the retrieved global on a register.
global := compiler.runtimeValueLocationStack().peek()
require.True(t, global.onRegister())
require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
switch tp {
case wasm.ValueTypeF32, wasm.ValueTypeF64:
require.True(t, isVectorRegister(global.register))
case wasm.ValueTypeI32, wasm.ValueTypeI64:
require.True(t, isGeneralPurposeRegister(global.register))
}
err = compiler.compileReturnFunction()
require.NoError(t, err)
code := asm.CodeSegment{}
defer func() { require.NoError(t, code.Unmap()) }()
// Generate the code under test.
_, err = compiler.compile(code.NextCodeSection())
require.NoError(t, err)
// Run the code assembled above.
env.exec(code.Bytes())
// Since we call global.get, the top of the stack must be the global value.
require.Equal(t, globalValue, env.stackTopAsUint64())
// Plus as we push the value, the stack pointer must be incremented.
require.Equal(t, uint64(1), env.stackPointer())
})
}
}
func TestCompiler_compileGlobalGet_v128(t *testing.T) {
const v128Type = wasm.ValueTypeV128
env := newCompilerEnvironment()
compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{
Globals: []wasm.GlobalType{{}, {ValType: v128Type}},
})
// Setup the global. (start with nil as a dummy so that global index can be non-trivial.)
globals := []*wasm.GlobalInstance{nil, {Val: 12345, ValHi: 6789, Type: wasm.GlobalType{ValType: v128Type}}}
env.addGlobals(globals...)
// Emit the code.
err := compiler.compilePreamble()
require.NoError(t, err)
op := operationPtr(wazeroir.NewOperationGlobalGet(1))
err = compiler.compileGlobalGet(op)
require.NoError(t, err)
// At this point, the top of stack must be the retrieved global on a register.
global := compiler.runtimeValueLocationStack().peek()
require.True(t, global.onRegister())
require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
require.True(t, isVectorRegister(global.register))
err = compiler.compileReturnFunction()
require.NoError(t, err)
code := asm.CodeSegment{}
defer func() { require.NoError(t, code.Unmap()) }()
// Generate the code under test.
_, err = compiler.compile(code.NextCodeSection())
require.NoError(t, err)
// Run the code assembled above.
env.exec(code.Bytes())
require.Equal(t, uint64(2), env.stackPointer())
require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
// Since we call global.get, the top of the stack must be the global value.
actual := globals[1]
sp := env.ce.stackContext.stackPointer
stack := env.stack()
require.Equal(t, actual.Val, stack[sp-2])
require.Equal(t, actual.ValHi, stack[sp-1])
}
func TestCompiler_compileGlobalSet(t *testing.T) {
const valueToSet uint64 = 12345
for _, tp := range []wasm.ValueType{
wasm.ValueTypeF32, wasm.ValueTypeF64,
wasm.ValueTypeI32, wasm.ValueTypeI64,
wasm.ValueTypeExternref, wasm.ValueTypeFuncref,
} {
tp := tp
t.Run(wasm.ValueTypeName(tp), func(t *testing.T) {
env := newCompilerEnvironment()
compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{
Globals: []wasm.GlobalType{{}, {ValType: tp}},
})
// Setup the global. (start with nil as a dummy so that global index can be non-trivial.)
env.addGlobals(nil, &wasm.GlobalInstance{Val: 40, Type: wasm.GlobalType{ValType: tp}})
err := compiler.compilePreamble()
require.NoError(t, err)
// Place the set target value.
loc := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
switch tp {
case wasm.ValueTypeI32:
loc.valueType = runtimeValueTypeI32
case wasm.ValueTypeI64, wasm.ValueTypeExternref, wasm.ValueTypeFuncref:
loc.valueType = runtimeValueTypeI64
case wasm.ValueTypeF32:
loc.valueType = runtimeValueTypeF32
case wasm.ValueTypeF64:
loc.valueType = runtimeValueTypeF64
}
env.stack()[loc.stackPointer] = valueToSet
const index = 1
op := operationPtr(wazeroir.NewOperationGlobalSet(index))
err = compiler.compileGlobalSet(op)
requireRuntimeLocationStackPointerEqual(t, 0, compiler)
require.NoError(t, err)
err = compiler.compileReturnFunction()
require.NoError(t, err)
code := asm.CodeSegment{}
defer func() { require.NoError(t, code.Unmap()) }()
// Generate the code under test.
_, err = compiler.compile(code.NextCodeSection())
require.NoError(t, err)
env.exec(code.Bytes())
// The global value should be set to valueToSet.
actual := env.globals()[index]
require.Equal(t, valueToSet, actual.Val)
// Plus we consumed the top of the stack, the stack pointer must be decremented.
require.Equal(t, uint64(0), env.stackPointer())
})
}
}
func TestCompiler_compileGlobalSet_v128(t *testing.T) {
const v128Type = wasm.ValueTypeV128
const valueToSetLo, valueToSetHi uint64 = 0xffffff, 1
env := newCompilerEnvironment()
compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{
Globals: []wasm.GlobalType{{}, {ValType: v128Type}},
})
// Setup the global. (start with nil as a dummy so that global index can be non-trivial.)
env.addGlobals(nil, &wasm.GlobalInstance{Val: 0, ValHi: 0, Type: wasm.GlobalType{ValType: v128Type}})
err := compiler.compilePreamble()
require.NoError(t, err)
// Place the set target value.
lo := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
lo.valueType = runtimeValueTypeV128Lo
env.stack()[lo.stackPointer] = valueToSetLo
hi := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
hi.valueType = runtimeValueTypeV128Hi
env.stack()[hi.stackPointer] = valueToSetHi
const index = 1
op := operationPtr(wazeroir.NewOperationGlobalSet(index))
err = compiler.compileGlobalSet(op)
requireRuntimeLocationStackPointerEqual(t, 0, compiler)
require.NoError(t, err)
err = compiler.compileReturnFunction()
require.NoError(t, err)
code := asm.CodeSegment{}
defer func() { require.NoError(t, code.Unmap()) }()
// Generate the code under test.
_, err = compiler.compile(code.NextCodeSection())
require.NoError(t, err)
env.exec(code.Bytes())
require.Equal(t, uint64(0), env.stackPointer())
require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
// The global value should be set to valueToSet.
actual := env.globals()[index]
require.Equal(t, valueToSetLo, actual.Val)
require.Equal(t, valueToSetHi, actual.ValHi)
}