From d9abbf78648e88fcdc4a96a59d46390127f6093e Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Fri, 10 Dec 2021 12:02:58 +0900 Subject: [PATCH 1/7] jit: support unreachable instruciton, and stack trace. Signed-off-by: Takeshi Yoneda --- wasm/jit/engine.go | 108 +++++++++++++++++++++-------- wasm/jit/engine_test.go | 50 ++++++++----- wasm/jit/jit_amd64.go | 21 ++++-- wasm/jit/jit_amd64_test.go | 46 ++++++++++-- wasm/jit/testdata/unreachable.wasm | Bin 0 -> 123 bytes wasm/jit/testdata/unreachable.wat | 14 ++++ 6 files changed, 179 insertions(+), 60 deletions(-) create mode 100644 wasm/jit/testdata/unreachable.wasm create mode 100644 wasm/jit/testdata/unreachable.wat diff --git a/wasm/jit/engine.go b/wasm/jit/engine.go index 1dd9820a8e..199f4f63e6 100644 --- a/wasm/jit/engine.go +++ b/wasm/jit/engine.go @@ -4,6 +4,7 @@ import ( "fmt" "math" "reflect" + "strings" "unsafe" "github.com/tetratelabs/wazero/wasm" @@ -34,20 +35,43 @@ type engine struct { compiledWasmFunctions []*compiledWasmFunction compiledWasmFunctionIndex map[*wasm.FunctionInstance]int64 // Store the host functions and indexes. - hostFunctions []hostFunction - hostFunctionIndex map[*wasm.FunctionInstance]int64 + compiledHostFunctions []*compiledHostFunction + compiledHostFunctionIndex map[*wasm.FunctionInstance]int64 } -type hostFunction = func(ctx *wasm.HostFunctionCallContext) - func (e *engine) Call(f *wasm.FunctionInstance, args ...uint64) (returns []uint64, err error) { + prevFrame := e.callFrameStack + defer func() { + if v := recover(); v != nil { + top := e.callFrameStack + var traces []string + var counter int + for top != prevFrame { + traces = append(traces, fmt.Sprintf("\t%d: %s", counter, top.getFunctionName())) + top = top.caller + counter++ + // TODO: include DWARF symbols. + } + err2, ok := v.(error) + if ok { + err = fmt.Errorf("wasm runtime error: %w", err2) + } else { + err = fmt.Errorf("wasm runtime error: %v", v) + } + + if len(traces) > 0 { + err = fmt.Errorf("%w\nwasm backtrace:\n%s", err, strings.Join(traces, "\n")) + } + } + }() + for _, arg := range args { e.push(arg) } // Note that there's no conflict between e.hostFunctionIndex and e.compiledWasmFunctionIndex, // meaning that each *wasm.FunctionInstance is assigned to either host function index or wasm function one. - if index, ok := e.hostFunctionIndex[f]; ok { - e.hostFunctions[index](&wasm.HostFunctionCallContext{Memory: f.ModuleInstance.Memory}) + if index, ok := e.compiledHostFunctionIndex[f]; ok { + e.compiledHostFunctions[index].f(&wasm.HostFunctionCallContext{Memory: f.ModuleInstance.Memory}) } else if index, ok := e.compiledWasmFunctionIndex[f]; ok { f := e.compiledWasmFunctions[index] e.exec(f) @@ -70,11 +94,11 @@ func (e *engine) PreCompile(fs []*wasm.FunctionInstance) error { var newUniqueHostFunctions, newUniqueWasmFunctions int for _, f := range fs { if f.HostFunction != nil { - if _, ok := e.hostFunctionIndex[f]; ok { + if _, ok := e.compiledHostFunctionIndex[f]; ok { continue } - id := getNewID(e.hostFunctionIndex) - e.hostFunctionIndex[f] = id + id := getNewID(e.compiledHostFunctionIndex) + e.compiledHostFunctionIndex[f] = id newUniqueHostFunctions++ } else { if _, ok := e.compiledWasmFunctionIndex[f]; ok { @@ -85,9 +109,9 @@ func (e *engine) PreCompile(fs []*wasm.FunctionInstance) error { newUniqueWasmFunctions++ } } - e.hostFunctions = append( - e.hostFunctions, - make([]hostFunction, newUniqueHostFunctions)..., + e.compiledHostFunctions = append( + e.compiledHostFunctions, + make([]*compiledHostFunction, newUniqueHostFunctions)..., ) e.compiledWasmFunctions = append( e.compiledWasmFunctions, @@ -102,8 +126,8 @@ func getNewID(idMap map[*wasm.FunctionInstance]int64) int64 { func (e *engine) Compile(f *wasm.FunctionInstance) error { if f.HostFunction != nil { - id := e.hostFunctionIndex[f] - if e.hostFunctions[id] != nil { + id := e.compiledHostFunctionIndex[f] + if e.compiledHostFunctions[id] != nil { // Already compiled. return nil } @@ -140,7 +164,7 @@ func (e *engine) Compile(f *wasm.FunctionInstance) error { } } } - e.hostFunctions[id] = hf + e.compiledHostFunctions[id] = &compiledHostFunction{f: hf, name: f.Name} } else { id := e.compiledWasmFunctionIndex[f] if e.compiledWasmFunctions[id] != nil { @@ -166,7 +190,7 @@ func newEngine() *engine { e := &engine{ stack: make([]uint64, initialStackSize), compiledWasmFunctionIndex: make(map[*wasm.FunctionInstance]int64), - hostFunctionIndex: make(map[*wasm.FunctionInstance]int64), + compiledHostFunctionIndex: make(map[*wasm.FunctionInstance]int64), } return e } @@ -195,6 +219,8 @@ const ( jitCallStatusCodeCallBuiltInFunction // jitCallStatusCodeCallWasmFunction means the jitcall returns to make a host function call. jitCallStatusCodeCallHostFunction + // jitCallStatusCodeUnreachable means the function invocation reaches "unreachable" instruction. + jitCallStatusCodeUnreachable // TODO: trap, etc? ) @@ -208,6 +234,8 @@ func (s jitCallStatusCode) String() (ret string) { ret = "call_builtin_function" case jitCallStatusCodeCallHostFunction: ret = "call_host_function" + case jitCallStatusCodeUnreachable: + ret = "unreachable" } return } @@ -226,18 +254,34 @@ type callFrame struct { continuationAddress uintptr continuationStackPointer uint64 baseStackPointer uint64 - f *compiledWasmFunction + wasmFunction *compiledWasmFunction + hostFunction *compiledHostFunction caller *callFrame } func (c *callFrame) String() string { return fmt.Sprintf( - "[continuation address=%d, continuation stack pointer=%d, base stack pointer=%d]", - c.continuationAddress, c.continuationStackPointer, c.baseStackPointer, + "[%s: continuation address=%d, continuation stack pointer=%d, base stack pointer=%d]", + c.getFunctionName(), c.continuationAddress, c.continuationStackPointer, c.baseStackPointer, ) } +func (c *callFrame) getFunctionName() string { + if c.wasmFunction != nil { + return c.wasmFunction.originalFunctionInstance.Name + } else { + return c.hostFunction.name + } +} + +type compiledHostFunction = struct { + f func(ctx *wasm.HostFunctionCallContext) + name string +} + type compiledWasmFunction struct { + // FunctionInstance from which this is compiled. + originalFunctionInstance *wasm.FunctionInstance // inputs,returns represents the number of input/returns of function. inputs, returns uint64 // codeSegment is holding the compiled native code as a byte slice. @@ -280,7 +324,7 @@ func (e *engine) maybeGrowStack(maxStackPointer uint64) { func (e *engine) exec(f *compiledWasmFunction) { e.callFrameStack = &callFrame{ continuationAddress: f.codeInitialAddress, - f: f, + wasmFunction: f, caller: nil, continuationStackPointer: f.inputs, } @@ -299,7 +343,7 @@ func (e *engine) exec(f *compiledWasmFunction) { jitcall( currentFrame.continuationAddress, uintptr(unsafe.Pointer(e)), - currentFrame.f.memoryAddress, + currentFrame.wasmFunction.memoryAddress, ) // Check the status code from JIT code. @@ -319,13 +363,13 @@ func (e *engine) exec(f *compiledWasmFunction) { nextFunc := e.compiledWasmFunctions[e.functionCallIndex] // Calculate the continuation address so // we can resume this caller function frame. - currentFrame.continuationAddress = currentFrame.f.codeInitialAddress + e.continuationAddressOffset + currentFrame.continuationAddress = currentFrame.wasmFunction.codeInitialAddress + e.continuationAddressOffset currentFrame.continuationStackPointer = e.currentStackPointer + nextFunc.returns - nextFunc.inputs currentFrame.baseStackPointer = e.currentBaseStackPointer // Create the callee frame. frame := &callFrame{ continuationAddress: nextFunc.codeInitialAddress, - f: nextFunc, + wasmFunction: nextFunc, // Set the caller frame so we can return back to the current frame! caller: currentFrame, // Set the base pointer to the beginning of the function inputs @@ -339,17 +383,23 @@ func (e *engine) exec(f *compiledWasmFunction) { // Set the stack pointer so that base+sp would point to the top of function inputs. e.currentStackPointer = nextFunc.inputs case jitCallStatusCodeCallBuiltInFunction: - // TODO: check the signature and modify stack pointer. switch e.functionCallIndex { case builtinFunctionIndexGrowMemory: v := e.pop() - e.memoryGrow(currentFrame.f.memory, v) + e.memoryGrow(currentFrame.wasmFunction.memory, v) } - currentFrame.continuationAddress = currentFrame.f.codeInitialAddress + e.continuationAddressOffset + currentFrame.continuationAddress = currentFrame.wasmFunction.codeInitialAddress + e.continuationAddressOffset case jitCallStatusCodeCallHostFunction: - e.hostFunctions[e.functionCallIndex](&wasm.HostFunctionCallContext{Memory: f.memory}) - // TODO: check the signature and modify stack pointer. - currentFrame.continuationAddress = currentFrame.f.codeInitialAddress + e.continuationAddressOffset + targetHostFunction := e.compiledHostFunctions[e.functionCallIndex] + currentFrame.continuationAddress = currentFrame.wasmFunction.codeInitialAddress + e.continuationAddressOffset + // Push the call frame for this host function. + e.callFrameStack = &callFrame{hostFunction: targetHostFunction, caller: currentFrame} + // Call into the host function. + targetHostFunction.f(&wasm.HostFunctionCallContext{Memory: f.memory}) + // Pop the call frame. + e.callFrameStack = currentFrame + case jitCallStatusCodeUnreachable: + panic("unreachable") } } } diff --git a/wasm/jit/engine_test.go b/wasm/jit/engine_test.go index 630076660f..e956f6344d 100644 --- a/wasm/jit/engine_test.go +++ b/wasm/jit/engine_test.go @@ -11,7 +11,6 @@ import ( "github.com/tetratelabs/wazero/wasi" "github.com/tetratelabs/wazero/wasm" - "github.com/tetratelabs/wazero/wasm/wazeroir" ) // Ensures that the offset consts do not drift when we manipulate the engine struct. @@ -32,25 +31,40 @@ func TestEngine_fibonacci(t *testing.T) { require.NoError(t, err) mod, err := wasm.DecodeModule(buf) require.NoError(t, err) - store := wasm.NewStore(wazeroir.NewEngine()) + store := wasm.NewStore(NewEngine()) require.NoError(t, err) err = wasi.NewEnvironment().Register(store) require.NoError(t, err) err = store.Instantiate(mod, "test") require.NoError(t, err) - m, ok := store.ModuleInstances["test"] - require.True(t, ok) - exp, ok := m.Exports["fib"] - require.True(t, ok) - f := exp.Function - eng := newEngine() - err = eng.PreCompile([]*wasm.FunctionInstance{f}) + out, _, err := store.CallFunction("test", "fib", 20) + require.NoError(t, err) + require.Equal(t, uint64(10946), out[0]) +} + +func TestEngine_unreachable(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skip() + } + buf, err := os.ReadFile("testdata/unreachable.wasm") require.NoError(t, err) - err = eng.Compile(f) + mod, err := wasm.DecodeModule(buf) require.NoError(t, err) - out, err := eng.Call(f, 20) + store := wasm.NewStore(NewEngine()) require.NoError(t, err) - require.Equal(t, uint64(10946), out[0]) + err = wasi.NewEnvironment().Register(store) + require.NoError(t, err) + err = store.Instantiate(mod, "test") + require.NoError(t, err) + _, _, err = store.CallFunction("test", "cause_unreachable") + exp := `wasm runtime error: unreachable +wasm backtrace: + 0: three + 1: two + 2: one + 3: cause_unreachable` + require.Error(t, err) + require.Equal(t, exp, err.Error()) } func TestEngine_PreCompile(t *testing.T) { @@ -70,18 +84,18 @@ func TestEngine_PreCompile(t *testing.T) { // Check the indexes. require.Len(t, eng.compiledWasmFunctions, 3) require.Len(t, eng.compiledWasmFunctionIndex, 3) - require.Len(t, eng.hostFunctions, 1) - require.Len(t, eng.hostFunctionIndex, 1) + require.Len(t, eng.compiledHostFunctions, 1) + require.Len(t, eng.compiledHostFunctionIndex, 1) prevCompiledFunctions := make([]*compiledWasmFunction, len(eng.compiledWasmFunctions)) - prevHostFunctions := make([]hostFunction, len(eng.hostFunctions)) + prevHostFunctions := make([]*compiledHostFunction, len(eng.compiledHostFunctions)) copy(prevCompiledFunctions, eng.compiledWasmFunctions) - copy(prevHostFunctions, eng.hostFunctions) + copy(prevHostFunctions, eng.compiledHostFunctions) err = eng.PreCompile(fs) // Precompiling same functions should be noop. require.NoError(t, err) require.Len(t, eng.compiledWasmFunctionIndex, 3) - require.Len(t, eng.hostFunctionIndex, 1) - require.Equal(t, prevHostFunctions, eng.hostFunctions) + require.Len(t, eng.compiledHostFunctionIndex, 1) + require.Equal(t, prevHostFunctions, eng.compiledHostFunctions) require.Equal(t, prevCompiledFunctions, eng.compiledWasmFunctions) } diff --git a/wasm/jit/jit_amd64.go b/wasm/jit/jit_amd64.go index 5c9b702425..fdec979d19 100644 --- a/wasm/jit/jit_amd64.go +++ b/wasm/jit/jit_amd64.go @@ -58,7 +58,7 @@ func (e *engine) compileWasmFunction(f *wasm.FunctionInstance) (*compiledWasmFun for _, op := range ir.Operations { switch o := op.(type) { case *wazeroir.OperationUnreachable: - return nil, fmt.Errorf("unsupported operation in JIT compiler: %v", o) + builder.handleUnreachable() case *wazeroir.OperationLabel: if err := builder.handleLabel(o); err != nil { return nil, fmt.Errorf("error handling label operation: %w", err) @@ -224,11 +224,12 @@ func (e *engine) compileWasmFunction(f *wasm.FunctionInstance) (*compiledWasmFun func (b *amd64Builder) newCompiledWasmFunction(code []byte) *compiledWasmFunction { cf := &compiledWasmFunction{ - codeSegment: code, - inputs: uint64(len(b.f.Signature.InputTypes)), - returns: uint64(len(b.f.Signature.ReturnTypes)), - memory: b.f.ModuleInstance.Memory, - maxStackPointer: b.locationStack.maxStackPointer, + originalFunctionInstance: b.f, + codeSegment: code, + inputs: uint64(len(b.f.Signature.InputTypes)), + returns: uint64(len(b.f.Signature.ReturnTypes)), + memory: b.f.ModuleInstance.Memory, + maxStackPointer: b.locationStack.maxStackPointer, } if cf.memory != nil { cf.memoryAddress = uintptr(unsafe.Pointer(&cf.memory.Buffer[0])) @@ -298,6 +299,12 @@ func (b *amd64Builder) newProg() (prog *obj.Prog) { return } +func (b *amd64Builder) handleUnreachable() { + b.releaseAllRegistersToStack() + b.setJITStatus(jitCallStatusCodeUnreachable) + b.returnFunction() +} + func (b *amd64Builder) handleBr(o *wazeroir.OperationBr) error { if o.Target.IsReturnTarget() { // Release all the registers as our calling convention requires the callee-save. @@ -491,7 +498,7 @@ func (b *amd64Builder) handleLabel(o *wazeroir.OperationLabel) error { func (b *amd64Builder) handleCall(o *wazeroir.OperationCall) error { target := b.f.ModuleInstance.Functions[o.FunctionIndex] if target.HostFunction != nil { - index := b.eng.hostFunctionIndex[target] + index := b.eng.compiledHostFunctionIndex[target] b.callHostFunctionFromConstIndex(index) } else { index := b.eng.compiledWasmFunctionIndex[target] diff --git a/wasm/jit/jit_amd64_test.go b/wasm/jit/jit_amd64_test.go index e98f7ca106..5c7656dda1 100644 --- a/wasm/jit/jit_amd64_test.go +++ b/wasm/jit/jit_amd64_test.go @@ -194,6 +194,7 @@ func Test_setJITStatus(t *testing.T) { jitCallStatusCodeCallWasmFunction, jitCallStatusCodeCallBuiltInFunction, jitCallStatusCodeCallHostFunction, + jitCallStatusCodeUnreachable, } { t.Run(s.String(), func(t *testing.T) { @@ -421,9 +422,12 @@ func TestEngine_exec_callHostFunction(t *testing.T) { // Setup. eng := newEngine() - eng.hostFunctions = append(eng.hostFunctions, func(ctx *wasm.HostFunctionCallContext) { - eng.stack[eng.currentStackPointer-1] *= 100 - }) + hostFunction := &compiledHostFunction{ + f: func(ctx *wasm.HostFunctionCallContext) { + eng.stack[eng.currentStackPointer-1] *= 100 + }, + } + eng.compiledHostFunctions = append(eng.compiledHostFunctions, hostFunction) mem := newMemoryInst() // Call into the function @@ -470,8 +474,8 @@ func TestEngine_exec_callHostFunction(t *testing.T) { ReturnTypes: []wasm.ValueType{wasm.ValueTypeI64}, }, } - eng.hostFunctionIndex[hostFunctionInstance] = 1 - eng.hostFunctions = make([]hostFunction, 2) + eng.compiledHostFunctionIndex[hostFunctionInstance] = 1 + eng.compiledHostFunctions = make([]*compiledHostFunction, 2) err = eng.Compile(hostFunctionInstance) require.NoError(t, err) mem := newMemoryInst() @@ -1188,7 +1192,7 @@ func TestAmd64Builder_handleCall(t *testing.T) { hostFuncInstance := &wasm.FunctionInstance{HostFunction: &hostFuncRefValue} builder.f.ModuleInstance.Functions = make([]*wasm.FunctionInstance, functionIndex+1) builder.f.ModuleInstance.Functions[functionIndex] = hostFuncInstance - eng.hostFunctionIndex[hostFuncInstance] = functionIndex + eng.compiledHostFunctionIndex[hostFuncInstance] = functionIndex // Build codes. builder.initializeReservedRegisters() @@ -1447,3 +1451,33 @@ func TestAmd64Builder_assemble(t *testing.T) { actual := binary.LittleEndian.Uint64(code[2:10]) require.Equal(t, uint64(prog.Pc), actual) } + +func TestAmd64Builder_handleUnreachable(t *testing.T) { + builder := requireNewBuilder(t) + builder.initializeReservedRegisters() + x1Reg := int16(x86.REG_AX) + x2Reg := int16(x86.REG_R10) + builder.locationStack.pushValueOnRegister(x1Reg) + builder.locationStack.pushValueOnRegister(x2Reg) + builder.movConstToRegister(300, x1Reg) + builder.movConstToRegister(51, x2Reg) + builder.handleUnreachable() + + // Assemble. + code, err := builder.assemble() + require.NoError(t, err) + // Run code. + eng := newEngine() + mem := newMemoryInst() + jitcall( + uintptr(unsafe.Pointer(&code[0])), + uintptr(unsafe.Pointer(eng)), + uintptr(unsafe.Pointer(&mem.Buffer[0])), + ) + + // Check the jitCallStatus of engine. + require.Equal(t, jitCallStatusCodeUnreachable, eng.jitCallStatusCode) + // All the values on registers must be written back to stack. + require.Equal(t, uint64(300), eng.stack[0]) + require.Equal(t, uint64(51), eng.stack[1]) +} diff --git a/wasm/jit/testdata/unreachable.wasm b/wasm/jit/testdata/unreachable.wasm new file mode 100644 index 0000000000000000000000000000000000000000..b7340f6a3f9512abfe59c00172a082f1445f5a15 GIT binary patch literal 123 zcmZXNNeaS15CE&EI|QAJf<7T{$aAy}4LKwe$b#R`c=l4%r6_!x1rU=rXk*oBpK>4k x*y44RHGCNTGX-29X51uKq#raZWO^$;hjhk&B)XJHx_=jK_Hm6!mwFHq$_YDg7drp| literal 0 HcmV?d00001 diff --git a/wasm/jit/testdata/unreachable.wat b/wasm/jit/testdata/unreachable.wat new file mode 100644 index 0000000000..86225422eb --- /dev/null +++ b/wasm/jit/testdata/unreachable.wat @@ -0,0 +1,14 @@ +(module + (func $cause_unreachable (export "cause_unreachable") + (call $one) + ) + (func $one + (call $two) + ) + (func $two + (call $three) + ) + (func $three + (unreachable) + ) +) From 52d2434970f4bc201f3008c1364bf2c7dcb28dbf Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Fri, 10 Dec 2021 12:05:19 +0900 Subject: [PATCH 2/7] remove Signed-off-by: Takeshi Yoneda --- wasm/jit/engine_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/wasm/jit/engine_test.go b/wasm/jit/engine_test.go index e956f6344d..b434c4f831 100644 --- a/wasm/jit/engine_test.go +++ b/wasm/jit/engine_test.go @@ -9,7 +9,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/tetratelabs/wazero/wasi" "github.com/tetratelabs/wazero/wasm" ) @@ -33,8 +32,6 @@ func TestEngine_fibonacci(t *testing.T) { require.NoError(t, err) store := wasm.NewStore(NewEngine()) require.NoError(t, err) - err = wasi.NewEnvironment().Register(store) - require.NoError(t, err) err = store.Instantiate(mod, "test") require.NoError(t, err) out, _, err := store.CallFunction("test", "fib", 20) @@ -52,8 +49,6 @@ func TestEngine_unreachable(t *testing.T) { require.NoError(t, err) store := wasm.NewStore(NewEngine()) require.NoError(t, err) - err = wasi.NewEnvironment().Register(store) - require.NoError(t, err) err = store.Instantiate(mod, "test") require.NoError(t, err) _, _, err = store.CallFunction("test", "cause_unreachable") From f32d945bcaf015395a948d135ec0e0e111a39392 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Fri, 10 Dec 2021 12:08:06 +0900 Subject: [PATCH 3/7] Update wasm/jit/engine.go --- wasm/jit/engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/jit/engine.go b/wasm/jit/engine.go index 199f4f63e6..6253332c9d 100644 --- a/wasm/jit/engine.go +++ b/wasm/jit/engine.go @@ -50,7 +50,7 @@ func (e *engine) Call(f *wasm.FunctionInstance, args ...uint64) (returns []uint6 traces = append(traces, fmt.Sprintf("\t%d: %s", counter, top.getFunctionName())) top = top.caller counter++ - // TODO: include DWARF symbols. + // TODO: include DWARF symbols. See #58 } err2, ok := v.(error) if ok { From c2cee035ac34d10db897e3aeae7bb3f6b0c88ad4 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Fri, 10 Dec 2021 12:10:10 +0900 Subject: [PATCH 4/7] format Signed-off-by: Takeshi Yoneda --- wasm/jit/engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/jit/engine.go b/wasm/jit/engine.go index 6253332c9d..053f2d4a8f 100644 --- a/wasm/jit/engine.go +++ b/wasm/jit/engine.go @@ -50,7 +50,7 @@ func (e *engine) Call(f *wasm.FunctionInstance, args ...uint64) (returns []uint6 traces = append(traces, fmt.Sprintf("\t%d: %s", counter, top.getFunctionName())) top = top.caller counter++ - // TODO: include DWARF symbols. See #58 + // TODO: include DWARF symbols. See #58 } err2, ok := v.(error) if ok { From 1895995a2fcb61399e42c50bff84acd3560c8cc7 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Fri, 10 Dec 2021 13:22:07 +0900 Subject: [PATCH 5/7] reviews Signed-off-by: Takeshi Yoneda --- wasm/jit/engine.go | 15 ++++++++------- wasm/jit/jit_amd64.go | 12 ++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/wasm/jit/engine.go b/wasm/jit/engine.go index 053f2d4a8f..f2e9e5cb14 100644 --- a/wasm/jit/engine.go +++ b/wasm/jit/engine.go @@ -41,13 +41,14 @@ type engine struct { func (e *engine) Call(f *wasm.FunctionInstance, args ...uint64) (returns []uint64, err error) { prevFrame := e.callFrameStack + // We ensure that this function never panics. defer func() { if v := recover(); v != nil { top := e.callFrameStack - var traces []string + var frames []string var counter int for top != prevFrame { - traces = append(traces, fmt.Sprintf("\t%d: %s", counter, top.getFunctionName())) + frames = append(frames, fmt.Sprintf("\t%d: %s", counter, top.getFunctionName())) top = top.caller counter++ // TODO: include DWARF symbols. See #58 @@ -59,8 +60,8 @@ func (e *engine) Call(f *wasm.FunctionInstance, args ...uint64) (returns []uint6 err = fmt.Errorf("wasm runtime error: %v", v) } - if len(traces) > 0 { - err = fmt.Errorf("%w\nwasm backtrace:\n%s", err, strings.Join(traces, "\n")) + if len(frames) > 0 { + err = fmt.Errorf("%w\nwasm backtrace:\n%s", err, strings.Join(frames, "\n")) } } }() @@ -268,7 +269,7 @@ func (c *callFrame) String() string { func (c *callFrame) getFunctionName() string { if c.wasmFunction != nil { - return c.wasmFunction.originalFunctionInstance.Name + return c.wasmFunction.source.Name } else { return c.hostFunction.name } @@ -280,8 +281,8 @@ type compiledHostFunction = struct { } type compiledWasmFunction struct { - // FunctionInstance from which this is compiled. - originalFunctionInstance *wasm.FunctionInstance + // The source function instance from which this is compiled. + source *wasm.FunctionInstance // inputs,returns represents the number of input/returns of function. inputs, returns uint64 // codeSegment is holding the compiled native code as a byte slice. diff --git a/wasm/jit/jit_amd64.go b/wasm/jit/jit_amd64.go index fdec979d19..c1abf74678 100644 --- a/wasm/jit/jit_amd64.go +++ b/wasm/jit/jit_amd64.go @@ -224,12 +224,12 @@ func (e *engine) compileWasmFunction(f *wasm.FunctionInstance) (*compiledWasmFun func (b *amd64Builder) newCompiledWasmFunction(code []byte) *compiledWasmFunction { cf := &compiledWasmFunction{ - originalFunctionInstance: b.f, - codeSegment: code, - inputs: uint64(len(b.f.Signature.InputTypes)), - returns: uint64(len(b.f.Signature.ReturnTypes)), - memory: b.f.ModuleInstance.Memory, - maxStackPointer: b.locationStack.maxStackPointer, + source: b.f, + codeSegment: code, + inputs: uint64(len(b.f.Signature.InputTypes)), + returns: uint64(len(b.f.Signature.ReturnTypes)), + memory: b.f.ModuleInstance.Memory, + maxStackPointer: b.locationStack.maxStackPointer, } if cf.memory != nil { cf.memoryAddress = uintptr(unsafe.Pointer(&cf.memory.Buffer[0])) From 4b146e71d57c5edb66dc117e49d8315ec59f6173 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Fri, 10 Dec 2021 13:24:15 +0900 Subject: [PATCH 6/7] more comment Signed-off-by: Takeshi Yoneda --- wasm/jit/engine.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wasm/jit/engine.go b/wasm/jit/engine.go index f2e9e5cb14..4666d681da 100644 --- a/wasm/jit/engine.go +++ b/wasm/jit/engine.go @@ -41,7 +41,10 @@ type engine struct { func (e *engine) Call(f *wasm.FunctionInstance, args ...uint64) (returns []uint64, err error) { prevFrame := e.callFrameStack - // We ensure that this function never panics. + // We ensure that this Call method never panics as + // this is Call method indirectly invoked by embedders via store.CallFunction, + // and we have to make sure that all the runtime errors, including the one happening inside + // host functions, will be capatured as errors, not panics. defer func() { if v := recover(); v != nil { top := e.callFrameStack From 7d5bb30dcaaa18f5e61ff7e96f045e769542e2af Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Fri, 10 Dec 2021 13:24:25 +0900 Subject: [PATCH 7/7] more comment Signed-off-by: Takeshi Yoneda --- wasm/jit/engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/jit/engine.go b/wasm/jit/engine.go index 4666d681da..bad7e388d3 100644 --- a/wasm/jit/engine.go +++ b/wasm/jit/engine.go @@ -42,7 +42,7 @@ type engine struct { func (e *engine) Call(f *wasm.FunctionInstance, args ...uint64) (returns []uint64, err error) { prevFrame := e.callFrameStack // We ensure that this Call method never panics as - // this is Call method indirectly invoked by embedders via store.CallFunction, + // this Call method is indirectly invoked by embedders via store.CallFunction, // and we have to make sure that all the runtime errors, including the one happening inside // host functions, will be capatured as errors, not panics. defer func() {