Commit f5357637 authored by bmeurer@chromium.org's avatar bmeurer@chromium.org

[turbofan] Implement the correct semantics for integer division/modulus.

Also fix the sdiv/udiv instructions on ARM as a nice side effect.

TEST=cctest,unittests
R=jarin@chromium.org

Review URL: https://codereview.chromium.org/677483005

Cr-Commit-Position: refs/heads/master@{#24888}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24888 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a13acdf6
......@@ -2741,15 +2741,12 @@ void Simulator::DecodeType3(Instruction* instr) {
int rs = instr->RsValue();
int32_t rs_val = get_register(rs);
int32_t ret_val = 0;
DCHECK(rs_val != 0);
// udiv
if (instr->Bit(21) == 0x1) {
ret_val = static_cast<int32_t>(static_cast<uint32_t>(rm_val) /
static_cast<uint32_t>(rs_val));
} else if ((rm_val == kMinInt) && (rs_val == -1)) {
ret_val = kMinInt;
ret_val = bit_cast<int32_t>(base::bits::UnsignedDiv32(
bit_cast<uint32_t>(rm_val), bit_cast<uint32_t>(rs_val)));
} else {
ret_val = rm_val / rs_val;
ret_val = base::bits::SignedDiv32(rm_val, rs_val);
}
set_register(rn, ret_val);
return;
......
......@@ -3,6 +3,9 @@
// found in the LICENSE file.
#include "src/base/bits.h"
#include <limits>
#include "src/base/logging.h"
namespace v8 {
......@@ -32,6 +35,19 @@ int32_t SignedMulHighAndAdd32(int32_t lhs, int32_t rhs, int32_t acc) {
bit_cast<uint32_t>(SignedMulHigh32(lhs, rhs)));
}
int32_t SignedDiv32(int32_t lhs, int32_t rhs) {
if (rhs == 0) return 0;
if (rhs == -1) return -lhs;
return lhs / rhs;
}
int32_t SignedMod32(int32_t lhs, int32_t rhs) {
if (rhs == 0 || rhs == -1) return 0;
return lhs % rhs;
}
} // namespace bits
} // namespace base
} // namespace v8
......@@ -199,6 +199,32 @@ int32_t SignedMulHigh32(int32_t lhs, int32_t rhs);
// adds the accumulate value |acc|.
int32_t SignedMulHighAndAdd32(int32_t lhs, int32_t rhs, int32_t acc);
// SignedDiv32(lhs, rhs) divides |lhs| by |rhs| and returns the quotient
// truncated to int32. If |rhs| is zero, then zero is returned. If |lhs|
// is minint and |rhs| is -1, it returns minint.
int32_t SignedDiv32(int32_t lhs, int32_t rhs);
// SignedMod32(lhs, rhs) divides |lhs| by |rhs| and returns the remainder
// truncated to int32. If either |rhs| is zero or |lhs| is minint and |rhs|
// is -1, it returns zero.
int32_t SignedMod32(int32_t lhs, int32_t rhs);
// UnsignedDiv32(lhs, rhs) divides |lhs| by |rhs| and returns the quotient
// truncated to uint32. If |rhs| is zero, then zero is returned.
inline uint32_t UnsignedDiv32(uint32_t lhs, uint32_t rhs) {
return rhs ? lhs / rhs : 0u;
}
// UnsignedMod32(lhs, rhs) divides |lhs| by |rhs| and returns the remainder
// truncated to uint32. If |rhs| is zero, then zero is returned.
inline uint32_t UnsignedMod32(uint32_t lhs, uint32_t rhs) {
return rhs ? lhs % rhs : 0u;
}
} // namespace bits
} // namespace base
} // namespace v8
......
......@@ -58,6 +58,11 @@ Node* MachineOperatorReducer::Word32Shr(Node* lhs, uint32_t rhs) {
}
Node* MachineOperatorReducer::Word32Equal(Node* lhs, Node* rhs) {
return graph()->NewNode(machine()->Word32Equal(), lhs, rhs);
}
Node* MachineOperatorReducer::Int32Add(Node* lhs, Node* rhs) {
return graph()->NewNode(machine()->Int32Add(), lhs, rhs);
}
......@@ -299,40 +304,12 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
}
case IrOpcode::kInt32Div:
return ReduceInt32Div(node);
case IrOpcode::kUint32Div: {
Uint32BinopMatcher m(node);
if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
// TODO(turbofan): if (m.left().Is(0))
// TODO(turbofan): if (m.right().Is(0))
// TODO(turbofan): if (m.LeftEqualsRight())
if (m.IsFoldable() && !m.right().Is(0)) { // K / K => K
return ReplaceInt32(m.left().Value() / m.right().Value());
}
if (m.right().IsPowerOf2()) { // x / 2^n => x >> n
node->set_op(machine()->Word32Shr());
node->ReplaceInput(1, Int32Constant(WhichPowerOf2(m.right().Value())));
return Changed(node);
}
break;
}
case IrOpcode::kUint32Div:
return ReduceUint32Div(node);
case IrOpcode::kInt32Mod:
return ReduceInt32Mod(node);
case IrOpcode::kUint32Mod: {
Uint32BinopMatcher m(node);
if (m.right().Is(1)) return ReplaceInt32(0); // x % 1 => 0
// TODO(turbofan): if (m.left().Is(0))
// TODO(turbofan): if (m.right().Is(0))
// TODO(turbofan): if (m.LeftEqualsRight())
if (m.IsFoldable() && !m.right().Is(0)) { // K % K => K
return ReplaceInt32(m.left().Value() % m.right().Value());
}
if (m.right().IsPowerOf2()) { // x % 2^n => x & 2^n-1
node->set_op(machine()->Word32And());
node->ReplaceInput(1, Int32Constant(m.right().Value() - 1));
return Changed(node);
}
break;
}
case IrOpcode::kUint32Mod:
return ReduceUint32Mod(node);
case IrOpcode::kInt32LessThan: {
Int32BinopMatcher m(node);
if (m.IsFoldable()) { // K < K => K
......@@ -554,13 +531,16 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
Reduction MachineOperatorReducer::ReduceInt32Div(Node* node) {
Int32BinopMatcher m(node);
if (m.left().Is(0)) return Replace(m.left().node()); // 0 / x => 0
if (m.right().Is(0)) return Replace(m.right().node()); // x / 0 => 0
if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
// TODO(turbofan): if (m.left().Is(0))
// TODO(turbofan): if (m.LeftEqualsRight())
if (m.IsFoldable() && !m.right().Is(0)) { // K / K => K
if (m.right().Is(-1)) return ReplaceInt32(-m.left().Value());
return ReplaceInt32(m.left().Value() / m.right().Value());
if (m.IsFoldable()) { // K / K => K
return ReplaceInt32(
base::bits::SignedDiv32(m.left().Value(), m.right().Value()));
}
if (m.LeftEqualsRight()) { // x / x => x != 0
Node* const zero = Int32Constant(0);
return Replace(Word32Equal(Word32Equal(m.left().node(), zero), zero));
}
if (m.right().Is(-1)) { // x / -1 => 0 - x
node->set_op(machine()->Int32Sub());
......@@ -595,15 +575,38 @@ Reduction MachineOperatorReducer::ReduceInt32Div(Node* node) {
}
Reduction MachineOperatorReducer::ReduceUint32Div(Node* node) {
Uint32BinopMatcher m(node);
if (m.left().Is(0)) return Replace(m.left().node()); // 0 / x => 0
if (m.right().Is(0)) return Replace(m.right().node()); // x / 0 => 0
if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
if (m.IsFoldable()) { // K / K => K
return ReplaceUint32(
base::bits::UnsignedDiv32(m.left().Value(), m.right().Value()));
}
if (m.LeftEqualsRight()) { // x / x => x != 0
Node* const zero = Int32Constant(0);
return Replace(Word32Equal(Word32Equal(m.left().node(), zero), zero));
}
if (m.right().IsPowerOf2()) { // x / 2^n => x >> n
node->set_op(machine()->Word32Shr());
node->ReplaceInput(1, Uint32Constant(WhichPowerOf2(m.right().Value())));
return Changed(node);
}
return NoChange();
}
Reduction MachineOperatorReducer::ReduceInt32Mod(Node* node) {
Int32BinopMatcher m(node);
if (m.right().Is(1)) return ReplaceInt32(0); // x % 1 => 0
if (m.right().Is(-1)) return ReplaceInt32(0); // x % -1 => 0
// TODO(turbofan): if (m.left().Is(0))
// TODO(turbofan): if (m.right().Is(0))
// TODO(turbofan): if (m.LeftEqualsRight())
if (m.IsFoldable() && !m.right().Is(0)) { // K % K => K
return ReplaceInt32(m.left().Value() % m.right().Value());
if (m.left().Is(0)) return Replace(m.left().node()); // 0 % x => 0
if (m.right().Is(0)) return Replace(m.right().node()); // x % 0 => 0
if (m.right().Is(1)) return ReplaceInt32(0); // x % 1 => 0
if (m.right().Is(-1)) return ReplaceInt32(0); // x % -1 => 0
if (m.LeftEqualsRight()) return ReplaceInt32(0); // x % x => 0
if (m.IsFoldable()) { // K % K => K
return ReplaceInt32(
base::bits::SignedMod32(m.left().Value(), m.right().Value()));
}
if (m.right().HasValue()) {
Node* const dividend = m.left().node();
......@@ -639,6 +642,25 @@ Reduction MachineOperatorReducer::ReduceInt32Mod(Node* node) {
}
Reduction MachineOperatorReducer::ReduceUint32Mod(Node* node) {
Uint32BinopMatcher m(node);
if (m.left().Is(0)) return Replace(m.left().node()); // 0 % x => 0
if (m.right().Is(0)) return Replace(m.right().node()); // x % 0 => 0
if (m.right().Is(1)) return ReplaceUint32(0); // x % 1 => 0
if (m.LeftEqualsRight()) return ReplaceInt32(0); // x % x => 0
if (m.IsFoldable()) { // K % K => K
return ReplaceUint32(
base::bits::UnsignedMod32(m.left().Value(), m.right().Value()));
}
if (m.right().IsPowerOf2()) { // x % 2^n => x & 2^n-1
node->set_op(machine()->Word32And());
node->ReplaceInput(1, Uint32Constant(m.right().Value() - 1));
return Changed(node);
}
return NoChange();
}
Reduction MachineOperatorReducer::ReduceProjection(size_t index, Node* node) {
switch (node->opcode()) {
case IrOpcode::kInt32AddWithOverflow: {
......
......@@ -37,6 +37,7 @@ class MachineOperatorReducer FINAL : public Reducer {
Node* Word32And(Node* lhs, uint32_t rhs);
Node* Word32Sar(Node* lhs, uint32_t rhs);
Node* Word32Shr(Node* lhs, uint32_t rhs);
Node* Word32Equal(Node* lhs, Node* rhs);
Node* Int32Add(Node* lhs, Node* rhs);
Node* Int32Sub(Node* lhs, Node* rhs);
Node* Int32Mul(Node* lhs, Node* rhs);
......@@ -53,12 +54,17 @@ class MachineOperatorReducer FINAL : public Reducer {
Reduction ReplaceInt32(int32_t value) {
return Replace(Int32Constant(value));
}
Reduction ReplaceUint32(uint32_t value) {
return Replace(Uint32Constant(value));
}
Reduction ReplaceInt64(int64_t value) {
return Replace(Int64Constant(value));
}
Reduction ReduceInt32Div(Node* node);
Reduction ReduceUint32Div(Node* node);
Reduction ReduceInt32Mod(Node* node);
Reduction ReduceUint32Mod(Node* node);
Reduction ReduceProjection(size_t index, Node* node);
Graph* graph() const;
......
......@@ -1442,21 +1442,19 @@ TEST(17) {
CHECK_EQ(expected_, t.result);
TEST(18) {
TEST(sdiv) {
// Test the sdiv.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
typedef struct {
uint32_t dividend;
uint32_t divisor;
uint32_t result;
} T;
T t;
Assembler assm(isolate, NULL, 0);
struct T {
int32_t dividend;
int32_t divisor;
int32_t result;
} t;
if (CpuFeatures::IsSupported(SUDIV)) {
CpuFeatureScope scope(&assm, SUDIV);
......@@ -1480,6 +1478,8 @@ TEST(18) {
#endif
F3 f = FUNCTION_CAST<F3>(code->entry());
Object* dummy;
TEST_SDIV(0, kMinInt, 0);
TEST_SDIV(0, 1024, 0);
TEST_SDIV(1073741824, kMinInt, -2);
TEST_SDIV(kMinInt, kMinInt, -1);
TEST_SDIV(5, 10, 2);
......@@ -1498,6 +1498,62 @@ TEST(18) {
#undef TEST_SDIV
#define TEST_UDIV(expected_, dividend_, divisor_) \
t.dividend = dividend_; \
t.divisor = divisor_; \
t.result = 0; \
dummy = CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0); \
CHECK_EQ(expected_, t.result);
TEST(udiv) {
// Test the udiv.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Assembler assm(isolate, NULL, 0);
struct T {
uint32_t dividend;
uint32_t divisor;
uint32_t result;
} t;
if (CpuFeatures::IsSupported(SUDIV)) {
CpuFeatureScope scope(&assm, SUDIV);
__ mov(r3, Operand(r0));
__ ldr(r0, MemOperand(r3, OFFSET_OF(T, dividend)));
__ ldr(r1, MemOperand(r3, OFFSET_OF(T, divisor)));
__ sdiv(r2, r0, r1);
__ str(r2, MemOperand(r3, OFFSET_OF(T, result)));
__ bx(lr);
CodeDesc desc;
assm.GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
#ifdef DEBUG
OFStream os(stdout);
code->Print(os);
#endif
F3 f = FUNCTION_CAST<F3>(code->entry());
Object* dummy;
TEST_UDIV(0, 0, 0);
TEST_UDIV(0, 1024, 0);
TEST_UDIV(5, 10, 2);
TEST_UDIV(3, 10, 3);
USE(dummy);
}
}
#undef TEST_UDIV
TEST(smmla) {
CcTest::InitializeVM();
Isolate* const isolate = CcTest::i_isolate();
......
......@@ -224,6 +224,58 @@ TEST(Bits, SignedMulHighAndAdd32) {
}
}
TEST(Bits, SignedDiv32) {
EXPECT_EQ(std::numeric_limits<int32_t>::min(),
SignedDiv32(std::numeric_limits<int32_t>::min(), -1));
EXPECT_EQ(std::numeric_limits<int32_t>::max(),
SignedDiv32(std::numeric_limits<int32_t>::max(), 1));
TRACED_FORRANGE(int32_t, i, 0, 50) {
EXPECT_EQ(0, SignedDiv32(i, 0));
TRACED_FORRANGE(int32_t, j, 1, i) {
EXPECT_EQ(1, SignedDiv32(j, j));
EXPECT_EQ(i / j, SignedDiv32(i, j));
EXPECT_EQ(-i / j, SignedDiv32(i, -j));
}
}
}
TEST(Bits, SignedMod32) {
EXPECT_EQ(0, SignedMod32(std::numeric_limits<int32_t>::min(), -1));
EXPECT_EQ(0, SignedMod32(std::numeric_limits<int32_t>::max(), 1));
TRACED_FORRANGE(int32_t, i, 0, 50) {
EXPECT_EQ(0, SignedMod32(i, 0));
TRACED_FORRANGE(int32_t, j, 1, i) {
EXPECT_EQ(0, SignedMod32(j, j));
EXPECT_EQ(i % j, SignedMod32(i, j));
EXPECT_EQ(i % j, SignedMod32(i, -j));
}
}
}
TEST(Bits, UnsignedDiv32) {
TRACED_FORRANGE(uint32_t, i, 0, 50) {
EXPECT_EQ(0u, UnsignedDiv32(i, 0));
TRACED_FORRANGE(uint32_t, j, i + 1, 100) {
EXPECT_EQ(1u, UnsignedDiv32(j, j));
EXPECT_EQ(i / j, UnsignedDiv32(i, j));
}
}
}
TEST(Bits, UnsignedMod32) {
TRACED_FORRANGE(uint32_t, i, 0, 50) {
EXPECT_EQ(0u, UnsignedMod32(i, 0));
TRACED_FORRANGE(uint32_t, j, i + 1, 100) {
EXPECT_EQ(0u, UnsignedMod32(j, j));
EXPECT_EQ(i % j, UnsignedMod32(i, j));
}
}
}
} // namespace bits
} // namespace base
} // namespace v8
......@@ -731,19 +731,111 @@ TEST_F(MachineOperatorReducerTest, Int32DivWithConstant) {
}
TEST_F(MachineOperatorReducerTest, Int32DivWithParameters) {
Node* const p0 = Parameter(0);
Reduction const r = Reduce(graph()->NewNode(machine()->Int32Div(), p0, p0));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsWord32Equal(IsWord32Equal(p0, IsInt32Constant(0)), IsInt32Constant(0)));
}
// -----------------------------------------------------------------------------
// Uint32Div
TEST_F(MachineOperatorReducerTest, Uint32DivWithConstant) {
Node* const p0 = Parameter(0);
{
Reduction const r =
Reduce(graph()->NewNode(machine()->Uint32Div(), Int32Constant(0), p0));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsInt32Constant(0));
}
{
Reduction const r =
Reduce(graph()->NewNode(machine()->Uint32Div(), p0, Int32Constant(0)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsInt32Constant(0));
}
{
Reduction const r =
Reduce(graph()->NewNode(machine()->Uint32Div(), p0, Int32Constant(1)));
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement(), p0);
}
TRACED_FOREACH(uint32_t, dividend, kUint32Values) {
TRACED_FOREACH(uint32_t, divisor, kUint32Values) {
Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Div(),
Uint32Constant(dividend),
Uint32Constant(divisor)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsInt32Constant(bit_cast<int32_t>(
base::bits::UnsignedDiv32(dividend, divisor))));
}
}
TRACED_FORRANGE(uint32_t, shift, 1, 31) {
Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Div(), p0,
Uint32Constant(1u << shift)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsWord32Shr(p0, IsInt32Constant(bit_cast<int32_t>(shift))));
}
}
TEST_F(MachineOperatorReducerTest, Uint32DivWithParameters) {
Node* const p0 = Parameter(0);
Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Div(), p0, p0));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsWord32Equal(IsWord32Equal(p0, IsInt32Constant(0)), IsInt32Constant(0)));
}
// -----------------------------------------------------------------------------
// Int32Mod
TEST_F(MachineOperatorReducerTest, Int32ModWithConstant) {
Node* const p0 = Parameter(0);
static const int32_t kOnes[] = {-1, 1};
TRACED_FOREACH(int32_t, one, kOnes) {
{
Reduction const r =
Reduce(graph()->NewNode(machine()->Int32Mod(), Int32Constant(0), p0));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsInt32Constant(0));
}
{
Reduction const r =
Reduce(graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(0)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsInt32Constant(0));
}
{
Reduction const r =
Reduce(graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(one)));
Reduce(graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(1)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsInt32Constant(0));
}
{
Reduction const r =
Reduce(graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(-1)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsInt32Constant(0));
}
TRACED_FOREACH(int32_t, dividend, kInt32Values) {
TRACED_FOREACH(int32_t, divisor, kInt32Values) {
Reduction const r = Reduce(graph()->NewNode(machine()->Int32Mod(),
Int32Constant(dividend),
Int32Constant(divisor)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsInt32Constant(base::bits::SignedMod32(dividend, divisor)));
}
}
TRACED_FORRANGE(int32_t, shift, 1, 30) {
Reduction const r = Reduce(
graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(1 << shift)));
......@@ -797,6 +889,68 @@ TEST_F(MachineOperatorReducerTest, Int32ModWithConstant) {
}
TEST_F(MachineOperatorReducerTest, Int32ModWithParameters) {
Node* const p0 = Parameter(0);
Reduction const r = Reduce(graph()->NewNode(machine()->Int32Mod(), p0, p0));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsInt32Constant(0));
}
// -----------------------------------------------------------------------------
// Uint32Mod
TEST_F(MachineOperatorReducerTest, Uint32ModWithConstant) {
Node* const p0 = Parameter(0);
{
Reduction const r =
Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, Int32Constant(0)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsInt32Constant(0));
}
{
Reduction const r =
Reduce(graph()->NewNode(machine()->Uint32Mod(), Int32Constant(0), p0));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsInt32Constant(0));
}
{
Reduction const r =
Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, Int32Constant(1)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsInt32Constant(0));
}
TRACED_FOREACH(uint32_t, dividend, kUint32Values) {
TRACED_FOREACH(uint32_t, divisor, kUint32Values) {
Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Mod(),
Uint32Constant(dividend),
Uint32Constant(divisor)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsInt32Constant(bit_cast<int32_t>(
base::bits::UnsignedMod32(dividend, divisor))));
}
}
TRACED_FORRANGE(uint32_t, shift, 1, 31) {
Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Mod(), p0,
Uint32Constant(1u << shift)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsWord32And(p0, IsInt32Constant(
bit_cast<int32_t>((1u << shift) - 1u))));
}
}
TEST_F(MachineOperatorReducerTest, Uint32ModWithParameters) {
Node* const p0 = Parameter(0);
Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, p0));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsInt32Constant(0));
}
// -----------------------------------------------------------------------------
// Int32AddWithOverflow
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment