Repository: tetratelabs/wazero
Pull request #1900 (2024-01-05 05:24:13)

threads: compile atomic ops in wazeroir


Patch:
diff --git a/internal/wazeroir/compiler.go b/internal/wazeroir/compiler.go
index 47ddc92b..1dc2cd62 100644
--- a/internal/wazeroir/compiler.go
+++ b/internal/wazeroir/compiler.go
@@ -377,6 +377,8 @@ func (c *Compiler) handleInstruction() error {
 		var instName string
 		if op == wasm.OpcodeVecPrefix {
 			instName = wasm.VectorInstructionName(c.body[c.pc+1])
+		} else if op == wasm.OpcodeAtomicPrefix {
+			instName = wasm.AtomicInstructionName(c.body[c.pc+1])
 		} else if op == wasm.OpcodeMiscPrefix {
 			instName = wasm.MiscInstructionName(c.body[c.pc+1])
 		} else {
@@ -2848,6 +2850,548 @@ operatorSwitch:
 		default:
 			return fmt.Errorf("unsupported vector instruction in wazeroir: %s", wasm.VectorInstructionName(vecOp))
 		}
+	case wasm.OpcodeAtomicPrefix:
+		c.pc++
+		atomicOp := c.body[c.pc]
+		switch atomicOp {
+		case wasm.OpcodeAtomicMemoryWait32:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicMemoryWait32Name)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicMemoryWait(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicMemoryWait64:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicMemoryWait64Name)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicMemoryWait(UnsignedTypeI64, imm),
+			)
+		case wasm.OpcodeAtomicMemoryNotify:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicMemoryNotifyName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicMemoryNotify(imm),
+			)
+		case wasm.OpcodeAtomicFence:
+			// Skip immediate value
+			c.pc++
+			_ = c.body[c.pc]
+			c.emit(
+				NewOperationAtomicFence(),
+			)
+		case wasm.OpcodeAtomicI32Load:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32LoadName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicLoad(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicI64Load:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64LoadName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicLoad(UnsignedTypeI64, imm),
+			)
+		case wasm.OpcodeAtomicI32Load8U:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Load8UName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicLoad8(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicI32Load16U:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Load16UName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicLoad16(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicI64Load8U:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Load8UName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicLoad8(UnsignedTypeI64, imm),
+			)
+		case wasm.OpcodeAtomicI64Load16U:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Load16UName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicLoad16(UnsignedTypeI64, imm),
+			)
+		case wasm.OpcodeAtomicI64Load32U:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Load32UName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicLoad(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicI32Store:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32StoreName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicStore(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicI32Store8:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Store8Name)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicStore8(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicI32Store16:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Store16Name)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicStore16(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicI64Store:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64StoreName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicStore(UnsignedTypeI64, imm),
+			)
+		case wasm.OpcodeAtomicI64Store8:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Store8Name)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicStore8(UnsignedTypeI64, imm),
+			)
+		case wasm.OpcodeAtomicI64Store16:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Store16Name)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicStore16(UnsignedTypeI64, imm),
+			)
+		case wasm.OpcodeAtomicI64Store32:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Store32Name)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicStore(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicI32RmwAdd:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwAddName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpAdd),
+			)
+		case wasm.OpcodeAtomicI64RmwAdd:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwAddName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpAdd),
+			)
+		case wasm.OpcodeAtomicI32Rmw8AddU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8AddUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpAdd),
+			)
+		case wasm.OpcodeAtomicI64Rmw8AddU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8AddUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpAdd),
+			)
+		case wasm.OpcodeAtomicI32Rmw16AddU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16AddUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpAdd),
+			)
+		case wasm.OpcodeAtomicI64Rmw16AddU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16AddUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpAdd),
+			)
+		case wasm.OpcodeAtomicI64Rmw32AddU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32AddUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpAdd),
+			)
+		case wasm.OpcodeAtomicI32RmwSub:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwSubName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpSub),
+			)
+		case wasm.OpcodeAtomicI64RmwSub:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwSubName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpSub),
+			)
+		case wasm.OpcodeAtomicI32Rmw8SubU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8SubUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpSub),
+			)
+		case wasm.OpcodeAtomicI64Rmw8SubU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8SubUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpSub),
+			)
+		case wasm.OpcodeAtomicI32Rmw16SubU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16SubUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpSub),
+			)
+		case wasm.OpcodeAtomicI64Rmw16SubU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16SubUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpSub),
+			)
+		case wasm.OpcodeAtomicI64Rmw32SubU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32SubUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpSub),
+			)
+		case wasm.OpcodeAtomicI32RmwAnd:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwAndName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpAnd),
+			)
+		case wasm.OpcodeAtomicI64RmwAnd:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwAndName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpAnd),
+			)
+		case wasm.OpcodeAtomicI32Rmw8AndU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8AndUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpAnd),
+			)
+		case wasm.OpcodeAtomicI64Rmw8AndU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8AndUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpAnd),
+			)
+		case wasm.OpcodeAtomicI32Rmw16AndU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16AndUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpAnd),
+			)
+		case wasm.OpcodeAtomicI64Rmw16AndU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16AndUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpAnd),
+			)
+		case wasm.OpcodeAtomicI64Rmw32AndU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32AndUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpAnd),
+			)
+		case wasm.OpcodeAtomicI32RmwOr:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwOrName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpOr),
+			)
+		case wasm.OpcodeAtomicI64RmwOr:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwOrName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpOr),
+			)
+		case wasm.OpcodeAtomicI32Rmw8OrU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8OrUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpOr),
+			)
+		case wasm.OpcodeAtomicI64Rmw8OrU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8OrUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpOr),
+			)
+		case wasm.OpcodeAtomicI32Rmw16OrU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16OrUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpOr),
+			)
+		case wasm.OpcodeAtomicI64Rmw16OrU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16OrUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpOr),
+			)
+		case wasm.OpcodeAtomicI64Rmw32OrU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32OrUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpOr),
+			)
+		case wasm.OpcodeAtomicI32RmwXor:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwXorName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpXor),
+			)
+		case wasm.OpcodeAtomicI64RmwXor:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwXorName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpXor),
+			)
+		case wasm.OpcodeAtomicI32Rmw8XorU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8XorUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpXor),
+			)
+		case wasm.OpcodeAtomicI64Rmw8XorU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8XorUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpXor),
+			)
+		case wasm.OpcodeAtomicI32Rmw16XorU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16XorUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpXor),
+			)
+		case wasm.OpcodeAtomicI64Rmw16XorU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16XorUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpXor),
+			)
+		case wasm.OpcodeAtomicI64Rmw32XorU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32XorUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpXor),
+			)
+		case wasm.OpcodeAtomicI32RmwXchg:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwXchgName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpNop),
+			)
+		case wasm.OpcodeAtomicI64RmwXchg:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwXchgName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpNop),
+			)
+		case wasm.OpcodeAtomicI32Rmw8XchgU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8XchgUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpNop),
+			)
+		case wasm.OpcodeAtomicI64Rmw8XchgU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8XchgUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpNop),
+			)
+		case wasm.OpcodeAtomicI32Rmw16XchgU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16XchgUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpNop),
+			)
+		case wasm.OpcodeAtomicI64Rmw16XchgU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16XchgUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpNop),
+			)
+		case wasm.OpcodeAtomicI64Rmw32XchgU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32XchgUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpNop),
+			)
+		case wasm.OpcodeAtomicI32RmwCmpxchg:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwCmpxchgName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMWCmpxchg(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicI64RmwCmpxchg:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwCmpxchgName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMWCmpxchg(UnsignedTypeI64, imm),
+			)
+		case wasm.OpcodeAtomicI32Rmw8CmpxchgU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8CmpxchgUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8Cmpxchg(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicI64Rmw8CmpxchgU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8CmpxchgUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW8Cmpxchg(UnsignedTypeI64, imm),
+			)
+		case wasm.OpcodeAtomicI32Rmw16CmpxchgU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16CmpxchgUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16Cmpxchg(UnsignedTypeI32, imm),
+			)
+		case wasm.OpcodeAtomicI64Rmw16CmpxchgU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16CmpxchgUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMW16Cmpxchg(UnsignedTypeI64, imm),
+			)
+		case wasm.OpcodeAtomicI64Rmw32CmpxchgU:
+			imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32CmpxchgUName)
+			if err != nil {
+				return err
+			}
+			c.emit(
+				NewOperationAtomicRMWCmpxchg(UnsignedTypeI32, imm),
+			)
+		default:
+			return fmt.Errorf("unsupported atomic instruction in wazeroir: %s", wasm.AtomicInstructionName(atomicOp))
+		}
 	default:
 		return fmt.Errorf("unsupported instruction in wazeroir: 0x%x", op)
 	}
diff --git a/internal/wazeroir/operations.go b/internal/wazeroir/operations.go
index 4041944a..d27dc0b7 100644
--- a/internal/wazeroir/operations.go
+++ b/internal/wazeroir/operations.go
@@ -415,6 +415,36 @@ func (o OperationKind) String() (ret string) {
 		ret = "V128ITruncSatFromF"
 	case OperationKindBuiltinFunctionCheckExitCode:
 		ret = "BuiltinFunctionCheckExitCode"
+	case OperationKindAtomicMemoryWait:
+		ret = "OperationKindAtomicMemoryWait"
+	case OperationKindAtomicMemoryNotify:
+		ret = "OperationKindAtomicMemoryNotify"
+	case OperationKindAtomicFence:
+		ret = "OperationKindAtomicFence"
+	case OperationKindAtomicLoad:
+		ret = "OperationKindAtomicLoad"
+	case OperationKindAtomicLoad8:
+		ret = "OperationKindAtomicLoad8"
+	case OperationKindAtomicLoad16:
+		ret = "OperationKindAtomicLoad16"
+	case OperationKindAtomicStore:
+		ret = "OperationKindAtomicStore"
+	case OperationKindAtomicStore8:
+		ret = "OperationKindAtomicStore8"
+	case OperationKindAtomicStore16:
+		ret = "OperationKindAtomicStore16"
+	case OperationKindAtomicRMW:
+		ret = "OperationKindAtomicRMW"
+	case OperationKindAtomicRMW8:
+		ret = "OperationKindAtomicRMW8"
+	case OperationKindAtomicRMW16:
+		ret = "OperationKindAtomicRMW16"
+	case OperationKindAtomicRMWCmpxchg:
+		ret = "OperationKindAtomicRMWCmpxchg"
+	case OperationKindAtomicRMW8Cmpxchg:
+		ret = "OperationKindAtomicRMW8Cmpxchg"
+	case OperationKindAtomicRMW16Cmpxchg:
+		ret = "OperationKindAtomicRMW16Cmpxchg"
 	default:
 		panic(fmt.Errorf("unknown operation %d", o))
 	}
@@ -705,6 +735,39 @@ const (
 	// OperationKindBuiltinFunctionCheckExitCode is the Kind for NewOperationBuiltinFunctionCheckExitCode.
 	OperationKindBuiltinFunctionCheckExitCode
 
+	// OperationKindAtomicMemoryWait is the kind for NewOperationAtomicMemoryWait.
+	OperationKindAtomicMemoryWait
+	// OperationKindAtomicMemoryNotify is the kind for NewOperationAtomicMemoryNotify.
+	OperationKindAtomicMemoryNotify
+	// OperationKindAtomicFence is the kind for NewOperationAtomicFence.
+	OperationKindAtomicFence
+	// OperationKindAtomicLoad is the kind for NewOperationAtomicLoad.
+	OperationKindAtomicLoad
+	// OperationKindAtomicLoad8 is the kind for NewOperationAtomicLoad8.
+	OperationKindAtomicLoad8
+	// OperationKindAtomicLoad16 is the kind for NewOperationAtomicLoad16.
+	OperationKindAtomicLoad16
+	// OperationKindAtomicStore is the kind for NewOperationAtomicStore.
+	OperationKindAtomicStore
+	// OperationKindAtomicStore8 is the kind for NewOperationAtomicStore8.
+	OperationKindAtomicStore8
+	// OperationKindAtomicStore16 is the kind for NewOperationAtomicStore16.
+	OperationKindAtomicStore16
+
+	// OperationKindAtomicRMW is the kind for NewOperationAtomicRMW.
+	OperationKindAtomicRMW
+	// OperationKindAtomicRMW8 is the kind for NewOperationAtomicRMW8.
+	OperationKindAtomicRMW8
+	// OperationKindAtomicRMW16 is the kind for NewOperationAtomicRMW16.
+	OperationKindAtomicRMW16
+
+	// OperationKindAtomicRMWCmpxchg is the kind for NewOperationAtomicRMWCmpxchg.
+	OperationKindAtomicRMWCmpxchg
+	// OperationKindAtomicRMW8Cmpxchg is the kind for NewOperationAtomicRMW8Cmpxchg.
+	OperationKindAtomicRMW8Cmpxchg
+	// OperationKindAtomicRMW16Cmpxchg is the kind for NewOperationAtomicRMW16Cmpxchg.
+	OperationKindAtomicRMW16Cmpxchg
+
 	// operationKindEnd is always placed at the bottom of this iota definition to be used in the test.
 	operationKindEnd
 )
