asm(amd64): ROL and ROR on Register To Memory type. (#423)

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
Takeshi Yoneda
2022-03-31 15:36:38 +09:00
committed by GitHub
parent be3aed46be
commit a351daa858
3 changed files with 126 additions and 90 deletions

View File

@@ -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)
} }

View File

@@ -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{

View File

@@ -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())
}
}
})
} }
}) })
} }