Repository: tetratelabs/wazero
Pull request #105 (2021-12-31 05:48:19)

TypeIndexPtr -> FuncTypeIndex
It is easier to reason with the function type index as a number instead
of a pointer. It is also easier to unit test. We don't need the second
pointer guard because the import definition already checks the type (ex
func vs table) first.

This also fixes a typo



Patch:
diff --git a/wasm/jit/compiler.go b/wasm/jit/compiler.go
index b20e1fca..a553594a 100644
--- a/wasm/jit/compiler.go
+++ b/wasm/jit/compiler.go
@@ -25,6 +25,11 @@ type compiler interface {
 	compilePick(o *wazeroir.OperationPick) error
 	compileAdd(o *wazeroir.OperationAdd) error
 	compileSub(o *wazeroir.OperationSub) error
+	compileEq(o *wazeroir.OperationEq) error
+	compileNe(o *wazeroir.OperationNe) error
+	compileEqz(o *wazeroir.OperationEqz) error
+	compileLt(o *wazeroir.OperationLt) error
+	compileGt(o *wazeroir.OperationGt) error
 	compileLe(o *wazeroir.OperationLe) error
 	compileGe(o *wazeroir.OperationGe) error
 	compileLoad(o *wazeroir.OperationLoad) error
diff --git a/wasm/jit/engine.go b/wasm/jit/engine.go
index e32ebfbe..a4f0bc58 100644
--- a/wasm/jit/engine.go
+++ b/wasm/jit/engine.go
@@ -519,15 +519,15 @@ func (e *engine) compileWasmFunction(f *wasm.FunctionInstance) (*compiledWasmFun
 		case *wazeroir.OperationConstF64:
 			err = compiler.compileConstF64(o)
 		case *wazeroir.OperationEq:
-			err = fmt.Errorf("unsupported operation")
+			err = compiler.compileEq(o)
 		case *wazeroir.OperationNe:
-			err = fmt.Errorf("unsupported operation")
+			err = compiler.compileNe(o)
 		case *wazeroir.OperationEqz:
-			err = fmt.Errorf("unsupported operation")
+			err = compiler.compileEqz(o)
 		case *wazeroir.OperationLt:
-			err = fmt.Errorf("unsupported operation")
+			err = compiler.compileLt(o)
 		case *wazeroir.OperationGt:
-			err = fmt.Errorf("unsupported operation")
+			err = compiler.compileGt(o)
 		case *wazeroir.OperationLe:
 			err = compiler.compileLe(o)
 		case *wazeroir.OperationGe:
diff --git a/wasm/jit/jit_amd64.go b/wasm/jit/jit_amd64.go
index 19c26a30..c77b216a 100644
--- a/wasm/jit/jit_amd64.go
+++ b/wasm/jit/jit_amd64.go
@@ -781,6 +781,229 @@ func (c *amd64Compiler) compileSub(o *wazeroir.OperationSub) error {
 	return nil
 }
 
+func (c *amd64Compiler) compileEq(o *wazeroir.OperationEq) error {
+	return c.emitEqOrNe(o.Type, true)
+}
+
+func (c *amd64Compiler) compileNe(o *wazeroir.OperationNe) error {
+	return c.emitEqOrNe(o.Type, false)
+}
+
+func (c *amd64Compiler) emitEqOrNe(t wazeroir.UnsignedType, shouldEqual bool) error {
+	x2 := c.locationStack.pop()
+	if err := c.ensureOnGeneralPurposeRegister(x2); err != nil {
+		return err
+	}
+
+	x1 := c.locationStack.pop()
+	if err := c.ensureOnGeneralPurposeRegister(x1); err != nil {
+		return err
+	}
+
+	// Emit the compare instruction.
+	prog := c.newProg()
+	prog.From.Type = obj.TYPE_REG
+	prog.From.Reg = x1.register
+	prog.To.Type = obj.TYPE_REG
+	prog.To.Reg = x2.register
+	switch t {
+	case wazeroir.UnsignedTypeI32:
+		prog.As = x86.ACMPL
+	case wazeroir.UnsignedTypeI64:
+		prog.As = x86.ACMPQ
+	case wazeroir.UnsignedTypeF32:
+		prog.As = x86.ACOMISS
+	case wazeroir.UnsignedTypeF64:
+		prog.As = x86.ACOMISD
+	}
+	c.addInstruction(prog)
+
+	// TODO: emit NaN value handings for floats.
+
+	// x1 and x2 are temporary registers only used for the cmp operation. Release them.
+	c.locationStack.releaseRegister(x1)
+	c.locationStack.releaseRegister(x2)
+
+	// Finally, record that the result is on the conditional register.
+	var condReg conditionalRegisterState
+	if shouldEqual {
+		condReg = conditionalRegisterStateE
+	} else {
+		condReg = conditionalRegisterStateNE
+	}
+	loc := c.locationStack.pushValueOnConditionalRegister(condReg)
+	loc.setRegisterType(generalPurposeRegisterTypeInt)
+	return nil
+}
+
+func (c *amd64Compiler) compileEqz(o *wazeroir.OperationEqz) error {
+	v := c.locationStack.pop()
+	if err := c.ensureOnGeneralPurposeRegister(v); err != nil {
+		return err
+	}
+
+	// Take the temporary register for holding the zero value.
+	zeroRegister, err := c.allocateRegister(v.registerType())
+	if err != nil {
+		return err
+	}
+
+	// First, we have to clear the register so the value becomes zero via XOR on itself.
+	xorZero := c.newProg()
+	xorZero.As = x86.AXORQ
+	xorZero.From.Type = obj.TYPE_REG
+	xorZero.From.Reg = zeroRegister
+	xorZero.To.Type = obj.TYPE_REG
+	xorZero.To.Reg = zeroRegister
+	c.addInstruction(xorZero)
+
+	// Emit the compare instruction.
+	prog := c.newProg()
+	prog.From.Type = obj.TYPE_REG
+	prog.From.Reg = zeroRegister
+	prog.To.Type = obj.TYPE_REG
+	prog.To.Reg = v.register
+	switch o.Type {
+	case wazeroir.UnsignedInt32:
+		prog.As = x86.ACMPL
+	case wazeroir.UnsignedInt64:
+		prog.As = x86.ACMPQ
+	}
+	c.addInstruction(prog)
+
+	// v is consumed by the cmp operation so release it.
+	c.locationStack.releaseRegister(v)
+
+	// Finally, record that the result is on the conditional register.
+	loc := c.locationStack.pushValueOnConditionalRegister(conditionalRegisterStateE)
+	loc.setRegisterType(generalPurposeRegisterTypeInt)
+	return nil
+}
+
+func (c *amd64Compiler) compileLt(o *wazeroir.OperationLt) error {
+	x2 := c.locationStack.pop()
+	if err := c.ensureOnGeneralPurposeRegister(x2); err != nil {
+		return err
+	}
+
+	x1 := c.locationStack.pop()
+	if err := c.ensureOnGeneralPurposeRegister(x1); err != nil {
+		return err
+	}
+
+	// Emit the compare instruction.
+	prog := c.newProg()
+	prog.From.Type = obj.TYPE_REG
+	prog.To.Type = obj.TYPE_REG
+	var resultConditionState conditionalRegisterState
+	switch o.Type {
+	case wazeroir.SignedTypeInt32:
+		resultConditionState = conditionalRegisterStateL
+		prog.As = x86.ACMPL
+		prog.From.Reg = x1.register
+		prog.To.Reg = x2.register
+	case wazeroir.SignedTypeUint32:
+		resultConditionState = conditionalRegisterStateB
+		prog.As = x86.ACMPL
+		prog.From.Reg = x1.register
+		prog.To.Reg = x2.register
+	case wazeroir.SignedTypeInt64:
+		resultConditionState = conditionalRegisterStateL
+		prog.As = x86.ACMPQ
+		prog.From.Reg = x1.register
+		prog.To.Reg = x2.register
+	case wazeroir.SignedTypeUint64:
+		resultConditionState = conditionalRegisterStateB
+		prog.As = x86.ACMPQ
+		prog.From.Reg = x1.register
+		prog.To.Reg = x2.register
+	case wazeroir.SignedTypeFloat32:
+		resultConditionState = conditionalRegisterStateB
+		prog.As = x86.ACOMISS
+		prog.From.Reg = x2.register
+		prog.To.Reg = x1.register
+	case wazeroir.SignedTypeFloat64:
+		resultConditionState = conditionalRegisterStateB
+		prog.As = x86.ACOMISD
+		prog.From.Reg = x2.register
+		prog.To.Reg = x1.register
+	}
+	c.addInstruction(prog)
+
+	// TODO: emit NaN value handings for floats.
+
+	// x1 and x2 are temporary registers only used for the cmp operation. Release them.
+	c.locationStack.releaseRegister(x1)
+	c.locationStack.releaseRegister(x2)
+
+	// Finally, record that the result is on the conditional register.
+	loc := c.locationStack.pushValueOnConditionalRegister(resultConditionState)
+	loc.setRegisterType(generalPurposeRegisterTypeInt)
+	return nil
+}
+
+func (c *amd64Compiler) compileGt(o *wazeroir.OperationGt) error {
+	x2 := c.locationStack.pop()
+	if err := c.ensureOnGeneralPurposeRegister(x2); err != nil {
+		return err
+	}
+
+	x1 := c.locationStack.pop()
+	if err := c.ensureOnGeneralPurposeRegister(x1); err != nil {
+		return err
+	}
+
+	// Emit the compare instruction.
+	prog := c.newProg()
+	prog.From.Type = obj.TYPE_REG
+	prog.To.Type = obj.TYPE_REG
+	var resultConditionState conditionalRegisterState
+	switch o.Type {
+	case wazeroir.SignedTypeInt32:
+		resultConditionState = conditionalRegisterStateG
+		prog.As = x86.ACMPL
+		prog.From.Reg = x1.register
+		prog.To.Reg = x2.register
+	case wazeroir.SignedTypeUint32:
+		resultConditionState = conditionalRegisterStateA
+		prog.As = x86.ACMPL
+		prog.From.Reg = x1.register
+		prog.To.Reg = x2.register
+	case wazeroir.SignedTypeInt64:
+		resultConditionState = conditionalRegisterStateG
+		prog.As = x86.ACMPQ
+		prog.From.Reg = x1.register
+		prog.To.Reg = x2.register
+	case wazeroir.SignedTypeUint64:
+		resultConditionState = conditionalRegisterStateA
+		prog.As = x86.ACMPQ
+		prog.From.Reg = x1.register
+		prog.To.Reg = x2.register
+	case wazeroir.SignedTypeFloat32:
+		resultConditionState = conditionalRegisterStateA
+		prog.As = x86.ACOMISS
+		prog.From.Reg = x2.register
+		prog.To.Reg = x1.register
+	case wazeroir.SignedTypeFloat64:
+		resultConditionState = conditionalRegisterStateA
+		prog.As = x86.ACOMISD
+		prog.From.Reg = x2.register
+		prog.To.Reg = x1.register
+	}
+	c.addInstruction(prog)
+
+	// TODO: emit NaN value handings for floats.
+
+	// x1 and x2 are temporary registers only used for the cmp operation. Release them.
+	c.locationStack.releaseRegister(x1)
+	c.locationStack.releaseRegister(x2)
+
+	// Finally, record that the result is on the conditional register.
+	loc := c.locationStack.pushValueOnConditionalRegister(resultConditionState)
+	loc.setRegisterType(generalPurposeRegisterTypeInt)
+	return nil
+}
+
 func (c *amd64Compiler) compileLe(o *wazeroir.OperationLe) error {
 	x2 := c.locationStack.pop()
 	if err := c.ensureOnGeneralPurposeRegister(x2); err != nil {
diff --git a/wasm/segment.go b/wasm/segment.go
index 54190b5b..cfcb9a45 100644
--- a/wasm/segment.go
+++ b/wasm/segment.go
@@ -20,7 +20,9 @@ const (
 type ImportDesc struct {
 	Kind byte
 
-	TypeIndexPtr  *uint32
+	// FuncTypeIndex is the index in Module.TypeSection corresponding to this import's FunctionType.
+	// This is only valid when Kind equals ImportKindFunction
+	FuncTypeIndex uint32
 	TableTypePtr  *TableType
 	MemTypePtr    *MemoryType
 	GlobalTypePtr *GlobalType
@@ -39,8 +41,8 @@ func readImportDesc(r io.Reader) (*ImportDesc, error) {
 			return nil, fmt.Errorf("read typeindex: %v", err)
 		}
 		return &ImportDesc{
-			Kind:         ImportKindFunction,
-			TypeIndexPtr: &tID,
+			Kind:          ImportKindFunction,
+			FuncTypeIndex: tID,
 		}, nil
 	case ImportKindTable:
 		tt, err := readTableType(r)
diff --git a/wasm/store.go b/wasm/store.go
index f78ad5c1..f39aa9eb 100644
--- a/wasm/store.go
+++ b/wasm/store.go
@@ -219,19 +219,19 @@ func (s *Store) resolveImport(target *ModuleInstance, is *ImportSegment) error {
 		return fmt.Errorf("type mismatch on export: got %#x but want %#x", e.Kind, is.Desc.Kind)
 	}
 	switch is.Desc.Kind {
-	case 0x00: // function
-		if err := s.applyFunctionImport(target, is.Desc.TypeIndexPtr, e); err != nil {
+	case ImportKindFunction:
+		if err := s.applyFunctionImport(target, is.Desc.FuncTypeIndex, e); err != nil {
 			return fmt.Errorf("applyFunctionImport: %w", err)
 		}
-	case 0x01: // table
+	case ImportKindTable:
 		if err := s.applyTableImport(target, is.Desc.TableTypePtr, e); err != nil {
 			return fmt.Errorf("applyTableImport: %w", err)
 		}
-	case 0x02: // mem
+	case ImportKindMemory:
 		if err := s.applyMemoryImport(target, is.Desc.MemTypePtr, e); err != nil {
 			return fmt.Errorf("applyMemoryImport: %w", err)
 		}
-	case 0x03: // global
+	case ImportKindGlobal:
 		if err := s.applyGlobalImport(target, is.Desc.GlobalTypePtr, e); err != nil {
 			return fmt.Errorf("applyGlobalImport: %w", err)
 		}
@@ -242,12 +242,8 @@ func (s *Store) resolveImport(target *ModuleInstance, is *ImportSegment) error {
 	return nil
 }
 
-func (s *Store) applyFunctionImport(target *ModuleInstance, typeIndexPtr *uint32, externModuleExportIsntance *ExportInstance) error {
-	if typeIndexPtr == nil {
-		return fmt.Errorf("type index is invalid")
-	}
+func (s *Store) applyFunctionImport(target *ModuleInstance, typeIndex uint32, externModuleExportIsntance *ExportInstance) error {
 	f := externModuleExportIsntance.Function
-	typeIndex := *typeIndexPtr
 	if int(typeIndex) >= len(target.Types) {
 		return fmt.Errorf("unknown type for function import")
 	}
@@ -414,15 +410,15 @@ func (s *Store) buildFunctionInstances(module *Module, target *ModuleInstance) (
 		s.Functions = s.Functions[:prevLen]
 	})
 	var functionDeclarations []uint32
-	var globalDecalarations []*GlobalType
+	var globalDeclarations []*GlobalType
 	var memoryDeclarations []*MemoryType
 	var tableDeclarations []*TableType
 	for _, imp := range module.ImportSection {
 		switch imp.Desc.Kind {
 		case ImportKindFunction:
-			functionDeclarations = append(functionDeclarations, *imp.Desc.TypeIndexPtr)
+			functionDeclarations = append(functionDeclarations, imp.Desc.FuncTypeIndex)
 		case ImportKindGlobal:
-			globalDecalarations = append(globalDecalarations, imp.Desc.GlobalTypePtr)
+			globalDeclarations = append(globalDeclarations, imp.Desc.GlobalTypePtr)
 		case ImportKindMemory:
 			memoryDeclarations = append(memoryDeclarations, imp.Desc.MemTypePtr)
 		case ImportKindTable:
@@ -432,7 +428,7 @@ func (s *Store) buildFunctionInstances(module *Module, target *ModuleInstance) (
 	importedFunctionCount := len(functionDeclarations)
 	functionDeclarations = append(functionDeclarations, module.FunctionSection...)
 	for _, g := range module.GlobalSection {
-		globalDecalarations = append(globalDecalarations, g.Type)
+		globalDeclarations = append(globalDeclarations, g.Type)
 	}
 	memoryDeclarations = append(memoryDeclarations, module.MemorySection...)
 	tableDeclarations = append(tableDeclarations, module.TableSection...)
@@ -467,7 +463,7 @@ func (s *Store) buildFunctionInstances(module *Module, target *ModuleInstance) (
 
 		if _, ok := analysisCache[codeIndex]; !ok {
 			err := analyzeFunction(
-				module, f, functionDeclarations, globalDecalarations,
+				module, f, functionDeclarations, globalDeclarations,
 				memoryDeclarations, tableDeclarations,
 			)
 			if err != nil {


Test patch:
diff --git a/wasm/jit/jit_amd64_test.go b/wasm/jit/jit_amd64_test.go
index 227a0606..221cdf6c 100644
--- a/wasm/jit/jit_amd64_test.go
+++ b/wasm/jit/jit_amd64_test.go
@@ -1094,290 +1094,322 @@ func TestAmd64Compiler_compileAdd(t *testing.T) {
 	})
 }
 
-func TestAmd64Compiler_compileLe(t *testing.T) {
-	t.Run("int32", func(t *testing.T) {
-		for _, tc := range []struct {
-			x1, x2 int32
-			signed bool
-		}{
-			{x1: 100, x2: -1, signed: false}, // interpret x2 as max uint32
-			{x1: -1, x2: -1, signed: false},  // interpret x1 and x2 as max uint32
-			{x1: -1, x2: 100, signed: false}, // interpret x1 as max uint32
-			{x1: 100, x2: 200, signed: true},
-			{x1: 100, x2: 100, signed: true},
-			{x1: 200, x2: 100, signed: true},
-		} {
-			var o *wazeroir.OperationLe
-			if tc.signed {
-				o = &wazeroir.OperationLe{Type: wazeroir.SignedTypeInt32}
-			} else {
-				o = &wazeroir.OperationLe{Type: wazeroir.SignedTypeUint32}
-			}
-			compiler := requireNewCompiler(t)
-			compiler.initializeReservedRegisters()
-			x1Reg := int16(x86.REG_R9)
-			x2Reg := int16(x86.REG_R10)
-			compiler.locationStack.pushValueOnRegister(x1Reg)
-			compiler.locationStack.pushValueOnRegister(x2Reg)
-			compiler.movIntConstToRegister(int64(tc.x1), x1Reg)
-			compiler.movIntConstToRegister(int64(tc.x2), x2Reg)
-			err := compiler.compileLe(o)
-			require.NoError(t, err)
-
-			require.NotContains(t, compiler.locationStack.usedRegisters, x1Reg)
-			require.NotContains(t, compiler.locationStack.usedRegisters, x2Reg)
-			// To verify the behavior, we push the flag value
-			// to the stack.
-			top := compiler.locationStack.peek()
-			require.True(t, top.onConditionalRegister() && !top.onRegister())
-			err = compiler.moveConditionalToGeneralPurposeRegister(top)
-			require.NoError(t, err)
-			require.True(t, !top.onConditionalRegister() && top.onRegister())
-			compiler.releaseRegisterToStack(top)
-			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 stack.
-			require.Equal(t, uint64(1), eng.stackPointer)
-			if tc.signed {
-				require.Equal(t, int32(tc.x1) <= int32(tc.x2), eng.stack[eng.stackPointer-1] == 1)
-			} else {
-				require.Equal(t, uint32(tc.x1) <= uint32(tc.x2), eng.stack[eng.stackPointer-1] == 1)
-			}
-		}
-	})
-	t.Run("int64", func(t *testing.T) {
-		for _, tc := range []struct {
-			x1, x2 int64
-			signed bool
-		}{
-			{x1: 100, x2: -1, signed: false}, // interpret x2 as max uint64
-			{x1: -1, x2: -1, signed: false},  // interpret x1 and x2 as max uint32
-			{x1: -1, x2: 100, signed: false}, // interpret x1 as max uint64
-			{x1: 100, x2: 200, signed: true},
-			{x1: 200, x2: 100, signed: true},
-			{x1: 1 << 56, x2: 100, signed: true},
-			{x1: 1 << 56, x2: 1 << 61, signed: true},
-			{x1: math.MaxInt64, x2: 100, signed: true},
-			{x1: math.MinInt64, x2: 100, signed: true},
-		} {
-			var o *wazeroir.OperationLe
-			if tc.signed {
-				o = &wazeroir.OperationLe{Type: wazeroir.SignedTypeInt64}
-			} else {
-				o = &wazeroir.OperationLe{Type: wazeroir.SignedTypeUint64}
-			}
-			compiler := requireNewCompiler(t)
-			compiler.initializeReservedRegisters()
-			x1Reg := int16(x86.REG_R9)
-			x2Reg := int16(x86.REG_R10)
-			compiler.locationStack.pushValueOnRegister(x1Reg)
-			compiler.locationStack.pushValueOnRegister(x2Reg)
-			compiler.movIntConstToRegister(tc.x1, x1Reg)
-			compiler.movIntConstToRegister(tc.x2, x2Reg)
-			err := compiler.compileLe(o)
-			require.NoError(t, err)
-			require.NotContains(t, compiler.locationStack.usedRegisters, x1Reg)
-			require.NotContains(t, compiler.locationStack.usedRegisters, x2Reg)
-			// To verify the behavior, we push the flag value
-			// to the stack.
-			top := compiler.locationStack.peek()
-			require.True(t, top.onConditionalRegister() && !top.onRegister())
-			err = compiler.moveConditionalToGeneralPurposeRegister(top)
-			require.NoError(t, err)
-			require.True(t, !top.onConditionalRegister() && top.onRegister())
-			compiler.releaseRegisterToStack(top)
-			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 stack.
-			require.Equal(t, uint64(1), eng.stackPointer)
-			if tc.signed {
-				require.Equal(t, tc.x1 <= tc.x2, eng.stack[eng.stackPointer-1] == 1)
-			} else {
-				require.Equal(t, uint64(tc.x1) <= uint64(tc.x2), eng.stack[eng.stackPointer-1] == 1)
-			}
-		}
-	})
-	t.Run("float32", func(t *testing.T) {
-		for i, tc := range []struct {
-			x1, x2 float32
-		}{
-			{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},
-		} {
-			t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
-				// Prepare operands.
-				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()
-
-				// Emit the Le instructions,
-				err = compiler.compileLe(&wazeroir.OperationLe{Type: wazeroir.SignedTypeFloat32})
-				require.NoError(t, err)
-
-				// At this point, these registers must be consumed.
-				require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
-				require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
-				// Plus the result must be pushed.
-				require.Equal(t, uint64(1), compiler.locationStack.sp)
-
-				// To verify the behavior, we push the flag value
-				// to the stack.
-				flag := compiler.locationStack.peek()
-				require.True(t, flag.onConditionalRegister() && !flag.onRegister())
-				err = compiler.moveConditionalToGeneralPurposeRegister(flag)
-				require.NoError(t, err)
-				require.True(t, !flag.onConditionalRegister() && flag.onRegister())
-				compiler.releaseRegisterToStack(flag)
-				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 stack.
-				require.Equal(t, uint64(1), eng.stackPointer)
-				require.Equal(t, tc.x1 <= tc.x2, eng.stack[eng.stackPointer-1] == 1)
+func TestAmd64Compiler_emitEqOrNe(t *testing.T) {
+	for _, instruction := range []struct {
+		name string
+		isEq bool
+	}{
+		{name: "eq", isEq: true},
+		{name: "ne", isEq: false},
+	} {
+		instruction := instruction
+		t.Run(instruction.name, func(t *testing.T) {
+			t.Run("int32", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 uint32
+				}{
+					{x1: 100, x2: math.MaxUint32},
+					{x1: math.MaxUint32, x2: math.MaxUint32},
+					{x1: math.MaxUint32, x2: 100},
+					{x1: 100, x2: 200},
+					{x1: 100, x2: 100},
+					{x1: 200, x2: 100},
+				} {
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						compiler := requireNewCompiler(t)
+						compiler.initializeReservedRegisters()
+
+						// Push the cmp target values.
+						err := compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(tc.x1)})
+						require.NoError(t, err)
+						x1 := compiler.locationStack.peek()
+						err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(tc.x2)})
+						require.NoError(t, err)
+						x2 := compiler.locationStack.peek()
+
+						// Emit the cmp instructions.
+						if instruction.isEq {
+							err = compiler.compileEq(&wazeroir.OperationEq{Type: wazeroir.UnsignedTypeI32})
+						} else {
+							err = compiler.compileNe(&wazeroir.OperationNe{Type: wazeroir.UnsignedTypeI32})
+						}
+						require.NoError(t, err)
+						// At this point, these registers must be consumed.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						top := compiler.locationStack.peek()
+						require.True(t, top.onConditionalRegister() && !top.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(top)
+						require.NoError(t, err)
+						require.True(t, !top.onConditionalRegister() && top.onRegister())
+						compiler.releaseRegisterToStack(top)
+						compiler.returnFunction()
+
+						// Generate the code under test.
+						// and the verification code (moving the result to the stack so we can assert against it)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						if instruction.isEq {
+							require.Equal(t, tc.x1 == tc.x2, eng.stack[eng.stackPointer-1] == 1)
+						} else {
+							require.Equal(t, tc.x1 != tc.x2, eng.stack[eng.stackPointer-1] == 1)
+						}
+					})
+				}
 			})
-		}
-	})
-	t.Run("float64", func(t *testing.T) {
-		for i, tc := range []struct {
-			x1, x2 float64
-		}{
-			{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: 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},
-		} {
-			t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
-				// Prepare operands.
-				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()
-
-				// Emit the Le instructions,
-				err = compiler.compileLe(&wazeroir.OperationLe{Type: wazeroir.SignedTypeFloat64})
-				require.NoError(t, err)
-
-				// At this point, these registers must be consumed.
-				require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
-				require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
-				// Plus the result must be pushed.
-				require.Equal(t, uint64(1), compiler.locationStack.sp)
-
-				// To verify the behavior, we push the flag value
-				// to the stack.
-				flag := compiler.locationStack.peek()
-				require.True(t, flag.onConditionalRegister() && !flag.onRegister())
-				err = compiler.moveConditionalToGeneralPurposeRegister(flag)
-				require.NoError(t, err)
-				require.True(t, !flag.onConditionalRegister() && flag.onRegister())
-				compiler.releaseRegisterToStack(flag)
-				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 stack.
-				require.Equal(t, uint64(1), eng.stackPointer)
-				require.Equal(t, tc.x1 <= tc.x2, eng.stack[eng.stackPointer-1] == 1)
+			t.Run("int64", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 uint64
+				}{
+					{x1: 1, x2: math.MaxUint64},
+					{x1: 100, x2: 200},
+					{x1: 200, x2: 100},
+					{x1: 1 << 56, x2: 100},
+					{x1: 1 << 56, x2: 1 << 61},
+					{x1: math.MaxUint64, x2: 100},
+					{x1: 0, x2: 100},
+				} {
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						compiler := requireNewCompiler(t)
+						compiler.initializeReservedRegisters()
+
+						// Push the cmp target values.
+						err := compiler.compileConstI64(&wazeroir.OperationConstI64{Value: uint64(tc.x1)})
+						require.NoError(t, err)
+						x1 := compiler.locationStack.peek()
+						err = compiler.compileConstI64(&wazeroir.OperationConstI64{Value: uint64(tc.x2)})
+						require.NoError(t, err)
+						x2 := compiler.locationStack.peek()
+
+						// Emit the cmp instructions.
+						if instruction.isEq {
+							err = compiler.compileEq(&wazeroir.OperationEq{Type: wazeroir.UnsignedTypeI64})
+						} else {
+							err = compiler.compileNe(&wazeroir.OperationNe{Type: wazeroir.UnsignedTypeI64})
+						}
+						require.NoError(t, err)
+						// At this point, these registers must be consumed.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						top := compiler.locationStack.peek()
+						require.True(t, top.onConditionalRegister() && !top.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(top)
+						require.NoError(t, err)
+						require.True(t, !top.onConditionalRegister() && top.onRegister())
+						compiler.releaseRegisterToStack(top)
+						compiler.returnFunction()
+
+						// Generate the code under test.
+						// and the verification code (moving the result to the stack so we can assert against it)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						if instruction.isEq {
+							require.Equal(t, tc.x1 == tc.x2, eng.stack[eng.stackPointer-1] == 1)
+						} else {
+							require.Equal(t, tc.x1 != tc.x2, eng.stack[eng.stackPointer-1] == 1)
+						}
+					})
+				}
 			})
-		}
-	})
+			t.Run("float32", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 float32
+				}{
+					{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: 100, x2: float32(math.Inf(1))},
+					{x1: float32(math.Inf(1)), x2: float32(math.Inf(1))},
+					{x1: float32(math.Inf(-1)), x2: 100},
+					{x1: 100, x2: float32(math.Inf(-1))},
+					{x1: float32(math.Inf(-1)), x2: float32(math.Inf(-1))},
+				} {
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						compiler := requireNewCompiler(t)
+						compiler.initializeReservedRegisters()
+
+						// Push the cmp target values.
+						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()
+
+						// Emit the cmp instructions.
+						if instruction.isEq {
+							err = compiler.compileEq(&wazeroir.OperationEq{Type: wazeroir.UnsignedTypeF32})
+						} else {
+							err = compiler.compileNe(&wazeroir.OperationNe{Type: wazeroir.UnsignedTypeF32})
+						}
+						require.NoError(t, err)
+						// At this point, these registers must be consumed.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+						// Plus the result must be pushed.
+						require.Equal(t, uint64(1), compiler.locationStack.sp)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						flag := compiler.locationStack.peek()
+						require.True(t, flag.onConditionalRegister() && !flag.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(flag)
+						require.NoError(t, err)
+						require.True(t, !flag.onConditionalRegister() && flag.onRegister())
+						compiler.releaseRegisterToStack(flag)
+						compiler.returnFunction()
+
+						// Generate the code under test.
+						// and the verification code (moving the result to the stack so we can assert against it)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						if instruction.isEq {
+							require.Equal(t, tc.x1 == tc.x2, eng.stack[eng.stackPointer-1] == 1)
+						} else {
+							require.Equal(t, tc.x1 != tc.x2, eng.stack[eng.stackPointer-1] == 1)
+						}
+					})
+				}
+			})
+			t.Run("float64", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 float64
+				}{
+					{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: 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},
+				} {
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						compiler := requireNewCompiler(t)
+						compiler.initializeReservedRegisters()
+
+						// Push the cmp target values.
+						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()
+
+						// Emit the cmp instructions
+						if instruction.isEq {
+							err = compiler.compileEq(&wazeroir.OperationEq{Type: wazeroir.UnsignedTypeF64})
+						} else {
+							err = compiler.compileNe(&wazeroir.OperationNe{Type: wazeroir.UnsignedTypeF64})
+						}
+						require.NoError(t, err)
+
+						// At this point, these registers must be consumed.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+						// Plus the result must be pushed.
+						require.Equal(t, uint64(1), compiler.locationStack.sp)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						flag := compiler.locationStack.peek()
+						require.True(t, flag.onConditionalRegister() && !flag.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(flag)
+						require.NoError(t, err)
+						require.True(t, !flag.onConditionalRegister() && flag.onRegister())
+						compiler.releaseRegisterToStack(flag)
+						compiler.returnFunction()
+
+						// Generate the code under test.
+						// and the verification code (moving the result to the stack so we can assert against it)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						if instruction.isEq {
+							require.Equal(t, tc.x1 == tc.x2, eng.stack[eng.stackPointer-1] == 1)
+						} else {
+							require.Equal(t, tc.x1 != tc.x2, eng.stack[eng.stackPointer-1] == 1)
+						}
+					})
+				}
+			})
+		})
+	}
 }
 
