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

[turbofan] Improve code generation for inline comparisons with zero.

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

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24832 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent cedd1401
......@@ -85,20 +85,10 @@ class InstructionSelector FINAL {
return Features(CpuFeatures::SupportedFeatures());
}
// Checks if {node} is currently live.
bool IsLive(Node* node) const { return !IsDefined(node) && IsUsed(node); }
private:
friend class OperandGenerator;
// ===========================================================================
// ============ Architecture-independent graph covering methods. =============
// ===========================================================================
// Checks if {block} will appear directly after {current_block_} when
// assembling code, in which case, a fall-through can be used.
bool IsNextInAssemblyOrder(const BasicBlock* block) const;
// Used in pattern matching during code generation.
// Check if {node} can be covered while generating code for the current
// instruction. A node can be covered if the {user} of the node has the only
......@@ -109,13 +99,23 @@ class InstructionSelector FINAL {
// generated for it.
bool IsDefined(Node* node) const;
// Inform the instruction selection that {node} was just defined.
void MarkAsDefined(Node* node);
// Checks if {node} has any uses, and therefore code has to be generated for
// it.
bool IsUsed(Node* node) const;
// Checks if {node} is currently live.
bool IsLive(Node* node) const { return !IsDefined(node) && IsUsed(node); }
private:
friend class OperandGenerator;
// Checks if {block} will appear directly after {current_block_} when
// assembling code, in which case, a fall-through can be used.
bool IsNextInAssemblyOrder(const BasicBlock* block) const;
// Inform the instruction selection that {node} was just defined.
void MarkAsDefined(Node* node);
// Inform the instruction selection that {node} has at least one use and we
// will need to generate code for it.
void MarkAsUsed(Node* node);
......
......@@ -243,6 +243,21 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
if (m.LeftEqualsRight()) return ReplaceBool(true); // x == x => true
break;
}
case IrOpcode::kWord64Equal: {
Int64BinopMatcher m(node);
if (m.IsFoldable()) { // K == K => K
return ReplaceBool(m.left().Value() == m.right().Value());
}
if (m.left().IsInt64Sub() && m.right().Is(0)) { // x - y == 0 => x == y
Int64BinopMatcher msub(m.left().node());
node->ReplaceInput(0, msub.left().node());
node->ReplaceInput(1, msub.right().node());
return Changed(node);
}
// TODO(turbofan): fold HeapConstant, ExternalReference, pointer compares
if (m.LeftEqualsRight()) return ReplaceBool(true); // x == x => true
break;
}
case IrOpcode::kInt32Add: {
Int32BinopMatcher m(node);
if (m.right().Is(0)) return Replace(m.left().node()); // x + 0 => x
......
......@@ -456,8 +456,8 @@ class RepresentationSelector {
if (lower()) {
MachineTypeUnion input = GetInfo(node->InputAt(0))->output;
if (input & kRepBit) {
// BooleanNot(x: kRepBit) => WordEqual(x, #0)
node->set_op(lowering->machine()->WordEqual());
// BooleanNot(x: kRepBit) => Word32Equal(x, #0)
node->set_op(lowering->machine()->Word32Equal());
node->AppendInput(jsgraph_->zone(), jsgraph_->Int32Constant(0));
} else {
// BooleanNot(x: kRepTagged) => WordEqual(x, #false)
......
......@@ -1044,7 +1044,7 @@ Bounds Typer::Visitor::TypeBooleanNot(Node* node) {
Bounds Typer::Visitor::TypeBooleanToNumber(Node* node) {
return Bounds(Type::None(zone()), Type::Number(zone()));
return Bounds(Type::None(zone()), typer_->zero_or_one);
}
......
......@@ -821,6 +821,14 @@ static void VisitWordCompare(InstructionSelector* selector, Node* node,
}
// Shared routine for comparison with zero.
static void VisitCompareZero(InstructionSelector* selector, Node* node,
InstructionCode opcode, FlagsContinuation* cont) {
X64OperandGenerator g(selector);
VisitCompare(selector, opcode, g.Use(node), g.TempImmediate(0), cont);
}
// Shared routine for multiple float64 compare operations.
static void VisitFloat64Compare(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) {
......@@ -946,16 +954,30 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
}
// Branch could not be combined with a compare, emit compare against 0.
VisitCompare(this, kX64Cmp32, g.Use(value), g.TempImmediate(0), &cont);
VisitCompareZero(this, value, kX64Cmp32, &cont);
}
void InstructionSelector::VisitWord32Equal(Node* const node) {
Node* const user = node;
Node* user = node;
FlagsContinuation cont(kEqual, node);
Int32BinopMatcher m(user);
if (m.right().Is(0)) {
Node* const value = m.left().node();
Node* value = m.left().node();
// Try to combine with comparisons against 0 by simply inverting the branch.
while (CanCover(user, value) && value->opcode() == IrOpcode::kWord32Equal) {
Int32BinopMatcher m(value);
if (m.right().Is(0)) {
user = value;
value = m.left().node();
cont.Negate();
} else {
break;
}
}
// Try to combine the branch with a comparison.
if (CanCover(user, value)) {
switch (value->opcode()) {
case IrOpcode::kInt32Sub:
......@@ -966,6 +988,7 @@ void InstructionSelector::VisitWord32Equal(Node* const node) {
break;
}
}
return VisitCompareZero(this, value, kX64Cmp32, &cont);
}
VisitWordCompare(this, node, kX64Cmp32, &cont);
}
......@@ -996,11 +1019,25 @@ void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) {
void InstructionSelector::VisitWord64Equal(Node* const node) {
Node* const user = node;
Node* user = node;
FlagsContinuation cont(kEqual, node);
Int64BinopMatcher m(user);
if (m.right().Is(0)) {
Node* const value = m.left().node();
Node* value = m.left().node();
// Try to combine with comparisons against 0 by simply inverting the branch.
while (CanCover(user, value) && value->opcode() == IrOpcode::kWord64Equal) {
Int64BinopMatcher m(value);
if (m.right().Is(0)) {
user = value;
value = m.left().node();
cont.Negate();
} else {
break;
}
}
// Try to combine the branch with a comparison.
if (CanCover(user, value)) {
switch (value->opcode()) {
case IrOpcode::kInt64Sub:
......@@ -1011,6 +1048,7 @@ void InstructionSelector::VisitWord64Equal(Node* const node) {
break;
}
}
return VisitCompareZero(this, value, kX64Cmp, &cont);
}
VisitWordCompare(this, node, kX64Cmp, &cont);
}
......
......@@ -556,6 +556,142 @@ TEST(RunInt32AddP) {
}
TEST(RunInt32AddAndWord32EqualP) {
{
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
m.Return(m.Int32Add(m.Parameter(0),
m.Word32Equal(m.Parameter(1), m.Parameter(2))));
FOR_INT32_INPUTS(i) {
FOR_INT32_INPUTS(j) {
FOR_INT32_INPUTS(k) {
// Use uint32_t because signed overflow is UB in C.
int32_t const expected =
bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j == *k));
CHECK_EQ(expected, m.Call(*i, *j, *k));
}
}
}
}
{
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
m.Return(m.Int32Add(m.Word32Equal(m.Parameter(0), m.Parameter(1)),
m.Parameter(2)));
FOR_INT32_INPUTS(i) {
FOR_INT32_INPUTS(j) {
FOR_INT32_INPUTS(k) {
// Use uint32_t because signed overflow is UB in C.
int32_t const expected =
bit_cast<int32_t>((*i == *j) + bit_cast<uint32_t>(*k));
CHECK_EQ(expected, m.Call(*i, *j, *k));
}
}
}
}
}
TEST(RunInt32AddAndWord32EqualImm) {
{
FOR_INT32_INPUTS(i) {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
m.Return(m.Int32Add(m.Int32Constant(*i),
m.Word32Equal(m.Parameter(0), m.Parameter(1))));
FOR_INT32_INPUTS(j) {
FOR_INT32_INPUTS(k) {
// Use uint32_t because signed overflow is UB in C.
int32_t const expected =
bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j == *k));
CHECK_EQ(expected, m.Call(*j, *k));
}
}
}
}
{
FOR_INT32_INPUTS(i) {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
m.Return(m.Int32Add(m.Word32Equal(m.Int32Constant(*i), m.Parameter(0)),
m.Parameter(1)));
FOR_INT32_INPUTS(j) {
FOR_INT32_INPUTS(k) {
// Use uint32_t because signed overflow is UB in C.
int32_t const expected =
bit_cast<int32_t>((*i == *j) + bit_cast<uint32_t>(*k));
CHECK_EQ(expected, m.Call(*j, *k));
}
}
}
}
}
TEST(RunInt32AddAndWord32NotEqualP) {
{
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
m.Return(m.Int32Add(m.Parameter(0),
m.Word32NotEqual(m.Parameter(1), m.Parameter(2))));
FOR_INT32_INPUTS(i) {
FOR_INT32_INPUTS(j) {
FOR_INT32_INPUTS(k) {
// Use uint32_t because signed overflow is UB in C.
int32_t const expected =
bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j != *k));
CHECK_EQ(expected, m.Call(*i, *j, *k));
}
}
}
}
{
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
m.Return(m.Int32Add(m.Word32NotEqual(m.Parameter(0), m.Parameter(1)),
m.Parameter(2)));
FOR_INT32_INPUTS(i) {
FOR_INT32_INPUTS(j) {
FOR_INT32_INPUTS(k) {
// Use uint32_t because signed overflow is UB in C.
int32_t const expected =
bit_cast<int32_t>((*i != *j) + bit_cast<uint32_t>(*k));
CHECK_EQ(expected, m.Call(*i, *j, *k));
}
}
}
}
}
TEST(RunInt32AddAndWord32NotEqualImm) {
{
FOR_INT32_INPUTS(i) {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
m.Return(m.Int32Add(m.Int32Constant(*i),
m.Word32NotEqual(m.Parameter(0), m.Parameter(1))));
FOR_INT32_INPUTS(j) {
FOR_INT32_INPUTS(k) {
// Use uint32_t because signed overflow is UB in C.
int32_t const expected =
bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j != *k));
CHECK_EQ(expected, m.Call(*j, *k));
}
}
}
}
{
FOR_INT32_INPUTS(i) {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
m.Return(m.Int32Add(m.Word32NotEqual(m.Int32Constant(*i), m.Parameter(0)),
m.Parameter(1)));
FOR_INT32_INPUTS(j) {
FOR_INT32_INPUTS(k) {
// Use uint32_t because signed overflow is UB in C.
int32_t const expected =
bit_cast<int32_t>((*i != *j) + bit_cast<uint32_t>(*k));
CHECK_EQ(expected, m.Call(*j, *k));
}
}
}
}
}
TEST(RunInt32AddAndWord32SarP) {
{
RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32, kMachUint32);
......
......@@ -791,7 +791,7 @@ TEST(LowerBooleanNot_bit_bit) {
Node* use = t.Branch(inv);
t.Lower();
Node* cmp = use->InputAt(0);
CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
CHECK_EQ(t.machine()->Word32Equal()->opcode(), cmp->opcode());
CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
Node* f = t.jsgraph.Int32Constant(0);
CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
......@@ -808,7 +808,7 @@ TEST(LowerBooleanNot_bit_tagged) {
t.Lower();
CHECK_EQ(IrOpcode::kChangeBitToBool, use->InputAt(0)->opcode());
Node* cmp = use->InputAt(0)->InputAt(0);
CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
CHECK_EQ(t.machine()->Word32Equal()->opcode(), cmp->opcode());
CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
Node* f = t.jsgraph.Int32Constant(0);
CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
......
......@@ -1371,6 +1371,113 @@ TEST_F(InstructionSelectorTest, TruncateFloat64ToFloat32WithParameter) {
}
// -----------------------------------------------------------------------------
// Comparisons.
namespace {
struct Comparison {
Constructor constructor;
const char* constructor_name;
FlagsCondition flags_condition;
FlagsCondition negated_flags_condition;
};
std::ostream& operator<<(std::ostream& os, const Comparison& cmp) {
return os << cmp.constructor_name;
}
const Comparison kComparisons[] = {
{&RawMachineAssembler::Word32Equal, "Word32Equal", kEqual, kNotEqual},
{&RawMachineAssembler::Int32LessThan, "Int32LessThan", kSignedLessThan,
kSignedGreaterThanOrEqual},
{&RawMachineAssembler::Int32LessThanOrEqual, "Int32LessThanOrEqual",
kSignedLessThanOrEqual, kSignedGreaterThan},
{&RawMachineAssembler::Uint32LessThan, "Uint32LessThan", kUnsignedLessThan,
kUnsignedGreaterThanOrEqual},
{&RawMachineAssembler::Uint32LessThanOrEqual, "Uint32LessThanOrEqual",
kUnsignedLessThanOrEqual, kUnsignedGreaterThan}};
} // namespace
typedef InstructionSelectorTestWithParam<Comparison>
InstructionSelectorComparisonTest;
TEST_P(InstructionSelectorComparisonTest, Parameters) {
const Comparison& cmp = GetParam();
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
Node* const r = (m.*cmp.constructor)(p0, p1);
m.Return(r);
Stream const s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kArmCmp, s[0]->arch_opcode());
EXPECT_EQ(kMode_Operand2_R, s[0]->addressing_mode());
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(r), s.ToVreg(s[0]->OutputAt(0)));
EXPECT_EQ(kFlags_set, s[0]->flags_mode());
EXPECT_EQ(cmp.flags_condition, s[0]->flags_condition());
}
TEST_P(InstructionSelectorComparisonTest, Word32EqualWithZero) {
{
const Comparison& cmp = GetParam();
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
Node* const r =
m.Word32Equal((m.*cmp.constructor)(p0, p1), m.Int32Constant(0));
m.Return(r);
Stream const s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kArmCmp, s[0]->arch_opcode());
EXPECT_EQ(kMode_Operand2_R, s[0]->addressing_mode());
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(r), s.ToVreg(s[0]->OutputAt(0)));
EXPECT_EQ(kFlags_set, s[0]->flags_mode());
EXPECT_EQ(cmp.negated_flags_condition, s[0]->flags_condition());
}
{
const Comparison& cmp = GetParam();
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
Node* const r =
m.Word32Equal(m.Int32Constant(0), (m.*cmp.constructor)(p0, p1));
m.Return(r);
Stream const s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kArmCmp, s[0]->arch_opcode());
EXPECT_EQ(kMode_Operand2_R, s[0]->addressing_mode());
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(r), s.ToVreg(s[0]->OutputAt(0)));
EXPECT_EQ(kFlags_set, s[0]->flags_mode());
EXPECT_EQ(cmp.negated_flags_condition, s[0]->flags_condition());
}
}
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
InstructionSelectorComparisonTest,
::testing::ValuesIn(kComparisons));
// -----------------------------------------------------------------------------
// Miscellaneous.
......
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