Merge V8 5.3.332.45. DO NOT MERGE
Test: Manual
FPIIM-449
Change-Id: Id3254828b068abdea3cb10442e0172a8c9a98e03
(cherry picked from commit 13e2dadd00298019ed862f2b2fc5068bba730bcf)
diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc
index 240a4f2..637acac 100644
--- a/src/compiler/arm64/instruction-selector-arm64.cc
+++ b/src/compiler/arm64/instruction-selector-arm64.cc
@@ -256,36 +256,96 @@
}
}
+// Bitfields describing binary operator properties:
+// CanCommuteField is true if we can switch the two operands, potentially
+// requiring commuting the flags continuation condition.
+typedef BitField8<bool, 1, 1> CanCommuteField;
+// MustCommuteCondField is true when we need to commute the flags continuation
+// condition in order to switch the operands.
+typedef BitField8<bool, 2, 1> MustCommuteCondField;
+// IsComparisonField is true when the operation is a comparison and has no other
+// result other than the condition.
+typedef BitField8<bool, 3, 1> IsComparisonField;
+// IsAddSubField is true when an instruction is encoded as ADD or SUB.
+typedef BitField8<bool, 4, 1> IsAddSubField;
+
+// Get properties of a binary operator.
+uint8_t GetBinopProperties(InstructionCode opcode) {
+ uint8_t result = 0;
+ switch (opcode) {
+ case kArm64Cmp32:
+ case kArm64Cmp:
+ // We can commute CMP by switching the inputs and commuting
+ // the flags continuation.
+ result = CanCommuteField::update(result, true);
+ result = MustCommuteCondField::update(result, true);
+ result = IsComparisonField::update(result, true);
+ // The CMP and CMN instructions are encoded as SUB or ADD
+ // with zero output register, and therefore support the same
+ // operand modes.
+ result = IsAddSubField::update(result, true);
+ break;
+ case kArm64Cmn32:
+ case kArm64Cmn:
+ result = CanCommuteField::update(result, true);
+ result = IsComparisonField::update(result, true);
+ result = IsAddSubField::update(result, true);
+ break;
+ case kArm64Add32:
+ case kArm64Add:
+ result = CanCommuteField::update(result, true);
+ result = IsAddSubField::update(result, true);
+ break;
+ case kArm64Sub32:
+ case kArm64Sub:
+ result = IsAddSubField::update(result, true);
+ break;
+ case kArm64Tst32:
+ case kArm64Tst:
+ result = CanCommuteField::update(result, true);
+ result = IsComparisonField::update(result, true);
+ break;
+ case kArm64And32:
+ case kArm64And:
+ case kArm64Or32:
+ case kArm64Or:
+ case kArm64Eor32:
+ case kArm64Eor:
+ result = CanCommuteField::update(result, true);
+ break;
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+ DCHECK_IMPLIES(MustCommuteCondField::decode(result),
+ CanCommuteField::decode(result));
+ return result;
+}
+
// Shared routine for multiple binary operations.
template <typename Matcher>
void VisitBinop(InstructionSelector* selector, Node* node,
InstructionCode opcode, ImmediateMode operand_mode,
FlagsContinuation* cont) {
Arm64OperandGenerator g(selector);
- Matcher m(node);
InstructionOperand inputs[5];
size_t input_count = 0;
InstructionOperand outputs[2];
size_t output_count = 0;
- bool is_cmp = (opcode == kArm64Cmp32) || (opcode == kArm64Cmn32);
- // We can commute cmp by switching the inputs and commuting the flags
- // continuation.
- bool can_commute = m.HasProperty(Operator::kCommutative) || is_cmp;
+ Node* left_node = node->InputAt(0);
+ Node* right_node = node->InputAt(1);
- // The cmp and cmn instructions are encoded as sub or add with zero output
- // register, and therefore support the same operand modes.
- bool is_add_sub = m.IsInt32Add() || m.IsInt64Add() || m.IsInt32Sub() ||
- m.IsInt64Sub() || is_cmp;
-
- Node* left_node = m.left().node();
- Node* right_node = m.right().node();
+ uint8_t properties = GetBinopProperties(opcode);
+ bool can_commute = CanCommuteField::decode(properties);
+ bool must_commute_cond = MustCommuteCondField::decode(properties);
+ bool is_add_sub = IsAddSubField::decode(properties);
if (g.CanBeImmediate(right_node, operand_mode)) {
inputs[input_count++] = g.UseRegister(left_node);
inputs[input_count++] = g.UseImmediate(right_node);
- } else if (is_cmp && g.CanBeImmediate(left_node, operand_mode)) {
- cont->Commute();
+ } else if (can_commute && g.CanBeImmediate(left_node, operand_mode)) {
+ if (must_commute_cond) cont->Commute();
inputs[input_count++] = g.UseRegister(right_node);
inputs[input_count++] = g.UseImmediate(left_node);
} else if (is_add_sub &&
@@ -295,7 +355,7 @@
} else if (is_add_sub && can_commute &&
TryMatchAnyExtend(&g, selector, node, right_node, left_node,
&inputs[0], &inputs[1], &opcode)) {
- if (is_cmp) cont->Commute();
+ if (must_commute_cond) cont->Commute();
input_count += 2;
} else if (TryMatchAnyShift(selector, node, right_node, &opcode,
!is_add_sub)) {
@@ -305,7 +365,7 @@
inputs[input_count++] = g.UseImmediate(m_shift.right().node());
} else if (can_commute && TryMatchAnyShift(selector, node, left_node, &opcode,
!is_add_sub)) {
- if (is_cmp) cont->Commute();
+ if (must_commute_cond) cont->Commute();
Matcher m_shift(left_node);
inputs[input_count++] = g.UseRegisterOrImmediateZero(right_node);
inputs[input_count++] = g.UseRegister(m_shift.left().node());
@@ -320,7 +380,7 @@
inputs[input_count++] = g.Label(cont->false_block());
}
- if (!is_cmp) {
+ if (!IsComparisonField::decode(properties)) {
outputs[output_count++] = g.DefineAsRegister(node);
}
@@ -329,7 +389,7 @@
}
DCHECK_NE(0u, input_count);
- DCHECK((output_count != 0) || is_cmp);
+ DCHECK((output_count != 0) || IsComparisonField::decode(properties));
DCHECK_GE(arraysize(inputs), input_count);
DCHECK_GE(arraysize(outputs), output_count);
@@ -593,6 +653,17 @@
UNREACHABLE();
return;
}
+ // If the length is a constant power of two, allow the code generator to
+ // pick a more efficient bounds check sequence by passing the length as an
+ // immediate.
+ if (length->opcode() == IrOpcode::kInt32Constant) {
+ Int32Matcher m(length);
+ if (m.IsPowerOf2()) {
+ Emit(opcode, g.DefineAsRegister(node), g.UseRegister(buffer),
+ g.UseRegister(offset), g.UseImmediate(length));
+ return;
+ }
+ }
Emit(opcode, g.DefineAsRegister(node), g.UseRegister(buffer),
g.UseRegister(offset), g.UseOperand(length, kArithmeticImm));
}
@@ -632,6 +703,17 @@
UNREACHABLE();
return;
}
+ // If the length is a constant power of two, allow the code generator to
+ // pick a more efficient bounds check sequence by passing the length as an
+ // immediate.
+ if (length->opcode() == IrOpcode::kInt32Constant) {
+ Int32Matcher m(length);
+ if (m.IsPowerOf2()) {
+ Emit(opcode, g.NoOutput(), g.UseRegister(buffer), g.UseRegister(offset),
+ g.UseImmediate(length), g.UseRegisterOrImmediateZero(value));
+ return;
+ }
+ }
Emit(opcode, g.NoOutput(), g.UseRegister(buffer), g.UseRegister(offset),
g.UseOperand(length, kArithmeticImm),
g.UseRegisterOrImmediateZero(value));
@@ -1665,7 +1747,6 @@
VisitRR(this, kArm64Float64Abs, node);
}
-
void InstructionSelector::VisitFloat32Sqrt(Node* node) {
VisitRR(this, kArm64Float32Sqrt, node);
}
@@ -1720,6 +1801,28 @@
VisitRR(this, kArm64Float64RoundTiesEven, node);
}
+void InstructionSelector::VisitFloat32Neg(Node* node) {
+ VisitRR(this, kArm64Float32Neg, node);
+}
+
+void InstructionSelector::VisitFloat64Neg(Node* node) {
+ VisitRR(this, kArm64Float64Neg, node);
+}
+
+void InstructionSelector::VisitFloat64Ieee754Binop(Node* node,
+ InstructionCode opcode) {
+ Arm64OperandGenerator g(this);
+ Emit(opcode, g.DefineAsFixed(node, d0), g.UseFixed(node->InputAt(0), d0),
+ g.UseFixed(node->InputAt(1), d1))
+ ->MarkAsCall();
+}
+
+void InstructionSelector::VisitFloat64Ieee754Unop(Node* node,
+ InstructionCode opcode) {
+ Arm64OperandGenerator g(this);
+ Emit(opcode, g.DefineAsFixed(node, d0), g.UseFixed(node->InputAt(0), d0))
+ ->MarkAsCall();
+}
void InstructionSelector::EmitPrepareArguments(
ZoneVector<PushParameter>* arguments, const CallDescriptor* descriptor,
@@ -1853,6 +1956,23 @@
VisitWordTest(selector, node, kArm64Tst, cont);
}
+template <typename Matcher, ArchOpcode kOpcode>
+bool TryEmitTestAndBranch(InstructionSelector* selector, Node* node,
+ FlagsContinuation* cont) {
+ Arm64OperandGenerator g(selector);
+ Matcher m(node);
+ if (cont->IsBranch() && m.right().HasValue() &&
+ (base::bits::CountPopulation(m.right().Value()) == 1)) {
+ // If the mask has only one bit set, we can use tbz/tbnz.
+ DCHECK((cont->condition() == kEqual) || (cont->condition() == kNotEqual));
+ selector->Emit(
+ cont->Encode(kOpcode), g.NoOutput(), g.UseRegister(m.left().node()),
+ g.TempImmediate(base::bits::CountTrailingZeros(m.right().Value())),
+ g.Label(cont->true_block()), g.Label(cont->false_block()));
+ return true;
+ }
+ return false;
+}
// Shared routine for multiple float32 compare operations.
void VisitFloat32Compare(InstructionSelector* selector, Node* node,
@@ -1897,6 +2017,8 @@
while (selector->CanCover(user, value)) {
switch (value->opcode()) {
case IrOpcode::kWord32Equal: {
+ // Combine with comparisons against 0 by simply inverting the
+ // continuation.
Int32BinopMatcher m(value);
if (m.right().Is(0)) {
user = value;
@@ -1919,10 +2041,33 @@
case IrOpcode::kUint32LessThanOrEqual:
cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
return VisitWord32Compare(selector, value, cont);
- case IrOpcode::kWord64Equal:
+ case IrOpcode::kWord64Equal: {
cont->OverwriteAndNegateIfEqual(kEqual);
+ Int64BinopMatcher m(value);
+ if (m.right().Is(0)) {
+ Node* const left = m.left().node();
+ if (selector->CanCover(value, left) &&
+ left->opcode() == IrOpcode::kWord64And) {
+ // Attempt to merge the Word64Equal(Word64And(x, y), 0) comparison
+ // into a tbz/tbnz instruction.
+ if (TryEmitTestAndBranch<Uint64BinopMatcher, kArm64TestAndBranch>(
+ selector, left, cont)) {
+ return;
+ }
+ return VisitWordCompare(selector, left, kArm64Tst, cont, true,
+ kLogical64Imm);
+ }
+ // Merge the Word64Equal(x, 0) comparison into a cbz instruction.
+ if (cont->IsBranch()) {
+ selector->Emit(cont->Encode(kArm64CompareAndBranch), g.NoOutput(),
+ g.UseRegister(left), g.Label(cont->true_block()),
+ g.Label(cont->false_block()));
+ return;
+ }
+ }
return VisitWordCompare(selector, value, kArm64Cmp, cont, false,
kArithmeticImm);
+ }
case IrOpcode::kInt64LessThan:
cont->OverwriteAndNegateIfEqual(kSignedLessThan);
return VisitWordCompare(selector, value, kArm64Cmp, cont, false,
@@ -1997,42 +2142,20 @@
kArithmeticImm);
case IrOpcode::kInt32Sub:
return VisitWord32Compare(selector, value, cont);
- case IrOpcode::kWord32And: {
- Int32BinopMatcher m(value);
- if (cont->IsBranch() && m.right().HasValue() &&
- (base::bits::CountPopulation32(m.right().Value()) == 1)) {
- // If the mask has only one bit set, we can use tbz/tbnz.
- DCHECK((cont->condition() == kEqual) ||
- (cont->condition() == kNotEqual));
- selector->Emit(
- cont->Encode(kArm64TestAndBranch32), g.NoOutput(),
- g.UseRegister(m.left().node()),
- g.TempImmediate(
- base::bits::CountTrailingZeros32(m.right().Value())),
- g.Label(cont->true_block()), g.Label(cont->false_block()));
+ case IrOpcode::kWord32And:
+ if (TryEmitTestAndBranch<Uint32BinopMatcher, kArm64TestAndBranch32>(
+ selector, value, cont)) {
return;
}
return VisitWordCompare(selector, value, kArm64Tst32, cont, true,
kLogical32Imm);
- }
- case IrOpcode::kWord64And: {
- Int64BinopMatcher m(value);
- if (cont->IsBranch() && m.right().HasValue() &&
- (base::bits::CountPopulation64(m.right().Value()) == 1)) {
- // If the mask has only one bit set, we can use tbz/tbnz.
- DCHECK((cont->condition() == kEqual) ||
- (cont->condition() == kNotEqual));
- selector->Emit(
- cont->Encode(kArm64TestAndBranch), g.NoOutput(),
- g.UseRegister(m.left().node()),
- g.TempImmediate(
- base::bits::CountTrailingZeros64(m.right().Value())),
- g.Label(cont->true_block()), g.Label(cont->false_block()));
+ case IrOpcode::kWord64And:
+ if (TryEmitTestAndBranch<Uint64BinopMatcher, kArm64TestAndBranch>(
+ selector, value, cont)) {
return;
}
return VisitWordCompare(selector, value, kArm64Tst, cont, true,
kLogical64Imm);
- }
default:
break;
}
@@ -2338,6 +2461,10 @@
g.UseRegister(left), g.UseRegister(right));
}
+void InstructionSelector::VisitFloat64SilenceNaN(Node* node) {
+ VisitRR(this, kArm64Float64SilenceNaN, node);
+}
+
void InstructionSelector::VisitAtomicLoad(Node* node) {
LoadRepresentation load_rep = LoadRepresentationOf(node->op());
Arm64OperandGenerator g(this);
@@ -2414,7 +2541,16 @@
MachineOperatorBuilder::kInt32DivIsSafe |
MachineOperatorBuilder::kUint32DivIsSafe |
MachineOperatorBuilder::kWord32ReverseBits |
- MachineOperatorBuilder::kWord64ReverseBits;
+ MachineOperatorBuilder::kWord64ReverseBits |
+ MachineOperatorBuilder::kFloat32Neg |
+ MachineOperatorBuilder::kFloat64Neg;
+}
+
+// static
+MachineOperatorBuilder::AlignmentRequirements
+InstructionSelector::AlignmentRequirements() {
+ return MachineOperatorBuilder::AlignmentRequirements::
+ FullUnalignedAccessSupport();
}
} // namespace compiler