Commit 53c1525d authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[turbofan] Distinguish two further modes of CheckBounds

Extend the flag parameter with a bit that decides if the input should
be converted (-0 to 0, and a string to the array index it represents).
Instruct redundancy elimination to never replace x with CheckBounds(x)
when this CheckBounds is of the converting kind.

Bug: chromium:1070892, chromium:1071743
Change-Id: I3125a6e267d56dae6bf6cb2f5f52d27ef65d7c79
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2157365
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67408}
parent 0d7377c8
...@@ -2370,25 +2370,21 @@ Node* EffectControlLinearizer::LowerCheckedUint32Bounds(Node* node, ...@@ -2370,25 +2370,21 @@ Node* EffectControlLinearizer::LowerCheckedUint32Bounds(Node* node,
const CheckBoundsParameters& params = CheckBoundsParametersOf(node->op()); const CheckBoundsParameters& params = CheckBoundsParametersOf(node->op());
Node* check = __ Uint32LessThan(index, limit); Node* check = __ Uint32LessThan(index, limit);
switch (params.mode()) { if (!(params.flags() & CheckBoundsFlag::kAbortOnOutOfBounds)) {
case CheckBoundsParameters::kDeoptOnOutOfBounds: __ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds,
__ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds, params.check_parameters().feedback(), check, frame_state,
params.check_parameters().feedback(), check, IsSafetyCheck::kCriticalSafetyCheck);
frame_state, IsSafetyCheck::kCriticalSafetyCheck); } else {
break; auto if_abort = __ MakeDeferredLabel();
case CheckBoundsParameters::kAbortOnOutOfBounds: { auto done = __ MakeLabel();
auto if_abort = __ MakeDeferredLabel();
auto done = __ MakeLabel();
__ Branch(check, &done, &if_abort); __ Branch(check, &done, &if_abort);
__ Bind(&if_abort); __ Bind(&if_abort);
__ Unreachable(); __ Unreachable();
__ Goto(&done); __ Goto(&done);
__ Bind(&done); __ Bind(&done);
break;
}
} }
return index; return index;
...@@ -2421,25 +2417,21 @@ Node* EffectControlLinearizer::LowerCheckedUint64Bounds(Node* node, ...@@ -2421,25 +2417,21 @@ Node* EffectControlLinearizer::LowerCheckedUint64Bounds(Node* node,
const CheckBoundsParameters& params = CheckBoundsParametersOf(node->op()); const CheckBoundsParameters& params = CheckBoundsParametersOf(node->op());
Node* check = __ Uint64LessThan(index, limit); Node* check = __ Uint64LessThan(index, limit);
switch (params.mode()) { if (!(params.flags() & CheckBoundsFlag::kAbortOnOutOfBounds)) {
case CheckBoundsParameters::kDeoptOnOutOfBounds: __ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds,
__ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds, params.check_parameters().feedback(), check, frame_state,
params.check_parameters().feedback(), check, IsSafetyCheck::kCriticalSafetyCheck);
frame_state, IsSafetyCheck::kCriticalSafetyCheck); } else {
break; auto if_abort = __ MakeDeferredLabel();
case CheckBoundsParameters::kAbortOnOutOfBounds: { auto done = __ MakeLabel();
auto if_abort = __ MakeDeferredLabel();
auto done = __ MakeLabel();
__ Branch(check, &done, &if_abort); __ Branch(check, &done, &if_abort);
__ Bind(&if_abort); __ Bind(&if_abort);
__ Unreachable(); __ Unreachable();
__ Goto(&done); __ Goto(&done);
__ Bind(&done); __ Bind(&done);
break;
}
} }
return index; return index;
} }
......
...@@ -6011,9 +6011,10 @@ Reduction JSCallReducer::ReduceStringFromCodePoint(Node* node) { ...@@ -6011,9 +6011,10 @@ Reduction JSCallReducer::ReduceStringFromCodePoint(Node* node) {
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
Node* input = NodeProperties::GetValueInput(node, 2); Node* input = NodeProperties::GetValueInput(node, 2);
input = effect = input = effect = graph()->NewNode(
graph()->NewNode(simplified()->CheckBounds(p.feedback()), input, simplified()->CheckBounds(p.feedback(),
jsgraph()->Constant(0x10FFFF + 1), effect, control); CheckBoundsFlag::kConvertStringAndMinusZero),
input, jsgraph()->Constant(0x10FFFF + 1), effect, control);
Node* value = Node* value =
graph()->NewNode(simplified()->StringFromSingleCodePoint(), input); graph()->NewNode(simplified()->StringFromSingleCodePoint(), input);
......
...@@ -2617,9 +2617,10 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -2617,9 +2617,10 @@ JSNativeContextSpecialization::BuildElementAccess(
situation = kHandleOOB_SmiCheckDone; situation = kHandleOOB_SmiCheckDone;
} else { } else {
// Check that the {index} is in the valid range for the {receiver}. // Check that the {index} is in the valid range for the {receiver}.
index = effect = index = effect = graph()->NewNode(
graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index, simplified()->CheckBounds(
length, effect, control); FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
index, length, effect, control);
situation = kBoundsCheckDone; situation = kBoundsCheckDone;
} }
...@@ -2647,7 +2648,8 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -2647,7 +2648,8 @@ JSNativeContextSpecialization::BuildElementAccess(
index = etrue = graph()->NewNode( index = etrue = graph()->NewNode(
simplified()->CheckBounds( simplified()->CheckBounds(
FeedbackSource(), FeedbackSource(),
CheckBoundsParameters::kAbortOnOutOfBounds), CheckBoundsFlag::kConvertStringAndMinusZero |
CheckBoundsFlag::kAbortOnOutOfBounds),
index, length, etrue, if_true); index, length, etrue, if_true);
// Perform the actual load // Perform the actual load
...@@ -2717,7 +2719,8 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -2717,7 +2719,8 @@ JSNativeContextSpecialization::BuildElementAccess(
index = etrue = graph()->NewNode( index = etrue = graph()->NewNode(
simplified()->CheckBounds( simplified()->CheckBounds(
FeedbackSource(), FeedbackSource(),
CheckBoundsParameters::kAbortOnOutOfBounds), CheckBoundsFlag::kConvertStringAndMinusZero |
CheckBoundsFlag::kAbortOnOutOfBounds),
index, length, etrue, if_true); index, length, etrue, if_true);
// Perform the actual store. // Perform the actual store.
...@@ -2801,13 +2804,15 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -2801,13 +2804,15 @@ JSNativeContextSpecialization::BuildElementAccess(
// bounds check below and just skip the store below if it's out of // bounds check below and just skip the store below if it's out of
// bounds for the {receiver}. // bounds for the {receiver}.
index = effect = graph()->NewNode( index = effect = graph()->NewNode(
simplified()->CheckBounds(FeedbackSource()), index, simplified()->CheckBounds(
jsgraph()->Constant(Smi::kMaxValue), effect, control); FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
index, jsgraph()->Constant(Smi::kMaxValue), effect, control);
} else { } else {
// Check that the {index} is in the valid range for the {receiver}. // Check that the {index} is in the valid range for the {receiver}.
index = effect = index = effect = graph()->NewNode(
graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index, simplified()->CheckBounds(
length, effect, control); FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
index, length, effect, control);
} }
// Compute the element access. // Compute the element access.
...@@ -2855,10 +2860,12 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -2855,10 +2860,12 @@ JSNativeContextSpecialization::BuildElementAccess(
// Do a real bounds check against {length}. This is in order to // Do a real bounds check against {length}. This is in order to
// protect against a potential typer bug leading to the elimination of // protect against a potential typer bug leading to the elimination of
// the NumberLessThan above. // the NumberLessThan above.
index = etrue = graph()->NewNode( index = etrue =
simplified()->CheckBounds( graph()->NewNode(simplified()->CheckBounds(
FeedbackSource(), CheckBoundsParameters::kAbortOnOutOfBounds), FeedbackSource(),
index, length, etrue, if_true); CheckBoundsFlag::kConvertStringAndMinusZero |
CheckBoundsFlag::kAbortOnOutOfBounds),
index, length, etrue, if_true);
// Perform the actual load // Perform the actual load
vtrue = etrue = vtrue = etrue =
...@@ -2957,9 +2964,10 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -2957,9 +2964,10 @@ JSNativeContextSpecialization::BuildElementAccess(
Node* if_true = graph()->NewNode(common()->IfTrue(), branch); Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = effect; Node* etrue = effect;
Node* checked = etrue = Node* checked = etrue = graph()->NewNode(
graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index, simplified()->CheckBounds(
length, etrue, if_true); FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
index, length, etrue, if_true);
Node* element = etrue = Node* element = etrue =
graph()->NewNode(simplified()->LoadElement(element_access), graph()->NewNode(simplified()->LoadElement(element_access),
...@@ -3046,9 +3054,10 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -3046,9 +3054,10 @@ JSNativeContextSpecialization::BuildElementAccess(
jsgraph()->Constant(JSObject::kMaxGap)) jsgraph()->Constant(JSObject::kMaxGap))
: graph()->NewNode(simplified()->NumberAdd(), length, : graph()->NewNode(simplified()->NumberAdd(), length,
jsgraph()->OneConstant()); jsgraph()->OneConstant());
index = effect = index = effect = graph()->NewNode(
graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index, simplified()->CheckBounds(
limit, effect, control); FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
index, limit, effect, control);
// Grow {elements} backing store if necessary. // Grow {elements} backing store if necessary.
GrowFastElementsMode mode = GrowFastElementsMode mode =
...@@ -3116,8 +3125,9 @@ Node* JSNativeContextSpecialization::BuildIndexedStringLoad( ...@@ -3116,8 +3125,9 @@ Node* JSNativeContextSpecialization::BuildIndexedStringLoad(
dependencies()->DependOnNoElementsProtector()) { dependencies()->DependOnNoElementsProtector()) {
// Ensure that the {index} is a valid String length. // Ensure that the {index} is a valid String length.
index = *effect = graph()->NewNode( index = *effect = graph()->NewNode(
simplified()->CheckBounds(FeedbackSource()), index, simplified()->CheckBounds(FeedbackSource(),
jsgraph()->Constant(String::kMaxLength), *effect, *control); CheckBoundsFlag::kConvertStringAndMinusZero),
index, jsgraph()->Constant(String::kMaxLength), *effect, *control);
// Load the single character string from {receiver} or yield // Load the single character string from {receiver} or yield
// undefined if the {index} is not within the valid bounds. // undefined if the {index} is not within the valid bounds.
...@@ -3134,7 +3144,8 @@ Node* JSNativeContextSpecialization::BuildIndexedStringLoad( ...@@ -3134,7 +3144,8 @@ Node* JSNativeContextSpecialization::BuildIndexedStringLoad(
// NumberLessThan above. // NumberLessThan above.
Node* etrue = index = graph()->NewNode( Node* etrue = index = graph()->NewNode(
simplified()->CheckBounds(FeedbackSource(), simplified()->CheckBounds(FeedbackSource(),
CheckBoundsParameters::kAbortOnOutOfBounds), CheckBoundsFlag::kConvertStringAndMinusZero |
CheckBoundsFlag::kAbortOnOutOfBounds),
index, length, *effect, if_true); index, length, *effect, if_true);
Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index); Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
Node* vtrue = etrue = Node* vtrue = etrue =
...@@ -3152,9 +3163,10 @@ Node* JSNativeContextSpecialization::BuildIndexedStringLoad( ...@@ -3152,9 +3163,10 @@ Node* JSNativeContextSpecialization::BuildIndexedStringLoad(
vtrue, vfalse, *control); vtrue, vfalse, *control);
} else { } else {
// Ensure that {index} is less than {receiver} length. // Ensure that {index} is less than {receiver} length.
index = *effect = index = *effect = graph()->NewNode(
graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index, simplified()->CheckBounds(FeedbackSource(),
length, *effect, *control); CheckBoundsFlag::kConvertStringAndMinusZero),
index, length, *effect, *control);
Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index); Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
......
...@@ -238,7 +238,9 @@ Node* RedundancyElimination::EffectPathChecks::LookupBoundsCheckFor( ...@@ -238,7 +238,9 @@ Node* RedundancyElimination::EffectPathChecks::LookupBoundsCheckFor(
Node* node) const { Node* node) const {
for (Check const* check = head_; check != nullptr; check = check->next) { for (Check const* check = head_; check != nullptr; check = check->next) {
if (check->node->opcode() == IrOpcode::kCheckBounds && if (check->node->opcode() == IrOpcode::kCheckBounds &&
check->node->InputAt(0) == node && TypeSubsumes(node, check->node)) { check->node->InputAt(0) == node && TypeSubsumes(node, check->node) &&
!(CheckBoundsParametersOf(check->node->op()).flags() &
CheckBoundsFlag::kConvertStringAndMinusZero)) {
return check->node; return check->node;
} }
} }
......
...@@ -1585,15 +1585,22 @@ class RepresentationSelector { ...@@ -1585,15 +1585,22 @@ class RepresentationSelector {
FeedbackSource const& feedback = p.check_parameters().feedback(); FeedbackSource const& feedback = p.check_parameters().feedback();
Type const index_type = TypeOf(node->InputAt(0)); Type const index_type = TypeOf(node->InputAt(0));
Type const length_type = TypeOf(node->InputAt(1)); Type const length_type = TypeOf(node->InputAt(1));
// Conversions, if requested and needed, will be handled by the
// representation changer, not by the lower-level Checked*Bounds operators.
CheckBoundsFlags new_flags =
p.flags().without(CheckBoundsFlag::kConvertStringAndMinusZero);
if (length_type.Is(Type::Unsigned31())) { if (length_type.Is(Type::Unsigned31())) {
if (index_type.Is(Type::Integral32OrMinusZero())) { if (index_type.Is(Type::Integral32()) ||
// Map -0 to 0, and the values in the [-2^31,-1] range to the (index_type.Is(Type::Integral32OrMinusZero()) &&
// [2^31,2^32-1] range, which will be considered out-of-bounds p.flags() & CheckBoundsFlag::kConvertStringAndMinusZero)) {
// as well, because the {length_type} is limited to Unsigned31. // Map the values in the [-2^31,-1] range to the [2^31,2^32-1] range,
// which will be considered out-of-bounds because the {length_type} is
// limited to Unsigned31. This also converts -0 to 0.
VisitBinop<T>(node, UseInfo::TruncatingWord32(), VisitBinop<T>(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32); MachineRepresentation::kWord32);
if (lower<T>()) { if (lower<T>()) {
CheckBoundsParameters::Mode mode = p.mode();
if (lowering->poisoning_level_ == if (lowering->poisoning_level_ ==
PoisoningMitigationLevel::kDontPoison && PoisoningMitigationLevel::kDontPoison &&
(index_type.IsNone() || length_type.IsNone() || (index_type.IsNone() || length_type.IsNone() ||
...@@ -1601,32 +1608,45 @@ class RepresentationSelector { ...@@ -1601,32 +1608,45 @@ class RepresentationSelector {
index_type.Max() < length_type.Min()))) { index_type.Max() < length_type.Min()))) {
// The bounds check is redundant if we already know that // The bounds check is redundant if we already know that
// the index is within the bounds of [0.0, length[. // the index is within the bounds of [0.0, length[.
mode = CheckBoundsParameters::kAbortOnOutOfBounds; // TODO(neis): Move this into TypedOptimization?
new_flags |= CheckBoundsFlag::kAbortOnOutOfBounds;
} }
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, simplified()->CheckedUint32Bounds(feedback, mode)); node, simplified()->CheckedUint32Bounds(feedback, new_flags));
} }
} else { } else if (p.flags() & CheckBoundsFlag::kConvertStringAndMinusZero) {
VisitBinop<T>(node, UseInfo::CheckedTaggedAsArrayIndex(feedback), VisitBinop<T>(node, UseInfo::CheckedTaggedAsArrayIndex(feedback),
UseInfo::Word(), MachineType::PointerRepresentation()); UseInfo::Word(), MachineType::PointerRepresentation());
if (lower<T>()) { if (lower<T>()) {
if (jsgraph_->machine()->Is64()) { if (jsgraph_->machine()->Is64()) {
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, simplified()->CheckedUint64Bounds(feedback, p.mode())); node, simplified()->CheckedUint64Bounds(feedback, new_flags));
} else { } else {
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, simplified()->CheckedUint32Bounds(feedback, p.mode())); node, simplified()->CheckedUint32Bounds(feedback, new_flags));
} }
} }
} else {
VisitBinop<T>(
node, UseInfo::CheckedSigned32AsWord32(kDistinguishZeros, feedback),
UseInfo::TruncatingWord32(), MachineRepresentation::kWord32);
if (lower<T>()) {
NodeProperties::ChangeOp(
node, simplified()->CheckedUint32Bounds(feedback, new_flags));
}
} }
} else { } else {
CHECK(length_type.Is(type_cache_->kPositiveSafeInteger)); CHECK(length_type.Is(type_cache_->kPositiveSafeInteger));
IdentifyZeros zero_handling =
(p.flags() & CheckBoundsFlag::kConvertStringAndMinusZero)
? kIdentifyZeros
: kDistinguishZeros;
VisitBinop<T>(node, VisitBinop<T>(node,
UseInfo::CheckedSigned64AsWord64(kIdentifyZeros, feedback), UseInfo::CheckedSigned64AsWord64(zero_handling, feedback),
UseInfo::Word64(), MachineRepresentation::kWord64); UseInfo::Word64(), MachineRepresentation::kWord64);
if (lower<T>()) { if (lower<T>()) {
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, simplified()->CheckedUint64Bounds(feedback, p.mode())); node, simplified()->CheckedUint64Bounds(feedback, new_flags));
} }
} }
} }
......
...@@ -839,7 +839,6 @@ bool operator==(CheckMinusZeroParameters const& lhs, ...@@ -839,7 +839,6 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(CheckedUint64ToTaggedSigned, 1, 1) V(CheckedUint64ToTaggedSigned, 1, 1)
#define CHECKED_BOUNDS_OP_LIST(V) \ #define CHECKED_BOUNDS_OP_LIST(V) \
V(CheckBounds) \
V(CheckedUint32Bounds) \ V(CheckedUint32Bounds) \
V(CheckedUint64Bounds) V(CheckedUint64Bounds)
...@@ -889,19 +888,26 @@ struct SimplifiedOperatorGlobalCache final { ...@@ -889,19 +888,26 @@ struct SimplifiedOperatorGlobalCache final {
CHECKED_WITH_FEEDBACK_OP_LIST(CHECKED_WITH_FEEDBACK) CHECKED_WITH_FEEDBACK_OP_LIST(CHECKED_WITH_FEEDBACK)
#undef CHECKED_WITH_FEEDBACK #undef CHECKED_WITH_FEEDBACK
#define CHECKED_BOUNDS(Name) \ #define CHECKED_BOUNDS(Name) \
struct Name##Operator final : public Operator1<CheckBoundsParameters> { \ struct Name##Operator final : public Operator1<CheckBoundsParameters> { \
Name##Operator(FeedbackSource feedback, CheckBoundsParameters::Mode mode) \ Name##Operator(FeedbackSource feedback, CheckBoundsFlags flags) \
: Operator1<CheckBoundsParameters>( \ : Operator1<CheckBoundsParameters>( \
IrOpcode::k##Name, Operator::kFoldable | Operator::kNoThrow, \ IrOpcode::k##Name, Operator::kFoldable | Operator::kNoThrow, \
#Name, 2, 1, 1, 1, 1, 0, \ #Name, 2, 1, 1, 1, 1, 0, \
CheckBoundsParameters(feedback, mode)) {} \ CheckBoundsParameters(feedback, flags)) {} \
}; \ }; \
Name##Operator k##Name##Deopting = { \ Name##Operator k##Name = {FeedbackSource(), CheckBoundsFlags()}; \
FeedbackSource(), CheckBoundsParameters::kDeoptOnOutOfBounds}; \ Name##Operator k##Name##Aborting = {FeedbackSource(), \
Name##Operator k##Name##Aborting = { \ CheckBoundsFlag::kAbortOnOutOfBounds};
FeedbackSource(), CheckBoundsParameters::kAbortOnOutOfBounds};
CHECKED_BOUNDS_OP_LIST(CHECKED_BOUNDS) CHECKED_BOUNDS_OP_LIST(CHECKED_BOUNDS)
CHECKED_BOUNDS(CheckBounds)
// For IrOpcode::kCheckBounds, we allow additional flags:
CheckBoundsOperator kCheckBoundsConverting = {
FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero};
CheckBoundsOperator kCheckBoundsAbortingAndConverting = {
FeedbackSource(),
CheckBoundsFlags(CheckBoundsFlag::kAbortOnOutOfBounds) |
CheckBoundsFlags(CheckBoundsFlag::kConvertStringAndMinusZero)};
#undef CHECKED_BOUNDS #undef CHECKED_BOUNDS
template <DeoptimizeReason kDeoptimizeReason> template <DeoptimizeReason kDeoptimizeReason>
...@@ -1206,23 +1212,45 @@ GET_FROM_CACHE(LoadFieldByIndex) ...@@ -1206,23 +1212,45 @@ GET_FROM_CACHE(LoadFieldByIndex)
CHECKED_WITH_FEEDBACK_OP_LIST(GET_FROM_CACHE_WITH_FEEDBACK) CHECKED_WITH_FEEDBACK_OP_LIST(GET_FROM_CACHE_WITH_FEEDBACK)
#undef GET_FROM_CACHE_WITH_FEEDBACK #undef GET_FROM_CACHE_WITH_FEEDBACK
#define GET_FROM_CACHE_WITH_FEEDBACK(Name) \ #define GET_FROM_CACHE_WITH_FEEDBACK(Name) \
const Operator* SimplifiedOperatorBuilder::Name( \ const Operator* SimplifiedOperatorBuilder::Name( \
const FeedbackSource& feedback, CheckBoundsParameters::Mode mode) { \ const FeedbackSource& feedback, CheckBoundsFlags flags) { \
if (!feedback.IsValid()) { \ DCHECK(!(flags & CheckBoundsFlag::kConvertStringAndMinusZero)); \
switch (mode) { \ if (!feedback.IsValid()) { \
case CheckBoundsParameters::kDeoptOnOutOfBounds: \ if (flags & CheckBoundsFlag::kAbortOnOutOfBounds) { \
return &cache_.k##Name##Deopting; \ return &cache_.k##Name##Aborting; \
case CheckBoundsParameters::kAbortOnOutOfBounds: \ } else { \
return &cache_.k##Name##Aborting; \ return &cache_.k##Name; \
} \ } \
} \ } \
return new (zone()) \ return new (zone()) \
SimplifiedOperatorGlobalCache::Name##Operator(feedback, mode); \ SimplifiedOperatorGlobalCache::Name##Operator(feedback, flags); \
} }
CHECKED_BOUNDS_OP_LIST(GET_FROM_CACHE_WITH_FEEDBACK) CHECKED_BOUNDS_OP_LIST(GET_FROM_CACHE_WITH_FEEDBACK)
#undef GET_FROM_CACHE_WITH_FEEDBACK #undef GET_FROM_CACHE_WITH_FEEDBACK
// For IrOpcode::kCheckBounds, we allow additional flags:
const Operator* SimplifiedOperatorBuilder::CheckBounds(
const FeedbackSource& feedback, CheckBoundsFlags flags) {
if (!feedback.IsValid()) {
if (flags & CheckBoundsFlag::kAbortOnOutOfBounds) {
if (flags & CheckBoundsFlag::kConvertStringAndMinusZero) {
return &cache_.kCheckBoundsAbortingAndConverting;
} else {
return &cache_.kCheckBoundsAborting;
}
} else {
if (flags & CheckBoundsFlag::kConvertStringAndMinusZero) {
return &cache_.kCheckBoundsConverting;
} else {
return &cache_.kCheckBounds;
}
}
}
return new (zone())
SimplifiedOperatorGlobalCache::CheckBoundsOperator(feedback, flags);
}
bool IsCheckedWithFeedback(const Operator* op) { bool IsCheckedWithFeedback(const Operator* op) {
#define CASE(Name, ...) case IrOpcode::k##Name: #define CASE(Name, ...) case IrOpcode::k##Name:
switch (op->opcode()) { switch (op->opcode()) {
...@@ -1628,23 +1656,15 @@ CheckParameters const& CheckParametersOf(Operator const* op) { ...@@ -1628,23 +1656,15 @@ CheckParameters const& CheckParametersOf(Operator const* op) {
bool operator==(CheckBoundsParameters const& lhs, bool operator==(CheckBoundsParameters const& lhs,
CheckBoundsParameters const& rhs) { CheckBoundsParameters const& rhs) {
return lhs.check_parameters() == rhs.check_parameters() && return lhs.check_parameters() == rhs.check_parameters() &&
lhs.mode() == rhs.mode(); lhs.flags() == rhs.flags();
} }
size_t hash_value(CheckBoundsParameters const& p) { size_t hash_value(CheckBoundsParameters const& p) {
return base::hash_combine(hash_value(p.check_parameters()), p.mode()); return base::hash_combine(hash_value(p.check_parameters()), p.flags());
} }
std::ostream& operator<<(std::ostream& os, CheckBoundsParameters const& p) { std::ostream& operator<<(std::ostream& os, CheckBoundsParameters const& p) {
os << p.check_parameters() << ", "; os << p.check_parameters() << ", " << p.flags();
switch (p.mode()) {
case CheckBoundsParameters::kDeoptOnOutOfBounds:
os << "deopt";
break;
case CheckBoundsParameters::kAbortOnOutOfBounds:
os << "abort";
break;
}
return os; return os;
} }
......
...@@ -220,19 +220,24 @@ std::ostream& operator<<(std::ostream&, CheckParameters const&); ...@@ -220,19 +220,24 @@ std::ostream& operator<<(std::ostream&, CheckParameters const&);
CheckParameters const& CheckParametersOf(Operator const*) V8_WARN_UNUSED_RESULT; CheckParameters const& CheckParametersOf(Operator const*) V8_WARN_UNUSED_RESULT;
enum class CheckBoundsFlag : uint8_t {
kConvertStringAndMinusZero = 1 << 0, // instead of deopting on such inputs
kAbortOnOutOfBounds = 1 << 1, // instead of deopting if input is OOB
};
using CheckBoundsFlags = base::Flags<CheckBoundsFlag>;
DEFINE_OPERATORS_FOR_FLAGS(CheckBoundsFlags)
class CheckBoundsParameters final { class CheckBoundsParameters final {
public: public:
enum Mode { kAbortOnOutOfBounds, kDeoptOnOutOfBounds }; CheckBoundsParameters(const FeedbackSource& feedback, CheckBoundsFlags flags)
: check_parameters_(feedback), flags_(flags) {}
CheckBoundsParameters(const FeedbackSource& feedback, Mode mode) CheckBoundsFlags flags() const { return flags_; }
: check_parameters_(feedback), mode_(mode) {}
Mode mode() const { return mode_; }
const CheckParameters& check_parameters() const { return check_parameters_; } const CheckParameters& check_parameters() const { return check_parameters_; }
private: private:
CheckParameters check_parameters_; CheckParameters check_parameters_;
Mode mode_; CheckBoundsFlags flags_;
}; };
bool operator==(CheckBoundsParameters const&, CheckBoundsParameters const&); bool operator==(CheckBoundsParameters const&, CheckBoundsParameters const&);
...@@ -377,10 +382,9 @@ size_t hash_value(const CheckMinusZeroParameters& params); ...@@ -377,10 +382,9 @@ size_t hash_value(const CheckMinusZeroParameters& params);
bool operator==(CheckMinusZeroParameters const&, bool operator==(CheckMinusZeroParameters const&,
CheckMinusZeroParameters const&); CheckMinusZeroParameters const&);
// Flags for map checks.
enum class CheckMapsFlag : uint8_t { enum class CheckMapsFlag : uint8_t {
kNone = 0u, kNone = 0u,
kTryMigrateInstance = 1u << 0, // Try instance migration. kTryMigrateInstance = 1u << 0,
}; };
using CheckMapsFlags = base::Flags<CheckMapsFlag>; using CheckMapsFlags = base::Flags<CheckMapsFlag>;
...@@ -784,12 +788,11 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -784,12 +788,11 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* MapGuard(ZoneHandleSet<Map> maps); const Operator* MapGuard(ZoneHandleSet<Map> maps);
const Operator* CheckBounds(const FeedbackSource& feedback, const Operator* CheckBounds(const FeedbackSource& feedback,
CheckBoundsParameters::Mode mode = CheckBoundsFlags flags = {});
CheckBoundsParameters::kDeoptOnOutOfBounds);
const Operator* CheckedUint32Bounds(const FeedbackSource& feedback, const Operator* CheckedUint32Bounds(const FeedbackSource& feedback,
CheckBoundsParameters::Mode mode); CheckBoundsFlags flags);
const Operator* CheckedUint64Bounds(const FeedbackSource& feedback, const Operator* CheckedUint64Bounds(const FeedbackSource& feedback,
CheckBoundsParameters::Mode mode); CheckBoundsFlags flags);
const Operator* CheckClosure(const Handle<FeedbackCell>& feedback_cell); const Operator* CheckClosure(const Handle<FeedbackCell>& feedback_cell);
const Operator* CheckEqualsInternalizedString(); const Operator* CheckEqualsInternalizedString();
......
...@@ -42,6 +42,8 @@ Reduction TypedOptimization::Reduce(Node* node) { ...@@ -42,6 +42,8 @@ Reduction TypedOptimization::Reduce(Node* node) {
return ReduceMaybeGrowFastElements(node); return ReduceMaybeGrowFastElements(node);
case IrOpcode::kCheckHeapObject: case IrOpcode::kCheckHeapObject:
return ReduceCheckHeapObject(node); return ReduceCheckHeapObject(node);
case IrOpcode::kCheckBounds:
return ReduceCheckBounds(node);
case IrOpcode::kCheckNotTaggedHole: case IrOpcode::kCheckNotTaggedHole:
return ReduceCheckNotTaggedHole(node); return ReduceCheckNotTaggedHole(node);
case IrOpcode::kCheckMaps: case IrOpcode::kCheckMaps:
...@@ -177,7 +179,7 @@ Reduction TypedOptimization::ReduceMaybeGrowFastElements(Node* node) { ...@@ -177,7 +179,7 @@ Reduction TypedOptimization::ReduceMaybeGrowFastElements(Node* node) {
index_type.Max() < length_type.Min()) { index_type.Max() < length_type.Min()) {
Node* check_bounds = graph()->NewNode( Node* check_bounds = graph()->NewNode(
simplified()->CheckBounds(FeedbackSource{}, simplified()->CheckBounds(FeedbackSource{},
CheckBoundsParameters::kAbortOnOutOfBounds), CheckBoundsFlag::kAbortOnOutOfBounds),
index, length, effect, control); index, length, effect, control);
ReplaceWithValue(node, elements); ReplaceWithValue(node, elements);
return Replace(check_bounds); return Replace(check_bounds);
...@@ -186,6 +188,23 @@ Reduction TypedOptimization::ReduceMaybeGrowFastElements(Node* node) { ...@@ -186,6 +188,23 @@ Reduction TypedOptimization::ReduceMaybeGrowFastElements(Node* node) {
return NoChange(); return NoChange();
} }
Reduction TypedOptimization::ReduceCheckBounds(Node* node) {
CheckBoundsParameters const& p = CheckBoundsParametersOf(node->op());
Node* const input = NodeProperties::GetValueInput(node, 0);
Type const input_type = NodeProperties::GetType(input);
if (p.flags() & CheckBoundsFlag::kConvertStringAndMinusZero &&
!input_type.Maybe(Type::String()) &&
!input_type.Maybe(Type::MinusZero())) {
NodeProperties::ChangeOp(
node,
simplified()->CheckBounds(
p.check_parameters().feedback(),
p.flags().without(CheckBoundsFlag::kConvertStringAndMinusZero)));
return Changed(node);
}
return NoChange();
}
Reduction TypedOptimization::ReduceCheckNotTaggedHole(Node* node) { Reduction TypedOptimization::ReduceCheckNotTaggedHole(Node* node) {
Node* const input = NodeProperties::GetValueInput(node, 0); Node* const input = NodeProperties::GetValueInput(node, 0);
Type const input_type = NodeProperties::GetType(input); Type const input_type = NodeProperties::GetType(input);
......
...@@ -38,6 +38,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final ...@@ -38,6 +38,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final
private: private:
Reduction ReduceConvertReceiver(Node* node); Reduction ReduceConvertReceiver(Node* node);
Reduction ReduceMaybeGrowFastElements(Node* node); Reduction ReduceMaybeGrowFastElements(Node* node);
Reduction ReduceCheckBounds(Node* node);
Reduction ReduceCheckHeapObject(Node* node); Reduction ReduceCheckHeapObject(Node* node);
Reduction ReduceCheckMaps(Node* node); Reduction ReduceCheckMaps(Node* node);
Reduction ReduceCheckNumber(Node* node); Reduction ReduceCheckNumber(Node* node);
......
...@@ -155,10 +155,8 @@ ...@@ -155,10 +155,8 @@
%OptimizeFunctionOnNextCall(foo); %OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo([1, 2], 0)); assertEquals(1, foo([1, 2], 0));
assertEquals(1, foo([1, 2], 1)); assertEquals(1, foo([1, 2], 1));
// Even passing -0 should not deoptimize and
// of course still pass the equality test above.
assertEquals(9, foo([9, 2], -0));
assertOptimized(foo); assertOptimized(foo);
assertEquals(9, foo([9, 2], -0));
})(); })();
// Test the RedundancyElimination::ReduceSpeculativeNumberComparison() // Test the RedundancyElimination::ReduceSpeculativeNumberComparison()
...@@ -176,10 +174,8 @@ ...@@ -176,10 +174,8 @@
%OptimizeFunctionOnNextCall(foo); %OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo([1, 2], 0)); assertEquals(1, foo([1, 2], 0));
assertEquals(1, foo([1, 2], 1)); assertEquals(1, foo([1, 2], 1));
// Even passing -0 should not deoptimize and
// of course still pass the equality test above.
assertEquals(9, foo([9, 2], -0));
assertOptimized(foo); assertOptimized(foo);
assertEquals(9, foo([9, 2], -0));
})(); })();
// Test the RedundancyElimination::ReduceSpeculativeNumberComparison() // Test the RedundancyElimination::ReduceSpeculativeNumberComparison()
...@@ -197,8 +193,6 @@ ...@@ -197,8 +193,6 @@
%OptimizeFunctionOnNextCall(foo); %OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo([1, 2], 0)); assertEquals(1, foo([1, 2], 0));
assertEquals(1, foo([1, 2], 1)); assertEquals(1, foo([1, 2], 1));
// Even passing -0 should not deoptimize and
// of course still pass the equality test above.
assertEquals(9, foo([9, 2], -0));
assertOptimized(foo); assertOptimized(foo);
assertEquals(9, foo([9, 2], -0));
})(); })();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
var v = {0: 0, 1: 1, '01': 7};
function foo(index) {
return [v[index], v[index + 1], index + 1];
};
%PrepareFunctionForOptimization(foo);
assertEquals(foo(0), [0, 1, 1]);
assertEquals(foo(0), [0, 1, 1]);
%OptimizeFunctionOnNextCall(foo);
assertEquals(foo(0), [0, 1, 1]);
assertEquals(foo('0'), [0, 7, '01']);
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function foo(v) {
let x = Math.floor(v);
Number.prototype[v] = 42;
return x + Math.floor(v);
}
%PrepareFunctionForOptimization(foo);
assertSame(foo(-0), -0);
assertSame(foo(-0), -0);
%OptimizeFunctionOnNextCall(foo);
assertSame(foo(-0), -0);
function bar(v) {
v = v ? (v|0) : -0; // v has now type Integral32OrMinusZero.
let x = Math.floor(v);
Number.prototype[v] = 42;
return x + Math.floor(v);
}
%PrepareFunctionForOptimization(bar);
assertSame(2, bar(1));
assertSame(2, bar(1));
%OptimizeFunctionOnNextCall(bar);
assertSame(-0, bar(-0));
...@@ -670,18 +670,16 @@ TEST_F(RedundancyEliminationTest, CheckedUint32Bounds) { ...@@ -670,18 +670,16 @@ TEST_F(RedundancyEliminationTest, CheckedUint32Bounds) {
Node* effect = graph()->start(); Node* effect = graph()->start();
Node* control = graph()->start(); Node* control = graph()->start();
Node* check1 = effect = graph()->NewNode( Node* check1 = effect =
simplified()->CheckedUint32Bounds( graph()->NewNode(simplified()->CheckedUint32Bounds(feedback1, {}),
feedback1, CheckBoundsParameters::kDeoptOnOutOfBounds), index, length, effect, control);
index, length, effect, control);
Reduction r1 = Reduce(check1); Reduction r1 = Reduce(check1);
ASSERT_TRUE(r1.Changed()); ASSERT_TRUE(r1.Changed());
EXPECT_EQ(r1.replacement(), check1); EXPECT_EQ(r1.replacement(), check1);
Node* check2 = effect = graph()->NewNode( Node* check2 = effect =
simplified()->CheckedUint32Bounds( graph()->NewNode(simplified()->CheckedUint32Bounds(feedback2, {}),
feedback2, CheckBoundsParameters::kDeoptOnOutOfBounds), index, length, effect, control);
index, length, effect, control);
Reduction r2 = Reduce(check2); Reduction r2 = Reduce(check2);
ASSERT_TRUE(r2.Changed()); ASSERT_TRUE(r2.Changed());
EXPECT_EQ(r2.replacement(), check1); EXPECT_EQ(r2.replacement(), check1);
...@@ -754,18 +752,16 @@ TEST_F(RedundancyEliminationTest, CheckedUint64Bounds) { ...@@ -754,18 +752,16 @@ TEST_F(RedundancyEliminationTest, CheckedUint64Bounds) {
Node* effect = graph()->start(); Node* effect = graph()->start();
Node* control = graph()->start(); Node* control = graph()->start();
Node* check1 = effect = graph()->NewNode( Node* check1 = effect =
simplified()->CheckedUint64Bounds( graph()->NewNode(simplified()->CheckedUint64Bounds(feedback1, {}),
feedback1, CheckBoundsParameters::kDeoptOnOutOfBounds), index, length, effect, control);
index, length, effect, control);
Reduction r1 = Reduce(check1); Reduction r1 = Reduce(check1);
ASSERT_TRUE(r1.Changed()); ASSERT_TRUE(r1.Changed());
EXPECT_EQ(r1.replacement(), check1); EXPECT_EQ(r1.replacement(), check1);
Node* check2 = effect = graph()->NewNode( Node* check2 = effect =
simplified()->CheckedUint64Bounds( graph()->NewNode(simplified()->CheckedUint64Bounds(feedback2, {}),
feedback2, CheckBoundsParameters::kDeoptOnOutOfBounds), index, length, effect, control);
index, length, effect, control);
Reduction r2 = Reduce(check2); Reduction r2 = Reduce(check2);
ASSERT_TRUE(r2.Changed()); ASSERT_TRUE(r2.Changed());
EXPECT_EQ(r2.replacement(), check1); EXPECT_EQ(r2.replacement(), check1);
......
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