// 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];
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 {
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.
// <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;
}
}
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;
+ }
+
+ // 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);
}
+}
- // Branch could not be combined with a compare, emit compare against 0.
- return VisitWordTest(this, value, &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::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);
}
}
+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,
// 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);
}
+// 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);
}
-// 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.
// *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;
}
}
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);
}
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
// 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);
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
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)
Bounds Typer::Visitor::TypeBooleanToNumber(Node* node) {
- return Bounds(Type::None(zone()), Type::Number(zone()));
+ return Bounds(Type::None(zone()), typer_->zero_or_one);
}
}
+// 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) {
}
// 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:
break;
}
}
+ return VisitCompareZero(this, value, kX64Cmp32, &cont);
}
VisitWordCompare(this, node, kX64Cmp32, &cont);
}
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:
break;
}
}
+ return VisitCompareZero(this, value, kX64Cmp, &cont);
}
VisitWordCompare(this, node, kX64Cmp, &cont);
}
}
+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);
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));
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));
// -----------------------------------------------------------------------------
+// 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.