Commit 11261f42 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Support HOLEY_DOUBLE_ELEMENTS for Array#find() and findIndex().

This adds the missing support for HOLEY_DOUBLE_ELEMENTS to both
`Array#find()` and `Array#findIndex()`. The implementation just deopts
whenever it hits a double hole. In order to prevent deoptimization
loops we add feedback to the CheckFloat64Hole operator, which also
addresses the TODO in the `%ArrayIteratorPrototype%.next()` lowering.

This provides a speed-up of up to 8x in microbenchmarks when using
`Array#find()` or `Array#findIndex()` on HOLEY_DOUBLE_ELEMENTS arrays.

Bug: chromium:791045, v8:1956, v8:6587, v8:7165, v8:8015
Change-Id: I1be22d3fcba56c676a81dc31a9042f8123ef3a55
Reviewed-on: https://chromium-review.googlesource.com/1183906Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55321}
parent b8705eb6
...@@ -3311,15 +3311,16 @@ Node* EffectControlLinearizer::LowerCheckFloat64Hole(Node* node, ...@@ -3311,15 +3311,16 @@ Node* EffectControlLinearizer::LowerCheckFloat64Hole(Node* node,
// If we reach this point w/o eliminating the {node} that's marked // If we reach this point w/o eliminating the {node} that's marked
// with allow-return-hole, we cannot do anything, so just deoptimize // with allow-return-hole, we cannot do anything, so just deoptimize
// in case of the hole NaN (similar to Crankshaft). // in case of the hole NaN (similar to Crankshaft).
CheckFloat64HoleParameters const& params =
CheckFloat64HoleParametersOf(node->op());
Node* value = node->InputAt(0); Node* value = node->InputAt(0);
Node* check = __ Word32Equal(__ Float64ExtractHighWord32(value), Node* check = __ Word32Equal(__ Float64ExtractHighWord32(value),
__ Int32Constant(kHoleNanUpper32)); __ Int32Constant(kHoleNanUpper32));
__ DeoptimizeIf(DeoptimizeReason::kHole, VectorSlotPair(), check, __ DeoptimizeIf(DeoptimizeReason::kHole, params.feedback(), check,
frame_state); frame_state);
return value; return value;
} }
Node* EffectControlLinearizer::LowerCheckNotTaggedHole(Node* node, Node* EffectControlLinearizer::LowerCheckNotTaggedHole(Node* node,
Node* frame_state) { Node* frame_state) {
Node* value = node->InputAt(0); Node* value = node->InputAt(0);
......
...@@ -1992,11 +1992,6 @@ Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant, ...@@ -1992,11 +1992,6 @@ Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant,
const ElementsKind kind = receiver_maps[0]->elements_kind(); const ElementsKind kind = receiver_maps[0]->elements_kind();
// TODO(pwong): Handle holey double elements kinds.
if (IsDoubleElementsKind(kind) && IsHoleyElementsKind(kind)) {
return NoChange();
}
for (Handle<Map> receiver_map : receiver_maps) { for (Handle<Map> receiver_map : receiver_maps) {
if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map))
return NoChange(); return NoChange();
...@@ -2082,12 +2077,15 @@ Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant, ...@@ -2082,12 +2077,15 @@ Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant,
graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
// Replace holes with undefined. // Replace holes with undefined.
if (IsHoleyElementsKind(kind)) { if (kind == HOLEY_DOUBLE_ELEMENTS) {
element = graph()->NewNode( // TODO(7409): avoid deopt if not all uses of value are truncated.
common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse), CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole;
graph()->NewNode(simplified()->ReferenceEqual(), element, element = effect =
jsgraph()->TheHoleConstant()), graph()->NewNode(simplified()->CheckFloat64Hole(mode, p.feedback()),
jsgraph()->UndefinedConstant(), element); element, effect, control);
} else if (IsHoleyElementsKind(kind)) {
element =
graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), element);
} }
Node* if_found_return_value = Node* if_found_return_value =
...@@ -5109,7 +5107,8 @@ Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) { ...@@ -5109,7 +5107,8 @@ Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) {
// TODO(6587): avoid deopt if not all uses of value are truncated. // TODO(6587): avoid deopt if not all uses of value are truncated.
CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole; CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole;
value_true = etrue = graph()->NewNode( value_true = etrue = graph()->NewNode(
simplified()->CheckFloat64Hole(mode), value_true, etrue, if_true); simplified()->CheckFloat64Hole(mode, p.feedback()), value_true,
etrue, if_true);
} }
} }
......
...@@ -2534,10 +2534,10 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -2534,10 +2534,10 @@ JSNativeContextSpecialization::BuildElementAccess(
} else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) { } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) {
// Return the signaling NaN hole directly if all uses are // Return the signaling NaN hole directly if all uses are
// truncating. // truncating.
vtrue = etrue = vtrue = etrue = graph()->NewNode(
graph()->NewNode(simplified()->CheckFloat64Hole( simplified()->CheckFloat64Hole(
CheckFloat64HoleMode::kAllowReturnHole), CheckFloat64HoleMode::kAllowReturnHole, VectorSlotPair()),
vtrue, etrue, if_true); vtrue, etrue, if_true);
} }
} }
...@@ -2585,7 +2585,8 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -2585,7 +2585,8 @@ JSNativeContextSpecialization::BuildElementAccess(
mode = CheckFloat64HoleMode::kAllowReturnHole; mode = CheckFloat64HoleMode::kAllowReturnHole;
} }
value = effect = graph()->NewNode( value = effect = graph()->NewNode(
simplified()->CheckFloat64Hole(mode), value, effect, control); simplified()->CheckFloat64Hole(mode, VectorSlotPair()), value,
effect, control);
} }
} }
} else { } else {
......
...@@ -2973,7 +2973,8 @@ class RepresentationSelector { ...@@ -2973,7 +2973,8 @@ class RepresentationSelector {
if (input_type.Is(Type::Number())) { if (input_type.Is(Type::Number())) {
VisitNoop(node, truncation); VisitNoop(node, truncation);
} else { } else {
CheckFloat64HoleMode mode = CheckFloat64HoleModeOf(node->op()); CheckFloat64HoleMode mode =
CheckFloat64HoleParametersOf(node->op()).mode();
switch (mode) { switch (mode) {
case CheckFloat64HoleMode::kAllowReturnHole: case CheckFloat64HoleMode::kAllowReturnHole:
if (truncation.IsUnused()) return VisitUnused(node); if (truncation.IsUnused()) return VisitUnused(node);
......
...@@ -166,9 +166,31 @@ std::ostream& operator<<(std::ostream& os, CheckFloat64HoleMode mode) { ...@@ -166,9 +166,31 @@ std::ostream& operator<<(std::ostream& os, CheckFloat64HoleMode mode) {
UNREACHABLE(); UNREACHABLE();
} }
CheckFloat64HoleMode CheckFloat64HoleModeOf(const Operator* op) { CheckFloat64HoleParameters const& CheckFloat64HoleParametersOf(
Operator const* op) {
DCHECK_EQ(IrOpcode::kCheckFloat64Hole, op->opcode()); DCHECK_EQ(IrOpcode::kCheckFloat64Hole, op->opcode());
return OpParameter<CheckFloat64HoleMode>(op); return OpParameter<CheckFloat64HoleParameters>(op);
}
std::ostream& operator<<(std::ostream& os,
CheckFloat64HoleParameters const& params) {
os << params.mode();
if (params.feedback().IsValid()) os << "; " << params.feedback();
return os;
}
size_t hash_value(const CheckFloat64HoleParameters& params) {
return base::hash_combine(params.mode(), params.feedback());
}
bool operator==(CheckFloat64HoleParameters const& lhs,
CheckFloat64HoleParameters const& rhs) {
return lhs.mode() == rhs.mode() && lhs.feedback() == rhs.feedback();
}
bool operator!=(CheckFloat64HoleParameters const& lhs,
CheckFloat64HoleParameters const& rhs) {
return !(lhs == rhs);
} }
CheckForMinusZeroMode CheckMinusZeroModeOf(const Operator* op) { CheckForMinusZeroMode CheckMinusZeroModeOf(const Operator* op) {
...@@ -1005,12 +1027,13 @@ struct SimplifiedOperatorGlobalCache final { ...@@ -1005,12 +1027,13 @@ struct SimplifiedOperatorGlobalCache final {
template <CheckFloat64HoleMode kMode> template <CheckFloat64HoleMode kMode>
struct CheckFloat64HoleNaNOperator final struct CheckFloat64HoleNaNOperator final
: public Operator1<CheckFloat64HoleMode> { : public Operator1<CheckFloat64HoleParameters> {
CheckFloat64HoleNaNOperator() CheckFloat64HoleNaNOperator()
: Operator1<CheckFloat64HoleMode>( : Operator1<CheckFloat64HoleParameters>(
IrOpcode::kCheckFloat64Hole, IrOpcode::kCheckFloat64Hole,
Operator::kFoldable | Operator::kNoThrow, "CheckFloat64Hole", 1, Operator::kFoldable | Operator::kNoThrow, "CheckFloat64Hole", 1,
1, 1, 1, 1, 0, kMode) {} 1, 1, 1, 1, 0,
CheckFloat64HoleParameters(kMode, VectorSlotPair())) {}
}; };
CheckFloat64HoleNaNOperator<CheckFloat64HoleMode::kAllowReturnHole> CheckFloat64HoleNaNOperator<CheckFloat64HoleMode::kAllowReturnHole>
kCheckFloat64HoleAllowReturnHoleOperator; kCheckFloat64HoleAllowReturnHoleOperator;
...@@ -1289,14 +1312,20 @@ const Operator* SimplifiedOperatorBuilder::ConvertReceiver( ...@@ -1289,14 +1312,20 @@ const Operator* SimplifiedOperatorBuilder::ConvertReceiver(
} }
const Operator* SimplifiedOperatorBuilder::CheckFloat64Hole( const Operator* SimplifiedOperatorBuilder::CheckFloat64Hole(
CheckFloat64HoleMode mode) { CheckFloat64HoleMode mode, VectorSlotPair const& feedback) {
switch (mode) { if (!feedback.IsValid()) {
case CheckFloat64HoleMode::kAllowReturnHole: switch (mode) {
return &cache_.kCheckFloat64HoleAllowReturnHoleOperator; case CheckFloat64HoleMode::kAllowReturnHole:
case CheckFloat64HoleMode::kNeverReturnHole: return &cache_.kCheckFloat64HoleAllowReturnHoleOperator;
return &cache_.kCheckFloat64HoleNeverReturnHoleOperator; case CheckFloat64HoleMode::kNeverReturnHole:
return &cache_.kCheckFloat64HoleNeverReturnHoleOperator;
}
UNREACHABLE();
} }
UNREACHABLE(); return new (zone()) Operator1<CheckFloat64HoleParameters>(
IrOpcode::kCheckFloat64Hole, Operator::kFoldable | Operator::kNoThrow,
"CheckFloat64Hole", 1, 1, 1, 1, 1, 0,
CheckFloat64HoleParameters(mode, feedback));
} }
const Operator* SimplifiedOperatorBuilder::SpeculativeToNumber( const Operator* SimplifiedOperatorBuilder::SpeculativeToNumber(
......
...@@ -195,9 +195,32 @@ size_t hash_value(CheckFloat64HoleMode); ...@@ -195,9 +195,32 @@ size_t hash_value(CheckFloat64HoleMode);
std::ostream& operator<<(std::ostream&, CheckFloat64HoleMode); std::ostream& operator<<(std::ostream&, CheckFloat64HoleMode);
CheckFloat64HoleMode CheckFloat64HoleModeOf(const Operator*) class CheckFloat64HoleParameters {
public:
CheckFloat64HoleParameters(CheckFloat64HoleMode mode,
VectorSlotPair const& feedback)
: mode_(mode), feedback_(feedback) {}
CheckFloat64HoleMode mode() const { return mode_; }
VectorSlotPair const& feedback() const { return feedback_; }
private:
CheckFloat64HoleMode mode_;
VectorSlotPair feedback_;
};
CheckFloat64HoleParameters const& CheckFloat64HoleParametersOf(Operator const*)
V8_WARN_UNUSED_RESULT; V8_WARN_UNUSED_RESULT;
std::ostream& operator<<(std::ostream&, CheckFloat64HoleParameters const&);
size_t hash_value(CheckFloat64HoleParameters const&);
bool operator==(CheckFloat64HoleParameters const&,
CheckFloat64HoleParameters const&);
bool operator!=(CheckFloat64HoleParameters const&,
CheckFloat64HoleParameters const&);
enum class CheckTaggedInputMode : uint8_t { enum class CheckTaggedInputMode : uint8_t {
kNumber, kNumber,
kNumberOrOddball, kNumberOrOddball,
...@@ -641,7 +664,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -641,7 +664,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* CheckBounds(const VectorSlotPair& feedback); const Operator* CheckBounds(const VectorSlotPair& feedback);
const Operator* CheckEqualsInternalizedString(); const Operator* CheckEqualsInternalizedString();
const Operator* CheckEqualsSymbol(); const Operator* CheckEqualsSymbol();
const Operator* CheckFloat64Hole(CheckFloat64HoleMode); const Operator* CheckFloat64Hole(CheckFloat64HoleMode, VectorSlotPair const&);
const Operator* CheckHeapObject(); const Operator* CheckHeapObject();
const Operator* CheckIf(DeoptimizeReason deoptimize_reason, const Operator* CheckIf(DeoptimizeReason deoptimize_reason,
const VectorSlotPair& feedback = VectorSlotPair()); const VectorSlotPair& feedback = VectorSlotPair());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment