github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/engine/compiler/engine.go (about)

     1  package compiler
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  	"runtime"
     9  	"sort"
    10  	"sync"
    11  	"unsafe"
    12  
    13  	"github.com/bananabytelabs/wazero/api"
    14  	"github.com/bananabytelabs/wazero/experimental"
    15  	"github.com/bananabytelabs/wazero/internal/asm"
    16  	"github.com/bananabytelabs/wazero/internal/bitpack"
    17  	"github.com/bananabytelabs/wazero/internal/filecache"
    18  	"github.com/bananabytelabs/wazero/internal/internalapi"
    19  	"github.com/bananabytelabs/wazero/internal/platform"
    20  	"github.com/bananabytelabs/wazero/internal/version"
    21  	"github.com/bananabytelabs/wazero/internal/wasm"
    22  	"github.com/bananabytelabs/wazero/internal/wasmdebug"
    23  	"github.com/bananabytelabs/wazero/internal/wasmruntime"
    24  	"github.com/bananabytelabs/wazero/internal/wazeroir"
    25  )
    26  
    27  // NOTE: The offset of many of the struct fields defined here are referenced from
    28  // assembly using the constants below such as moduleEngineFunctionsOffset.
    29  // If changing a struct, update the constant and associated tests as needed.
    30  type (
    31  	// engine is a Compiler implementation of wasm.Engine
    32  	engine struct {
    33  		enabledFeatures api.CoreFeatures
    34  		codes           map[wasm.ModuleID]*compiledModule // guarded by mutex.
    35  		fileCache       filecache.Cache
    36  		mux             sync.RWMutex
    37  		// setFinalizer defaults to runtime.SetFinalizer, but overridable for tests.
    38  		setFinalizer  func(obj interface{}, finalizer interface{})
    39  		wazeroVersion string
    40  	}
    41  
    42  	// moduleEngine implements wasm.ModuleEngine
    43  	moduleEngine struct {
    44  		// See note at top of file before modifying this struct.
    45  
    46  		// functions are the functions in a module instances.
    47  		// The index is module instance-scoped. We intentionally avoid using map
    48  		// as the underlying memory region is accessed by assembly directly by using
    49  		// codesElement0Address.
    50  		functions []function
    51  
    52  		// Keep a reference to the compiled module to prevent the GC from reclaiming
    53  		// it while the code may still be needed.
    54  		module *compiledModule
    55  	}
    56  
    57  	// callEngine holds context per moduleEngine.Call, and shared across all the
    58  	// function calls originating from the same moduleEngine.Call execution.
    59  	//
    60  	// This implements api.Function.
    61  	callEngine struct {
    62  		internalapi.WazeroOnlyType
    63  
    64  		// See note at top of file before modifying this struct.
    65  
    66  		// These contexts are read and written by compiled code.
    67  		// Note: structs are embedded to reduce the costs to access fields inside them. Also, this eases field offset
    68  		// calculation.
    69  		moduleContext
    70  		stackContext
    71  		exitContext
    72  		archContext
    73  
    74  		// The following fields are not accessed by compiled code directly.
    75  
    76  		// stack is the go-allocated stack for holding values and call frames.
    77  		// Note: We never edit len or cap in compiled code, so we won't get screwed when GC comes in.
    78  		//
    79  		// At any point of execution, say currently executing function F2 which was called by F1, then
    80  		// the stack should look like like:
    81  		//
    82  		// 	[..., arg0, arg1, ..., argN, _, _, _, v1, v2, v3, ....
    83  		//	      ^                     {       }
    84  		//	      |                F1's callFrame
    85  		//	      |
    86  		//  stackBasePointer
    87  		//
    88  		// where
    89  		//  - callFrame is the F1's callFrame which called F2. It contains F1's return address, F1's base pointer, and F1's *function.
    90  		//  - stackBasePointer is the stack base pointer stored at (callEngine stackContext.stackBasePointerInBytes)
    91  		//  - arg0, ..., argN are the function parameters, and v1, v2, v3,... are the local variables
    92  		//    including the non-function param locals as well as the temporary variable produced by instructions (e.g i32.const).
    93  		//
    94  		// If the F2 makes a function call to F3 which takes two arguments, then the stack will become:
    95  		//
    96  		// 	[..., arg0, arg1, ..., argN, _, _, _, v1, v2, v3, _, _, _
    97  		//	                            {       }     ^      {       }
    98  		//	                       F1's callFrame     | F2's callFrame
    99  		//	                                          |
   100  		//                                     stackBasePointer
   101  		// where
   102  		// 	- F2's callFrame is pushed above the v2 and v3 (arguments for F3).
   103  		//  - The previous stackBasePointer (pointed at arg0) was saved inside the F2's callFrame.
   104  		//
   105  		// Then, if F3 returns one result, say w1, then the result will look like:
   106  		//
   107  		// 	[..., arg0, arg1, ..., argN, _, _, _, v1, w1, ...
   108  		//	      ^                     {       }
   109  		//	      |                F1's callFrame
   110  		//	      |
   111  		//  stackBasePointer
   112  		//
   113  		// where
   114  		// 	- stackBasePointer was reverted to the position at arg0
   115  		//  - The result from F3 was pushed above v1
   116  		//
   117  		// If the number of parameters is smaller than that of return values, then the empty slots are reserved
   118  		// below the callFrame to store the results on teh return.
   119  		// For example, if F3 takes no parameter but returns N(>0) results, then the stack
   120  		// after making a call against F3 will look like:
   121  		//
   122  		// 	[..., arg0, arg1, ..., argN, _, _, _, v1, v2, v3, res_1, _, res_N, _, _, _
   123  		//	                            {       }            ^                {       }
   124  		//	                       F1's callFrame            |           F2's callFrame
   125  		//	                                                 |
   126  		//                                            stackBasePointer
   127  		// where res_1, ..., res_N are the reserved slots below the call frame. In general,
   128  		// the number of reserved slots equals max(0, len(results)-len(params).
   129  		//
   130  		// This reserved slots are necessary to save the result values onto the stack while not destroying
   131  		// the callFrame value on function returns.
   132  		stack []uint64
   133  
   134  		// initialFn is the initial function for this call engine.
   135  		initialFn *function
   136  
   137  		// Keep a reference to the compiled module to prevent the GC from reclaiming
   138  		// it while the code may still be needed.
   139  		module *compiledModule
   140  
   141  		// stackIterator provides a way to iterate over the stack for Listeners.
   142  		// It is setup and valid only during a call to a Listener hook.
   143  		stackIterator stackIterator
   144  	}
   145  
   146  	// moduleContext holds the per-function call specific module information.
   147  	// This is subject to be manipulated from compiled native code whenever we make function calls.
   148  	moduleContext struct {
   149  		// See note at top of file before modifying this struct.
   150  
   151  		// fn holds the currently executed *function.
   152  		fn *function
   153  
   154  		// moduleInstance is the address of module instance from which we initialize
   155  		// the following fields. This is set whenever we enter a function or return from function calls.
   156  		//
   157  		// On the entry to the native code, this must be initialized to zero to let native code preamble know
   158  		// that this is the initial function call (which leads to moduleContext initialization pass).
   159  		moduleInstance *wasm.ModuleInstance //lint:ignore U1000 This is only used by Compiler code.
   160  
   161  		// globalElement0Address is the address of the first element in the global slice,
   162  		// i.e. &ModuleInstance.Globals[0] as uintptr.
   163  		globalElement0Address uintptr
   164  		// memoryElement0Address is the address of the first element in the global slice,
   165  		// i.e. &ModuleInstance.Memory.Buffer[0] as uintptr.
   166  		memoryElement0Address uintptr
   167  		// memorySliceLen is the length of the memory buffer, i.e. len(ModuleInstance.Memory.Buffer).
   168  		memorySliceLen uint64
   169  		// memoryInstance holds the memory instance for this module instance.
   170  		memoryInstance *wasm.MemoryInstance
   171  		// tableElement0Address is the address of the first item in the tables slice,
   172  		// i.e. &ModuleInstance.Tables[0] as uintptr.
   173  		tablesElement0Address uintptr
   174  
   175  		// functionsElement0Address is &moduleContext.functions[0] as uintptr.
   176  		functionsElement0Address uintptr
   177  
   178  		// typeIDsElement0Address holds the &ModuleInstance.TypeIDs[0] as uintptr.
   179  		typeIDsElement0Address uintptr
   180  
   181  		// dataInstancesElement0Address holds the &ModuleInstance.DataInstances[0] as uintptr.
   182  		dataInstancesElement0Address uintptr
   183  
   184  		// elementInstancesElement0Address holds the &ModuleInstance.ElementInstances[0] as uintptr.
   185  		elementInstancesElement0Address uintptr
   186  	}
   187  
   188  	// stackContext stores the data to access engine.stack.
   189  	stackContext struct {
   190  		// See note at top of file before modifying this struct.
   191  
   192  		// stackPointer on .stack field which is accessed by stack[stackBasePointer+stackBasePointerInBytes*8].
   193  		//
   194  		// Note: stackPointer is not used in assembly since the native code knows exact position of
   195  		// each variable in the value stack from the info from compilation.
   196  		// Therefore, only updated when native code exit from the Compiler world and go back to the Go function.
   197  		stackPointer uint64
   198  
   199  		// stackBasePointerInBytes is updated whenever we make function calls.
   200  		// Background: Functions might be compiled as if they use the stack from the bottom.
   201  		// However, in reality, they have to use it from the middle of the stack depending on
   202  		// when these function calls are made. So instead of accessing stack via stackPointer alone,
   203  		// functions are compiled, so they access the stack via [stackBasePointer](fixed for entire function) + [stackPointer].
   204  		// More precisely, stackBasePointer is set to [callee's stack pointer] + [callee's stack base pointer] - [caller's params].
   205  		// This way, compiled functions can be independent of the timing of functions calls made against them.
   206  		stackBasePointerInBytes uint64
   207  
   208  		// stackElement0Address is &engine.stack[0] as uintptr.
   209  		// Note: this is updated when growing the stack in builtinFunctionGrowStack.
   210  		stackElement0Address uintptr
   211  
   212  		// stackLenInBytes is len(engine.stack[0]) * 8 (bytes).
   213  		// Note: this is updated when growing the stack in builtinFunctionGrowStack.
   214  		stackLenInBytes uint64
   215  	}
   216  
   217  	// exitContext will be manipulated whenever compiled native code returns into the Go function.
   218  	exitContext struct {
   219  		// See note at top of file before modifying this struct.
   220  
   221  		// Where we store the status code of Compiler execution.
   222  		statusCode nativeCallStatusCode
   223  
   224  		// Set when statusCode == compilerStatusCallBuiltInFunction
   225  		// Indicating the function call index.
   226  		builtinFunctionCallIndex wasm.Index
   227  
   228  		// returnAddress is the return address which the engine jumps into
   229  		// after executing a builtin function or host function.
   230  		returnAddress uintptr
   231  
   232  		// callerModuleInstance holds the caller's wasm.ModuleInstance, and is only valid if currently executing a host function.
   233  		callerModuleInstance *wasm.ModuleInstance
   234  	}
   235  
   236  	// callFrame holds the information to which the caller function can return.
   237  	// This is mixed in callEngine.stack with other Wasm values just like any other
   238  	// native program (where the stack is the system stack though), and we retrieve the struct
   239  	// with unsafe pointer casts.
   240  	callFrame struct {
   241  		// See note at top of file before modifying this struct.
   242  
   243  		// returnAddress is the return address to which the engine jumps when the callee function returns.
   244  		returnAddress uintptr
   245  		// returnStackBasePointerInBytes is the stack base pointer to set on stackContext.stackBasePointerInBytes
   246  		// when the callee function returns.
   247  		returnStackBasePointerInBytes uint64
   248  		// function is the caller *function, and is used to retrieve the stack trace.
   249  		// Note: should be possible to revive *function from returnAddress, but might be costly.
   250  		function *function
   251  	}
   252  
   253  	// Function corresponds to function instance in Wasm, and is created from `code`.
   254  	function struct {
   255  		// See note at top of file before modifying this struct.
   256  
   257  		// codeInitialAddress is the pre-calculated pointer pointing to the initial byte of .codeSegment slice.
   258  		// That mean codeInitialAddress always equals uintptr(unsafe.Pointer(&.codeSegment[0]))
   259  		// and we cache the value (uintptr(unsafe.Pointer(&.codeSegment[0]))) to this field,
   260  		// so we don't need to repeat the calculation on each function call.
   261  		codeInitialAddress uintptr
   262  		// moduleInstance holds the address of source.ModuleInstance.
   263  		moduleInstance *wasm.ModuleInstance
   264  		// typeID is the corresponding wasm.FunctionTypeID for funcType.
   265  		typeID wasm.FunctionTypeID
   266  		// funcType is the function type for this function. Created during compilation.
   267  		funcType *wasm.FunctionType
   268  		// parent holds code from which this is created.
   269  		parent *compiledFunction
   270  	}
   271  
   272  	compiledModule struct {
   273  		// The data that need to be accessed by compiledFunction.parent are
   274  		// separated in an embedded field because we use finalizers to manage
   275  		// the lifecycle of compiledModule instances and having cyclic pointers
   276  		// prevents the Go runtime from calling them, which results in memory
   277  		// leaks since the memory mapped code segments cannot be released.
   278  		//
   279  		// The indirection guarantees that the finalizer set on compiledModule
   280  		// instances can run when all references are gone, and the Go GC can
   281  		// manage to reclaim the compiledCode when all compiledFunction objects
   282  		// referencing it have been freed.
   283  		*compiledCode
   284  		functions []compiledFunction
   285  
   286  		ensureTermination bool
   287  	}
   288  
   289  	compiledCode struct {
   290  		source     *wasm.Module
   291  		executable asm.CodeSegment
   292  	}
   293  
   294  	// compiledFunction corresponds to a function in a module (not instantiated one). This holds the machine code
   295  	// compiled by wazero compiler.
   296  	compiledFunction struct {
   297  		// codeSegment is holding the compiled native code as a byte slice.
   298  		executableOffset uintptr
   299  		// See the doc for codeStaticData type.
   300  		// stackPointerCeil is the max of the stack pointer this function can reach. Lazily applied via maybeGrowStack.
   301  		stackPointerCeil uint64
   302  
   303  		index           wasm.Index
   304  		goFunc          interface{}
   305  		listener        experimental.FunctionListener
   306  		parent          *compiledCode
   307  		sourceOffsetMap sourceOffsetMap
   308  	}
   309  
   310  	// sourceOffsetMap holds the information to retrieve the original offset in
   311  	// the Wasm binary from the offset in the native binary.
   312  	//
   313  	// The fields are implemented as bit-packed arrays of 64 bits integers to
   314  	// reduce the memory footprint. Indexing into such arrays is not as fast as
   315  	// indexing into a simple slice, but the source offset map is intended to be
   316  	// used for debugging, lookups into the arrays should not appear on code
   317  	// paths that are critical to the application performance.
   318  	//
   319  	// The bitpack.OffsetArray fields may be nil, use bitpack.OffsetArrayLen to
   320  	// determine whether they are empty prior to indexing into the arrays to
   321  	// avoid panics caused by accessing nil pointers.
   322  	sourceOffsetMap struct {
   323  		// See note at top of file before modifying this struct.
   324  
   325  		// irOperationOffsetsInNativeBinary is index-correlated with irOperationSourceOffsetsInWasmBinary,
   326  		// and maps each index (corresponding to each IR Operation) to the offset in the compiled native code.
   327  		irOperationOffsetsInNativeBinary bitpack.OffsetArray
   328  		// irOperationSourceOffsetsInWasmBinary is index-correlated with irOperationOffsetsInNativeBinary.
   329  		// See wazeroir.CompilationResult irOperationOffsetsInNativeBinary.
   330  		irOperationSourceOffsetsInWasmBinary bitpack.OffsetArray
   331  	}
   332  
   333  	// functionListenerInvocation captures arguments needed to perform function
   334  	// listener invocations when unwinding the call stack.
   335  	functionListenerInvocation struct {
   336  		experimental.FunctionListener
   337  		def api.FunctionDefinition
   338  	}
   339  )
   340  
   341  // Native code reads/writes Go's structs with the following constants.
   342  // See TestVerifyOffsetValue for how to derive these values.
   343  const (
   344  	// Offsets for moduleEngine.functions
   345  	moduleEngineFunctionsOffset = 0
   346  
   347  	// Offsets for callEngine moduleContext.
   348  	callEngineModuleContextFnOffset                              = 0
   349  	callEngineModuleContextModuleInstanceOffset                  = 8
   350  	callEngineModuleContextGlobalElement0AddressOffset           = 16
   351  	callEngineModuleContextMemoryElement0AddressOffset           = 24
   352  	callEngineModuleContextMemorySliceLenOffset                  = 32
   353  	callEngineModuleContextMemoryInstanceOffset                  = 40
   354  	callEngineModuleContextTablesElement0AddressOffset           = 48
   355  	callEngineModuleContextFunctionsElement0AddressOffset        = 56
   356  	callEngineModuleContextTypeIDsElement0AddressOffset          = 64
   357  	callEngineModuleContextDataInstancesElement0AddressOffset    = 72
   358  	callEngineModuleContextElementInstancesElement0AddressOffset = 80
   359  
   360  	// Offsets for callEngine stackContext.
   361  	callEngineStackContextStackPointerOffset            = 88
   362  	callEngineStackContextStackBasePointerInBytesOffset = 96
   363  	callEngineStackContextStackElement0AddressOffset    = 104
   364  	callEngineStackContextStackLenInBytesOffset         = 112
   365  
   366  	// Offsets for callEngine exitContext.
   367  	callEngineExitContextNativeCallStatusCodeOffset     = 120
   368  	callEngineExitContextBuiltinFunctionCallIndexOffset = 124
   369  	callEngineExitContextReturnAddressOffset            = 128
   370  	callEngineExitContextCallerModuleInstanceOffset     = 136
   371  
   372  	// Offsets for function.
   373  	functionCodeInitialAddressOffset = 0
   374  	functionModuleInstanceOffset     = 8
   375  	functionTypeIDOffset             = 16
   376  	functionSize                     = 40
   377  
   378  	// Offsets for wasm.ModuleInstance.
   379  	moduleInstanceGlobalsOffset          = 24
   380  	moduleInstanceMemoryOffset           = 48
   381  	moduleInstanceTablesOffset           = 56
   382  	moduleInstanceEngineOffset           = 80
   383  	moduleInstanceTypeIDsOffset          = 96
   384  	moduleInstanceDataInstancesOffset    = 120
   385  	moduleInstanceElementInstancesOffset = 144
   386  
   387  	// Offsets for wasm.TableInstance.
   388  	tableInstanceTableOffset    = 0
   389  	tableInstanceTableLenOffset = 8
   390  
   391  	// Offsets for wasm.MemoryInstance.
   392  	memoryInstanceBufferOffset    = 0
   393  	memoryInstanceBufferLenOffset = 8
   394  
   395  	// Offsets for wasm.GlobalInstance.
   396  	globalInstanceValueOffset = 8
   397  
   398  	// Offsets for Go's interface.
   399  	// https://research.swtch.com/interfaces
   400  	// https://github.com/golang/go/blob/release-branch.go1.20/src/runtime/runtime2.go#L207-L210
   401  	interfaceDataOffset = 8
   402  
   403  	// Consts for wasm.DataInstance.
   404  	dataInstanceStructSize = 24
   405  
   406  	// Consts for wasm.ElementInstance.
   407  	elementInstanceStructSize = 24
   408  
   409  	// pointerSizeLog2 satisfies: 1 << pointerSizeLog2 = sizeOf(uintptr)
   410  	pointerSizeLog2 = 3
   411  
   412  	// callFrameDataSizeInUint64 is the size of callFrame struct per 8 bytes (= size of uint64).
   413  	callFrameDataSizeInUint64 = 24 / 8
   414  )
   415  
   416  // nativeCallStatusCode represents the result of `nativecall`.
   417  // This is set by the native code.
   418  type nativeCallStatusCode uint32
   419  
   420  const (
   421  	// nativeCallStatusCodeReturned means the nativecall reaches the end of function, and returns successfully.
   422  	nativeCallStatusCodeReturned nativeCallStatusCode = iota
   423  	// nativeCallStatusCodeCallGoHostFunction means the nativecall returns to make a host function call.
   424  	nativeCallStatusCodeCallGoHostFunction
   425  	// nativeCallStatusCodeCallBuiltInFunction means the nativecall returns to make a builtin function call.
   426  	nativeCallStatusCodeCallBuiltInFunction
   427  	// nativeCallStatusCodeUnreachable means the function invocation reaches "unreachable" instruction.
   428  	nativeCallStatusCodeUnreachable
   429  	// nativeCallStatusCodeInvalidFloatToIntConversion means an invalid conversion of integer to floats happened.
   430  	nativeCallStatusCodeInvalidFloatToIntConversion
   431  	// nativeCallStatusCodeMemoryOutOfBounds means an out-of-bounds memory access happened.
   432  	nativeCallStatusCodeMemoryOutOfBounds
   433  	// nativeCallStatusCodeInvalidTableAccess means either offset to the table was out of bounds of table, or
   434  	// the target element in the table was uninitialized during call_indirect instruction.
   435  	nativeCallStatusCodeInvalidTableAccess
   436  	// nativeCallStatusCodeTypeMismatchOnIndirectCall means the type check failed during call_indirect.
   437  	nativeCallStatusCodeTypeMismatchOnIndirectCall
   438  	nativeCallStatusIntegerOverflow
   439  	nativeCallStatusIntegerDivisionByZero
   440  	nativeCallStatusModuleClosed
   441  )
   442  
   443  // causePanic causes a panic with the corresponding error to the nativeCallStatusCode.
   444  func (s nativeCallStatusCode) causePanic() {
   445  	var err error
   446  	switch s {
   447  	case nativeCallStatusIntegerOverflow:
   448  		err = wasmruntime.ErrRuntimeIntegerOverflow
   449  	case nativeCallStatusIntegerDivisionByZero:
   450  		err = wasmruntime.ErrRuntimeIntegerDivideByZero
   451  	case nativeCallStatusCodeInvalidFloatToIntConversion:
   452  		err = wasmruntime.ErrRuntimeInvalidConversionToInteger
   453  	case nativeCallStatusCodeUnreachable:
   454  		err = wasmruntime.ErrRuntimeUnreachable
   455  	case nativeCallStatusCodeMemoryOutOfBounds:
   456  		err = wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess
   457  	case nativeCallStatusCodeInvalidTableAccess:
   458  		err = wasmruntime.ErrRuntimeInvalidTableAccess
   459  	case nativeCallStatusCodeTypeMismatchOnIndirectCall:
   460  		err = wasmruntime.ErrRuntimeIndirectCallTypeMismatch
   461  	}
   462  	panic(err)
   463  }
   464  
   465  func (s nativeCallStatusCode) String() (ret string) {
   466  	switch s {
   467  	case nativeCallStatusCodeReturned:
   468  		ret = "returned"
   469  	case nativeCallStatusCodeCallGoHostFunction:
   470  		ret = "call_host_function"
   471  	case nativeCallStatusCodeCallBuiltInFunction:
   472  		ret = "call_builtin_function"
   473  	case nativeCallStatusCodeUnreachable:
   474  		ret = "unreachable"
   475  	case nativeCallStatusCodeInvalidFloatToIntConversion:
   476  		ret = "invalid float to int conversion"
   477  	case nativeCallStatusCodeMemoryOutOfBounds:
   478  		ret = "memory out of bounds"
   479  	case nativeCallStatusCodeInvalidTableAccess:
   480  		ret = "invalid table access"
   481  	case nativeCallStatusCodeTypeMismatchOnIndirectCall:
   482  		ret = "type mismatch on indirect call"
   483  	case nativeCallStatusIntegerOverflow:
   484  		ret = "integer overflow"
   485  	case nativeCallStatusIntegerDivisionByZero:
   486  		ret = "integer division by zero"
   487  	case nativeCallStatusModuleClosed:
   488  		ret = "module closed"
   489  	default:
   490  		panic("BUG")
   491  	}
   492  	return
   493  }
   494  
   495  // releaseCompiledModule is a runtime.SetFinalizer function that munmaps the compiledModule.executable.
   496  func releaseCompiledModule(cm *compiledModule) {
   497  	if err := cm.executable.Unmap(); err != nil {
   498  		// munmap failure cannot recover, and happen asynchronously on the
   499  		// finalizer thread. While finalizer functions can return errors,
   500  		// they are ignored.
   501  		panic(fmt.Errorf("compiler: failed to munmap code segment: %w", err))
   502  	}
   503  }
   504  
   505  // CompiledModuleCount implements the same method as documented on wasm.Engine.
   506  func (e *engine) CompiledModuleCount() uint32 {
   507  	return uint32(len(e.codes))
   508  }
   509  
   510  // DeleteCompiledModule implements the same method as documented on wasm.Engine.
   511  func (e *engine) DeleteCompiledModule(module *wasm.Module) {
   512  	e.deleteCompiledModule(module)
   513  }
   514  
   515  // Close implements the same method as documented on wasm.Engine.
   516  func (e *engine) Close() (err error) {
   517  	e.mux.Lock()
   518  	defer e.mux.Unlock()
   519  	// Releasing the references to compiled codes including the memory-mapped machine codes.
   520  	e.codes = nil
   521  	return
   522  }
   523  
   524  // CompileModule implements the same method as documented on wasm.Engine.
   525  func (e *engine) CompileModule(_ context.Context, module *wasm.Module, listeners []experimental.FunctionListener, ensureTermination bool) error {
   526  	if _, ok, err := e.getCompiledModule(module, listeners); ok { // cache hit!
   527  		return nil
   528  	} else if err != nil {
   529  		return err
   530  	}
   531  
   532  	irCompiler, err := wazeroir.NewCompiler(e.enabledFeatures, callFrameDataSizeInUint64, module, ensureTermination)
   533  	if err != nil {
   534  		return err
   535  	}
   536  
   537  	var withGoFunc bool
   538  	localFuncs, importedFuncs := len(module.FunctionSection), module.ImportFunctionCount
   539  	cm := &compiledModule{
   540  		compiledCode: &compiledCode{
   541  			source: module,
   542  		},
   543  		functions:         make([]compiledFunction, localFuncs),
   544  		ensureTermination: ensureTermination,
   545  	}
   546  
   547  	if localFuncs == 0 {
   548  		return e.addCompiledModule(module, cm, withGoFunc)
   549  	}
   550  
   551  	// As this uses mmap, we need to munmap on the compiled machine code when it's GCed.
   552  	e.setFinalizer(cm, releaseCompiledModule)
   553  	ln := len(listeners)
   554  	cmp := newCompiler()
   555  	asmNodes := new(asmNodes)
   556  	offsets := new(offsets)
   557  
   558  	// The executable code is allocated in memory mappings held by the
   559  	// CodeSegment, which gros on demand when it exhausts its capacity.
   560  	var executable asm.CodeSegment
   561  	defer func() {
   562  		// At the end of the function, the executable is set on the compiled
   563  		// module and the local variable cleared; until then, the function owns
   564  		// the memory mapping and is reponsible for clearing it if it returns
   565  		// due to an error. Note that an error at this stage is not recoverable
   566  		// so we panic if we fail to unmap the memory segment.
   567  		if err := executable.Unmap(); err != nil {
   568  			panic(fmt.Errorf("compiler: failed to munmap code segment: %w", err))
   569  		}
   570  	}()
   571  
   572  	for i := range module.CodeSection {
   573  		typ := &module.TypeSection[module.FunctionSection[i]]
   574  		buf := executable.NextCodeSection()
   575  		funcIndex := wasm.Index(i)
   576  		compiledFn := &cm.functions[i]
   577  		compiledFn.executableOffset = executable.Size()
   578  		compiledFn.parent = cm.compiledCode
   579  		compiledFn.index = importedFuncs + funcIndex
   580  		if i < ln {
   581  			compiledFn.listener = listeners[i]
   582  		}
   583  
   584  		if codeSeg := &module.CodeSection[i]; codeSeg.GoFunc != nil {
   585  			cmp.Init(typ, nil, compiledFn.listener != nil)
   586  			withGoFunc = true
   587  			if err = compileGoDefinedHostFunction(buf, cmp); err != nil {
   588  				def := module.FunctionDefinition(compiledFn.index)
   589  				return fmt.Errorf("error compiling host go func[%s]: %w", def.DebugName(), err)
   590  			}
   591  			compiledFn.goFunc = codeSeg.GoFunc
   592  		} else {
   593  			ir, err := irCompiler.Next()
   594  			if err != nil {
   595  				return fmt.Errorf("failed to lower func[%d]: %v", i, err)
   596  			}
   597  			cmp.Init(typ, ir, compiledFn.listener != nil)
   598  
   599  			compiledFn.stackPointerCeil, compiledFn.sourceOffsetMap, err = compileWasmFunction(buf, cmp, ir, asmNodes, offsets)
   600  			if err != nil {
   601  				def := module.FunctionDefinition(compiledFn.index)
   602  				return fmt.Errorf("error compiling wasm func[%s]: %w", def.DebugName(), err)
   603  			}
   604  		}
   605  	}
   606  
   607  	if runtime.GOARCH == "arm64" {
   608  		// On arm64, we cannot give all of rwx at the same time, so we change it to exec.
   609  		if err := platform.MprotectRX(executable.Bytes()); err != nil {
   610  			return err
   611  		}
   612  	}
   613  	cm.executable, executable = executable, asm.CodeSegment{}
   614  	return e.addCompiledModule(module, cm, withGoFunc)
   615  }
   616  
   617  // NewModuleEngine implements the same method as documented on wasm.Engine.
   618  func (e *engine) NewModuleEngine(module *wasm.Module, instance *wasm.ModuleInstance) (wasm.ModuleEngine, error) {
   619  	me := &moduleEngine{
   620  		functions: make([]function, len(module.FunctionSection)+int(module.ImportFunctionCount)),
   621  	}
   622  
   623  	// Note: imported functions are resolved in moduleEngine.ResolveImportedFunction.
   624  
   625  	cm, ok, err := e.getCompiledModule(module,
   626  		// listeners arg is not needed here since NewModuleEngine is called after CompileModule which
   627  		// ensures the association of listener with *code.
   628  		nil)
   629  	if !ok {
   630  		return nil, errors.New("source module must be compiled before instantiation")
   631  	} else if err != nil {
   632  		return nil, err
   633  	}
   634  
   635  	for i := range cm.functions {
   636  		c := &cm.functions[i]
   637  		offset := int(module.ImportFunctionCount) + i
   638  		typeIndex := module.FunctionSection[i]
   639  		me.functions[offset] = function{
   640  			codeInitialAddress: cm.executable.Addr() + c.executableOffset,
   641  			moduleInstance:     instance,
   642  			typeID:             instance.TypeIDs[typeIndex],
   643  			funcType:           &module.TypeSection[typeIndex],
   644  			parent:             c,
   645  		}
   646  	}
   647  
   648  	me.module = cm
   649  	return me, nil
   650  }
   651  
   652  // ResolveImportedFunction implements wasm.ModuleEngine.
   653  func (e *moduleEngine) ResolveImportedFunction(index, indexInImportedModule wasm.Index, importedModuleEngine wasm.ModuleEngine) {
   654  	imported := importedModuleEngine.(*moduleEngine)
   655  	// Copies the content from the import target moduleEngine.
   656  	e.functions[index] = imported.functions[indexInImportedModule]
   657  }
   658  
   659  // GetGlobalValue implements the same method as documented on wasm.ModuleEngine.
   660  func (e *moduleEngine) GetGlobalValue(wasm.Index) (lo, hi uint64) {
   661  	panic("BUG: GetGlobalValue should never be called on compiler mode")
   662  }
   663  
   664  // OwnsGlobals implements the same method as documented on wasm.ModuleEngine.
   665  func (e *moduleEngine) OwnsGlobals() bool { return false }
   666  
   667  // ResolveImportedMemory implements wasm.ModuleEngine.
   668  func (e *moduleEngine) ResolveImportedMemory(wasm.ModuleEngine) {}
   669  
   670  // FunctionInstanceReference implements the same method as documented on wasm.ModuleEngine.
   671  func (e *moduleEngine) FunctionInstanceReference(funcIndex wasm.Index) wasm.Reference {
   672  	return uintptr(unsafe.Pointer(&e.functions[funcIndex]))
   673  }
   674  
   675  // DoneInstantiation implements wasm.ModuleEngine.
   676  func (e *moduleEngine) DoneInstantiation() {}
   677  
   678  // NewFunction implements wasm.ModuleEngine.
   679  func (e *moduleEngine) NewFunction(index wasm.Index) api.Function {
   680  	return e.newFunction(&e.functions[index])
   681  }
   682  
   683  func (e *moduleEngine) newFunction(f *function) api.Function {
   684  	initStackSize := initialStackSize
   685  	if initialStackSize < f.parent.stackPointerCeil {
   686  		initStackSize = f.parent.stackPointerCeil * 2
   687  	}
   688  	return e.newCallEngine(initStackSize, f)
   689  }
   690  
   691  // LookupFunction implements the same method as documented on wasm.ModuleEngine.
   692  func (e *moduleEngine) LookupFunction(t *wasm.TableInstance, typeId wasm.FunctionTypeID, tableOffset wasm.Index) (*wasm.ModuleInstance, wasm.Index) {
   693  	if tableOffset >= uint32(len(t.References)) || t.Type != wasm.RefTypeFuncref {
   694  		panic(wasmruntime.ErrRuntimeInvalidTableAccess)
   695  	}
   696  	rawPtr := t.References[tableOffset]
   697  	if rawPtr == 0 {
   698  		panic(wasmruntime.ErrRuntimeInvalidTableAccess)
   699  	}
   700  
   701  	tf := functionFromUintptr(rawPtr)
   702  	if tf.typeID != typeId {
   703  		panic(wasmruntime.ErrRuntimeIndirectCallTypeMismatch)
   704  	}
   705  	return tf.moduleInstance, tf.parent.index
   706  }
   707  
   708  // functionFromUintptr resurrects the original *function from the given uintptr
   709  // which comes from either funcref table or OpcodeRefFunc instruction.
   710  func functionFromUintptr(ptr uintptr) *function {
   711  	// Wraps ptrs as the double pointer in order to avoid the unsafe access as detected by race detector.
   712  	//
   713  	// For example, if we have (*function)(unsafe.Pointer(ptr)) instead, then the race detector's "checkptr"
   714  	// subroutine wanrs as "checkptr: pointer arithmetic result points to invalid allocation"
   715  	// https://github.com/golang/go/blob/1ce7fcf139417d618c2730010ede2afb41664211/src/runtime/checkptr.go#L69
   716  	var wrapped *uintptr = &ptr
   717  	return *(**function)(unsafe.Pointer(wrapped))
   718  }
   719  
   720  // Definition implements the same method as documented on wasm.ModuleEngine.
   721  func (ce *callEngine) Definition() api.FunctionDefinition {
   722  	return ce.initialFn.definition()
   723  }
   724  
   725  func (f *function) definition() api.FunctionDefinition {
   726  	compiled := f.parent
   727  	return compiled.parent.source.FunctionDefinition(compiled.index)
   728  }
   729  
   730  // Call implements the same method as documented on wasm.ModuleEngine.
   731  func (ce *callEngine) Call(ctx context.Context, params ...uint64) (results []uint64, err error) {
   732  	ft := ce.initialFn.funcType
   733  	if n := ft.ParamNumInUint64; n != len(params) {
   734  		return nil, fmt.Errorf("expected %d params, but passed %d", n, len(params))
   735  	}
   736  	return ce.call(ctx, params, nil)
   737  }
   738  
   739  // CallWithStack implements the same method as documented on wasm.ModuleEngine.
   740  func (ce *callEngine) CallWithStack(ctx context.Context, stack []uint64) error {
   741  	params, results, err := wasm.SplitCallStack(ce.initialFn.funcType, stack)
   742  	if err != nil {
   743  		return err
   744  	}
   745  	_, err = ce.call(ctx, params, results)
   746  	return err
   747  }
   748  
   749  func (ce *callEngine) call(ctx context.Context, params, results []uint64) (_ []uint64, err error) {
   750  	m := ce.initialFn.moduleInstance
   751  	if ce.module.ensureTermination {
   752  		select {
   753  		case <-ctx.Done():
   754  			// If the provided context is already done, close the call context
   755  			// and return the error.
   756  			m.CloseWithCtxErr(ctx)
   757  			return nil, m.FailIfClosed()
   758  		default:
   759  		}
   760  	}
   761  
   762  	// We ensure that this Call method never panics as
   763  	// this Call method is indirectly invoked by embedders via store.CallFunction,
   764  	// and we have to make sure that all the runtime errors, including the one happening inside
   765  	// host functions, will be captured as errors, not panics.
   766  	defer func() {
   767  		err = ce.deferredOnCall(ctx, m, recover())
   768  		if err == nil {
   769  			// If the module closed during the call, and the call didn't err for another reason, set an ExitError.
   770  			err = m.FailIfClosed()
   771  		}
   772  		// Ensure that the compiled module will never be GC'd before this method returns.
   773  		runtime.KeepAlive(ce.module)
   774  	}()
   775  
   776  	ft := ce.initialFn.funcType
   777  	ce.initializeStack(ft, params)
   778  
   779  	if ce.module.ensureTermination {
   780  		done := m.CloseModuleOnCanceledOrTimeout(ctx)
   781  		defer done()
   782  	}
   783  
   784  	ce.execWasmFunction(ctx, m)
   785  
   786  	// This returns a safe copy of the results, instead of a slice view. If we
   787  	// returned a re-slice, the caller could accidentally or purposefully
   788  	// corrupt the stack of subsequent calls.
   789  	if results == nil && ft.ResultNumInUint64 > 0 {
   790  		results = make([]uint64, ft.ResultNumInUint64)
   791  	}
   792  	copy(results, ce.stack)
   793  	return results, nil
   794  }
   795  
   796  // initializeStack initializes callEngine.stack before entering native code.
   797  //
   798  // The stack must look like, if len(params) < len(results):
   799  //
   800  //	[arg0, arg1, ..., argN, 0, 0, 0, ...
   801  //	                       {       } ^
   802  //	                       callFrame |
   803  //	                                 |
   804  //	                            stackPointer
   805  //
   806  // else:
   807  //
   808  //	[arg0, arg1, ..., argN, _, _, _,  0, 0, 0, ...
   809  //	                      |        | {       }  ^
   810  //	                      |reserved| callFrame  |
   811  //	                      |        |            |
   812  //	                      |-------->       stackPointer
   813  //	                 len(results)-len(params)
   814  //
   815  //		 where we reserve the slots below the callframe with the length len(results)-len(params).
   816  //
   817  // Note: callFrame {  } is zeroed to indicate that the initial "caller" is this callEngine, not the Wasm function.
   818  //
   819  // See callEngine.stack as well.
   820  func (ce *callEngine) initializeStack(tp *wasm.FunctionType, args []uint64) {
   821  	for _, v := range args {
   822  		ce.pushValue(v)
   823  	}
   824  
   825  	ce.stackPointer = uint64(callFrameOffset(tp))
   826  
   827  	for i := 0; i < callFrameDataSizeInUint64; i++ {
   828  		ce.stack[ce.stackPointer] = 0
   829  		ce.stackPointer++
   830  	}
   831  }
   832  
   833  // callFrameOffset returns the offset of the call frame from the stack base pointer.
   834  //
   835  // See the diagram in callEngine.stack.
   836  func callFrameOffset(funcType *wasm.FunctionType) (ret int) {
   837  	ret = funcType.ResultNumInUint64
   838  	if ret < funcType.ParamNumInUint64 {
   839  		ret = funcType.ParamNumInUint64
   840  	}
   841  	return
   842  }
   843  
   844  // deferredOnCall takes the recovered value `recovered`, and wraps it
   845  // with the call frame stack traces when not nil. This also resets
   846  // the state of callEngine so that it can be used for the subsequent calls.
   847  //
   848  // This is defined for testability.
   849  func (ce *callEngine) deferredOnCall(ctx context.Context, m *wasm.ModuleInstance, recovered interface{}) (err error) {
   850  	if recovered != nil {
   851  		builder := wasmdebug.NewErrorBuilder()
   852  
   853  		// Unwinds call frames from the values stack, starting from the
   854  		// current function `ce.fn`, and the current stack base pointer `ce.stackBasePointerInBytes`.
   855  		fn := ce.fn
   856  		pc := uint64(ce.returnAddress)
   857  		stackBasePointer := int(ce.stackBasePointerInBytes >> 3)
   858  		functionListeners := make([]functionListenerInvocation, 0, 16)
   859  
   860  		for {
   861  			def := fn.definition()
   862  
   863  			// sourceInfo holds the source code information corresponding to the frame.
   864  			// It is not empty only when the DWARF is enabled.
   865  			var sources []string
   866  			if p := fn.parent; p.parent.executable.Bytes() != nil {
   867  				if fn.parent.sourceOffsetMap.irOperationSourceOffsetsInWasmBinary != nil {
   868  					offset := fn.getSourceOffsetInWasmBinary(pc)
   869  					sources = p.parent.source.DWARFLines.Line(offset)
   870  				}
   871  			}
   872  			builder.AddFrame(def.DebugName(), def.ParamTypes(), def.ResultTypes(), sources)
   873  
   874  			if fn.parent.listener != nil {
   875  				functionListeners = append(functionListeners, functionListenerInvocation{
   876  					FunctionListener: fn.parent.listener,
   877  					def:              fn.definition(),
   878  				})
   879  			}
   880  
   881  			callFrameOffset := callFrameOffset(fn.funcType)
   882  			if stackBasePointer != 0 {
   883  				frame := *(*callFrame)(unsafe.Pointer(&ce.stack[stackBasePointer+callFrameOffset]))
   884  				fn = frame.function
   885  				pc = uint64(frame.returnAddress)
   886  				stackBasePointer = int(frame.returnStackBasePointerInBytes >> 3)
   887  			} else { // base == 0 means that this was the last call frame stacked.
   888  				break
   889  			}
   890  		}
   891  
   892  		err = builder.FromRecovered(recovered)
   893  		for i := range functionListeners {
   894  			functionListeners[i].Abort(ctx, m, functionListeners[i].def, err)
   895  		}
   896  	}
   897  
   898  	// Allows the reuse of CallEngine.
   899  	ce.stackBasePointerInBytes, ce.stackPointer, ce.moduleInstance = 0, 0, nil
   900  	ce.moduleContext.fn = ce.initialFn
   901  	return
   902  }
   903  
   904  // getSourceOffsetInWasmBinary returns the corresponding offset in the original Wasm binary's code section
   905  // for the given pc (which is an absolute address in the memory).
   906  // If needPreviousInstr equals true, this returns the previous instruction's offset for the given pc.
   907  func (f *function) getSourceOffsetInWasmBinary(pc uint64) uint64 {
   908  	srcMap := &f.parent.sourceOffsetMap
   909  	n := bitpack.OffsetArrayLen(srcMap.irOperationOffsetsInNativeBinary) + 1
   910  
   911  	// Calculate the offset in the compiled native binary.
   912  	pcOffsetInNativeBinary := pc - uint64(f.codeInitialAddress)
   913  
   914  	// Then, do the binary search on the list of offsets in the native binary
   915  	// for all the IR operations. This returns the index of the *next* IR
   916  	// operation of the one corresponding to the origin of this pc.
   917  	// See sort.Search.
   918  	//
   919  	// TODO: the underlying implementation of irOperationOffsetsInNativeBinary
   920  	// uses uses delta encoding an calls to the Index method might require a
   921  	// O(N)  scan of the underlying array, turning binary search into a
   922  	// O(N*log(N)) operation. If this code path ends up being a bottleneck,
   923  	// we could add a Search method on the bitpack.OffsetArray types to delegate
   924  	// the lookup to the underlying data structure, allowing for the selection
   925  	// of a more optimized version of the algorithm. If you do so, please add a
   926  	// benchmark to verify the impact on compute time.
   927  	index := sort.Search(n, func(i int) bool {
   928  		if i == n-1 {
   929  			return true
   930  		}
   931  		return srcMap.irOperationOffsetsInNativeBinary.Index(i) >= pcOffsetInNativeBinary
   932  	})
   933  	if index == 0 && bitpack.OffsetArrayLen(srcMap.irOperationSourceOffsetsInWasmBinary) > 0 {
   934  		// When pc is the beginning of the function, the next IR
   935  		// operation (returned by sort.Search) is the first of the
   936  		// offset map.
   937  		return srcMap.irOperationSourceOffsetsInWasmBinary.Index(0)
   938  	}
   939  
   940  	if index == n || index == 0 { // This case, somehow pc is not found in the source offset map.
   941  		return 0
   942  	} else {
   943  		return srcMap.irOperationSourceOffsetsInWasmBinary.Index(index - 1)
   944  	}
   945  }
   946  
   947  func NewEngine(_ context.Context, enabledFeatures api.CoreFeatures, fileCache filecache.Cache) wasm.Engine {
   948  	return newEngine(enabledFeatures, fileCache)
   949  }
   950  
   951  func newEngine(enabledFeatures api.CoreFeatures, fileCache filecache.Cache) *engine {
   952  	return &engine{
   953  		enabledFeatures: enabledFeatures,
   954  		codes:           map[wasm.ModuleID]*compiledModule{},
   955  		setFinalizer:    runtime.SetFinalizer,
   956  		fileCache:       fileCache,
   957  		wazeroVersion:   version.GetWazeroVersion(),
   958  	}
   959  }
   960  
   961  // Do not make this variable as constant, otherwise there would be
   962  // dangerous memory access from native code.
   963  //
   964  // Background: Go has a mechanism called "goroutine stack-shrink" where Go
   965  // runtime shrinks Goroutine's stack when it is GCing. Shrinking means that
   966  // all the contents on the goroutine stack will be relocated by runtime,
   967  // Therefore, the memory address of these contents change undeterministically.
   968  // Not only shrinks, but also Go runtime grows the goroutine stack at any point
   969  // of function call entries, which also might end up relocating contents.
   970  //
   971  // On the other hand, we hold pointers to the data region of value stack and
   972  // call-frame stack slices and use these raw pointers from native code.
   973  // Therefore, it is dangerous if these two stacks are allocated on stack
   974  // as these stack's address might be changed by Goroutine which we cannot
   975  // detect.
   976  //
   977  // By declaring these values as `var`, slices created via `make([]..., var)`
   978  // will never be allocated on stack [1]. This means accessing these slices via
   979  // raw pointers is safe: As of version 1.21, Go's garbage collector never relocates
   980  // heap-allocated objects (aka no compaction of memory [2]).
   981  //
   982  // On Go upgrades, re-validate heap-allocation via `go build -gcflags='-m' ./internal/engine/compiler/...`.
   983  //
   984  //	[1] https://github.com/golang/go/blob/c19c4c566c63818dfd059b352e52c4710eecf14d/src/cmd/compile/internal/escape/utils.go#L213-L215
   985  //	[2] https://github.com/golang/go/blob/c19c4c566c63818dfd059b352e52c4710eecf14d/src/runtime/mgc.go#L9
   986  //	[3] https://mayurwadekar2.medium.com/escape-analysis-in-golang-ee40a1c064c1
   987  //	[4] https://medium.com/@yulang.chu/go-stack-or-heap-2-slices-which-keep-in-stack-have-limitation-of-size-b3f3adfd6190
   988  var initialStackSize uint64 = 512
   989  
   990  func (e *moduleEngine) newCallEngine(stackSize uint64, fn *function) *callEngine {
   991  	ce := &callEngine{
   992  		stack:         make([]uint64, stackSize),
   993  		archContext:   newArchContext(),
   994  		initialFn:     fn,
   995  		moduleContext: moduleContext{fn: fn},
   996  		module:        e.module,
   997  	}
   998  
   999  	stackHeader := (*reflect.SliceHeader)(unsafe.Pointer(&ce.stack))
  1000  	ce.stackContext = stackContext{
  1001  		stackElement0Address: stackHeader.Data,
  1002  		stackLenInBytes:      uint64(stackHeader.Len) << 3,
  1003  	}
  1004  	return ce
  1005  }
  1006  
  1007  func (ce *callEngine) popValue() (ret uint64) {
  1008  	ce.stackContext.stackPointer--
  1009  	ret = ce.stack[ce.stackTopIndex()]
  1010  	return
  1011  }
  1012  
  1013  func (ce *callEngine) pushValue(v uint64) {
  1014  	ce.stack[ce.stackTopIndex()] = v
  1015  	ce.stackContext.stackPointer++
  1016  }
  1017  
  1018  func (ce *callEngine) stackTopIndex() uint64 {
  1019  	return ce.stackContext.stackPointer + (ce.stackContext.stackBasePointerInBytes >> 3)
  1020  }
  1021  
  1022  const (
  1023  	builtinFunctionIndexMemoryGrow wasm.Index = iota
  1024  	builtinFunctionIndexGrowStack
  1025  	builtinFunctionIndexTableGrow
  1026  	builtinFunctionIndexFunctionListenerBefore
  1027  	builtinFunctionIndexFunctionListenerAfter
  1028  	builtinFunctionIndexCheckExitCode
  1029  	// builtinFunctionIndexBreakPoint is internal (only for wazero developers). Disabled by default.
  1030  	builtinFunctionIndexBreakPoint
  1031  )
  1032  
  1033  func (ce *callEngine) execWasmFunction(ctx context.Context, m *wasm.ModuleInstance) {
  1034  	codeAddr := ce.initialFn.codeInitialAddress
  1035  	modAddr := ce.initialFn.moduleInstance
  1036  
  1037  entry:
  1038  	{
  1039  		// Call into the native code.
  1040  		nativecall(codeAddr, ce, modAddr)
  1041  
  1042  		// Check the status code from Compiler code.
  1043  		switch status := ce.exitContext.statusCode; status {
  1044  		case nativeCallStatusCodeReturned:
  1045  		case nativeCallStatusCodeCallGoHostFunction:
  1046  			calleeHostFunction := ce.moduleContext.fn
  1047  			base := int(ce.stackBasePointerInBytes >> 3)
  1048  
  1049  			// In the compiler engine, ce.stack has enough capacity for the
  1050  			// max of param or result length, so we don't need to grow when
  1051  			// there are more results than parameters.
  1052  			stackLen := calleeHostFunction.funcType.ParamNumInUint64
  1053  			if resultLen := calleeHostFunction.funcType.ResultNumInUint64; resultLen > stackLen {
  1054  				stackLen = resultLen
  1055  			}
  1056  			stack := ce.stack[base : base+stackLen]
  1057  
  1058  			fn := calleeHostFunction.parent.goFunc
  1059  			switch fn := fn.(type) {
  1060  			case api.GoModuleFunction:
  1061  				fn.Call(ctx, ce.callerModuleInstance, stack)
  1062  			case api.GoFunction:
  1063  				fn.Call(ctx, stack)
  1064  			}
  1065  
  1066  			codeAddr, modAddr = ce.returnAddress, ce.moduleInstance
  1067  			goto entry
  1068  		case nativeCallStatusCodeCallBuiltInFunction:
  1069  			caller := ce.moduleContext.fn
  1070  			switch ce.exitContext.builtinFunctionCallIndex {
  1071  			case builtinFunctionIndexMemoryGrow:
  1072  				ce.builtinFunctionMemoryGrow(caller.moduleInstance.MemoryInstance)
  1073  			case builtinFunctionIndexGrowStack:
  1074  				ce.builtinFunctionGrowStack(caller.parent.stackPointerCeil)
  1075  			case builtinFunctionIndexTableGrow:
  1076  				ce.builtinFunctionTableGrow(caller.moduleInstance.Tables)
  1077  			case builtinFunctionIndexFunctionListenerBefore:
  1078  				ce.builtinFunctionFunctionListenerBefore(ctx, m, caller)
  1079  			case builtinFunctionIndexFunctionListenerAfter:
  1080  				ce.builtinFunctionFunctionListenerAfter(ctx, m, caller)
  1081  			case builtinFunctionIndexCheckExitCode:
  1082  				// Note: this operation must be done in Go, not native code. The reason is that
  1083  				// native code cannot be preempted and that means it can block forever if there are not
  1084  				// enough OS threads (which we don't have control over).
  1085  				if err := m.FailIfClosed(); err != nil {
  1086  					panic(err)
  1087  				}
  1088  			}
  1089  			if false {
  1090  				if ce.exitContext.builtinFunctionCallIndex == builtinFunctionIndexBreakPoint {
  1091  					runtime.Breakpoint()
  1092  				}
  1093  			}
  1094  
  1095  			codeAddr, modAddr = ce.returnAddress, ce.moduleInstance
  1096  			goto entry
  1097  		default:
  1098  			status.causePanic()
  1099  		}
  1100  	}
  1101  }
  1102  
  1103  // callStackCeiling is the maximum WebAssembly call frame stack height. This allows wazero to raise
  1104  // wasm.ErrCallStackOverflow instead of overflowing the Go runtime.
  1105  //
  1106  // The default value should suffice for most use cases. Those wishing to change this can via `go build -ldflags`.
  1107  //
  1108  // TODO: allows to configure this via context?
  1109  var callStackCeiling = uint64(5000000) // in uint64 (8 bytes) == 40000000 bytes in total == 40mb.
  1110  
  1111  func (ce *callEngine) builtinFunctionGrowStack(stackPointerCeil uint64) {
  1112  	oldLen := uint64(len(ce.stack))
  1113  	if callStackCeiling < oldLen {
  1114  		panic(wasmruntime.ErrRuntimeStackOverflow)
  1115  	}
  1116  
  1117  	// Extends the stack's length to oldLen*2+stackPointerCeil.
  1118  	newLen := oldLen<<1 + (stackPointerCeil)
  1119  	newStack := make([]uint64, newLen)
  1120  	top := ce.stackTopIndex()
  1121  	copy(newStack[:top], ce.stack[:top])
  1122  	ce.stack = newStack
  1123  	stackHeader := (*reflect.SliceHeader)(unsafe.Pointer(&ce.stack))
  1124  	ce.stackContext.stackElement0Address = stackHeader.Data
  1125  	ce.stackContext.stackLenInBytes = newLen << 3
  1126  }
  1127  
  1128  func (ce *callEngine) builtinFunctionMemoryGrow(mem *wasm.MemoryInstance) {
  1129  	newPages := ce.popValue()
  1130  
  1131  	if res, ok := mem.Grow(uint32(newPages)); !ok {
  1132  		ce.pushValue(uint64(0xffffffff)) // = -1 in signed 32-bit integer.
  1133  	} else {
  1134  		ce.pushValue(uint64(res))
  1135  	}
  1136  
  1137  	// Update the moduleContext fields as they become stale after the update ^^.
  1138  	bufSliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&mem.Buffer))
  1139  	ce.moduleContext.memorySliceLen = uint64(bufSliceHeader.Len)
  1140  	ce.moduleContext.memoryElement0Address = bufSliceHeader.Data
  1141  }
  1142  
  1143  func (ce *callEngine) builtinFunctionTableGrow(tables []*wasm.TableInstance) {
  1144  	tableIndex := uint32(ce.popValue())
  1145  	table := tables[tableIndex] // verified not to be out of range by the func validation at compilation phase.
  1146  	num := ce.popValue()
  1147  	ref := ce.popValue()
  1148  	res := table.Grow(uint32(num), uintptr(ref))
  1149  	ce.pushValue(uint64(res))
  1150  }
  1151  
  1152  // stackIterator implements experimental.StackIterator.
  1153  type stackIterator struct {
  1154  	stack   []uint64
  1155  	fn      *function
  1156  	base    int
  1157  	pc      uint64
  1158  	started bool
  1159  }
  1160  
  1161  func (si *stackIterator) reset(stack []uint64, fn *function, base int, pc uint64) {
  1162  	si.stack = stack
  1163  	si.fn = fn
  1164  	si.base = base
  1165  	si.pc = pc
  1166  	si.started = false
  1167  }
  1168  
  1169  func (si *stackIterator) clear() {
  1170  	si.stack = nil
  1171  	si.fn = nil
  1172  	si.base = 0
  1173  	si.started = false
  1174  }
  1175  
  1176  // Next implements the same method as documented on experimental.StackIterator.
  1177  func (si *stackIterator) Next() bool {
  1178  	if !si.started {
  1179  		si.started = true
  1180  		return true
  1181  	}
  1182  
  1183  	if si.fn == nil || si.base == 0 {
  1184  		return false
  1185  	}
  1186  
  1187  	frame := si.base + callFrameOffset(si.fn.funcType)
  1188  	si.pc = si.stack[frame+0]
  1189  	si.base = int(si.stack[frame+1] >> 3)
  1190  	// *function lives in the third field of callFrame struct. This must be
  1191  	// aligned with the definition of callFrame struct.
  1192  	si.fn = *(**function)(unsafe.Pointer(&si.stack[frame+2]))
  1193  	return si.fn != nil
  1194  }
  1195  
  1196  // ProgramCounter implements the same method as documented on experimental.StackIterator.
  1197  func (si *stackIterator) ProgramCounter() experimental.ProgramCounter {
  1198  	return experimental.ProgramCounter(si.pc)
  1199  }
  1200  
  1201  // Function implements the same method as documented on experimental.StackIterator.
  1202  func (si *stackIterator) Function() experimental.InternalFunction {
  1203  	return internalFunction{si.fn}
  1204  }
  1205  
  1206  // internalFunction implements experimental.InternalFunction.
  1207  type internalFunction struct{ *function }
  1208  
  1209  // Definition implements the same method as documented on experimental.InternalFunction.
  1210  func (f internalFunction) Definition() api.FunctionDefinition {
  1211  	return f.definition()
  1212  }
  1213  
  1214  // SourceOffsetForPC implements the same method as documented on experimental.InternalFunction.
  1215  func (f internalFunction) SourceOffsetForPC(pc experimental.ProgramCounter) uint64 {
  1216  	p := f.parent
  1217  	if bitpack.OffsetArrayLen(p.sourceOffsetMap.irOperationSourceOffsetsInWasmBinary) == 0 {
  1218  		return 0 // source not available
  1219  	}
  1220  	return f.getSourceOffsetInWasmBinary(uint64(pc))
  1221  }
  1222  
  1223  func (ce *callEngine) builtinFunctionFunctionListenerBefore(ctx context.Context, mod api.Module, fn *function) {
  1224  	base := int(ce.stackBasePointerInBytes >> 3)
  1225  	pc := uint64(ce.returnAddress)
  1226  	ce.stackIterator.reset(ce.stack, fn, base, pc)
  1227  
  1228  	params := ce.stack[base : base+fn.funcType.ParamNumInUint64]
  1229  	fn.parent.listener.Before(ctx, mod, fn.definition(), params, &ce.stackIterator)
  1230  
  1231  	ce.stackIterator.clear()
  1232  }
  1233  
  1234  func (ce *callEngine) builtinFunctionFunctionListenerAfter(ctx context.Context, mod api.Module, fn *function) {
  1235  	base := int(ce.stackBasePointerInBytes >> 3)
  1236  	fn.parent.listener.After(ctx, mod, fn.definition(), ce.stack[base:base+fn.funcType.ResultNumInUint64])
  1237  }
  1238  
  1239  func compileGoDefinedHostFunction(buf asm.Buffer, cmp compiler) error {
  1240  	if err := cmp.compileGoDefinedHostFunction(); err != nil {
  1241  		return err
  1242  	}
  1243  	_, err := cmp.compile(buf)
  1244  	return err
  1245  }
  1246  
  1247  type asmNodes struct {
  1248  	nodes []asm.Node
  1249  }
  1250  
  1251  type offsets struct {
  1252  	values []uint64
  1253  }
  1254  
  1255  func compileWasmFunction(buf asm.Buffer, cmp compiler, ir *wazeroir.CompilationResult, asmNodes *asmNodes, offsets *offsets) (spCeil uint64, sm sourceOffsetMap, err error) {
  1256  	if err = cmp.compilePreamble(); err != nil {
  1257  		err = fmt.Errorf("failed to emit preamble: %w", err)
  1258  		return
  1259  	}
  1260  
  1261  	needSourceOffsets := len(ir.IROperationSourceOffsetsInWasmBinary) > 0
  1262  	var irOpBegins []asm.Node
  1263  	if needSourceOffsets {
  1264  		irOpBegins = append(asmNodes.nodes[:0], make([]asm.Node, len(ir.Operations))...)
  1265  		defer func() { asmNodes.nodes = irOpBegins }()
  1266  	}
  1267  
  1268  	var skip bool
  1269  	for i := range ir.Operations {
  1270  		op := &ir.Operations[i]
  1271  		if needSourceOffsets {
  1272  			// If this compilation requires source offsets for DWARF based back trace,
  1273  			// we emit a NOP node at the beginning of each IR operation to get the
  1274  			// binary offset of the beginning of the corresponding compiled native code.
  1275  			irOpBegins[i] = cmp.compileNOP()
  1276  		}
  1277  
  1278  		// Compiler determines whether skip the entire label.
  1279  		// For example, if the label doesn't have any caller,
  1280  		// we don't need to generate native code at all as we never reach the region.
  1281  		if op.Kind == wazeroir.OperationKindLabel {
  1282  			skip = cmp.compileLabel(op)
  1283  		}
  1284  		if skip {
  1285  			continue
  1286  		}
  1287  
  1288  		if false {
  1289  			fmt.Printf("compiling op=%s: %s\n", op.Kind, cmp)
  1290  		}
  1291  		switch op.Kind {
  1292  		case wazeroir.OperationKindUnreachable:
  1293  			err = cmp.compileUnreachable()
  1294  		case wazeroir.OperationKindLabel:
  1295  		// label op is already handled ^^.
  1296  		case wazeroir.OperationKindBr:
  1297  			err = cmp.compileBr(op)
  1298  		case wazeroir.OperationKindBrIf:
  1299  			err = cmp.compileBrIf(op)
  1300  		case wazeroir.OperationKindBrTable:
  1301  			err = cmp.compileBrTable(op)
  1302  		case wazeroir.OperationKindCall:
  1303  			err = cmp.compileCall(op)
  1304  		case wazeroir.OperationKindCallIndirect:
  1305  			err = cmp.compileCallIndirect(op)
  1306  		case wazeroir.OperationKindDrop:
  1307  			err = cmp.compileDrop(op)
  1308  		case wazeroir.OperationKindSelect:
  1309  			err = cmp.compileSelect(op)
  1310  		case wazeroir.OperationKindPick:
  1311  			err = cmp.compilePick(op)
  1312  		case wazeroir.OperationKindSet:
  1313  			err = cmp.compileSet(op)
  1314  		case wazeroir.OperationKindGlobalGet:
  1315  			err = cmp.compileGlobalGet(op)
  1316  		case wazeroir.OperationKindGlobalSet:
  1317  			err = cmp.compileGlobalSet(op)
  1318  		case wazeroir.OperationKindLoad:
  1319  			err = cmp.compileLoad(op)
  1320  		case wazeroir.OperationKindLoad8:
  1321  			err = cmp.compileLoad8(op)
  1322  		case wazeroir.OperationKindLoad16:
  1323  			err = cmp.compileLoad16(op)
  1324  		case wazeroir.OperationKindLoad32:
  1325  			err = cmp.compileLoad32(op)
  1326  		case wazeroir.OperationKindStore:
  1327  			err = cmp.compileStore(op)
  1328  		case wazeroir.OperationKindStore8:
  1329  			err = cmp.compileStore8(op)
  1330  		case wazeroir.OperationKindStore16:
  1331  			err = cmp.compileStore16(op)
  1332  		case wazeroir.OperationKindStore32:
  1333  			err = cmp.compileStore32(op)
  1334  		case wazeroir.OperationKindMemorySize:
  1335  			err = cmp.compileMemorySize()
  1336  		case wazeroir.OperationKindMemoryGrow:
  1337  			err = cmp.compileMemoryGrow()
  1338  		case wazeroir.OperationKindConstI32:
  1339  			err = cmp.compileConstI32(op)
  1340  		case wazeroir.OperationKindConstI64:
  1341  			err = cmp.compileConstI64(op)
  1342  		case wazeroir.OperationKindConstF32:
  1343  			err = cmp.compileConstF32(op)
  1344  		case wazeroir.OperationKindConstF64:
  1345  			err = cmp.compileConstF64(op)
  1346  		case wazeroir.OperationKindEq:
  1347  			err = cmp.compileEq(op)
  1348  		case wazeroir.OperationKindNe:
  1349  			err = cmp.compileNe(op)
  1350  		case wazeroir.OperationKindEqz:
  1351  			err = cmp.compileEqz(op)
  1352  		case wazeroir.OperationKindLt:
  1353  			err = cmp.compileLt(op)
  1354  		case wazeroir.OperationKindGt:
  1355  			err = cmp.compileGt(op)
  1356  		case wazeroir.OperationKindLe:
  1357  			err = cmp.compileLe(op)
  1358  		case wazeroir.OperationKindGe:
  1359  			err = cmp.compileGe(op)
  1360  		case wazeroir.OperationKindAdd:
  1361  			err = cmp.compileAdd(op)
  1362  		case wazeroir.OperationKindSub:
  1363  			err = cmp.compileSub(op)
  1364  		case wazeroir.OperationKindMul:
  1365  			err = cmp.compileMul(op)
  1366  		case wazeroir.OperationKindClz:
  1367  			err = cmp.compileClz(op)
  1368  		case wazeroir.OperationKindCtz:
  1369  			err = cmp.compileCtz(op)
  1370  		case wazeroir.OperationKindPopcnt:
  1371  			err = cmp.compilePopcnt(op)
  1372  		case wazeroir.OperationKindDiv:
  1373  			err = cmp.compileDiv(op)
  1374  		case wazeroir.OperationKindRem:
  1375  			err = cmp.compileRem(op)
  1376  		case wazeroir.OperationKindAnd:
  1377  			err = cmp.compileAnd(op)
  1378  		case wazeroir.OperationKindOr:
  1379  			err = cmp.compileOr(op)
  1380  		case wazeroir.OperationKindXor:
  1381  			err = cmp.compileXor(op)
  1382  		case wazeroir.OperationKindShl:
  1383  			err = cmp.compileShl(op)
  1384  		case wazeroir.OperationKindShr:
  1385  			err = cmp.compileShr(op)
  1386  		case wazeroir.OperationKindRotl:
  1387  			err = cmp.compileRotl(op)
  1388  		case wazeroir.OperationKindRotr:
  1389  			err = cmp.compileRotr(op)
  1390  		case wazeroir.OperationKindAbs:
  1391  			err = cmp.compileAbs(op)
  1392  		case wazeroir.OperationKindNeg:
  1393  			err = cmp.compileNeg(op)
  1394  		case wazeroir.OperationKindCeil:
  1395  			err = cmp.compileCeil(op)
  1396  		case wazeroir.OperationKindFloor:
  1397  			err = cmp.compileFloor(op)
  1398  		case wazeroir.OperationKindTrunc:
  1399  			err = cmp.compileTrunc(op)
  1400  		case wazeroir.OperationKindNearest:
  1401  			err = cmp.compileNearest(op)
  1402  		case wazeroir.OperationKindSqrt:
  1403  			err = cmp.compileSqrt(op)
  1404  		case wazeroir.OperationKindMin:
  1405  			err = cmp.compileMin(op)
  1406  		case wazeroir.OperationKindMax:
  1407  			err = cmp.compileMax(op)
  1408  		case wazeroir.OperationKindCopysign:
  1409  			err = cmp.compileCopysign(op)
  1410  		case wazeroir.OperationKindI32WrapFromI64:
  1411  			err = cmp.compileI32WrapFromI64()
  1412  		case wazeroir.OperationKindITruncFromF:
  1413  			err = cmp.compileITruncFromF(op)
  1414  		case wazeroir.OperationKindFConvertFromI:
  1415  			err = cmp.compileFConvertFromI(op)
  1416  		case wazeroir.OperationKindF32DemoteFromF64:
  1417  			err = cmp.compileF32DemoteFromF64()
  1418  		case wazeroir.OperationKindF64PromoteFromF32:
  1419  			err = cmp.compileF64PromoteFromF32()
  1420  		case wazeroir.OperationKindI32ReinterpretFromF32:
  1421  			err = cmp.compileI32ReinterpretFromF32()
  1422  		case wazeroir.OperationKindI64ReinterpretFromF64:
  1423  			err = cmp.compileI64ReinterpretFromF64()
  1424  		case wazeroir.OperationKindF32ReinterpretFromI32:
  1425  			err = cmp.compileF32ReinterpretFromI32()
  1426  		case wazeroir.OperationKindF64ReinterpretFromI64:
  1427  			err = cmp.compileF64ReinterpretFromI64()
  1428  		case wazeroir.OperationKindExtend:
  1429  			err = cmp.compileExtend(op)
  1430  		case wazeroir.OperationKindSignExtend32From8:
  1431  			err = cmp.compileSignExtend32From8()
  1432  		case wazeroir.OperationKindSignExtend32From16:
  1433  			err = cmp.compileSignExtend32From16()
  1434  		case wazeroir.OperationKindSignExtend64From8:
  1435  			err = cmp.compileSignExtend64From8()
  1436  		case wazeroir.OperationKindSignExtend64From16:
  1437  			err = cmp.compileSignExtend64From16()
  1438  		case wazeroir.OperationKindSignExtend64From32:
  1439  			err = cmp.compileSignExtend64From32()
  1440  		case wazeroir.OperationKindMemoryInit:
  1441  			err = cmp.compileMemoryInit(op)
  1442  		case wazeroir.OperationKindDataDrop:
  1443  			err = cmp.compileDataDrop(op)
  1444  		case wazeroir.OperationKindMemoryCopy:
  1445  			err = cmp.compileMemoryCopy()
  1446  		case wazeroir.OperationKindMemoryFill:
  1447  			err = cmp.compileMemoryFill()
  1448  		case wazeroir.OperationKindTableInit:
  1449  			err = cmp.compileTableInit(op)
  1450  		case wazeroir.OperationKindElemDrop:
  1451  			err = cmp.compileElemDrop(op)
  1452  		case wazeroir.OperationKindTableCopy:
  1453  			err = cmp.compileTableCopy(op)
  1454  		case wazeroir.OperationKindRefFunc:
  1455  			err = cmp.compileRefFunc(op)
  1456  		case wazeroir.OperationKindTableGet:
  1457  			err = cmp.compileTableGet(op)
  1458  		case wazeroir.OperationKindTableSet:
  1459  			err = cmp.compileTableSet(op)
  1460  		case wazeroir.OperationKindTableGrow:
  1461  			err = cmp.compileTableGrow(op)
  1462  		case wazeroir.OperationKindTableSize:
  1463  			err = cmp.compileTableSize(op)
  1464  		case wazeroir.OperationKindTableFill:
  1465  			err = cmp.compileTableFill(op)
  1466  		case wazeroir.OperationKindV128Const:
  1467  			err = cmp.compileV128Const(op)
  1468  		case wazeroir.OperationKindV128Add:
  1469  			err = cmp.compileV128Add(op)
  1470  		case wazeroir.OperationKindV128Sub:
  1471  			err = cmp.compileV128Sub(op)
  1472  		case wazeroir.OperationKindV128Load:
  1473  			err = cmp.compileV128Load(op)
  1474  		case wazeroir.OperationKindV128LoadLane:
  1475  			err = cmp.compileV128LoadLane(op)
  1476  		case wazeroir.OperationKindV128Store:
  1477  			err = cmp.compileV128Store(op)
  1478  		case wazeroir.OperationKindV128StoreLane:
  1479  			err = cmp.compileV128StoreLane(op)
  1480  		case wazeroir.OperationKindV128ExtractLane:
  1481  			err = cmp.compileV128ExtractLane(op)
  1482  		case wazeroir.OperationKindV128ReplaceLane:
  1483  			err = cmp.compileV128ReplaceLane(op)
  1484  		case wazeroir.OperationKindV128Splat:
  1485  			err = cmp.compileV128Splat(op)
  1486  		case wazeroir.OperationKindV128Shuffle:
  1487  			err = cmp.compileV128Shuffle(op)
  1488  		case wazeroir.OperationKindV128Swizzle:
  1489  			err = cmp.compileV128Swizzle(op)
  1490  		case wazeroir.OperationKindV128AnyTrue:
  1491  			err = cmp.compileV128AnyTrue(op)
  1492  		case wazeroir.OperationKindV128AllTrue:
  1493  			err = cmp.compileV128AllTrue(op)
  1494  		case wazeroir.OperationKindV128BitMask:
  1495  			err = cmp.compileV128BitMask(op)
  1496  		case wazeroir.OperationKindV128And:
  1497  			err = cmp.compileV128And(op)
  1498  		case wazeroir.OperationKindV128Not:
  1499  			err = cmp.compileV128Not(op)
  1500  		case wazeroir.OperationKindV128Or:
  1501  			err = cmp.compileV128Or(op)
  1502  		case wazeroir.OperationKindV128Xor:
  1503  			err = cmp.compileV128Xor(op)
  1504  		case wazeroir.OperationKindV128Bitselect:
  1505  			err = cmp.compileV128Bitselect(op)
  1506  		case wazeroir.OperationKindV128AndNot:
  1507  			err = cmp.compileV128AndNot(op)
  1508  		case wazeroir.OperationKindV128Shl:
  1509  			err = cmp.compileV128Shl(op)
  1510  		case wazeroir.OperationKindV128Shr:
  1511  			err = cmp.compileV128Shr(op)
  1512  		case wazeroir.OperationKindV128Cmp:
  1513  			err = cmp.compileV128Cmp(op)
  1514  		case wazeroir.OperationKindV128AddSat:
  1515  			err = cmp.compileV128AddSat(op)
  1516  		case wazeroir.OperationKindV128SubSat:
  1517  			err = cmp.compileV128SubSat(op)
  1518  		case wazeroir.OperationKindV128Mul:
  1519  			err = cmp.compileV128Mul(op)
  1520  		case wazeroir.OperationKindV128Div:
  1521  			err = cmp.compileV128Div(op)
  1522  		case wazeroir.OperationKindV128Neg:
  1523  			err = cmp.compileV128Neg(op)
  1524  		case wazeroir.OperationKindV128Sqrt:
  1525  			err = cmp.compileV128Sqrt(op)
  1526  		case wazeroir.OperationKindV128Abs:
  1527  			err = cmp.compileV128Abs(op)
  1528  		case wazeroir.OperationKindV128Popcnt:
  1529  			err = cmp.compileV128Popcnt(op)
  1530  		case wazeroir.OperationKindV128Min:
  1531  			err = cmp.compileV128Min(op)
  1532  		case wazeroir.OperationKindV128Max:
  1533  			err = cmp.compileV128Max(op)
  1534  		case wazeroir.OperationKindV128AvgrU:
  1535  			err = cmp.compileV128AvgrU(op)
  1536  		case wazeroir.OperationKindV128Pmin:
  1537  			err = cmp.compileV128Pmin(op)
  1538  		case wazeroir.OperationKindV128Pmax:
  1539  			err = cmp.compileV128Pmax(op)
  1540  		case wazeroir.OperationKindV128Ceil:
  1541  			err = cmp.compileV128Ceil(op)
  1542  		case wazeroir.OperationKindV128Floor:
  1543  			err = cmp.compileV128Floor(op)
  1544  		case wazeroir.OperationKindV128Trunc:
  1545  			err = cmp.compileV128Trunc(op)
  1546  		case wazeroir.OperationKindV128Nearest:
  1547  			err = cmp.compileV128Nearest(op)
  1548  		case wazeroir.OperationKindV128Extend:
  1549  			err = cmp.compileV128Extend(op)
  1550  		case wazeroir.OperationKindV128ExtMul:
  1551  			err = cmp.compileV128ExtMul(op)
  1552  		case wazeroir.OperationKindV128Q15mulrSatS:
  1553  			err = cmp.compileV128Q15mulrSatS(op)
  1554  		case wazeroir.OperationKindV128ExtAddPairwise:
  1555  			err = cmp.compileV128ExtAddPairwise(op)
  1556  		case wazeroir.OperationKindV128FloatPromote:
  1557  			err = cmp.compileV128FloatPromote(op)
  1558  		case wazeroir.OperationKindV128FloatDemote:
  1559  			err = cmp.compileV128FloatDemote(op)
  1560  		case wazeroir.OperationKindV128FConvertFromI:
  1561  			err = cmp.compileV128FConvertFromI(op)
  1562  		case wazeroir.OperationKindV128Dot:
  1563  			err = cmp.compileV128Dot(op)
  1564  		case wazeroir.OperationKindV128Narrow:
  1565  			err = cmp.compileV128Narrow(op)
  1566  		case wazeroir.OperationKindV128ITruncSatFromF:
  1567  			err = cmp.compileV128ITruncSatFromF(op)
  1568  		case wazeroir.OperationKindBuiltinFunctionCheckExitCode:
  1569  			err = cmp.compileBuiltinFunctionCheckExitCode()
  1570  		default:
  1571  			err = errors.New("unsupported")
  1572  		}
  1573  		if err != nil {
  1574  			err = fmt.Errorf("operation %s: %w", op.Kind.String(), err)
  1575  			return
  1576  		}
  1577  	}
  1578  
  1579  	spCeil, err = cmp.compile(buf)
  1580  	if err != nil {
  1581  		err = fmt.Errorf("failed to compile: %w", err)
  1582  		return
  1583  	}
  1584  
  1585  	if needSourceOffsets {
  1586  		offsetInNativeBin := append(offsets.values[:0], make([]uint64, len(irOpBegins))...)
  1587  		offsets.values = offsetInNativeBin
  1588  		for i, nop := range irOpBegins {
  1589  			offsetInNativeBin[i] = nop.OffsetInBinary()
  1590  		}
  1591  		sm.irOperationOffsetsInNativeBinary = bitpack.NewOffsetArray(offsetInNativeBin)
  1592  		sm.irOperationSourceOffsetsInWasmBinary = bitpack.NewOffsetArray(ir.IROperationSourceOffsetsInWasmBinary)
  1593  	}
  1594  	return
  1595  }