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;
// found in the LICENSE file.
#include "src/base/bits.h"
+
+#include <limits>
+
#include "src/base/logging.h"
namespace v8 {
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
// 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
}
+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);
}
}
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
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());
}
+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();
}
+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: {
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);
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;
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);
#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);
#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();
}
}
+
+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
}
+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)));
}
+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