github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/wazevo/call_engine.go (about)

     1  package wazevo
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"reflect"
     8  	"unsafe"
     9  
    10  	"github.com/wasilibs/wazerox/api"
    11  	"github.com/wasilibs/wazerox/experimental"
    12  	"github.com/wasilibs/wazerox/internal/engine/wazevo/wazevoapi"
    13  	"github.com/wasilibs/wazerox/internal/internalapi"
    14  	"github.com/wasilibs/wazerox/internal/wasm"
    15  	"github.com/wasilibs/wazerox/internal/wasmdebug"
    16  	"github.com/wasilibs/wazerox/internal/wasmruntime"
    17  )
    18  
    19  type (
    20  	// callEngine implements api.Function.
    21  	callEngine struct {
    22  		internalapi.WazeroOnly
    23  		stack []byte
    24  		// stackTop is the pointer to the *aligned* top of the stack. This must be updated
    25  		// whenever the stack is changed. This is passed to the assembly function
    26  		// at the very beginning of api.Function Call/CallWithStack.
    27  		stackTop uintptr
    28  		// executable is the pointer to the executable code for this function.
    29  		executable         *byte
    30  		preambleExecutable *byte
    31  		// parent is the *moduleEngine from which this callEngine is created.
    32  		parent *moduleEngine
    33  		// indexInModule is the index of the function in the module.
    34  		indexInModule wasm.Index
    35  		// sizeOfParamResultSlice is the size of the parameter/result slice.
    36  		sizeOfParamResultSlice int
    37  		requiredParams         int
    38  		// execCtx holds various information to be read/written by assembly functions.
    39  		execCtx executionContext
    40  		// execCtxPtr holds the pointer to the executionContext which doesn't change after callEngine is created.
    41  		execCtxPtr        uintptr
    42  		numberOfResults   int
    43  		stackIteratorImpl stackIterator
    44  	}
    45  
    46  	// executionContext is the struct to be read/written by assembly functions.
    47  	executionContext struct {
    48  		// exitCode holds the wazevoapi.ExitCode describing the state of the function execution.
    49  		exitCode wazevoapi.ExitCode
    50  		// callerModuleContextPtr holds the moduleContextOpaque for Go function calls.
    51  		callerModuleContextPtr *byte
    52  		// originalFramePointer holds the original frame pointer of the caller of the assembly function.
    53  		originalFramePointer uintptr
    54  		// originalStackPointer holds the original stack pointer of the caller of the assembly function.
    55  		originalStackPointer uintptr
    56  		// goReturnAddress holds the return address to go back to the caller of the assembly function.
    57  		goReturnAddress uintptr
    58  		// stackBottomPtr holds the pointer to the bottom of the stack.
    59  		stackBottomPtr *byte
    60  		// goCallReturnAddress holds the return address to go back to the caller of the Go function.
    61  		goCallReturnAddress *byte
    62  		// stackPointerBeforeGoCall holds the stack pointer before calling a Go function.
    63  		stackPointerBeforeGoCall *uint64
    64  		// stackGrowRequiredSize holds the required size of stack grow.
    65  		stackGrowRequiredSize uintptr
    66  		// memoryGrowTrampolineAddress holds the address of memory grow trampoline function.
    67  		memoryGrowTrampolineAddress *byte
    68  		// stackGrowCallTrampolineAddress holds the address of stack grow trampoline function.
    69  		stackGrowCallTrampolineAddress *byte
    70  		// checkModuleExitCodeTrampolineAddress holds the address of check-module-exit-code function.
    71  		checkModuleExitCodeTrampolineAddress *byte
    72  		// savedRegisters is the opaque spaces for save/restore registers.
    73  		// We want to align 16 bytes for each register, so we use [64][2]uint64.
    74  		savedRegisters [64][2]uint64
    75  		// goFunctionCallCalleeModuleContextOpaque is the pointer to the target Go function's moduleContextOpaque.
    76  		goFunctionCallCalleeModuleContextOpaque uintptr
    77  		// tableGrowTrampolineAddress holds the address of table grow trampoline function.
    78  		tableGrowTrampolineAddress *byte
    79  		// refFuncTrampolineAddress holds the address of ref-func trampoline function.
    80  		refFuncTrampolineAddress *byte
    81  		// memmoveAddress holds the address of memmove function implemented by Go runtime. See memmove.go.
    82  		memmoveAddress uintptr
    83  	}
    84  )
    85  
    86  func (c *callEngine) requiredInitialStackSize() int {
    87  	const initialStackSizeDefault = 512
    88  	stackSize := initialStackSizeDefault
    89  	paramResultInBytes := c.sizeOfParamResultSlice * 8 * 2 // * 8 because uint64 is 8 bytes, and *2 because we need both separated param/result slots.
    90  	required := paramResultInBytes + 32 + 16               // 32 is enough to accommodate the call frame info, and 16 exists just in case when []byte is not aligned to 16 bytes.
    91  	if required > stackSize {
    92  		stackSize = required
    93  	}
    94  	return stackSize
    95  }
    96  
    97  func (c *callEngine) init() {
    98  	stackSize := c.requiredInitialStackSize()
    99  	if wazevoapi.StackGuardCheckEnabled {
   100  		stackSize += wazevoapi.StackGuardCheckGuardPageSize
   101  	}
   102  	c.stack = make([]byte, stackSize)
   103  	c.stackTop = alignedStackTop(c.stack)
   104  	if wazevoapi.StackGuardCheckEnabled {
   105  		c.execCtx.stackBottomPtr = &c.stack[wazevoapi.StackGuardCheckGuardPageSize]
   106  	} else {
   107  		c.execCtx.stackBottomPtr = &c.stack[0]
   108  	}
   109  	c.execCtxPtr = uintptr(unsafe.Pointer(&c.execCtx))
   110  }
   111  
   112  // alignedStackTop returns 16-bytes aligned stack top of given stack.
   113  // 16 bytes should be good for all platform (arm64/amd64).
   114  func alignedStackTop(s []byte) uintptr {
   115  	stackAddr := uintptr(unsafe.Pointer(&s[len(s)-1]))
   116  	return stackAddr - (stackAddr & (16 - 1))
   117  }
   118  
   119  // Definition implements api.Function.
   120  func (c *callEngine) Definition() api.FunctionDefinition {
   121  	return c.parent.module.Source.FunctionDefinition(c.indexInModule)
   122  }
   123  
   124  // Call implements api.Function.
   125  func (c *callEngine) Call(ctx context.Context, params ...uint64) ([]uint64, error) {
   126  	if c.requiredParams != len(params) {
   127  		return nil, fmt.Errorf("expected %d params, but passed %d", c.requiredParams, len(params))
   128  	}
   129  	paramResultSlice := make([]uint64, c.sizeOfParamResultSlice)
   130  	copy(paramResultSlice, params)
   131  	if err := c.callWithStack(ctx, paramResultSlice); err != nil {
   132  		return nil, err
   133  	}
   134  	return paramResultSlice[:c.numberOfResults], nil
   135  }
   136  
   137  func (c *callEngine) addFrame(builder wasmdebug.ErrorBuilder, addr uintptr) (def api.FunctionDefinition, listener experimental.FunctionListener) {
   138  	eng := c.parent.parent.parent
   139  	cm := eng.compiledModuleOfAddr(addr)
   140  	if cm != nil {
   141  		index := cm.functionIndexOf(addr)
   142  		def = cm.module.FunctionDefinition(cm.module.ImportFunctionCount + index)
   143  		var sources []string
   144  		if dw := cm.module.DWARFLines; dw != nil {
   145  			sourceOffset := cm.getSourceOffset(addr)
   146  			sources = dw.Line(sourceOffset)
   147  		}
   148  		builder.AddFrame(def.DebugName(), def.ParamTypes(), def.ResultTypes(), sources)
   149  		if len(cm.listeners) > 0 {
   150  			listener = cm.listeners[index]
   151  		}
   152  	}
   153  	return
   154  }
   155  
   156  // CallWithStack implements api.Function.
   157  func (c *callEngine) CallWithStack(ctx context.Context, paramResultStack []uint64) (err error) {
   158  	if c.sizeOfParamResultSlice > len(paramResultStack) {
   159  		return fmt.Errorf("need %d params, but stack size is %d", c.sizeOfParamResultSlice, len(paramResultStack))
   160  	}
   161  	return c.callWithStack(ctx, paramResultStack)
   162  }
   163  
   164  // CallWithStack implements api.Function.
   165  func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint64) (err error) {
   166  	if wazevoapi.StackGuardCheckEnabled {
   167  		defer func() {
   168  			wazevoapi.CheckStackGuardPage(c.stack)
   169  		}()
   170  	}
   171  
   172  	p := c.parent
   173  	ensureTermination := p.parent.ensureTermination
   174  	m := p.module
   175  	if ensureTermination {
   176  		select {
   177  		case <-ctx.Done():
   178  			// If the provided context is already done, close the module and return the error.
   179  			m.CloseWithCtxErr(ctx)
   180  			return m.FailIfClosed()
   181  		default:
   182  		}
   183  	}
   184  
   185  	var paramResultPtr *uint64
   186  	if len(paramResultStack) > 0 {
   187  		paramResultPtr = &paramResultStack[0]
   188  	}
   189  	defer func() {
   190  		if r := recover(); r != nil {
   191  			type listenerForAbort struct {
   192  				def api.FunctionDefinition
   193  				lsn experimental.FunctionListener
   194  			}
   195  
   196  			var listeners []listenerForAbort
   197  			builder := wasmdebug.NewErrorBuilder()
   198  			def, lsn := c.addFrame(builder, uintptr(unsafe.Pointer(c.execCtx.goCallReturnAddress)))
   199  			if lsn != nil {
   200  				listeners = append(listeners, listenerForAbort{def, lsn})
   201  			}
   202  			returnAddrs := unwindStack(uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)), c.stackTop, nil)
   203  			for _, retAddr := range returnAddrs[:len(returnAddrs)-1] { // the last return addr is the trampoline, so we skip it.
   204  				def, lsn = c.addFrame(builder, retAddr)
   205  				if lsn != nil {
   206  					listeners = append(listeners, listenerForAbort{def, lsn})
   207  				}
   208  			}
   209  			err = builder.FromRecovered(r)
   210  
   211  			for _, lsn := range listeners {
   212  				lsn.lsn.Abort(ctx, m, lsn.def, err)
   213  			}
   214  		} else {
   215  			if err != wasmruntime.ErrRuntimeStackOverflow { // Stackoverflow case shouldn't be panic (to avoid extreme stack unwinding).
   216  				err = c.parent.module.FailIfClosed()
   217  			}
   218  		}
   219  
   220  		if err != nil {
   221  			// Ensures that we can reuse this callEngine even after an error.
   222  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   223  		}
   224  	}()
   225  
   226  	if ensureTermination {
   227  		done := m.CloseModuleOnCanceledOrTimeout(ctx)
   228  		defer done()
   229  	}
   230  
   231  	entrypoint(c.preambleExecutable, c.executable, c.execCtxPtr, c.parent.opaquePtr, paramResultPtr, c.stackTop)
   232  	for {
   233  		switch ec := c.execCtx.exitCode; ec & wazevoapi.ExitCodeMask {
   234  		case wazevoapi.ExitCodeOK:
   235  			return nil
   236  		case wazevoapi.ExitCodeGrowStack:
   237  			var newsp uintptr
   238  			if wazevoapi.StackGuardCheckEnabled {
   239  				newsp, err = c.growStackWithGuarded()
   240  			} else {
   241  				newsp, err = c.growStack()
   242  			}
   243  			if err != nil {
   244  				return err
   245  			}
   246  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   247  			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, newsp)
   248  		case wazevoapi.ExitCodeGrowMemory:
   249  			mod := c.callerModuleInstance()
   250  			mem := mod.MemoryInstance
   251  			s := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
   252  			argRes := &s[0]
   253  			if res, ok := mem.Grow(uint32(*argRes)); !ok {
   254  				*argRes = uint64(0xffffffff) // = -1 in signed 32-bit integer.
   255  			} else {
   256  				*argRes = uint64(res)
   257  				calleeOpaque := opaqueViewFromPtr(uintptr(unsafe.Pointer(c.execCtx.callerModuleContextPtr)))
   258  				if mod.Source.MemorySection != nil { // Local memory.
   259  					putLocalMemory(calleeOpaque, 8 /* local memory begins at 8 */, mem)
   260  				} else {
   261  					// Imported memory's owner at offset 16 of the callerModuleContextPtr.
   262  					opaquePtr := uintptr(binary.LittleEndian.Uint64(calleeOpaque[16:]))
   263  					importedMemOwner := opaqueViewFromPtr(opaquePtr)
   264  					putLocalMemory(importedMemOwner, 8 /* local memory begins at 8 */, mem)
   265  				}
   266  			}
   267  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   268  			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
   269  		case wazevoapi.ExitCodeTableGrow:
   270  			mod := c.callerModuleInstance()
   271  			s := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
   272  			tableIndex, num, ref := s[0], uint32(s[1]), uintptr(s[2])
   273  			table := mod.Tables[tableIndex]
   274  			s[0] = uint64(uint32(int32(table.Grow(num, ref))))
   275  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   276  			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
   277  		case wazevoapi.ExitCodeCallGoFunction:
   278  			index := wazevoapi.GoFunctionIndexFromExitCode(ec)
   279  			f := hostModuleGoFuncFromOpaque[api.GoFunction](index, c.execCtx.goFunctionCallCalleeModuleContextOpaque)
   280  			f.Call(ctx, goCallStackView(c.execCtx.stackPointerBeforeGoCall))
   281  			// Back to the native code.
   282  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   283  			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
   284  		case wazevoapi.ExitCodeCallGoFunctionWithListener:
   285  			index := wazevoapi.GoFunctionIndexFromExitCode(ec)
   286  			f := hostModuleGoFuncFromOpaque[api.GoFunction](index, c.execCtx.goFunctionCallCalleeModuleContextOpaque)
   287  			listeners := hostModuleListenersSliceFromOpaque(c.execCtx.goFunctionCallCalleeModuleContextOpaque)
   288  			s := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
   289  			// Call Listener.Before.
   290  			callerModule := c.callerModuleInstance()
   291  			listener := listeners[index]
   292  			hostModule := hostModuleFromOpaque(c.execCtx.goFunctionCallCalleeModuleContextOpaque)
   293  			def := hostModule.FunctionDefinition(wasm.Index(index))
   294  			listener.Before(ctx, callerModule, def, s, c.stackIterator(true))
   295  			// Call into the Go function.
   296  			f.Call(ctx, s)
   297  			// Call Listener.After.
   298  			listener.After(ctx, callerModule, def, s)
   299  			// Back to the native code.
   300  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   301  			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
   302  		case wazevoapi.ExitCodeCallGoModuleFunction:
   303  			index := wazevoapi.GoFunctionIndexFromExitCode(ec)
   304  			f := hostModuleGoFuncFromOpaque[api.GoModuleFunction](index, c.execCtx.goFunctionCallCalleeModuleContextOpaque)
   305  			mod := c.callerModuleInstance()
   306  			f.Call(ctx, mod, goCallStackView(c.execCtx.stackPointerBeforeGoCall))
   307  			// Back to the native code.
   308  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   309  			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
   310  		case wazevoapi.ExitCodeCallGoModuleFunctionWithListener:
   311  			index := wazevoapi.GoFunctionIndexFromExitCode(ec)
   312  			f := hostModuleGoFuncFromOpaque[api.GoModuleFunction](index, c.execCtx.goFunctionCallCalleeModuleContextOpaque)
   313  			listeners := hostModuleListenersSliceFromOpaque(c.execCtx.goFunctionCallCalleeModuleContextOpaque)
   314  			s := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
   315  			// Call Listener.Before.
   316  			callerModule := c.callerModuleInstance()
   317  			listener := listeners[index]
   318  			hostModule := hostModuleFromOpaque(c.execCtx.goFunctionCallCalleeModuleContextOpaque)
   319  			def := hostModule.FunctionDefinition(wasm.Index(index))
   320  			listener.Before(ctx, callerModule, def, s, c.stackIterator(true))
   321  			// Call into the Go function.
   322  			f.Call(ctx, callerModule, s)
   323  			// Call Listener.After.
   324  			listener.After(ctx, callerModule, def, s)
   325  			// Back to the native code.
   326  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   327  			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
   328  		case wazevoapi.ExitCodeCallListenerBefore:
   329  			stack := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
   330  			index := stack[0]
   331  			mod := c.callerModuleInstance()
   332  			listener := mod.Engine.(*moduleEngine).listeners[index]
   333  			def := mod.Source.FunctionDefinition(wasm.Index(index))
   334  			listener.Before(ctx, mod, def, stack[1:], c.stackIterator(false))
   335  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   336  			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
   337  		case wazevoapi.ExitCodeCallListenerAfter:
   338  			stack := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
   339  			index := stack[0]
   340  			mod := c.callerModuleInstance()
   341  			listener := mod.Engine.(*moduleEngine).listeners[index]
   342  			def := mod.Source.FunctionDefinition(wasm.Index(index))
   343  			listener.After(ctx, mod, def, stack[1:])
   344  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   345  			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
   346  		case wazevoapi.ExitCodeCheckModuleExitCode:
   347  			// Note: this operation must be done in Go, not native code. The reason is that
   348  			// native code cannot be preempted and that means it can block forever if there are not
   349  			// enough OS threads (which we don't have control over).
   350  			if err := m.FailIfClosed(); err != nil {
   351  				panic(err)
   352  			}
   353  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   354  			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
   355  		case wazevoapi.ExitCodeRefFunc:
   356  			mod := c.callerModuleInstance()
   357  			s := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
   358  			funcIndex := s[0]
   359  			ref := mod.Engine.FunctionInstanceReference(wasm.Index(funcIndex))
   360  			s[0] = uint64(ref)
   361  			c.execCtx.exitCode = wazevoapi.ExitCodeOK
   362  			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
   363  		case wazevoapi.ExitCodeUnreachable:
   364  			panic(wasmruntime.ErrRuntimeUnreachable)
   365  		case wazevoapi.ExitCodeMemoryOutOfBounds:
   366  			panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
   367  		case wazevoapi.ExitCodeTableOutOfBounds:
   368  			panic(wasmruntime.ErrRuntimeInvalidTableAccess)
   369  		case wazevoapi.ExitCodeIndirectCallNullPointer:
   370  			panic(wasmruntime.ErrRuntimeInvalidTableAccess)
   371  		case wazevoapi.ExitCodeIndirectCallTypeMismatch:
   372  			panic(wasmruntime.ErrRuntimeIndirectCallTypeMismatch)
   373  		case wazevoapi.ExitCodeIntegerOverflow:
   374  			panic(wasmruntime.ErrRuntimeIntegerOverflow)
   375  		case wazevoapi.ExitCodeIntegerDivisionByZero:
   376  			panic(wasmruntime.ErrRuntimeIntegerDivideByZero)
   377  		case wazevoapi.ExitCodeInvalidConversionToInteger:
   378  			panic(wasmruntime.ErrRuntimeInvalidConversionToInteger)
   379  		default:
   380  			panic("BUG")
   381  		}
   382  	}
   383  }
   384  
   385  func (c *callEngine) callerModuleInstance() *wasm.ModuleInstance {
   386  	return moduleInstanceFromOpaquePtr(c.execCtx.callerModuleContextPtr)
   387  }
   388  
   389  func opaqueViewFromPtr(ptr uintptr) []byte {
   390  	var opaque []byte
   391  	sh := (*reflect.SliceHeader)(unsafe.Pointer(&opaque))
   392  	sh.Data = ptr
   393  	sh.Len = 24
   394  	sh.Cap = 24
   395  	return opaque
   396  }
   397  
   398  const callStackCeiling = uintptr(50000000) // in uint64 (8 bytes) == 400000000 bytes in total == 400mb.
   399  
   400  func (c *callEngine) growStackWithGuarded() (newSP uintptr, err error) {
   401  	if wazevoapi.StackGuardCheckEnabled {
   402  		wazevoapi.CheckStackGuardPage(c.stack)
   403  	}
   404  	newSP, err = c.growStack()
   405  	if err != nil {
   406  		return
   407  	}
   408  	if wazevoapi.StackGuardCheckEnabled {
   409  		c.execCtx.stackBottomPtr = &c.stack[wazevoapi.StackGuardCheckGuardPageSize]
   410  	}
   411  	return
   412  }
   413  
   414  // growStack grows the stack, and returns the new stack pointer.
   415  func (c *callEngine) growStack() (newSP uintptr, err error) {
   416  	currentLen := uintptr(len(c.stack))
   417  	if callStackCeiling < currentLen {
   418  		err = wasmruntime.ErrRuntimeStackOverflow
   419  		return
   420  	}
   421  
   422  	newLen := 2*currentLen + c.execCtx.stackGrowRequiredSize + 16 // Stack might be aligned to 16 bytes, so add 16 bytes just in case.
   423  	newStack := make([]byte, newLen)
   424  
   425  	relSp := c.stackTop - uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))
   426  
   427  	// Copy the existing contents in the previous Go-allocated stack into the new one.
   428  	var prevStackAligned, newStackAligned []byte
   429  	{
   430  		sh := (*reflect.SliceHeader)(unsafe.Pointer(&prevStackAligned))
   431  		sh.Data = c.stackTop - relSp
   432  		sh.Len = int(relSp)
   433  		sh.Cap = int(relSp)
   434  	}
   435  	newTop := alignedStackTop(newStack)
   436  	{
   437  		newSP = newTop - relSp
   438  		sh := (*reflect.SliceHeader)(unsafe.Pointer(&newStackAligned))
   439  		sh.Data = newSP
   440  		sh.Len = int(relSp)
   441  		sh.Cap = int(relSp)
   442  	}
   443  	copy(newStackAligned, prevStackAligned)
   444  
   445  	c.stack = newStack
   446  	c.stackTop = newTop
   447  	c.execCtx.stackBottomPtr = &newStack[0]
   448  	return
   449  }
   450  
   451  func (c *callEngine) stackIterator(onHostCall bool) experimental.StackIterator {
   452  	c.stackIteratorImpl.reset(c, onHostCall)
   453  	return &c.stackIteratorImpl
   454  }
   455  
   456  // stackIterator implements experimental.StackIterator.
   457  type stackIterator struct {
   458  	retAddrs      []uintptr
   459  	retAddrCursor int
   460  	eng           *engine
   461  	pc            uint64
   462  
   463  	currentDef *wasm.FunctionDefinition
   464  }
   465  
   466  func (si *stackIterator) reset(c *callEngine, onHostCall bool) {
   467  	if onHostCall {
   468  		si.retAddrs = append(si.retAddrs[:0], uintptr(unsafe.Pointer(c.execCtx.goCallReturnAddress)))
   469  	} else {
   470  		si.retAddrs = si.retAddrs[:0]
   471  	}
   472  	si.retAddrs = unwindStack(uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)), c.stackTop, si.retAddrs)
   473  	si.retAddrs = si.retAddrs[:len(si.retAddrs)-1] // the last return addr is the trampoline, so we skip it.
   474  	si.retAddrCursor = 0
   475  	si.eng = c.parent.parent.parent
   476  }
   477  
   478  // Next implements the same method as documented on experimental.StackIterator.
   479  func (si *stackIterator) Next() bool {
   480  	if si.retAddrCursor >= len(si.retAddrs) {
   481  		return false
   482  	}
   483  
   484  	addr := si.retAddrs[si.retAddrCursor]
   485  	cm := si.eng.compiledModuleOfAddr(addr)
   486  	if cm != nil {
   487  		index := cm.functionIndexOf(addr)
   488  		def := cm.module.FunctionDefinition(cm.module.ImportFunctionCount + index)
   489  		si.currentDef = def
   490  		si.retAddrCursor++
   491  		si.pc = uint64(addr)
   492  		return true
   493  	}
   494  	return false
   495  }
   496  
   497  // ProgramCounter implements the same method as documented on experimental.StackIterator.
   498  func (si *stackIterator) ProgramCounter() experimental.ProgramCounter {
   499  	return experimental.ProgramCounter(si.pc)
   500  }
   501  
   502  // Function implements the same method as documented on experimental.StackIterator.
   503  func (si *stackIterator) Function() experimental.InternalFunction {
   504  	return si
   505  }
   506  
   507  // Definition implements the same method as documented on experimental.InternalFunction.
   508  func (si *stackIterator) Definition() api.FunctionDefinition {
   509  	return si.currentDef
   510  }
   511  
   512  // SourceOffsetForPC implements the same method as documented on experimental.InternalFunction.
   513  func (si *stackIterator) SourceOffsetForPC(pc experimental.ProgramCounter) uint64 {
   514  	upc := uintptr(pc)
   515  	cm := si.eng.compiledModuleOfAddr(upc)
   516  	return cm.getSourceOffset(upc)
   517  }