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) } 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) + } + + 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) - } - // At this point, two values exist. - require.Equal(t, uint64(2), compiler.valueLocationStack().sp) + // 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) - // 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) + err = compiler.compileReturnFunction() + require.NoError(t, err) - // 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) + // Compile and execute the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) - err = compiler.compileReturnFunction() - require.NoError(t, err) + // Check the stack. + require.Equal(t, uint64(1), env.stackPointer()) - // 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()) + 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)<