asm(amd64): ROL and ROR on Register To Memory type. (#423)
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
@@ -1299,6 +1299,26 @@ func (a *AssemblerImpl) EncodeRegisterToMemory(n *NodeImpl) (err error) {
|
|||||||
modRM |= 0b00_101_000
|
modRM |= 0b00_101_000
|
||||||
opcode = []byte{0xd3}
|
opcode = []byte{0xd3}
|
||||||
isShiftInstruction = true
|
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:
|
default:
|
||||||
return errorEncodingUnsupported(n)
|
return errorEncodingUnsupported(n)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1036,7 +1036,10 @@ func TestAssemblerImpl_EncodeRegisterToMemory(t *testing.T) {
|
|||||||
t.Run("shift", func(t *testing.T) {
|
t.Run("shift", func(t *testing.T) {
|
||||||
regs := []asm.Register{amd64.REG_AX, amd64.REG_R8}
|
regs := []asm.Register{amd64.REG_AX, amd64.REG_R8}
|
||||||
scales := []byte{1, 4}
|
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 {
|
for _, DstReg := range regs {
|
||||||
DstReg := DstReg
|
DstReg := DstReg
|
||||||
for _, offset := range []int64{
|
for _, offset := range []int64{
|
||||||
|
|||||||
@@ -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},
|
{1 << 63, 1}, {1, 1 << 63}, {1 << 63, 1 << 63},
|
||||||
} {
|
} {
|
||||||
x1, x2 := values[0], values[1]
|
x1, x2 := values[0], values[1]
|
||||||
t.Run(fmt.Sprintf("x1=0x%x,x2=0x%x", x1, x2), func(t *testing.T) {
|
for _, x1OnRegister := range []bool{
|
||||||
env := newJITEnvironment()
|
true, false,
|
||||||
compiler := env.requireNewCompiler(t, newCompiler, nil)
|
} {
|
||||||
err := compiler.compilePreamble()
|
x1OnRegister := x1OnRegister
|
||||||
require.NoError(t, err)
|
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.
|
// Emit consts operands.
|
||||||
for _, v := range []uint64{x1, x2} {
|
var x1Location *valueLocation
|
||||||
switch unsignedInt {
|
switch unsignedInt {
|
||||||
case wazeroir.UnsignedInt32:
|
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:
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !x1OnRegister {
|
||||||
|
compiler.compileReleaseRegisterToStack(x1Location)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, two values exist.
|
// We consumed two values, but push the result back.
|
||||||
require.Equal(t, uint64(2), compiler.valueLocationStack().sp)
|
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)
|
||||||
|
|
||||||
// Emit the operation.
|
err = compiler.compileReturnFunction()
|
||||||
switch kind {
|
require.NoError(t, err)
|
||||||
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)
|
|
||||||
|
|
||||||
// We consumed two values, but push the result back.
|
// Compile and execute the code under test.
|
||||||
require.Equal(t, uint64(1), compiler.valueLocationStack().sp)
|
code, _, _, err := compiler.compile()
|
||||||
resultLocation := compiler.valueLocationStack().peek()
|
require.NoError(t, err)
|
||||||
// Plus the result must be located on a register.
|
env.exec(code)
|
||||||
require.True(t, resultLocation.onRegister())
|
|
||||||
// Also, the result must have an appropriate register type.
|
|
||||||
require.Equal(t, generalPurposeRegisterTypeInt, resultLocation.regType)
|
|
||||||
|
|
||||||
err = compiler.compileReturnFunction()
|
// Check the stack.
|
||||||
require.NoError(t, err)
|
require.Equal(t, uint64(1), env.stackPointer())
|
||||||
|
|
||||||
// Compile and execute the code under test.
|
switch kind {
|
||||||
code, _, _, err := compiler.compile()
|
case wazeroir.OperationKindAnd:
|
||||||
require.NoError(t, err)
|
switch unsignedInt {
|
||||||
env.exec(code)
|
case wazeroir.UnsignedInt32:
|
||||||
|
require.Equal(t, uint32(x1)&uint32(x2), env.stackTopAsUint32())
|
||||||
// Check the stack.
|
case wazeroir.UnsignedInt64:
|
||||||
require.Equal(t, uint64(1), env.stackPointer())
|
require.Equal(t, x1&x2, env.stackTopAsUint64())
|
||||||
|
}
|
||||||
switch kind {
|
case wazeroir.OperationKindOr:
|
||||||
case wazeroir.OperationKindAnd:
|
switch unsignedInt {
|
||||||
switch unsignedInt {
|
case wazeroir.UnsignedInt32:
|
||||||
case wazeroir.UnsignedInt32:
|
require.Equal(t, uint32(x1)|uint32(x2), env.stackTopAsUint32())
|
||||||
require.Equal(t, uint32(x1)&uint32(x2), env.stackTopAsUint32())
|
case wazeroir.UnsignedInt64:
|
||||||
case wazeroir.UnsignedInt64:
|
require.Equal(t, x1|x2, env.stackTopAsUint64())
|
||||||
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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user