-func TestAmd64Compiler_compileGe(t *testing.T) {
+func TestAmd64Compiler_compileEqz(t *testing.T) {
 	t.Run("int32", func(t *testing.T) {
-		for i, tc := range []struct {
-			x1, x2 int32
-			signed bool
-		}{
-			{x1: 100, x2: -1, signed: false}, // interpret x2 as max uint32
-			{x1: -1, x2: -1, signed: false},  // interpret x1 and x2 as max uint32
-			{x1: -1, x2: 100, signed: false}, // interpret x1 as max uint32
-			{x1: 100, x2: 200, signed: true},
-			{x1: 100, x2: 100, signed: true},
-			{x1: 200, x2: 100, signed: true},
+		for i, v := range []uint32{
+			0, 1 << 16, math.MaxUint32,
 		} {
+			v := v
 			t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
 				compiler := requireNewCompiler(t)
 				compiler.initializeReservedRegisters()
-				err := compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(tc.x1)})
-				require.NoError(t, err)
-				x1 := compiler.locationStack.peek()
-				err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(tc.x2)})
+
+				// Push the cmp target value.
+				err := compiler.compileConstI32(&wazeroir.OperationConstI32{Value: v})
 				require.NoError(t, err)
-				x2 := compiler.locationStack.peek()
-				var o *wazeroir.OperationGe
-				if tc.signed {
-					o = &wazeroir.OperationGe{Type: wazeroir.SignedTypeInt64}
-				} else {
-					o = &wazeroir.OperationGe{Type: wazeroir.SignedTypeUint64}
-				}
-				err = compiler.compileGe(o)
+				loc := compiler.locationStack.peek()
+
+				// Emit the eqz instructions.
+				err = compiler.compileEqz(&wazeroir.OperationEqz{Type: wazeroir.UnsignedInt32})
 				require.NoError(t, err)
+				// At this point, the target value must be consumed
+				// so the corresponding register must be marked unused.
+				require.NotContains(t, compiler.locationStack.usedRegisters, loc.register)
 
-				require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
-				require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
 				// To verify the behavior, we push the flag value
 				// to the stack.
 				top := compiler.locationStack.peek()
@@ -1389,7 +1421,6 @@ func TestAmd64Compiler_compileGe(t *testing.T) {
 				compiler.returnFunction()
 
 				// Generate the code under test.
-				// and the verification code (moving the result to the stack so we can assert against it)
 				code, _, err := compiler.generate()
 				require.NoError(t, err)
 				// Run code.
@@ -1402,48 +1433,31 @@ func TestAmd64Compiler_compileGe(t *testing.T) {
 				)
 				// Check the stack.
 				require.Equal(t, uint64(1), eng.stackPointer)
-				if tc.signed {
-					require.Equal(t, int32(tc.x1) >= int32(tc.x2), eng.stack[eng.stackPointer-1] == 1)
-				} else {
-					require.Equal(t, uint32(tc.x1) >= uint32(tc.x2), eng.stack[eng.stackPointer-1] == 1)
-				}
+				require.Equal(t, v == uint32(0), eng.stack[eng.stackPointer-1] == 1)
 			})
 		}
 	})
 	t.Run("int64", func(t *testing.T) {
-		for i, tc := range []struct {
-			x1, x2 int64
-			signed bool
-		}{
-			{x1: 100, x2: -1, signed: false}, // interpret x2 as max uint64
-			{x1: -1, x2: -1, signed: false},  // interpret x1 and x2 as max uint32
-			{x1: -1, x2: 100, signed: false}, // interpret x1 as max uint64
-			{x1: 100, x2: 200, signed: true},
-			{x1: 200, x2: 100, signed: true},
-			{x1: 1 << 56, x2: 100, signed: true},
-			{x1: 1 << 56, x2: 1 << 61, signed: true},
-			{x1: math.MaxInt64, x2: 100, signed: true},
-			{x1: math.MinInt64, x2: 100, signed: true},
+		for i, v := range []uint64{
+			0, 1 << 16, 1 << 36, math.MaxUint64,
 		} {
+			v := v
 			t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
 				compiler := requireNewCompiler(t)
 				compiler.initializeReservedRegisters()
-				err := compiler.compileConstI64(&wazeroir.OperationConstI64{Value: uint64(tc.x1)})
-				require.NoError(t, err)
-				x1 := compiler.locationStack.peek()
-				err = compiler.compileConstI64(&wazeroir.OperationConstI64{Value: uint64(tc.x2)})
+
+				// Push the cmp target values.
+				err := compiler.compileConstI64(&wazeroir.OperationConstI64{Value: v})
 				require.NoError(t, err)
-				x2 := compiler.locationStack.peek()
-				var o *wazeroir.OperationGe
-				if tc.signed {
-					o = &wazeroir.OperationGe{Type: wazeroir.SignedTypeInt64}
-				} else {
-					o = &wazeroir.OperationGe{Type: wazeroir.SignedTypeUint64}
-				}
-				err = compiler.compileGe(o)
+				loc := compiler.locationStack.peek()
+
+				// Emit the eqz instructions.
+				err = compiler.compileEqz(&wazeroir.OperationEqz{Type: wazeroir.UnsignedInt64})
 				require.NoError(t, err)
-				require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
-				require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+				// At this point, the target value must be consumed
+				// so the corresponding register must be marked unused.
+				require.NotContains(t, compiler.locationStack.usedRegisters, loc.register)
+
 				// To verify the behavior, we push the flag value
 				// to the stack.
 				top := compiler.locationStack.peek()
@@ -1455,7 +1469,6 @@ func TestAmd64Compiler_compileGe(t *testing.T) {
 				compiler.returnFunction()
 
 				// Generate the code under test.
-				// and the verification code (moving the result to the stack so we can assert against it)
 				code, _, err := compiler.generate()
 				require.NoError(t, err)
 				// Run code.
@@ -1468,140 +1481,649 @@ func TestAmd64Compiler_compileGe(t *testing.T) {
 				)
 				// Check the stack.
 				require.Equal(t, uint64(1), eng.stackPointer)
-				if tc.signed {
-					require.Equal(t, tc.x1 >= tc.x2, eng.stack[eng.stackPointer-1] == 1)
-				} else {
-					require.Equal(t, uint64(tc.x1) >= uint64(tc.x2), eng.stack[eng.stackPointer-1] == 1)
-				}
+				require.Equal(t, v == uint64(0), eng.stack[eng.stackPointer-1] == 1)
 			})
 		}
 	})
-	t.Run("float32", func(t *testing.T) {
-		for i, tc := range []struct {
-			x1, x2 float32
-		}{
-			{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},
-		} {
-			t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
-				// Prepare operands.
-				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()
-
-				// Emit the Ge instructions,
-				err = compiler.compileGe(&wazeroir.OperationGe{Type: wazeroir.SignedTypeFloat32})
-				require.NoError(t, err)
-
-				// At this point, these registers must be consumed.
-				require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
-				require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
-				// Plus the result must be pushed.
-				require.Equal(t, uint64(1), compiler.locationStack.sp)
-
-				// To verify the behavior, we push the flag value
-				// to the stack.
-				flag := compiler.locationStack.peek()
-				require.True(t, flag.onConditionalRegister() && !flag.onRegister())
-				err = compiler.moveConditionalToGeneralPurposeRegister(flag)
-				require.NoError(t, err)
-				require.True(t, !flag.onConditionalRegister() && flag.onRegister())
-				compiler.releaseRegisterToStack(flag)
-				compiler.returnFunction()
+}
 
-				// Generate the code under test.
-				// and the verification code (moving the result to the stack so we can assert against it)
-				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 stack.
-				require.Equal(t, uint64(1), eng.stackPointer)
-				require.Equal(t, tc.x1 >= tc.x2, eng.stack[eng.stackPointer-1] == 1)
+func TestAmd64Compiler_compileLe_or_Lt(t *testing.T) {
+	for _, instruction := range []struct {
+		name      string
+		inclusive bool
+	}{
+		{name: "less_than_or_equal", inclusive: true},
+		{name: "less_than", inclusive: false},
+	} {
+		instruction := instruction
+		t.Run(instruction.name, func(t *testing.T) {
+			t.Run("int32", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 int32
+					signed bool
+				}{
+					{x1: 100, x2: -1, signed: false}, // interpret x2 as max uint32
+					{x1: -1, x2: -1, signed: false},  // interpret x1 and x2 as max uint32
+					{x1: -1, x2: 100, signed: false}, // interpret x1 as max uint32
+					{x1: 100, x2: 200, signed: true},
+					{x1: 100, x2: 100, signed: true},
+					{x1: 200, x2: 100, signed: true},
+				} {
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						compiler := requireNewCompiler(t)
+						compiler.initializeReservedRegisters()
+
+						// Push the target values.
+						err := compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(tc.x1)})
+						require.NoError(t, err)
+						x1 := compiler.locationStack.peek()
+						err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(tc.x2)})
+						require.NoError(t, err)
+						x2 := compiler.locationStack.peek()
+
+						// Emit the cmp instructions.
+						var targetType wazeroir.SignedType
+						if tc.signed {
+							targetType = wazeroir.SignedTypeInt32
+						} else {
+							targetType = wazeroir.SignedTypeUint32
+						}
+						if instruction.inclusive {
+							err = compiler.compileLe(&wazeroir.OperationLe{Type: targetType})
+						} else {
+							err = compiler.compileLt(&wazeroir.OperationLt{Type: targetType})
+						}
+						require.NoError(t, err)
+
+						// At this point, all the registesr must be consumed by cmp
+						// so they should be marked as unused.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						top := compiler.locationStack.peek()
+						require.True(t, top.onConditionalRegister() && !top.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(top)
+						require.NoError(t, err)
+						require.True(t, !top.onConditionalRegister() && top.onRegister())
+						compiler.releaseRegisterToStack(top)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						var exp bool
+						if tc.signed {
+							exp = tc.x1 < tc.x2
+						} else {
+							exp = uint32(tc.x1) < uint32(tc.x2)
+						}
+						if instruction.inclusive {
+							exp = exp || tc.x1 == tc.x2
+						}
+						require.Equal(t, exp, eng.stack[eng.stackPointer-1] == 1)
+					})
+				}
 			})
-		}
-	})
-	t.Run("float64", func(t *testing.T) {
-		for i, tc := range []struct {
-			x1, x2 float64
-		}{
-			{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: 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},
-		} {
-			t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
-				// Prepare operands.
-				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()
-
-				// Emit the Ge instructions,
-				err = compiler.compileGe(&wazeroir.OperationGe{Type: wazeroir.SignedTypeFloat64})
-				require.NoError(t, err)
-
-				// At this point, these registers must be consumed.
-				require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
-				require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
-				// Plus the result must be pushed.
-				require.Equal(t, uint64(1), compiler.locationStack.sp)
-
-				// To verify the behavior, we push the flag value
-				// to the stack.
-				flag := compiler.locationStack.peek()
-				require.True(t, flag.onConditionalRegister() && !flag.onRegister())
-				err = compiler.moveConditionalToGeneralPurposeRegister(flag)
-				require.NoError(t, err)
-				require.True(t, !flag.onConditionalRegister() && flag.onRegister())
-				compiler.releaseRegisterToStack(flag)
-				compiler.returnFunction()
+			t.Run("int64", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 int64
+					signed bool
+				}{
+					{x1: 100, x2: -1, signed: false}, // interpret x2 as max uint64
+					{x1: -1, x2: -1, signed: false},  // interpret x1 and x2 as max uint32
+					{x1: -1, x2: 100, signed: false}, // interpret x1 as max uint64
+					{x1: 100, x2: 200, signed: true},
+					{x1: 200, x2: 100, signed: true},
+					{x1: 1 << 56, x2: 100, signed: true},
+					{x1: 1 << 56, x2: 1 << 61, signed: true},
+					{x1: math.MaxInt64, x2: 100, signed: true},
+					{x1: math.MinInt64, x2: 100, signed: true},
+				} {
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						compiler := requireNewCompiler(t)
+						compiler.initializeReservedRegisters()
+
+						// Push the target values.
+						err := compiler.compileConstI64(&wazeroir.OperationConstI64{Value: uint64(tc.x1)})
+						require.NoError(t, err)
+						x1 := compiler.locationStack.peek()
+						err = compiler.compileConstI64(&wazeroir.OperationConstI64{Value: uint64(tc.x2)})
+						require.NoError(t, err)
+						x2 := compiler.locationStack.peek()
+
+						// Emit the cmp instructions.
+						var targetType wazeroir.SignedType
+						if tc.signed {
+							targetType = wazeroir.SignedTypeInt64
+						} else {
+							targetType = wazeroir.SignedTypeUint64
+						}
+						if instruction.inclusive {
+							err = compiler.compileLe(&wazeroir.OperationLe{Type: targetType})
+						} else {
+							err = compiler.compileLt(&wazeroir.OperationLt{Type: targetType})
+						}
+						require.NoError(t, err)
+
+						// At this point, all the registesr must be consumed by cmp
+						// so they should be marked as unused.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						top := compiler.locationStack.peek()
+						require.True(t, top.onConditionalRegister() && !top.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(top)
+						require.NoError(t, err)
+						require.True(t, !top.onConditionalRegister() && top.onRegister())
+						compiler.releaseRegisterToStack(top)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						var exp bool
+						if tc.signed {
+							exp = tc.x1 < tc.x2
+						} else {
+							exp = uint64(tc.x1) < uint64(tc.x2)
+						}
+						if instruction.inclusive {
+							exp = exp || tc.x1 == tc.x2
+						}
+						require.Equal(t, exp, eng.stack[eng.stackPointer-1] == 1)
+					})
+				}
+			})
+			t.Run("float32", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 float32
+				}{
+					{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},
+				} {
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						// Prepare operands.
+						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()
+
+						// Emit the cmp instructions
+						if instruction.inclusive {
+							err = compiler.compileLe(&wazeroir.OperationLe{Type: wazeroir.SignedTypeFloat32})
+						} else {
+							err = compiler.compileLt(&wazeroir.OperationLt{Type: wazeroir.SignedTypeFloat32})
+						}
+						require.NoError(t, err)
+
+						// At this point, these registers must be consumed.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+						// Plus the result must be pushed.
+						require.Equal(t, uint64(1), compiler.locationStack.sp)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						flag := compiler.locationStack.peek()
+						require.True(t, flag.onConditionalRegister() && !flag.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(flag)
+						require.NoError(t, err)
+						require.True(t, !flag.onConditionalRegister() && flag.onRegister())
+						compiler.releaseRegisterToStack(flag)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						exp := tc.x1 < tc.x2
+						if instruction.inclusive {
+							exp = exp || tc.x1 == tc.x2
+						}
+						require.Equal(t, exp, eng.stack[eng.stackPointer-1] == 1)
+					})
+				}
+			})
+			t.Run("float64", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 float64
+				}{
+					{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: 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},
+				} {
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						// Prepare operands.
+						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()
+
+						// Emit the cmp instructions,
+						if instruction.inclusive {
+							err = compiler.compileLe(&wazeroir.OperationLe{Type: wazeroir.SignedTypeFloat64})
+						} else {
+							err = compiler.compileLt(&wazeroir.OperationLt{Type: wazeroir.SignedTypeFloat64})
+						}
+						require.NoError(t, err)
+
+						// At this point, these registers must be consumed.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+						// Plus the result must be pushed.
+						require.Equal(t, uint64(1), compiler.locationStack.sp)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						flag := compiler.locationStack.peek()
+						require.True(t, flag.onConditionalRegister() && !flag.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(flag)
+						require.NoError(t, err)
+						require.True(t, !flag.onConditionalRegister() && flag.onRegister())
+						compiler.releaseRegisterToStack(flag)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						exp := tc.x1 < tc.x2
+						if instruction.inclusive {
+							exp = exp || tc.x1 == tc.x2
+						}
+						require.Equal(t, exp, eng.stack[eng.stackPointer-1] == 1)
+					})
+				}
+			})
+		})
+	}
+}
 
