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,
const CheckBoundsParameters& params = CheckBoundsParametersOf(node->op());
Node* check = __ Uint32LessThan(index, limit);
switch (params.mode()) {
case CheckBoundsParameters::kDeoptOnOutOfBounds:
__ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds,
params.check_parameters().feedback(), check,
frame_state, IsSafetyCheck::kCriticalSafetyCheck);
break;
case CheckBoundsParameters::kAbortOnOutOfBounds: {
auto if_abort = __ MakeDeferredLabel();
auto done = __ MakeLabel();
if (!(params.flags() & CheckBoundsFlag::kAbortOnOutOfBounds)) {
__ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds,
params.check_parameters().feedback(), check, frame_state,
IsSafetyCheck::kCriticalSafetyCheck);
} else {
auto if_abort = __ MakeDeferredLabel();
auto done = __ MakeLabel();
__ Branch(check, &done, &if_abort);
__ Branch(check, &done, &if_abort);
__ Bind(&if_abort);
__ Unreachable();
__ Goto(&done);
__ Bind(&if_abort);
__ Unreachable();
__ Goto(&done);
__ Bind(&done);
break;
}
__ Bind(&done);
}
return index;
......@@ -2421,25 +2417,21 @@ Node* EffectControlLinearizer::LowerCheckedUint64Bounds(Node* node,
const CheckBoundsParameters& params = CheckBoundsParametersOf(node->op());
Node* check = __ Uint64LessThan(index, limit);
switch (params.mode()) {
case CheckBoundsParameters::kDeoptOnOutOfBounds:
__ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds,
params.check_parameters().feedback(), check,
frame_state, IsSafetyCheck::kCriticalSafetyCheck);
break;
case CheckBoundsParameters::kAbortOnOutOfBounds: {
auto if_abort = __ MakeDeferredLabel();
auto done = __ MakeLabel();
if (!(params.flags() & CheckBoundsFlag::kAbortOnOutOfBounds)) {
__ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds,
params.check_parameters().feedback(), check, frame_state,
IsSafetyCheck::kCriticalSafetyCheck);
} else {
auto if_abort = __ MakeDeferredLabel();
auto done = __ MakeLabel();
__ Branch(check, &done, &if_abort);
__ Branch(check, &done, &if_abort);
__ Bind(&if_abort);
__ Unreachable();
__ Goto(&done);
__ Bind(&if_abort);
__ Unreachable();
__ Goto(&done);
__ Bind(&done);
break;
}
__ Bind(&done);
}
return index;
}
......
......@@ -6011,9 +6011,10 @@ Reduction JSCallReducer::ReduceStringFromCodePoint(Node* node) {
Node* control = NodeProperties::GetControlInput(node);
Node* input = NodeProperties::GetValueInput(node, 2);
input = effect =
graph()->NewNode(simplified()->CheckBounds(p.feedback()), input,
jsgraph()->Constant(0x10FFFF + 1), effect, control);
input = effect = graph()->NewNode(
simplified()->CheckBounds(p.feedback(),
CheckBoundsFlag::kConvertStringAndMinusZero),
input, jsgraph()->Constant(0x10FFFF + 1), effect, control);
Node* value =
graph()->NewNode(simplified()->StringFromSingleCodePoint(), input);
......
......@@ -2617,9 +2617,10 @@ JSNativeContextSpecialization::BuildElementAccess(
situation = kHandleOOB_SmiCheckDone;
} else {
// Check that the {index} is in the valid range for the {receiver}.
index = effect =
graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index,
length, effect, control);
index = effect = graph()->NewNode(
simplified()->CheckBounds(
FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
index, length, effect, control);
situation = kBoundsCheckDone;
}
......@@ -2647,7 +2648,8 @@ JSNativeContextSpecialization::BuildElementAccess(
index = etrue = graph()->NewNode(
simplified()->CheckBounds(
FeedbackSource(),
CheckBoundsParameters::kAbortOnOutOfBounds),
CheckBoundsFlag::kConvertStringAndMinusZero |
CheckBoundsFlag::kAbortOnOutOfBounds),
index, length, etrue, if_true);
// Perform the actual load
......@@ -2717,7 +2719,8 @@ JSNativeContextSpecialization::BuildElementAccess(
index = etrue = graph()->NewNode(
simplified()->CheckBounds(
FeedbackSource(),
CheckBoundsParameters::kAbortOnOutOfBounds),
CheckBoundsFlag::kConvertStringAndMinusZero |
CheckBoundsFlag::kAbortOnOutOfBounds),
index, length, etrue, if_true);
// Perform the actual store.
......@@ -2801,13 +2804,15 @@ JSNativeContextSpecialization::BuildElementAccess(
// bounds check below and just skip the store below if it's out of
// bounds for the {receiver}.
index = effect = graph()->NewNode(
simplified()->CheckBounds(FeedbackSource()), index,
jsgraph()->Constant(Smi::kMaxValue), effect, control);
simplified()->CheckBounds(
FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
index, jsgraph()->Constant(Smi::kMaxValue), effect, control);
} else {
// Check that the {index} is in the valid range for the {receiver}.
index = effect =
graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index,
length, effect, control);
index = effect = graph()->NewNode(
simplified()->CheckBounds(
FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
index, length, effect, control);
}
// Compute the element access.
......@@ -2855,10 +2860,12 @@ JSNativeContextSpecialization::BuildElementAccess(
// Do a real bounds check against {length}. This is in order to
// protect against a potential typer bug leading to the elimination of
// the NumberLessThan above.
index = etrue = graph()->NewNode(
simplified()->CheckBounds(
FeedbackSource(), CheckBoundsParameters::kAbortOnOutOfBounds),
index, length, etrue, if_true);
index = etrue =
graph()->NewNode(simplified()->CheckBounds(
FeedbackSource(),
CheckBoundsFlag::kConvertStringAndMinusZero |
CheckBoundsFlag::kAbortOnOutOfBounds),
index, length, etrue, if_true);
// Perform the actual load
vtrue = etrue =
......@@ -2957,9 +2964,10 @@ JSNativeContextSpecialization::BuildElementAccess(
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = effect;
Node* checked = etrue =
graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index,
length, etrue, if_true);
Node* checked = etrue = graph()->NewNode(
simplified()->CheckBounds(
FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
index, length, etrue, if_true);
Node* element = etrue =
graph()->NewNode(simplified()->LoadElement(element_access),
......@@ -3046,9 +3054,10 @@ JSNativeContextSpecialization::BuildElementAccess(
jsgraph()->Constant(JSObject::kMaxGap))
: graph()->NewNode(simplified()->NumberAdd(), length,
jsgraph()->OneConstant());
index = effect =
graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index,
limit, effect, control);
index = effect = graph()->NewNode(
simplified()->CheckBounds(
FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
index, limit, effect, control);
// Grow {elements} backing store if necessary.
GrowFastElementsMode mode =
......@@ -3116,8 +3125,9 @@ Node* JSNativeContextSpecialization::BuildIndexedStringLoad(
dependencies()->DependOnNoElementsProtector()) {
// Ensure that the {index} is a valid String length.
index = *effect = graph()->NewNode(
simplified()->CheckBounds(FeedbackSource()), index,
jsgraph()->Constant(String::kMaxLength), *effect, *control);
simplified()->CheckBounds(FeedbackSource(),
CheckBoundsFlag::kConvertStringAndMinusZero),
index, jsgraph()->Constant(String::kMaxLength), *effect, *control);
// Load the single character string from {receiver} or yield
// undefined if the {index} is not within the valid bounds.
......@@ -3134,7 +3144,8 @@ Node* JSNativeContextSpecialization::BuildIndexedStringLoad(
// NumberLessThan above.
Node* etrue = index = graph()->NewNode(
simplified()->CheckBounds(FeedbackSource(),
CheckBoundsParameters::kAbortOnOutOfBounds),
CheckBoundsFlag::kConvertStringAndMinusZero |
CheckBoundsFlag::kAbortOnOutOfBounds),
index, length, *effect, if_true);
Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
Node* vtrue = etrue =
......@@ -3152,9 +3163,10 @@ Node* JSNativeContextSpecialization::BuildIndexedStringLoad(
vtrue, vfalse, *control);
} else {
// Ensure that {index} is less than {receiver} length.
index = *effect =
graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index,
length, *effect, *control);
index = *effect = graph()->NewNode(
simplified()->CheckBounds(FeedbackSource(),
CheckBoundsFlag::kConvertStringAndMinusZero),
index, length, *effect, *control);
Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
......
......@@ -238,7 +238,9 @@ Node* RedundancyElimination::EffectPathChecks::LookupBoundsCheckFor(
Node* node) const {
for (Check const* check = head_; check != nullptr; check = check->next) {
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;
}
}
......
......@@ -1585,15 +1585,22 @@ class RepresentationSelector {
FeedbackSource const& feedback = p.check_parameters().feedback();
Type const index_type = TypeOf(node->InputAt(0));
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 (index_type.Is(Type::Integral32OrMinusZero())) {
// Map -0 to 0, and the values in the [-2^31,-1] range to the
// [2^31,2^32-1] range, which will be considered out-of-bounds
// as well, because the {length_type} is limited to Unsigned31.
if (index_type.Is(Type::Integral32()) ||
(index_type.Is(Type::Integral32OrMinusZero()) &&
p.flags() & CheckBoundsFlag::kConvertStringAndMinusZero)) {
// 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(),
MachineRepresentation::kWord32);
if (lower<T>()) {
CheckBoundsParameters::Mode mode = p.mode();
if (lowering->poisoning_level_ ==
PoisoningMitigationLevel::kDontPoison &&
(index_type.IsNone() || length_type.IsNone() ||
......@@ -1601,32 +1608,45 @@ class RepresentationSelector {
index_type.Max() < length_type.Min()))) {
// The bounds check is redundant if we already know that
// 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(
node, simplified()->CheckedUint32Bounds(feedback, mode));
node, simplified()->CheckedUint32Bounds(feedback, new_flags));
}
} else {
} else if (p.flags() & CheckBoundsFlag::kConvertStringAndMinusZero) {
VisitBinop<T>(node, UseInfo::CheckedTaggedAsArrayIndex(feedback),
UseInfo::Word(), MachineType::PointerRepresentation());
if (lower<T>()) {
if (jsgraph_->machine()->Is64()) {
NodeProperties::ChangeOp(
node, simplified()->CheckedUint64Bounds(feedback, p.mode()));
node, simplified()->CheckedUint64Bounds(feedback, new_flags));
} else {
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 {
CHECK(length_type.Is(type_cache_->kPositiveSafeInteger));
IdentifyZeros zero_handling =
(p.flags() & CheckBoundsFlag::kConvertStringAndMinusZero)
? kIdentifyZeros
: kDistinguishZeros;
VisitBinop<T>(node,
UseInfo::CheckedSigned64AsWord64(kIdentifyZeros, feedback),
UseInfo::CheckedSigned64AsWord64(zero_handling, feedback),
UseInfo::Word64(), MachineRepresentation::kWord64);
if (lower<T>()) {
NodeProperties::ChangeOp(
node, simplified()->CheckedUint64Bounds(feedback, p.mode()));
node, simplified()->CheckedUint64Bounds(feedback, new_flags));
}
}
}
......
......@@ -839,7 +839,6 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(CheckedUint64ToTaggedSigned, 1, 1)
#define CHECKED_BOUNDS_OP_LIST(V) \
V(CheckBounds) \
V(CheckedUint32Bounds) \
V(CheckedUint64Bounds)
......@@ -889,19 +888,26 @@ struct SimplifiedOperatorGlobalCache final {
CHECKED_WITH_FEEDBACK_OP_LIST(CHECKED_WITH_FEEDBACK)
#undef CHECKED_WITH_FEEDBACK
#define CHECKED_BOUNDS(Name) \
struct Name##Operator final : public Operator1<CheckBoundsParameters> { \
Name##Operator(FeedbackSource feedback, CheckBoundsParameters::Mode mode) \
: Operator1<CheckBoundsParameters>( \
IrOpcode::k##Name, Operator::kFoldable | Operator::kNoThrow, \
#Name, 2, 1, 1, 1, 1, 0, \
CheckBoundsParameters(feedback, mode)) {} \
}; \
Name##Operator k##Name##Deopting = { \
FeedbackSource(), CheckBoundsParameters::kDeoptOnOutOfBounds}; \
Name##Operator k##Name##Aborting = { \
FeedbackSource(), CheckBoundsParameters::kAbortOnOutOfBounds};
#define CHECKED_BOUNDS(Name) \
struct Name##Operator final : public Operator1<CheckBoundsParameters> { \
Name##Operator(FeedbackSource feedback, CheckBoundsFlags flags) \
: Operator1<CheckBoundsParameters>( \
IrOpcode::k##Name, Operator::kFoldable | Operator::kNoThrow, \
#Name, 2, 1, 1, 1, 1, 0, \
CheckBoundsParameters(feedback, flags)) {} \
}; \
Name##Operator k##Name = {FeedbackSource(), CheckBoundsFlags()}; \
Name##Operator k##Name##Aborting = {FeedbackSource(), \
CheckBoundsFlag::kAbortOnOutOfBounds};
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
template <DeoptimizeReason kDeoptimizeReason>
......@@ -1206,23 +1212,45 @@ GET_FROM_CACHE(LoadFieldByIndex)
CHECKED_WITH_FEEDBACK_OP_LIST(GET_FROM_CACHE_WITH_FEEDBACK)
#undef GET_FROM_CACHE_WITH_FEEDBACK
#define GET_FROM_CACHE_WITH_FEEDBACK(Name) \
const Operator* SimplifiedOperatorBuilder::Name( \
const FeedbackSource& feedback, CheckBoundsParameters::Mode mode) { \
if (!feedback.IsValid()) { \
switch (mode) { \
case CheckBoundsParameters::kDeoptOnOutOfBounds: \
return &cache_.k##Name##Deopting; \
case CheckBoundsParameters::kAbortOnOutOfBounds: \
return &cache_.k##Name##Aborting; \
} \
} \
return new (zone()) \
SimplifiedOperatorGlobalCache::Name##Operator(feedback, mode); \
#define GET_FROM_CACHE_WITH_FEEDBACK(Name) \
const Operator* SimplifiedOperatorBuilder::Name( \
const FeedbackSource& feedback, CheckBoundsFlags flags) { \
DCHECK(!(flags & CheckBoundsFlag::kConvertStringAndMinusZero)); \
if (!feedback.IsValid()) { \
if (flags & CheckBoundsFlag::kAbortOnOutOfBounds) { \
return &cache_.k##Name##Aborting; \
} else { \
return &cache_.k##Name; \
} \
} \
return new (zone()) \
SimplifiedOperatorGlobalCache::Name##Operator(feedback, flags); \
}
CHECKED_BOUNDS_OP_LIST(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) {
#define CASE(Name, ...) case IrOpcode::k##Name:
switch (op->opcode()) {
......@@ -1628,23 +1656,15 @@ CheckParameters const& CheckParametersOf(Operator const* op) {
bool operator==(CheckBoundsParameters const& lhs,
CheckBoundsParameters const& rhs) {
return lhs.check_parameters() == rhs.check_parameters() &&
lhs.mode() == rhs.mode();
lhs.flags() == rhs.flags();
}
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) {
os << p.check_parameters() << ", ";
switch (p.mode()) {
case CheckBoundsParameters::kDeoptOnOutOfBounds:
os << "deopt";
break;
case CheckBoundsParameters::kAbortOnOutOfBounds:
os << "abort";
break;
}
os << p.check_parameters() << ", " << p.flags();
return os;
}
......
......@@ -220,19 +220,24 @@ std::ostream& operator<<(std::ostream&, CheckParameters const&);
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 {
public:
enum Mode { kAbortOnOutOfBounds, kDeoptOnOutOfBounds };
CheckBoundsParameters(const FeedbackSource& feedback, CheckBoundsFlags flags)
: check_parameters_(feedback), flags_(flags) {}
CheckBoundsParameters(const FeedbackSource& feedback, Mode mode)
: check_parameters_(feedback), mode_(mode) {}
Mode mode() const { return mode_; }
CheckBoundsFlags flags() const { return flags_; }
const CheckParameters& check_parameters() const { return check_parameters_; }
private:
CheckParameters check_parameters_;
Mode mode_;
CheckBoundsFlags flags_;
};
bool operator==(CheckBoundsParameters const&, CheckBoundsParameters const&);
......@@ -377,10 +382,9 @@ size_t hash_value(const CheckMinusZeroParameters& params);
bool operator==(CheckMinusZeroParameters const&,
CheckMinusZeroParameters const&);
// Flags for map checks.
enum class CheckMapsFlag : uint8_t {
kNone = 0u,
kTryMigrateInstance = 1u << 0, // Try instance migration.
kTryMigrateInstance = 1u << 0,
};
using CheckMapsFlags = base::Flags<CheckMapsFlag>;
......@@ -784,12 +788,11 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* MapGuard(ZoneHandleSet<Map> maps);
const Operator* CheckBounds(const FeedbackSource& feedback,
CheckBoundsParameters::Mode mode =
CheckBoundsParameters::kDeoptOnOutOfBounds);
CheckBoundsFlags flags = {});
const Operator* CheckedUint32Bounds(const FeedbackSource& feedback,
CheckBoundsParameters::Mode mode);
CheckBoundsFlags flags);
const Operator* CheckedUint64Bounds(const FeedbackSource& feedback,
CheckBoundsParameters::Mode mode);
CheckBoundsFlags flags);
const Operator* CheckClosure(const Handle<FeedbackCell>& feedback_cell);
const Operator* CheckEqualsInternalizedString();
......
......@@ -42,6 +42,8 @@ Reduction TypedOptimization::Reduce(Node* node) {
return ReduceMaybeGrowFastElements(node);
case IrOpcode::kCheckHeapObject:
return ReduceCheckHeapObject(node);
case IrOpcode::kCheckBounds:
return ReduceCheckBounds(node);
case IrOpcode::kCheckNotTaggedHole:
return ReduceCheckNotTaggedHole(node);
case IrOpcode::kCheckMaps:
......@@ -177,7 +179,7 @@ Reduction TypedOptimization::ReduceMaybeGrowFastElements(Node* node) {
index_type.Max() < length_type.Min()) {
Node* check_bounds = graph()->NewNode(
simplified()->CheckBounds(FeedbackSource{},
CheckBoundsParameters::kAbortOnOutOfBounds),
CheckBoundsFlag::kAbortOnOutOfBounds),
index, length, effect, control);
ReplaceWithValue(node, elements);
return Replace(check_bounds);
......@@ -186,6 +188,23 @@ Reduction TypedOptimization::ReduceMaybeGrowFastElements(Node* node) {
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) {
Node* const input = NodeProperties::GetValueInput(node, 0);
Type const input_type = NodeProperties::GetType(input);
......
......@@ -38,6 +38,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final
private:
Reduction ReduceConvertReceiver(Node* node);
Reduction ReduceMaybeGrowFastElements(Node* node);
Reduction ReduceCheckBounds(Node* node);
Reduction ReduceCheckHeapObject(Node* node);
Reduction ReduceCheckMaps(Node* node);
Reduction ReduceCheckNumber(Node* node);
......
......@@ -155,10 +155,8 @@
%OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo([1, 2], 0));
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);
assertEquals(9, foo([9, 2], -0));
})();
// Test the RedundancyElimination::ReduceSpeculativeNumberComparison()
......@@ -176,10 +174,8 @@
%OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo([1, 2], 0));
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);
assertEquals(9, foo([9, 2], -0));
})();
// Test the RedundancyElimination::ReduceSpeculativeNumberComparison()
......@@ -197,8 +193,6 @@
%OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo([1, 2], 0));
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);
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) {
Node* effect = graph()->start();
Node* control = graph()->start();
Node* check1 = effect = graph()->NewNode(
simplified()->CheckedUint32Bounds(
feedback1, CheckBoundsParameters::kDeoptOnOutOfBounds),
index, length, effect, control);
Node* check1 = effect =
graph()->NewNode(simplified()->CheckedUint32Bounds(feedback1, {}),
index, length, effect, control);
Reduction r1 = Reduce(check1);
ASSERT_TRUE(r1.Changed());
EXPECT_EQ(r1.replacement(), check1);
Node* check2 = effect = graph()->NewNode(
simplified()->CheckedUint32Bounds(
feedback2, CheckBoundsParameters::kDeoptOnOutOfBounds),
index, length, effect, control);
Node* check2 = effect =
graph()->NewNode(simplified()->CheckedUint32Bounds(feedback2, {}),
index, length, effect, control);
Reduction r2 = Reduce(check2);
ASSERT_TRUE(r2.Changed());
EXPECT_EQ(r2.replacement(), check1);
......@@ -754,18 +752,16 @@ TEST_F(RedundancyEliminationTest, CheckedUint64Bounds) {
Node* effect = graph()->start();
Node* control = graph()->start();
Node* check1 = effect = graph()->NewNode(
simplified()->CheckedUint64Bounds(
feedback1, CheckBoundsParameters::kDeoptOnOutOfBounds),
index, length, effect, control);
Node* check1 = effect =
graph()->NewNode(simplified()->CheckedUint64Bounds(feedback1, {}),
index, length, effect, control);
Reduction r1 = Reduce(check1);
ASSERT_TRUE(r1.Changed());
EXPECT_EQ(r1.replacement(), check1);
Node* check2 = effect = graph()->NewNode(
simplified()->CheckedUint64Bounds(
feedback2, CheckBoundsParameters::kDeoptOnOutOfBounds),
index, length, effect, control);
Node* check2 = effect =
graph()->NewNode(simplified()->CheckedUint64Bounds(feedback2, {}),
index, length, effect, control);
Reduction r2 = Reduce(check2);
ASSERT_TRUE(r2.Changed());
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