Commit 81877a64 authored by bmeurer@chromium.org's avatar bmeurer@chromium.org

[turbofan] Optimize division/modulus by constant.

TEST=cctest,mjsunit,unittests
R=dcarney@chromium.org, svenpanne@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24595 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent f58558d9
......@@ -239,6 +239,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
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));
......
......@@ -26,6 +26,8 @@ namespace compiler {
V(ArmMul) \
V(ArmMla) \
V(ArmMls) \
V(ArmSmmul) \
V(ArmSmmla) \
V(ArmSdiv) \
V(ArmUdiv) \
V(ArmMov) \
......
......@@ -78,6 +78,8 @@ class ArmOperandGenerator : public OperandGenerator {
case kArmMul:
case kArmMla:
case kArmMls:
case kArmSmmul:
case kArmSmmla:
case kArmSdiv:
case kArmUdiv:
case kArmBfc:
......@@ -569,6 +571,20 @@ void InstructionSelector::VisitInt32Add(Node* node) {
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);
}
......@@ -612,6 +628,13 @@ void InstructionSelector::VisitInt32Mul(Node* node) {
}
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,
......
......@@ -252,6 +252,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
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));
......
......@@ -36,6 +36,7 @@ namespace compiler {
V(Arm64Sub32) \
V(Arm64Mul) \
V(Arm64Mul32) \
V(Arm64Smull) \
V(Arm64Madd) \
V(Arm64Madd32) \
V(Arm64Msub) \
......
......@@ -746,6 +746,16 @@ void InstructionSelector::VisitInt64Mul(Node* node) {
}
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);
}
......
......@@ -244,6 +244,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ imul(i.OutputRegister(), i.InputOperand(1));
}
break;
case kIA32ImulHigh:
__ imul(i.InputRegister(1));
break;
case kIA32Idiv:
__ cdq();
__ idiv(i.InputOperand(1));
......
......@@ -20,6 +20,7 @@ namespace compiler {
V(IA32Xor) \
V(IA32Sub) \
V(IA32Imul) \
V(IA32ImulHigh) \
V(IA32Idiv) \
V(IA32Udiv) \
V(IA32Not) \
......
......@@ -455,6 +455,16 @@ void InstructionSelector::VisitInt32Mul(Node* node) {
}
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);
......
......@@ -658,6 +658,8 @@ void InstructionSelector::VisitNode(Node* node) {
return VisitInt32SubWithOverflow(node);
case IrOpcode::kInt32Mul:
return VisitInt32Mul(node);
case IrOpcode::kInt32MulHigh:
return VisitInt32MulHigh(node);
case IrOpcode::kInt32Div:
return VisitInt32Div(node);
case IrOpcode::kInt32Mod:
......
......@@ -5,6 +5,7 @@
#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"
......@@ -42,6 +43,54 @@ Node* MachineOperatorReducer::Int64Constant(int64_t value) {
}
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()) {
......@@ -227,25 +276,8 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
}
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
......@@ -499,7 +531,50 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
}
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
......@@ -509,29 +584,35 @@ Reduction MachineOperatorReducer::ReduceInt32Mod(Node* const node) {
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();
}
......
......@@ -34,6 +34,14 @@ class MachineOperatorReducer FINAL : public Reducer {
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) {
......@@ -49,6 +57,7 @@ class MachineOperatorReducer FINAL : public Reducer {
return Replace(Int64Constant(value));
}
Reduction ReduceInt32Div(Node* node);
Reduction ReduceInt32Mod(Node* node);
Reduction ReduceProjection(size_t index, Node* node);
......
......@@ -75,6 +75,7 @@ StoreRepresentation const& StoreRepresentationOf(Operator const* op) {
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) \
......
......@@ -82,6 +82,7 @@ class MachineOperatorBuilder FINAL {
const Operator* Int32Sub();
const Operator* Int32SubWithOverflow();
const Operator* Int32Mul();
const Operator* Int32MulHigh();
const Operator* Int32Div();
const Operator* Int32Mod();
const Operator* Int32LessThan();
......
......@@ -185,6 +185,7 @@
V(Int32Sub) \
V(Int32SubWithOverflow) \
V(Int32Mul) \
V(Int32MulHigh) \
V(Int32Div) \
V(Int32Mod) \
V(Int32LessThan) \
......
......@@ -225,6 +225,9 @@ class RawMachineAssembler : public GraphBuilder {
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);
}
......
......@@ -283,6 +283,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kX64Imul:
ASSEMBLE_MULT(imulq);
break;
case kX64ImulHigh32:
__ imull(i.InputRegister(1));
break;
case kX64Idiv32:
__ cdq();
__ idivl(i.InputRegister(1));
......
......@@ -28,6 +28,7 @@ namespace compiler {
V(X64Sub32) \
V(X64Imul) \
V(X64Imul32) \
V(X64ImulHigh32) \
V(X64Idiv) \
V(X64Idiv32) \
V(X64Udiv) \
......
......@@ -546,6 +546,16 @@ void InstructionSelector::VisitInt64Mul(Node* node) {
}
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);
......
......@@ -58,25 +58,25 @@ static Node* Int32Input(RawMachineAssemblerTester<int32_t>* m, int index) {
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();
}
}
......@@ -1233,6 +1233,20 @@ TEST(RunInt32MulP) {
}
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) {
......@@ -2674,22 +2688,22 @@ TEST(RunDeadNodes) {
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));
......
// 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));
}
}
// 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));
}
}
......@@ -1369,27 +1369,105 @@ TEST_F(InstructionSelectorTest, TruncateFloat64ToFloat32WithParameter) {
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)));
......@@ -1554,29 +1632,20 @@ TEST_F(InstructionSelectorTest, Int32MulWithImmediate) {
}
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()));
}
......
......@@ -1759,6 +1759,28 @@ TEST_F(InstructionSelectorTest, Word64AndWithImmediateWithWord64Shr) {
}
}
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
......@@ -941,6 +941,7 @@ IS_BINOP_MATCHER(NumberSubtract)
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)
......@@ -948,8 +949,10 @@ IS_BINOP_MATCHER(Word64Sar)
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)
......
......@@ -134,6 +134,8 @@ Matcher<Node*> IsWord32Sar(const Matcher<Node*>& lhs_matcher,
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,
......@@ -148,10 +150,14 @@ Matcher<Node*> IsWord64Equal(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,
......
......@@ -103,6 +103,7 @@ TEST_F(InstructionSelectorTest, TruncateFloat64ToFloat32WithParameter) {
// -----------------------------------------------------------------------------
// Better left operand for commutative binops
TEST_F(InstructionSelectorTest, BetterLeftOperandTestAddBinop) {
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
Node* param1 = m.Parameter(0);
......@@ -136,6 +137,7 @@ TEST_F(InstructionSelectorTest, BetterLeftOperandTestMulBinop) {
// -----------------------------------------------------------------------------
// Conversions.
TEST_F(InstructionSelectorTest, ChangeUint32ToFloat64WithParameter) {
StreamBuilder m(this, kMachFloat64, kMachUint32);
m.Return(m.ChangeUint32ToFloat64(m.Parameter(0)));
......@@ -148,6 +150,7 @@ TEST_F(InstructionSelectorTest, ChangeUint32ToFloat64WithParameter) {
// -----------------------------------------------------------------------------
// Loads and stores
namespace {
struct MemoryAccess {
......@@ -295,6 +298,7 @@ INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
// -----------------------------------------------------------------------------
// AddressingMode for loads and stores.
class AddressingModeUnitTest : public InstructionSelectorTest {
public:
AddressingModeUnitTest() : m(NULL) { Reset(); }
......@@ -438,6 +442,7 @@ TEST_F(AddressingModeUnitTest, AddressingMode_MI) {
// -----------------------------------------------------------------------------
// Multiplication.
namespace {
struct MultParam {
......@@ -576,6 +581,23 @@ TEST_P(InstructionSelectorMultTest, MultAdd32) {
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
......@@ -173,35 +173,36 @@ const PureOperator kPureOperators[] = {
&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
};
......
......@@ -498,6 +498,23 @@ TEST_P(InstructionSelectorMultTest, MultAdd64) {
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
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