-				// Generate the code under test.
-				// and the verification code (moving the result to the stack so we can assert against it)
-				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 stack.
-				require.Equal(t, uint64(1), eng.stackPointer)
-				require.Equal(t, tc.x1 >= tc.x2, eng.stack[eng.stackPointer-1] == 1)
+func TestAmd64Compiler_compileGe_or_Gt(t *testing.T) {
+	for _, instruction := range []struct {
+		name      string
+		inclusive bool
+	}{
+		{name: "greater_than_or_equal", inclusive: true},
+		{name: "greater_than", inclusive: false},
+	} {
+		instruction := instruction
+		t.Run(instruction.name, func(t *testing.T) {
+			t.Run("int32", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 int32
+					signed bool
+				}{
+					{x1: 100, x2: -1, signed: false}, // interpret x2 as max uint32
+					{x1: -1, x2: -1, signed: false},  // interpret x1 and x2 as max uint32
+					{x1: -1, x2: 100, signed: false}, // interpret x1 as max uint32
+					{x1: 100, x2: 200, signed: true},
+					{x1: 100, x2: 100, signed: true},
+					{x1: 200, x2: 100, signed: true},
+				} {
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						compiler := requireNewCompiler(t)
+						compiler.initializeReservedRegisters()
+
+						// Push the target values.
+						err := compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(tc.x1)})
+						require.NoError(t, err)
+						x1 := compiler.locationStack.peek()
+						err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(tc.x2)})
+						require.NoError(t, err)
+						x2 := compiler.locationStack.peek()
+
+						// Emit the cmp instructions.
+						var targetType wazeroir.SignedType
+						if tc.signed {
+							targetType = wazeroir.SignedTypeInt32
+						} else {
+							targetType = wazeroir.SignedTypeUint32
+						}
+						if instruction.inclusive {
+							err = compiler.compileGe(&wazeroir.OperationGe{Type: targetType})
+						} else {
+							err = compiler.compileGt(&wazeroir.OperationGt{Type: targetType})
+						}
+						require.NoError(t, err)
+
+						// At this point, all the registesr must be consumed by cmp
+						// so they should be marked as unused.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						top := compiler.locationStack.peek()
+						require.True(t, top.onConditionalRegister() && !top.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(top)
+						require.NoError(t, err)
+						require.True(t, !top.onConditionalRegister() && top.onRegister())
+						compiler.releaseRegisterToStack(top)
+						compiler.returnFunction()
+
+						// Generate the code under test.
+						// and the verification code (moving the result to the stack so we can assert against it)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						var exp bool
+						if tc.signed {
+							exp = tc.x1 > tc.x2
+						} else {
+							exp = uint32(tc.x1) > uint32(tc.x2)
+						}
+						if instruction.inclusive {
+							exp = exp || tc.x1 == tc.x2
+						}
+						require.Equal(t, exp, eng.stack[eng.stackPointer-1] == 1)
+					})
+				}
 			})
