DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
}
+ case kArmSmmul:
+ __ smmul(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ DCHECK_EQ(LeaveCC, i.OutputSBit());
+ break;
+ case kArmSmmla:
+ __ smmla(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
+ i.InputRegister(2));
+ DCHECK_EQ(LeaveCC, i.OutputSBit());
+ break;
case kArmSdiv: {
CpuFeatureScope scope(masm(), SUDIV);
__ sdiv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
V(ArmMul) \
V(ArmMla) \
V(ArmMls) \
+ V(ArmSmmul) \
+ V(ArmSmmla) \
V(ArmSdiv) \
V(ArmUdiv) \
V(ArmMov) \
case kArmMul:
case kArmMla:
case kArmMls:
+ case kArmSmmul:
+ case kArmSmmla:
case kArmSdiv:
case kArmUdiv:
case kArmBfc:
g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
return;
}
+ if (m.left().IsInt32MulHigh() && CanCover(node, m.left().node())) {
+ Int32BinopMatcher mleft(m.left().node());
+ Emit(kArmSmmla, g.DefineAsRegister(node),
+ g.UseRegister(mleft.left().node()),
+ g.UseRegister(mleft.right().node()), g.UseRegister(m.right().node()));
+ return;
+ }
+ if (m.right().IsInt32MulHigh() && CanCover(node, m.right().node())) {
+ Int32BinopMatcher mright(m.right().node());
+ Emit(kArmSmmla, g.DefineAsRegister(node),
+ g.UseRegister(mright.left().node()),
+ g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
+ return;
+ }
VisitBinop(this, node, kArmAdd, kArmAdd);
}
}
+void InstructionSelector::VisitInt32MulHigh(Node* node) {
+ ArmOperandGenerator g(this);
+ Emit(kArmSmmul, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)),
+ g.UseRegister(node->InputAt(1)));
+}
+
+
static void EmitDiv(InstructionSelector* selector, ArchOpcode div_opcode,
ArchOpcode f64i32_opcode, ArchOpcode i32f64_opcode,
InstructionOperand* result_operand,
case kArm64Mul32:
__ Mul(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1));
break;
+ case kArm64Smull:
+ __ Smull(i.OutputRegister(), i.InputRegister32(0), i.InputRegister32(1));
+ break;
case kArm64Madd:
__ Madd(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.InputRegister(2));
V(Arm64Sub32) \
V(Arm64Mul) \
V(Arm64Mul32) \
+ V(Arm64Smull) \
V(Arm64Madd) \
V(Arm64Madd32) \
V(Arm64Msub) \
}
+void InstructionSelector::VisitInt32MulHigh(Node* node) {
+ // TODO(arm64): Can we do better here?
+ Arm64OperandGenerator g(this);
+ InstructionOperand* const smull_operand = g.TempRegister();
+ Emit(kArm64Smull, smull_operand, g.UseRegister(node->InputAt(0)),
+ g.UseRegister(node->InputAt(1)));
+ Emit(kArm64Asr, g.DefineAsRegister(node), smull_operand, g.TempImmediate(32));
+}
+
+
void InstructionSelector::VisitInt32Div(Node* node) {
VisitRRR(this, kArm64Idiv32, node);
}
__ imul(i.OutputRegister(), i.InputOperand(1));
}
break;
+ case kIA32ImulHigh:
+ __ imul(i.InputRegister(1));
+ break;
case kIA32Idiv:
__ cdq();
__ idiv(i.InputOperand(1));
V(IA32Xor) \
V(IA32Sub) \
V(IA32Imul) \
+ V(IA32ImulHigh) \
V(IA32Idiv) \
V(IA32Udiv) \
V(IA32Not) \
}
+void InstructionSelector::VisitInt32MulHigh(Node* node) {
+ IA32OperandGenerator g(this);
+ InstructionOperand* temps[] = {g.TempRegister(eax)};
+ size_t temp_count = arraysize(temps);
+ Emit(kIA32ImulHigh, g.DefineAsFixed(node, edx),
+ g.UseFixed(node->InputAt(0), eax), g.UseRegister(node->InputAt(1)),
+ temp_count, temps);
+}
+
+
static inline void VisitDiv(InstructionSelector* selector, Node* node,
ArchOpcode opcode) {
IA32OperandGenerator g(selector);
return VisitInt32SubWithOverflow(node);
case IrOpcode::kInt32Mul:
return VisitInt32Mul(node);
+ case IrOpcode::kInt32MulHigh:
+ return VisitInt32MulHigh(node);
case IrOpcode::kInt32Div:
return VisitInt32Div(node);
case IrOpcode::kInt32Mod:
#include "src/compiler/machine-operator-reducer.h"
#include "src/base/bits.h"
+#include "src/base/division-by-constant.h"
#include "src/codegen.h"
#include "src/compiler/generic-node-inl.h"
#include "src/compiler/graph.h"
}
+Node* MachineOperatorReducer::Word32And(Node* lhs, uint32_t rhs) {
+ return graph()->NewNode(machine()->Word32And(), lhs, Uint32Constant(rhs));
+}
+
+
+Node* MachineOperatorReducer::Word32Sar(Node* lhs, uint32_t rhs) {
+ return graph()->NewNode(machine()->Word32Sar(), lhs, Uint32Constant(rhs));
+}
+
+
+Node* MachineOperatorReducer::Word32Shr(Node* lhs, uint32_t rhs) {
+ return graph()->NewNode(machine()->Word32Shr(), lhs, Uint32Constant(rhs));
+}
+
+
+Node* MachineOperatorReducer::Int32Add(Node* lhs, Node* rhs) {
+ return graph()->NewNode(machine()->Int32Add(), lhs, rhs);
+}
+
+
+Node* MachineOperatorReducer::Int32Sub(Node* lhs, Node* rhs) {
+ return graph()->NewNode(machine()->Int32Sub(), lhs, rhs);
+}
+
+
+Node* MachineOperatorReducer::Int32Mul(Node* lhs, Node* rhs) {
+ return graph()->NewNode(machine()->Int32Mul(), lhs, rhs);
+}
+
+
+Node* MachineOperatorReducer::TruncatingDiv(Node* dividend, int32_t divisor) {
+ DCHECK_NE(std::numeric_limits<int32_t>::min(), divisor);
+ base::MagicNumbersForDivision<uint32_t> const mag =
+ base::SignedDivisionByConstant(bit_cast<uint32_t>(divisor));
+ Node* quotient = graph()->NewNode(machine()->Int32MulHigh(), dividend,
+ Uint32Constant(mag.multiplier));
+ if (divisor > 0 && bit_cast<int32_t>(mag.multiplier) < 0) {
+ quotient = Int32Add(quotient, dividend);
+ } else if (divisor < 0 && bit_cast<int32_t>(mag.multiplier) > 0) {
+ quotient = Int32Sub(quotient, dividend);
+ }
+ if (mag.shift) {
+ quotient = Word32Sar(quotient, mag.shift);
+ }
+ return Int32Add(quotient, Word32Shr(dividend, 31));
+}
+
+
// Perform constant folding and strength reduction on machine operators.
Reduction MachineOperatorReducer::Reduce(Node* node) {
switch (node->opcode()) {
}
break;
}
- case IrOpcode::kInt32Div: {
- Int32BinopMatcher 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().IsPowerOf2())
- // TODO(turbofan): if (m.right().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.right().Is(-1)) { // x / -1 => 0 - x
- node->set_op(machine()->Int32Sub());
- node->ReplaceInput(0, Int32Constant(0));
- node->ReplaceInput(1, m.left().node());
- return Changed(node);
- }
- break;
- }
+ 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
}
-Reduction MachineOperatorReducer::ReduceInt32Mod(Node* const node) {
+Reduction MachineOperatorReducer::ReduceInt32Div(Node* node) {
+ Int32BinopMatcher m(node);
+ 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.right().Is(-1)) { // x / -1 => 0 - x
+ node->set_op(machine()->Int32Sub());
+ node->ReplaceInput(0, Int32Constant(0));
+ node->ReplaceInput(1, m.left().node());
+ return Changed(node);
+ }
+ if (m.right().HasValue()) {
+ int32_t const divisor = m.right().Value();
+ Node* const dividend = m.left().node();
+ Node* quotient = dividend;
+ if (base::bits::IsPowerOfTwo32(Abs(divisor))) {
+ uint32_t const shift = WhichPowerOf2Abs(divisor);
+ DCHECK_NE(0, shift);
+ if (shift > 1) {
+ quotient = Word32Sar(quotient, 31);
+ }
+ quotient = Int32Add(Word32Shr(quotient, 32u - shift), dividend);
+ quotient = Word32Sar(quotient, shift);
+ } else {
+ quotient = TruncatingDiv(quotient, Abs(divisor));
+ }
+ if (divisor < 0) {
+ node->set_op(machine()->Int32Sub());
+ node->ReplaceInput(0, Int32Constant(0));
+ node->ReplaceInput(1, quotient);
+ return Changed(node);
+ }
+ return Replace(quotient);
+ }
+ 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
if (m.IsFoldable() && !m.right().Is(0)) { // K % K => K
return ReplaceInt32(m.left().Value() % m.right().Value());
}
- if (m.right().IsPowerOf2()) {
- int32_t const divisor = m.right().Value();
- Node* zero = Int32Constant(0);
- Node* mask = Int32Constant(divisor - 1);
- Node* dividend = m.left().node();
-
- Node* check = graph()->NewNode(machine()->Int32LessThan(), dividend, zero);
- Node* branch =
- graph()->NewNode(common()->Branch(), check, graph()->start());
-
- Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
- Node* neg = graph()->NewNode(
- machine()->Int32Sub(), zero,
- graph()->NewNode(
- machine()->Word32And(),
- graph()->NewNode(machine()->Int32Sub(), zero, dividend), mask));
-
- Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
- Node* pos = graph()->NewNode(machine()->Word32And(), dividend, mask);
-
- Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
- Node* phi = graph()->NewNode(common()->Phi(kMachInt32, 2), neg, pos, merge);
- return Replace(phi);
+ if (m.right().HasValue()) {
+ Node* const dividend = m.left().node();
+ int32_t const divisor = Abs(m.right().Value());
+ if (base::bits::IsPowerOfTwo32(divisor)) {
+ uint32_t const mask = divisor - 1;
+ Node* const zero = Int32Constant(0);
+
+ Node* check =
+ graph()->NewNode(machine()->Int32LessThan(), dividend, zero);
+ Node* branch =
+ graph()->NewNode(common()->Branch(), check, graph()->start());
+
+ Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+ Node* neg = Int32Sub(zero, Word32And(Int32Sub(zero, dividend), mask));
+
+ Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+ Node* pos = Word32And(dividend, mask);
+
+ Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
+ Node* phi =
+ graph()->NewNode(common()->Phi(kMachInt32, 2), neg, pos, merge);
+ return Replace(phi);
+ } else {
+ Node* quotient = TruncatingDiv(dividend, divisor);
+ node->set_op(machine()->Int32Sub());
+ DCHECK_EQ(dividend, node->InputAt(0));
+ node->ReplaceInput(1, Int32Mul(quotient, Int32Constant(divisor)));
+ return Changed(node);
+ }
}
return NoChange();
}
Node* Uint32Constant(uint32_t value) {
return Int32Constant(bit_cast<uint32_t>(value));
}
+ Node* Word32And(Node* lhs, uint32_t rhs);
+ Node* Word32Sar(Node* lhs, uint32_t rhs);
+ Node* Word32Shr(Node* lhs, uint32_t rhs);
+ Node* Int32Add(Node* lhs, Node* rhs);
+ Node* Int32Sub(Node* lhs, Node* rhs);
+ Node* Int32Mul(Node* lhs, Node* rhs);
+
+ Node* TruncatingDiv(Node* dividend, int32_t divisor);
Reduction ReplaceBool(bool value) { return ReplaceInt32(value ? 1 : 0); }
Reduction ReplaceFloat32(volatile float value) {
return Replace(Int64Constant(value));
}
+ Reduction ReduceInt32Div(Node* node);
Reduction ReduceInt32Mod(Node* node);
Reduction ReduceProjection(size_t index, Node* node);
V(Int32Sub, Operator::kNoProperties, 2, 1) \
V(Int32SubWithOverflow, Operator::kNoProperties, 2, 2) \
V(Int32Mul, Operator::kAssociative | Operator::kCommutative, 2, 1) \
+ V(Int32MulHigh, Operator::kAssociative | Operator::kCommutative, 2, 1) \
V(Int32Div, Operator::kNoProperties, 2, 1) \
V(Int32Mod, Operator::kNoProperties, 2, 1) \
V(Int32LessThan, Operator::kNoProperties, 2, 1) \
const Operator* Int32Sub();
const Operator* Int32SubWithOverflow();
const Operator* Int32Mul();
+ const Operator* Int32MulHigh();
const Operator* Int32Div();
const Operator* Int32Mod();
const Operator* Int32LessThan();
V(Int32Sub) \
V(Int32SubWithOverflow) \
V(Int32Mul) \
+ V(Int32MulHigh) \
V(Int32Div) \
V(Int32Mod) \
V(Int32LessThan) \
Node* Int32Mul(Node* a, Node* b) {
return NewNode(machine()->Int32Mul(), a, b);
}
+ Node* Int32MulHigh(Node* a, Node* b) {
+ return NewNode(machine()->Int32MulHigh(), a, b);
+ }
Node* Int32Div(Node* a, Node* b) {
return NewNode(machine()->Int32Div(), a, b);
}
case kX64Imul:
ASSEMBLE_MULT(imulq);
break;
+ case kX64ImulHigh32:
+ __ imull(i.InputRegister(1));
+ break;
case kX64Idiv32:
__ cdq();
__ idivl(i.InputRegister(1));
V(X64Sub32) \
V(X64Imul) \
V(X64Imul32) \
+ V(X64ImulHigh32) \
V(X64Idiv) \
V(X64Idiv32) \
V(X64Udiv) \
}
+void InstructionSelector::VisitInt32MulHigh(Node* node) {
+ X64OperandGenerator g(this);
+ InstructionOperand* temps[] = {g.TempRegister(rax)};
+ size_t temp_count = arraysize(temps);
+ Emit(kX64ImulHigh32, g.DefineAsFixed(node, rdx),
+ g.UseFixed(node->InputAt(0), rax), g.UseRegister(node->InputAt(1)),
+ temp_count, temps);
+}
+
+
static void VisitDiv(InstructionSelector* selector, Node* node,
ArchOpcode opcode) {
X64OperandGenerator g(selector);
TEST(CodeGenInt32Binop) {
RawMachineAssemblerTester<void> m;
- const Operator* ops[] = {
- m.machine()->Word32And(), m.machine()->Word32Or(),
- m.machine()->Word32Xor(), m.machine()->Word32Shl(),
- m.machine()->Word32Shr(), m.machine()->Word32Sar(),
- m.machine()->Word32Equal(), m.machine()->Int32Add(),
- m.machine()->Int32Sub(), m.machine()->Int32Mul(),
- m.machine()->Int32Div(), m.machine()->Uint32Div(),
- m.machine()->Int32Mod(), m.machine()->Uint32Mod(),
- m.machine()->Int32LessThan(), m.machine()->Int32LessThanOrEqual(),
- m.machine()->Uint32LessThan(), m.machine()->Uint32LessThanOrEqual(),
- NULL};
-
- for (int i = 0; ops[i] != NULL; i++) {
+ const Operator* kOps[] = {
+ m.machine()->Word32And(), m.machine()->Word32Or(),
+ m.machine()->Word32Xor(), m.machine()->Word32Shl(),
+ m.machine()->Word32Shr(), m.machine()->Word32Sar(),
+ m.machine()->Word32Equal(), m.machine()->Int32Add(),
+ m.machine()->Int32Sub(), m.machine()->Int32Mul(),
+ m.machine()->Int32MulHigh(), m.machine()->Int32Div(),
+ m.machine()->Uint32Div(), m.machine()->Int32Mod(),
+ m.machine()->Uint32Mod(), m.machine()->Int32LessThan(),
+ m.machine()->Int32LessThanOrEqual(), m.machine()->Uint32LessThan(),
+ m.machine()->Uint32LessThanOrEqual()};
+
+ for (size_t i = 0; i < arraysize(kOps); ++i) {
for (int j = 0; j < 8; j++) {
for (int k = 0; k < 8; k++) {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
Node* a = Int32Input(&m, j);
Node* b = Int32Input(&m, k);
- m.Return(m.NewNode(ops[i], a, b));
+ m.Return(m.NewNode(kOps[i], a, b));
m.GenerateCode();
}
}
}
+TEST(RunInt32MulHighP) {
+ RawMachineAssemblerTester<int32_t> m;
+ Int32BinopTester bt(&m);
+ bt.AddReturn(m.Int32MulHigh(bt.param0, bt.param1));
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ int32_t expected = static_cast<int32_t>(
+ (static_cast<int64_t>(*i) * static_cast<int64_t>(*j)) >> 32);
+ CHECK_EQ(expected, bt.call(*i, *j));
+ }
+ }
+}
+
+
TEST(RunInt32MulImm) {
{
FOR_UINT32_INPUTS(i) {
TEST(RunDeadInt32Binops) {
RawMachineAssemblerTester<int32_t> m;
- const Operator* ops[] = {
- m.machine()->Word32And(), m.machine()->Word32Or(),
- m.machine()->Word32Xor(), m.machine()->Word32Shl(),
- m.machine()->Word32Shr(), m.machine()->Word32Sar(),
- m.machine()->Word32Ror(), m.machine()->Word32Equal(),
- m.machine()->Int32Add(), m.machine()->Int32Sub(),
- m.machine()->Int32Mul(), m.machine()->Int32Div(),
- m.machine()->Uint32Div(), m.machine()->Int32Mod(),
- m.machine()->Uint32Mod(), m.machine()->Int32LessThan(),
- m.machine()->Int32LessThanOrEqual(), m.machine()->Uint32LessThan(),
- m.machine()->Uint32LessThanOrEqual(), NULL};
+ const Operator* kOps[] = {
+ m.machine()->Word32And(), m.machine()->Word32Or(),
+ m.machine()->Word32Xor(), m.machine()->Word32Shl(),
+ m.machine()->Word32Shr(), m.machine()->Word32Sar(),
+ m.machine()->Word32Ror(), m.machine()->Word32Equal(),
+ m.machine()->Int32Add(), m.machine()->Int32Sub(),
+ m.machine()->Int32Mul(), m.machine()->Int32MulHigh(),
+ m.machine()->Int32Div(), m.machine()->Uint32Div(),
+ m.machine()->Int32Mod(), m.machine()->Uint32Mod(),
+ m.machine()->Int32LessThan(), m.machine()->Int32LessThanOrEqual(),
+ m.machine()->Uint32LessThan(), m.machine()->Uint32LessThanOrEqual()};
- for (int i = 0; ops[i] != NULL; i++) {
+ for (size_t i = 0; i < arraysize(kOps); ++i) {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
- int constant = 0x55555 + i;
- m.NewNode(ops[i], m.Parameter(0), m.Parameter(1));
+ int32_t constant = static_cast<int32_t>(0x55555 + i);
+ m.NewNode(kOps[i], m.Parameter(0), m.Parameter(1));
m.Return(m.Int32Constant(constant));
CHECK_EQ(constant, m.Call(1, 1));
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var stdlib = {};
+var foreign = {};
+var heap = new ArrayBuffer(64 * 1024);
+
+function Int32Div(divisor) {
+ var name = "div_";
+ if (divisor < 0) {
+ name += "minus_";
+ }
+ name += Math.abs(divisor);
+ var m = eval("function Module(stdlib, foreign, heap) {\n"
+ + " \"use asm\";\n"
+ + " function " + name + "(dividend) {\n"
+ + " return ((dividend | 0) / " + divisor + ") | 0;\n"
+ + " }\n"
+ + " return { f: " + name + "}\n"
+ + "}; Module");
+ return m(stdlib, foreign, heap).f;
+}
+
+var divisors = [-2147483648, -32 * 1024, -1000, -16, -7, -2, -1, 0,
+ 1, 3, 4, 10, 64, 100, 1024, 2147483647];
+for (var i in divisors) {
+ var divisor = divisors[i];
+ var div = Int32Div(divisor);
+ for (var dividend = -2147483648; dividend < 2147483648; dividend += 3999773) {
+ assertEquals((dividend / divisor) | 0, div(dividend));
+ }
+}
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var stdlib = {};
+var foreign = {};
+var heap = new ArrayBuffer(64 * 1024);
+
+function Int32Mod(divisor) {
+ var name = "mod_";
+ if (divisor < 0) {
+ name += "minus_";
+ }
+ name += Math.abs(divisor);
+ var m = eval("function Module(stdlib, foreign, heap) {\n"
+ + " \"use asm\";\n"
+ + " function " + name + "(dividend) {\n"
+ + " return ((dividend | 0) % " + divisor + ") | 0;\n"
+ + " }\n"
+ + " return { f: " + name + "}\n"
+ + "}; Module");
+ return m(stdlib, foreign, heap).f;
+}
+
+var divisors = [-2147483648, -32 * 1024, -1000, -16, -7, -2, -1,
+ 1, 3, 4, 10, 64, 100, 1024, 2147483647];
+for (var i in divisors) {
+ var divisor = divisors[i];
+ var mod = Int32Mod(divisor);
+ for (var dividend = -2147483648; dividend < 2147483648; dividend += 3999773) {
+ assertEquals((dividend % divisor) | 0, mod(dividend));
+ }
+}
TEST_F(InstructionSelectorTest, Int32AddWithInt32Mul) {
{
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32, kMachInt32);
- m.Return(
- m.Int32Add(m.Parameter(0), m.Int32Mul(m.Parameter(1), m.Parameter(2))));
+ Node* const p0 = m.Parameter(0);
+ Node* const p1 = m.Parameter(1);
+ Node* const p2 = m.Parameter(2);
+ Node* const n = m.Int32Add(p0, m.Int32Mul(p1, p2));
+ m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kArmMla, s[0]->arch_opcode());
- EXPECT_EQ(3U, s[0]->InputCount());
- EXPECT_EQ(1U, s[0]->OutputCount());
+ ASSERT_EQ(3U, s[0]->InputCount());
+ EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0)));
+ EXPECT_EQ(s.ToVreg(p2), s.ToVreg(s[0]->InputAt(1)));
+ EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(2)));
+ ASSERT_EQ(1U, s[0]->OutputCount());
+ EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
}
{
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32, kMachInt32);
- m.Return(
- m.Int32Add(m.Int32Mul(m.Parameter(1), m.Parameter(2)), m.Parameter(0)));
+ Node* const p0 = m.Parameter(0);
+ Node* const p1 = m.Parameter(1);
+ Node* const p2 = m.Parameter(2);
+ Node* const n = m.Int32Add(m.Int32Mul(p1, p2), p0);
+ m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kArmMla, s[0]->arch_opcode());
- EXPECT_EQ(3U, s[0]->InputCount());
- EXPECT_EQ(1U, s[0]->OutputCount());
+ ASSERT_EQ(3U, s[0]->InputCount());
+ EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0)));
+ EXPECT_EQ(s.ToVreg(p2), s.ToVreg(s[0]->InputAt(1)));
+ EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(2)));
+ ASSERT_EQ(1U, s[0]->OutputCount());
+ EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
}
}
+TEST_F(InstructionSelectorTest, Int32AddWithInt32MulHigh) {
+ {
+ StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32, kMachInt32);
+ Node* const p0 = m.Parameter(0);
+ Node* const p1 = m.Parameter(1);
+ Node* const p2 = m.Parameter(2);
+ Node* const n = m.Int32Add(p0, m.Int32MulHigh(p1, p2));
+ m.Return(n);
+ Stream s = m.Build();
+ ASSERT_EQ(1U, s.size());
+ EXPECT_EQ(kArmSmmla, s[0]->arch_opcode());
+ ASSERT_EQ(3U, s[0]->InputCount());
+ EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0)));
+ EXPECT_EQ(s.ToVreg(p2), s.ToVreg(s[0]->InputAt(1)));
+ EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(2)));
+ ASSERT_EQ(1U, s[0]->OutputCount());
+ EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
+ }
+ {
+ StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32, kMachInt32);
+ Node* const p0 = m.Parameter(0);
+ Node* const p1 = m.Parameter(1);
+ Node* const p2 = m.Parameter(2);
+ Node* const n = m.Int32Add(m.Int32MulHigh(p1, p2), p0);
+ m.Return(n);
+ Stream s = m.Build();
+ ASSERT_EQ(1U, s.size());
+ EXPECT_EQ(kArmSmmla, s[0]->arch_opcode());
+ ASSERT_EQ(3U, s[0]->InputCount());
+ EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0)));
+ EXPECT_EQ(s.ToVreg(p2), s.ToVreg(s[0]->InputAt(1)));
+ EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(2)));
+ ASSERT_EQ(1U, s[0]->OutputCount());
+ EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
+ }
+}
+
+
+TEST_F(InstructionSelectorTest, Int32SubWithInt32Mul) {
+ StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32, kMachInt32);
+ m.Return(
+ m.Int32Sub(m.Parameter(0), m.Int32Mul(m.Parameter(1), m.Parameter(2))));
+ Stream s = m.Build();
+ ASSERT_EQ(2U, s.size());
+ EXPECT_EQ(kArmMul, s[0]->arch_opcode());
+ ASSERT_EQ(1U, s[0]->OutputCount());
+ EXPECT_EQ(kArmSub, s[1]->arch_opcode());
+ ASSERT_EQ(2U, s[1]->InputCount());
+ EXPECT_EQ(s.ToVreg(s[0]->Output()), s.ToVreg(s[1]->InputAt(1)));
+}
+
+
+TEST_F(InstructionSelectorTest, Int32SubWithInt32MulForMLS) {
+ StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32, kMachInt32);
+ m.Return(
+ m.Int32Sub(m.Parameter(0), m.Int32Mul(m.Parameter(1), m.Parameter(2))));
+ Stream s = m.Build(MLS);
+ ASSERT_EQ(1U, s.size());
+ EXPECT_EQ(kArmMls, s[0]->arch_opcode());
+ EXPECT_EQ(1U, s[0]->OutputCount());
+ EXPECT_EQ(3U, s[0]->InputCount());
+}
+
+
TEST_F(InstructionSelectorTest, Int32DivWithParameters) {
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
m.Return(m.Int32Div(m.Parameter(0), m.Parameter(1)));
}
-TEST_F(InstructionSelectorTest, Int32SubWithInt32Mul) {
- StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32, kMachInt32);
- m.Return(
- m.Int32Sub(m.Parameter(0), m.Int32Mul(m.Parameter(1), m.Parameter(2))));
+TEST_F(InstructionSelectorTest, Int32MulHighWithParameters) {
+ StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
+ Node* const p0 = m.Parameter(0);
+ Node* const p1 = m.Parameter(1);
+ Node* const n = m.Int32MulHigh(p0, p1);
+ m.Return(n);
Stream s = m.Build();
- ASSERT_EQ(2U, s.size());
- EXPECT_EQ(kArmMul, s[0]->arch_opcode());
- ASSERT_EQ(1U, s[0]->OutputCount());
- EXPECT_EQ(kArmSub, s[1]->arch_opcode());
- ASSERT_EQ(2U, s[1]->InputCount());
- EXPECT_EQ(s.ToVreg(s[0]->Output()), s.ToVreg(s[1]->InputAt(1)));
-}
-
-
-TEST_F(InstructionSelectorTest, Int32SubWithInt32MulForMLS) {
- StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32, kMachInt32);
- m.Return(
- m.Int32Sub(m.Parameter(0), m.Int32Mul(m.Parameter(1), m.Parameter(2))));
- Stream s = m.Build(MLS);
ASSERT_EQ(1U, s.size());
- EXPECT_EQ(kArmMls, s[0]->arch_opcode());
- EXPECT_EQ(1U, s[0]->OutputCount());
- EXPECT_EQ(3U, s[0]->InputCount());
+ EXPECT_EQ(kArmSmmul, s[0]->arch_opcode());
+ ASSERT_EQ(2U, s[0]->InputCount());
+ EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
+ EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
+ ASSERT_EQ(1U, s[0]->OutputCount());
+ EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
}
}
}
+
+TEST_F(InstructionSelectorTest, Int32MulHighWithParameters) {
+ StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
+ Node* const p0 = m.Parameter(0);
+ Node* const p1 = m.Parameter(1);
+ Node* const n = m.Int32MulHigh(p0, p1);
+ m.Return(n);
+ Stream s = m.Build();
+ ASSERT_EQ(2U, s.size());
+ EXPECT_EQ(kArm64Smull, s[0]->arch_opcode());
+ ASSERT_EQ(2U, s[0]->InputCount());
+ EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
+ EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
+ ASSERT_EQ(1U, s[0]->OutputCount());
+ EXPECT_EQ(kArm64Asr, s[1]->arch_opcode());
+ ASSERT_EQ(2U, s[1]->InputCount());
+ EXPECT_EQ(s.ToVreg(s[0]->Output()), s.ToVreg(s[1]->InputAt(0)));
+ EXPECT_EQ(32, s.ToInt64(s[1]->InputAt(1)));
+ ASSERT_EQ(1U, s[1]->OutputCount());
+ EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[1]->Output()));
+}
+
} // namespace compiler
} // namespace internal
} // namespace v8
IS_BINOP_MATCHER(Word32And)
IS_BINOP_MATCHER(Word32Sar)
IS_BINOP_MATCHER(Word32Shl)
+IS_BINOP_MATCHER(Word32Shr)
IS_BINOP_MATCHER(Word32Ror)
IS_BINOP_MATCHER(Word32Equal)
IS_BINOP_MATCHER(Word64And)
IS_BINOP_MATCHER(Word64Shl)
IS_BINOP_MATCHER(Word64Equal)
IS_BINOP_MATCHER(Int32AddWithOverflow)
+IS_BINOP_MATCHER(Int32Add)
IS_BINOP_MATCHER(Int32Sub)
IS_BINOP_MATCHER(Int32Mul)
+IS_BINOP_MATCHER(Int32MulHigh)
IS_BINOP_MATCHER(Int32LessThan)
IS_BINOP_MATCHER(Uint32LessThan)
IS_BINOP_MATCHER(Uint32LessThanOrEqual)
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsWord32Shl(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
+Matcher<Node*> IsWord32Shr(const Matcher<Node*>& lhs_matcher,
+ const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsWord32Ror(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsWord32Equal(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsInt32AddWithOverflow(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
+Matcher<Node*> IsInt32Add(const Matcher<Node*>& lhs_matcher,
+ const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsInt32Sub(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsInt32Mul(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
+Matcher<Node*> IsInt32MulHigh(const Matcher<Node*>& lhs_matcher,
+ const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsInt32LessThan(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsUint32LessThan(const Matcher<Node*>& lhs_matcher,
// -----------------------------------------------------------------------------
// Better left operand for commutative binops
+
TEST_F(InstructionSelectorTest, BetterLeftOperandTestAddBinop) {
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
Node* param1 = m.Parameter(0);
// -----------------------------------------------------------------------------
// Conversions.
+
TEST_F(InstructionSelectorTest, ChangeUint32ToFloat64WithParameter) {
StreamBuilder m(this, kMachFloat64, kMachUint32);
m.Return(m.ChangeUint32ToFloat64(m.Parameter(0)));
// -----------------------------------------------------------------------------
// Loads and stores
+
namespace {
struct MemoryAccess {
// -----------------------------------------------------------------------------
// AddressingMode for loads and stores.
+
class AddressingModeUnitTest : public InstructionSelectorTest {
public:
AddressingModeUnitTest() : m(NULL) { Reset(); }
// -----------------------------------------------------------------------------
// Multiplication.
+
namespace {
struct MultParam {
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorMultTest,
::testing::ValuesIn(kMultParams));
+
+TEST_F(InstructionSelectorTest, Int32MulHigh) {
+ StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
+ Node* const p0 = m.Parameter(0);
+ Node* const p1 = m.Parameter(1);
+ Node* const n = m.Int32MulHigh(p0, p1);
+ m.Return(n);
+ Stream s = m.Build();
+ ASSERT_EQ(1U, s.size());
+ EXPECT_EQ(kIA32ImulHigh, s[0]->arch_opcode());
+ ASSERT_EQ(2U, s[0]->InputCount());
+ EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
+ EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
+ ASSERT_EQ(1U, s[0]->OutputCount());
+ EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
+}
+
} // namespace compiler
} // namespace internal
} // namespace v8
// found in the LICENSE file.
#include "src/base/bits.h"
+#include "src/base/division-by-constant.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/machine-operator-reducer.h"
#include "src/compiler/typer.h"
return reducer.Reduce(node);
}
+ Matcher<Node*> IsTruncatingDiv(const Matcher<Node*>& dividend_matcher,
+ const int32_t divisor) {
+ base::MagicNumbersForDivision<uint32_t> const mag =
+ base::SignedDivisionByConstant(bit_cast<uint32_t>(divisor));
+ int32_t const multiplier = bit_cast<int32_t>(mag.multiplier);
+ int32_t const shift = bit_cast<int32_t>(mag.shift);
+ Matcher<Node*> quotient_matcher =
+ IsInt32MulHigh(dividend_matcher, IsInt32Constant(multiplier));
+ if (divisor > 0 && multiplier < 0) {
+ quotient_matcher = IsInt32Add(quotient_matcher, dividend_matcher);
+ } else if (divisor < 0 && multiplier > 0) {
+ quotient_matcher = IsInt32Sub(quotient_matcher, dividend_matcher);
+ }
+ if (shift) {
+ quotient_matcher = IsWord32Sar(quotient_matcher, IsInt32Constant(shift));
+ }
+ return IsInt32Add(quotient_matcher,
+ IsWord32Shr(dividend_matcher, IsInt32Constant(31)));
+ }
+
MachineOperatorBuilder* machine() { return &machine_; }
private:
namespace {
-static const float kFloat32Values[] = {
+const float kFloat32Values[] = {
-std::numeric_limits<float>::infinity(), -2.70497e+38f, -1.4698e+37f,
-1.22813e+35f, -1.20555e+35f, -1.34584e+34f,
-1.0079e+32f, -6.49364e+26f, -3.06077e+25f,
std::numeric_limits<float>::infinity()};
-static const double kFloat64Values[] = {
+const double kFloat64Values[] = {
-V8_INFINITY, -4.23878e+275, -5.82632e+265, -6.60355e+220, -6.26172e+212,
-2.56222e+211, -4.82408e+201, -1.84106e+157, -1.63662e+127, -1.55772e+100,
-1.67813e+72, -2.3382e+55, -3.179e+30, -1.441e+09, -1.0647e+09,
5.72883e+289, 8.5798e+290, 1.40256e+294, 1.79769e+308, V8_INFINITY};
-static const int32_t kInt32Values[] = {
- -2147483647 - 1, -1914954528, -1698749618, -1578693386, -1577976073,
- -1573998034, -1529085059, -1499540537, -1299205097, -1090814845,
- -938186388, -806828902, -750927650, -520676892, -513661538,
- -453036354, -433622833, -282638793, -28375, -27788,
- -22770, -18806, -14173, -11956, -11200,
- -10212, -8160, -3751, -2758, -1522,
- -121, -120, -118, -117, -106,
- -84, -80, -74, -59, -52,
- -48, -39, -35, -17, -11,
- -10, -9, -7, -5, 0,
- 9, 12, 17, 23, 29,
- 31, 33, 35, 40, 47,
- 55, 56, 62, 64, 67,
- 68, 69, 74, 79, 84,
- 89, 90, 97, 104, 118,
- 124, 126, 127, 7278, 17787,
- 24136, 24202, 25570, 26680, 30242,
- 32399, 420886487, 642166225, 821912648, 822577803,
- 851385718, 1212241078, 1411419304, 1589626102, 1596437184,
- 1876245816, 1954730266, 2008792749, 2045320228, 2147483647};
-
-
-static const int64_t kInt64Values[] = {
- V8_INT64_C(-9223372036854775807) - 1, V8_INT64_C(-8974392461363618006),
- V8_INT64_C(-8874367046689588135), V8_INT64_C(-8269197512118230839),
- V8_INT64_C(-8146091527100606733), V8_INT64_C(-7550917981466150848),
- V8_INT64_C(-7216590251577894337), V8_INT64_C(-6464086891160048440),
- V8_INT64_C(-6365616494908257190), V8_INT64_C(-6305630541365849726),
- V8_INT64_C(-5982222642272245453), V8_INT64_C(-5510103099058504169),
- V8_INT64_C(-5496838675802432701), V8_INT64_C(-4047626578868642657),
- V8_INT64_C(-4033755046900164544), V8_INT64_C(-3554299241457877041),
- V8_INT64_C(-2482258764588614470), V8_INT64_C(-1688515425526875335),
- V8_INT64_C(-924784137176548532), V8_INT64_C(-725316567157391307),
- V8_INT64_C(-439022654781092241), V8_INT64_C(-105545757668917080),
- V8_INT64_C(-2088319373), V8_INT64_C(-2073699916),
- V8_INT64_C(-1844949911), V8_INT64_C(-1831090548),
- V8_INT64_C(-1756711933), V8_INT64_C(-1559409497),
- V8_INT64_C(-1281179700), V8_INT64_C(-1211513985),
- V8_INT64_C(-1182371520), V8_INT64_C(-785934753),
- V8_INT64_C(-767480697), V8_INT64_C(-705745662),
- V8_INT64_C(-514362436), V8_INT64_C(-459916580),
- V8_INT64_C(-312328082), V8_INT64_C(-302949707),
- V8_INT64_C(-285499304), V8_INT64_C(-125701262),
- V8_INT64_C(-95139843), V8_INT64_C(-32768),
- V8_INT64_C(-27542), V8_INT64_C(-23600),
- V8_INT64_C(-18582), V8_INT64_C(-17770),
- V8_INT64_C(-9086), V8_INT64_C(-9010),
- V8_INT64_C(-8244), V8_INT64_C(-2890),
- V8_INT64_C(-103), V8_INT64_C(-34),
- V8_INT64_C(-27), V8_INT64_C(-25),
- V8_INT64_C(-9), V8_INT64_C(-7),
- V8_INT64_C(0), V8_INT64_C(2),
- V8_INT64_C(38), V8_INT64_C(58),
- V8_INT64_C(65), V8_INT64_C(93),
- V8_INT64_C(111), V8_INT64_C(1003),
- V8_INT64_C(1267), V8_INT64_C(12797),
- V8_INT64_C(23122), V8_INT64_C(28200),
- V8_INT64_C(30888), V8_INT64_C(42648848),
- V8_INT64_C(116836693), V8_INT64_C(263003643),
- V8_INT64_C(571039860), V8_INT64_C(1079398689),
- V8_INT64_C(1145196402), V8_INT64_C(1184846321),
- V8_INT64_C(1758281648), V8_INT64_C(1859991374),
- V8_INT64_C(1960251588), V8_INT64_C(2042443199),
- V8_INT64_C(296220586027987448), V8_INT64_C(1015494173071134726),
- V8_INT64_C(1151237951914455318), V8_INT64_C(1331941174616854174),
- V8_INT64_C(2022020418667972654), V8_INT64_C(2450251424374977035),
- V8_INT64_C(3668393562685561486), V8_INT64_C(4858229301215502171),
- V8_INT64_C(4919426235170669383), V8_INT64_C(5034286595330341762),
- V8_INT64_C(5055797915536941182), V8_INT64_C(6072389716149252074),
- V8_INT64_C(6185309910199801210), V8_INT64_C(6297328311011094138),
- V8_INT64_C(6932372858072165827), V8_INT64_C(8483640924987737210),
- V8_INT64_C(8663764179455849203), V8_INT64_C(8877197042645298254),
- V8_INT64_C(8901543506779157333), V8_INT64_C(9223372036854775807)};
-
-
-static const uint32_t kUint32Values[] = {
+const int32_t kInt32Values[] = {
+ std::numeric_limits<int32_t>::min(), -1914954528, -1698749618,
+ -1578693386, -1577976073, -1573998034,
+ -1529085059, -1499540537, -1299205097,
+ -1090814845, -938186388, -806828902,
+ -750927650, -520676892, -513661538,
+ -453036354, -433622833, -282638793,
+ -28375, -27788, -22770,
+ -18806, -14173, -11956,
+ -11200, -10212, -8160,
+ -3751, -2758, -1522,
+ -121, -120, -118,
+ -117, -106, -84,
+ -80, -74, -59,
+ -52, -48, -39,
+ -35, -17, -11,
+ -10, -9, -7,
+ -5, 0, 9,
+ 12, 17, 23,
+ 29, 31, 33,
+ 35, 40, 47,
+ 55, 56, 62,
+ 64, 67, 68,
+ 69, 74, 79,
+ 84, 89, 90,
+ 97, 104, 118,
+ 124, 126, 127,
+ 7278, 17787, 24136,
+ 24202, 25570, 26680,
+ 30242, 32399, 420886487,
+ 642166225, 821912648, 822577803,
+ 851385718, 1212241078, 1411419304,
+ 1589626102, 1596437184, 1876245816,
+ 1954730266, 2008792749, 2045320228,
+ std::numeric_limits<int32_t>::max()};
+
+
+const int64_t kInt64Values[] = {
+ std::numeric_limits<int64_t>::min(), V8_INT64_C(-8974392461363618006),
+ V8_INT64_C(-8874367046689588135), V8_INT64_C(-8269197512118230839),
+ V8_INT64_C(-8146091527100606733), V8_INT64_C(-7550917981466150848),
+ V8_INT64_C(-7216590251577894337), V8_INT64_C(-6464086891160048440),
+ V8_INT64_C(-6365616494908257190), V8_INT64_C(-6305630541365849726),
+ V8_INT64_C(-5982222642272245453), V8_INT64_C(-5510103099058504169),
+ V8_INT64_C(-5496838675802432701), V8_INT64_C(-4047626578868642657),
+ V8_INT64_C(-4033755046900164544), V8_INT64_C(-3554299241457877041),
+ V8_INT64_C(-2482258764588614470), V8_INT64_C(-1688515425526875335),
+ V8_INT64_C(-924784137176548532), V8_INT64_C(-725316567157391307),
+ V8_INT64_C(-439022654781092241), V8_INT64_C(-105545757668917080),
+ V8_INT64_C(-2088319373), V8_INT64_C(-2073699916),
+ V8_INT64_C(-1844949911), V8_INT64_C(-1831090548),
+ V8_INT64_C(-1756711933), V8_INT64_C(-1559409497),
+ V8_INT64_C(-1281179700), V8_INT64_C(-1211513985),
+ V8_INT64_C(-1182371520), V8_INT64_C(-785934753),
+ V8_INT64_C(-767480697), V8_INT64_C(-705745662),
+ V8_INT64_C(-514362436), V8_INT64_C(-459916580),
+ V8_INT64_C(-312328082), V8_INT64_C(-302949707),
+ V8_INT64_C(-285499304), V8_INT64_C(-125701262),
+ V8_INT64_C(-95139843), V8_INT64_C(-32768),
+ V8_INT64_C(-27542), V8_INT64_C(-23600),
+ V8_INT64_C(-18582), V8_INT64_C(-17770),
+ V8_INT64_C(-9086), V8_INT64_C(-9010),
+ V8_INT64_C(-8244), V8_INT64_C(-2890),
+ V8_INT64_C(-103), V8_INT64_C(-34),
+ V8_INT64_C(-27), V8_INT64_C(-25),
+ V8_INT64_C(-9), V8_INT64_C(-7),
+ V8_INT64_C(0), V8_INT64_C(2),
+ V8_INT64_C(38), V8_INT64_C(58),
+ V8_INT64_C(65), V8_INT64_C(93),
+ V8_INT64_C(111), V8_INT64_C(1003),
+ V8_INT64_C(1267), V8_INT64_C(12797),
+ V8_INT64_C(23122), V8_INT64_C(28200),
+ V8_INT64_C(30888), V8_INT64_C(42648848),
+ V8_INT64_C(116836693), V8_INT64_C(263003643),
+ V8_INT64_C(571039860), V8_INT64_C(1079398689),
+ V8_INT64_C(1145196402), V8_INT64_C(1184846321),
+ V8_INT64_C(1758281648), V8_INT64_C(1859991374),
+ V8_INT64_C(1960251588), V8_INT64_C(2042443199),
+ V8_INT64_C(296220586027987448), V8_INT64_C(1015494173071134726),
+ V8_INT64_C(1151237951914455318), V8_INT64_C(1331941174616854174),
+ V8_INT64_C(2022020418667972654), V8_INT64_C(2450251424374977035),
+ V8_INT64_C(3668393562685561486), V8_INT64_C(4858229301215502171),
+ V8_INT64_C(4919426235170669383), V8_INT64_C(5034286595330341762),
+ V8_INT64_C(5055797915536941182), V8_INT64_C(6072389716149252074),
+ V8_INT64_C(6185309910199801210), V8_INT64_C(6297328311011094138),
+ V8_INT64_C(6932372858072165827), V8_INT64_C(8483640924987737210),
+ V8_INT64_C(8663764179455849203), V8_INT64_C(8877197042645298254),
+ V8_INT64_C(8901543506779157333), std::numeric_limits<int64_t>::max()};
+
+
+const uint32_t kUint32Values[] = {
0x00000000, 0x00000001, 0xffffffff, 0x1b09788b, 0x04c5fce8, 0xcc0de5bf,
0x273a798e, 0x187937a3, 0xece3af83, 0x5495a16b, 0x0b668ecc, 0x11223344,
0x0000009e, 0x00000043, 0x0000af73, 0x0000116b, 0x00658ecc, 0x002b3b4c,
// -----------------------------------------------------------------------------
+// Int32Div
+
+
+TEST_F(MachineOperatorReducerTest, Int32DivWithConstant) {
+ Node* const p0 = Parameter(0);
+ {
+ Reduction const r =
+ Reduce(graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(0)));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_THAT(r.replacement(), IsInt32Constant(0));
+ }
+ {
+ Reduction const r =
+ Reduce(graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(1)));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_EQ(r.replacement(), p0);
+ }
+ {
+ Reduction const r =
+ Reduce(graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(-1)));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_THAT(r.replacement(), IsInt32Sub(IsInt32Constant(0), p0));
+ }
+ {
+ Reduction const r =
+ Reduce(graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(2)));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_THAT(
+ r.replacement(),
+ IsWord32Sar(IsInt32Add(IsWord32Shr(p0, IsInt32Constant(31)), p0),
+ IsInt32Constant(1)));
+ }
+ {
+ Reduction const r =
+ Reduce(graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(-2)));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_THAT(
+ r.replacement(),
+ IsInt32Sub(
+ IsInt32Constant(0),
+ IsWord32Sar(IsInt32Add(IsWord32Shr(p0, IsInt32Constant(31)), p0),
+ IsInt32Constant(1))));
+ }
+ TRACED_FORRANGE(int32_t, shift, 2, 30) {
+ Reduction const r = Reduce(
+ graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(1 << shift)));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_THAT(
+ r.replacement(),
+ IsWord32Sar(IsInt32Add(IsWord32Shr(IsWord32Sar(p0, IsInt32Constant(31)),
+ IsInt32Constant(32 - shift)),
+ p0),
+ IsInt32Constant(shift)));
+ }
+ TRACED_FORRANGE(int32_t, shift, 2, 31) {
+ Reduction const r = Reduce(graph()->NewNode(
+ machine()->Int32Div(), p0,
+ Uint32Constant(bit_cast<uint32_t, int32_t>(-1) << shift)));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_THAT(
+ r.replacement(),
+ IsInt32Sub(
+ IsInt32Constant(0),
+ IsWord32Sar(
+ IsInt32Add(IsWord32Shr(IsWord32Sar(p0, IsInt32Constant(31)),
+ IsInt32Constant(32 - shift)),
+ p0),
+ IsInt32Constant(shift))));
+ }
+ TRACED_FOREACH(int32_t, divisor, kInt32Values) {
+ if (divisor < 0) {
+ if (base::bits::IsPowerOfTwo32(-divisor)) continue;
+ Reduction const r = Reduce(
+ graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(divisor)));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_THAT(r.replacement(), IsInt32Sub(IsInt32Constant(0),
+ IsTruncatingDiv(p0, -divisor)));
+ } else if (divisor > 0) {
+ if (base::bits::IsPowerOfTwo32(divisor)) continue;
+ Reduction const r = Reduce(
+ graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(divisor)));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_THAT(r.replacement(), IsTruncatingDiv(p0, divisor));
+ }
+ }
+}
+
+
+// -----------------------------------------------------------------------------
// Int32Mod
-TEST_F(MachineOperatorReducerTest, Int32ModWithPowerOfTwo) {
- Node* p0 = Parameter(0);
- TRACED_FORRANGE(int32_t, x, 1, 30) {
- int32_t const divisor = 1 << x;
- Node* node =
- graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(divisor));
- Reduction r = Reduce(node);
+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(), p0, Int32Constant(one)));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_THAT(r.replacement(), IsInt32Constant(0));
+ }
+ TRACED_FORRANGE(int32_t, shift, 1, 30) {
+ Reduction const r = Reduce(
+ graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(1 << shift)));
ASSERT_TRUE(r.Changed());
Capture<Node*> branch;
- Node* phi = r.replacement();
- int32_t const mask = divisor - 1;
+ Node* const phi = r.replacement();
+ int32_t const mask = (1 << shift) - 1;
EXPECT_THAT(
phi, IsPhi(kMachInt32,
IsInt32Sub(IsInt32Constant(0),
IsBranch(IsInt32LessThan(p0, IsInt32Constant(0)),
graph()->start()))))));
}
+ TRACED_FORRANGE(int32_t, shift, 1, 31) {
+ Reduction const r = Reduce(graph()->NewNode(
+ machine()->Int32Mod(), p0,
+ Uint32Constant(bit_cast<uint32_t, int32_t>(-1) << shift)));
+ ASSERT_TRUE(r.Changed());
+
+ Capture<Node*> branch;
+ Node* const phi = r.replacement();
+ int32_t const mask = bit_cast<int32_t, uint32_t>((1U << shift) - 1);
+ EXPECT_THAT(
+ phi, IsPhi(kMachInt32,
+ IsInt32Sub(IsInt32Constant(0),
+ IsWord32And(IsInt32Sub(IsInt32Constant(0), p0),
+ IsInt32Constant(mask))),
+ IsWord32And(p0, IsInt32Constant(mask)),
+ IsMerge(IsIfTrue(CaptureEq(&branch)),
+ IsIfFalse(AllOf(
+ CaptureEq(&branch),
+ IsBranch(IsInt32LessThan(p0, IsInt32Constant(0)),
+ graph()->start()))))));
+ }
+ TRACED_FOREACH(int32_t, divisor, kInt32Values) {
+ if (divisor == 0 || base::bits::IsPowerOfTwo32(Abs(divisor))) continue;
+ Reduction const r = Reduce(
+ graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(divisor)));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_THAT(r.replacement(),
+ IsInt32Sub(p0, IsInt32Mul(IsTruncatingDiv(p0, Abs(divisor)),
+ IsInt32Constant(Abs(divisor)))));
+ }
}
&MachineOperatorBuilder::Name, IrOpcode::k##Name, input_count, \
output_count \
}
- PURE(Word32And, 2, 1), PURE(Word32Or, 2, 1),
- PURE(Word32Xor, 2, 1), PURE(Word32Shl, 2, 1),
- PURE(Word32Shr, 2, 1), PURE(Word32Sar, 2, 1),
- PURE(Word32Ror, 2, 1), PURE(Word32Equal, 2, 1),
- PURE(Word64And, 2, 1), PURE(Word64Or, 2, 1),
- PURE(Word64Xor, 2, 1), PURE(Word64Shl, 2, 1),
- PURE(Word64Shr, 2, 1), PURE(Word64Sar, 2, 1),
- PURE(Word64Ror, 2, 1), PURE(Word64Equal, 2, 1),
- PURE(Int32Add, 2, 1), PURE(Int32AddWithOverflow, 2, 2),
- PURE(Int32Sub, 2, 1), PURE(Int32SubWithOverflow, 2, 2),
- PURE(Int32Mul, 2, 1), PURE(Int32Div, 2, 1),
- PURE(Uint32Div, 2, 1), PURE(Int32Mod, 2, 1),
- PURE(Uint32Mod, 2, 1), PURE(Int32LessThan, 2, 1),
- PURE(Int32LessThanOrEqual, 2, 1), PURE(Uint32LessThan, 2, 1),
- PURE(Uint32LessThanOrEqual, 2, 1), PURE(Int64Add, 2, 1),
- PURE(Int64Sub, 2, 1), PURE(Int64Mul, 2, 1),
- PURE(Int64Div, 2, 1), PURE(Uint64Div, 2, 1),
- PURE(Int64Mod, 2, 1), PURE(Uint64Mod, 2, 1),
- PURE(Int64LessThan, 2, 1), PURE(Int64LessThanOrEqual, 2, 1),
- PURE(Uint64LessThan, 2, 1), PURE(ChangeFloat32ToFloat64, 1, 1),
- PURE(ChangeFloat64ToInt32, 1, 1), PURE(ChangeFloat64ToUint32, 1, 1),
- PURE(ChangeInt32ToInt64, 1, 1), PURE(ChangeUint32ToFloat64, 1, 1),
- PURE(ChangeUint32ToUint64, 1, 1), PURE(TruncateFloat64ToFloat32, 1, 1),
- PURE(TruncateFloat64ToInt32, 1, 1), PURE(TruncateInt64ToInt32, 1, 1),
- PURE(Float64Add, 2, 1), PURE(Float64Sub, 2, 1),
- PURE(Float64Mul, 2, 1), PURE(Float64Div, 2, 1),
- PURE(Float64Mod, 2, 1), PURE(Float64Sqrt, 1, 1),
- PURE(Float64Equal, 2, 1), PURE(Float64LessThan, 2, 1),
- PURE(Float64LessThanOrEqual, 2, 1), PURE(LoadStackPointer, 0, 1)
+ PURE(Word32And, 2, 1), PURE(Word32Or, 2, 1),
+ PURE(Word32Xor, 2, 1), PURE(Word32Shl, 2, 1),
+ PURE(Word32Shr, 2, 1), PURE(Word32Sar, 2, 1),
+ PURE(Word32Ror, 2, 1), PURE(Word32Equal, 2, 1),
+ PURE(Word64And, 2, 1), PURE(Word64Or, 2, 1),
+ PURE(Word64Xor, 2, 1), PURE(Word64Shl, 2, 1),
+ PURE(Word64Shr, 2, 1), PURE(Word64Sar, 2, 1),
+ PURE(Word64Ror, 2, 1), PURE(Word64Equal, 2, 1),
+ PURE(Int32Add, 2, 1), PURE(Int32AddWithOverflow, 2, 2),
+ PURE(Int32Sub, 2, 1), PURE(Int32SubWithOverflow, 2, 2),
+ PURE(Int32Mul, 2, 1), PURE(Int32MulHigh, 2, 1),
+ PURE(Int32Div, 2, 1), PURE(Uint32Div, 2, 1),
+ PURE(Int32Mod, 2, 1), PURE(Uint32Mod, 2, 1),
+ PURE(Int32LessThan, 2, 1), PURE(Int32LessThanOrEqual, 2, 1),
+ PURE(Uint32LessThan, 2, 1), PURE(Uint32LessThanOrEqual, 2, 1),
+ PURE(Int64Add, 2, 1), PURE(Int64Sub, 2, 1),
+ PURE(Int64Mul, 2, 1), PURE(Int64Div, 2, 1),
+ PURE(Uint64Div, 2, 1), PURE(Int64Mod, 2, 1),
+ PURE(Uint64Mod, 2, 1), PURE(Int64LessThan, 2, 1),
+ PURE(Int64LessThanOrEqual, 2, 1), PURE(Uint64LessThan, 2, 1),
+ PURE(ChangeFloat32ToFloat64, 1, 1), PURE(ChangeFloat64ToInt32, 1, 1),
+ PURE(ChangeFloat64ToUint32, 1, 1), PURE(ChangeInt32ToInt64, 1, 1),
+ PURE(ChangeUint32ToFloat64, 1, 1), PURE(ChangeUint32ToUint64, 1, 1),
+ PURE(TruncateFloat64ToFloat32, 1, 1), PURE(TruncateFloat64ToInt32, 1, 1),
+ PURE(TruncateInt64ToInt32, 1, 1), PURE(Float64Add, 2, 1),
+ PURE(Float64Sub, 2, 1), PURE(Float64Mul, 2, 1),
+ PURE(Float64Div, 2, 1), PURE(Float64Mod, 2, 1),
+ PURE(Float64Sqrt, 1, 1), PURE(Float64Equal, 2, 1),
+ PURE(Float64LessThan, 2, 1), PURE(Float64LessThanOrEqual, 2, 1),
+ PURE(LoadStackPointer, 0, 1)
#undef PURE
};
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorMultTest,
::testing::ValuesIn(kMultParams));
+
+TEST_F(InstructionSelectorTest, Int32MulHigh) {
+ StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
+ Node* const p0 = m.Parameter(0);
+ Node* const p1 = m.Parameter(1);
+ Node* const n = m.Int32MulHigh(p0, p1);
+ m.Return(n);
+ Stream s = m.Build();
+ ASSERT_EQ(1U, s.size());
+ EXPECT_EQ(kX64ImulHigh32, s[0]->arch_opcode());
+ ASSERT_EQ(2U, s[0]->InputCount());
+ EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
+ EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
+ ASSERT_EQ(1U, s[0]->OutputCount());
+ EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
+}
+
} // namespace compiler
} // namespace internal
} // namespace v8