Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io> Signed-off-by: Adrian Cole <adrian@tetrate.io>
200 lines
5.2 KiB
Go
200 lines
5.2 KiB
Go
package compiler
|
|
|
|
import (
|
|
"math"
|
|
"os"
|
|
"runtime"
|
|
"testing"
|
|
"unsafe"
|
|
|
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
|
"github.com/tetratelabs/wazero/internal/wasm"
|
|
"github.com/tetratelabs/wazero/internal/wazeroir"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" {
|
|
// Compiler is currently implemented only for amd64 or arm64.
|
|
os.Exit(0)
|
|
}
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
type compilerEnv struct {
|
|
me *moduleEngine
|
|
ce *callEngine
|
|
moduleInstance *wasm.ModuleInstance
|
|
}
|
|
|
|
func (j *compilerEnv) stackTopAsUint32() uint32 {
|
|
return uint32(j.stack()[j.stackPointer()-1])
|
|
}
|
|
|
|
func (j *compilerEnv) stackTopAsInt32() int32 {
|
|
return int32(j.stack()[j.stackPointer()-1])
|
|
}
|
|
func (j *compilerEnv) stackTopAsUint64() uint64 {
|
|
return j.stack()[j.stackPointer()-1]
|
|
}
|
|
|
|
func (j *compilerEnv) stackTopAsInt64() int64 {
|
|
return int64(j.stack()[j.stackPointer()-1])
|
|
}
|
|
|
|
func (j *compilerEnv) stackTopAsFloat32() float32 {
|
|
return math.Float32frombits(uint32(j.stack()[j.stackPointer()-1]))
|
|
}
|
|
|
|
func (j *compilerEnv) stackTopAsFloat64() float64 {
|
|
return math.Float64frombits(j.stack()[j.stackPointer()-1])
|
|
}
|
|
|
|
func (j *compilerEnv) stackTopAsV128() (lo uint64, hi uint64) {
|
|
st := j.stack()
|
|
return st[j.stackPointer()-2], st[j.stackPointer()-1]
|
|
}
|
|
|
|
func (j *compilerEnv) memory() []byte {
|
|
return j.moduleInstance.Memory.Buffer
|
|
}
|
|
|
|
func (j *compilerEnv) stack() []uint64 {
|
|
return j.ce.valueStack
|
|
}
|
|
|
|
func (j *compilerEnv) compilerStatus() nativeCallStatusCode {
|
|
return j.ce.exitContext.statusCode
|
|
}
|
|
|
|
func (j *compilerEnv) builtinFunctionCallAddress() wasm.Index {
|
|
return j.ce.exitContext.builtinFunctionCallIndex
|
|
}
|
|
|
|
func (j *compilerEnv) stackPointer() uint64 {
|
|
return j.ce.valueStackContext.stackPointer
|
|
}
|
|
|
|
func (j *compilerEnv) stackBasePointer() uint64 {
|
|
return j.ce.valueStackContext.stackBasePointer
|
|
}
|
|
|
|
func (j *compilerEnv) setStackPointer(sp uint64) {
|
|
j.ce.valueStackContext.stackPointer = sp
|
|
}
|
|
|
|
func (j *compilerEnv) addGlobals(g ...*wasm.GlobalInstance) {
|
|
j.moduleInstance.Globals = append(j.moduleInstance.Globals, g...)
|
|
}
|
|
|
|
func (j *compilerEnv) globals() []*wasm.GlobalInstance {
|
|
return j.moduleInstance.Globals
|
|
}
|
|
|
|
func (j *compilerEnv) addTable(table *wasm.TableInstance) {
|
|
j.moduleInstance.Tables = append(j.moduleInstance.Tables, table)
|
|
}
|
|
|
|
func (j *compilerEnv) callFrameStackPeek() *callFrame {
|
|
return &j.ce.callFrameStack[j.ce.globalContext.callFrameStackPointer-1]
|
|
}
|
|
|
|
func (j *compilerEnv) callFrameStackPointer() uint64 {
|
|
return j.ce.globalContext.callFrameStackPointer
|
|
}
|
|
|
|
func (j *compilerEnv) setValueStackBasePointer(sp uint64) {
|
|
j.ce.valueStackContext.stackBasePointer = sp
|
|
}
|
|
|
|
func (j *compilerEnv) setCallFrameStackPointerLen(l uint64) {
|
|
j.ce.callFrameStackLen = l
|
|
}
|
|
|
|
func (j *compilerEnv) module() *wasm.ModuleInstance {
|
|
return j.moduleInstance
|
|
}
|
|
|
|
func (j *compilerEnv) moduleEngine() *moduleEngine {
|
|
return j.me
|
|
}
|
|
|
|
func (j *compilerEnv) callEngine() *callEngine {
|
|
return j.ce
|
|
}
|
|
|
|
func (j *compilerEnv) exec(codeSegment []byte) {
|
|
f := &function{
|
|
parent: &code{codeSegment: codeSegment},
|
|
codeInitialAddress: uintptr(unsafe.Pointer(&codeSegment[0])),
|
|
moduleInstanceAddress: uintptr(unsafe.Pointer(j.moduleInstance)),
|
|
source: &wasm.FunctionInstance{
|
|
Kind: wasm.FunctionKindWasm,
|
|
Type: &wasm.FunctionType{},
|
|
Module: j.moduleInstance,
|
|
},
|
|
}
|
|
|
|
j.ce.callFrameStack[j.ce.globalContext.callFrameStackPointer] = callFrame{function: f}
|
|
j.ce.globalContext.callFrameStackPointer++
|
|
|
|
nativecall(
|
|
uintptr(unsafe.Pointer(&codeSegment[0])),
|
|
uintptr(unsafe.Pointer(j.ce)),
|
|
uintptr(unsafe.Pointer(j.moduleInstance)),
|
|
)
|
|
}
|
|
|
|
// newTestCompiler allows us to test a different architecture than the current one.
|
|
type newTestCompiler func(ir *wazeroir.CompilationResult) (compiler, error)
|
|
|
|
func (j *compilerEnv) requireNewCompiler(t *testing.T, fn newTestCompiler, ir *wazeroir.CompilationResult) compilerImpl {
|
|
requireSupportedOSArch(t)
|
|
|
|
if ir == nil {
|
|
ir = &wazeroir.CompilationResult{
|
|
LabelCallers: map[string]uint32{},
|
|
Signature: &wasm.FunctionType{},
|
|
}
|
|
}
|
|
c, err := fn(ir)
|
|
|
|
require.NoError(t, err)
|
|
|
|
ret, ok := c.(compilerImpl)
|
|
require.True(t, ok)
|
|
return ret
|
|
}
|
|
|
|
// CompilerImpl is the interface used for architecture-independent unit tests in this file.
|
|
// This is currently implemented by amd64 and arm64.
|
|
type compilerImpl interface {
|
|
compiler
|
|
compileExitFromNativeCode(nativeCallStatusCode)
|
|
compileMaybeGrowValueStack() error
|
|
compileReturnFunction() error
|
|
getOnStackPointerCeilDeterminedCallBack() func(uint64)
|
|
setStackPointerCeil(uint64)
|
|
compileReleaseRegisterToStack(loc *runtimeValueLocation)
|
|
runtimeValueLocationStack() *runtimeValueLocationStack
|
|
setRuntimeValueLocationStack(*runtimeValueLocationStack)
|
|
compileEnsureOnGeneralPurposeRegister(loc *runtimeValueLocation) error
|
|
compileModuleContextInitialization() error
|
|
compileNOP()
|
|
}
|
|
|
|
const defaultMemoryPageNumInTest = 1
|
|
|
|
func newCompilerEnvironment() *compilerEnv {
|
|
me := &moduleEngine{}
|
|
return &compilerEnv{
|
|
me: me,
|
|
moduleInstance: &wasm.ModuleInstance{
|
|
Memory: &wasm.MemoryInstance{Buffer: make([]byte, wasm.MemoryPageSize*defaultMemoryPageNumInTest)},
|
|
Tables: []*wasm.TableInstance{},
|
|
Globals: []*wasm.GlobalInstance{},
|
|
Engine: me,
|
|
},
|
|
ce: me.newCallEngine(),
|
|
}
|
|
}
|