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,
// If we reach this point w/o eliminating the {node} that's marked
// with allow-return-hole, we cannot do anything, so just deoptimize
// in case of the hole NaN (similar to Crankshaft).
CheckFloat64HoleParameters const& params =
CheckFloat64HoleParametersOf(node->op());
Node* value = node->InputAt(0);
Node* check = __ Word32Equal(__ Float64ExtractHighWord32(value),
__ Int32Constant(kHoleNanUpper32));
__ DeoptimizeIf(DeoptimizeReason::kHole, VectorSlotPair(), check,
__ DeoptimizeIf(DeoptimizeReason::kHole, params.feedback(), check,
frame_state);
return value;
}
Node* EffectControlLinearizer::LowerCheckNotTaggedHole(Node* node,
Node* frame_state) {
Node* value = node->InputAt(0);
......
......@@ -1992,11 +1992,6 @@ Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant,
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) {
if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map))
return NoChange();
......@@ -2082,12 +2077,15 @@ Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant,
graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
// Replace holes with undefined.
if (IsHoleyElementsKind(kind)) {
element = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
graph()->NewNode(simplified()->ReferenceEqual(), element,
jsgraph()->TheHoleConstant()),
jsgraph()->UndefinedConstant(), element);
if (kind == HOLEY_DOUBLE_ELEMENTS) {
// TODO(7409): avoid deopt if not all uses of value are truncated.
CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole;
element = effect =
graph()->NewNode(simplified()->CheckFloat64Hole(mode, p.feedback()),
element, effect, control);
} else if (IsHoleyElementsKind(kind)) {
element =
graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), element);
}
Node* if_found_return_value =
......@@ -5109,7 +5107,8 @@ Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) {
// TODO(6587): avoid deopt if not all uses of value are truncated.
CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole;
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(
} else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) {
// Return the signaling NaN hole directly if all uses are
// truncating.
vtrue = etrue =
graph()->NewNode(simplified()->CheckFloat64Hole(
CheckFloat64HoleMode::kAllowReturnHole),
vtrue, etrue, if_true);
vtrue = etrue = graph()->NewNode(
simplified()->CheckFloat64Hole(
CheckFloat64HoleMode::kAllowReturnHole, VectorSlotPair()),
vtrue, etrue, if_true);
}
}
......@@ -2585,7 +2585,8 @@ JSNativeContextSpecialization::BuildElementAccess(
mode = CheckFloat64HoleMode::kAllowReturnHole;
}
value = effect = graph()->NewNode(
simplified()->CheckFloat64Hole(mode), value, effect, control);
simplified()->CheckFloat64Hole(mode, VectorSlotPair()), value,
effect, control);
}
}
} else {
......
......@@ -2973,7 +2973,8 @@ class RepresentationSelector {
if (input_type.Is(Type::Number())) {
VisitNoop(node, truncation);
} else {
CheckFloat64HoleMode mode = CheckFloat64HoleModeOf(node->op());
CheckFloat64HoleMode mode =
CheckFloat64HoleParametersOf(node->op()).mode();
switch (mode) {
case CheckFloat64HoleMode::kAllowReturnHole:
if (truncation.IsUnused()) return VisitUnused(node);
......
......@@ -166,9 +166,31 @@ std::ostream& operator<<(std::ostream& os, CheckFloat64HoleMode mode) {
UNREACHABLE();
}
CheckFloat64HoleMode CheckFloat64HoleModeOf(const Operator* op) {
CheckFloat64HoleParameters const& CheckFloat64HoleParametersOf(
Operator const* op) {
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) {
......@@ -1005,12 +1027,13 @@ struct SimplifiedOperatorGlobalCache final {
template <CheckFloat64HoleMode kMode>
struct CheckFloat64HoleNaNOperator final
: public Operator1<CheckFloat64HoleMode> {
: public Operator1<CheckFloat64HoleParameters> {
CheckFloat64HoleNaNOperator()
: Operator1<CheckFloat64HoleMode>(
: Operator1<CheckFloat64HoleParameters>(
IrOpcode::kCheckFloat64Hole,
Operator::kFoldable | Operator::kNoThrow, "CheckFloat64Hole", 1,
1, 1, 1, 1, 0, kMode) {}
1, 1, 1, 1, 0,
CheckFloat64HoleParameters(kMode, VectorSlotPair())) {}
};
CheckFloat64HoleNaNOperator<CheckFloat64HoleMode::kAllowReturnHole>
kCheckFloat64HoleAllowReturnHoleOperator;
......@@ -1289,14 +1312,20 @@ const Operator* SimplifiedOperatorBuilder::ConvertReceiver(
}
const Operator* SimplifiedOperatorBuilder::CheckFloat64Hole(
CheckFloat64HoleMode mode) {
switch (mode) {
case CheckFloat64HoleMode::kAllowReturnHole:
return &cache_.kCheckFloat64HoleAllowReturnHoleOperator;
case CheckFloat64HoleMode::kNeverReturnHole:
return &cache_.kCheckFloat64HoleNeverReturnHoleOperator;
CheckFloat64HoleMode mode, VectorSlotPair const& feedback) {
if (!feedback.IsValid()) {
switch (mode) {
case CheckFloat64HoleMode::kAllowReturnHole:
return &cache_.kCheckFloat64HoleAllowReturnHoleOperator;
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(
......
......@@ -195,9 +195,32 @@ size_t hash_value(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;
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 {
kNumber,
kNumberOrOddball,
......@@ -641,7 +664,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* CheckBounds(const VectorSlotPair& feedback);
const Operator* CheckEqualsInternalizedString();
const Operator* CheckEqualsSymbol();
const Operator* CheckFloat64Hole(CheckFloat64HoleMode);
const Operator* CheckFloat64Hole(CheckFloat64HoleMode, VectorSlotPair const&);
const Operator* CheckHeapObject();
const Operator* CheckIf(DeoptimizeReason deoptimize_reason,
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