[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
This commit is contained in:
bmeurer@chromium.org 2014-10-23 10:22:06 +00:00
parent cedd140126
commit 80836787a3
10 changed files with 481 additions and 220 deletions

View File

@ -900,8 +900,7 @@ void VisitFloat64Compare(InstructionSelector* selector, Node* node,
// Shared routine for multiple word compare operations.
void VisitWordCompare(InstructionSelector* selector, Node* node,
InstructionCode opcode, FlagsContinuation* cont,
bool commutative) {
InstructionCode opcode, FlagsContinuation* cont) {
ArmOperandGenerator g(selector);
Int32BinopMatcher m(node);
InstructionOperand* inputs[5];
@ -915,7 +914,7 @@ void VisitWordCompare(InstructionSelector* selector, Node* node,
input_count++;
} else if (TryMatchImmediateOrShift(selector, &opcode, m.left().node(),
&input_count, &inputs[1])) {
if (!commutative) cont->Commute();
if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
inputs[0] = g.UseRegister(m.right().node());
input_count++;
} else {
@ -944,81 +943,49 @@ void VisitWordCompare(InstructionSelector* selector, Node* node,
void VisitWordCompare(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) {
VisitWordCompare(selector, node, kArmCmp, cont, false);
VisitWordCompare(selector, node, kArmCmp, cont);
}
void VisitWordTest(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) {
ArmOperandGenerator g(selector);
InstructionCode opcode =
cont->Encode(kArmTst) | AddressingModeField::encode(kMode_Operand2_R);
if (cont->IsBranch()) {
selector->Emit(opcode, nullptr, g.UseRegister(node), g.UseRegister(node),
g.Label(cont->true_block()),
g.Label(cont->false_block()))->MarkAsControl();
} else {
selector->Emit(opcode, g.DefineAsRegister(cont->result()),
g.UseRegister(node), g.UseRegister(node));
}
}
} // namespace
void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
BasicBlock* fbranch) {
ArmOperandGenerator g(this);
Node* user = branch;
Node* value = branch->InputAt(0);
FlagsContinuation cont(kNotEqual, tbranch, fbranch);
// If we can fall through to the true block, invert the branch.
if (IsNextInAssemblyOrder(tbranch)) {
cont.Negate();
cont.SwapBlocks();
}
// 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)) {
// Shared routine for word comparisons against zero.
void VisitWordCompareZero(InstructionSelector* selector, Node* user,
Node* value, FlagsContinuation* cont) {
while (selector->CanCover(user, value)) {
switch (value->opcode()) {
case IrOpcode::kWord32Equal:
cont.OverwriteAndNegateIfEqual(kEqual);
return VisitWordCompare(this, value, &cont);
case IrOpcode::kWord32Equal: {
// Combine with comparisons against 0 by simply inverting the
// continuation.
Int32BinopMatcher m(value);
if (m.right().Is(0)) {
user = value;
value = m.left().node();
cont->Negate();
continue;
}
cont->OverwriteAndNegateIfEqual(kEqual);
return VisitWordCompare(selector, value, cont);
}
case IrOpcode::kInt32LessThan:
cont.OverwriteAndNegateIfEqual(kSignedLessThan);
return VisitWordCompare(this, value, &cont);
cont->OverwriteAndNegateIfEqual(kSignedLessThan);
return VisitWordCompare(selector, value, cont);
case IrOpcode::kInt32LessThanOrEqual:
cont.OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
return VisitWordCompare(this, value, &cont);
cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
return VisitWordCompare(selector, value, cont);
case IrOpcode::kUint32LessThan:
cont.OverwriteAndNegateIfEqual(kUnsignedLessThan);
return VisitWordCompare(this, value, &cont);
cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
return VisitWordCompare(selector, value, cont);
case IrOpcode::kUint32LessThanOrEqual:
cont.OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
return VisitWordCompare(this, value, &cont);
cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
return VisitWordCompare(selector, value, cont);
case IrOpcode::kFloat64Equal:
cont.OverwriteAndNegateIfEqual(kUnorderedEqual);
return VisitFloat64Compare(this, value, &cont);
cont->OverwriteAndNegateIfEqual(kUnorderedEqual);
return VisitFloat64Compare(selector, value, cont);
case IrOpcode::kFloat64LessThan:
cont.OverwriteAndNegateIfEqual(kUnorderedLessThan);
return VisitFloat64Compare(this, value, &cont);
cont->OverwriteAndNegateIfEqual(kUnorderedLessThan);
return VisitFloat64Compare(selector, value, cont);
case IrOpcode::kFloat64LessThanOrEqual:
cont.OverwriteAndNegateIfEqual(kUnorderedLessThanOrEqual);
return VisitFloat64Compare(this, value, &cont);
cont->OverwriteAndNegateIfEqual(kUnorderedLessThanOrEqual);
return VisitFloat64Compare(selector, value, cont);
case IrOpcode::kProjection:
// Check if this is the overflow output projection of an
// <Operation>WithOverflow node.
@ -1028,16 +995,16 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
// <Operation> is either NULL, which means there's no use of the
// actual value, or was already defined, which means it is scheduled
// *AFTER* this branch).
Node* node = value->InputAt(0);
Node* result = node->FindProjection(0);
if (!result || IsDefined(result)) {
Node* const node = value->InputAt(0);
Node* const result = node->FindProjection(0);
if (!result || selector->IsDefined(result)) {
switch (node->opcode()) {
case IrOpcode::kInt32AddWithOverflow:
cont.OverwriteAndNegateIfEqual(kOverflow);
return VisitBinop(this, node, kArmAdd, kArmAdd, &cont);
cont->OverwriteAndNegateIfEqual(kOverflow);
return VisitBinop(selector, node, kArmAdd, kArmAdd, cont);
case IrOpcode::kInt32SubWithOverflow:
cont.OverwriteAndNegateIfEqual(kOverflow);
return VisitBinop(this, node, kArmSub, kArmRsb, &cont);
cont->OverwriteAndNegateIfEqual(kOverflow);
return VisitBinop(selector, node, kArmSub, kArmRsb, cont);
default:
break;
}
@ -1045,64 +1012,63 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
}
break;
case IrOpcode::kInt32Add:
return VisitWordCompare(this, value, kArmCmn, &cont, true);
return VisitWordCompare(selector, value, kArmCmn, cont);
case IrOpcode::kInt32Sub:
return VisitWordCompare(this, value, kArmCmp, &cont, false);
return VisitWordCompare(selector, value, kArmCmp, cont);
case IrOpcode::kWord32And:
return VisitWordCompare(this, value, kArmTst, &cont, true);
return VisitWordCompare(selector, value, kArmTst, cont);
case IrOpcode::kWord32Or:
return VisitBinop(this, value, kArmOrr, kArmOrr, &cont);
return VisitBinop(selector, value, kArmOrr, kArmOrr, cont);
case IrOpcode::kWord32Xor:
return VisitWordCompare(this, value, kArmTeq, &cont, true);
return VisitWordCompare(selector, value, kArmTeq, cont);
case IrOpcode::kWord32Sar:
return VisitShift(this, value, TryMatchASR, &cont);
return VisitShift(selector, value, TryMatchASR, cont);
case IrOpcode::kWord32Shl:
return VisitShift(this, value, TryMatchLSL, &cont);
return VisitShift(selector, value, TryMatchLSL, cont);
case IrOpcode::kWord32Shr:
return VisitShift(this, value, TryMatchLSR, &cont);
return VisitShift(selector, value, TryMatchLSR, cont);
case IrOpcode::kWord32Ror:
return VisitShift(this, value, TryMatchROR, &cont);
return VisitShift(selector, value, TryMatchROR, cont);
default:
break;
}
break;
}
// Branch could not be combined with a compare, emit compare against 0.
return VisitWordTest(this, value, &cont);
// Continuation could not be combined with a compare, emit compare against 0.
ArmOperandGenerator g(selector);
InstructionCode const opcode =
cont->Encode(kArmTst) | AddressingModeField::encode(kMode_Operand2_R);
InstructionOperand* const value_operand = g.UseRegister(value);
if (cont->IsBranch()) {
selector->Emit(opcode, nullptr, value_operand, value_operand,
g.Label(cont->true_block()),
g.Label(cont->false_block()))->MarkAsControl();
} else {
selector->Emit(opcode, g.DefineAsRegister(cont->result()), value_operand,
value_operand);
}
}
} // namespace
void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
BasicBlock* fbranch) {
FlagsContinuation cont(kNotEqual, tbranch, fbranch);
if (IsNextInAssemblyOrder(tbranch)) { // We can fallthru to the true block.
cont.Negate();
cont.SwapBlocks();
}
VisitWordCompareZero(this, branch, branch->InputAt(0), &cont);
}
void InstructionSelector::VisitWord32Equal(Node* const node) {
Node* const user = node;
FlagsContinuation cont(kEqual, node);
Int32BinopMatcher m(user);
Int32BinopMatcher m(node);
if (m.right().Is(0)) {
Node* const value = m.left().node();
if (CanCover(user, value)) {
switch (value->opcode()) {
case IrOpcode::kInt32Add:
return VisitWordCompare(this, value, kArmCmn, &cont, true);
case IrOpcode::kInt32Sub:
return VisitWordCompare(this, value, kArmCmp, &cont, false);
case IrOpcode::kWord32And:
return VisitWordCompare(this, value, kArmTst, &cont, true);
case IrOpcode::kWord32Or:
return VisitBinop(this, value, kArmOrr, kArmOrr, &cont);
case IrOpcode::kWord32Xor:
return VisitWordCompare(this, value, kArmTeq, &cont, true);
case IrOpcode::kWord32Sar:
return VisitShift(this, value, TryMatchASR, &cont);
case IrOpcode::kWord32Shl:
return VisitShift(this, value, TryMatchLSL, &cont);
case IrOpcode::kWord32Shr:
return VisitShift(this, value, TryMatchLSR, &cont);
case IrOpcode::kWord32Ror:
return VisitShift(this, value, TryMatchROR, &cont);
default:
break;
}
return VisitWordTest(this, value, &cont);
}
return VisitWordCompareZero(this, m.node(), m.left().node(), &cont);
}
VisitWordCompare(this, node, &cont);
}

View File

@ -647,10 +647,12 @@ void InstructionSelector::VisitCall(Node* node) {
}
namespace {
// Shared routine for multiple compare operations.
static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
InstructionOperand* left, InstructionOperand* right,
FlagsContinuation* cont) {
void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
InstructionOperand* left, InstructionOperand* right,
FlagsContinuation* cont) {
IA32OperandGenerator g(selector);
if (cont->IsBranch()) {
selector->Emit(cont->Encode(opcode), NULL, left, right,
@ -666,9 +668,9 @@ static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
// Shared routine for multiple compare operations.
static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
Node* left, Node* right, FlagsContinuation* cont,
bool commutative) {
void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
Node* left, Node* right, FlagsContinuation* cont,
bool commutative) {
IA32OperandGenerator g(selector);
if (commutative && g.CanBeBetterLeftOperand(right)) {
std::swap(left, right);
@ -677,9 +679,17 @@ static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
}
// Shared routine for multiple float compare operations.
void VisitFloat64Compare(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) {
VisitCompare(selector, kSSEFloat64Cmp, node->InputAt(0), node->InputAt(1),
cont, node->op()->HasProperty(Operator::kCommutative));
}
// Shared routine for multiple word compare operations.
static void VisitWordCompare(InstructionSelector* selector, Node* node,
InstructionCode opcode, FlagsContinuation* cont) {
void VisitWordCompare(InstructionSelector* selector, Node* node,
InstructionCode opcode, FlagsContinuation* cont) {
IA32OperandGenerator g(selector);
Node* const left = node->InputAt(0);
Node* const right = node->InputAt(1);
@ -697,67 +707,52 @@ static void VisitWordCompare(InstructionSelector* selector, Node* node,
}
// Shared routine for multiple float compare operations.
static void VisitFloat64Compare(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) {
VisitCompare(selector, kSSEFloat64Cmp, node->InputAt(0), node->InputAt(1),
cont, node->op()->HasProperty(Operator::kCommutative));
void VisitWordCompare(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) {
VisitWordCompare(selector, node, kIA32Cmp, cont);
}
void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
BasicBlock* fbranch) {
IA32OperandGenerator g(this);
Node* user = branch;
Node* value = branch->InputAt(0);
FlagsContinuation cont(kNotEqual, tbranch, fbranch);
// If we can fall through to the true block, invert the branch.
if (IsNextInAssemblyOrder(tbranch)) {
cont.Negate();
cont.SwapBlocks();
}
// 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;
}
}
// Shared routine for word comparison with zero.
void VisitWordCompareZero(InstructionSelector* selector, Node* user,
Node* value, FlagsContinuation* cont) {
// Try to combine the branch with a comparison.
if (CanCover(user, value)) {
while (selector->CanCover(user, value)) {
switch (value->opcode()) {
case IrOpcode::kWord32Equal:
cont.OverwriteAndNegateIfEqual(kEqual);
return VisitWordCompare(this, value, kIA32Cmp, &cont);
case IrOpcode::kWord32Equal: {
// Try to combine with comparisons against 0 by simply inverting the
// continuation.
Int32BinopMatcher m(value);
if (m.right().Is(0)) {
user = value;
value = m.left().node();
cont->Negate();
continue;
}
cont->OverwriteAndNegateIfEqual(kEqual);
return VisitWordCompare(selector, value, cont);
}
case IrOpcode::kInt32LessThan:
cont.OverwriteAndNegateIfEqual(kSignedLessThan);
return VisitWordCompare(this, value, kIA32Cmp, &cont);
cont->OverwriteAndNegateIfEqual(kSignedLessThan);
return VisitWordCompare(selector, value, cont);
case IrOpcode::kInt32LessThanOrEqual:
cont.OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
return VisitWordCompare(this, value, kIA32Cmp, &cont);
cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
return VisitWordCompare(selector, value, cont);
case IrOpcode::kUint32LessThan:
cont.OverwriteAndNegateIfEqual(kUnsignedLessThan);
return VisitWordCompare(this, value, kIA32Cmp, &cont);
cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
return VisitWordCompare(selector, value, cont);
case IrOpcode::kUint32LessThanOrEqual:
cont.OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
return VisitWordCompare(this, value, kIA32Cmp, &cont);
cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
return VisitWordCompare(selector, value, cont);
case IrOpcode::kFloat64Equal:
cont.OverwriteAndNegateIfEqual(kUnorderedEqual);
return VisitFloat64Compare(this, value, &cont);
cont->OverwriteAndNegateIfEqual(kUnorderedEqual);
return VisitFloat64Compare(selector, value, cont);
case IrOpcode::kFloat64LessThan:
cont.OverwriteAndNegateIfEqual(kUnorderedLessThan);
return VisitFloat64Compare(this, value, &cont);
cont->OverwriteAndNegateIfEqual(kUnorderedLessThan);
return VisitFloat64Compare(selector, value, cont);
case IrOpcode::kFloat64LessThanOrEqual:
cont.OverwriteAndNegateIfEqual(kUnorderedLessThanOrEqual);
return VisitFloat64Compare(this, value, &cont);
cont->OverwriteAndNegateIfEqual(kUnorderedLessThanOrEqual);
return VisitFloat64Compare(selector, value, cont);
case IrOpcode::kProjection:
// Check if this is the overflow output projection of an
// <Operation>WithOverflow node.
@ -769,14 +764,14 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
// *AFTER* this branch).
Node* node = value->InputAt(0);
Node* result = node->FindProjection(0);
if (result == NULL || IsDefined(result)) {
if (result == NULL || selector->IsDefined(result)) {
switch (node->opcode()) {
case IrOpcode::kInt32AddWithOverflow:
cont.OverwriteAndNegateIfEqual(kOverflow);
return VisitBinop(this, node, kIA32Add, &cont);
cont->OverwriteAndNegateIfEqual(kOverflow);
return VisitBinop(selector, node, kIA32Add, cont);
case IrOpcode::kInt32SubWithOverflow:
cont.OverwriteAndNegateIfEqual(kOverflow);
return VisitBinop(this, node, kIA32Sub, &cont);
cont->OverwriteAndNegateIfEqual(kOverflow);
return VisitBinop(selector, node, kIA32Sub, cont);
default:
break;
}
@ -784,61 +779,65 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
}
break;
case IrOpcode::kInt32Sub:
return VisitWordCompare(this, value, kIA32Cmp, &cont);
return VisitWordCompare(selector, value, cont);
case IrOpcode::kWord32And:
return VisitWordCompare(this, value, kIA32Test, &cont);
return VisitWordCompare(selector, value, kIA32Test, cont);
default:
break;
}
break;
}
// Branch could not be combined with a compare, emit compare against 0.
VisitCompare(this, kIA32Cmp, g.Use(value), g.TempImmediate(0), &cont);
// Continuation could not be combined with a compare, emit compare against 0.
IA32OperandGenerator g(selector);
VisitCompare(selector, kIA32Cmp, g.Use(value), g.TempImmediate(0), cont);
}
} // namespace
void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
BasicBlock* fbranch) {
FlagsContinuation cont(kNotEqual, tbranch, fbranch);
if (IsNextInAssemblyOrder(tbranch)) { // We can fallthru to the true block.
cont.Negate();
cont.SwapBlocks();
}
VisitWordCompareZero(this, branch, branch->InputAt(0), &cont);
}
void InstructionSelector::VisitWord32Equal(Node* const node) {
Node* const user = node;
FlagsContinuation cont(kEqual, node);
Int32BinopMatcher m(user);
Int32BinopMatcher m(node);
if (m.right().Is(0)) {
Node* const value = m.left().node();
if (CanCover(user, value)) {
switch (value->opcode()) {
case IrOpcode::kInt32Sub:
return VisitWordCompare(this, value, kIA32Cmp, &cont);
case IrOpcode::kWord32And:
return VisitWordCompare(this, value, kIA32Test, &cont);
default:
break;
}
}
return VisitWordCompareZero(this, m.node(), m.left().node(), &cont);
}
VisitWordCompare(this, node, kIA32Cmp, &cont);
VisitWordCompare(this, node, &cont);
}
void InstructionSelector::VisitInt32LessThan(Node* node) {
FlagsContinuation cont(kSignedLessThan, node);
VisitWordCompare(this, node, kIA32Cmp, &cont);
VisitWordCompare(this, node, &cont);
}
void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) {
FlagsContinuation cont(kSignedLessThanOrEqual, node);
VisitWordCompare(this, node, kIA32Cmp, &cont);
VisitWordCompare(this, node, &cont);
}
void InstructionSelector::VisitUint32LessThan(Node* node) {
FlagsContinuation cont(kUnsignedLessThan, node);
VisitWordCompare(this, node, kIA32Cmp, &cont);
VisitWordCompare(this, node, &cont);
}
void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) {
FlagsContinuation cont(kUnsignedLessThanOrEqual, node);
VisitWordCompare(this, node, kIA32Cmp, &cont);
VisitWordCompare(this, node, &cont);
}

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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));

View File

@ -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.