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 }