[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:
parent
cedd140126
commit
80836787a3
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user