wa-lang.org/wazero@v1.0.2/internal/engine/compiler/engine.go (about)

     1  package compiler
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  	"runtime"
     9  	"sync"
    10  	"unsafe"
    11  
    12  	"wa-lang.org/wazero/api"
    13  	"wa-lang.org/wazero/experimental"
    14  	"wa-lang.org/wazero/internal/compilationcache"
    15  	"wa-lang.org/wazero/internal/platform"
    16  	"wa-lang.org/wazero/internal/version"
    17  	"wa-lang.org/wazero/internal/wasm"
    18  	"wa-lang.org/wazero/internal/wasmdebug"
    19  	"wa-lang.org/wazero/internal/wasmruntime"
    20  	"wa-lang.org/wazero/internal/wazeroir"
    21  )
    22  
    23  type (
    24  	// engine is a Compiler implementation of wasm.Engine
    25  	engine struct {
    26  		enabledFeatures api.CoreFeatures
    27  		codes           map[wasm.ModuleID][]*code // guarded by mutex.
    28  		Cache           compilationcache.Cache
    29  		mux             sync.RWMutex
    30  		// setFinalizer defaults to runtime.SetFinalizer, but overridable for tests.
    31  		setFinalizer  func(obj interface{}, finalizer interface{})
    32  		wazeroVersion string
    33  	}
    34  
    35  	// moduleEngine implements wasm.ModuleEngine
    36  	moduleEngine struct {
    37  		// name is the name the module was instantiated with used for error handling.
    38  		name string
    39  
    40  		// functions are the functions in a module instances.
    41  		// The index is module instance-scoped. We intentionally avoid using map
    42  		// as the underlying memory region is accessed by assembly directly by using
    43  		// codesElement0Address.
    44  		functions []*function
    45  
    46  		importedFunctionCount uint32
    47  	}
    48  
    49  	// callEngine holds context per moduleEngine.Call, and shared across all the
    50  	// function calls originating from the same moduleEngine.Call execution.
    51  	callEngine struct {
    52  		// These contexts are read and written by compiled code.
    53  		// Note: structs are embedded to reduce the costs to access fields inside them. Also, this eases field offset
    54  		// calculation.
    55  		moduleContext
    56  		stackContext
    57  		exitContext
    58  		archContext
    59  
    60  		// The following fields are not accessed by compiled code directly.
    61  
    62  		// stack is the go-allocated stack for holding values and call frames.
    63  		// Note: We never edit len or cap in compiled code, so we won't get screwed when GC comes in.
    64  		//
    65  		// At any point of execution, say currently executing function F2 which was called by F1, then
    66  		// the stack should look like like:
    67  		//
    68  		// 	[..., arg0, arg1, ..., argN, _, _, _, v1, v2, v3, ....
    69  		//	      ^                     {       }
    70  		//	      |                F1's callFrame
    71  		//	      |
    72  		//  stackBasePointer
    73  		//
    74  		// where
    75  		//  - callFrame is the F1's callFrame which called F2. It contains F1's return address, F1's base pointer, and F1's *function.
    76  		//  - stackBasePointer is the stack base pointer stored at (callEngine stackContext.stackBasePointerInBytes)
    77  		//  - arg0, ..., argN are the function parameters, and v1, v2, v3,... are the local variables
    78  		//    including the non-function param locals as well as the temporary variable produced by instructions (e.g i32.const).
    79  		//
    80  		// If the F2 makes a function call to F3 which takes two arguments, then the stack will become:
    81  		//
    82  		// 	[..., arg0, arg1, ..., argN, _, _, _, v1, v2, v3, _, _, _
    83  		//	                            {       }     ^      {       }
    84  		//	                       F1's callFrame     | F2's callFrame
    85  		//	                                          |
    86  		//                                     stackBasePointer
    87  		// where
    88  		// 	- F2's callFrame is pushed above the v2 and v3 (arguments for F3).
    89  		//  - The previous stackBasePointer (pointed at arg0) was saved inside the F2's callFrame.
    90  		//
    91  		// Then, if F3 returns one result, say w1, then the result will look like:
    92  		//
    93  		// 	[..., arg0, arg1, ..., argN, _, _, _, v1, w1, ...
    94  		//	      ^                     {       }
    95  		//	      |                F1's callFrame
    96  		//	      |
    97  		//  stackBasePointer
    98  		//
    99  		// where
   100  		// 	- stackBasePointer was reverted to the position at arg0
   101  		//  - The result from F3 was pushed above v1
   102  		//
   103  		// If the number of parameters is smaller than that of return values, then the empty slots are reserved
   104  		// below the callFrame to store the results on teh return.
   105  		// For example, if F3 takes no parameter but returns N(>0) results, then the stack
   106  		// after making a call against F3 will look like:
   107  		//
   108  		// 	[..., arg0, arg1, ..., argN, _, _, _, v1, v2, v3, res_1, _, res_N, _, _, _
   109  		//	                            {       }            ^                {       }
   110  		//	                       F1's callFrame            |           F2's callFrame
   111  		//	                                                 |
   112  		//                                            stackBasePointer
   113  		// where res_1, ..., res_N are the reserved slots below the call frame. In general,
   114  		// the number of reserved slots equals max(0, len(results)-len(params).
   115  		//
   116  		// This reserved slots are necessary to save the result values onto the stack while not destroying
   117  		// the callFrame value on function returns.
   118  		stack []uint64
   119  
   120  		// initialFn is the initial function for this call engine.
   121  		initialFn *function
   122  
   123  		// ctx is the context.Context passed to all the host function calls.
   124  		// This is modified when there's a function listener call, otherwise it's always the context.Context
   125  		// passed to the Call API.
   126  		ctx context.Context
   127  		// contextStack is a stack of contexts which is pushed and popped by function listeners.
   128  		// This is used and modified when there are function listeners.
   129  		contextStack *contextStack
   130  	}
   131  
   132  	// contextStack is a stack of context.Context.
   133  	contextStack struct {
   134  		self context.Context
   135  		prev *contextStack
   136  	}
   137  
   138  	// moduleContext holds the per-function call specific module information.
   139  	// This is subject to be manipulated from compiled native code whenever we make function calls.
   140  	moduleContext struct {
   141  		// fn holds the currently executed *function.
   142  		fn *function
   143  
   144  		// moduleInstanceAddress is the address of module instance from which we initialize
   145  		// the following fields. This is set whenever we enter a function or return from function calls.
   146  		//
   147  		// On the entry to the native code, this must be initialized to zero to let native code preamble know
   148  		// that this is the initial function call (which leads to moduleContext initialization pass).
   149  		moduleInstanceAddress uintptr //lint:ignore U1000 This is only used by Compiler code.
   150  
   151  		// globalElement0Address is the address of the first element in the global slice,
   152  		// i.e. &ModuleInstance.Globals[0] as uintptr.
   153  		globalElement0Address uintptr
   154  		// memoryElement0Address is the address of the first element in the global slice,
   155  		// i.e. &ModuleInstance.Memory.Buffer[0] as uintptr.
   156  		memoryElement0Address uintptr
   157  		// memorySliceLen is the length of the memory buffer, i.e. len(ModuleInstance.Memory.Buffer).
   158  		memorySliceLen uint64
   159  		// memoryInstance holds the memory instance for this module instance.
   160  		memoryInstance *wasm.MemoryInstance
   161  		// tableElement0Address is the address of the first item in the tables slice,
   162  		// i.e. &ModuleInstance.Tables[0] as uintptr.
   163  		tablesElement0Address uintptr
   164  
   165  		// functionsElement0Address is &moduleContext.functions[0] as uintptr.
   166  		functionsElement0Address uintptr
   167  
   168  		// typeIDsElement0Address holds the &ModuleInstance.TypeIDs[0] as uintptr.
   169  		typeIDsElement0Address uintptr
   170  
   171  		// dataInstancesElement0Address holds the &ModuleInstance.DataInstances[0] as uintptr.
   172  		dataInstancesElement0Address uintptr
   173  
   174  		// elementInstancesElement0Address holds the &ModuleInstance.ElementInstances[0] as uintptr.
   175  		elementInstancesElement0Address uintptr
   176  	}
   177  
   178  	// stackContext stores the data to access engine.stack.
   179  	stackContext struct {
   180  		// stackPointer on .stack field which is accessed by stack[stackBasePointer+stackBasePointerInBytes*8].
   181  		//
   182  		// Note: stackPointer is not used in assembly since the native code knows exact position of
   183  		// each variable in the value stack from the info from compilation.
   184  		// Therefore, only updated when native code exit from the Compiler world and go back to the Go function.
   185  		stackPointer uint64
   186  
   187  		// stackBasePointerInBytes is updated whenever we make function calls.
   188  		// Background: Functions might be compiled as if they use the stack from the bottom.
   189  		// However, in reality, they have to use it from the middle of the stack depending on
   190  		// when these function calls are made. So instead of accessing stack via stackPointer alone,
   191  		// functions are compiled, so they access the stack via [stackBasePointer](fixed for entire function) + [stackPointer].
   192  		// More precisely, stackBasePointer is set to [callee's stack pointer] + [callee's stack base pointer] - [caller's params].
   193  		// This way, compiled functions can be independent of the timing of functions calls made against them.
   194  		stackBasePointerInBytes uint64
   195  
   196  		// stackElement0Address is &engine.stack[0] as uintptr.
   197  		// Note: this is updated when growing the stack in builtinFunctionGrowStack.
   198  		stackElement0Address uintptr
   199  
   200  		// stackLenInBytes is len(engine.stack[0]) * 8 (bytes).
   201  		// Note: this is updated when growing the stack in builtinFunctionGrowStack.
   202  		stackLenInBytes uint64
   203  	}
   204  
   205  	// exitContext will be manipulated whenever compiled native code returns into the Go function.
   206  	exitContext struct {
   207  		// Where we store the status code of Compiler execution.
   208  		statusCode nativeCallStatusCode
   209  
   210  		// Set when statusCode == compilerStatusCallBuiltInFunction
   211  		// Indicating the function call index.
   212  		builtinFunctionCallIndex wasm.Index
   213  
   214  		// returnAddress is the return address which the engine jumps into
   215  		// after executing a builtin function or host function.
   216  		returnAddress uintptr
   217  	}
   218  
   219  	// callFrame holds the information to which the caller function can return.
   220  	// This is mixed in callEngine.stack with other Wasm values just like any other
   221  	// native program (where the stack is the system stack though), and we retrieve the struct
   222  	// with unsafe pointer casts.
   223  	callFrame struct {
   224  		// returnAddress is the return address to which the engine jumps when the callee function returns.
   225  		returnAddress uintptr
   226  		// returnStackBasePointerInBytes is the stack base pointer to set on stackContext.stackBasePointerInBytes
   227  		// when the callee function returns.
   228  		returnStackBasePointerInBytes uint64
   229  		// function is the caller *function, and is used to retrieve the stack trace.
   230  		// Note: should be possible to revive *function from returnAddress, but might be costly.
   231  		function *function
   232  	}
   233  
   234  	// Function corresponds to function instance in Wasm, and is created from `code`.
   235  	function struct {
   236  		// codeInitialAddress is the pre-calculated pointer pointing to the initial byte of .codeSegment slice.
   237  		// That mean codeInitialAddress always equals uintptr(unsafe.Pointer(&.codeSegment[0]))
   238  		// and we cache the value (uintptr(unsafe.Pointer(&.codeSegment[0]))) to this field,
   239  		// so we don't need to repeat the calculation on each function call.
   240  		codeInitialAddress uintptr
   241  		// stackPointerCeil is the max of the stack pointer this function can reach. Lazily applied via maybeGrowStack.
   242  		stackPointerCeil uint64
   243  		// source is the source function instance from which this is compiled.
   244  		source *wasm.FunctionInstance
   245  		// moduleInstanceAddress holds the address of source.ModuleInstance.
   246  		moduleInstanceAddress uintptr
   247  		// parent holds code from which this is crated.
   248  		parent *code
   249  	}
   250  
   251  	// code corresponds to a function in a module (not instantiated one). This holds the machine code
   252  	// compiled by wazero compiler.
   253  	code struct {
   254  		// codeSegment is holding the compiled native code as a byte slice.
   255  		codeSegment []byte
   256  		// See the doc for codeStaticData type.
   257  		// stackPointerCeil is the max of the stack pointer this function can reach. Lazily applied via maybeGrowStack.
   258  		stackPointerCeil uint64
   259  
   260  		// indexInModule is the index of this function in the module. For logging purpose.
   261  		indexInModule wasm.Index
   262  		// sourceModule is the module from which this function is compiled. For logging purpose.
   263  		sourceModule *wasm.Module
   264  		// listener holds a listener to notify when this function is called.
   265  		listener experimental.FunctionListener
   266  	}
   267  )
   268  
   269  // createFunction creates a new function which uses the native code compiled.
   270  func (c *code) createFunction(f *wasm.FunctionInstance) *function {
   271  	return &function{
   272  		codeInitialAddress:    uintptr(unsafe.Pointer(&c.codeSegment[0])),
   273  		stackPointerCeil:      c.stackPointerCeil,
   274  		moduleInstanceAddress: uintptr(unsafe.Pointer(f.Module)),
   275  		source:                f,
   276  		parent:                c,
   277  	}
   278  }
   279  
   280  // Native code reads/writes Go's structs with the following constants.
   281  // See TestVerifyOffsetValue for how to derive these values.
   282  const (
   283  	// Offsets for moduleEngine.functions
   284  	moduleEngineFunctionsOffset = 16
   285  
   286  	// Offsets for callEngine moduleContext.
   287  	callEngineModuleContextFnOffset                              = 0
   288  	callEngineModuleContextModuleInstanceAddressOffset           = 8
   289  	callEngineModuleContextGlobalElement0AddressOffset           = 16
   290  	callEngineModuleContextMemoryElement0AddressOffset           = 24
   291  	callEngineModuleContextMemorySliceLenOffset                  = 32
   292  	callEngineModuleContextMemoryInstanceOffset                  = 40
   293  	callEngineModuleContextTablesElement0AddressOffset           = 48
   294  	callEngineModuleContextFunctionsElement0AddressOffset        = 56
   295  	callEngineModuleContextTypeIDsElement0AddressOffset          = 64
   296  	callEngineModuleContextDataInstancesElement0AddressOffset    = 72
   297  	callEngineModuleContextElementInstancesElement0AddressOffset = 80
   298  
   299  	// Offsets for callEngine stackContext.
   300  	callEngineStackContextStackPointerOffset            = 88
   301  	callEngineStackContextStackBasePointerInBytesOffset = 96
   302  	callEngineStackContextStackElement0AddressOffset    = 104
   303  	callEngineStackContextStackLenInBytesOffset         = 112
   304  
   305  	// Offsets for callEngine exitContext.
   306  	callEngineExitContextNativeCallStatusCodeOffset     = 120
   307  	callEngineExitContextBuiltinFunctionCallIndexOffset = 124
   308  	callEngineExitContextReturnAddressOffset            = 128
   309  
   310  	// Offsets for function.
   311  	functionCodeInitialAddressOffset    = 0
   312  	functionSourceOffset                = 16
   313  	functionModuleInstanceAddressOffset = 24
   314  
   315  	// Offsets for wasm.ModuleInstance.
   316  	moduleInstanceGlobalsOffset          = 48
   317  	moduleInstanceMemoryOffset           = 72
   318  	moduleInstanceTablesOffset           = 80
   319  	moduleInstanceEngineOffset           = 136
   320  	moduleInstanceTypeIDsOffset          = 152
   321  	moduleInstanceDataInstancesOffset    = 184
   322  	moduleInstanceElementInstancesOffset = 208
   323  
   324  	// Offsets for wasm.TableInstance.
   325  	tableInstanceTableOffset    = 0
   326  	tableInstanceTableLenOffset = 8
   327  
   328  	// Offsets for wasm.FunctionInstance.
   329  	functionInstanceTypeIDOffset = 88
   330  
   331  	// Offsets for wasm.MemoryInstance.
   332  	memoryInstanceBufferOffset    = 0
   333  	memoryInstanceBufferLenOffset = 8
   334  
   335  	// Offsets for wasm.GlobalInstance.
   336  	globalInstanceValueOffset = 8
   337  
   338  	// Offsets for Go's interface.
   339  	// https://research.swtch.com/interfaces
   340  	// https://github.com/golang/go/blob/release-branch.go1.17/src/runtime/runtime2.go#L207-L210
   341  	interfaceDataOffset = 8
   342  
   343  	// Consts for wasm.DataInstance.
   344  	dataInstanceStructSize = 24
   345  
   346  	// Consts for wasm.ElementInstance.
   347  	elementInstanceStructSize = 32
   348  
   349  	// pointerSizeLog2 satisfies: 1 << pointerSizeLog2 = sizeOf(uintptr)
   350  	pointerSizeLog2 = 3
   351  
   352  	// callFrameDataSizeInUint64 is the size of callFrame struct per 8 bytes (= size of uint64).
   353  	callFrameDataSizeInUint64 = 24 / 8
   354  )
   355  
   356  // nativeCallStatusCode represents the result of `nativecall`.
   357  // This is set by the native code.
   358  type nativeCallStatusCode uint32
   359  
   360  const (
   361  	// nativeCallStatusCodeReturned means the nativecall reaches the end of function, and returns successfully.
   362  	nativeCallStatusCodeReturned nativeCallStatusCode = iota
   363  	// nativeCallStatusCodeCallGoHostFunction means the nativecall returns to make a host function call.
   364  	nativeCallStatusCodeCallGoHostFunction
   365  	// nativeCallStatusCodeCallBuiltInFunction means the nativecall returns to make a builtin function call.
   366  	nativeCallStatusCodeCallBuiltInFunction
   367  	// nativeCallStatusCodeUnreachable means the function invocation reaches "unreachable" instruction.
   368  	nativeCallStatusCodeUnreachable
   369  	// nativeCallStatusCodeInvalidFloatToIntConversion means an invalid conversion of integer to floats happened.
   370  	nativeCallStatusCodeInvalidFloatToIntConversion
   371  	// nativeCallStatusCodeMemoryOutOfBounds means an out-of-bounds memory access happened.
   372  	nativeCallStatusCodeMemoryOutOfBounds
   373  	// nativeCallStatusCodeInvalidTableAccess means either offset to the table was out of bounds of table, or
   374  	// the target element in the table was uninitialized during call_indirect instruction.
   375  	nativeCallStatusCodeInvalidTableAccess
   376  	// nativeCallStatusCodeTypeMismatchOnIndirectCall means the type check failed during call_indirect.
   377  	nativeCallStatusCodeTypeMismatchOnIndirectCall
   378  	nativeCallStatusIntegerOverflow
   379  	nativeCallStatusIntegerDivisionByZero
   380  )
   381  
   382  // causePanic causes a panic with the corresponding error to the nativeCallStatusCode.
   383  func (s nativeCallStatusCode) causePanic() {
   384  	var err error
   385  	switch s {
   386  	case nativeCallStatusIntegerOverflow:
   387  		err = wasmruntime.ErrRuntimeIntegerOverflow
   388  	case nativeCallStatusIntegerDivisionByZero:
   389  		err = wasmruntime.ErrRuntimeIntegerDivideByZero
   390  	case nativeCallStatusCodeInvalidFloatToIntConversion:
   391  		err = wasmruntime.ErrRuntimeInvalidConversionToInteger
   392  	case nativeCallStatusCodeUnreachable:
   393  		err = wasmruntime.ErrRuntimeUnreachable
   394  	case nativeCallStatusCodeMemoryOutOfBounds:
   395  		err = wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess
   396  	case nativeCallStatusCodeInvalidTableAccess:
   397  		err = wasmruntime.ErrRuntimeInvalidTableAccess
   398  	case nativeCallStatusCodeTypeMismatchOnIndirectCall:
   399  		err = wasmruntime.ErrRuntimeIndirectCallTypeMismatch
   400  	}
   401  	panic(err)
   402  }
   403  
   404  func (s nativeCallStatusCode) String() (ret string) {
   405  	switch s {
   406  	case nativeCallStatusCodeReturned:
   407  		ret = "returned"
   408  	case nativeCallStatusCodeCallGoHostFunction:
   409  		ret = "call_host_function"
   410  	case nativeCallStatusCodeCallBuiltInFunction:
   411  		ret = "call_builtin_function"
   412  	case nativeCallStatusCodeUnreachable:
   413  		ret = "unreachable"
   414  	case nativeCallStatusCodeInvalidFloatToIntConversion:
   415  		ret = "invalid float to int conversion"
   416  	case nativeCallStatusCodeMemoryOutOfBounds:
   417  		ret = "memory out of bounds"
   418  	case nativeCallStatusCodeInvalidTableAccess:
   419  		ret = "invalid table access"
   420  	case nativeCallStatusCodeTypeMismatchOnIndirectCall:
   421  		ret = "type mismatch on indirect call"
   422  	case nativeCallStatusIntegerOverflow:
   423  		ret = "integer overflow"
   424  	case nativeCallStatusIntegerDivisionByZero:
   425  		ret = "integer division by zero"
   426  	default:
   427  		panic("BUG")
   428  	}
   429  	return
   430  }
   431  
   432  // releaseCode is a runtime.SetFinalizer function that munmaps the code.codeSegment.
   433  func releaseCode(compiledFn *code) {
   434  	codeSegment := compiledFn.codeSegment
   435  	if codeSegment == nil {
   436  		return // already released
   437  	}
   438  
   439  	// Setting this to nil allows tests to know the correct finalizer function was called.
   440  	compiledFn.codeSegment = nil
   441  	if err := platform.MunmapCodeSegment(codeSegment); err != nil {
   442  		// munmap failure cannot recover, and happen asynchronously on the finalizer thread. While finalizer
   443  		// functions can return errors, they are ignored. To make these visible for troubleshooting, we panic
   444  		// with additional context. module+funcidx should be enough, but if not, we can add more later.
   445  		panic(fmt.Errorf("compiler: failed to munmap code segment for %s.function[%d]: %w", compiledFn.sourceModule.NameSection.ModuleName,
   446  			compiledFn.indexInModule, err))
   447  	}
   448  }
   449  
   450  // CompiledModuleCount implements the same method as documented on wasm.Engine.
   451  func (e *engine) CompiledModuleCount() uint32 {
   452  	return uint32(len(e.codes))
   453  }
   454  
   455  // DeleteCompiledModule implements the same method as documented on wasm.Engine.
   456  func (e *engine) DeleteCompiledModule(module *wasm.Module) {
   457  	e.deleteCodes(module)
   458  }
   459  
   460  // CompileModule implements the same method as documented on wasm.Engine.
   461  func (e *engine) CompileModule(ctx context.Context, module *wasm.Module, listeners []experimental.FunctionListener) error {
   462  	if _, ok, err := e.getCodes(module); ok { // cache hit!
   463  		return nil
   464  	} else if err != nil {
   465  		return err
   466  	}
   467  
   468  	irs, err := wazeroir.CompileFunctions(ctx, e.enabledFeatures, callFrameDataSizeInUint64, module)
   469  	if err != nil {
   470  		return err
   471  	}
   472  
   473  	importedFuncs := module.ImportFuncCount()
   474  	funcs := make([]*code, len(module.FunctionSection))
   475  	ln := len(listeners)
   476  	for i, ir := range irs {
   477  		var lsn experimental.FunctionListener
   478  		if i < ln {
   479  			lsn = listeners[i]
   480  		}
   481  
   482  		funcIndex := wasm.Index(i)
   483  		var compiled *code
   484  		if ir.GoFunc != nil {
   485  			if compiled, err = compileGoDefinedHostFunction(ir, lsn != nil); err != nil {
   486  				def := module.FunctionDefinitionSection[funcIndex+importedFuncs]
   487  				return fmt.Errorf("error compiling host go func[%s]: %w", def.DebugName(), err)
   488  			}
   489  		} else if compiled, err = compileWasmFunction(e.enabledFeatures, ir, lsn != nil); err != nil {
   490  			def := module.FunctionDefinitionSection[funcIndex+importedFuncs]
   491  			return fmt.Errorf("error compiling wasm func[%s]: %w", def.DebugName(), err)
   492  		}
   493  
   494  		// As this uses mmap, we need to munmap on the compiled machine code when it's GCed.
   495  		e.setFinalizer(compiled, releaseCode)
   496  
   497  		compiled.listener = lsn
   498  		compiled.indexInModule = funcIndex
   499  		compiled.sourceModule = module
   500  		funcs[funcIndex] = compiled
   501  	}
   502  	return e.addCodes(module, funcs)
   503  }
   504  
   505  // NewModuleEngine implements the same method as documented on wasm.Engine.
   506  func (e *engine) NewModuleEngine(name string, module *wasm.Module, importedFunctions, moduleFunctions []*wasm.FunctionInstance) (wasm.ModuleEngine, error) {
   507  	imported := len(importedFunctions)
   508  	me := &moduleEngine{
   509  		name:                  name,
   510  		functions:             make([]*function, imported+len(moduleFunctions)),
   511  		importedFunctionCount: uint32(imported),
   512  	}
   513  
   514  	for i, f := range importedFunctions {
   515  		cf := f.Module.Engine.(*moduleEngine).functions[f.Idx]
   516  		me.functions[i] = cf
   517  	}
   518  
   519  	codes, ok, err := e.getCodes(module)
   520  	if !ok {
   521  		return nil, fmt.Errorf("source module for %s must be compiled before instantiation", name)
   522  	} else if err != nil {
   523  		return nil, err
   524  	}
   525  
   526  	for i, c := range codes {
   527  		f := moduleFunctions[i]
   528  		function := c.createFunction(f)
   529  		me.functions[imported+i] = function
   530  	}
   531  	return me, nil
   532  }
   533  
   534  // Name implements the same method as documented on wasm.ModuleEngine.
   535  func (e *moduleEngine) Name() string {
   536  	return e.name
   537  }
   538  
   539  // FunctionInstanceReference implements the same method as documented on wasm.ModuleEngine.
   540  func (e *moduleEngine) FunctionInstanceReference(funcIndex wasm.Index) wasm.Reference {
   541  	return uintptr(unsafe.Pointer(e.functions[funcIndex]))
   542  }
   543  
   544  // CreateFuncElementInstance implements the same method as documented on wasm.ModuleEngine.
   545  func (e *moduleEngine) CreateFuncElementInstance(indexes []*wasm.Index) *wasm.ElementInstance {
   546  	refs := make([]wasm.Reference, len(indexes))
   547  	for i, index := range indexes {
   548  		if index != nil {
   549  			refs[i] = uintptr(unsafe.Pointer(e.functions[*index]))
   550  		}
   551  	}
   552  	return &wasm.ElementInstance{
   553  		References: refs,
   554  		Type:       wasm.RefTypeFuncref,
   555  	}
   556  }
   557  
   558  // InitializeFuncrefGlobals implements the same method as documented on wasm.InitializeFuncrefGlobals.
   559  func (e *moduleEngine) InitializeFuncrefGlobals(globals []*wasm.GlobalInstance) {
   560  	for _, g := range globals {
   561  		if g.Type.ValType == wasm.ValueTypeFuncref {
   562  			if int64(g.Val) == wasm.GlobalInstanceNullFuncRefValue {
   563  				g.Val = 0 // Null funcref is expressed as zero.
   564  			} else {
   565  				// Lowers the stored function index into the interpreter specific function's opaque pointer.
   566  				g.Val = uint64(uintptr(unsafe.Pointer(e.functions[g.Val])))
   567  			}
   568  		}
   569  	}
   570  }
   571  
   572  func (e *moduleEngine) NewCallEngine(callCtx *wasm.CallContext, f *wasm.FunctionInstance) (ce wasm.CallEngine, err error) {
   573  	// Note: The input parameters are pre-validated, so a compiled function is only absent on close. Updates to
   574  	// code on close aren't locked, neither is this read.
   575  	compiled := e.functions[f.Idx]
   576  	if compiled == nil { // Lazy check the cause as it could be because the module was already closed.
   577  		if err = callCtx.FailIfClosed(); err == nil {
   578  			panic(fmt.Errorf("BUG: %s.func[%d] was nil before close", e.name, f.Idx))
   579  		}
   580  		return
   581  	}
   582  
   583  	initStackSize := initialStackSize
   584  	if initialStackSize < compiled.stackPointerCeil {
   585  		initStackSize = compiled.stackPointerCeil * 2
   586  	}
   587  	return e.newCallEngine(initStackSize, compiled), nil
   588  }
   589  
   590  // LookupFunction implements the same method as documented on wasm.ModuleEngine.
   591  func (e *moduleEngine) LookupFunction(t *wasm.TableInstance, typeId wasm.FunctionTypeID, tableOffset wasm.Index) (idx wasm.Index, err error) {
   592  	if tableOffset >= uint32(len(t.References)) || t.Type != wasm.RefTypeFuncref {
   593  		err = wasmruntime.ErrRuntimeInvalidTableAccess
   594  		return
   595  	}
   596  	rawPtr := t.References[tableOffset]
   597  	if rawPtr == 0 {
   598  		err = wasmruntime.ErrRuntimeInvalidTableAccess
   599  		return
   600  	}
   601  
   602  	tf := functionFromUintptr(rawPtr)
   603  	if tf.source.TypeID != typeId {
   604  		err = wasmruntime.ErrRuntimeIndirectCallTypeMismatch
   605  		return
   606  	}
   607  	idx = tf.source.Idx
   608  
   609  	return
   610  }
   611  
   612  // functionFromUintptr resurrects the original *function from the given uintptr
   613  // which comes from either funcref table or OpcodeRefFunc instruction.
   614  func functionFromUintptr(ptr uintptr) *function {
   615  	// Wraps ptrs as the double pointer in order to avoid the unsafe access as detected by race detector.
   616  	//
   617  	// For example, if we have (*function)(unsafe.Pointer(ptr)) instead, then the race detector's "checkptr"
   618  	// subroutine wanrs as "checkptr: pointer arithmetic result points to invalid allocation"
   619  	// https://github.com/golang/go/blob/1ce7fcf139417d618c2730010ede2afb41664211/src/runtime/checkptr.go#L69
   620  	var wrapped *uintptr = &ptr
   621  	return *(**function)(unsafe.Pointer(wrapped))
   622  }
   623  
   624  // Call implements the same method as documented on wasm.ModuleEngine.
   625  func (ce *callEngine) Call(ctx context.Context, callCtx *wasm.CallContext, params []uint64) (results []uint64, err error) {
   626  	tp := ce.initialFn.source.Type
   627  
   628  	paramCount := len(params)
   629  	if tp.ParamNumInUint64 != paramCount {
   630  		return nil, fmt.Errorf("expected %d params, but passed %d", ce.initialFn.source.Type.ParamNumInUint64, paramCount)
   631  	}
   632  
   633  	// We ensure that this Call method never panics as
   634  	// this Call method is indirectly invoked by embedders via store.CallFunction,
   635  	// and we have to make sure that all the runtime errors, including the one happening inside
   636  	// host functions, will be captured as errors, not panics.
   637  	defer func() {
   638  		err = ce.deferredOnCall(recover())
   639  		if err == nil {
   640  			// If the module closed during the call, and the call didn't err for another reason, set an ExitError.
   641  			err = callCtx.FailIfClosed()
   642  			// TODO: ^^ Will not fail if the function was imported from a closed module.
   643  		}
   644  	}()
   645  
   646  	ce.initializeStack(tp, params)
   647  	ce.execWasmFunction(ctx, callCtx)
   648  
   649  	// This returns a safe copy of the results, instead of a slice view. If we
   650  	// returned a re-slice, the caller could accidentally or purposefully
   651  	// corrupt the stack of subsequent calls
   652  	if resultCount := tp.ResultNumInUint64; resultCount > 0 {
   653  		results = make([]uint64, resultCount)
   654  		copy(results, ce.stack[:resultCount])
   655  	}
   656  	return
   657  }
   658  
   659  // initializeStack initializes callEngine.stack before entering native code.
   660  //
   661  // The stack must look like, if len(params) < len(results):
   662  //
   663  //	[arg0, arg1, ..., argN, 0, 0, 0, ...
   664  //	                       {       } ^
   665  //	                       callFrame |
   666  //	                                 |
   667  //	                            stackPointer
   668  //
   669  // else:
   670  //
   671  //	[arg0, arg1, ..., argN, _, _, _,  0, 0, 0, ...
   672  //	                      |        | {       }  ^
   673  //	                      |reserved| callFrame  |
   674  //	                      |        |            |
   675  //	                      |-------->       stackPointer
   676  //	                 len(results)-len(params)
   677  //
   678  //		 where we reserve the slots below the callframe with the length len(results)-len(params).
   679  //
   680  // Note: callFrame {  } is zeroed to indicate that the initial "caller" is this callEngine, not the Wasm function.
   681  //
   682  // See callEngine.stack as well.
   683  func (ce *callEngine) initializeStack(tp *wasm.FunctionType, args []uint64) {
   684  	for _, v := range args {
   685  		ce.pushValue(v)
   686  	}
   687  
   688  	ce.stackPointer = uint64(callFrameOffset(tp))
   689  
   690  	for i := 0; i < callFrameDataSizeInUint64; i++ {
   691  		ce.stack[ce.stackPointer] = 0
   692  		ce.stackPointer++
   693  	}
   694  }
   695  
   696  // callFrameOffset returns the offset of the call frame from the stack base pointer.
   697  //
   698  // See the diagram in callEngine.stack.
   699  func callFrameOffset(funcType *wasm.FunctionType) (ret int) {
   700  	ret = funcType.ResultNumInUint64
   701  	if ret < funcType.ParamNumInUint64 {
   702  		ret = funcType.ParamNumInUint64
   703  	}
   704  	return
   705  }
   706  
   707  // deferredOnCall takes the recovered value `recovered`, and wraps it
   708  // with the call frame stack traces when not nil. This also resets
   709  // the state of callEngine so that it can be used for the subsequent calls.
   710  //
   711  // This is defined for testability.
   712  func (ce *callEngine) deferredOnCall(recovered interface{}) (err error) {
   713  	if recovered != nil {
   714  		builder := wasmdebug.NewErrorBuilder()
   715  
   716  		// Unwinds call frames from the values stack, starting from the
   717  		// current function `ce.fn`, and the current stack base pointer `ce.stackBasePointerInBytes`.
   718  		fn := ce.fn
   719  		stackBasePointer := int(ce.stackBasePointerInBytes >> 3)
   720  		for {
   721  			def := fn.source.Definition
   722  			builder.AddFrame(def.DebugName(), def.ParamTypes(), def.ResultTypes())
   723  
   724  			callFrameOffset := callFrameOffset(fn.source.Type)
   725  			if stackBasePointer != 0 {
   726  				frame := *(*callFrame)(unsafe.Pointer(&ce.stack[stackBasePointer+callFrameOffset]))
   727  				fn = frame.function
   728  				stackBasePointer = int(frame.returnStackBasePointerInBytes >> 3)
   729  			} else { // base == 0 means that this was the last call frame stacked.
   730  				break
   731  			}
   732  		}
   733  		err = builder.FromRecovered(recovered)
   734  	}
   735  
   736  	// Allows the reuse of CallEngine.
   737  	ce.stackBasePointerInBytes, ce.stackPointer, ce.moduleInstanceAddress = 0, 0, 0
   738  	ce.moduleContext.fn = ce.initialFn
   739  	return
   740  }
   741  
   742  func NewEngine(ctx context.Context, enabledFeatures api.CoreFeatures) wasm.Engine {
   743  	return newEngine(ctx, enabledFeatures)
   744  }
   745  
   746  func newEngine(ctx context.Context, enabledFeatures api.CoreFeatures) *engine {
   747  	var wazeroVersion string
   748  	if v := ctx.Value(version.WazeroVersionKey{}); v != nil {
   749  		wazeroVersion = v.(string)
   750  	}
   751  	return &engine{
   752  		enabledFeatures: enabledFeatures,
   753  		codes:           map[wasm.ModuleID][]*code{},
   754  		setFinalizer:    runtime.SetFinalizer,
   755  		Cache:           compilationcache.NewFileCache(ctx),
   756  		wazeroVersion:   wazeroVersion,
   757  	}
   758  }
   759  
   760  // Do not make this variable as constant, otherwise there would be
   761  // dangerous memory access from native code.
   762  //
   763  // Background: Go has a mechanism called "goroutine stack-shrink" where Go
   764  // runtime shrinks Goroutine's stack when it is GCing. Shrinking means that
   765  // all the contents on the goroutine stack will be relocated by runtime,
   766  // Therefore, the memory address of these contents change undeterministically.
   767  // Not only shrinks, but also Go runtime grows the goroutine stack at any point
   768  // of function call entries, which also might end up relocating contents.
   769  //
   770  // On the other hand, we hold pointers to the data region of value stack and
   771  // call-frame stack slices and use these raw pointers from native code.
   772  // Therefore, it is dangerous if these two stacks are allocated on stack
   773  // as these stack's address might be changed by Goroutine which we cannot
   774  // detect.
   775  //
   776  // By declaring these values as `var`, slices created via `make([]..., var)`
   777  // will never be allocated on stack [1]. This means accessing these slices via
   778  // raw pointers is safe: As of version 1.18, Go's garbage collector never relocates
   779  // heap-allocated objects (aka no compaction of memory [2]).
   780  //
   781  // On Go upgrades, re-validate heap-allocation via `go build -gcflags='-m' ./internal/engine/compiler/...`.
   782  //
   783  //	[1] https://github.com/golang/go/blob/68ecdc2c70544c303aa923139a5f16caf107d955/src/cmd/compile/internal/escape/utils.go#L206-L208
   784  //	[2] https://github.com/golang/go/blob/68ecdc2c70544c303aa923139a5f16caf107d955/src/runtime/mgc.go#L9
   785  //	[3] https://mayurwadekar2.medium.com/escape-analysis-in-golang-ee40a1c064c1
   786  //	[4] https://medium.com/@yulang.chu/go-stack-or-heap-2-slices-which-keep-in-stack-have-limitation-of-size-b3f3adfd6190
   787  var initialStackSize uint64 = 512
   788  
   789  func (e *moduleEngine) newCallEngine(stackSize uint64, fn *function) *callEngine {
   790  	ce := &callEngine{
   791  		stack:         make([]uint64, stackSize),
   792  		archContext:   newArchContext(),
   793  		initialFn:     fn,
   794  		moduleContext: moduleContext{fn: fn},
   795  	}
   796  
   797  	stackHeader := (*reflect.SliceHeader)(unsafe.Pointer(&ce.stack))
   798  	ce.stackContext = stackContext{
   799  		stackElement0Address: stackHeader.Data,
   800  		stackLenInBytes:      uint64(stackHeader.Len) << 3,
   801  	}
   802  	return ce
   803  }
   804  
   805  func (ce *callEngine) popValue() (ret uint64) {
   806  	ce.stackContext.stackPointer--
   807  	ret = ce.stack[ce.stackTopIndex()]
   808  	return
   809  }
   810  
   811  func (ce *callEngine) pushValue(v uint64) {
   812  	ce.stack[ce.stackTopIndex()] = v
   813  	ce.stackContext.stackPointer++
   814  }
   815  
   816  func (ce *callEngine) stackTopIndex() uint64 {
   817  	return ce.stackContext.stackPointer + (ce.stackContext.stackBasePointerInBytes >> 3)
   818  }
   819  
   820  const (
   821  	builtinFunctionIndexMemoryGrow wasm.Index = iota
   822  	builtinFunctionIndexGrowStack
   823  	builtinFunctionIndexTableGrow
   824  	builtinFunctionIndexFunctionListenerBefore
   825  	builtinFunctionIndexFunctionListenerAfter
   826  	// builtinFunctionIndexBreakPoint is internal (only for wazero developers). Disabled by default.
   827  	builtinFunctionIndexBreakPoint
   828  )
   829  
   830  func (ce *callEngine) execWasmFunction(ctx context.Context, callCtx *wasm.CallContext) {
   831  	codeAddr := ce.initialFn.codeInitialAddress
   832  	modAddr := ce.initialFn.moduleInstanceAddress
   833  	ce.ctx = ctx
   834  
   835  entry:
   836  	{
   837  		// Call into the native code.
   838  		nativecall(codeAddr, uintptr(unsafe.Pointer(ce)), modAddr)
   839  
   840  		// Check the status code from Compiler code.
   841  		switch status := ce.exitContext.statusCode; status {
   842  		case nativeCallStatusCodeReturned:
   843  		case nativeCallStatusCodeCallGoHostFunction:
   844  			calleeHostFunction := ce.moduleContext.fn
   845  			base := int(ce.stackBasePointerInBytes >> 3)
   846  
   847  			// In the compiler engine, ce.stack has enough capacity for the
   848  			// max of param or result length, so we don't need to grow when
   849  			// there are more results than parameters.
   850  			stackLen := calleeHostFunction.source.Type.ParamNumInUint64
   851  			if resultLen := calleeHostFunction.source.Type.ResultNumInUint64; resultLen > stackLen {
   852  				stackLen = resultLen
   853  			}
   854  			stack := ce.stack[base : base+stackLen]
   855  
   856  			fn := calleeHostFunction.source.GoFunc
   857  			switch fn := fn.(type) {
   858  			case api.GoModuleFunction:
   859  				fn.Call(ce.ctx, callCtx.WithMemory(ce.memoryInstance), stack)
   860  			case api.GoFunction:
   861  				fn.Call(ce.ctx, stack)
   862  			}
   863  
   864  			codeAddr, modAddr = ce.returnAddress, ce.moduleInstanceAddress
   865  			goto entry
   866  		case nativeCallStatusCodeCallBuiltInFunction:
   867  			caller := ce.moduleContext.fn
   868  			switch ce.exitContext.builtinFunctionCallIndex {
   869  			case builtinFunctionIndexMemoryGrow:
   870  				ce.builtinFunctionMemoryGrow(ce.ctx, caller.source.Module.Memory)
   871  			case builtinFunctionIndexGrowStack:
   872  				ce.builtinFunctionGrowStack(caller.stackPointerCeil)
   873  			case builtinFunctionIndexTableGrow:
   874  				ce.builtinFunctionTableGrow(ce.ctx, caller.source.Module.Tables)
   875  			case builtinFunctionIndexFunctionListenerBefore:
   876  				ce.builtinFunctionFunctionListenerBefore(ce.ctx, caller)
   877  			case builtinFunctionIndexFunctionListenerAfter:
   878  				ce.builtinFunctionFunctionListenerAfter(ce.ctx, caller)
   879  			}
   880  			if false {
   881  				if ce.exitContext.builtinFunctionCallIndex == builtinFunctionIndexBreakPoint {
   882  					runtime.Breakpoint()
   883  				}
   884  			}
   885  
   886  			codeAddr, modAddr = ce.returnAddress, ce.moduleInstanceAddress
   887  			goto entry
   888  		default:
   889  			status.causePanic()
   890  		}
   891  	}
   892  }
   893  
   894  // callStackCeiling is the maximum WebAssembly call frame stack height. This allows wazero to raise
   895  // wasm.ErrCallStackOverflow instead of overflowing the Go runtime.
   896  //
   897  // The default value should suffice for most use cases. Those wishing to change this can via `go build -ldflags`.
   898  //
   899  // TODO: allows to configure this via context?
   900  var callStackCeiling = uint64(5000000) // in uint64 (8 bytes) == 40000000 bytes in total == 40mb.
   901  
   902  func (ce *callEngine) builtinFunctionGrowStack(stackPointerCeil uint64) {
   903  	oldLen := uint64(len(ce.stack))
   904  	if callStackCeiling < oldLen {
   905  		panic(wasmruntime.ErrRuntimeStackOverflow)
   906  	}
   907  
   908  	// Extends the stack's length to oldLen*2+stackPointerCeil.
   909  	newLen := oldLen<<1 + (stackPointerCeil)
   910  	newStack := make([]uint64, newLen)
   911  	top := ce.stackTopIndex()
   912  	copy(newStack[:top], ce.stack[:top])
   913  	ce.stack = newStack
   914  	stackHeader := (*reflect.SliceHeader)(unsafe.Pointer(&ce.stack))
   915  	ce.stackContext.stackElement0Address = stackHeader.Data
   916  	ce.stackContext.stackLenInBytes = newLen << 3
   917  }
   918  
   919  func (ce *callEngine) builtinFunctionMemoryGrow(ctx context.Context, mem *wasm.MemoryInstance) {
   920  	newPages := ce.popValue()
   921  
   922  	if res, ok := mem.Grow(ctx, uint32(newPages)); !ok {
   923  		ce.pushValue(uint64(0xffffffff)) // = -1 in signed 32-bit integer.
   924  	} else {
   925  		ce.pushValue(uint64(res))
   926  	}
   927  
   928  	// Update the moduleContext fields as they become stale after the update ^^.
   929  	bufSliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&mem.Buffer))
   930  	ce.moduleContext.memorySliceLen = uint64(bufSliceHeader.Len)
   931  	ce.moduleContext.memoryElement0Address = bufSliceHeader.Data
   932  }
   933  
   934  func (ce *callEngine) builtinFunctionTableGrow(ctx context.Context, tables []*wasm.TableInstance) {
   935  	tableIndex := uint32(ce.popValue())
   936  	table := tables[tableIndex] // verified not to be out of range by the func validation at compilation phase.
   937  	num := ce.popValue()
   938  	ref := ce.popValue()
   939  	res := table.Grow(ctx, uint32(num), uintptr(ref))
   940  	ce.pushValue(uint64(res))
   941  }
   942  
   943  func (ce *callEngine) builtinFunctionFunctionListenerBefore(ctx context.Context, fn *function) {
   944  	base := int(ce.stackBasePointerInBytes >> 3)
   945  	listerCtx := fn.parent.listener.Before(ctx, fn.source.Definition, ce.stack[base:base+fn.source.Type.ParamNumInUint64])
   946  	prevStackTop := ce.contextStack
   947  	ce.contextStack = &contextStack{self: ctx, prev: prevStackTop}
   948  	ce.ctx = listerCtx
   949  }
   950  
   951  func (ce *callEngine) builtinFunctionFunctionListenerAfter(ctx context.Context, fn *function) {
   952  	base := int(ce.stackBasePointerInBytes >> 3)
   953  	fn.parent.listener.After(ctx, fn.source.Definition, nil, ce.stack[base:base+fn.source.Type.ResultNumInUint64])
   954  	ce.ctx = ce.contextStack.self
   955  	ce.contextStack = ce.contextStack.prev
   956  }
   957  
   958  func compileGoDefinedHostFunction(ir *wazeroir.CompilationResult, withListener bool) (*code, error) {
   959  	compiler, err := newCompiler(ir, withListener)
   960  	if err != nil {
   961  		return nil, err
   962  	}
   963  
   964  	if err = compiler.compileGoDefinedHostFunction(); err != nil {
   965  		return nil, err
   966  	}
   967  
   968  	c, _, err := compiler.compile()
   969  	if err != nil {
   970  		return nil, err
   971  	}
   972  
   973  	return &code{codeSegment: c}, nil
   974  }
   975  
   976  func compileWasmFunction(_ api.CoreFeatures, ir *wazeroir.CompilationResult, withListener bool) (*code, error) {
   977  	compiler, err := newCompiler(ir, withListener)
   978  	if err != nil {
   979  		return nil, fmt.Errorf("failed to initialize assembly builder: %w", err)
   980  	}
   981  
   982  	if err := compiler.compilePreamble(); err != nil {
   983  		return nil, fmt.Errorf("failed to emit preamble: %w", err)
   984  	}
   985  
   986  	var skip bool
   987  	for _, op := range ir.Operations {
   988  		// Compiler determines whether skip the entire label.
   989  		// For example, if the label doesn't have any caller,
   990  		// we don't need to generate native code at all as we never reach the region.
   991  		if op.Kind() == wazeroir.OperationKindLabel {
   992  			skip = compiler.compileLabel(op.(*wazeroir.OperationLabel))
   993  		}
   994  		if skip {
   995  			continue
   996  		}
   997  
   998  		if false {
   999  			fmt.Printf("compiling op=%s: %s\n", op.Kind(), compiler)
  1000  		}
  1001  		var err error
  1002  		switch o := op.(type) {
  1003  		case *wazeroir.OperationLabel:
  1004  			// Label op is already handled ^^.
  1005  		case *wazeroir.OperationUnreachable:
  1006  			err = compiler.compileUnreachable()
  1007  		case *wazeroir.OperationBr:
  1008  			err = compiler.compileBr(o)
  1009  		case *wazeroir.OperationBrIf:
  1010  			err = compiler.compileBrIf(o)
  1011  		case *wazeroir.OperationBrTable:
  1012  			err = compiler.compileBrTable(o)
  1013  		case *wazeroir.OperationCall:
  1014  			err = compiler.compileCall(o)
  1015  		case *wazeroir.OperationCallIndirect:
  1016  			err = compiler.compileCallIndirect(o)
  1017  		case *wazeroir.OperationDrop:
  1018  			err = compiler.compileDrop(o)
  1019  		case *wazeroir.OperationSelect:
  1020  			err = compiler.compileSelect(o)
  1021  		case *wazeroir.OperationPick:
  1022  			err = compiler.compilePick(o)
  1023  		case *wazeroir.OperationSet:
  1024  			err = compiler.compileSet(o)
  1025  		case *wazeroir.OperationGlobalGet:
  1026  			err = compiler.compileGlobalGet(o)
  1027  		case *wazeroir.OperationGlobalSet:
  1028  			err = compiler.compileGlobalSet(o)
  1029  		case *wazeroir.OperationLoad:
  1030  			err = compiler.compileLoad(o)
  1031  		case *wazeroir.OperationLoad8:
  1032  			err = compiler.compileLoad8(o)
  1033  		case *wazeroir.OperationLoad16:
  1034  			err = compiler.compileLoad16(o)
  1035  		case *wazeroir.OperationLoad32:
  1036  			err = compiler.compileLoad32(o)
  1037  		case *wazeroir.OperationStore:
  1038  			err = compiler.compileStore(o)
  1039  		case *wazeroir.OperationStore8:
  1040  			err = compiler.compileStore8(o)
  1041  		case *wazeroir.OperationStore16:
  1042  			err = compiler.compileStore16(o)
  1043  		case *wazeroir.OperationStore32:
  1044  			err = compiler.compileStore32(o)
  1045  		case *wazeroir.OperationMemorySize:
  1046  			err = compiler.compileMemorySize()
  1047  		case *wazeroir.OperationMemoryGrow:
  1048  			err = compiler.compileMemoryGrow()
  1049  		case *wazeroir.OperationConstI32:
  1050  			err = compiler.compileConstI32(o)
  1051  		case *wazeroir.OperationConstI64:
  1052  			err = compiler.compileConstI64(o)
  1053  		case *wazeroir.OperationConstF32:
  1054  			err = compiler.compileConstF32(o)
  1055  		case *wazeroir.OperationConstF64:
  1056  			err = compiler.compileConstF64(o)
  1057  		case *wazeroir.OperationEq:
  1058  			err = compiler.compileEq(o)
  1059  		case *wazeroir.OperationNe:
  1060  			err = compiler.compileNe(o)
  1061  		case *wazeroir.OperationEqz:
  1062  			err = compiler.compileEqz(o)
  1063  		case *wazeroir.OperationLt:
  1064  			err = compiler.compileLt(o)
  1065  		case *wazeroir.OperationGt:
  1066  			err = compiler.compileGt(o)
  1067  		case *wazeroir.OperationLe:
  1068  			err = compiler.compileLe(o)
  1069  		case *wazeroir.OperationGe:
  1070  			err = compiler.compileGe(o)
  1071  		case *wazeroir.OperationAdd:
  1072  			err = compiler.compileAdd(o)
  1073  		case *wazeroir.OperationSub:
  1074  			err = compiler.compileSub(o)
  1075  		case *wazeroir.OperationMul:
  1076  			err = compiler.compileMul(o)
  1077  		case *wazeroir.OperationClz:
  1078  			err = compiler.compileClz(o)
  1079  		case *wazeroir.OperationCtz:
  1080  			err = compiler.compileCtz(o)
  1081  		case *wazeroir.OperationPopcnt:
  1082  			err = compiler.compilePopcnt(o)
  1083  		case *wazeroir.OperationDiv:
  1084  			err = compiler.compileDiv(o)
  1085  		case *wazeroir.OperationRem:
  1086  			err = compiler.compileRem(o)
  1087  		case *wazeroir.OperationAnd:
  1088  			err = compiler.compileAnd(o)
  1089  		case *wazeroir.OperationOr:
  1090  			err = compiler.compileOr(o)
  1091  		case *wazeroir.OperationXor:
  1092  			err = compiler.compileXor(o)
  1093  		case *wazeroir.OperationShl:
  1094  			err = compiler.compileShl(o)
  1095  		case *wazeroir.OperationShr:
  1096  			err = compiler.compileShr(o)
  1097  		case *wazeroir.OperationRotl:
  1098  			err = compiler.compileRotl(o)
  1099  		case *wazeroir.OperationRotr:
  1100  			err = compiler.compileRotr(o)
  1101  		case *wazeroir.OperationAbs:
  1102  			err = compiler.compileAbs(o)
  1103  		case *wazeroir.OperationNeg:
  1104  			err = compiler.compileNeg(o)
  1105  		case *wazeroir.OperationCeil:
  1106  			err = compiler.compileCeil(o)
  1107  		case *wazeroir.OperationFloor:
  1108  			err = compiler.compileFloor(o)
  1109  		case *wazeroir.OperationTrunc:
  1110  			err = compiler.compileTrunc(o)
  1111  		case *wazeroir.OperationNearest:
  1112  			err = compiler.compileNearest(o)
  1113  		case *wazeroir.OperationSqrt:
  1114  			err = compiler.compileSqrt(o)
  1115  		case *wazeroir.OperationMin:
  1116  			err = compiler.compileMin(o)
  1117  		case *wazeroir.OperationMax:
  1118  			err = compiler.compileMax(o)
  1119  		case *wazeroir.OperationCopysign:
  1120  			err = compiler.compileCopysign(o)
  1121  		case *wazeroir.OperationI32WrapFromI64:
  1122  			err = compiler.compileI32WrapFromI64()
  1123  		case *wazeroir.OperationITruncFromF:
  1124  			err = compiler.compileITruncFromF(o)
  1125  		case *wazeroir.OperationFConvertFromI:
  1126  			err = compiler.compileFConvertFromI(o)
  1127  		case *wazeroir.OperationF32DemoteFromF64:
  1128  			err = compiler.compileF32DemoteFromF64()
  1129  		case *wazeroir.OperationF64PromoteFromF32:
  1130  			err = compiler.compileF64PromoteFromF32()
  1131  		case *wazeroir.OperationI32ReinterpretFromF32:
  1132  			err = compiler.compileI32ReinterpretFromF32()
  1133  		case *wazeroir.OperationI64ReinterpretFromF64:
  1134  			err = compiler.compileI64ReinterpretFromF64()
  1135  		case *wazeroir.OperationF32ReinterpretFromI32:
  1136  			err = compiler.compileF32ReinterpretFromI32()
  1137  		case *wazeroir.OperationF64ReinterpretFromI64:
  1138  			err = compiler.compileF64ReinterpretFromI64()
  1139  		case *wazeroir.OperationExtend:
  1140  			err = compiler.compileExtend(o)
  1141  		case *wazeroir.OperationSignExtend32From8:
  1142  			err = compiler.compileSignExtend32From8()
  1143  		case *wazeroir.OperationSignExtend32From16:
  1144  			err = compiler.compileSignExtend32From16()
  1145  		case *wazeroir.OperationSignExtend64From8:
  1146  			err = compiler.compileSignExtend64From8()
  1147  		case *wazeroir.OperationSignExtend64From16:
  1148  			err = compiler.compileSignExtend64From16()
  1149  		case *wazeroir.OperationSignExtend64From32:
  1150  			err = compiler.compileSignExtend64From32()
  1151  		case *wazeroir.OperationDataDrop:
  1152  			err = compiler.compileDataDrop(o)
  1153  		case *wazeroir.OperationMemoryInit:
  1154  			err = compiler.compileMemoryInit(o)
  1155  		case *wazeroir.OperationMemoryCopy:
  1156  			err = compiler.compileMemoryCopy()
  1157  		case *wazeroir.OperationMemoryFill:
  1158  			err = compiler.compileMemoryFill()
  1159  		case *wazeroir.OperationTableInit:
  1160  			err = compiler.compileTableInit(o)
  1161  		case *wazeroir.OperationTableCopy:
  1162  			err = compiler.compileTableCopy(o)
  1163  		case *wazeroir.OperationElemDrop:
  1164  			err = compiler.compileElemDrop(o)
  1165  		case *wazeroir.OperationRefFunc:
  1166  			err = compiler.compileRefFunc(o)
  1167  		case *wazeroir.OperationTableGet:
  1168  			err = compiler.compileTableGet(o)
  1169  		case *wazeroir.OperationTableSet:
  1170  			err = compiler.compileTableSet(o)
  1171  		case *wazeroir.OperationTableGrow:
  1172  			err = compiler.compileTableGrow(o)
  1173  		case *wazeroir.OperationTableSize:
  1174  			err = compiler.compileTableSize(o)
  1175  		case *wazeroir.OperationTableFill:
  1176  			err = compiler.compileTableFill(o)
  1177  		case *wazeroir.OperationV128Const:
  1178  			err = compiler.compileV128Const(o)
  1179  		case *wazeroir.OperationV128Add:
  1180  			err = compiler.compileV128Add(o)
  1181  		case *wazeroir.OperationV128Sub:
  1182  			err = compiler.compileV128Sub(o)
  1183  		case *wazeroir.OperationV128Load:
  1184  			err = compiler.compileV128Load(o)
  1185  		case *wazeroir.OperationV128LoadLane:
  1186  			err = compiler.compileV128LoadLane(o)
  1187  		case *wazeroir.OperationV128Store:
  1188  			err = compiler.compileV128Store(o)
  1189  		case *wazeroir.OperationV128StoreLane:
  1190  			err = compiler.compileV128StoreLane(o)
  1191  		case *wazeroir.OperationV128ExtractLane:
  1192  			err = compiler.compileV128ExtractLane(o)
  1193  		case *wazeroir.OperationV128ReplaceLane:
  1194  			err = compiler.compileV128ReplaceLane(o)
  1195  		case *wazeroir.OperationV128Splat:
  1196  			err = compiler.compileV128Splat(o)
  1197  		case *wazeroir.OperationV128Shuffle:
  1198  			err = compiler.compileV128Shuffle(o)
  1199  		case *wazeroir.OperationV128Swizzle:
  1200  			err = compiler.compileV128Swizzle(o)
  1201  		case *wazeroir.OperationV128AnyTrue:
  1202  			err = compiler.compileV128AnyTrue(o)
  1203  		case *wazeroir.OperationV128AllTrue:
  1204  			err = compiler.compileV128AllTrue(o)
  1205  		case *wazeroir.OperationV128BitMask:
  1206  			err = compiler.compileV128BitMask(o)
  1207  		case *wazeroir.OperationV128And:
  1208  			err = compiler.compileV128And(o)
  1209  		case *wazeroir.OperationV128Not:
  1210  			err = compiler.compileV128Not(o)
  1211  		case *wazeroir.OperationV128Or:
  1212  			err = compiler.compileV128Or(o)
  1213  		case *wazeroir.OperationV128Xor:
  1214  			err = compiler.compileV128Xor(o)
  1215  		case *wazeroir.OperationV128Bitselect:
  1216  			err = compiler.compileV128Bitselect(o)
  1217  		case *wazeroir.OperationV128AndNot:
  1218  			err = compiler.compileV128AndNot(o)
  1219  		case *wazeroir.OperationV128Shr:
  1220  			err = compiler.compileV128Shr(o)
  1221  		case *wazeroir.OperationV128Shl:
  1222  			err = compiler.compileV128Shl(o)
  1223  		case *wazeroir.OperationV128Cmp:
  1224  			err = compiler.compileV128Cmp(o)
  1225  		case *wazeroir.OperationV128AddSat:
  1226  			err = compiler.compileV128AddSat(o)
  1227  		case *wazeroir.OperationV128SubSat:
  1228  			err = compiler.compileV128SubSat(o)
  1229  		case *wazeroir.OperationV128Mul:
  1230  			err = compiler.compileV128Mul(o)
  1231  		case *wazeroir.OperationV128Div:
  1232  			err = compiler.compileV128Div(o)
  1233  		case *wazeroir.OperationV128Neg:
  1234  			err = compiler.compileV128Neg(o)
  1235  		case *wazeroir.OperationV128Sqrt:
  1236  			err = compiler.compileV128Sqrt(o)
  1237  		case *wazeroir.OperationV128Abs:
  1238  			err = compiler.compileV128Abs(o)
  1239  		case *wazeroir.OperationV128Popcnt:
  1240  			err = compiler.compileV128Popcnt(o)
  1241  		case *wazeroir.OperationV128Min:
  1242  			err = compiler.compileV128Min(o)
  1243  		case *wazeroir.OperationV128Max:
  1244  			err = compiler.compileV128Max(o)
  1245  		case *wazeroir.OperationV128AvgrU:
  1246  			err = compiler.compileV128AvgrU(o)
  1247  		case *wazeroir.OperationV128Pmin:
  1248  			err = compiler.compileV128Pmin(o)
  1249  		case *wazeroir.OperationV128Pmax:
  1250  			err = compiler.compileV128Pmax(o)
  1251  		case *wazeroir.OperationV128Ceil:
  1252  			err = compiler.compileV128Ceil(o)
  1253  		case *wazeroir.OperationV128Floor:
  1254  			err = compiler.compileV128Floor(o)
  1255  		case *wazeroir.OperationV128Trunc:
  1256  			err = compiler.compileV128Trunc(o)
  1257  		case *wazeroir.OperationV128Nearest:
  1258  			err = compiler.compileV128Nearest(o)
  1259  		case *wazeroir.OperationV128Extend:
  1260  			err = compiler.compileV128Extend(o)
  1261  		case *wazeroir.OperationV128ExtMul:
  1262  			err = compiler.compileV128ExtMul(o)
  1263  		case *wazeroir.OperationV128Q15mulrSatS:
  1264  			err = compiler.compileV128Q15mulrSatS(o)
  1265  		case *wazeroir.OperationV128ExtAddPairwise:
  1266  			err = compiler.compileV128ExtAddPairwise(o)
  1267  		case *wazeroir.OperationV128FloatPromote:
  1268  			err = compiler.compileV128FloatPromote(o)
  1269  		case *wazeroir.OperationV128FloatDemote:
  1270  			err = compiler.compileV128FloatDemote(o)
  1271  		case *wazeroir.OperationV128FConvertFromI:
  1272  			err = compiler.compileV128FConvertFromI(o)
  1273  		case *wazeroir.OperationV128Dot:
  1274  			err = compiler.compileV128Dot(o)
  1275  		case *wazeroir.OperationV128Narrow:
  1276  			err = compiler.compileV128Narrow(o)
  1277  		case *wazeroir.OperationV128ITruncSatFromF:
  1278  			err = compiler.compileV128ITruncSatFromF(o)
  1279  		default:
  1280  			err = errors.New("unsupported")
  1281  		}
  1282  		if err != nil {
  1283  			return nil, fmt.Errorf("operation %s: %w", op.Kind().String(), err)
  1284  		}
  1285  	}
  1286  
  1287  	c, stackPointerCeil, err := compiler.compile()
  1288  	if err != nil {
  1289  		return nil, fmt.Errorf("failed to compile: %w", err)
  1290  	}
  1291  
  1292  	return &code{codeSegment: c, stackPointerCeil: stackPointerCeil}, nil
  1293  }