Repository: tetratelabs/wazero
Pull request #423 (2022-03-31 06:24:55)

asm(amd64): ROL and ROR on Register To Memory type.
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>


Patch:
diff --git a/internal/asm/amd64/impl.go b/internal/asm/amd64/impl.go
index dcef08e0..90e88485 100644
--- a/internal/asm/amd64/impl.go
+++ b/internal/asm/amd64/impl.go
@@ -1299,6 +1299,26 @@ func (a *AssemblerImpl) EncodeRegisterToMemory(n *NodeImpl) (err error) {
 		modRM |= 0b00_101_000
 		opcode = []byte{0xd3}
 		isShiftInstruction = true
+	case ROLL:
+		// https://www.felixcloutier.com/x86/rcl:rcr:rol:ror
+		opcode = []byte{0xd3}
+		isShiftInstruction = true
+	case ROLQ:
+		// https://www.felixcloutier.com/x86/rcl:rcr:rol:ror
+		RexPrefix |= RexPrefixW
+		opcode = []byte{0xd3}
+		isShiftInstruction = true
+	case RORL:
+		// https://www.felixcloutier.com/x86/rcl:rcr:rol:ror
+		modRM |= 0b00_001_000
+		opcode = []byte{0xd3}
+		isShiftInstruction = true
+	case RORQ:
+		// https://www.felixcloutier.com/x86/rcl:rcr:rol:ror
+		RexPrefix |= RexPrefixW
+		opcode = []byte{0xd3}
+		modRM |= 0b00_001_000
+		isShiftInstruction = true
 	default:
 		return errorEncodingUnsupported(n)
 	}


Test patch:
diff --git a/internal/asm/amd64_debug/impl_test.go b/internal/asm/amd64_debug/impl_test.go
index f150058f..72072bde 100644
--- a/internal/asm/amd64_debug/impl_test.go
+++ b/internal/asm/amd64_debug/impl_test.go
@@ -1036,7 +1036,10 @@ func TestAssemblerImpl_EncodeRegisterToMemory(t *testing.T) {
 	t.Run("shift", func(t *testing.T) {
 		regs := []asm.Register{amd64.REG_AX, amd64.REG_R8}
 		scales := []byte{1, 4}
-		for _, instruction := range []asm.Instruction{amd64.SARL, amd64.SARQ, amd64.SHLL, amd64.SHLQ, amd64.SHRL, amd64.SHRQ} {
+		for _, instruction := range []asm.Instruction{
+			amd64.SARL, amd64.SARQ, amd64.SHLL, amd64.SHLQ, amd64.SHRL, amd64.SHRQ,
+			amd64.ROLL, amd64.ROLQ, amd64.RORL, amd64.RORQ,
+		} {
 			for _, DstReg := range regs {
 				DstReg := DstReg
 				for _, offset := range []int64{
diff --git a/internal/wasm/jit/jit_numeric_test.go b/internal/wasm/jit/jit_numeric_test.go
index c3171c73..1274ded4 100644
--- a/internal/wasm/jit/jit_numeric_test.go
+++ b/internal/wasm/jit/jit_numeric_test.go
@@ -279,107 +279,120 @@ func TestCompiler_compile_And_Or_Xor_Shl_Rotl_Rotr(t *testing.T) {
 						{1 << 63, 1}, {1, 1 << 63}, {1 << 63, 1 << 63},
 					} {
 						x1, x2 := values[0], values[1]
-						t.Run(fmt.Sprintf("x1=0x%x,x2=0x%x", x1, x2), func(t *testing.T) {
-							env := newJITEnvironment()
-							compiler := env.requireNewCompiler(t, newCompiler, nil)
-							err := compiler.compilePreamble()
-							require.NoError(t, err)
+						for _, x1OnRegister := range []bool{
+							true, false,
+						} {
+							x1OnRegister := x1OnRegister
+							t.Run(fmt.Sprintf("x1=0x%x(on_register=%v),x2=0x%x", x1, x1OnRegister, x2), func(t *testing.T) {
+								env := newJITEnvironment()
+								compiler := env.requireNewCompiler(t, newCompiler, nil)
+								err := compiler.compilePreamble()
+								require.NoError(t, err)
 
-							// Emit consts operands.
-							for _, v := range []uint64{x1, x2} {
+								// Emit consts operands.
+								var x1Location *valueLocation
 								switch unsignedInt {
 								case wazeroir.UnsignedInt32:
-									err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(v)})
+									err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(x1)})
+									require.NoError(t, err)
+									x1Location = compiler.valueLocationStack().peek()
+									err = compiler.compileConstI64(&wazeroir.OperationConstI64{Value: x2})
+									require.NoError(t, err)
 								case wazeroir.UnsignedInt64:
-									err = compiler.compileConstI64(&wazeroir.OperationConstI64{Value: v})
+									err = compiler.compileConstI64(&wazeroir.OperationConstI64{Value: x1})
+									require.NoError(t, err)
+									x1Location = compiler.valueLocationStack().peek()
+									err = compiler.compileConstI64(&wazeroir.OperationConstI64{Value: x2})
+									require.NoError(t, err)
 								}
-								require.NoError(t, err)
-							}
 
-							// At this point, two values exist.
-							require.Equal(t, uint64(2), compiler.valueLocationStack().sp)
-
-							// Emit the operation.
-							switch kind {
-							case wazeroir.OperationKindAnd:
-								err = compiler.compileAnd(&wazeroir.OperationAnd{Type: unsignedInt})
-							case wazeroir.OperationKindOr:
-								err = compiler.compileOr(&wazeroir.OperationOr{Type: unsignedInt})
-							case wazeroir.OperationKindXor:
-								err = compiler.compileXor(&wazeroir.OperationXor{Type: unsignedInt})
-							case wazeroir.OperationKindShl:
-								err = compiler.compileShl(&wazeroir.OperationShl{Type: unsignedInt})
-							case wazeroir.OperationKindRotl:
-								err = compiler.compileRotl(&wazeroir.OperationRotl{Type: unsignedInt})
-							case wazeroir.OperationKindRotr:
-								err = compiler.compileRotr(&wazeroir.OperationRotr{Type: unsignedInt})
-							}
-							require.NoError(t, err)
+								if !x1OnRegister {
+									compiler.compileReleaseRegisterToStack(x1Location)
+								}
 
-							// We consumed two values, but push the result back.
-							require.Equal(t, uint64(1), compiler.valueLocationStack().sp)
-							resultLocation := compiler.valueLocationStack().peek()
-							// Plus the result must be located on a register.
-							require.True(t, resultLocation.onRegister())
-							// Also, the result must have an appropriate register type.
-							require.Equal(t, generalPurposeRegisterTypeInt, resultLocation.regType)
+								// At this point, two values exist.
+								require.Equal(t, uint64(2), compiler.valueLocationStack().sp)
 
-							err = compiler.compileReturnFunction()
-							require.NoError(t, err)
+								// Emit the operation.
+								switch kind {
+								case wazeroir.OperationKindAnd:
+									err = compiler.compileAnd(&wazeroir.OperationAnd{Type: unsignedInt})
+								case wazeroir.OperationKindOr:
+									err = compiler.compileOr(&wazeroir.OperationOr{Type: unsignedInt})
+								case wazeroir.OperationKindXor:
+									err = compiler.compileXor(&wazeroir.OperationXor{Type: unsignedInt})
+								case wazeroir.OperationKindShl:
+									err = compiler.compileShl(&wazeroir.OperationShl{Type: unsignedInt})
+								case wazeroir.OperationKindRotl:
+									err = compiler.compileRotl(&wazeroir.OperationRotl{Type: unsignedInt})
+								case wazeroir.OperationKindRotr:
+									err = compiler.compileRotr(&wazeroir.OperationRotr{Type: unsignedInt})
+								}
+								require.NoError(t, err)
 
-							// Compile and execute the code under test.
-							code, _, _, err := compiler.compile()
-							require.NoError(t, err)
-							env.exec(code)
+								// We consumed two values, but push the result back.
+								require.Equal(t, uint64(1), compiler.valueLocationStack().sp)
+								resultLocation := compiler.valueLocationStack().peek()
+								// Also, the result must have an appropriate register type.
+								require.Equal(t, generalPurposeRegisterTypeInt, resultLocation.regType)
 
-							// Check the stack.
-							require.Equal(t, uint64(1), env.stackPointer())
+								err = compiler.compileReturnFunction()
+								require.NoError(t, err)
 
-							switch kind {
-							case wazeroir.OperationKindAnd:
-								switch unsignedInt {
-								case wazeroir.UnsignedInt32:
-									require.Equal(t, uint32(x1)&uint32(x2), env.stackTopAsUint32())
-								case wazeroir.UnsignedInt64:
-									require.Equal(t, x1&x2, env.stackTopAsUint64())
-								}
-							case wazeroir.OperationKindOr:
-								switch unsignedInt {
-								case wazeroir.UnsignedInt32:
-									require.Equal(t, uint32(x1)|uint32(x2), env.stackTopAsUint32())
-								case wazeroir.UnsignedInt64:
-									require.Equal(t, x1|x2, env.stackTopAsUint64())
-								}
-							case wazeroir.OperationKindXor:
-								switch unsignedInt {
-								case wazeroir.UnsignedInt32:
-									require.Equal(t, uint32(x1)^uint32(x2), env.stackTopAsUint32())
-								case wazeroir.UnsignedInt64:
-									require.Equal(t, x1^x2, env.stackTopAsUint64())
-								}
-							case wazeroir.OperationKindShl:
-								switch unsignedInt {
-								case wazeroir.UnsignedInt32:
-									require.Equal(t, uint32(x1)<<uint32(x2%32), env.stackTopAsUint32())
-								case wazeroir.UnsignedInt64:
-									require.Equal(t, x1<<(x2%64), env.stackTopAsUint64())
-								}
-							case wazeroir.OperationKindRotl:
-								switch unsignedInt {
-								case wazeroir.UnsignedInt32:
-									require.Equal(t, bits.RotateLeft32(uint32(x1), int(x2)), env.stackTopAsUint32())
-								case wazeroir.UnsignedInt64:
-									require.Equal(t, bits.RotateLeft64(x1, int(x2)), env.stackTopAsUint64())
-								}
-							case wazeroir.OperationKindRotr:
-								switch unsignedInt {
-								case wazeroir.UnsignedInt32:
-									require.Equal(t, bits.RotateLeft32(uint32(x1), -int(x2)), env.stackTopAsUint32())
-								case wazeroir.UnsignedInt64:
-									require.Equal(t, bits.RotateLeft64(x1, -int(x2)), env.stackTopAsUint64())
+								// Compile and execute the code under test.
+								code, _, _, err := compiler.compile()
+								require.NoError(t, err)
+								env.exec(code)
+
+								// Check the stack.
+								require.Equal(t, uint64(1), env.stackPointer())
+
+								switch kind {
+								case wazeroir.OperationKindAnd:
+									switch unsignedInt {
+									case wazeroir.UnsignedInt32:
+										require.Equal(t, uint32(x1)&uint32(x2), env.stackTopAsUint32())
+									case wazeroir.UnsignedInt64:
+										require.Equal(t, x1&x2, env.stackTopAsUint64())
+									}
+								case wazeroir.OperationKindOr:
+									switch unsignedInt {
+									case wazeroir.UnsignedInt32:
+										require.Equal(t, uint32(x1)|uint32(x2), env.stackTopAsUint32())
+									case wazeroir.UnsignedInt64:
+										require.Equal(t, x1|x2, env.stackTopAsUint64())
+									}
+								case wazeroir.OperationKindXor:
+									switch unsignedInt {
+									case wazeroir.UnsignedInt32:
+										require.Equal(t, uint32(x1)^uint32(x2), env.stackTopAsUint32())
+									case wazeroir.UnsignedInt64:
+										require.Equal(t, x1^x2, env.stackTopAsUint64())
+									}
+								case wazeroir.OperationKindShl:
+									switch unsignedInt {
+									case wazeroir.UnsignedInt32:
+										require.Equal(t, uint32(x1)<<uint32(x2%32), env.stackTopAsUint32())
+									case wazeroir.UnsignedInt64:
+										require.Equal(t, x1<<(x2%64), env.stackTopAsUint64())
+									}
+								case wazeroir.OperationKindRotl:
+									switch unsignedInt {
+									case wazeroir.UnsignedInt32:
+										require.Equal(t, bits.RotateLeft32(uint32(x1), int(x2)), env.stackTopAsUint32())
+									case wazeroir.UnsignedInt64:
+										require.Equal(t, bits.RotateLeft64(x1, int(x2)), env.stackTopAsUint64())
+									}
+								case wazeroir.OperationKindRotr:
+									switch unsignedInt {
+									case wazeroir.UnsignedInt32:
+										require.Equal(t, bits.RotateLeft32(uint32(x1), -int(x2)), env.stackTopAsUint32())
+									case wazeroir.UnsignedInt64:
+										require.Equal(t, bits.RotateLeft64(x1, -int(x2)), env.stackTopAsUint64())
+									}
 								}
-							}
-						})
+							})
+						}
 					}
 				})
 			}