-		}
-	})
+			t.Run("int64", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 int64
+					signed bool
+				}{
+					{x1: 100, x2: -1, signed: false}, // interpret x2 as max uint64
+					{x1: -1, x2: -1, signed: false},  // interpret x1 and x2 as max uint32
+					{x1: -1, x2: 100, signed: false}, // interpret x1 as max uint64
+					{x1: 100, x2: 200, signed: true},
+					{x1: 200, x2: 100, signed: true},
+					{x1: 1 << 56, x2: 100, signed: true},
+					{x1: 1 << 56, x2: 1 << 61, signed: true},
+					{x1: math.MaxInt64, x2: 100, signed: true},
+					{x1: math.MinInt64, x2: 100, signed: true},
+				} {
+
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						compiler := requireNewCompiler(t)
+						compiler.initializeReservedRegisters()
+
+						// Push the target values.
+						err := compiler.compileConstI64(&wazeroir.OperationConstI64{Value: uint64(tc.x1)})
+						require.NoError(t, err)
+						x1 := compiler.locationStack.peek()
+						err = compiler.compileConstI64(&wazeroir.OperationConstI64{Value: uint64(tc.x2)})
+						require.NoError(t, err)
+						x2 := compiler.locationStack.peek()
+
+						// Emit the cmp instructions.
+						var targetType wazeroir.SignedType
+						if tc.signed {
+							targetType = wazeroir.SignedTypeInt64
+						} else {
+							targetType = wazeroir.SignedTypeUint64
+						}
+						if instruction.inclusive {
+							err = compiler.compileGe(&wazeroir.OperationGe{Type: targetType})
+						} else {
+							err = compiler.compileGt(&wazeroir.OperationGt{Type: targetType})
+						}
+						require.NoError(t, err)
+
+						// At this point, all the registesr must be consumed by cmp
+						// so they should be marked as unused.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						top := compiler.locationStack.peek()
+						require.True(t, top.onConditionalRegister() && !top.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(top)
+						require.NoError(t, err)
+						require.True(t, !top.onConditionalRegister() && top.onRegister())
+						compiler.releaseRegisterToStack(top)
+						compiler.returnFunction()
+
+						// Generate the code under test.
+						// and the verification code (moving the result to the stack so we can assert against it)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						var exp bool
+						if tc.signed {
+							exp = tc.x1 > tc.x2
+						} else {
+							exp = uint64(tc.x1) > uint64(tc.x2)
+						}
+						if instruction.inclusive {
+							exp = exp || tc.x1 == tc.x2
+						}
+						require.Equal(t, exp, eng.stack[eng.stackPointer-1] == 1)
+					})
+				}
+			})
+			t.Run("float32", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 float32
+				}{
+					{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},
+				} {
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						// Prepare operands.
+						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()
+
+						// Emit the cmp instructions,
+						if instruction.inclusive {
+							err = compiler.compileGe(&wazeroir.OperationGe{Type: wazeroir.SignedTypeFloat32})
+						} else {
+							err = compiler.compileGt(&wazeroir.OperationGt{Type: wazeroir.SignedTypeFloat32})
+						}
+						require.NoError(t, err)
+
+						// At this point, these registers must be consumed.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+						// Plus the result must be pushed.
+						require.Equal(t, uint64(1), compiler.locationStack.sp)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						flag := compiler.locationStack.peek()
+						require.True(t, flag.onConditionalRegister() && !flag.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(flag)
+						require.NoError(t, err)
+						require.True(t, !flag.onConditionalRegister() && flag.onRegister())
+						compiler.releaseRegisterToStack(flag)
+						compiler.returnFunction()
+
+						// Generate the code under test.
+						// and the verification code (moving the result to the stack so we can assert against it)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						exp := tc.x1 > tc.x2
+						if instruction.inclusive {
+							exp = exp || tc.x1 == tc.x2
+						}
+						require.Equal(t, exp, eng.stack[eng.stackPointer-1] == 1)
+					})
+				}
+			})
+			t.Run("float64", func(t *testing.T) {
+				for i, tc := range []struct {
+					x1, x2 float64
+				}{
+					{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: 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},
+				} {
+					t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+						// Prepare operands.
+						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()
+
+						// Emit the cmp instructions,
+						if instruction.inclusive {
+							err = compiler.compileGe(&wazeroir.OperationGe{Type: wazeroir.SignedTypeFloat64})
+						} else {
+							err = compiler.compileGt(&wazeroir.OperationGt{Type: wazeroir.SignedTypeFloat64})
+						}
+						require.NoError(t, err)
+
+						// At this point, these registers must be consumed.
+						require.NotContains(t, compiler.locationStack.usedRegisters, x1.register)
+						require.NotContains(t, compiler.locationStack.usedRegisters, x2.register)
+						// Plus the result must be pushed.
+						require.Equal(t, uint64(1), compiler.locationStack.sp)
+
+						// To verify the behavior, we push the flag value
+						// to the stack.
+						flag := compiler.locationStack.peek()
+						require.True(t, flag.onConditionalRegister() && !flag.onRegister())
+						err = compiler.moveConditionalToGeneralPurposeRegister(flag)
+						require.NoError(t, err)
+						require.True(t, !flag.onConditionalRegister() && flag.onRegister())
+						compiler.releaseRegisterToStack(flag)
+						compiler.returnFunction()
+
+						// Generate the code under test.
+						// and the verification code (moving the result to the stack so we can assert against it)
+						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 stack.
+						require.Equal(t, uint64(1), eng.stackPointer)
+						exp := tc.x1 > tc.x2
+						if instruction.inclusive {
+							exp = exp || tc.x1 == tc.x2
+						}
+						require.Equal(t, exp, eng.stack[eng.stackPointer-1] == 1)
+					})
+				}
+			})
+		})
+	}
 }
 
 func TestAmd64Compiler_compileSub(t *testing.T) {