Commit 7bb6dc0e authored by Jaroslav Sevcik's avatar Jaroslav Sevcik Committed by Commit Bot

[turbofan] Introduce aborting bounds checks.

Instead of eliminating bounds checks based on types, we introduce
an aborting bounds check that crashes rather than deopts.

Bug: v8:8806
Change-Id: Icbd9c4554b6ad20fe4135b8622590093679dac3f
Reviewed-on: https://chromium-review.googlesource.com/c/1460461
Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59467}
parent d3c4a0b0
......@@ -2061,11 +2061,30 @@ Node* EffectControlLinearizer::LowerCheckedUint32Bounds(Node* node,
Node* frame_state) {
Node* index = node->InputAt(0);
Node* limit = node->InputAt(1);
const CheckParameters& params = CheckParametersOf(node->op());
const CheckBoundsParameters& params = CheckBoundsParametersOf(node->op());
Node* check = __ Uint32LessThan(index, limit);
__ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds, params.feedback(), check,
frame_state, IsSafetyCheck::kCriticalSafetyCheck);
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();
__ Branch(check, &done, &if_abort);
__ Bind(&if_abort);
__ Unreachable();
__ Goto(&done);
__ Bind(&done);
break;
}
}
return index;
}
......
......@@ -1566,6 +1566,8 @@ class RepresentationSelector {
VisitBinop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
if (lower()) {
CheckBoundsParameters::Mode mode =
CheckBoundsParameters::kDeoptOnOutOfBounds;
if (lowering->poisoning_level_ ==
PoisoningMitigationLevel::kDontPoison &&
(index_type.IsNone() || length_type.IsNone() ||
......@@ -1573,11 +1575,10 @@ 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[.
DeferReplacement(node, node->InputAt(0));
} else {
NodeProperties::ChangeOp(
node, simplified()->CheckedUint32Bounds(p.feedback()));
mode = CheckBoundsParameters::kAbortOnOutOfBounds;
}
NodeProperties::ChangeOp(
node, simplified()->CheckedUint32Bounds(p.feedback(), mode));
}
} else {
VisitBinop(
......@@ -1586,7 +1587,9 @@ class RepresentationSelector {
UseInfo::TruncatingWord32(), MachineRepresentation::kWord32);
if (lower()) {
NodeProperties::ChangeOp(
node, simplified()->CheckedUint32Bounds(p.feedback()));
node,
simplified()->CheckedUint32Bounds(
p.feedback(), CheckBoundsParameters::kDeoptOnOutOfBounds));
}
}
} else {
......
......@@ -812,13 +812,14 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(CheckedTaggedSignedToInt32, 1, 1) \
V(CheckedTaggedToTaggedPointer, 1, 1) \
V(CheckedTaggedToTaggedSigned, 1, 1) \
V(CheckedUint32Bounds, 2, 1) \
V(CheckedUint32ToInt32, 1, 1) \
V(CheckedUint32ToTaggedSigned, 1, 1) \
V(CheckedUint64Bounds, 2, 1) \
V(CheckedUint64ToInt32, 1, 1) \
V(CheckedUint64ToTaggedSigned, 1, 1)
#define CHECKED_BOUNDS_OP_LIST(V) V(CheckedUint32Bounds)
struct SimplifiedOperatorGlobalCache final {
#define PURE(Name, properties, value_input_count, control_input_count) \
struct Name##Operator final : public Operator { \
......@@ -867,6 +868,21 @@ 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(VectorSlotPair 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 = { \
VectorSlotPair(), CheckBoundsParameters::kDeoptOnOutOfBounds}; \
Name##Operator k##Name##Aborting = { \
VectorSlotPair(), CheckBoundsParameters::kAbortOnOutOfBounds};
CHECKED_BOUNDS_OP_LIST(CHECKED_BOUNDS)
#undef CHECKED_BOUNDS
template <DeoptimizeReason kDeoptimizeReason>
struct CheckIfOperator final : public Operator1<CheckIfParameters> {
CheckIfOperator()
......@@ -1185,6 +1201,23 @@ 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 VectorSlotPair& 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); \
}
CHECKED_BOUNDS_OP_LIST(GET_FROM_CACHE_WITH_FEEDBACK)
#undef GET_FROM_CACHE_WITH_FEEDBACK
bool IsCheckedWithFeedback(const Operator* op) {
#define CASE(Name, ...) case IrOpcode::k##Name:
switch (op->opcode()) {
......@@ -1509,12 +1542,43 @@ std::ostream& operator<<(std::ostream& os, CheckParameters const& p) {
}
CheckParameters const& CheckParametersOf(Operator const* op) {
if (op->opcode() == IrOpcode::kCheckedUint32Bounds) {
return OpParameter<CheckBoundsParameters>(op).check_parameters();
}
#define MAKE_OR(name, arg2, arg3) op->opcode() == IrOpcode::k##name ||
CHECK((CHECKED_WITH_FEEDBACK_OP_LIST(MAKE_OR) false));
#undef MAKE_OR
return OpParameter<CheckParameters>(op);
}
bool operator==(CheckBoundsParameters const& lhs,
CheckBoundsParameters const& rhs) {
return lhs.check_parameters() == rhs.check_parameters() &&
lhs.mode() == rhs.mode();
}
size_t hash_value(CheckBoundsParameters const& p) {
return base::hash_combine(hash_value(p.check_parameters()), p.mode());
}
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;
}
return os;
}
CheckBoundsParameters const& CheckBoundsParametersOf(Operator const* op) {
CHECK_EQ(op->opcode(), IrOpcode::kCheckedUint32Bounds);
return OpParameter<CheckBoundsParameters>(op);
}
bool operator==(CheckIfParameters const& lhs, CheckIfParameters const& rhs) {
return lhs.reason() == rhs.reason() && lhs.feedback() == rhs.feedback();
}
......@@ -1698,6 +1762,7 @@ const Operator* SimplifiedOperatorBuilder::TransitionAndStoreNonNumberElement(
#undef EFFECT_DEPENDENT_OP_LIST
#undef SPECULATIVE_NUMBER_BINOP_LIST
#undef CHECKED_WITH_FEEDBACK_OP_LIST
#undef CHECKED_BOUNDS_OP_LIST
#undef CHECKED_OP_LIST
#undef ACCESS_OP_LIST
......
......@@ -163,6 +163,30 @@ std::ostream& operator<<(std::ostream&, CheckParameters const&);
CheckParameters const& CheckParametersOf(Operator const*) V8_WARN_UNUSED_RESULT;
class CheckBoundsParameters final {
public:
enum Mode { kAbortOnOutOfBounds, kDeoptOnOutOfBounds };
CheckBoundsParameters(const VectorSlotPair& feedback, Mode mode)
: check_parameters_(feedback), mode_(mode) {}
Mode mode() const { return mode_; }
const CheckParameters& check_parameters() const { return check_parameters_; }
private:
CheckParameters check_parameters_;
Mode mode_;
};
bool operator==(CheckBoundsParameters const&, CheckBoundsParameters const&);
size_t hash_value(CheckBoundsParameters const&);
std::ostream& operator<<(std::ostream&, CheckBoundsParameters const&);
CheckBoundsParameters const& CheckBoundsParametersOf(Operator const*)
V8_WARN_UNUSED_RESULT;
class CheckIfParameters final {
public:
explicit CheckIfParameters(DeoptimizeReason reason,
......@@ -709,7 +733,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const VectorSlotPair& feedback);
const Operator* CheckedUint32Div();
const Operator* CheckedUint32Mod();
const Operator* CheckedUint32Bounds(const VectorSlotPair& feedback);
const Operator* CheckedUint32Bounds(const VectorSlotPair& feedback,
CheckBoundsParameters::Mode mode);
const Operator* CheckedUint32ToInt32(const VectorSlotPair& feedback);
const Operator* CheckedUint32ToTaggedSigned(const VectorSlotPair& feedback);
const Operator* CheckedUint64Bounds(const VectorSlotPair& feedback);
......
......@@ -668,16 +668,18 @@ TEST_F(RedundancyEliminationTest, CheckedUint32Bounds) {
Node* effect = graph()->start();
Node* control = graph()->start();
Node* check1 = effect =
graph()->NewNode(simplified()->CheckedUint32Bounds(feedback1), index,
length, effect, control);
Node* check1 = effect = graph()->NewNode(
simplified()->CheckedUint32Bounds(
feedback1, CheckBoundsParameters::kDeoptOnOutOfBounds),
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), index,
length, effect, control);
Node* check2 = effect = graph()->NewNode(
simplified()->CheckedUint32Bounds(
feedback2, CheckBoundsParameters::kDeoptOnOutOfBounds),
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