Repository: tetratelabs/wazero
Pull request #1903 (2024-01-07 22:23:59)

wasi: sockets, use WSAPoll and GetOverlappedResult on Windows
Current `readSocket` function for WASI sockets is implemented incorrectly on Windows and WASI polling an async socket for a long time will eventually get `ERROR_WORKING_SET_QUOTA` (1453):

```
Insufficient quota to complete the requested service.
```

A call to `GetOverlappedResult` is expected after each `ERROR_IO_PENDING` returned by `ReadFile` and that is what's missing in the current implementation. Besides that, `ReadFile` would create a background worker to keep on trying reading from the handle into the `buf`, which could result in data loss if we don't keep tracking of it. So we add a call to `WSAPoll` before calling `ReadFile` to observe how many bytes are ready to be read. 

In this pull request, we modify how `readSocket` in `internal/sysfs/file_windows.go` is implemented by adding the following changes: 
- [x] Call `WSAPoll` before calling `ReadFile`, abort if ~no bytes are available~ no fd is ready to be read. 
- [x] Call `GetOverlappedResult` if `ReadFile` returns `ERROR_IO_PENDING`. 
- [x] ~Limit the read buffer's size according to how many bytes are ready to be read (see review comments)~ 
- [x] Cancel the ongoing overlapped read if it cannot be completed immediately.


Patch:
diff --git a/internal/engine/wazevo/backend/abi.go b/internal/engine/wazevo/backend/abi.go
index 67f464a5..bae83848 100644
--- a/internal/engine/wazevo/backend/abi.go
+++ b/internal/engine/wazevo/backend/abi.go
@@ -7,15 +7,9 @@ import (
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
 )
 
-type FunctionABIRegInfo interface {
-	// ArgsResultsRegs returns the registers used for passing parameters.
-	ArgsResultsRegs() (argInts, argFloats, resultInt, resultFloats []regalloc.RealReg)
-}
-
 type (
 	// FunctionABI represents the ABI information for a function which corresponds to a ssa.Signature.
-	FunctionABI[R FunctionABIRegInfo] struct {
-		r           R
+	FunctionABI struct {
 		Initialized bool
 
 		Args, Rets                 []ABIArg
@@ -70,19 +64,17 @@ func (a ABIArgKind) String() string {
 }
 
 // Init initializes the abiImpl for the given signature.
-func (a *FunctionABI[M]) Init(sig *ssa.Signature) {
-	argInts, argFloats, resultInts, resultFloats := a.r.ArgsResultsRegs()
-
+func (a *FunctionABI) Init(sig *ssa.Signature, argResultInts, argResultFloats []regalloc.RealReg) {
 	if len(a.Rets) < len(sig.Results) {
 		a.Rets = make([]ABIArg, len(sig.Results))
 	}
 	a.Rets = a.Rets[:len(sig.Results)]
-	a.RetStackSize = a.setABIArgs(a.Rets, sig.Results, argInts, argFloats)
+	a.RetStackSize = a.setABIArgs(a.Rets, sig.Results, argResultInts, argResultFloats)
 	if argsNum := len(sig.Params); len(a.Args) < argsNum {
 		a.Args = make([]ABIArg, argsNum)
 	}
 	a.Args = a.Args[:len(sig.Params)]
-	a.ArgStackSize = a.setABIArgs(a.Args, sig.Params, resultInts, resultFloats)
+	a.ArgStackSize = a.setABIArgs(a.Args, sig.Params, argResultInts, argResultFloats)
 
 	// Gather the real registers usages in arg/return.
 	a.RetRealRegs = a.RetRealRegs[:0]
@@ -106,7 +98,7 @@ func (a *FunctionABI[M]) Init(sig *ssa.Signature) {
 
 // setABIArgs sets the ABI arguments in the given slice. This assumes that len(s) >= len(types)
 // where if len(s) > len(types), the last elements of s is for the multi-return slot.
-func (a *FunctionABI[M]) setABIArgs(s []ABIArg, types []ssa.Type, ints, floats []regalloc.RealReg) (stackSize int64) {
+func (a *FunctionABI) setABIArgs(s []ABIArg, types []ssa.Type, ints, floats []regalloc.RealReg) (stackSize int64) {
 	il, fl := len(ints), len(floats)
 
 	var stackOffset int64
@@ -145,7 +137,7 @@ func (a *FunctionABI[M]) setABIArgs(s []ABIArg, types []ssa.Type, ints, floats [
 	return stackOffset
 }
 
-func (a *FunctionABI[M]) AlignedArgResultStackSlotSize() int64 {
+func (a *FunctionABI) AlignedArgResultStackSlotSize() int64 {
 	stackSlotSize := a.RetStackSize + a.ArgStackSize
 	// Align stackSlotSize to 16 bytes.
 	stackSlotSize = (stackSlotSize + 15) &^ 15
diff --git a/internal/engine/wazevo/backend/compiler.go b/internal/engine/wazevo/backend/compiler.go
index 87cc0c31..cca9e52c 100644
--- a/internal/engine/wazevo/backend/compiler.go
+++ b/internal/engine/wazevo/backend/compiler.go
@@ -16,9 +16,12 @@ func NewCompiler(ctx context.Context, mach Machine, builder ssa.Builder) Compile
 }
 
 func newCompiler(_ context.Context, mach Machine, builder ssa.Builder) *compiler {
+	argResultInts, argResultFloats := mach.ArgsResultsRegs()
 	c := &compiler{
 		mach: mach, ssaBuilder: builder,
-		nextVRegID: regalloc.VRegIDNonReservedBegin,
+		nextVRegID:      regalloc.VRegIDNonReservedBegin,
+		argResultInts:   argResultInts,
+		argResultFloats: argResultFloats,
 	}
 	mach.SetCompiler(c)
 	return c
@@ -94,8 +97,17 @@ type Compiler interface {
 	// SourceOffsetInfo returns the source offset information for the current buffer offset.
 	SourceOffsetInfo() []SourceOffsetInfo
 
+	// EmitByte appends a byte to the buffer. Used during the code emission.
+	EmitByte(b byte)
+
 	// Emit4Bytes appends 4 bytes to the buffer. Used during the code emission.
 	Emit4Bytes(b uint32)
+
+	// Emit8Bytes appends 8 bytes to the buffer. Used during the code emission.
+	Emit8Bytes(b uint64)
+
+	// GetFunctionABI returns the ABI information for the given signature.
+	GetFunctionABI(sig *ssa.Signature) *FunctionABI
 }
 
 // RelocationInfo represents the relocation information for a call instruction.
@@ -135,6 +147,9 @@ type compiler struct {
 	buf             []byte
 	relocations     []RelocationInfo
 	sourceOffsets   []SourceOffsetInfo
+	// abis maps ssa.SignatureID to the ABI implementation.
+	abis                           []FunctionABI
+	argResultInts, argResultFloats []regalloc.RealReg
 }
 
 // SourceOffsetInfo is a data to associate the source offset with the executable offset.
@@ -371,12 +386,36 @@ func (c *compiler) AddRelocationInfo(funcRef ssa.FuncRef) {
 	})
 }
 
-// Emit4Bytes implements Compiler.Add4Bytes.
+// Emit8Bytes implements Compiler.Emit8Bytes.
+func (c *compiler) Emit8Bytes(b uint64) {
+	c.buf = append(c.buf, byte(b), byte(b>>8), byte(b>>16), byte(b>>24), byte(b>>32), byte(b>>40), byte(b>>48), byte(b>>56))
+}
+
+// Emit4Bytes implements Compiler.Emit4Bytes.
 func (c *compiler) Emit4Bytes(b uint32) {
 	c.buf = append(c.buf, byte(b), byte(b>>8), byte(b>>16), byte(b>>24))
 }
 
+// EmitByte implements Compiler.EmitByte.
+func (c *compiler) EmitByte(b byte) {
+	c.buf = append(c.buf, b)
+}
+
 // Buf implements Compiler.Buf.
 func (c *compiler) Buf() []byte {
 	return c.buf
 }
+
+func (c *compiler) GetFunctionABI(sig *ssa.Signature) *FunctionABI {
+	if int(sig.ID) >= len(c.abis) {
+		c.abis = append(c.abis, make([]FunctionABI, int(sig.ID)+1)...)
+	}
+
+	abi := &c.abis[sig.ID]
+	if abi.Initialized {
+		return abi
+	}
+
+	abi.Init(sig, c.argResultInts, c.argResultFloats)
+	return abi
+}
diff --git a/internal/engine/wazevo/backend/compiler_lower.go b/internal/engine/wazevo/backend/compiler_lower.go
index 676c1a72..e59f8675 100644
--- a/internal/engine/wazevo/backend/compiler_lower.go
+++ b/internal/engine/wazevo/backend/compiler_lower.go
@@ -8,7 +8,7 @@ import (
 // Lower implements Compiler.Lower.
 func (c *compiler) Lower() {
 	c.assignVirtualRegisters()
-	c.mach.InitializeABI(c.ssaBuilder.Signature())
+	c.mach.SetCurrentABI(c.GetFunctionABI(c.ssaBuilder.Signature()))
 	c.mach.ExecutableContext().StartLoweringFunction(c.ssaBuilder.BlockIDMax())
 	c.lowerBlocks()
 }
diff --git a/internal/engine/wazevo/backend/isa/amd64/abi.go b/internal/engine/wazevo/backend/isa/amd64/abi.go
index 9771fb4f..19a29824 100644
--- a/internal/engine/wazevo/backend/isa/amd64/abi.go
+++ b/internal/engine/wazevo/backend/isa/amd64/abi.go
@@ -1,7 +1,6 @@
 package amd64
 
 import (
-	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
 )
@@ -47,28 +46,9 @@ var regInfo = &regalloc.RegisterInfo{
 	},
 }
 
-// functionABIRegInfo implements backend.FunctionABIRegInfo.
-type functionABIRegInfo struct{}
-
-// ArgsResultsRegs implements backend.FunctionABIRegInfo.
-func (f functionABIRegInfo) ArgsResultsRegs() (argInts, argFloats, resultInt, resultFloats []regalloc.RealReg) {
-	return intArgResultRegs, floatArgResultRegs, intArgResultRegs, floatArgResultRegs
-}
-
-type abiImpl = backend.FunctionABI[functionABIRegInfo]
-
-func (m *machine) getOrCreateFunctionABI(sig *ssa.Signature) *abiImpl {
-	if int(sig.ID) >= len(m.abis) {
-		m.abis = append(m.abis, make([]abiImpl, int(sig.ID)+1)...)
-	}
-
-	abi := &m.abis[sig.ID]
-	if abi.Initialized {
-		return abi
-	}
-
-	abi.Init(sig)
-	return abi
+// ArgsResultsRegs implements backend.Machine.
+func (m *machine) ArgsResultsRegs() (argResultInts, argResultFloats []regalloc.RealReg) {
+	return intArgResultRegs, floatArgResultRegs
 }
 
 // LowerParams implements backend.Machine.
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr.go b/internal/engine/wazevo/backend/isa/amd64/instr.go
index 8cf1369b..abc33a15 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr.go
@@ -1,12 +1,180 @@
 package amd64
 
+import (
+	"fmt"
+
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
+)
+
 type instruction struct {
-	prev, next *instruction
+	kind                instructionKind
+	prev, next          *instruction
+	abi                 *backend.FunctionABI
+	op1, op2            operand
+	u1                  uint64
+	b1                  bool
+	addedBeforeRegAlloc bool
+}
+
+// Next implements regalloc.Instr.
+func (i *instruction) Next() regalloc.Instr {
+	return i.next
+}
+
+// Prev implements regalloc.Instr.
+func (i *instruction) Prev() regalloc.Instr {
+	return i.prev
+}
+
+// IsCall implements regalloc.Instr.
+func (i *instruction) IsCall() bool { return i.kind == call }
+
+// IsIndirectCall implements regalloc.Instr.
+func (i *instruction) IsIndirectCall() bool { return i.kind == callIndirect }
+
+// IsReturn implements regalloc.Instr.
+func (i *instruction) IsReturn() bool { return i.kind == ret }
+
+// AddedBeforeRegAlloc implements regalloc.Instr.
+func (i *instruction) AddedBeforeRegAlloc() bool { return i.addedBeforeRegAlloc }
+
+// String implements regalloc.Instr.
+func (i *instruction) String() string {
+	switch i.kind {
+	case nop0:
+		return "nop"
+	case ret:
+		return "ret"
+	case imm:
+		if i.b1 {
+			return fmt.Sprintf("movabsq $%d, %s", int64(i.u1), i.op1.format(true))
+		} else {
+			return fmt.Sprintf("movl $%d, %s", int32(i.u1), i.op1.format(false))
+		}
+	case aluRmiR:
+		return fmt.Sprintf("%s %s, %s", aluRmiROpcode(i.u1), i.op1.format(i.b1), i.op2.format(i.b1))
+	case movRR:
+		if i.b1 {
+			return fmt.Sprintf("movq %s, %s", i.op1.format(true), i.op2.format(true))
+		} else {
+			return fmt.Sprintf("movl %s, %s", i.op1.format(false), i.op2.format(false))
+		}
+	case xmmRmR:
+		return fmt.Sprintf("%s %s, %s", sseOpcode(i.u1), i.op1.format(i.b1), i.op2.format(i.b1))
+	case gprToXmm:
+		return fmt.Sprintf("%s %s, %s", sseOpcode(i.u1), i.op1.format(i.b1), i.op2.format(i.b1))
+	case xmmUnaryRmR:
+		return fmt.Sprintf("%s %s, %s", sseOpcode(i.u1), i.op1.format(i.b1), i.op2.format(i.b1))
+	case unaryRmR:
+		panic("TODO")
+	case not:
+		panic("TODO")
+	case neg:
+		panic("TODO")
+	case div:
+		panic("TODO")
+	case mulHi:
+		panic("TODO")
+	case checkedDivOrRemSeq:
+		panic("TODO")
+	case signExtendData:
+		panic("TODO")
+	case movzxRmR:
+		panic("TODO")
+	case mov64MR:
+		panic("TODO")
+	case loadEffectiveAddress:
+		panic("TODO")
+	case movsxRmR:
+		panic("TODO")
+	case movRM:
+		panic("TODO")
+	case shiftR:
+		panic("TODO")
+	case xmmRmiReg:
+		panic("TODO")
+	case cmpRmiR:
+		panic("TODO")
+	case setcc:
+		panic("TODO")
+	case cmove:
+		panic("TODO")
+	case push64:
+		panic("TODO")
+	case pop64:
+		panic("TODO")
+	case xmmMovRM:
+		panic("TODO")
+	case xmmLoadConst:
+		panic("TODO")
+	case xmmToGpr:
+		panic("TODO")
+	case cvtUint64ToFloatSeq:
+		panic("TODO")
+	case cvtFloatToSintSeq:
+		panic("TODO")
+	case cvtFloatToUintSeq:
+		panic("TODO")
+	case xmmMinMaxSeq:
+		panic("TODO")
+	case xmmCmove:
+		panic("TODO")
+	case xmmCmpRmR:
+		panic("TODO")
+	case xmmRmRImm:
+		panic("TODO")
+	case jmpKnown:
+		panic("TODO")
+	case jmpIf:
+		panic("TODO")
+	case jmpCond:
+		panic("TODO")
+	case jmpTableSeq:
+		panic("TODO")
+	case jmpUnknown:
+		panic("TODO")
+	case trapIf:
+		panic("TODO")
+	case ud2:
+		panic("TODO")
+	default:
+		panic(fmt.Sprintf("BUG: %v", i.kind))
+	}
+}
+
+// Defs implements regalloc.Instr.
+func (i *instruction) Defs(i2 *[]regalloc.VReg) []regalloc.VReg {
+	// TODO implement me
+	panic("implement me")
+}
+
+// Uses implements regalloc.Instr.
+func (i *instruction) Uses(i2 *[]regalloc.VReg) []regalloc.VReg {
+	// TODO implement me
+	panic("implement me")
+}
+
+// AssignUse implements regalloc.Instr.
+func (i *instruction) AssignUse(index int, v regalloc.VReg) {
+	// TODO implement me
+	panic("implement me")
+}
+
+// AssignDef implements regalloc.Instr.
+func (i *instruction) AssignDef(reg regalloc.VReg) {
+	// TODO implement me
+	panic("implement me")
+}
+
+// IsCopy implements regalloc.Instr.
+func (i *instruction) IsCopy() bool {
+	// TODO implement me
+	panic("implement me")
 }
 
 func resetInstruction(i *instruction) {
-	i.prev = nil
-	i.next = nil
+	*i = instruction{}
 }
 
 func setNext(i *instruction, next *instruction) {
@@ -17,5 +185,865 @@ func setPrev(i *instruction, prev *instruction) {
 	i.prev = prev
 }
 
-func asNop(*instruction) {
+func asNop(i *instruction) {
+	i.kind = nop0
+}
+
+type instructionKind int
+
+const (
+	nop0 instructionKind = iota + 1
+
+	// Integer arithmetic/bit-twiddling: (add sub and or xor mul, etc.) (32 64) (reg addr imm) reg
+	aluRmiR
+
+	// Instructions on GPR that only read src and defines dst (dst is not modified): bsr, etc.
+	unaryRmR
+
+	// Bitwise not
+	not
+
+	// Integer negation
+	neg
+
+	// Integer quotient and remainder: (div idiv) $rax $rdx (reg addr)
+	div
+
+	// The high bits (RDX) of a (un)signed multiply: RDX:RAX := RAX * rhs.
+	mulHi
+
+	// A synthetic sequence to implement the right inline checks for remainder and division,
+	// assuming the dividend is in %rax.
+	// Puts the result back into %rax if is_div, %rdx if !is_div, to mimic what the div
+	// instruction does.
+	// The generated code sequence is described in the emit's function match arm for this
+	// instruction.
+	///
+	// Note: %rdx is marked as modified by this instruction, to avoid an early clobber problem
+	// with the temporary and divisor registers. Make sure to zero %rdx right before this
+	// instruction, or you might run into regalloc failures where %rdx is live before its first
+	// def!
+	checkedDivOrRemSeq
+
+	// Do a sign-extend based on the sign of the value in rax into rdx: (cwd cdq cqo)
+	// or al into ah: (cbw)
+	signExtendData
+
+	// Constant materialization: (imm32 imm64) reg.
+	// Either: movl $imm32, %reg32 or movabsq $imm64, %reg64.
+	imm
+
+	// GPR to GPR move: mov (64 32) reg reg.
+	movRR
+
+	// Zero-extended loads, except for 64 bits: movz (bl bq wl wq lq) addr reg.
+	// Note that the lq variant doesn't really exist since the default zero-extend rule makes it
+	// unnecessary. For that case we emit the equivalent "movl AM, reg32".
+	movzxRmR
+
+	// A plain 64-bit integer load, since MovZX_RM_R can't represent that.
+	mov64MR
+
+	// Loads the memory address of addr into dst.
+	loadEffectiveAddress
+
+	// Sign-extended loads and moves: movs (bl bq wl wq lq) addr reg.
+	movsxRmR
+
+	// Integer stores: mov (b w l q) reg addr.
+	movRM
+
+	// Arithmetic shifts: (shl shr sar) (b w l q) imm reg.
+	shiftR
+
+	// Arithmetic SIMD shifts.
+	xmmRmiReg
+
+	// Integer comparisons/tests: cmp or test (b w l q) (reg addr imm) reg.
+	cmpRmiR
+
+	// Materializes the requested condition code in the destination reg.
+	setcc
+
+	// Integer conditional move.
+	// Overwrites the destination register.
+	cmove
+
+	// pushq (reg addr imm)
+	push64
+
+	// popq reg
+	pop64
+
+	// XMM (scalar or vector) binary op: (add sub and or xor mul adc? sbb?) (32 64) (reg addr) reg
+	xmmRmR
+
+	// XMM (scalar or vector) unary op: mov between XMM registers (32 64) (reg addr) reg.
+	//
+	// This differs from xmmRmR in that the dst register of xmmUnaryRmR is not used in the
+	// computation of the instruction dst value and so does not have to be a previously valid
+	// value. This is characteristic of mov instructions.
+	xmmUnaryRmR
+
+	// XMM (scalar or vector) unary op (from xmm to reg/mem): stores, movd, movq
+	xmmMovRM
+
+	// XMM (vector) unary op (to move a constant value into an xmm register): movups
+	xmmLoadConst
+
+	// XMM (scalar) unary op (from xmm to integer reg): movd, movq, cvtts{s,d}2si
+	xmmToGpr
+
+	// XMM (scalar) unary op (from integer to float reg): movd, movq, cvtsi2s{s,d}
+	gprToXmm
+
+	// Converts an unsigned int64 to a float32/float64.
+	cvtUint64ToFloatSeq
+
+	// Converts a scalar xmm to a signed int32/int64.
+	cvtFloatToSintSeq
+
+	// Converts a scalar xmm to an unsigned int32/int64.
+	cvtFloatToUintSeq
+
+	// A sequence to compute min/max with the proper NaN semantics for xmm registers.
+	xmmMinMaxSeq
+
+	// XMM (scalar) conditional move.
+	// Overwrites the destination register if cc is set.
+	xmmCmove
+
+	// Float comparisons/tests: cmp (b w l q) (reg addr imm) reg.
+	xmmCmpRmR
+
+	// A binary XMM instruction with an 8-bit immediate: e.g. cmp (ps pd) imm (reg addr) reg
+	xmmRmRImm
+
+	// Direct call: call simm32.
+	call
+
+	// Indirect call: callq (reg mem).
+	callIndirect
+
+	// Return.
+	ret
+
+	// Jump to a known target: jmp simm32.
+	jmpKnown
+
+	// One-way conditional branch: jcond cond target.
+	///
+	// This instruction is useful when we have conditional jumps depending on more than two
+	// conditions, see for instance the lowering of Brz/brnz with Fcmp inputs.
+	///
+	// A note of caution: in contexts where the branch target is another block, this has to be the
+	// same successor as the one specified in the terminator branch of the current block.
+	// Otherwise, this might confuse register allocation by creating new invisible edges.
+	jmpIf
+
+	// Two-way conditional branch: jcond cond target target.
+	// Emitted as a compound sequence; the MachBuffer will shrink it as appropriate.
+	jmpCond
+
+	// Jump-table sequence, as one compound instruction (see note in lower.rs for rationale).
+	// The generated code sequence is described in the emit's function match arm for this
+	// instruction.
+	// See comment in lowering about the temporaries signedness.
+	jmpTableSeq
+
+	// Indirect jump: jmpq (reg mem).
+	jmpUnknown
+
+	// Traps if the condition code is set.
+	trapIf
+
+	// An instruction that will always trigger the illegal instruction exception.
+	ud2
+)
+
+func (k instructionKind) String() string {
+	switch k {
+	case nop0:
+		return "nop"
+	case ret:
+		return "ret"
+	case imm:
+		return "imm"
+	case aluRmiR:
+		return "aluRmiR"
+	case movRR:
+		return "movRR"
+	case xmmRmR:
+		return "xmmRmR"
+	case gprToXmm:
+		return "gprToXmm"
+	case xmmUnaryRmR:
+		return "xmmUnaryRmR"
+	case unaryRmR:
+		return "unaryRmR"
+	case not:
+		return "not"
+	case neg:
+		return "neg"
+	case div:
+		return "div"
+	case mulHi:
+		return "mulHi"
+	case checkedDivOrRemSeq:
+		return "checkedDivOrRemSeq"
+	case signExtendData:
+		return "signExtendData"
+	case movzxRmR:
+		return "movzxRmR"
+	case mov64MR:
+		return "mov64MR"
+	case loadEffectiveAddress:
+		return "loadEffectiveAddress"
+	case movsxRmR:
+		return "movsxRmR"
+	case movRM:
+		return "movRM"
+	case shiftR:
+		return "shiftR"
+	case xmmRmiReg:
+		return "xmmRmiReg"
+	case cmpRmiR:
+		return "cmpRmiR"
+	case setcc:
+		return "setcc"
+	case cmove:
+		return "cmove"
+	case push64:
+		return "push64"
+	case pop64:
+		return "pop64"
+	case xmmMovRM:
+		return "xmmMovRM"
+	case xmmLoadConst:
+		return "xmmLoadConst"
+	case xmmToGpr:
+		return "xmmToGpr"
+	case cvtUint64ToFloatSeq:
+		return "cvtUint64ToFloatSeq"
+	case cvtFloatToSintSeq:
+		return "cvtFloatToSintSeq"
+	case cvtFloatToUintSeq:
+		return "cvtFloatToUintSeq"
+	case xmmMinMaxSeq:
+		return "xmmMinMaxSeq"
+	case xmmCmove:
+		return "xmmCmove"
+	case xmmCmpRmR:
+		return "xmmCmpRmR"
+	case xmmRmRImm:
+		return "xmmRmRImm"
+	case jmpKnown:
+		return "jmpKnown"
+	case jmpIf:
+		return "jmpIf"
+	case jmpCond:
+		return "jmpCond"
+	case jmpTableSeq:
+		return "jmpTableSeq"
+	case jmpUnknown:
+		return "jmpUnknown"
+	case trapIf:
+		return "trapIf"
+	case ud2:
+		return "ud2"
+	default:
+		panic("BUG")
+	}
+}
+
+type aluRmiROpcode byte
+
+const (
+	aluRmiROpcodeAdd aluRmiROpcode = iota + 1
+	aluRmiROpcodeSub
+	aluRmiROpcodeAnd
+	aluRmiROpcodeOr
+	aluRmiROpcodeXor
+	aluRmiROpcodeMul
+)
+
+func (a aluRmiROpcode) String() string {
+	switch a {
+	case aluRmiROpcodeAdd:
+		return "add"
+	case aluRmiROpcodeSub:
+		return "sub"
+	case aluRmiROpcodeAnd:
+		return "and"
+	case aluRmiROpcodeOr:
+		return "or"
+	case aluRmiROpcodeXor:
+		return "xor"
+	case aluRmiROpcodeMul:
+		return "mul"
+	default:
+		panic("BUG")
+	}
+}
+
+func (i *instruction) asRet(abi *backend.FunctionABI) *instruction {
+	i.kind = ret
+	i.abi = abi
+	return i
+}
+
+func (i *instruction) asImm(dst regalloc.VReg, value uint64, _64 bool) *instruction {
+	i.kind = imm
+	i.op1 = newOperandReg(dst)
+	i.u1 = value
+	i.b1 = _64
+	return i
+}
+
+func (i *instruction) asAluRmiR(op aluRmiROpcode, rm operand, rd regalloc.VReg, _64 bool) *instruction {
+	if rm.kind != operandKindReg && rm.kind != operandKindMem && rm.kind != operandImm32 {
+		panic("BUG")
+	}
+	i.kind = aluRmiR
+	i.op1 = rm
+	i.op2 = newOperandReg(rd)
+	i.u1 = uint64(op)
+	i.b1 = _64
+	return i
+}
+
+func (i *instruction) asXmmRmR(op sseOpcode, rm operand, rd regalloc.VReg, _64 bool) *instruction {
+	if rm.kind != operandKindReg && rm.kind != operandKindMem {
+		panic("BUG")
+	}
+	i.kind = xmmRmR
+	i.op1 = rm
+	i.op2 = newOperandReg(rd)
+	i.u1 = uint64(op)
+	i.b1 = _64
+	return i
+}
+
+func (i *instruction) asGprToXmm(op sseOpcode, rm operand, rd regalloc.VReg, _64 bool) *instruction {
+	if rm.kind != operandKindReg && rm.kind != operandKindMem {
+		panic("BUG")
+	}
+	i.kind = gprToXmm
+	i.op1 = rm
+	i.op2 = newOperandReg(rd)
+	i.u1 = uint64(op)
+	i.b1 = _64
+	return i
+}
+
+func (i *instruction) asMovRR(rm, rd regalloc.VReg, _64 bool) *instruction {
+	i.kind = movRR
+	i.op1 = newOperandReg(rm)
+	i.op2 = newOperandReg(rd)
+	i.b1 = _64
+	return i
+}
+
+func (i *instruction) asXmmUnaryRmR(op sseOpcode, rm operand, rd regalloc.VReg, _64 bool) *instruction {
+	if rm.kind != operandKindReg && rm.kind != operandKindMem {
+		panic("BUG")
+	}
+	i.kind = xmmUnaryRmR
+	i.op1 = rm
+	i.op2 = newOperandReg(rd)
+	i.u1 = uint64(op)
+	i.b1 = _64
+	return i
+}
+
+type sseOpcode byte
+
+const (
+	sseOpcodeAddps sseOpcode = iota + 1
+	sseOpcodeAddpd
+	sseOpcodeAddss
+	sseOpcodeAddsd
+	sseOpcodeAndps
+	sseOpcodeAndpd
+	sseOpcodeAndnps
+	sseOpcodeAndnpd
+	sseOpcodeComiss
+	sseOpcodeComisd
+	sseOpcodeCmpps
+	sseOpcodeCmppd
+	sseOpcodeCmpss
+	sseOpcodeCmpsd
+	sseOpcodeCvtdq2ps
+	sseOpcodeCvtsd2ss
+	sseOpcodeCvtsd2si
+	sseOpcodeCvtsi2ss
+	sseOpcodeCvtsi2sd
+	sseOpcodeCvtss2si
+	sseOpcodeCvtss2sd
+	sseOpcodeCvttps2dq
+	sseOpcodeCvttss2si
+	sseOpcodeCvttsd2si
+	sseOpcodeDivps
+	sseOpcodeDivpd
+	sseOpcodeDivss
+	sseOpcodeDivsd
+	sseOpcodeInsertps
+	sseOpcodeMaxps
+	sseOpcodeMaxpd
+	sseOpcodeMaxss
+	sseOpcodeMaxsd
+	sseOpcodeMinps
+	sseOpcodeMinpd
+	sseOpcodeMinss
+	sseOpcodeMinsd
+	sseOpcodeMovaps
+	sseOpcodeMovapd
+	sseOpcodeMovd
+	sseOpcodeMovdqa
+	sseOpcodeMovdqu
+	sseOpcodeMovlhps
+	sseOpcodeMovmskps
+	sseOpcodeMovmskpd
+	sseOpcodeMovq
+	sseOpcodeMovss
+	sseOpcodeMovsd
+	sseOpcodeMovups
+	sseOpcodeMovupd
+	sseOpcodeMulps
+	sseOpcodeMulpd
+	sseOpcodeMulss
+	sseOpcodeMulsd
+	sseOpcodeOrps
+	sseOpcodeOrpd
+	sseOpcodePabsb
+	sseOpcodePabsw
+	sseOpcodePabsd
+	sseOpcodePackssdw
+	sseOpcodePacksswb
+	sseOpcodePackusdw
+	sseOpcodePackuswb
+	sseOpcodePaddb
+	sseOpcodePaddd
+	sseOpcodePaddq
+	sseOpcodePaddw
+	sseOpcodePaddsb
+	sseOpcodePaddsw
+	sseOpcodePaddusb
+	sseOpcodePaddusw
+	sseOpcodePalignr
+	sseOpcodePand
+	sseOpcodePandn
+	sseOpcodePavgb
+	sseOpcodePavgw
+	sseOpcodePcmpeqb
+	sseOpcodePcmpeqw
+	sseOpcodePcmpeqd
+	sseOpcodePcmpeqq
+	sseOpcodePcmpgtb
+	sseOpcodePcmpgtw
+	sseOpcodePcmpgtd
+	sseOpcodePcmpgtq
+	sseOpcodePextrb
+	sseOpcodePextrw
+	sseOpcodePextrd
+	sseOpcodePinsrb
+	sseOpcodePinsrw
+	sseOpcodePinsrd
+	sseOpcodePmaddwd
+	sseOpcodePmaxsb
+	sseOpcodePmaxsw
+	sseOpcodePmaxsd
+	sseOpcodePmaxub
+	sseOpcodePmaxuw
+	sseOpcodePmaxud
+	sseOpcodePminsb
+	sseOpcodePminsw
+	sseOpcodePminsd
+	sseOpcodePminub
+	sseOpcodePminuw
+	sseOpcodePminud
+	sseOpcodePmovmskb
+	sseOpcodePmovsxbd
+	sseOpcodePmovsxbw
+	sseOpcodePmovsxbq
+	sseOpcodePmovsxwd
+	sseOpcodePmovsxwq
+	sseOpcodePmovsxdq
+	sseOpcodePmovzxbd
+	sseOpcodePmovzxbw
+	sseOpcodePmovzxbq
+	sseOpcodePmovzxwd
+	sseOpcodePmovzxwq
+	sseOpcodePmovzxdq
+	sseOpcodePmulld
+	sseOpcodePmullw
+	sseOpcodePmuludq
+	sseOpcodePor
+	sseOpcodePshufb
+	sseOpcodePshufd
+	sseOpcodePsllw
+	sseOpcodePslld
+	sseOpcodePsllq
+	sseOpcodePsraw
+	sseOpcodePsrad
+	sseOpcodePsrlw
+	sseOpcodePsrld
+	sseOpcodePsrlq
+	sseOpcodePsubb
+	sseOpcodePsubd
+	sseOpcodePsubq
+	sseOpcodePsubw
+	sseOpcodePsubsb
+	sseOpcodePsubsw
+	sseOpcodePsubusb
+	sseOpcodePsubusw
+	sseOpcodePtest
+	sseOpcodePunpckhbw
+	sseOpcodePunpcklbw
+	sseOpcodePxor
+	sseOpcodeRcpss
+	sseOpcodeRoundps
+	sseOpcodeRoundpd
+	sseOpcodeRoundss
+	sseOpcodeRoundsd
+	sseOpcodeRsqrtss
+	sseOpcodeSqrtps
+	sseOpcodeSqrtpd
+	sseOpcodeSqrtss
+	sseOpcodeSqrtsd
+	sseOpcodeSubps
+	sseOpcodeSubpd
+	sseOpcodeSubss
+	sseOpcodeSubsd
+	sseOpcodeUcomiss
+	sseOpcodeUcomisd
+	sseOpcodeXorps
+	sseOpcodeXorpd
+)
+
+func (s sseOpcode) String() string {
+	switch s {
+	case sseOpcodeAddps:
+		return "addps"
+	case sseOpcodeAddpd:
+		return "addpd"
+	case sseOpcodeAddss:
+		return "addss"
+	case sseOpcodeAddsd:
+		return "addsd"
+	case sseOpcodeAndps:
+		return "andps"
+	case sseOpcodeAndpd:
+		return "andpd"
+	case sseOpcodeAndnps:
+		return "andnps"
+	case sseOpcodeAndnpd:
+		return "andnpd"
+	case sseOpcodeComiss:
+		return "comiss"
+	case sseOpcodeComisd:
+		return "comisd"
+	case sseOpcodeCmpps:
+		return "cmpps"
+	case sseOpcodeCmppd:
+		return "cmppd"
+	case sseOpcodeCmpss:
+		return "cmpss"
+	case sseOpcodeCmpsd:
+		return "cmpsd"
+	case sseOpcodeCvtdq2ps:
+		return "cvtdq2ps"
+	case sseOpcodeCvtsd2ss:
+		return "cvtsd2ss"
+	case sseOpcodeCvtsd2si:
+		return "cvtsd2si"
+	case sseOpcodeCvtsi2ss:
+		return "cvtsi2ss"
+	case sseOpcodeCvtsi2sd:
+		return "cvtsi2sd"
+	case sseOpcodeCvtss2si:
+		return "cvtss2si"
+	case sseOpcodeCvtss2sd:
+		return "cvtss2sd"
+	case sseOpcodeCvttps2dq:
+		return "cvttps2dq"
+	case sseOpcodeCvttss2si:
+		return "cvttss2si"
+	case sseOpcodeCvttsd2si:
+		return "cvttsd2si"
+	case sseOpcodeDivps:
+		return "divps"
+	case sseOpcodeDivpd:
+		return "divpd"
+	case sseOpcodeDivss:
+		return "divss"
+	case sseOpcodeDivsd:
+		return "divsd"
+	case sseOpcodeInsertps:
+		return "insertps"
+	case sseOpcodeMaxps:
+		return "maxps"
+	case sseOpcodeMaxpd:
+		return "maxpd"
+	case sseOpcodeMaxss:
+		return "maxss"
+	case sseOpcodeMaxsd:
+		return "maxsd"
+	case sseOpcodeMinps:
+		return "minps"
+	case sseOpcodeMinpd:
+		return "minpd"
+	case sseOpcodeMinss:
+		return "minss"
+	case sseOpcodeMinsd:
+		return "minsd"
+	case sseOpcodeMovaps:
+		return "movaps"
+	case sseOpcodeMovapd:
+		return "movapd"
+	case sseOpcodeMovd:
+		return "movd"
+	case sseOpcodeMovdqa:
+		return "movdqa"
+	case sseOpcodeMovdqu:
+		return "movdqu"
+	case sseOpcodeMovlhps:
+		return "movlhps"
+	case sseOpcodeMovmskps:
+		return "movmskps"
+	case sseOpcodeMovmskpd:
+		return "movmskpd"
+	case sseOpcodeMovq:
+		return "movq"
+	case sseOpcodeMovss:
+		return "movss"
+	case sseOpcodeMovsd:
+		return "movsd"
+	case sseOpcodeMovups:
+		return "movups"
+	case sseOpcodeMovupd:
+		return "movupd"
+	case sseOpcodeMulps:
+		return "mulps"
+	case sseOpcodeMulpd:
+		return "mulpd"
+	case sseOpcodeMulss:
+		return "mulss"
+	case sseOpcodeMulsd:
+		return "mulsd"
+	case sseOpcodeOrps:
+		return "orps"
+	case sseOpcodeOrpd:
+		return "orpd"
+	case sseOpcodePabsb:
+		return "pabsb"
+	case sseOpcodePabsw:
+		return "pabsw"
+	case sseOpcodePabsd:
+		return "pabsd"
+	case sseOpcodePackssdw:
+		return "packssdw"
+	case sseOpcodePacksswb:
+		return "packsswb"
+	case sseOpcodePackusdw:
+		return "packusdw"
+	case sseOpcodePackuswb:
+		return "packuswb"
+	case sseOpcodePaddb:
+		return "paddb"
+	case sseOpcodePaddd:
+		return "paddd"
+	case sseOpcodePaddq:
+		return "paddq"
+	case sseOpcodePaddw:
+		return "paddw"
+	case sseOpcodePaddsb:
+		return "paddsb"
+	case sseOpcodePaddsw:
+		return "paddsw"
+	case sseOpcodePaddusb:
+		return "paddusb"
+	case sseOpcodePaddusw:
+		return "paddusw"
+	case sseOpcodePalignr:
+		return "palignr"
+	case sseOpcodePand:
+		return "pand"
+	case sseOpcodePandn:
+		return "pandn"
+	case sseOpcodePavgb:
+		return "pavgb"
+	case sseOpcodePavgw:
+		return "pavgw"
+	case sseOpcodePcmpeqb:
+		return "pcmpeqb"
+	case sseOpcodePcmpeqw:
+		return "pcmpeqw"
+	case sseOpcodePcmpeqd:
+		return "pcmpeqd"
+	case sseOpcodePcmpeqq:
+		return "pcmpeqq"
+	case sseOpcodePcmpgtb:
+		return "pcmpgtb"
+	case sseOpcodePcmpgtw:
+		return "pcmpgtw"
+	case sseOpcodePcmpgtd:
+		return "pcmpgtd"
+	case sseOpcodePcmpgtq:
+		return "pcmpgtq"
+	case sseOpcodePextrb:
+		return "pextrb"
+	case sseOpcodePextrw:
+		return "pextrw"
+	case sseOpcodePextrd:
+		return "pextrd"
+	case sseOpcodePinsrb:
+		return "pinsrb"
+	case sseOpcodePinsrw:
+		return "pinsrw"
+	case sseOpcodePinsrd:
+		return "pinsrd"
+	case sseOpcodePmaddwd:
+		return "pmaddwd"
+	case sseOpcodePmaxsb:
+		return "pmaxsb"
+	case sseOpcodePmaxsw:
+		return "pmaxsw"
+	case sseOpcodePmaxsd:
+		return "pmaxsd"
+	case sseOpcodePmaxub:
+		return "pmaxub"
+	case sseOpcodePmaxuw:
+		return "pmaxuw"
+	case sseOpcodePmaxud:
+		return "pmaxud"
+	case sseOpcodePminsb:
+		return "pminsb"
+	case sseOpcodePminsw:
+		return "pminsw"
+	case sseOpcodePminsd:
+		return "pminsd"
+	case sseOpcodePminub:
+		return "pminub"
+	case sseOpcodePminuw:
+		return "pminuw"
+	case sseOpcodePminud:
+		return "pminud"
+	case sseOpcodePmovmskb:
+		return "pmovmskb"
+	case sseOpcodePmovsxbd:
+		return "pmovsxbd"
+	case sseOpcodePmovsxbw:
+		return "pmovsxbw"
+	case sseOpcodePmovsxbq:
+		return "pmovsxbq"
+	case sseOpcodePmovsxwd:
+		return "pmovsxwd"
+	case sseOpcodePmovsxwq:
+		return "pmovsxwq"
+	case sseOpcodePmovsxdq:
+		return "pmovsxdq"
+	case sseOpcodePmovzxbd:
+		return "pmovzxbd"
+	case sseOpcodePmovzxbw:
+		return "pmovzxbw"
+	case sseOpcodePmovzxbq:
+		return "pmovzxbq"
+	case sseOpcodePmovzxwd:
+		return "pmovzxwd"
+	case sseOpcodePmovzxwq:
+		return "pmovzxwq"
+	case sseOpcodePmovzxdq:
+		return "pmovzxdq"
+	case sseOpcodePmulld:
+		return "pmulld"
+	case sseOpcodePmullw:
+		return "pmullw"
+	case sseOpcodePmuludq:
+		return "pmuludq"
+	case sseOpcodePor:
+		return "por"
+	case sseOpcodePshufb:
+		return "pshufb"
+	case sseOpcodePshufd:
+		return "pshufd"
+	case sseOpcodePsllw:
+		return "psllw"
+	case sseOpcodePslld:
+		return "pslld"
+	case sseOpcodePsllq:
+		return "psllq"
+	case sseOpcodePsraw:
+		return "psraw"
+	case sseOpcodePsrad:
+		return "psrad"
+	case sseOpcodePsrlw:
+		return "psrlw"
+	case sseOpcodePsrld:
+		return "psrld"
+	case sseOpcodePsrlq:
+		return "psrlq"
+	case sseOpcodePsubb:
+		return "psubb"
+	case sseOpcodePsubd:
+		return "psubd"
+	case sseOpcodePsubq:
+		return "psubq"
+	case sseOpcodePsubw:
+		return "psubw"
+	case sseOpcodePsubsb:
+		return "psubsb"
+	case sseOpcodePsubsw:
+		return "psubsw"
+	case sseOpcodePsubusb:
+		return "psubusb"
+	case sseOpcodePsubusw:
+		return "psubusw"
+	case sseOpcodePtest:
+		return "ptest"
+	case sseOpcodePunpckhbw:
+		return "punpckhbw"
+	case sseOpcodePunpcklbw:
+		return "punpcklbw"
+	case sseOpcodePxor:
+		return "pxor"
+	case sseOpcodeRcpss:
+		return "rcpss"
+	case sseOpcodeRoundps:
+		return "roundps"
+	case sseOpcodeRoundpd:
+		return "roundpd"
+	case sseOpcodeRoundss:
+		return "roundss"
+	case sseOpcodeRoundsd:
+		return "roundsd"
+	case sseOpcodeRsqrtss:
+		return "rsqrtss"
+	case sseOpcodeSqrtps:
+		return "sqrtps"
+	case sseOpcodeSqrtpd:
+		return "sqrtpd"
+	case sseOpcodeSqrtss:
+		return "sqrtss"
+	case sseOpcodeSqrtsd:
+		return "sqrtsd"
+	case sseOpcodeSubps:
+		return "subps"
+	case sseOpcodeSubpd:
+		return "subpd"
+	case sseOpcodeSubss:
+		return "subss"
+	case sseOpcodeSubsd:
+		return "subsd"
+	case sseOpcodeUcomiss:
+		return "ucomiss"
+	case sseOpcodeUcomisd:
+		return "ucomisd"
+	case sseOpcodeXorps:
+		return "xorps"
+	case sseOpcodeXorpd:
+		return "xorpd"
+	default:
+		panic("BUG")
+	}
 }
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go b/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
new file mode 100644
index 00000000..f24c27dc
--- /dev/null
+++ b/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
@@ -0,0 +1,609 @@
+package amd64
+
+import (
+	"fmt"
+
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
+)
+
+// Encode implements backend.Machine Encode.
+func (m *machine) Encode() {
+	m.encode(m.ectx.RootInstr)
+}
+
+func (m *machine) encode(root *instruction) {
+	for cur := root; cur != nil; cur = cur.next {
+		cur.encode(m.c)
+	}
+}
+
+func (i *instruction) encode(c backend.Compiler) {
+	switch i.kind {
+	case nop0:
+	case ret:
+		c.EmitByte(0xc3)
+	case imm:
+		dst := regEncodings[i.op1.r.RealReg()]
+		con := i.u1
+		if i.b1 { // 64 bit.
+			if lower32willSignExtendTo64(con) {
+				// Sign extend mov(imm32).
+				encodeRegReg(c,
+					legacyPrefixesNone,
+					0xc7, 1,
+					0,
+					dst,
+					rexInfo(0).setW(),
+				)
+				c.Emit4Bytes(uint32(con))
+			} else {
+				c.EmitByte(rexEncodingW | dst.rexBit())
+				c.EmitByte(0xb8 | dst.encoding())
+				c.Emit8Bytes(con)
+			}
+		} else {
+			if dst.rexBit() > 0 {
+				c.EmitByte(rexEncodingDefault | 0x1)
+			}
+			c.EmitByte(0xb8 | dst.encoding())
+			c.Emit4Bytes(uint32(con))
+		}
+
+	case aluRmiR:
+		panic("TODO")
+
+	case movRR:
+		src := regEncodings[i.op1.r.RealReg()]
+		dst := regEncodings[i.op2.r.RealReg()]
+		var rex rexInfo
+		if i.b1 {
+			rex = rex.setW()
+		} else {
+			rex = rex.clearW()
+		}
+		encodeRegReg(c, legacyPrefixesNone, 0x89, 1, src, dst, rex)
+
+	case xmmRmR:
+		op := sseOpcode(i.u1)
+		var legPrex legacyPrefixes
+		var opcode uint32
+		var opcodeNum uint32
+		switch op {
+		case sseOpcodeAddps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F58, 2
+		case sseOpcodeAddpd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F58, 2
+		case sseOpcodeAddss:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF3, 0x0F58, 2
+		case sseOpcodeAddsd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF2, 0x0F58, 2
+		case sseOpcodeAndps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F54, 2
+		case sseOpcodeAndpd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F54, 2
+		case sseOpcodeAndnps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F55, 2
+		case sseOpcodeAndnpd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F55, 2
+		case sseOpcodeCvttps2dq:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF3, 0x0F5B, 2
+		case sseOpcodeCvtdq2ps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F5B, 2
+		case sseOpcodeDivps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F5E, 2
+		case sseOpcodeDivpd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F5E, 2
+		case sseOpcodeDivss:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF3, 0x0F5E, 2
+		case sseOpcodeDivsd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF2, 0x0F5E, 2
+		case sseOpcodeMaxps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F5F, 2
+		case sseOpcodeMaxpd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F5F, 2
+		case sseOpcodeMaxss:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF3, 0x0F5F, 2
+		case sseOpcodeMaxsd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF2, 0x0F5F, 2
+		case sseOpcodeMinps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F5D, 2
+		case sseOpcodeMinpd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F5D, 2
+		case sseOpcodeMinss:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF3, 0x0F5D, 2
+		case sseOpcodeMinsd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF2, 0x0F5D, 2
+		case sseOpcodeMovlhps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F16, 2
+		case sseOpcodeMovsd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF2, 0x0F10, 2
+		case sseOpcodeMulps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F59, 2
+		case sseOpcodeMulpd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F59, 2
+		case sseOpcodeMulss:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF3, 0x0F59, 2
+		case sseOpcodeMulsd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF2, 0x0F59, 2
+		case sseOpcodeOrpd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F56, 2
+		case sseOpcodeOrps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F56, 2
+		case sseOpcodePackssdw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F6B, 2
+		case sseOpcodePacksswb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F63, 2
+		case sseOpcodePackusdw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F382B, 3
+		case sseOpcodePackuswb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F67, 2
+		case sseOpcodePaddb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FFC, 2
+		case sseOpcodePaddd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FFE, 2
+		case sseOpcodePaddq:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FD4, 2
+		case sseOpcodePaddw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FFD, 2
+		case sseOpcodePaddsb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FEC, 2
+		case sseOpcodePaddsw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FED, 2
+		case sseOpcodePaddusb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FDC, 2
+		case sseOpcodePaddusw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FDD, 2
+		case sseOpcodePand:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FDB, 2
+		case sseOpcodePandn:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FDF, 2
+		case sseOpcodePavgb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FE0, 2
+		case sseOpcodePavgw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FE3, 2
+		case sseOpcodePcmpeqb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F74, 2
+		case sseOpcodePcmpeqw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F75, 2
+		case sseOpcodePcmpeqd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F76, 2
+		case sseOpcodePcmpeqq:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3829, 3
+		case sseOpcodePcmpgtb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F64, 2
+		case sseOpcodePcmpgtw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F65, 2
+		case sseOpcodePcmpgtd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F66, 2
+		case sseOpcodePcmpgtq:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3837, 3
+		case sseOpcodePmaddwd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FF5, 2
+		case sseOpcodePmaxsb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F383C, 3
+		case sseOpcodePmaxsw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FEE, 2
+		case sseOpcodePmaxsd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F383D, 3
+		case sseOpcodePmaxub:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FDE, 2
+		case sseOpcodePmaxuw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F383E, 3
+		case sseOpcodePmaxud:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F383F, 3
+		case sseOpcodePminsb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3838, 3
+		case sseOpcodePminsw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FEA, 2
+		case sseOpcodePminsd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3839, 3
+		case sseOpcodePminub:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FDA, 2
+		case sseOpcodePminuw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F383A, 3
+		case sseOpcodePminud:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F383B, 3
+		case sseOpcodePmulld:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3840, 3
+		case sseOpcodePmullw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FD5, 2
+		case sseOpcodePmuludq:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FF4, 2
+		case sseOpcodePor:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FEB, 2
+		case sseOpcodePshufb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3800, 3
+		case sseOpcodePsubb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FF8, 2
+		case sseOpcodePsubd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FFA, 2
+		case sseOpcodePsubq:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FFB, 2
+		case sseOpcodePsubw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FF9, 2
+		case sseOpcodePsubsb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FE8, 2
+		case sseOpcodePsubsw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FE9, 2
+		case sseOpcodePsubusb:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FD8, 2
+		case sseOpcodePsubusw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FD9, 2
+		case sseOpcodePunpckhbw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F68, 2
+		case sseOpcodePunpcklbw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F60, 2
+		case sseOpcodePxor:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0FEF, 2
+		case sseOpcodeSubps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F5C, 2
+		case sseOpcodeSubpd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F5C, 2
+		case sseOpcodeSubss:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF3, 0x0F5C, 2
+		case sseOpcodeSubsd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0xF2, 0x0F5C, 2
+		case sseOpcodeXorps:
+			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F57, 2
+		case sseOpcodeXorpd:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F57, 2
+		default:
+			panic(fmt.Sprintf("Unsupported sseOpcode: %s", op))
+		}
+
+		op1 := i.op1
+		if op1.kind == operandKindReg {
+			src := regEncodings[op1.r.RealReg()]
+			dst := regEncodings[i.op2.r.RealReg()]
+			encodeRegReg(c, legPrex, opcode, opcodeNum, dst, src, rexInfo(0).clearW())
+		} else if i.op1.kind == operandKindMem {
+			panic("TODO")
+		} else {
+			panic("BUG: invalid operand kind")
+		}
+
+	case gprToXmm:
+		var legPrefix legacyPrefixes
+		var opcode uint32
+		const opcodeNum = 2
+		switch sseOpcode(i.u1) {
+		case sseOpcodeMovd, sseOpcodeMovq:
+			legPrefix, opcode = legacyPrefixes0x66, 0x0f6e
+		case sseOpcodeCvtsi2ss:
+			legPrefix, opcode = legacyPrefixes0xF3, 0x0f2a
+		case sseOpcodeCvtsi2sd:
+			legPrefix, opcode = legacyPrefixes0xF2, 0x0f2a
+		default:
+			panic(fmt.Sprintf("Unsupported sseOpcode: %s", sseOpcode(i.u1)))
+		}
+
+		var rex rexInfo
+		if i.b1 {
+			rex = rex.setW()
+		} else {
+			rex = rex.clearW()
+		}
+
+		op1 := i.op1
+		if op1.kind == operandKindReg {
+			src := regEncodings[op1.r.RealReg()]
+			dst := regEncodings[i.op2.r.RealReg()]
+			encodeRegReg(c, legPrefix, opcode, opcodeNum, dst, src, rex)
+		} else if i.op1.kind == operandKindMem {
+			panic("TODO")
+		} else {
+			panic("BUG: invalid operand kind")
+		}
+
+	case xmmUnaryRmR:
+		var prefix legacyPrefixes
+		var opcode uint32
+		var opcodeNum uint32
+		op := sseOpcode(i.u1)
+		switch op {
+		case sseOpcodeCvtss2sd:
+			prefix, opcode, opcodeNum = legacyPrefixes0xF3, 0x0F5A, 2
+		case sseOpcodeCvtsd2ss:
+			prefix, opcode, opcodeNum = legacyPrefixes0xF2, 0x0F5A, 2
+		case sseOpcodeMovaps:
+			prefix, opcode, opcodeNum = legacyPrefixesNone, 0x0F28, 2
+		case sseOpcodeMovapd:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F28, 2
+		case sseOpcodeMovdqa:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F6F, 2
+		case sseOpcodeMovdqu:
+			prefix, opcode, opcodeNum = legacyPrefixes0xF3, 0x0F6F, 2
+		case sseOpcodeMovsd:
+			prefix, opcode, opcodeNum = legacyPrefixes0xF2, 0x0F10, 2
+		case sseOpcodeMovss:
+			prefix, opcode, opcodeNum = legacyPrefixes0xF3, 0x0F10, 2
+		case sseOpcodeMovups:
+			prefix, opcode, opcodeNum = legacyPrefixesNone, 0x0F10, 2
+		case sseOpcodeMovupd:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F10, 2
+		case sseOpcodePabsb:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F381C, 3
+		case sseOpcodePabsw:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F381D, 3
+		case sseOpcodePabsd:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F381E, 3
+		case sseOpcodePmovsxbd:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3821, 3
+		case sseOpcodePmovsxbw:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3820, 3
+		case sseOpcodePmovsxbq:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3822, 3
+		case sseOpcodePmovsxwd:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3823, 3
+		case sseOpcodePmovsxwq:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3824, 3
+		case sseOpcodePmovsxdq:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3825, 3
+		case sseOpcodePmovzxbd:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3831, 3
+		case sseOpcodePmovzxbw:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3830, 3
+		case sseOpcodePmovzxbq:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3832, 3
+		case sseOpcodePmovzxwd:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3833, 3
+		case sseOpcodePmovzxwq:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3834, 3
+		case sseOpcodePmovzxdq:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F3835, 3
+		case sseOpcodeSqrtps:
+			prefix, opcode, opcodeNum = legacyPrefixesNone, 0x0F51, 2
+		case sseOpcodeSqrtpd:
+			prefix, opcode, opcodeNum = legacyPrefixes0x66, 0x0F51, 2
+		case sseOpcodeSqrtss:
+			prefix, opcode, opcodeNum = legacyPrefixes0xF3, 0x0F51, 2
+		case sseOpcodeSqrtsd:
+			prefix, opcode, opcodeNum = legacyPrefixes0xF2, 0x0F51, 2
+		default:
+			panic(fmt.Sprintf("Unsupported sseOpcode: %s", op))
+		}
+
+		op1 := i.op1
+		if op1.kind == operandKindReg {
+			src := regEncodings[op1.r.RealReg()]
+			dst := regEncodings[i.op2.r.RealReg()]
+			encodeRegReg(c, prefix, opcode, opcodeNum, dst, src, rexInfo(0).clearW())
+		} else if i.op1.kind == operandKindMem {
+			panic("TODO")
+		} else {
+			panic("BUG: invalid operand kind")
+		}
+
+	case unaryRmR:
+		panic("TODO")
+	case not:
+		panic("TODO")
+	case neg:
+		panic("TODO")
+	case div:
+		panic("TODO")
+	case mulHi:
+		panic("TODO")
+	case checkedDivOrRemSeq:
+		panic("TODO")
+	case signExtendData:
+		panic("TODO")
+	case movzxRmR:
+		panic("TODO")
+	case mov64MR:
+		panic("TODO")
+	case loadEffectiveAddress:
+		panic("TODO")
+	case movsxRmR:
+		panic("TODO")
+	case movRM:
+		panic("TODO")
+	case shiftR:
+		panic("TODO")
+	case xmmRmiReg:
+		panic("TODO")
+	case cmpRmiR:
+		panic("TODO")
+	case setcc:
+		panic("TODO")
+	case cmove:
+		panic("TODO")
+	case push64:
+		panic("TODO")
+	case pop64:
+		panic("TODO")
+	case xmmMovRM:
+		panic("TODO")
+	case xmmLoadConst:
+		panic("TODO")
+	case xmmToGpr:
+		panic("TODO")
+	case cvtUint64ToFloatSeq:
+		panic("TODO")
+	case cvtFloatToSintSeq:
+		panic("TODO")
+	case cvtFloatToUintSeq:
+		panic("TODO")
+	case xmmMinMaxSeq:
+		panic("TODO")
+	case xmmCmove:
+		panic("TODO")
+	case xmmCmpRmR:
+		panic("TODO")
+	case xmmRmRImm:
+		panic("TODO")
+	case jmpKnown:
+		panic("TODO")
+	case jmpIf:
+		panic("TODO")
+	case jmpCond:
+		panic("TODO")
+	case jmpTableSeq:
+		panic("TODO")
+	case jmpUnknown:
+		panic("TODO")
+	case trapIf:
+		panic("TODO")
+	case ud2:
+		panic("TODO")
+	default:
+		panic(fmt.Sprintf("TODO: %v", i.kind))
+	}
+}
+
+func encodeRegReg(
+	c backend.Compiler,
+	legPrefixes legacyPrefixes,
+	opcodes uint32,
+	opcodeNum uint32,
+	r regEnc,
+	rm regEnc,
+	rex rexInfo,
+) {
+	legPrefixes.encode(c)
+	rex.encode(c, r, rm)
+
+	for opcodeNum > 0 {
+		opcodeNum--
+		c.EmitByte(byte((opcodes >> (opcodeNum << 3)) & 0xff))
+	}
+	c.EmitByte(encodeModRM(3, r.encoding(), rm.encoding()))
+}
+
+func encodeModRM(mod byte, reg byte, rm byte) byte {
+	return mod<<6 | reg<<3 | rm
+}
+
+const (
+	rexEncodingDefault byte = 0x40
+	rexEncodingW            = rexEncodingDefault | 0x08
+)
+
+// rexInfo is a bit set to indicate:
+//
+//	0x01: W bit must be cleared.
+//	0x02: REX prefix must be emitted.
+type rexInfo byte
+
+func (ri rexInfo) setW() rexInfo {
+	return ri | 0x01
+}
+
+func (ri rexInfo) clearW() rexInfo { //nolint
+	return ri & 0x02
+}
+
+func (ri rexInfo) always() rexInfo { //nolint
+	return ri | 0x02
+}
+
+func (ri rexInfo) notAlways() rexInfo { //nolint
+	return ri & 0x01
+}
+
+func (ri rexInfo) encode(c backend.Compiler, encR regEnc, encRM regEnc) {
+	var w byte = 0
+	if ri&0x01 != 0 {
+		w = 0x01
+	}
+	r := encR.rexBit()
+	b := encRM.rexBit()
+	rex := rexEncodingDefault | w<<3 | r<<2 | b
+	if rex != rexEncodingDefault || ri&0x02 == 1 {
+		c.EmitByte(rex)
+	}
+}
+
+func (ri rexInfo) encodeForIndex(c backend.Compiler, encR regEnc, encIndex regEnc, encBase regEnc) { //nolint
+	var w byte = 0
+	if ri&0x01 != 0 {
+		w = 0x01
+	}
+	r := encR.rexBit()
+	x := encIndex.rexBit()
+	b := encBase.rexBit()
+	rex := byte(0x40) | w<<3 | r<<2 | x<<1 | b
+	if rex != 0x40 || ri&0x02 == 1 {
+		c.EmitByte(rex)
+	}
+}
+
+type regEnc byte
+
+func (r regEnc) rexBit() byte {
+	return byte(r) >> 3
+}
+
+func (r regEnc) encoding() byte {
+	return byte(r) & 0x07
+}
+
+var regEncodings = [...]regEnc{
+	rax:   0b000,
+	rcx:   0b001,
+	rdx:   0b010,
+	rbx:   0b011,
+	rsp:   0b100,
+	rbp:   0b101,
+	rsi:   0b110,
+	rdi:   0b111,
+	r8:    0b1000,
+	r9:    0b1001,
+	r10:   0b1010,
+	r11:   0b1011,
+	r12:   0b1100,
+	r13:   0b1101,
+	r14:   0b1110,
+	r15:   0b1111,
+	xmm0:  0b000,
+	xmm1:  0b001,
+	xmm2:  0b010,
+	xmm3:  0b011,
+	xmm4:  0b100,
+	xmm5:  0b101,
+	xmm6:  0b110,
+	xmm7:  0b111,
+	xmm8:  0b1000,
+	xmm9:  0b1001,
+	xmm10: 0b1010,
+	xmm11: 0b1011,
+	xmm12: 0b1100,
+	xmm13: 0b1101,
+	xmm14: 0b1110,
+	xmm15: 0b1111,
+}
+
+type legacyPrefixes byte
+
+const (
+	legacyPrefixesNone legacyPrefixes = iota
+	legacyPrefixes0x66
+	legacyPrefixes0xF0
+	legacyPrefixes0x660xF0
+	legacyPrefixes0xF2
+	legacyPrefixes0xF3
+)
+
+func (p legacyPrefixes) encode(c backend.Compiler) {
+	switch p {
+	case legacyPrefixesNone:
+	case legacyPrefixes0x66:
+		c.EmitByte(0x66)
+	case legacyPrefixes0xF0:
+		c.EmitByte(0xf0)
+	case legacyPrefixes0x660xF0:
+		c.EmitByte(0x66)
+		c.EmitByte(0xf0)
+	case legacyPrefixes0xF2:
+		c.EmitByte(0xf2)
+	case legacyPrefixes0xF3:
+		c.EmitByte(0xf3)
+	default:
+		panic("BUG: invalid legacy prefix")
+	}
+}
+
+func lower32willSignExtendTo64(x uint64) bool {
+	xs := int64(x)
+	return xs == int64(uint64(int32(xs)))
+}
diff --git a/internal/engine/wazevo/backend/isa/amd64/lower_constant.go b/internal/engine/wazevo/backend/isa/amd64/lower_constant.go
new file mode 100644
index 00000000..b2305da8
--- /dev/null
+++ b/internal/engine/wazevo/backend/isa/amd64/lower_constant.go
@@ -0,0 +1,57 @@
+package amd64
+
+import (
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
+)
+
+// InsertLoadConstant implements backend.Machine.
+func (m *machine) InsertLoadConstant(instr *ssa.Instruction, vr regalloc.VReg) {
+	val := instr.Return()
+	valType := val.Type()
+	v := instr.ConstantVal()
+
+	bits := valType.Bits()
+	if bits < 64 { // Clear the redundant bits just in case it's unexpectedly sign-extended, etc.
+		v = v & ((1 << valType.Bits()) - 1)
+	}
+
+	switch valType {
+	case ssa.TypeF32, ssa.TypeF64:
+		m.lowerFconst(vr, v, bits == 64)
+	case ssa.TypeI32, ssa.TypeI64:
+		m.lowerIconst(vr, v, bits == 64)
+	default:
+		panic("TODO")
+	}
+}
+
+func (m *machine) lowerFconst(dst regalloc.VReg, c uint64, _64 bool) {
+	if c == 0 {
+		xor := m.allocateInstr().asXmmRmR(sseOpcodeXorpd, operand{kind: operandKindReg, r: dst}, dst, _64)
+		m.insert(xor)
+	} else {
+		var tmpType ssa.Type
+		if _64 {
+			tmpType = ssa.TypeI64
+		} else {
+			tmpType = ssa.TypeI32
+		}
+		tmpInt := m.c.AllocateVReg(tmpType)
+		loadToGP := m.allocateInstr().asImm(tmpInt, c, _64)
+		m.insert(loadToGP)
+
+		movToXmm := m.allocateInstr().asGprToXmm(sseOpcodeMovq, operand{kind: operandKindReg, r: tmpInt}, dst, _64)
+		m.insert(movToXmm)
+	}
+}
+
+func (m *machine) lowerIconst(dst regalloc.VReg, c uint64, _64 bool) {
+	i := m.allocateInstr()
+	if c == 0 {
+		i.asAluRmiR(aluRmiROpcodeXor, operand{kind: operandKindReg, r: dst}, dst, _64)
+	} else {
+		i.asImm(dst, c, _64)
+	}
+	m.insert(i)
+}
diff --git a/internal/engine/wazevo/backend/isa/amd64/machine.go b/internal/engine/wazevo/backend/isa/amd64/machine.go
index 26d20878..354118fe 100644
--- a/internal/engine/wazevo/backend/isa/amd64/machine.go
+++ b/internal/engine/wazevo/backend/isa/amd64/machine.go
@@ -2,6 +2,8 @@ package amd64
 
 import (
 	"context"
+	"fmt"
+	"strings"
 
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
@@ -23,23 +25,31 @@ func NewBackend() backend.Machine {
 	}
 }
 
-// machine implements backend.Machine for amd64.
-type machine struct {
-	c                        backend.Compiler
-	ectx                     *backend.ExecutableContextT[instruction]
-	stackBoundsCheckDisabled bool
+type (
+	// machine implements backend.Machine for amd64.
+	machine struct {
+		c                        backend.Compiler
+		ectx                     *backend.ExecutableContextT[instruction]
+		stackBoundsCheckDisabled bool
 
-	regAlloc regalloc.Allocator
+		regAlloc        regalloc.Allocator
+		regAllocFn      *backend.RegAllocFunction[*instruction, *machine]
+		regAllocStarted bool
 
-	// abis maps ssa.SignatureID to the ABI implementation.
-	abis       []abiImpl
-	currentABI *abiImpl
-}
+		spillSlotSize int64
+		currentABI    *backend.FunctionABI
+		clobberedRegs []regalloc.VReg
+	}
+)
 
 // Reset implements backend.Machine.
 func (m *machine) Reset() {
 	m.stackBoundsCheckDisabled = false
 	m.ectx.Reset()
+
+	m.regAllocFn.Reset()
+	m.regAlloc.Reset()
+	m.regAllocStarted = false
 }
 
 // ExecutableContext implements backend.Machine.
@@ -51,9 +61,28 @@ func (m *machine) DisableStackCheck() { m.stackBoundsCheckDisabled = true }
 // SetCompiler implements backend.Machine.
 func (m *machine) SetCompiler(compiler backend.Compiler) { m.c = compiler }
 
-// InitializeABI implements backend.Machine.
-func (m *machine) InitializeABI(sig *ssa.Signature) {
-	m.currentABI = m.getOrCreateFunctionABI(sig)
+// SetCurrentABI implements backend.Machine.
+func (m *machine) SetCurrentABI(abi *backend.FunctionABI) {
+	m.currentABI = abi
+}
+
+// RegAlloc implements backend.Machine.
+func (m *machine) RegAlloc() {
+	rf := m.regAllocFn
+	for _, pos := range m.ectx.OrderedBlockLabels {
+		rf.AddBlock(pos.SB, pos.L, pos.Begin, pos.End)
+	}
+
+	m.regAllocStarted = true
+	m.regAlloc.DoAllocation(rf)
+	// Now that we know the final spill slot size, we must align spillSlotSize to 16 bytes.
+	m.spillSlotSize = (m.spillSlotSize + 15) &^ 15
+}
+
+// InsertReturn implements backend.Machine.
+func (m *machine) InsertReturn() {
+	i := m.allocateInstr().asRet(m.currentABI)
+	m.insert(i)
 }
 
 // LowerSingleBranch implements backend.Machine.
@@ -76,26 +105,57 @@ func (m *machine) LowerInstr(instruction *ssa.Instruction) {
 
 // InsertMove implements backend.Machine.
 func (m *machine) InsertMove(dst, src regalloc.VReg, typ ssa.Type) {
-	// TODO implement me
-	panic("implement me")
-}
-
-// InsertReturn implements backend.Machine.
-func (m *machine) InsertReturn() {
-	// TODO implement me
-	panic("implement me")
-}
-
-// InsertLoadConstant implements backend.Machine.
-func (m *machine) InsertLoadConstant(instr *ssa.Instruction, vr regalloc.VReg) {
-	// TODO implement me
-	panic("implement me")
+	switch typ {
+	case ssa.TypeI32, ssa.TypeI64:
+		i := m.allocateInstr().asMovRR(src, dst, typ.Bits() == 64)
+		m.insert(i)
+	case ssa.TypeF32, ssa.TypeF64, ssa.TypeV128:
+		var op sseOpcode
+		switch typ {
+		case ssa.TypeF32:
+			op = sseOpcodeMovss
+		case ssa.TypeF64:
+			op = sseOpcodeMovsd
+		case ssa.TypeV128:
+			op = sseOpcodeMovdqa
+		}
+		i := m.allocateInstr().asXmmUnaryRmR(op, operand{kind: operandKindReg, r: src}, dst, typ.Bits() == 64)
+		m.insert(i)
+	default:
+		panic("BUG")
+	}
 }
 
 // Format implements backend.Machine.
 func (m *machine) Format() string {
-	// TODO implement me
-	panic("implement me")
+	ectx := m.ectx
+	begins := map[*instruction]backend.Label{}
+	for l, pos := range ectx.LabelPositions {
+		begins[pos.Begin] = l
+	}
+
+	irBlocks := map[backend.Label]ssa.BasicBlockID{}
+	for i, l := range ectx.SsaBlockIDToLabels {
+		irBlocks[l] = ssa.BasicBlockID(i)
+	}
+
+	var lines []string
+	for cur := ectx.RootInstr; cur != nil; cur = cur.next {
+		if l, ok := begins[cur]; ok {
+			var labelStr string
+			if blkID, ok := irBlocks[l]; ok {
+				labelStr = fmt.Sprintf("%s (SSA Block: %s):", l, blkID)
+			} else {
+				labelStr = fmt.Sprintf("%s:", l)
+			}
+			lines = append(lines, labelStr)
+		}
+		if cur.kind == nop0 {
+			continue
+		}
+		lines = append(lines, "\t"+cur.String())
+	}
+	return "\n" + strings.Join(lines, "\n") + "\n"
 }
 
 // SetupPrologue implements backend.Machine.
@@ -122,12 +182,6 @@ func (m *machine) ResolveRelocations(refToBinaryOffset map[ssa.FuncRef]int, bina
 	panic("implement me")
 }
 
-// Encode implements backend.Machine.
-func (m *machine) Encode() {
-	// TODO implement me
-	panic("implement me")
-}
-
 // CompileGoFunctionTrampoline implements backend.Machine.
 func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *ssa.Signature, needModuleContextPtr bool) []byte {
 	// TODO implement me
@@ -146,5 +200,16 @@ func (m *machine) CompileEntryPreamble(signature *ssa.Signature) []byte {
 	panic("implement me")
 }
 
-// RegAlloc implements backend.Machine.
-func (m *machine) RegAlloc() { panic("implement me") }
+// allocateInstr allocates an instruction.
+func (m *machine) allocateInstr() *instruction {
+	instr := m.ectx.InstructionPool.Allocate()
+	if !m.regAllocStarted {
+		instr.addedBeforeRegAlloc = true
+	}
+	return instr
+}
+
+func (m *machine) insert(i *instruction) {
+	ectx := m.ectx
+	ectx.PendingInstructions = append(ectx.PendingInstructions, i)
+}
diff --git a/internal/engine/wazevo/backend/isa/amd64/machine_regalloc.go b/internal/engine/wazevo/backend/isa/amd64/machine_regalloc.go
new file mode 100644
index 00000000..ac6e6000
--- /dev/null
+++ b/internal/engine/wazevo/backend/isa/amd64/machine_regalloc.go
@@ -0,0 +1,48 @@
+package amd64
+
+import (
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
+)
+
+// InsertMoveBefore implements backend.RegAllocFunctionMachine.
+func (m *machine) InsertMoveBefore(dst, src regalloc.VReg, instr *instruction) {
+	// TODO implement me
+	panic("implement me")
+}
+
+// InsertStoreRegisterAt implements backend.RegAllocFunctionMachine.
+func (m *machine) InsertStoreRegisterAt(v regalloc.VReg, instr *instruction, after bool) *instruction {
+	// TODO implement me
+	panic("implement me")
+}
+
+// InsertReloadRegisterAt implements backend.RegAllocFunctionMachine.
+func (m *machine) InsertReloadRegisterAt(v regalloc.VReg, instr *instruction, after bool) *instruction {
+	// TODO implement me
+	panic("implement me")
+}
+
+// ClobberedRegisters implements backend.RegAllocFunctionMachine.
+func (m *machine) ClobberedRegisters(regs []regalloc.VReg) {
+	m.clobberedRegs = append(m.clobberedRegs[:0], regs...)
+}
+
+// Swap implements backend.RegAllocFunctionMachine.
+func (m *machine) Swap(cur *instruction, x1, x2, tmp regalloc.VReg) {
+	// TODO implement me
+	panic("implement me")
+}
+
+// LastInstrForInsertion implements backend.RegAllocFunctionMachine.
+func (m *machine) LastInstrForInsertion(begin, end *instruction) *instruction {
+	// TODO implement me
+	panic("implement me")
+}
+
+// SSABlockLabel implements backend.RegAllocFunctionMachine.
+func (m *machine) SSABlockLabel(id ssa.BasicBlockID) backend.Label {
+	// TODO implement me
+	panic("implement me")
+}
diff --git a/internal/engine/wazevo/backend/isa/amd64/operands.go b/internal/engine/wazevo/backend/isa/amd64/operands.go
new file mode 100644
index 00000000..feba2aa1
--- /dev/null
+++ b/internal/engine/wazevo/backend/isa/amd64/operands.go
@@ -0,0 +1,78 @@
+package amd64
+
+import (
+	"fmt"
+
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
+)
+
+type operand struct {
+	kind  operandKind
+	r     regalloc.VReg
+	imm32 uint32
+	amode amode
+}
+
+type operandKind byte
+
+const (
+	// operandKindReg is an operand which is an integer Register.
+	operandKindReg operandKind = iota + 1
+
+	// operandKindMem is an operand which is either an integer Register or a value in Memory.  This can denote an 8, 16,
+	// 32, 64, or 128 bit value.
+	operandKindMem
+
+	// operandKindRegMemImm is either an integer Register, a value in Memory or an Immediate.
+	operandImm32
+)
+
+func (o *operand) format(_64 bool) string {
+	switch o.kind {
+	case operandKindReg:
+		return formatVRegSized(o.r, _64)
+	case operandKindMem:
+		return o.amode.String()
+	case operandImm32:
+		return fmt.Sprintf("$%d", o.imm32)
+	default:
+		panic("BUG: invalid operand kind")
+	}
+}
+
+func newOperandReg(r regalloc.VReg) operand {
+	return operand{kind: operandKindReg, r: r}
+}
+
+// nolint
+type amode struct {
+	kind  amodeKind
+	imm32 uint32
+
+	// For amodeRegRegShit:
+
+	base  regalloc.VReg
+	index regalloc.VReg
+	shift byte // 0, 1, 2, 3
+}
+
+type amodeKind byte
+
+const (
+
+	// immediate sign-extended and a Register.
+	amodeImmReg amodeKind = iota + 1
+
+	// sign-extend-32-to-64(Immediate) + Register1 + (Register2 << Shift)
+	amodeRegRegShit
+)
+
+func (a *amode) String() string {
+	switch a.kind {
+	case amodeImmReg:
+		panic("TODO")
+	case amodeRegRegShit:
+		panic("TODO")
+	}
+	panic("BUG: invalid amode kind")
+}
diff --git a/internal/engine/wazevo/backend/isa/amd64/reg.go b/internal/engine/wazevo/backend/isa/amd64/reg.go
index 4b1abe67..c47343a2 100644
--- a/internal/engine/wazevo/backend/isa/amd64/reg.go
+++ b/internal/engine/wazevo/backend/isa/amd64/reg.go
@@ -1,6 +1,8 @@
 package amd64
 
 import (
+	"fmt"
+
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
 )
 
@@ -147,3 +149,37 @@ var regNames = [...]string{
 	xmm14: "xmm14",
 	xmm15: "xmm15",
 }
+
+func formatVRegSized(r regalloc.VReg, _64 bool) string {
+	if r.IsRealReg() {
+		if r.RegType() == regalloc.RegTypeInt {
+			rr := r.RealReg()
+			orig := regNames[rr]
+			if rr <= rdi {
+				if _64 {
+					return "%" + orig
+				} else {
+					return "%e" + orig[1:]
+				}
+			} else {
+				if _64 {
+					return "%" + orig
+				} else {
+					return "%" + orig + "d"
+				}
+			}
+		} else {
+			return "%" + regNames[r.RealReg()]
+		}
+	} else {
+		if r.RegType() == regalloc.RegTypeInt {
+			if _64 {
+				return fmt.Sprintf("%%r%d?", r.ID())
+			} else {
+				return fmt.Sprintf("%%r%dd?", r.ID())
+			}
+		} else {
+			return fmt.Sprintf("%%xmm%d?", r.ID())
+		}
+	}
+}
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi.go b/internal/engine/wazevo/backend/isa/arm64/abi.go
index edca84d4..de386771 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi.go
@@ -56,28 +56,9 @@ var regInfo = &regalloc.RegisterInfo{
 	},
 }
 
-// functionABIRegInfo implements backend.FunctionABIRegInfo.
-type functionABIRegInfo struct{}
-
-// ArgsResultsRegs implements backend.FunctionABIRegInfo.
-func (f functionABIRegInfo) ArgsResultsRegs() (argInts, argFloats, resultInt, resultFloats []regalloc.RealReg) {
-	return intParamResultRegs, floatParamResultRegs, intParamResultRegs, floatParamResultRegs
-}
-
-type functionABI = backend.FunctionABI[functionABIRegInfo]
-
-func (m *machine) getOrCreateFunctionABI(sig *ssa.Signature) *functionABI {
-	if int(sig.ID) >= len(m.abis) {
-		m.abis = append(m.abis, make([]functionABI, int(sig.ID)+1)...)
-	}
-
-	abi := &m.abis[sig.ID]
-	if abi.Initialized {
-		return abi
-	}
-
-	abi.Init(sig)
-	return abi
+// ArgsResultsRegs implements backend.Machine.
+func (m *machine) ArgsResultsRegs() (argResultInts, argResultFloats []regalloc.RealReg) {
+	return intParamResultRegs, floatParamResultRegs
 }
 
 // LowerParams implements backend.FunctionABI.
@@ -198,7 +179,7 @@ func (m *machine) LowerReturns(rets []ssa.Value) {
 
 // callerGenVRegToFunctionArg is the opposite of GenFunctionArgToVReg, which is used to generate the
 // caller side of the function call.
-func (m *machine) callerGenVRegToFunctionArg(a *functionABI, argIndex int, reg regalloc.VReg, def *backend.SSAValueDefinition, slotBegin int64) {
+func (m *machine) callerGenVRegToFunctionArg(a *backend.FunctionABI, argIndex int, reg regalloc.VReg, def *backend.SSAValueDefinition, slotBegin int64) {
 	arg := &a.Args[argIndex]
 	if def != nil && def.IsFromInstr() {
 		// Constant instructions are inlined.
@@ -220,7 +201,7 @@ func (m *machine) callerGenVRegToFunctionArg(a *functionABI, argIndex int, reg r
 	}
 }
 
-func (m *machine) callerGenFunctionReturnVReg(a *functionABI, retIndex int, reg regalloc.VReg, slotBegin int64) {
+func (m *machine) callerGenFunctionReturnVReg(a *backend.FunctionABI, retIndex int, reg regalloc.VReg, slotBegin int64) {
 	r := &a.Rets[retIndex]
 	if r.Kind == backend.ABIArgKindReg {
 		m.InsertMove(reg, r.Reg, r.Type)
@@ -284,7 +265,7 @@ func (m *machine) lowerCall(si *ssa.Instruction) {
 	} else {
 		indirectCalleePtr, sigID, args = si.CallIndirectData()
 	}
-	calleeABI := m.getOrCreateFunctionABI(m.compiler.SSABuilder().ResolveSignature(sigID))
+	calleeABI := m.compiler.GetFunctionABI(m.compiler.SSABuilder().ResolveSignature(sigID))
 
 	stackSlotSize := calleeABI.AlignedArgResultStackSlotSize()
 	if m.maxRequiredStackSizeForCalls < stackSlotSize+16 {
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi_entry_preamble.go b/internal/engine/wazevo/backend/isa/arm64/abi_entry_preamble.go
index 9bdda1a1..bf72976b 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi_entry_preamble.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi_entry_preamble.go
@@ -135,8 +135,9 @@ func (m *machine) goEntryPreamblePassResult(cur *instruction, resultSlicePtr reg
 }
 
 func (m *machine) constructEntryPreamble(sig *ssa.Signature) (root *instruction) {
-	abi := functionABI{}
-	abi.Init(sig)
+	abi := backend.FunctionABI{}
+	m.ArgsResultsRegs()
+	abi.Init(sig, intParamResultRegs, floatParamResultRegs)
 
 	root = m.allocateNop()
 
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi_go_call.go b/internal/engine/wazevo/backend/isa/arm64/abi_go_call.go
index 198d2f1b..7b10a821 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi_go_call.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi_go_call.go
@@ -20,8 +20,8 @@ func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *
 		argBegin++
 	}
 
-	abi := &functionABI{}
-	abi.Init(sig)
+	abi := &backend.FunctionABI{}
+	abi.Init(sig, intParamResultRegs, floatParamResultRegs)
 	m.currentABI = abi
 
 	cur := m.allocateInstr()
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr.go b/internal/engine/wazevo/backend/isa/arm64/instr.go
index bcd1566f..7f9515fa 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr.go
@@ -5,6 +5,7 @@ import (
 	"math"
 	"strings"
 
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
 )
@@ -26,7 +27,7 @@ type (
 		u1, u2, u3          uint64
 		rd, rm, rn, ra      operand
 		amode               addressMode
-		abi                 *functionABI
+		abi                 *backend.FunctionABI
 		targets             []uint32
 		addedBeforeRegAlloc bool
 	}
@@ -461,13 +462,13 @@ func (i *instruction) AssignUse(index int, reg regalloc.VReg) {
 	}
 }
 
-func (i *instruction) asCall(ref ssa.FuncRef, abi *functionABI) {
+func (i *instruction) asCall(ref ssa.FuncRef, abi *backend.FunctionABI) {
 	i.kind = call
 	i.u1 = uint64(ref)
 	i.abi = abi
 }
 
-func (i *instruction) asCallIndirect(ptr regalloc.VReg, abi *functionABI) {
+func (i *instruction) asCallIndirect(ptr regalloc.VReg, abi *backend.FunctionABI) {
 	i.kind = callInd
 	i.rn = operandNR(ptr)
 	i.abi = abi
@@ -524,7 +525,7 @@ func (i *instruction) nop0Label() label {
 	return label(i.u1)
 }
 
-func (i *instruction) asRet(abi *functionABI) {
+func (i *instruction) asRet(abi *backend.FunctionABI) {
 	i.kind = ret
 	i.abi = abi
 }
diff --git a/internal/engine/wazevo/backend/isa/arm64/machine.go b/internal/engine/wazevo/backend/isa/arm64/machine.go
index bdf88ae2..ad778552 100644
--- a/internal/engine/wazevo/backend/isa/arm64/machine.go
+++ b/internal/engine/wazevo/backend/isa/arm64/machine.go
@@ -16,9 +16,7 @@ type (
 	machine struct {
 		compiler          backend.Compiler
 		executableContext *backend.ExecutableContextT[instruction]
-		currentABI        *functionABI
-		// abis maps ssa.SignatureID to the ABI implementation.
-		abis []functionABI
+		currentABI        *backend.FunctionABI
 
 		regAlloc   regalloc.Allocator
 		regAllocFn *backend.RegAllocFunction[*instruction, *machine]
@@ -149,9 +147,9 @@ func (m *machine) Reset() {
 	m.executableContext.Reset()
 }
 
-// InitializeABI implements backend.Machine InitializeABI.
-func (m *machine) InitializeABI(sig *ssa.Signature) {
-	m.currentABI = m.getOrCreateFunctionABI(sig)
+// SetCurrentABI implements backend.Machine SetCurrentABI.
+func (m *machine) SetCurrentABI(abi *backend.FunctionABI) {
+	m.currentABI = abi
 }
 
 // DisableStackCheck implements backend.Machine DisableStackCheck.
diff --git a/internal/engine/wazevo/backend/machine.go b/internal/engine/wazevo/backend/machine.go
index 34dad417..8b7b0c47 100644
--- a/internal/engine/wazevo/backend/machine.go
+++ b/internal/engine/wazevo/backend/machine.go
@@ -16,8 +16,8 @@ type (
 		// DisableStackCheck disables the stack check for the current compilation for debugging/testing.
 		DisableStackCheck()
 
-		// InitializeABI initializes the FunctionABI for the given signature.
-		InitializeABI(sig *ssa.Signature)
+		// SetCurrentABI initializes the FunctionABI for the given signature.
+		SetCurrentABI(abi *FunctionABI)
 
 		// SetCompiler sets the compilation context used for the lifetime of Machine.
 		// This is only called once per Machine, i.e. before the first compilation.
@@ -88,5 +88,8 @@ type (
 
 		// LowerReturns lowers the given returns.
 		LowerReturns(returns []ssa.Value)
+
+		// ArgsResultsRegs returns the registers used for arguments and return values.
+		ArgsResultsRegs() (argResultInts, argResultFloats []regalloc.RealReg)
 	}
 )
diff --git a/internal/sysfs/file_windows.go b/internal/sysfs/file_windows.go
index 0658c820..37870ea3 100644
--- a/internal/sysfs/file_windows.go
+++ b/internal/sysfs/file_windows.go
@@ -1,6 +1,7 @@
 package sysfs
 
 import (
+	"errors"
 	"syscall"
 	"unsafe"
 
@@ -10,12 +11,21 @@ import (
 const (
 	nonBlockingFileReadSupported  = true
 	nonBlockingFileWriteSupported = false
+
+	_ERROR_IO_INCOMPLETE = syscall.Errno(996)
 )
 
 var kernel32 = syscall.NewLazyDLL("kernel32.dll")
 
 // procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe
-var procPeekNamedPipe = kernel32.NewProc("PeekNamedPipe")
+var (
+	// procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe
+	procPeekNamedPipe = kernel32.NewProc("PeekNamedPipe")
+	// procGetOverlappedResult is the syscall.LazyProc in kernel32 for GetOverlappedResult
+	procGetOverlappedResult = kernel32.NewProc("GetOverlappedResult")
+	// procCreateEventW is the syscall.LazyProc in kernel32 for CreateEventW
+	procCreateEventW = kernel32.NewProc("CreateEventW")
+)
 
 // readFd returns ENOSYS on unsupported platforms.
 //
@@ -47,12 +57,53 @@ func writeFd(fd uintptr, buf []byte) (int, sys.Errno) {
 }
 
 func readSocket(h uintptr, buf []byte) (int, sys.Errno) {
+	// Poll the socket to ensure that we never perform a blocking/overlapped Read.
+	//
+	// When the socket is closed by the remote peer, wsaPoll will return n=1 and
+	// errno=0, and syscall.ReadFile will return n=0 and errno=0 -- which indicates
+	// io.EOF.
+	if n, errno := wsaPoll(
+		[]pollFd{newPollFd(h, _POLLIN, 0)}, 0); !errors.Is(errno, sys.Errno(0)) {
+		return 0, sys.UnwrapOSError(errno)
+	} else if n <= 0 {
+		return 0, sys.EAGAIN
+	}
+
+	// Properly use overlapped result.
+	//
+	// If hFile was opened with FILE_FLAG_OVERLAPPED, the following conditions are in effect:
+	//  - The lpOverlapped parameter must point to a valid and unique OVERLAPPED structure,
+	//  otherwise the function can incorrectly report that the read operation is complete.
+	//  - The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult
+	//  function to get the actual number of bytes read. If the hFile parameter is associated
+	//  with an I/O completion port, you can also get the number of bytes read by calling the
+	//  GetQueuedCompletionStatus function.
+	//
+	// We are currently skipping checking if hFile was opened with FILE_FLAG_OVERLAPPED but using
+	// both lpOverlapped and lpNumberOfBytesRead.
 	var overlapped syscall.Overlapped
+
+	// Create an event to wait on.
+	if hEvent, err := createEventW(nil, true, false, nil); err != 0 {
+		return 0, sys.UnwrapOSError(err)
+	} else {
+		overlapped.HEvent = syscall.Handle(hEvent)
+	}
+
 	var done uint32
 	errno := syscall.ReadFile(syscall.Handle(h), buf, &done, &overlapped)
-	if errno == syscall.ERROR_IO_PENDING {
-		errno = sys.EAGAIN
+	if errors.Is(errno, syscall.ERROR_IO_PENDING) {
+		errno = syscall.CancelIo(syscall.Handle(h))
+		if errno != nil {
+			return 0, sys.UnwrapOSError(errno) // This is a fatal error. CancelIo failed.
+		}
+
+		done, errno = getOverlappedResult(syscall.Handle(h), &overlapped, true) // wait for I/O to complete(cancel or finish). Overwrite done and errno.
+		if errors.Is(errno, syscall.ERROR_OPERATION_ABORTED) {
+			return int(done), sys.EAGAIN // This is one of the expected behavior, I/O was cancelled(completed) before finished.
+		}
 	}
+
 	return int(done), sys.UnwrapOSError(errno)
 }
 
@@ -60,7 +111,7 @@ func writeSocket(fd uintptr, buf []byte) (int, sys.Errno) {
 	var done uint32
 	var overlapped syscall.Overlapped
 	errno := syscall.WriteFile(syscall.Handle(fd), buf, &done, &overlapped)
-	if errno == syscall.ERROR_IO_PENDING {
+	if errors.Is(errno, syscall.ERROR_IO_PENDING) {
 		errno = syscall.EAGAIN
 	}
 	return int(done), sys.UnwrapOSError(errno)
@@ -86,3 +137,39 @@ func rmdir(path string) sys.Errno {
 	err := syscall.Rmdir(path)
 	return sys.UnwrapOSError(err)
 }
+
+func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, wait bool) (uint32, syscall.Errno) {
+	var totalBytesAvail uint32
+	var bwait uintptr
+	if wait {
+		bwait = 0xFFFFFFFF
+	}
+	totalBytesPtr := unsafe.Pointer(&totalBytesAvail)
+	_, _, errno := syscall.SyscallN(
+		procGetOverlappedResult.Addr(),
+		uintptr(handle),                     // [in]  HANDLE       hFile,
+		uintptr(unsafe.Pointer(overlapped)), // [in]  LPOVERLAPPED lpOverlapped,
+		uintptr(totalBytesPtr),              // [out] LPDWORD      lpNumberOfBytesTransferred,
+		bwait)                               // [in]  BOOL         bWait
+	return totalBytesAvail, errno
+}
+
+func createEventW(lpEventAttributes *syscall.SecurityAttributes, bManualReset bool, bInitialState bool, lpName *uint16) (uintptr, syscall.Errno) {
+	var manualReset uintptr
+	var initialState uintptr
+	if bManualReset {
+		manualReset = 1
+	}
+	if bInitialState {
+		initialState = 1
+	}
+	handle, _, errno := syscall.SyscallN(
+		procCreateEventW.Addr(),
+		uintptr(unsafe.Pointer(lpEventAttributes)), // [in]     LPSECURITY_ATTRIBUTES lpEventAttributes,
+		manualReset,                     // [in]     BOOL                  bManualReset,
+		initialState,                    // [in]     BOOL                  bInitialState,
+		uintptr(unsafe.Pointer(lpName)), // [in, opt]LPCWSTR               lpName,
+	)
+
+	return handle, errno
+}


Test patch:
diff --git a/internal/engine/wazevo/backend/compiler_lower_test.go b/internal/engine/wazevo/backend/compiler_lower_test.go
index bfdc8747..851b4409 100644
--- a/internal/engine/wazevo/backend/compiler_lower_test.go
+++ b/internal/engine/wazevo/backend/compiler_lower_test.go
@@ -50,6 +50,10 @@ func TestCompiler_lowerBlockArguments(t *testing.T) {
 							target regalloc.VReg
 						}{instr: instr, target: vr})
 					},
+					argResultInts: []regalloc.RealReg{regalloc.RealReg(0), regalloc.RealReg(1)},
+					argResultFloats: []regalloc.RealReg{
+						regalloc.RealReg(2), regalloc.RealReg(3), regalloc.RealReg(4), regalloc.RealReg(5),
+					},
 				}
 
 				c := newCompiler(context.Background(), m, builder)
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go b/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
new file mode 100644
index 00000000..7f6f2a11
--- /dev/null
+++ b/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
@@ -0,0 +1,898 @@
+package amd64
+
+import (
+	"encoding/hex"
+	"testing"
+
+	"github.com/tetratelabs/wazero/internal/testing/require"
+)
+
+func TestInstruction_format_encode(t *testing.T) {
+	for _, tc := range []struct {
+		setup      func(*instruction)
+		want       string
+		wantFormat string
+	}{
+		{
+			setup:      func(i *instruction) { i.asRet(nil) },
+			wantFormat: "ret",
+			want:       "c3",
+		},
+		{
+			setup:      func(i *instruction) { i.asImm(r14VReg, 1234567, false) },
+			wantFormat: "movl $1234567, %r14d",
+			want:       "41be87d61200",
+		},
+		{
+			setup: func(i *instruction) {
+				v := -126
+				i.asImm(r14VReg, uint64(int64(v)), false)
+			},
+			wantFormat: "movl $-126, %r14d",
+			want:       "41be82ffffff",
+		},
+		{
+			setup:      func(i *instruction) { i.asImm(r14VReg, 0x200000000, true) },
+			want:       "49be0000000002000000",
+			wantFormat: "movabsq $8589934592, %r14",
+		},
+		{
+			setup: func(i *instruction) {
+				v := -126
+				i.asImm(r14VReg, uint64(int64(v)), true)
+			},
+			want:       "49c7c682ffffff",
+			wantFormat: "movabsq $-126, %r14",
+		},
+		{
+			setup:      func(i *instruction) { i.asImm(rcxVReg, 1234567, false) },
+			wantFormat: "movl $1234567, %ecx",
+			want:       "b987d61200",
+		},
+		{
+			setup: func(i *instruction) {
+				v := -126
+				i.asImm(rcxVReg, uint64(int64(v)), false)
+			},
+			wantFormat: "movl $-126, %ecx",
+			want:       "b982ffffff",
+		},
+		{
+			setup:      func(i *instruction) { i.asImm(rcxVReg, 0x200000000, true) },
+			want:       "48b90000000002000000",
+			wantFormat: "movabsq $8589934592, %rcx",
+		},
+		{
+			setup: func(i *instruction) {
+				v := -126
+				i.asImm(rcxVReg, uint64(int64(v)), true)
+			},
+			want:       "48c7c182ffffff",
+			wantFormat: "movabsq $-126, %rcx",
+		},
+		{
+			setup:      func(i *instruction) { i.asMovRR(raxVReg, rdiVReg, false) },
+			want:       "89c7",
+			wantFormat: "movl %eax, %edi",
+		},
+		{
+			setup:      func(i *instruction) { i.asMovRR(raxVReg, r15VReg, false) },
+			want:       "4189c7",
+			wantFormat: "movl %eax, %r15d",
+		},
+		{
+			setup:      func(i *instruction) { i.asMovRR(r14VReg, r15VReg, false) },
+			want:       "4589f7",
+			wantFormat: "movl %r14d, %r15d",
+		},
+		{
+			setup:      func(i *instruction) { i.asMovRR(raxVReg, rcxVReg, true) },
+			want:       "4889c1",
+			wantFormat: "movq %rax, %rcx",
+		},
+		{
+			setup:      func(i *instruction) { i.asMovRR(raxVReg, r15VReg, true) },
+			want:       "4989c7",
+			wantFormat: "movq %rax, %r15",
+		},
+		{
+			setup:      func(i *instruction) { i.asMovRR(r11VReg, r12VReg, true) },
+			want:       "4d89dc",
+			wantFormat: "movq %r11, %r12",
+		},
+		// addss
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAddss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f58c1",
+			wantFormat: "addss %xmm1, %xmm0",
+		},
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAddss, newOperandReg(xmm13VReg), xmm11VReg, false) },
+			want:       "f3450f58dd",
+			wantFormat: "addss %xmm13, %xmm11",
+		},
+		// addsd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAddsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f20f58c1",
+			wantFormat: "addsd %xmm1, %xmm0",
+		},
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAddsd, newOperandReg(xmm13VReg), xmm11VReg, false) },
+			want:       "f2450f58dd",
+			wantFormat: "addsd %xmm13, %xmm11",
+		},
+		// addps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAddps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f58c1",
+			wantFormat: "addps %xmm1, %xmm0",
+		},
+		// addpd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAddpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f58c1",
+			wantFormat: "addpd %xmm1, %xmm0",
+		},
+		// addss
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAddss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f58c1",
+			wantFormat: "addss %xmm1, %xmm0",
+		},
+		// addsd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAddsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f20f58c1",
+			wantFormat: "addsd %xmm1, %xmm0",
+		},
+		// andps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAndps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f54c1",
+			wantFormat: "andps %xmm1, %xmm0",
+		},
+		// andpd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAndpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f54c1",
+			wantFormat: "andpd %xmm1, %xmm0",
+		},
+		// andnps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAndnps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f55c1",
+			wantFormat: "andnps %xmm1, %xmm0",
+		},
+		// andnpd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAndnpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f55c1",
+			wantFormat: "andnpd %xmm1, %xmm0",
+		},
+		// cvttps2dq
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeCvttps2dq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f5bc1",
+			wantFormat: "cvttps2dq %xmm1, %xmm0",
+		},
+		// cvtdq2ps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeCvtdq2ps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f5bc1",
+			wantFormat: "cvtdq2ps %xmm1, %xmm0",
+		},
+		// divps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeDivps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f5ec1",
+			wantFormat: "divps %xmm1, %xmm0",
+		},
+		// divpd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeDivpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f5ec1",
+			wantFormat: "divpd %xmm1, %xmm0",
+		},
+		// divss
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeDivss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f5ec1",
+			wantFormat: "divss %xmm1, %xmm0",
+		},
+		// divsd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeDivsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f20f5ec1",
+			wantFormat: "divsd %xmm1, %xmm0",
+		},
+		// maxps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMaxps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f5fc1",
+			wantFormat: "maxps %xmm1, %xmm0",
+		},
+		// maxpd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMaxpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f5fc1",
+			wantFormat: "maxpd %xmm1, %xmm0",
+		},
+		// maxss
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMaxss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f5fc1",
+			wantFormat: "maxss %xmm1, %xmm0",
+		},
+		// maxsd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMaxsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f20f5fc1",
+			wantFormat: "maxsd %xmm1, %xmm0",
+		},
+		// minps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMinps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f5dc1",
+			wantFormat: "minps %xmm1, %xmm0",
+		},
+		// paddsw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePaddsw, newOperandReg(xmm7VReg), xmm6VReg, true) },
+			want:       "660fedf7",
+			wantFormat: "paddsw %xmm7, %xmm6",
+		},
+		// paddusb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePaddusb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fdcc1",
+			wantFormat: "paddusb %xmm1, %xmm0",
+		},
+		// paddusw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePaddusw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fddc1",
+			wantFormat: "paddusw %xmm1, %xmm0",
+		},
+		// pand
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePand, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fdbc1",
+			wantFormat: "pand %xmm1, %xmm0",
+		},
+		// pandn
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePandn, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fdfc1",
+			wantFormat: "pandn %xmm1, %xmm0",
+		},
+		// pavgb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePavgb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fe0c1",
+			wantFormat: "pavgb %xmm1, %xmm0",
+		},
+		// pavgw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePavgw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fe3c1",
+			wantFormat: "pavgw %xmm1, %xmm0",
+		},
+		// pcmpeqb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePcmpeqb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f74c1",
+			wantFormat: "pcmpeqb %xmm1, %xmm0",
+		},
+		// pcmpeqw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePcmpeqw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f75c1",
+			wantFormat: "pcmpeqw %xmm1, %xmm0",
+		},
+		// pcmpeqd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePcmpeqd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f76c1",
+			wantFormat: "pcmpeqd %xmm1, %xmm0",
+		},
+		// pcmpeqq
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePcmpeqq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3829c1",
+			wantFormat: "pcmpeqq %xmm1, %xmm0",
+		},
+		// pcmpgtb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePcmpgtb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f64c1",
+			wantFormat: "pcmpgtb %xmm1, %xmm0",
+		},
+		// pcmpgtw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePcmpgtw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f65c1",
+			wantFormat: "pcmpgtw %xmm1, %xmm0",
+		},
+		// pcmpgtd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePcmpgtd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f66c1",
+			wantFormat: "pcmpgtd %xmm1, %xmm0",
+		},
+		// pcmpgtq
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePcmpgtq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3837c1",
+			wantFormat: "pcmpgtq %xmm1, %xmm0",
+		},
+		// pmaddwd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePmaddwd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660ff5c1",
+			wantFormat: "pmaddwd %xmm1, %xmm0",
+		},
+		// pmaxsb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePmaxsb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f383cc1",
+			wantFormat: "pmaxsb %xmm1, %xmm0",
+		},
+		// pmaxsw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePmaxsw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660feec1",
+			wantFormat: "pmaxsw %xmm1, %xmm0",
+		},
+		// pmaxsd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePmaxsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f383dc1",
+			wantFormat: "pmaxsd %xmm1, %xmm0",
+		},
+		// pmaxub
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePmaxub, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fdec1",
+			wantFormat: "pmaxub %xmm1, %xmm0",
+		},
+		// pmaxuw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePmaxuw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f383ec1",
+			wantFormat: "pmaxuw %xmm1, %xmm0",
+		},
+		// pmaxud
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePmaxud, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f383fc1",
+			wantFormat: "pmaxud %xmm1, %xmm0",
+		},
+		// pminsb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePminsb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3838c1",
+			wantFormat: "pminsb %xmm1, %xmm0",
+		},
+		// pminsw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePminsw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660feac1",
+			wantFormat: "pminsw %xmm1, %xmm0",
+		},
+		// pminsd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePminsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3839c1",
+			wantFormat: "pminsd %xmm1, %xmm0",
+		},
+		// pminub
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePminub, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fdac1",
+			wantFormat: "pminub %xmm1, %xmm0",
+		},
+		// pminuw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePminuw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f383ac1",
+			wantFormat: "pminuw %xmm1, %xmm0",
+		},
+		// pminud
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePminud, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f383bc1",
+			wantFormat: "pminud %xmm1, %xmm0",
+		},
+		// pmulld
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePmulld, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3840c1",
+			wantFormat: "pmulld %xmm1, %xmm0",
+		},
+		// pmullw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePmullw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fd5c1",
+			wantFormat: "pmullw %xmm1, %xmm0",
+		},
+		// pmuludq
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePmuludq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660ff4c1",
+			wantFormat: "pmuludq %xmm1, %xmm0",
+		},
+		// por
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePor, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660febc1",
+			wantFormat: "por %xmm1, %xmm0",
+		},
+		// pshufb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePshufb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3800c1",
+			wantFormat: "pshufb %xmm1, %xmm0",
+		},
+		// psubb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePsubb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660ff8c1",
+			wantFormat: "psubb %xmm1, %xmm0",
+		},
+		// psubd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePsubd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660ffac1",
+			wantFormat: "psubd %xmm1, %xmm0",
+		},
+		// psubq
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePsubq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660ffbc1",
+			wantFormat: "psubq %xmm1, %xmm0",
+		},
+		// psubw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePsubw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660ff9c1",
+			wantFormat: "psubw %xmm1, %xmm0",
+		},
+		// psubsb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePsubsb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fe8c1",
+			wantFormat: "psubsb %xmm1, %xmm0",
+		},
+		// psubsw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePsubsw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fe9c1",
+			wantFormat: "psubsw %xmm1, %xmm0",
+		},
+		// psubusb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePsubusb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fd8c1",
+			wantFormat: "psubusb %xmm1, %xmm0",
+		},
+		// psubusw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePsubusw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fd9c1",
+			wantFormat: "psubusw %xmm1, %xmm0",
+		},
+		// punpckhbw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePunpckhbw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f68c1",
+			wantFormat: "punpckhbw %xmm1, %xmm0",
+		},
+		// punpcklbw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePunpcklbw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f60c1",
+			wantFormat: "punpcklbw %xmm1, %xmm0",
+		},
+		// pxor
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePxor, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fefc1",
+			wantFormat: "pxor %xmm1, %xmm0",
+		},
+		// subps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeSubps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f5cc1",
+			wantFormat: "subps %xmm1, %xmm0",
+		},
+		// subpd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeSubpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f5cc1",
+			wantFormat: "subpd %xmm1, %xmm0",
+		},
+		// subss
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeSubss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f5cc1",
+			wantFormat: "subss %xmm1, %xmm0",
+		},
+		// subsd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeSubsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f20f5cc1",
+			wantFormat: "subsd %xmm1, %xmm0",
+		},
+		// xorps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeXorps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f57c1",
+			wantFormat: "xorps %xmm1, %xmm0",
+		},
+		// xorpd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeXorpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f57c1",
+			wantFormat: "xorpd %xmm1, %xmm0",
+		},
+		// minpd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMinpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f5dc1",
+			wantFormat: "minpd %xmm1, %xmm0",
+		},
+		// minss
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMinss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f5dc1",
+			wantFormat: "minss %xmm1, %xmm0",
+		},
+		// minsd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMinsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f20f5dc1",
+			wantFormat: "minsd %xmm1, %xmm0",
+		},
+		// movlhps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMovlhps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f16c1",
+			wantFormat: "movlhps %xmm1, %xmm0",
+		},
+		// movsd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMovsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f20f10c1",
+			wantFormat: "movsd %xmm1, %xmm0",
+		},
+		// mulps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMulps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f59c1",
+			wantFormat: "mulps %xmm1, %xmm0",
+		},
+		// mulpd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMulpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f59c1",
+			wantFormat: "mulpd %xmm1, %xmm0",
+		},
+		// mulss
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMulss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f59c1",
+			wantFormat: "mulss %xmm1, %xmm0",
+		},
+		// mulsd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeMulsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f20f59c1",
+			wantFormat: "mulsd %xmm1, %xmm0",
+		},
+		// orpd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeOrpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f56c1",
+			wantFormat: "orpd %xmm1, %xmm0",
+		},
+		// orps
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeOrps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f56c1",
+			wantFormat: "orps %xmm1, %xmm0",
+		},
+		// packssdw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePackssdw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f6bc1",
+			wantFormat: "packssdw %xmm1, %xmm0",
+		},
+		// packsswb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePacksswb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f63c1",
+			wantFormat: "packsswb %xmm1, %xmm0",
+		},
+		// packusdw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePackusdw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f382bc1",
+			wantFormat: "packusdw %xmm1, %xmm0",
+		},
+		// packuswb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePackuswb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f67c1",
+			wantFormat: "packuswb %xmm1, %xmm0",
+		},
+		// paddb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePaddb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660ffcc1",
+			wantFormat: "paddb %xmm1, %xmm0",
+		},
+		// paddd
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePaddd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660ffec1",
+			wantFormat: "paddd %xmm1, %xmm0",
+		},
+		// paddq
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePaddq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fd4c1",
+			wantFormat: "paddq %xmm1, %xmm0",
+		},
+		// paddw
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePaddw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660ffdc1",
+			wantFormat: "paddw %xmm1, %xmm0",
+		},
+		// paddsb
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePaddsb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660fecc1",
+			wantFormat: "paddsb %xmm1, %xmm0",
+		},
+		// cvtss2sd
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeCvtss2sd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f5ac1",
+			wantFormat: "cvtss2sd %xmm1, %xmm0",
+		},
+		// cvtsd2ss
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeCvtsd2ss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f20f5ac1",
+			wantFormat: "cvtsd2ss %xmm1, %xmm0",
+		},
+		// movaps
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovaps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f28c1",
+			wantFormat: "movaps %xmm1, %xmm0",
+		},
+		// movapd
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovapd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f28c1",
+			wantFormat: "movapd %xmm1, %xmm0",
+		},
+		// movdqa
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovdqa, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f6fc1",
+			wantFormat: "movdqa %xmm1, %xmm0",
+		},
+		// movdqu
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovdqu, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f6fc1",
+			wantFormat: "movdqu %xmm1, %xmm0",
+		},
+		// movsd
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f20f10c1",
+			wantFormat: "movsd %xmm1, %xmm0",
+		},
+		// movss
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f10c1",
+			wantFormat: "movss %xmm1, %xmm0",
+		},
+		// movups
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovups, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f10c1",
+			wantFormat: "movups %xmm1, %xmm0",
+		},
+		// movupd
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovupd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f10c1",
+			wantFormat: "movupd %xmm1, %xmm0",
+		},
+		// pabsb
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePabsb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f381cc1",
+			wantFormat: "pabsb %xmm1, %xmm0",
+		},
+		// pabsw
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePabsw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f381dc1",
+			wantFormat: "pabsw %xmm1, %xmm0",
+		},
+		// pabsd
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePabsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f381ec1",
+			wantFormat: "pabsd %xmm1, %xmm0",
+		},
+		// pmovsxbd
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxbd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3821c1",
+			wantFormat: "pmovsxbd %xmm1, %xmm0",
+		},
+		// pmovsxbw
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxbw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3820c1",
+			wantFormat: "pmovsxbw %xmm1, %xmm0",
+		},
+		// pmovsxbq
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxbq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3822c1",
+			wantFormat: "pmovsxbq %xmm1, %xmm0",
+		},
+		// pmovsxwd
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxwd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3823c1",
+			wantFormat: "pmovsxwd %xmm1, %xmm0",
+		},
+		// pmovsxwq
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxwq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3824c1",
+			wantFormat: "pmovsxwq %xmm1, %xmm0",
+		},
+		// pmovsxdq
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxdq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3825c1",
+			wantFormat: "pmovsxdq %xmm1, %xmm0",
+		},
+		// pmovzxbd
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxbd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3831c1",
+			wantFormat: "pmovzxbd %xmm1, %xmm0",
+		},
+		// pmovzxbw
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxbw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3830c1",
+			wantFormat: "pmovzxbw %xmm1, %xmm0",
+		},
+		// pmovzxbq
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxbq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3832c1",
+			wantFormat: "pmovzxbq %xmm1, %xmm0",
+		},
+		// pmovzxwd
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3833c1",
+			wantFormat: "pmovzxwd %xmm1, %xmm0",
+		},
+		// pmovzxwq
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxwq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3834c1",
+			wantFormat: "pmovzxwq %xmm1, %xmm0",
+		},
+		// pmovzxdq
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxdq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f3835c1",
+			wantFormat: "pmovzxdq %xmm1, %xmm0",
+		},
+		// sqrtps
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "0f51c1",
+			wantFormat: "sqrtps %xmm1, %xmm0",
+		},
+		// sqrtpd
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "660f51c1",
+			wantFormat: "sqrtpd %xmm1, %xmm0",
+		},
+		// sqrtss
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f30f51c1",
+			wantFormat: "sqrtss %xmm1, %xmm0",
+		},
+		// sqrtsd
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			want:       "f20f51c1",
+			wantFormat: "sqrtsd %xmm1, %xmm0",
+		},
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm15VReg), xmm0VReg, true) },
+			want:       "f2410f51c7",
+			wantFormat: "sqrtsd %xmm15, %xmm0",
+		},
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm1VReg), xmm15VReg, true) },
+			want:       "f2440f51f9",
+			wantFormat: "sqrtsd %xmm1, %xmm15",
+		},
+		{
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm11VReg), xmm15VReg, true) },
+			want:       "f2450f51fb",
+			wantFormat: "sqrtsd %xmm11, %xmm15",
+		},
+		// movd
+		{
+			setup:      func(i *instruction) { i.asGprToXmm(sseOpcodeMovd, newOperandReg(rdiVReg), xmm0VReg, false) },
+			want:       "660f6ec7",
+			wantFormat: "movd %edi, %xmm0",
+		},
+		// movq
+		{
+			setup:      func(i *instruction) { i.asGprToXmm(sseOpcodeMovq, newOperandReg(rcxVReg), xmm0VReg, true) },
+			want:       "66480f6ec1",
+			wantFormat: "movq %rcx, %xmm0",
+		},
+		// cvtsi2ss
+		{
+			setup:      func(i *instruction) { i.asGprToXmm(sseOpcodeCvtsi2ss, newOperandReg(rdiVReg), xmm0VReg, true) },
+			want:       "f3480f2ac7",
+			wantFormat: "cvtsi2ss %rdi, %xmm0",
+		},
+		// cvtsi2sd
+		{
+			setup:      func(i *instruction) { i.asGprToXmm(sseOpcodeCvtsi2sd, newOperandReg(rdiVReg), xmm0VReg, true) },
+			want:       "f2480f2ac7",
+			wantFormat: "cvtsi2sd %rdi, %xmm0",
+		},
+		{
+			setup:      func(i *instruction) { i.asGprToXmm(sseOpcodeCvtsi2sd, newOperandReg(rdiVReg), xmm0VReg, false) },
+			want:       "f20f2ac7",
+			wantFormat: "cvtsi2sd %edi, %xmm0",
+		},
+	} {
+		tc := tc
+		t.Run(tc.wantFormat, func(t *testing.T) {
+			i := &instruction{}
+			tc.setup(i)
+
+			require.Equal(t, tc.wantFormat, i.String())
+
+			mc := &mockCompiler{}
+			m := &machine{c: mc}
+			i.encode(m.c)
+			require.Equal(t, tc.want, hex.EncodeToString(mc.buf))
+
+			// TODO: verify the size of the encoded instructions.
+			//var actualSize int
+			//for cur := i; cur != nil; cur = cur.next {
+			//	actualSize += int(cur.size())
+			//}
+			//require.Equal(t, len(tc.want)/2, actualSize)
+		})
+	}
+}
diff --git a/internal/engine/wazevo/backend/isa/amd64/reg_test.go b/internal/engine/wazevo/backend/isa/amd64/reg_test.go
new file mode 100644
index 00000000..da7b5bdd
--- /dev/null
+++ b/internal/engine/wazevo/backend/isa/amd64/reg_test.go
@@ -0,0 +1,36 @@
+package amd64
+
+import (
+	"testing"
+
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
+	"github.com/tetratelabs/wazero/internal/testing/require"
+)
+
+func Test_formatVRegSize(t *testing.T) {
+	tests := []struct {
+		r   regalloc.VReg
+		_64 bool
+		exp string
+	}{
+		// Real registers.
+		{r: raxVReg, _64: true, exp: "%rax"},
+		{r: raxVReg, _64: false, exp: "%eax"},
+		{r: r15VReg, _64: true, exp: "%r15"},
+		{r: r15VReg, _64: false, exp: "%r15d"},
+		{r: xmm0VReg, _64: true, exp: "%xmm0"},
+		{r: xmm0VReg, _64: false, exp: "%xmm0"},
+		{r: xmm15VReg, _64: true, exp: "%xmm15"},
+		{r: xmm15VReg, _64: false, exp: "%xmm15"},
+		// Non-real registers.
+		{r: regalloc.VReg(5555).SetRegType(regalloc.RegTypeInt), _64: true, exp: "%r5555?"},
+		{r: regalloc.VReg(5555).SetRegType(regalloc.RegTypeInt), _64: false, exp: "%r5555d?"},
+		{r: regalloc.VReg(123).SetRegType(regalloc.RegTypeFloat), _64: true, exp: "%xmm123?"},
+		{r: regalloc.VReg(432).SetRegType(regalloc.RegTypeFloat), _64: false, exp: "%xmm432?"},
+	}
+	for _, tt := range tests {
+		t.Run(tt.exp, func(t *testing.T) {
+			require.Equal(t, tt.exp, formatVRegSized(tt.r, tt._64))
+		})
+	}
+}
diff --git a/internal/engine/wazevo/backend/isa/amd64/util_test.go b/internal/engine/wazevo/backend/isa/amd64/util_test.go
new file mode 100644
index 00000000..8d2d220e
--- /dev/null
+++ b/internal/engine/wazevo/backend/isa/amd64/util_test.go
@@ -0,0 +1,125 @@
+package amd64
+
+import (
+	"context"
+
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
+)
+
+// mockCompiler implements backend.Compiler for testing.
+type mockCompiler struct {
+	currentGID  ssa.InstructionGroupID
+	vRegCounter int
+	vRegMap     map[ssa.Value]regalloc.VReg
+	definitions map[ssa.Value]*backend.SSAValueDefinition
+	sigs        map[ssa.SignatureID]*ssa.Signature
+	typeOf      map[regalloc.VRegID]ssa.Type
+	relocs      []backend.RelocationInfo
+	buf         []byte
+}
+
+func (m *mockCompiler) GetFunctionABI(sig *ssa.Signature) *backend.FunctionABI {
+	// TODO implement me
+	panic("implement me")
+}
+
+func (m *mockCompiler) SSABuilder() ssa.Builder { return nil }
+
+func (m *mockCompiler) LoopNestingForestRoots() []ssa.BasicBlock { panic("TODO") }
+
+func (m *mockCompiler) SourceOffsetInfo() []backend.SourceOffsetInfo { return nil }
+
+func (m *mockCompiler) AddSourceOffsetInfo(int64, ssa.SourceOffset) {}
+
+func (m *mockCompiler) AddRelocationInfo(funcRef ssa.FuncRef) {
+	m.relocs = append(m.relocs, backend.RelocationInfo{FuncRef: funcRef, Offset: int64(len(m.buf))})
+}
+
+func (m *mockCompiler) Emit4Bytes(b uint32) {
+	m.buf = append(m.buf, byte(b), byte(b>>8), byte(b>>16), byte(b>>24))
+}
+
+func (m *mockCompiler) EmitByte(b byte) {
+	m.buf = append(m.buf, b)
+}
+
+func (m *mockCompiler) Emit8Bytes(b uint64) {
+	m.buf = append(m.buf, byte(b), byte(b>>8), byte(b>>16), byte(b>>24), byte(b>>32), byte(b>>40), byte(b>>48), byte(b>>56))
+}
+
+func (m *mockCompiler) Encode()     {}
+func (m *mockCompiler) Buf() []byte { return m.buf }
+func (m *mockCompiler) TypeOf(v regalloc.VReg) (ret ssa.Type) {
+	return m.typeOf[v.ID()]
+}
+func (m *mockCompiler) Finalize(context.Context) {}
+func (m *mockCompiler) RegAlloc()                {}
+func (m *mockCompiler) Lower()                   {}
+func (m *mockCompiler) Format() string           { return "" }
+func (m *mockCompiler) Init()                    {}
+
+func newMockCompilationContext() *mockCompiler { //nolint
+	return &mockCompiler{
+		vRegMap:     make(map[ssa.Value]regalloc.VReg),
+		definitions: make(map[ssa.Value]*backend.SSAValueDefinition),
+		typeOf:      map[regalloc.VRegID]ssa.Type{},
+	}
+}
+
+// ResolveSignature implements backend.Compiler.
+func (m *mockCompiler) ResolveSignature(id ssa.SignatureID) *ssa.Signature {
+	return m.sigs[id]
+}
+
+// AllocateVReg implements backend.Compiler.
+func (m *mockCompiler) AllocateVReg(typ ssa.Type) regalloc.VReg {
+	m.vRegCounter++
+	regType := regalloc.RegTypeOf(typ)
+	ret := regalloc.VReg(m.vRegCounter).SetRegType(regType)
+	m.typeOf[ret.ID()] = typ
+	return ret
+}
+
+// ValueDefinition implements backend.Compiler.
+func (m *mockCompiler) ValueDefinition(value ssa.Value) *backend.SSAValueDefinition {
+	definition, exists := m.definitions[value]
+	if !exists {
+		return nil
+	}
+	return definition
+}
+
+// VRegOf implements backend.Compiler.
+func (m *mockCompiler) VRegOf(value ssa.Value) regalloc.VReg {
+	vReg, exists := m.vRegMap[value]
+	if !exists {
+		panic("Value does not exist")
+	}
+	return vReg
+}
+
+// MatchInstr implements backend.Compiler.
+func (m *mockCompiler) MatchInstr(def *backend.SSAValueDefinition, opcode ssa.Opcode) bool {
+	instr := def.Instr
+	return def.IsFromInstr() &&
+		instr.Opcode() == opcode &&
+		instr.GroupID() == m.currentGID &&
+		def.RefCount < 2
+}
+
+// MatchInstrOneOf implements backend.Compiler.
+func (m *mockCompiler) MatchInstrOneOf(def *backend.SSAValueDefinition, opcodes []ssa.Opcode) ssa.Opcode {
+	for _, opcode := range opcodes {
+		if m.MatchInstr(def, opcode) {
+			return opcode
+		}
+	}
+	return ssa.OpcodeInvalid
+}
+
+// Compile implements backend.Compiler.
+func (m *mockCompiler) Compile(context.Context) (_ []byte, _ []backend.RelocationInfo, _ error) {
+	return
+}
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi_go_call_test.go b/internal/engine/wazevo/backend/isa/arm64/abi_go_call_test.go
index d8b5c927..99a81fca 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi_go_call_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi_go_call_test.go
@@ -623,7 +623,7 @@ func Test_goFunctionCallStoreStackResult(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Run(tc.name, func(t *testing.T) {
 				_, _, m := newSetupWithMockContext()
-				m.currentABI = &functionABI{ArgStackSize: 8}
+				m.currentABI = &backend.FunctionABI{ArgStackSize: 8}
 
 				nop := m.allocateNop()
 				m.goFunctionCallStoreStackResult(nop, spVReg, tc.result, tc.resultReg)
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi_test.go b/internal/engine/wazevo/backend/isa/arm64/abi_test.go
index 9d4b8673..f320f175 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi_test.go
@@ -13,12 +13,12 @@ func TestAbiImpl_init(t *testing.T) {
 	for _, tc := range []struct {
 		name string
 		sig  *ssa.Signature
-		exp  functionABI
+		exp  backend.FunctionABI
 	}{
 		{
 			name: "empty sig",
 			sig:  &ssa.Signature{},
-			exp:  functionABI{},
+			exp:  backend.FunctionABI{},
 		},
 		{
 			name: "small sig",
@@ -26,7 +26,7 @@ func TestAbiImpl_init(t *testing.T) {
 				Params:  []ssa.Type{ssa.TypeI32, ssa.TypeF32, ssa.TypeI32},
 				Results: []ssa.Type{ssa.TypeI64, ssa.TypeF64},
 			},
-			exp: functionABI{
+			exp: backend.FunctionABI{
 				Args: []backend.ABIArg{
 					{Index: 0, Kind: backend.ABIArgKindReg, Reg: x0VReg, Type: ssa.TypeI32},
 					{Index: 1, Kind: backend.ABIArgKindReg, Reg: v0VReg, Type: ssa.TypeF32},
@@ -80,7 +80,7 @@ func TestAbiImpl_init(t *testing.T) {
 					ssa.TypeI64, ssa.TypeF64,
 				},
 			},
-			exp: functionABI{
+			exp: backend.FunctionABI{
 				ArgStackSize: 128, RetStackSize: 128,
 				Args: []backend.ABIArg{
 					{Index: 0, Kind: backend.ABIArgKindReg, Reg: x0VReg, Type: ssa.TypeI32},
@@ -178,7 +178,7 @@ func TestAbiImpl_init(t *testing.T) {
 					ssa.TypeI64, ssa.TypeF64,
 				},
 			},
-			exp: functionABI{
+			exp: backend.FunctionABI{
 				Args: []backend.ABIArg{
 					{Index: 0, Kind: backend.ABIArgKindReg, Reg: x0VReg, Type: ssa.TypeI32},
 					{Index: 1, Kind: backend.ABIArgKindReg, Reg: v0VReg, Type: ssa.TypeF32},
@@ -222,8 +222,8 @@ func TestAbiImpl_init(t *testing.T) {
 	} {
 		tc := tc
 		t.Run(tc.name, func(t *testing.T) {
-			abi := functionABI{}
-			abi.Init(tc.sig)
+			abi := backend.FunctionABI{}
+			abi.Init(tc.sig, intParamResultRegs, floatParamResultRegs)
 			require.Equal(t, tc.exp.Args, abi.Args)
 			require.Equal(t, tc.exp.Rets, abi.Rets)
 			require.Equal(t, tc.exp.ArgStackSize, abi.ArgStackSize)
@@ -264,7 +264,8 @@ func TestAbiImpl_callerGenVRegToFunctionArg_constant_inlining(t *testing.T) {
 
 	i64 := builder.AllocateInstruction().AsIconst64(10).Insert(builder)
 	f64 := builder.AllocateInstruction().AsF64const(3.14).Insert(builder)
-	abi := m.getOrCreateFunctionABI(&ssa.Signature{Params: []ssa.Type{ssa.TypeI64, ssa.TypeF64}})
+	abi := &backend.FunctionABI{}
+	abi.Init(&ssa.Signature{Params: []ssa.Type{ssa.TypeI64, ssa.TypeF64}}, intParamResultRegs, floatParamResultRegs)
 	m.callerGenVRegToFunctionArg(abi, 0, regalloc.VReg(100).SetRegType(regalloc.RegTypeInt), &backend.SSAValueDefinition{Instr: i64, RefCount: 1}, 0)
 	m.callerGenVRegToFunctionArg(abi, 1, regalloc.VReg(50).SetRegType(regalloc.RegTypeFloat), &backend.SSAValueDefinition{Instr: f64, RefCount: 1}, 0)
 	require.Equal(t, `movz x100?, #0xa, lsl 0
diff --git a/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue_test.go b/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue_test.go
index 26c6ab1c..13174407 100644
--- a/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue_test.go
@@ -3,6 +3,7 @@ package arm64
 import (
 	"testing"
 
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
@@ -12,7 +13,7 @@ func TestMachine_SetupPrologue(t *testing.T) {
 		spillSlotSize int64
 		clobberedRegs []regalloc.VReg
 		exp           string
-		abi           functionABI
+		abi           backend.FunctionABI
 	}{
 		{
 			spillSlotSize: 0,
@@ -24,7 +25,7 @@ func TestMachine_SetupPrologue(t *testing.T) {
 		},
 		{
 			spillSlotSize: 0,
-			abi:           functionABI{ArgStackSize: 16, RetStackSize: 16},
+			abi:           backend.FunctionABI{ArgStackSize: 16, RetStackSize: 16},
 			exp: `
 	orr x27, xzr, #0x20
 	sub sp, sp, x27
@@ -74,7 +75,7 @@ func TestMachine_SetupPrologue(t *testing.T) {
 		},
 		{
 			spillSlotSize: 320,
-			abi:           functionABI{ArgStackSize: 320, RetStackSize: 160},
+			abi:           backend.FunctionABI{ArgStackSize: 320, RetStackSize: 160},
 			clobberedRegs: []regalloc.VReg{v18VReg, v19VReg, x18VReg, x25VReg},
 			exp: `
 	orr x27, xzr, #0x1e0
@@ -117,7 +118,7 @@ func TestMachine_SetupPrologue(t *testing.T) {
 func TestMachine_SetupEpilogue(t *testing.T) {
 	for _, tc := range []struct {
 		exp           string
-		abi           functionABI
+		abi           backend.FunctionABI
 		clobberedRegs []regalloc.VReg
 		spillSlotSize int64
 	}{
@@ -148,7 +149,7 @@ func TestMachine_SetupEpilogue(t *testing.T) {
 	add sp, sp, #0x20
 	ret
 `,
-			abi:           functionABI{ArgStackSize: 16, RetStackSize: 16},
+			abi:           backend.FunctionABI{ArgStackSize: 16, RetStackSize: 16},
 			spillSlotSize: 16 * 5,
 			clobberedRegs: nil,
 		},
@@ -201,7 +202,7 @@ func TestMachine_SetupEpilogue(t *testing.T) {
 	ret
 `,
 			spillSlotSize: 16 * 10,
-			abi:           functionABI{ArgStackSize: 16, RetStackSize: 320},
+			abi:           backend.FunctionABI{ArgStackSize: 16, RetStackSize: 320},
 			clobberedRegs: []regalloc.VReg{v18VReg, v27VReg, x18VReg, x25VReg},
 		},
 	} {
diff --git a/internal/engine/wazevo/backend/isa/arm64/machine_test.go b/internal/engine/wazevo/backend/isa/arm64/machine_test.go
index 8d3699bd..a585f1fb 100644
--- a/internal/engine/wazevo/backend/isa/arm64/machine_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/machine_test.go
@@ -3,6 +3,7 @@ package arm64
 import (
 	"testing"
 
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
@@ -79,7 +80,7 @@ func TestMachine_arg0OffsetFromSP(t *testing.T) {
 func TestMachine_ret0OffsetFromSP(t *testing.T) {
 	m := &machine{
 		clobberedRegs: make([]regalloc.VReg, 10), spillSlotSize: 16 * 8,
-		currentABI: &functionABI{ArgStackSize: 180},
+		currentABI: &backend.FunctionABI{ArgStackSize: 180},
 	}
 	require.Equal(t, int64(16*18)+32+180, m.ret0OffsetFromSP())
 }
diff --git a/internal/engine/wazevo/backend/isa/arm64/util_test.go b/internal/engine/wazevo/backend/isa/arm64/util_test.go
index a033db70..6dd7862c 100644
--- a/internal/engine/wazevo/backend/isa/arm64/util_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/util_test.go
@@ -61,6 +61,11 @@ type mockCompiler struct {
 	buf         []byte
 }
 
+func (m *mockCompiler) GetFunctionABI(sig *ssa.Signature) *backend.FunctionABI {
+	// TODO implement me
+	panic("implement me")
+}
+
 func (m *mockCompiler) SSABuilder() ssa.Builder { return nil }
 
 func (m *mockCompiler) LoopNestingForestRoots() []ssa.BasicBlock { panic("TODO") }
@@ -77,6 +82,14 @@ func (m *mockCompiler) Emit4Bytes(b uint32) {
 	m.buf = append(m.buf, byte(b), byte(b>>8), byte(b>>16), byte(b>>24))
 }
 
+func (m *mockCompiler) EmitByte(b byte) {
+	m.buf = append(m.buf, b)
+}
+
+func (m *mockCompiler) Emit8Bytes(b uint64) {
+	m.buf = append(m.buf, byte(b), byte(b>>8), byte(b>>16), byte(b>>24), byte(b>>32), byte(b>>40), byte(b>>48), byte(b>>56))
+}
+
 func (m *mockCompiler) Encode()     {}
 func (m *mockCompiler) Buf() []byte { return m.buf }
 func (m *mockCompiler) TypeOf(v regalloc.VReg) (ret ssa.Type) {
diff --git a/internal/engine/wazevo/backend/machine_test.go b/internal/engine/wazevo/backend/machine_test.go
index 45ed0f22..e15185d0 100644
--- a/internal/engine/wazevo/backend/machine_test.go
+++ b/internal/engine/wazevo/backend/machine_test.go
@@ -10,19 +10,24 @@ import (
 
 // mockMachine implements Machine for testing.
 type mockMachine struct {
-	startLoweringFunction  func(id ssa.BasicBlockID)
-	startBlock             func(block ssa.BasicBlock)
-	lowerSingleBranch      func(b *ssa.Instruction)
-	lowerConditionalBranch func(b *ssa.Instruction)
-	lowerInstr             func(instruction *ssa.Instruction)
-	endBlock               func()
-	endLoweringFunction    func()
-	reset                  func()
-	insertMove             func(dst, src regalloc.VReg)
-	insertLoadConstant     func(instr *ssa.Instruction, vr regalloc.VReg)
-	format                 func() string
-	linkAdjacentBlocks     func(prev, next ssa.BasicBlock)
-	rinfo                  *regalloc.RegisterInfo
+	argResultInts, argResultFloats []regalloc.RealReg
+	startLoweringFunction          func(id ssa.BasicBlockID)
+	startBlock                     func(block ssa.BasicBlock)
+	lowerSingleBranch              func(b *ssa.Instruction)
+	lowerConditionalBranch         func(b *ssa.Instruction)
+	lowerInstr                     func(instruction *ssa.Instruction)
+	endBlock                       func()
+	endLoweringFunction            func()
+	reset                          func()
+	insertMove                     func(dst, src regalloc.VReg)
+	insertLoadConstant             func(instr *ssa.Instruction, vr regalloc.VReg)
+	format                         func() string
+	linkAdjacentBlocks             func(prev, next ssa.BasicBlock)
+	rinfo                          *regalloc.RegisterInfo
+}
+
+func (m mockMachine) ArgsResultsRegs() (argResultInts, argResultFloats []regalloc.RealReg) {
+	return m.argResultInts, m.argResultFloats
 }
 
 func (m mockMachine) RegAlloc() { panic("implement me") }
@@ -78,8 +83,8 @@ func (m mockMachine) InsertReturn() { panic("TODO") }
 // LinkAdjacentBlocks implements Machine.LinkAdjacentBlocks.
 func (m mockMachine) LinkAdjacentBlocks(prev, next ssa.BasicBlock) { m.linkAdjacentBlocks(prev, next) }
 
-// InitializeABI implements Machine.InitializeABI.
-func (m mockMachine) InitializeABI(*ssa.Signature) {}
+// SetCurrentABI implements Machine.SetCurrentABI.
+func (m mockMachine) SetCurrentABI(*FunctionABI) {}
 
 // SetCompiler implements Machine.SetCompiler.
 func (m mockMachine) SetCompiler(Compiler) {}
diff --git a/internal/sysfs/sock_test.go b/internal/sysfs/sock_test.go
index 82b8ab56..869441dd 100644
--- a/internal/sysfs/sock_test.go
+++ b/internal/sysfs/sock_test.go
@@ -48,6 +48,8 @@ func TestTcpConnFile_Write(t *testing.T) {
 }
 
 func TestTcpConnFile_Read(t *testing.T) {
+	// Test #1: Read from a TCP connection with default synchrony
+	// (i.e., without explicitly setting the non-blocking flag).
 	listen, err := net.Listen("tcp", "127.0.0.1:0")
 	require.NoError(t, err)
 	defer listen.Close()
@@ -83,6 +85,51 @@ func TestTcpConnFile_Read(t *testing.T) {
 	require.Zero(t, errno)
 	require.NoError(t, err)
 	require.Equal(t, "waze", string(bytes))
+
+	// Test #2: Read from a TCP connection asynchronously (i.e., with
+	// the non-blocking flag set explicitly).
+	tcpAddr2, err := net.ResolveTCPAddr("tcp", listen.Addr().String())
+	require.NoError(t, err)
+	tcp2, err := net.DialTCP("tcp", nil, tcpAddr2)
+	require.NoError(t, err)
+	defer tcp.Close() //nolint
+
+	// Use a goroutine to asynchronously write to the TCP connection
+	// with a delay that is visible to the test.
+	go func() {
+		time.Sleep(200 * time.Millisecond)
+		n2, err := tcp2.Write([]byte("wazero"))
+		require.NoError(t, err)
+		require.NotEqual(t, 0, n2)
+	}()
+
+	conn2, err := listen.Accept()
+	require.NoError(t, err)
+	defer conn.Close()
+
+	bytes2 := make([]byte, 4)
+
+	require.NoError(t, err)
+	errno2 := sys.Errno(0)
+	file2 := newTcpConn(conn2.(*net.TCPConn))
+	errno2 = file2.(*tcpConnFile).SetNonblock(true)
+	require.Zero(t, errno2)
+
+	// Ensure we start by getting EAGAIN.
+	_, errno2 = file2.Read(bytes2)
+	require.Equal(t, sys.EAGAIN, errno2)
+
+	// Ensure we don't interrupt until we get a non-zero errno,
+	// and we retry on EAGAIN (i.e. when nonblocking is true).
+	for {
+		_, errno2 = file2.Read(bytes2)
+		if errno2 != sys.EAGAIN {
+			break
+		}
+	}
+	require.Zero(t, errno2)
+	require.NoError(t, err)
+	require.Equal(t, "waze", string(bytes2))
 }
 
 func TestTcpConnFile_Stat(t *testing.T) {