Repository: tetratelabs/wazero
Pull request #116 (2022-01-06 04:36:48)

jit: support for div,rem

mathetake: whew this works for both mac and linux 😄 

Patch:
diff --git a/wasm/jit/compiler.go b/wasm/jit/compiler.go
index d13cc9b5..a974943f 100644
--- a/wasm/jit/compiler.go
+++ b/wasm/jit/compiler.go
@@ -29,6 +29,8 @@ type compiler interface {
 	compileClz(o *wazeroir.OperationClz) error
 	compileCtz(o *wazeroir.OperationCtz) error
 	compilePopcnt(o *wazeroir.OperationPopcnt) error
+	compileDiv(o *wazeroir.OperationDiv) error
+	compileRem(o *wazeroir.OperationRem) error
 	compileEq(o *wazeroir.OperationEq) error
 	compileNe(o *wazeroir.OperationNe) error
 	compileEqz(o *wazeroir.OperationEqz) error
diff --git a/wasm/jit/engine.go b/wasm/jit/engine.go
index 3d870a87..f803e35f 100644
--- a/wasm/jit/engine.go
+++ b/wasm/jit/engine.go
@@ -545,9 +545,9 @@ func (e *engine) compileWasmFunction(f *wasm.FunctionInstance) (*compiledWasmFun
 		case *wazeroir.OperationPopcnt:
 			err = compiler.compilePopcnt(o)
 		case *wazeroir.OperationDiv:
-			err = fmt.Errorf("unsupported operation")
+			err = compiler.compileDiv(o)
 		case *wazeroir.OperationRem:
-			err = fmt.Errorf("unsupported operation")
+			err = compiler.compileRem(o)
 		case *wazeroir.OperationAnd:
 			err = fmt.Errorf("unsupported operation")
 		case *wazeroir.OperationOr:
diff --git a/wasm/jit/jit_amd64.go b/wasm/jit/jit_amd64.go
index 3b9e7f15..7f87755c 100644
--- a/wasm/jit/jit_amd64.go
+++ b/wasm/jit/jit_amd64.go
@@ -1122,6 +1122,224 @@ func (c *amd64Compiler) compilePopcnt(o *wazeroir.OperationPopcnt) error {
 	return nil
 }
 
+// compileDiv emits the instructions to perform division on the top two values on the stack.
+func (c *amd64Compiler) compileDiv(o *wazeroir.OperationDiv) (err error) {
+	switch o.Type {
+	case wazeroir.SignedTypeUint32:
+		err = c.compileDivForInts(true, false)
+	case wazeroir.SignedTypeUint64:
+		err = c.compileDivForInts(false, false)
+	case wazeroir.SignedTypeInt32:
+		err = c.compileDivForInts(true, true)
+	case wazeroir.SignedTypeInt64:
+		err = c.compileDivForInts(false, true)
+	case wazeroir.SignedTypeFloat32:
+		err = c.compileDivForFloats(true)
+	case wazeroir.SignedTypeFloat64:
+		err = c.compileDivForFloats(false)
+	}
+	return
+}
+
+// compileDivForInts emits the instructions to perform division on the top
+// two values of integer type on the stack and puts the quotient of the result
+// onto the stack. For example, stack [..., 10, 3] results in [..., 3] where
+// the remainder is discarded. See compileRem for how to acquire remainder, not quotient.
+func (c *amd64Compiler) compileDivForInts(is32Bit bool, signed bool) error {
+	if err := c.performDivisionOnInts(is32Bit, signed); err != nil {
+		return err
+	}
+	// Now we have the quotient of the division result in the AX register,
+	// so we record it.
+	result := c.locationStack.pushValueOnRegister(x86.REG_AX)
+	result.setRegisterType(generalPurposeRegisterTypeInt)
+
+	// Make sure that the DX register (holding remainder of the division) is marked unused.
+	c.locationStack.markRegisterUnused(x86.REG_DX)
+	return nil
+}
+
+// compileRem emits the instructions to perform division on the top
+// two values of integer type on the stack and puts the remainder of the result
+// onto the stack. For example, stack [..., 10, 3] results in [..., 1] where
+// the quotient is discarded. See compileDivForInts for how to acquire quotient, not remainder.
+func (c *amd64Compiler) compileRem(o *wazeroir.OperationRem) (err error) {
+	switch o.Type {
+	case wazeroir.SignedInt32:
+		err = c.performDivisionOnInts(true, true)
+	case wazeroir.SignedInt64:
+		err = c.performDivisionOnInts(false, true)
+	case wazeroir.SignedUint32:
+		err = c.performDivisionOnInts(true, false)
+	case wazeroir.SignedUint64:
+		err = c.performDivisionOnInts(false, false)
+	}
+	if err != nil {
+		return err
+	}
+
+	// Now we have the remainder of the division result in the DX register,
+	// so we record it.
+	result := c.locationStack.pushValueOnRegister(x86.REG_DX)
+	result.setRegisterType(generalPurposeRegisterTypeInt)
+
+	// Make sure that the AX register (holding quotient of the division) is marked unused.
+	c.locationStack.markRegisterUnused(x86.REG_AX)
+	return
+}
+
+// performDivisionOnInts emits the instructions to do divisions on top two integers on the stack
+// via DIV (unsigned div) and IDIV (signed div) instructions.
+// See the following explanation of these instructions' semantics from https://www.lri.fr/~filliatr/ens/compil/x86-64.pdf
+//
+// >> Division requires special arrangements: idiv (signed) and div (unsigned) operate on a 2n-byte dividend and
+// >> an n-byte divisor to produce an n-byte quotient and n-byte remainder. The dividend always lives in a fixed pair of
+// >> registers (%edx and %eax for the 32-bit case; %rdx and %rax for the 64-bit case); the divisor is specified as the
+// >> source operand in the instruction. The quotient goes in %eax (resp. %rax); the remainder in %edx (resp. %rdx). For
+// >> signed division, the cltd (resp. ctqo) instruction is used to prepare %edx (resp. %rdx) with the sign extension of
+// >> %eax (resp. %rax). For example, if a,b, c are memory locations holding quad words, then we could set c = a/b
+// >> using the sequence: movq a(%rip), %rax; ctqo; idivq b(%rip); movq %rax, c(%rip).
+//
+// tl;dr is that the division result is placed in AX and DX registers after instructions emitted by this function
+// where AX holds the quotient while DX the remainder of the division result.
+func (c *amd64Compiler) performDivisionOnInts(is32Bit bool, signed bool) error {
+
+	const (
+		quotientRegister  = x86.REG_AX
+		remainderRegister = x86.REG_DX
+	)
+
+	x2 := c.locationStack.pop()
+	if x2.onConditionalRegister() {
+		if err := c.moveConditionalToFreeGeneralPurposeRegister(x2); err != nil {
+			return err
+		}
+	}
+	// If x2 is placed in the quotient (AX) register, we just release it to the memory stack
+	// as AX must be set to the x1's value below.
+	if x2.register == quotientRegister {
+		c.releaseRegisterToStack(x2)
+	}
+
+	x1 := c.locationStack.pop()
+	// Ensure that x1 is placed on the quotient (AX) register.
+	if x1.register != quotientRegister {
+		c.onValueReleaseRegisterToStack(quotientRegister)
+		if x2.onConditionalRegister() {
+			c.moveConditionalToGeneralPurposeRegister(x1, quotientRegister)
+		} else if x1.onStack() {
+			x1.setRegister(quotientRegister)
+			c.moveStackToRegister(x1)
+			c.locationStack.markRegisterUsed(quotientRegister)
+		} else {
+			moveX1ToQuotientRegister := c.newProg()
+			if is32Bit {
+				moveX1ToQuotientRegister.As = x86.AMOVL
+			} else {
+				moveX1ToQuotientRegister.As = x86.AMOVQ
+			}
+			moveX1ToQuotientRegister.To.Reg = quotientRegister
+			moveX1ToQuotientRegister.To.Type = obj.TYPE_REG
+			moveX1ToQuotientRegister.From.Reg = x1.register
+			moveX1ToQuotientRegister.From.Type = obj.TYPE_REG
+			c.addInstruction(moveX1ToQuotientRegister)
+			// We no longer uses the prev register of x1.
+			c.locationStack.releaseRegister(x1)
+			x1.setRegister(quotientRegister)
+			c.locationStack.markRegisterUsed(quotientRegister)
+		}
+	}
+
+	// We have to save the existing value on DX as the division instruction
+	// place the remainder of the result there.
+	c.onValueReleaseRegisterToStack(remainderRegister)
+
+	// Now ready to emit the div instruction.
+	div := c.newProg()
+	div.To.Type = obj.TYPE_NONE
+	// Since the div instructions takes 2n byte dividend placed in DX:AX registers...
+	// * signed case - we need to sign-extend the dividend into DX register via CDQ (32 bit) or CQO (64 bit).
+	// * unsigned case - we need to zero DX register via "XOR DX DX"
+	if is32Bit && signed {
+		div.As = x86.AIDIVL // Signed 32-bit
+		// Emit sign-extension to have 64 bit dividend over DX and AX registers.
+		extIntoDX := c.newProg()
+		extIntoDX.As = x86.ACDQ
+		c.addInstruction(extIntoDX)
+	} else if is32Bit && !signed {
+		div.As = x86.ADIVL // Unsigned 32-bit
+		// Zeros DX register to have 64 bit dividend over DX and AX registers.
+		zerosDX := c.newProg()
+		zerosDX.As = x86.AXORQ
+		zerosDX.From.Type = obj.TYPE_REG
+		zerosDX.From.Reg = x86.REG_DX
+		zerosDX.To.Type = obj.TYPE_REG
+		zerosDX.To.Reg = x86.REG_DX
+		c.addInstruction(zerosDX)
+	} else if !is32Bit && signed {
+		div.As = x86.AIDIVQ // Signed 64-bit
+		// Emits sign-extension to have 128 bit dividend over DX and AX registers.
+		extIntoDX := c.newProg()
+		extIntoDX.As = x86.ACQO
+		c.addInstruction(extIntoDX)
+	} else if !is32Bit && !signed {
+		div.As = x86.ADIVQ // Unsigned 64-bit
+		// Zeros DX register to have 128 bit dividend over DX and AX registers.
+		zerosDX := c.newProg()
+		zerosDX.As = x86.AXORQ
+		zerosDX.From.Type = obj.TYPE_REG
+		zerosDX.From.Reg = x86.REG_DX
+		zerosDX.To.Type = obj.TYPE_REG
+		zerosDX.To.Reg = x86.REG_DX
+		c.addInstruction(zerosDX)
+	}
+
+	if x2.onRegister() {
+		div.From.Reg = x2.register
+		div.From.Type = obj.TYPE_REG
+		c.locationStack.markRegisterUnused(x2.register)
+	} else {
+		// On stack case.
+		div.From.Type = obj.TYPE_MEM
+		div.From.Reg = reservedRegisterForStackBasePointer
+		div.From.Offset = int64(x2.stackPointer) * 8
+	}
+	c.addInstruction(div)
+	return nil
+}
+
+// compileDivForFloats emits the instructions to perform division
+// on the top two values of float type on the stack, placing the result back onto the stack.
+// For example, stack [..., 1.0, 4.0] results in [..., 0.25].
+func (c *amd64Compiler) compileDivForFloats(is32Bit bool) error {
+	x2 := c.locationStack.pop()
+	if err := c.ensureOnGeneralPurposeRegister(x2); err != nil {
+		return err
+	}
+
+	x1 := c.locationStack.peek() // Note this is peek!
+	if err := c.ensureOnGeneralPurposeRegister(x1); err != nil {
+		return err
+	}
+
+	div := c.newProg()
+	div.From.Type = obj.TYPE_REG
+	div.From.Reg = x2.register
+	div.To.Type = obj.TYPE_REG
+	div.To.Reg = x1.register
+	if is32Bit {
+		div.As = x86.ADIVSS
+	} else {
+		div.As = x86.ADIVSD
+	}
+	c.addInstruction(div)
+
+	// We consumed x2 register after DIV operation here,
+	// so we release it.
+	c.locationStack.releaseRegister(x2)
+	return nil
+}
+
 func (c *amd64Compiler) compileEq(o *wazeroir.OperationEq) error {
 	return c.emitEqOrNe(o.Type, true)
 }


Test patch:
diff --git a/wasm/jit/jit_amd64_test.go b/wasm/jit/jit_amd64_test.go
index 6cf6d5b9..1fa90501 100644
--- a/wasm/jit/jit_amd64_test.go
+++ b/wasm/jit/jit_amd64_test.go
@@ -2948,6 +2948,759 @@ func TestAmd64Compiler_compilPopcnt(t *testing.T) {
 	})
 }
 