@@ -1017,6 +1080,23 @@ func (o UnionOperation) String() string {
 			return fmt.Sprintf("%s.%sU", o.Kind, shapeName(o.B1))
 		}
 
+	case OperationKindAtomicMemoryWait,
+		OperationKindAtomicMemoryNotify,
+		OperationKindAtomicFence,
+		OperationKindAtomicLoad,
+		OperationKindAtomicLoad8,
+		OperationKindAtomicLoad16,
+		OperationKindAtomicStore,
+		OperationKindAtomicStore8,
+		OperationKindAtomicStore16,
+		OperationKindAtomicRMW,
+		OperationKindAtomicRMW8,
+		OperationKindAtomicRMW16,
+		OperationKindAtomicRMWCmpxchg,
+		OperationKindAtomicRMW8Cmpxchg,
+		OperationKindAtomicRMW16Cmpxchg:
+		return o.Kind.String()
+
 	default:
 		panic(fmt.Sprintf("TODO: %v", o.Kind))
 	}
@@ -2565,3 +2645,168 @@ func NewOperationV128Narrow(originShape Shape, signed bool) UnionOperation {
 func NewOperationV128ITruncSatFromF(originShape Shape, signed bool) UnionOperation {
 	return UnionOperation{Kind: OperationKindV128ITruncSatFromF, B1: originShape, B3: signed}
 }
+
+// AtomicArithmeticOp is the type for the operation kind of atomic arithmetic operations.
+type AtomicArithmeticOp byte
+
+const (
+	// AtomicArithmeticOpAdd is the kind for an add operation.
+	AtomicArithmeticOpAdd AtomicArithmeticOp = iota
+	// AtomicArithmeticOpSub is the kind for a sub operation.
+	AtomicArithmeticOpSub
+	// AtomicArithmeticOpAnd is the kind for a bitwise and operation.
+	AtomicArithmeticOpAnd
+	// AtomicArithmeticOpOr is the kind for a bitwise or operation.
+	AtomicArithmeticOpOr
+	// AtomicArithmeticOpXor is the kind for a bitwise xor operation.
+	AtomicArithmeticOpXor
+	// AtomicArithmeticOpNop is the kind for a nop operation.
+	AtomicArithmeticOpNop
+)
+
+// NewOperationAtomicMemoryWait is a constructor for UnionOperation with OperationKindAtomicMemoryWait.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicWait32Name wasm.OpcodeAtomicWait64Name
+func NewOperationAtomicMemoryWait(unsignedType UnsignedType, arg MemoryArg) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicMemoryWait, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicMemoryNotify is a constructor for UnionOperation with OperationKindAtomicMemoryNotify.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicNotifyName
+func NewOperationAtomicMemoryNotify(arg MemoryArg) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicMemoryNotify, U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicFence is a constructor for UnionOperation with OperationKindAtomicFence.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicFenceName
+func NewOperationAtomicFence() UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicFence}
+}
+
+// NewOperationAtomicLoad is a constructor for UnionOperation with OperationKindAtomicLoad.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32LoadName wasm.OpcodeAtomicI64LoadName
+func NewOperationAtomicLoad(unsignedType UnsignedType, arg MemoryArg) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicLoad, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicLoad8 is a constructor for UnionOperation with OperationKindAtomicLoad8.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32Load8UName wasm.OpcodeAtomicI64Load8UName
+func NewOperationAtomicLoad8(unsignedType UnsignedType, arg MemoryArg) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicLoad8, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicLoad16 is a constructor for UnionOperation with OperationKindAtomicLoad16.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32Load16UName wasm.OpcodeAtomicI64Load16UName
+func NewOperationAtomicLoad16(unsignedType UnsignedType, arg MemoryArg) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicLoad16, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicStore is a constructor for UnionOperation with OperationKindAtomicStore.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32StoreName wasm.OpcodeAtomicI64StoreName
+func NewOperationAtomicStore(unsignedType UnsignedType, arg MemoryArg) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicStore, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicStore8 is a constructor for UnionOperation with OperationKindAtomicStore8.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32Store8UName wasm.OpcodeAtomicI64Store8UName
+func NewOperationAtomicStore8(unsignedType UnsignedType, arg MemoryArg) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicStore8, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicStore16 is a constructor for UnionOperation with OperationKindAtomicStore16.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32Store16UName wasm.OpcodeAtomicI64Store16UName
+func NewOperationAtomicStore16(unsignedType UnsignedType, arg MemoryArg) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicStore16, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicRMW is a constructor for UnionOperation with OperationKindAtomicRMW.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32RMWAddName wasm.OpcodeAtomicI64RmwAddName
+//	wasm.OpcodeAtomicI32RMWSubName wasm.OpcodeAtomicI64RmwSubName
+//	wasm.OpcodeAtomicI32RMWAndName wasm.OpcodeAtomicI64RmwAndName
+//	wasm.OpcodeAtomicI32RMWOrName wasm.OpcodeAtomicI64RmwOrName
+//	wasm.OpcodeAtomicI32RMWXorName wasm.OpcodeAtomicI64RmwXorName
+func NewOperationAtomicRMW(unsignedType UnsignedType, arg MemoryArg, op AtomicArithmeticOp) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicRMW, B1: byte(unsignedType), B2: byte(op), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicRMW8 is a constructor for UnionOperation with OperationKindAtomicRMW8.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32RMW8AddUName wasm.OpcodeAtomicI64Rmw8AddUName
+//	wasm.OpcodeAtomicI32RMW8SubUName wasm.OpcodeAtomicI64Rmw8SubUName
+//	wasm.OpcodeAtomicI32RMW8AndUName wasm.OpcodeAtomicI64Rmw8AndUName
+//	wasm.OpcodeAtomicI32RMW8OrUName wasm.OpcodeAtomicI64Rmw8OrUName
+//	wasm.OpcodeAtomicI32RMW8XorUName wasm.OpcodeAtomicI64Rmw8XorUName
+func NewOperationAtomicRMW8(unsignedType UnsignedType, arg MemoryArg, op AtomicArithmeticOp) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicRMW8, B1: byte(unsignedType), B2: byte(op), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicRMW16 is a constructor for UnionOperation with OperationKindAtomicRMW16.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32RMW16AddUName wasm.OpcodeAtomicI64Rmw16AddUName
+//	wasm.OpcodeAtomicI32RMW16SubUName wasm.OpcodeAtomicI64Rmw16SubUName
+//	wasm.OpcodeAtomicI32RMW16AndUName wasm.OpcodeAtomicI64Rmw16AndUName
+//	wasm.OpcodeAtomicI32RMW16OrUName wasm.OpcodeAtomicI64Rmw16OrUName
+//	wasm.OpcodeAtomicI32RMW16XorUName wasm.OpcodeAtomicI64Rmw16XorUName
+func NewOperationAtomicRMW16(unsignedType UnsignedType, arg MemoryArg, op AtomicArithmeticOp) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicRMW16, B1: byte(unsignedType), B2: byte(op), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicRMWCmpxchg is a constructor for UnionOperation with OperationKindAtomicRMWCmpxchg.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32RMWCmpxchgName wasm.OpcodeAtomicI64RmwCmpxchgName
+func NewOperationAtomicRMWCmpxchg(unsignedType UnsignedType, arg MemoryArg) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicRMWCmpxchg, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicRMW8Cmpxchg is a constructor for UnionOperation with OperationKindAtomicRMW8Cmpxchg.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32RMW8CmpxchgUName wasm.OpcodeAtomicI64Rmw8CmpxchgUName
+func NewOperationAtomicRMW8Cmpxchg(unsignedType UnsignedType, arg MemoryArg) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicRMW8Cmpxchg, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}
+
+// NewOperationAtomicRMW16Cmpxchg is a constructor for UnionOperation with OperationKindAtomicRMW16Cmpxchg.
+//
+// This corresponds to
+//
+//	wasm.OpcodeAtomicI32RMW16CmpxchgUName wasm.OpcodeAtomicI64Rmw16CmpxchgUName
+func NewOperationAtomicRMW16Cmpxchg(unsignedType UnsignedType, arg MemoryArg) UnionOperation {
+	return UnionOperation{Kind: OperationKindAtomicRMW16Cmpxchg, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)}
+}


Test patch:
diff --git a/internal/wazeroir/compiler_test.go b/internal/wazeroir/compiler_test.go
index b167bbf0..37687c56 100644
--- a/internal/wazeroir/compiler_test.go
+++ b/internal/wazeroir/compiler_test.go
@@ -3071,3 +3071,651 @@ func Test_ensureTermination(t *testing.T) {
 		})
 	}
 }
+
+func TestCompiler_threads(t *testing.T) {
+	tests := []struct {
+		name               string
+		body               []byte
+		noDropBeforeReturn bool
+		expected           UnionOperation
+	}{
+		{
+			name: "i32.atomic.load8_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Load8U, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicLoad8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}),
+		},
+		{
+			name: "i32.atomic.load16_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Load16U, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicLoad16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}),
+		},
+		{
+			name: "i32.atomic.load",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Load, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicLoad(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.load8_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load8U, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicLoad8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.load16_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load16U, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicLoad16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.load32_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load32U, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicLoad(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.load",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load, 0x4, 0x8, // alignment=2^(4-1), offset=8
+			},
+			expected: NewOperationAtomicLoad(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}),
+		},
+		{
+			name: "i32.atomic.store8",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Store8, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			noDropBeforeReturn: true,
+			expected:           NewOperationAtomicStore8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}),
+		},
+		{
+			name: "i32.atomic.store16",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Store16, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			noDropBeforeReturn: true,
+			expected:           NewOperationAtomicStore16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}),
+		},
+		{
+			name: "i32.atomic.store",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Store, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			noDropBeforeReturn: true,
+			expected:           NewOperationAtomicStore(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.store8",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store8, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			noDropBeforeReturn: true,
+			expected:           NewOperationAtomicStore8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.store16",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store16, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			noDropBeforeReturn: true,
+			expected:           NewOperationAtomicStore16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.store32",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store32, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			noDropBeforeReturn: true,
+			expected:           NewOperationAtomicStore(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.store",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store, 0x4, 0x8, // alignment=2^(4-1), offset=8
+			},
+			noDropBeforeReturn: true,
+			expected:           NewOperationAtomicStore(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}),
+		},
+		{
+			name: "i32.atomic.rmw8.add_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8AddU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpAdd),
+		},
+		{
+			name: "i32.atomic.rmw16.add_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16AddU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpAdd),
+		},
+		{
+			name: "i32.atomic.rmw.add",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwAdd, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpAdd),
+		},
+		{
+			name: "i64.atomic.rmw8.add_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8AddU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpAdd),
+		},
+		{
+			name: "i64.atomic.rmw16.add_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16AddU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpAdd),
+		},
+		{
+			name: "i64.atomic.rmw32.add_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32AddU, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpAdd),
+		},
+		{
+			name: "i64.atomic.rmw.add",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwAdd, 0x4, 0x8, // alignment=2^(4-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpAdd),
+		},
+		{
+			name: "i32.atomic.rmw8.sub_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8SubU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpSub),
+		},
+		{
+			name: "i32.atomic.rmw16.sub_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16SubU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpSub),
+		},
+		{
+			name: "i32.atomic.rmw.sub",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwSub, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpSub),
+		},
+		{
+			name: "i64.atomic.rmw8.sub_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8SubU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpSub),
+		},
+		{
+			name: "i64.atomic.rmw16.sub_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16SubU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpSub),
+		},
+		{
+			name: "i64.atomic.rmw32.sub_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32SubU, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpSub),
+		},
+		{
+			name: "i64.atomic.rmw.sub",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwSub, 0x4, 0x8, // alignment=2^(4-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpSub),
+		},
+		{
+			name: "i32.atomic.rmw8.and_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8AndU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpAnd),
+		},
+		{
+			name: "i32.atomic.rmw16.and_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16AndU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpAnd),
+		},
+		{
+			name: "i32.atomic.rmw.and",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwAnd, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpAnd),
+		},
+		{
+			name: "i64.atomic.rmw8.and_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8AndU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpAnd),
+		},
+		{
+			name: "i64.atomic.rmw16.and_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16AndU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpAnd),
+		},
+		{
+			name: "i64.atomic.rmw32.and_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32AndU, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpAnd),
+		},
+		{
+			name: "i64.atomic.rmw.and",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwAnd, 0x4, 0x8, // alignment=2^(4-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpAnd),
+		},
+		{
+			name: "i32.atomic.rmw8.or",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8OrU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpOr),
+		},
+		{
+			name: "i32.atomic.rmw16.or_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16OrU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpOr),
+		},
+		{
+			name: "i32.atomic.rmw.or",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwOr, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpOr),
+		},
+		{
+			name: "i64.atomic.rmw8.or_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8OrU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpOr),
+		},
+		{
+			name: "i64.atomic.rmw16.or_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16OrU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpOr),
+		},
+		{
+			name: "i64.atomic.rmw32.or_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32OrU, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpOr),
+		},
+		{
+			name: "i64.atomic.rmw.or",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwOr, 0x4, 0x8, // alignment=2^(4-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpOr),
+		},
+		{
+			name: "i32.atomic.rmw8.xor_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8XorU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpXor),
+		},
+		{
+			name: "i32.atomic.rmw16.xor_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16XorU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpXor),
+		},
+		{
+			name: "i32.atomic.rmw.xor",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwXor, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpXor),
+		},
+		{
+			name: "i64.atomic.rmw8.xor_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8XorU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpXor),
+		},
+		{
+			name: "i64.atomic.rmw16.xor_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16XorU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpXor),
+		},
+		{
+			name: "i64.atomic.rmw32.xor_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32XorU, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpXor),
+		},
+		{
+			name: "i64.atomic.rmw.xor",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwXor, 0x4, 0x8, // alignment=2^(4-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpXor),
+		},
+		{
+			name: "i32.atomic.rmw8.xchg_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8XchgU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpNop),
+		},
+		{
+			name: "i32.atomic.rmw16.xchg_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16XchgU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpNop),
+		},
+		{
+			name: "i32.atomic.rmw.xchg",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwXchg, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpNop),
+		},
+		{
+			name: "i64.atomic.rmw8.xchg_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8XchgU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpNop),
+		},
+		{
+			name: "i64.atomic.rmw16.xchg_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16XchgU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpNop),
+		},
+		{
+			name: "i64.atomic.rmw32.xchg_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32XchgU, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpNop),
+		},
+		{
+			name: "i64.atomic.rmw.xchg",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwXchg, 0x4, 0x8, // alignment=2^(4-1), offset=8
+			},
+			expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpNop),
+		},
+		{
+			name: "i32.atomic.rmw8.cmpxchg_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeI32Const, 0x2,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8Cmpxchg(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}),
+		},
+		{
+			name: "i32.atomic.rmw16.cmpxchg_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeI32Const, 0x2,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16CmpxchgU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16Cmpxchg(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}),
+		},
+		{
+			name: "i32.atomic.rmw.cmpxchg",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeI32Const, 0x2,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwCmpxchg, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMWCmpxchg(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.rmw8.xchg_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeI64Const, 0x2,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8
+			},
+			expected: NewOperationAtomicRMW8Cmpxchg(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.rmw16.cmpxchg_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeI64Const, 0x2,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16CmpxchgU, 0x2, 0x8, // alignment=2^(2-1), offset=8
+			},
+			expected: NewOperationAtomicRMW16Cmpxchg(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.rmw32.cmpxchg_u",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32CmpxchgU, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicRMWCmpxchg(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}),
+		},
+		{
+			name: "i64.atomic.rmw.cmpxchg",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeI64Const, 0x2,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwCmpxchg, 0x4, 0x8, // alignment=2^(4-1), offset=8
+			},
+			expected: NewOperationAtomicRMWCmpxchg(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}),
+		},
+		{
+			name: "memory.atomic.wait32",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeI64Const, 0x2,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicMemoryWait32, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicMemoryWait(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}),
+		},
+		{
+			name: "memory.atomic.wait64",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI64Const, 0x1,
+				wasm.OpcodeI64Const, 0x2,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicMemoryWait64, 0x4, 0x8, // alignment=2^(4-1), offset=8
+			},
+			expected: NewOperationAtomicMemoryWait(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}),
+		},
+		{
+			name: "memory.atomic.notify",
+			body: []byte{
+				wasm.OpcodeI32Const, 0x0,
+				wasm.OpcodeI32Const, 0x1,
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicMemoryNotify, 0x3, 0x8, // alignment=2^(3-1), offset=8
+			},
+			expected: NewOperationAtomicMemoryNotify(MemoryArg{Alignment: 0x3, Offset: 0x8}),
+		},
+		{
+			name: "memory.atomic.fence",
+			body: []byte{
+				wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicFence, 0x0, // consistency=0
+			},
+			noDropBeforeReturn: true,
+			expected:           NewOperationAtomicFence(),
+		},
+	}
+
+	for _, tc := range tests {
+		tt := tc
+		t.Run(tt.name, func(t *testing.T) {
+			body := append([]byte{}, tt.body...)
+			if !tt.noDropBeforeReturn {
+				body = append(body, wasm.OpcodeDrop)
+			}
+			body = append(body, wasm.OpcodeEnd)
+			module := &wasm.Module{
+				TypeSection:     []wasm.FunctionType{v_v},
+				FunctionSection: []wasm.Index{0},
+				MemorySection:   &wasm.Memory{},
+				CodeSection:     []wasm.Code{{Body: body}},
+			}
+			c, err := NewCompiler(api.CoreFeaturesV2, 0, module, false)
+			require.NoError(t, err)
+
+			res, err := c.Next()
+			require.NoError(t, err)
+			if tt.noDropBeforeReturn {
+				require.Equal(t, tc.expected, res.Operations[len(res.Operations)-2])
+			} else {
+				require.Equal(t, tc.expected, res.Operations[len(res.Operations)-3])
+			}
+		})
+	}
+}