+// The division by zero error must be caught by Go's runtime via x86's exception caught by kernel.
+func getDivisionByZeroErrorRecoverFunc(t *testing.T) func() {
+	return func() {
+		if e := recover(); e != nil {
+			err, ok := e.(error)
+			require.True(t, ok)
+			require.Equal(t, "runtime error: integer divide by zero", err.Error())
+		}
+	}
+}
+
+func TestAmd64Compiler_compileDiv(t *testing.T) {
+	t.Run("int32", func(t *testing.T) {
+		for _, signed := range []struct {
+			name   string
+			signed bool
+		}{{name: "signed", signed: true}, {name: "unsigned", signed: false}} {
+			signed := signed
+			t.Run(signed.name, func(t *testing.T) {
+				for _, tc := range []struct {
+					name string
+					// Interpret -1 as stack.
+					x1Reg, x2Reg int16
+				}{
+					{
+						name:  "x1:ax,x2:random_reg",
+						x1Reg: x86.REG_AX,
+						x2Reg: x86.REG_R10,
+					},
+					{
+						name:  "x1:ax,x2:stack",
+						x1Reg: x86.REG_AX,
+						x2Reg: -1,
+					},
+					{
+						name:  "x1:random_reg,x2:ax",
+						x1Reg: x86.REG_R10,
+						x2Reg: x86.REG_AX,
+					},
+					{
+						name:  "x1:staack,x2:ax",
+						x1Reg: -1,
+						x2Reg: x86.REG_AX,
+					},
+					{
+						name:  "x1:random_reg,x2:random_reg",
+						x1Reg: x86.REG_R10,
+						x2Reg: x86.REG_R9,
+					},
+					{
+						name:  "x1:stack,x2:random_reg",
+						x1Reg: -1,
+						x2Reg: x86.REG_R9,
+					},
+					{
+						name:  "x1:random_reg,x2:stack",
+						x1Reg: x86.REG_R9,
+						x2Reg: -1,
+					},
+					{
+						name:  "x1:stack,x2:stack",
+						x1Reg: -1,
+						x2Reg: -1,
+					},
+				} {
+					tc := tc
+					t.Run(tc.name, func(t *testing.T) {
+						const dxValue uint64 = 111111
+						for i, vs := range []struct {
+							x1Value, x2Value uint32
+						}{
+							{x1Value: 2, x2Value: 1},
+							{x1Value: 1, x2Value: 2},
+							{x1Value: 0, x2Value: 2},
+							{x1Value: 1, x2Value: 0},
+							{x1Value: 0, x2Value: 0},
+							// Following cases produce different resulting bit patterns for signed and unsigned.
+							{x1Value: 0xffffffff /* -1 in signed 32bit */, x2Value: 1},
+							{x1Value: 0xffffffff /* -1 in signed 32bit */, x2Value: 0xfffffffe /* -2 in signed 32bit */},
+						} {
+							vs := vs
+							t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+
+								eng := newEngine()
+								compiler := requireNewCompiler(t)
+								compiler.initializeReservedRegisters()
+
+								// Pretend there was an existing value on the DX register. We expect compileDivForInts to save this to the stack.
+								// Here, we put it just before two operands as ["any value used by DX", x1, x2]
+								// but in reality, it can exist in any position of stack.
+								compiler.movIntConstToRegister(int64(dxValue), x86.REG_DX)
+								prevOnDX := compiler.locationStack.pushValueOnRegister(x86.REG_DX)
+
+								// Setup values.
+								if tc.x1Reg != -1 {
+									compiler.movIntConstToRegister(int64(vs.x1Value), tc.x1Reg)
+									compiler.locationStack.pushValueOnRegister(tc.x1Reg)
+								} else {
+									loc := compiler.locationStack.pushValueOnStack()
+									eng.stack[loc.stackPointer] = uint64(vs.x1Value)
+								}
+								if tc.x2Reg != -1 {
+									compiler.movIntConstToRegister(int64(vs.x2Value), tc.x2Reg)
+									compiler.locationStack.pushValueOnRegister(tc.x2Reg)
+								} else {
+									loc := compiler.locationStack.pushValueOnStack()
+									eng.stack[loc.stackPointer] = uint64(vs.x2Value)
+								}
+
+								var err error
+								if signed.signed {
+									err = compiler.compileDiv(&wazeroir.OperationDiv{Type: wazeroir.SignedTypeInt32})
+								} else {
+									err = compiler.compileDiv(&wazeroir.OperationDiv{Type: wazeroir.SignedTypeUint32})
+								}
+								require.NoError(t, err)
+
+								require.Equal(t, int16(x86.REG_AX), compiler.locationStack.peek().register)
+								require.Equal(t, generalPurposeRegisterTypeInt, compiler.locationStack.peek().regType)
+								require.Equal(t, uint64(2), compiler.locationStack.sp)
+								require.Len(t, compiler.locationStack.usedRegisters, 1)
+								// At this point, the previous value on the DX register is saved to the stack.
+								require.True(t, prevOnDX.onStack())
+
+								// We add the value previously on the DX with the multiplication result
+								// in order to ensure that not saving existing DX value would cause
+								// the failure in a subsequent instruction.
+								err = compiler.compileAdd(&wazeroir.OperationAdd{Type: wazeroir.UnsignedTypeI32})
+								require.NoError(t, err)
+
+								// To verify the behavior, we push the value
+								// to the stack.
+								compiler.releaseAllRegistersToStack()
+								compiler.returnFunction()
+
+								// Generate the code under test.
+								code, _, err := compiler.generate()
+								require.NoError(t, err)
+								// Run code.
+								defer getDivisionByZeroErrorRecoverFunc(t)()
+								jitcall(
+									uintptr(unsafe.Pointer(&code[0])),
+									uintptr(unsafe.Pointer(eng)),
+									0,
+								)
+
+								// Verify the stack is in the form of ["any value previously used by DX" + x1 / x2]
+								require.Equal(t, uint64(1), eng.stackPointer)
+								if signed.signed {
+									require.Equal(t, int32(vs.x1Value)/int32(vs.x2Value)+int32(dxValue), int32(eng.stack[eng.stackPointer-1]))
+								} else {
+									require.Equal(t, vs.x1Value/vs.x2Value+uint32(dxValue), uint32(eng.stack[eng.stackPointer-1]))
+								}
+							})
+						}
+					})
+				}
+			})
+		}
+	})
+	t.Run("int64", func(t *testing.T) {
+		for _, signed := range []struct {
+			name   string
+			signed bool
+		}{
+			{name: "signed", signed: true},
+			{name: "unsigned", signed: false},
+		} {
+			signed := signed
+			t.Run(signed.name, func(t *testing.T) {
+				for _, tc := range []struct {
+					name string
+					// Interpret -1 as stack.
+					x1Reg, x2Reg int16
+				}{
+					{
+						name:  "x1:ax,x2:random_reg",
+						x1Reg: x86.REG_AX,
+						x2Reg: x86.REG_R10,
+					},
+					{
+						name:  "x1:ax,x2:stack",
+						x1Reg: x86.REG_AX,
+						x2Reg: -1,
+					},
+					{
+						name:  "x1:random_reg,x2:ax",
+						x1Reg: x86.REG_R10,
+						x2Reg: x86.REG_AX,
+					},
+					{
+						name:  "x1:staack,x2:ax",
+						x1Reg: -1,
+						x2Reg: x86.REG_AX,
+					},
+					{
+						name:  "x1:random_reg,x2:random_reg",
+						x1Reg: x86.REG_R10,
+						x2Reg: x86.REG_R9,
+					},
+					{
+						name:  "x1:stack,x2:random_reg",
+						x1Reg: -1,
+						x2Reg: x86.REG_R9,
+					},
+					{
+						name:  "x1:random_reg,x2:stack",
+						x1Reg: x86.REG_R9,
+						x2Reg: -1,
+					},
+					{
+						name:  "x1:stack,x2:stack",
+						x1Reg: -1,
+						x2Reg: -1,
+					},
+				} {
+					tc := tc
+					t.Run(tc.name, func(t *testing.T) {
+						const dxValue uint64 = 111111
+						for i, vs := range []struct {
+							x1Value, x2Value uint64
+						}{
+							{x1Value: 2, x2Value: 1},
+							{x1Value: 1, x2Value: 2},
+							{x1Value: 0, x2Value: 1},
+							{x1Value: 1, x2Value: 0},
+							{x1Value: 0, x2Value: 0},
+							// Following cases produce different resulting bit patterns for signed and unsigned.
+							{x1Value: 0xffffffffffffffff /* -1 in signed 64bit */, x2Value: 1},
+							{x1Value: 0xffffffffffffffff /* -1 in signed 64bit */, x2Value: 0xfffffffffffffffe /* -2 in signed 64bit */},
+						} {
+							vs := vs
+							t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+
+								eng := newEngine()
+								compiler := requireNewCompiler(t)
+								compiler.initializeReservedRegisters()
+
+								// Pretend there was an existing value on the DX register. We expect compileDivForInts to save this to the stack.
+								// Here, we put it just before two operands as ["any value used by DX", x1, x2]
+								// but in reality, it can exist in any position of stack.
+								compiler.movIntConstToRegister(int64(dxValue), x86.REG_DX)
+								prevOnDX := compiler.locationStack.pushValueOnRegister(x86.REG_DX)
+
+								// Setup values.
+								if tc.x1Reg != -1 {
+									compiler.movIntConstToRegister(int64(vs.x1Value), tc.x1Reg)
+									compiler.locationStack.pushValueOnRegister(tc.x1Reg)
+								} else {
+									loc := compiler.locationStack.pushValueOnStack()
+									eng.stack[loc.stackPointer] = uint64(vs.x1Value)
+								}
+								if tc.x2Reg != -1 {
+									compiler.movIntConstToRegister(int64(vs.x2Value), tc.x2Reg)
+									compiler.locationStack.pushValueOnRegister(tc.x2Reg)
+								} else {
+									loc := compiler.locationStack.pushValueOnStack()
+									eng.stack[loc.stackPointer] = uint64(vs.x2Value)
+								}
+
+								var err error
+								if signed.signed {
+									err = compiler.compileDiv(&wazeroir.OperationDiv{Type: wazeroir.SignedTypeInt64})
+								} else {
+									err = compiler.compileDiv(&wazeroir.OperationDiv{Type: wazeroir.SignedTypeUint64})
+								}
+								require.NoError(t, err)
+
+								require.Equal(t, int16(x86.REG_AX), compiler.locationStack.peek().register)
+								require.Equal(t, generalPurposeRegisterTypeInt, compiler.locationStack.peek().regType)
+								require.Equal(t, uint64(2), compiler.locationStack.sp)
+								require.Len(t, compiler.locationStack.usedRegisters, 1)
+								// At this point, the previous value on the DX register is saved to the stack.
+								require.True(t, prevOnDX.onStack())
+
+								// We add the value previously on the DX with the quotiont of the division result
+								// in order to ensure that not saving existing DX value would cause
+								// the failure in a subsequent instruction.
+								err = compiler.compileAdd(&wazeroir.OperationAdd{Type: wazeroir.UnsignedTypeI64})
+								require.NoError(t, err)
+
+								// To verify the behavior, we push the value
+								// to the stack.
+								compiler.releaseAllRegistersToStack()
+								compiler.returnFunction()
+
+								// Generate the code under test.
+								code, _, err := compiler.generate()
+								require.NoError(t, err)
+								// Run code.
+								defer getDivisionByZeroErrorRecoverFunc(t)()
+								jitcall(
+									uintptr(unsafe.Pointer(&code[0])),
+									uintptr(unsafe.Pointer(eng)),
+									0,
+								)
+
+								// Verify the stack is in the form of ["any value previously used by DX" + x1 / x2]
+								require.Equal(t, uint64(1), eng.stackPointer)
+								if signed.signed {
+									require.Equal(t, int64(vs.x1Value)/int64(vs.x2Value)+int64(dxValue), int64(eng.stack[eng.stackPointer-1]))
+								} else {
+									require.Equal(t, vs.x1Value/vs.x2Value+dxValue, eng.stack[eng.stackPointer-1])
+								}
+							})
+						}
+					})
+				}
+			})
+		}
+	})
+	t.Run("float32", func(t *testing.T) {
+		for i, tc := range []struct {
+			x1, x2 float32
+		}{
+			{x1: 100, x2: 0},
+			{x1: 0, x2: 100},
+			{x1: 100, x2: -1.1},
+			{x1: -1, x2: 100},
+			{x1: 100, x2: 200},
+			{x1: 100.01234124, x2: 100.01234124},
+			{x1: 100.01234124, x2: -100.01234124},
+			{x1: 200.12315, x2: 100},
+			{x1: float32(math.Inf(1)), x2: 100},
+			{x1: float32(math.Inf(-1)), x2: -100},
+			{x1: 100, x2: float32(math.Inf(1))},
+			{x1: -100, x2: float32(math.Inf(-1))},
+			{x1: float32(math.Inf(1)), x2: 0},
+			{x1: float32(math.Inf(-1)), x2: 0},
+			{x1: 0, x2: float32(math.Inf(1))},
+			{x1: 0, x2: float32(math.Inf(-1))},
+		} {
+			tc := tc
+			t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+				compiler := requireNewCompiler(t)
+				compiler.initializeReservedRegisters()
+				err := compiler.compileConstF32(&wazeroir.OperationConstF32{Value: tc.x1})
+				require.NoError(t, err)
+				x1 := compiler.locationStack.peek()
+				err = compiler.compileConstF32(&wazeroir.OperationConstF32{Value: tc.x2})
+				require.NoError(t, err)
+				x2 := compiler.locationStack.peek()
+
+				err = compiler.compileDiv(&wazeroir.OperationDiv{Type: wazeroir.SignedTypeFloat32})
+				require.NoError(t, err)
+				require.Contains(t, compiler.locationStack.usedRegisters, x1.register)
+				require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+
+				// To verify the behavior, we push the value
+				// to the stack.
+				compiler.releaseRegisterToStack(x1)
+				compiler.returnFunction()
+
+				// Generate the code under test.
+				code, _, err := compiler.generate()
+				require.NoError(t, err)
+				// Run code.
+				eng := newEngine()
+				mem := newMemoryInst()
+				jitcall(
+					uintptr(unsafe.Pointer(&code[0])),
+					uintptr(unsafe.Pointer(eng)),
+					uintptr(unsafe.Pointer(&mem.Buffer[0])),
+				)
+
+				// Check the result.
+				require.Equal(t, uint64(1), eng.stackPointer)
+				exp := tc.x1 / tc.x2
+				actual := math.Float32frombits(uint32(eng.stack[eng.stackPointer-1]))
+				if math.IsNaN(float64(exp)) {
+					require.True(t, math.IsNaN(float64(actual)))
+				} else {
+					require.Equal(t, tc.x1/tc.x2, actual)
+				}
+			})
+		}
+	})
+	t.Run("float64", func(t *testing.T) {
+		for i, tc := range []struct {
+			x1, x2 float64
+		}{
+			{x1: 100, x2: -1.1},
+			{x1: 100, x2: 0},
+			{x1: 0, x2: 0},
+			{x1: -1, x2: 100},
+			{x1: 100, x2: 200},
+			{x1: 100.01234124, x2: 100.01234124},
+			{x1: 100.01234124, x2: -100.01234124},
+			{x1: 200.12315, x2: 100},
+			{x1: 6.8719476736e+10 /* = 1 << 36 */, x2: 100},
+			{x1: 6.8719476736e+10 /* = 1 << 36 */, x2: 1.37438953472e+11 /* = 1 << 37*/},
+			{x1: math.Inf(1), x2: 100},
+			{x1: math.Inf(1), x2: -100},
+			{x1: 100, x2: math.Inf(1)},
+			{x1: -100, x2: math.Inf(1)},
+			{x1: math.Inf(-1), x2: 100},
+			{x1: math.Inf(-1), x2: -100},
+			{x1: 100, x2: math.Inf(-1)},
+			{x1: -100, x2: math.Inf(-1)},
+			{x1: math.Inf(1), x2: 0},
+			{x1: math.Inf(-1), x2: 0},
+			{x1: 0, x2: math.Inf(1)},
+			{x1: 0, x2: math.Inf(-1)},
+		} {
+			tc := tc
+			t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+				compiler := requireNewCompiler(t)
+				compiler.initializeReservedRegisters()
+				err := compiler.compileConstF64(&wazeroir.OperationConstF64{Value: tc.x1})
+				require.NoError(t, err)
+				x1 := compiler.locationStack.peek()
+				err = compiler.compileConstF64(&wazeroir.OperationConstF64{Value: tc.x2})
+				require.NoError(t, err)
+				x2 := compiler.locationStack.peek()
+
+				err = compiler.compileDiv(&wazeroir.OperationDiv{Type: wazeroir.SignedTypeFloat64})
+				require.NoError(t, err)
+				require.Contains(t, compiler.locationStack.usedRegisters, x1.register)
+				require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+
+				// To verify the behavior, we push the value
+				// to the stack.
+				compiler.releaseRegisterToStack(x1)
+				compiler.returnFunction()
+
+				// Generate the code under test.
+				code, _, err := compiler.generate()
+				require.NoError(t, err)
+				// Run code.
+				eng := newEngine()
+				mem := newMemoryInst()
+				jitcall(
+					uintptr(unsafe.Pointer(&code[0])),
+					uintptr(unsafe.Pointer(eng)),
+					uintptr(unsafe.Pointer(&mem.Buffer[0])),
+				)
+
+				// Check the result.
+				require.Equal(t, uint64(1), eng.stackPointer)
+				exp := tc.x1 / tc.x2
+				actual := math.Float64frombits(eng.stack[eng.stackPointer-1])
+				if math.IsNaN(exp) {
+					require.True(t, math.IsNaN(actual))
+				} else {
+					require.Equal(t, tc.x1/tc.x2, actual)
+				}
+			})
+		}
+	})
+}
+
+func TestAmd64Compiler_compileRem(t *testing.T) {
+	t.Run("int32", func(t *testing.T) {
+		for _, signed := range []struct {
+			name   string
+			signed bool
+		}{{name: "signed", signed: true}, {name: "unsigned", signed: false}} {
+			signed := signed
+			t.Run(signed.name, func(t *testing.T) {
+				for _, tc := range []struct {
+					name string
+					// Interpret -1 as stack.
+					x1Reg, x2Reg int16
+				}{
+					{
+						name:  "x1:ax,x2:random_reg",
+						x1Reg: x86.REG_AX,
+						x2Reg: x86.REG_R10,
+					},
+					{
+						name:  "x1:ax,x2:stack",
+						x1Reg: x86.REG_AX,
+						x2Reg: -1,
+					},
+					{
+						name:  "x1:random_reg,x2:ax",
+						x1Reg: x86.REG_R10,
+						x2Reg: x86.REG_AX,
+					},
+					{
+						name:  "x1:staack,x2:ax",
+						x1Reg: -1,
+						x2Reg: x86.REG_AX,
+					},
+					{
+						name:  "x1:random_reg,x2:random_reg",
+						x1Reg: x86.REG_R10,
+						x2Reg: x86.REG_R9,
+					},
+					{
+						name:  "x1:stack,x2:random_reg",
+						x1Reg: -1,
+						x2Reg: x86.REG_R9,
+					},
+					{
+						name:  "x1:random_reg,x2:stack",
+						x1Reg: x86.REG_R9,
+						x2Reg: -1,
+					},
+					{
+						name:  "x1:stack,x2:stack",
+						x1Reg: -1,
+						x2Reg: -1,
+					},
+				} {
+					tc := tc
+					t.Run(tc.name, func(t *testing.T) {
+						const dxValue uint64 = 111111
+						for i, vs := range []struct {
+							x1Value, x2Value uint32
+						}{
+							{x1Value: 2, x2Value: 1},
+							{x1Value: 1, x2Value: 2},
+							{x1Value: 0, x2Value: 2},
+							{x1Value: 1, x2Value: 0},
+							{x1Value: 0, x2Value: 0},
+							// Following cases produce different resulting bit patterns for signed and unsigned.
+							{x1Value: 0xffffffff /* -1 in signed 32bit */, x2Value: 1},
+							{x1Value: 0xffffffff /* -1 in signed 32bit */, x2Value: 0xfffffffe /* -2 in signed 32bit */},
+						} {
+							vs := vs
+							t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+
+								eng := newEngine()
+								compiler := requireNewCompiler(t)
+								compiler.initializeReservedRegisters()
+
+								// Pretend there was an existing value on the DX register. We expect compileDivForInts to save this to the stack.
+								// Here, we put it just before two operands as ["any value used by DX", x1, x2]
+								// but in reality, it can exist in any position of stack.
+								compiler.movIntConstToRegister(int64(dxValue), x86.REG_DX)
+								prevOnDX := compiler.locationStack.pushValueOnRegister(x86.REG_DX)
+
+								// Setup values.
+								if tc.x1Reg != -1 {
+									compiler.movIntConstToRegister(int64(vs.x1Value), tc.x1Reg)
+									compiler.locationStack.pushValueOnRegister(tc.x1Reg)
+								} else {
+									loc := compiler.locationStack.pushValueOnStack()
+									eng.stack[loc.stackPointer] = uint64(vs.x1Value)
+								}
+								if tc.x2Reg != -1 {
+									compiler.movIntConstToRegister(int64(vs.x2Value), tc.x2Reg)
+									compiler.locationStack.pushValueOnRegister(tc.x2Reg)
+								} else {
+									loc := compiler.locationStack.pushValueOnStack()
+									eng.stack[loc.stackPointer] = uint64(vs.x2Value)
+								}
+
+								var err error
+								if signed.signed {
+									err = compiler.compileRem(&wazeroir.OperationRem{Type: wazeroir.SignedInt32})
+								} else {
+									err = compiler.compileRem(&wazeroir.OperationRem{Type: wazeroir.SignedUint32})
+								}
+								require.NoError(t, err)
+
+								require.Equal(t, int16(x86.REG_DX), compiler.locationStack.peek().register)
+								require.Equal(t, generalPurposeRegisterTypeInt, compiler.locationStack.peek().regType)
+								require.Equal(t, uint64(2), compiler.locationStack.sp)
+								require.Len(t, compiler.locationStack.usedRegisters, 1)
+								// At this point, the previous value on the DX register is saved to the stack.
+								require.True(t, prevOnDX.onStack())
+
+								// We add the value previously on the DX with the remainder result
+								// in order to ensure that not saving existing DX value would cause
+								// the failure in a subsequent instruction.
+								err = compiler.compileAdd(&wazeroir.OperationAdd{Type: wazeroir.UnsignedTypeI32})
+								require.NoError(t, err)
+
+								// To verify the behavior, we push the value
+								// to the stack.
+								compiler.releaseAllRegistersToStack()
+								compiler.returnFunction()
+
+								// Generate the code under test.
+								code, _, err := compiler.generate()
+								require.NoError(t, err)
+								// Run code.
+								defer getDivisionByZeroErrorRecoverFunc(t)()
+								jitcall(
+									uintptr(unsafe.Pointer(&code[0])),
+									uintptr(unsafe.Pointer(eng)),
+									0,
+								)
+
+								// Verify the stack is in the form of ["any value previously used by DX" + x1 / x2]
+								require.Equal(t, uint64(1), eng.stackPointer)
+								if signed.signed {
+									require.Equal(t, int32(vs.x1Value)%int32(vs.x2Value)+int32(dxValue), int32(eng.stack[eng.stackPointer-1]))
+								} else {
+									require.Equal(t, vs.x1Value%vs.x2Value+uint32(dxValue), uint32(eng.stack[eng.stackPointer-1]))
+								}
+							})
+						}
+					})
+				}
+			})
+		}
+	})
+	t.Run("int64", func(t *testing.T) {
+		for _, signed := range []struct {
+			name   string
+			signed bool
+		}{
+			{name: "signed", signed: true},
+			{name: "unsigned", signed: false},
+		} {
+			signed := signed
+			t.Run(signed.name, func(t *testing.T) {
+				for _, tc := range []struct {
+					name string
+					// Interpret -1 as stack.
+					x1Reg, x2Reg int16
+				}{
+					{
+						name:  "x1:ax,x2:random_reg",
+						x1Reg: x86.REG_AX,
+						x2Reg: x86.REG_R10,
+					},
+					{
+						name:  "x1:ax,x2:stack",
+						x1Reg: x86.REG_AX,
+						x2Reg: -1,
+					},
+					{
+						name:  "x1:random_reg,x2:ax",
+						x1Reg: x86.REG_R10,
+						x2Reg: x86.REG_AX,
+					},
+					{
+						name:  "x1:staack,x2:ax",
+						x1Reg: -1,
+						x2Reg: x86.REG_AX,
+					},
+					{
+						name:  "x1:random_reg,x2:random_reg",
+						x1Reg: x86.REG_R10,
+						x2Reg: x86.REG_R9,
+					},
+					{
+						name:  "x1:stack,x2:random_reg",
+						x1Reg: -1,
+						x2Reg: x86.REG_R9,
+					},
+					{
+						name:  "x1:random_reg,x2:stack",
+						x1Reg: x86.REG_R9,
+						x2Reg: -1,
+					},
+					{
+						name:  "x1:stack,x2:stack",
+						x1Reg: -1,
+						x2Reg: -1,
+					},
+				} {
+					tc := tc
+					t.Run(tc.name, func(t *testing.T) {
+						const dxValue uint64 = 111111
+						for i, vs := range []struct {
+							x1Value, x2Value uint64
+						}{
+							{x1Value: 2, x2Value: 1},
+							{x1Value: 1, x2Value: 2},
+							{x1Value: 0, x2Value: 1},
+							{x1Value: 1, x2Value: 0},
+							{x1Value: 0, x2Value: 0},
+							// Following cases produce different resulting bit patterns for signed and unsigned.
+							{x1Value: 0xffffffffffffffff /* -1 in signed 64bit */, x2Value: 1},
+							{x1Value: 0xffffffffffffffff /* -1 in signed 64bit */, x2Value: 0xfffffffffffffffe /* -2 in signed 64bit */},
+						} {
+							vs := vs
+							t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+
+								eng := newEngine()
+								compiler := requireNewCompiler(t)
+								compiler.initializeReservedRegisters()
+
+								// Pretend there was an existing value on the DX register. We expect compileDivForInts to save this to the stack.
+								// Here, we put it just before two operands as ["any value used by DX", x1, x2]
+								// but in reality, it can exist in any position of stack.
+								compiler.movIntConstToRegister(int64(dxValue), x86.REG_DX)
+								prevOnDX := compiler.locationStack.pushValueOnRegister(x86.REG_DX)
+
+								// Setup values.
+								if tc.x1Reg != -1 {
+									compiler.movIntConstToRegister(int64(vs.x1Value), tc.x1Reg)
+									compiler.locationStack.pushValueOnRegister(tc.x1Reg)
+								} else {
+									loc := compiler.locationStack.pushValueOnStack()
+									eng.stack[loc.stackPointer] = uint64(vs.x1Value)
+								}
+								if tc.x2Reg != -1 {
+									compiler.movIntConstToRegister(int64(vs.x2Value), tc.x2Reg)
+									compiler.locationStack.pushValueOnRegister(tc.x2Reg)
+								} else {
+									loc := compiler.locationStack.pushValueOnStack()
+									eng.stack[loc.stackPointer] = uint64(vs.x2Value)
+								}
+
+								var err error
+								if signed.signed {
+									err = compiler.compileRem(&wazeroir.OperationRem{Type: wazeroir.SignedInt64})
+								} else {
+									err = compiler.compileRem(&wazeroir.OperationRem{Type: wazeroir.SignedUint64})
+								}
+								require.NoError(t, err)
+
+								require.Equal(t, int16(x86.REG_DX), compiler.locationStack.peek().register)
+								require.Equal(t, generalPurposeRegisterTypeInt, compiler.locationStack.peek().regType)
+								require.Equal(t, uint64(2), compiler.locationStack.sp)
+								require.Len(t, compiler.locationStack.usedRegisters, 1)
+								// At this point, the previous value on the DX register is saved to the stack.
+								require.True(t, prevOnDX.onStack())
+
+								// We add the value previously on the DX with the quotiont of the division result
+								// in order to ensure that not saving existing DX value would cause
+								// the failure in a subsequent instruction.
+								err = compiler.compileAdd(&wazeroir.OperationAdd{Type: wazeroir.UnsignedTypeI64})
+								require.NoError(t, err)
+
+								// To verify the behavior, we push the value
+								// to the stack.
+								compiler.releaseAllRegistersToStack()
+								compiler.returnFunction()
+
+								// Generate the code under test.
+								code, _, err := compiler.generate()
+								require.NoError(t, err)
+								// Run code.
+								defer getDivisionByZeroErrorRecoverFunc(t)()
+								jitcall(
+									uintptr(unsafe.Pointer(&code[0])),
+									uintptr(unsafe.Pointer(eng)),
+									0,
+								)
+
+								// Verify the stack is in the form of ["any value previously used by DX" + x1 / x2]
+								require.Equal(t, uint64(1), eng.stackPointer)
+								if signed.signed {
+									require.Equal(t, int64(vs.x1Value)%int64(vs.x2Value)+int64(dxValue), int64(eng.stack[eng.stackPointer-1]))
+								} else {
+									require.Equal(t, vs.x1Value%vs.x2Value+dxValue, eng.stack[eng.stackPointer-1])
+								}
+							})
+						}
+					})
+				}
+			})
+		}
+	})
+}
+
 func TestAmd64Compiler_compileCall(t *testing.T) {
 	t.Run("host function", func(t *testing.T) {
 		const functionIndex = 5