[turbofan] Allow array grow to disable speculation

Add feedback to GrowFastElements operator and thread it
through to the deoptimize node it the lowering. The CL
uses the feedback to allow Array.push to disable speculation
if the grow operation deopts.

Bug: v8:7127, v8:7204
Change-Id: Ib5850a93759b9194c0fc2f191f6adf5d49cb7f55
Reviewed-on: https://chromium-review.googlesource.com/827128
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50145}
This commit is contained in:
Sigurd Schneider 2017-12-15 14:59:20 +01:00 committed by Commit Bot
parent fb8efb12fa
commit ffe7919f12
8 changed files with 118 additions and 22 deletions

View File

@ -3229,7 +3229,7 @@ Node* EffectControlLinearizer::LowerEnsureWritableFastElements(Node* node) {
Node* EffectControlLinearizer::LowerMaybeGrowFastElements(Node* node,
Node* frame_state) {
GrowFastElementsMode mode = GrowFastElementsModeOf(node->op());
GrowFastElementsParameters params = GrowFastElementsParametersOf(node->op());
Node* object = node->InputAt(0);
Node* elements = node->InputAt(1);
Node* index = node->InputAt(2);
@ -3248,7 +3248,7 @@ Node* EffectControlLinearizer::LowerMaybeGrowFastElements(Node* node,
// We need to grow the {elements} for {object}.
Operator::Properties properties = Operator::kEliminatable;
Callable callable =
(mode == GrowFastElementsMode::kDoubleElements)
(params.mode() == GrowFastElementsMode::kDoubleElements)
? Builtins::CallableFor(isolate(), Builtins::kGrowFastDoubleElements)
: Builtins::CallableFor(isolate(),
Builtins::kGrowFastSmiOrObjectElements);
@ -3262,7 +3262,7 @@ Node* EffectControlLinearizer::LowerMaybeGrowFastElements(Node* node,
// Ensure that we were able to grow the {elements}.
// TODO(turbofan): We use kSmi as reason here similar to Crankshaft,
// but maybe we should just introduce a reason that makes sense.
__ DeoptimizeIf(DeoptimizeReason::kSmi, VectorSlotPair(),
__ DeoptimizeIf(DeoptimizeReason::kSmi, params.feedback(),
ObjectIsSmi(new_elements), frame_state);
__ Goto(&done, new_elements);

View File

@ -1085,7 +1085,8 @@ Reduction JSBuiltinReducer::ReduceArrayPush(Node* node) {
? GrowFastElementsMode::kDoubleElements
: GrowFastElementsMode::kSmiOrObjectElements;
elements = effect = graph()->NewNode(
simplified()->MaybeGrowFastElements(mode), receiver, elements,
simplified()->MaybeGrowFastElements(mode, p.feedback()), receiver,
elements,
graph()->NewNode(simplified()->NumberAdd(), length,
jsgraph()->Constant(num_values - 1)),
elements_length, effect, control);

View File

@ -1636,9 +1636,9 @@ Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control,
GrowFastElementsMode mode =
IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements
: GrowFastElementsMode::kSmiOrObjectElements;
elements = etrue =
graph()->NewNode(simplified()->MaybeGrowFastElements(mode), a, elements,
checked_to, elements_length, etrue, if_true);
elements = etrue = graph()->NewNode(
simplified()->MaybeGrowFastElements(mode, VectorSlotPair()), a,
elements, checked_to, elements_length, etrue, if_true);
// Update the length of {a}.
Node* new_length_a = graph()->NewNode(simplified()->NumberAdd(), checked_to,

View File

@ -2444,8 +2444,8 @@ JSNativeContextSpecialization::BuildElementAccess(
? GrowFastElementsMode::kDoubleElements
: GrowFastElementsMode::kSmiOrObjectElements;
elements = effect = graph()->NewNode(
simplified()->MaybeGrowFastElements(mode), receiver, elements,
index, elements_length, effect, control);
simplified()->MaybeGrowFastElements(mode, VectorSlotPair()),
receiver, elements, index, elements_length, effect, control);
// Also update the "length" property if {receiver} is a JSArray.
if (receiver_is_jsarray) {

View File

@ -811,12 +811,12 @@ Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) {
}
Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) {
GrowFastElementsMode mode = GrowFastElementsModeOf(node->op());
GrowFastElementsParameters params = GrowFastElementsParametersOf(node->op());
Node* const object = NodeProperties::GetValueInput(node, 0);
Node* const effect = NodeProperties::GetEffectInput(node);
AbstractState const* state = node_states_.Get(effect);
if (state == nullptr) return NoChange();
if (mode == GrowFastElementsMode::kDoubleElements) {
if (params.mode() == GrowFastElementsMode::kDoubleElements) {
// We know that the resulting elements have the fixed double array map.
state = state->SetMaps(
node, ZoneHandleSet<Map>(factory()->fixed_double_array_map()), zone());

View File

@ -292,9 +292,28 @@ std::ostream& operator<<(std::ostream& os, GrowFastElementsMode mode) {
UNREACHABLE();
}
GrowFastElementsMode GrowFastElementsModeOf(const Operator* op) {
bool operator==(const GrowFastElementsParameters& lhs,
const GrowFastElementsParameters& rhs) {
return lhs.mode() == rhs.mode() && lhs.feedback() == rhs.feedback();
}
inline size_t hash_value(const GrowFastElementsParameters& params) {
return base::hash_combine(params.mode(), params.feedback());
}
std::ostream& operator<<(std::ostream& os,
const GrowFastElementsParameters& params) {
os << params.mode();
if (params.feedback().IsValid()) {
os << params.feedback();
}
return os;
}
const GrowFastElementsParameters& GrowFastElementsParametersOf(
const Operator* op) {
DCHECK_EQ(IrOpcode::kMaybeGrowFastElements, op->opcode());
return OpParameter<GrowFastElementsMode>(op);
return OpParameter<GrowFastElementsParameters>(op);
}
bool operator==(ElementsTransition const& lhs, ElementsTransition const& rhs) {
@ -903,6 +922,20 @@ struct SimplifiedOperatorGlobalCache final {
};
EnsureWritableFastElementsOperator kEnsureWritableFastElements;
template <GrowFastElementsMode kMode>
struct GrowFastElementsOperator final
: public Operator1<GrowFastElementsParameters> {
GrowFastElementsOperator()
: Operator1(IrOpcode::kMaybeGrowFastElements, Operator::kNoThrow,
"MaybeGrowFastElements", 4, 1, 1, 1, 1, 0,
GrowFastElementsParameters(kMode, VectorSlotPair())) {}
};
GrowFastElementsOperator<GrowFastElementsMode::kDoubleElements>
kGrowFastElementsOperatorDoubleElements;
GrowFastElementsOperator<GrowFastElementsMode::kSmiOrObjectElements>
kGrowFastElementsOperatorSmiOrObjectElements;
struct LoadFieldByIndexOperator final : public Operator {
LoadFieldByIndexOperator()
: Operator( // --
@ -1148,13 +1181,21 @@ const Operator* SimplifiedOperatorBuilder::EnsureWritableFastElements() {
}
const Operator* SimplifiedOperatorBuilder::MaybeGrowFastElements(
GrowFastElementsMode mode) {
return new (zone()) Operator1<GrowFastElementsMode>( // --
IrOpcode::kMaybeGrowFastElements, // opcode
Operator::kNoThrow, // flags
"MaybeGrowFastElements", // name
4, 1, 1, 1, 1, 0, // counts
mode); // parameter
GrowFastElementsMode mode, const VectorSlotPair& feedback) {
if (!feedback.IsValid()) {
switch (mode) {
case GrowFastElementsMode::kDoubleElements:
return &cache_.kGrowFastElementsOperatorDoubleElements;
case GrowFastElementsMode::kSmiOrObjectElements:
return &cache_.kGrowFastElementsOperatorSmiOrObjectElements;
}
}
return new (zone()) Operator1<GrowFastElementsParameters>( // --
IrOpcode::kMaybeGrowFastElements, // opcode
Operator::kNoThrow, // flags
"MaybeGrowFastElements", // name
4, 1, 1, 1, 1, 0, // counts
GrowFastElementsParameters(mode, feedback)); // parameter
}
const Operator* SimplifiedOperatorBuilder::TransitionElementsKind(

View File

@ -225,7 +225,29 @@ inline size_t hash_value(GrowFastElementsMode mode) {
std::ostream& operator<<(std::ostream&, GrowFastElementsMode);
GrowFastElementsMode GrowFastElementsModeOf(const Operator*) WARN_UNUSED_RESULT;
class GrowFastElementsParameters {
public:
GrowFastElementsParameters(GrowFastElementsMode mode,
const VectorSlotPair& feedback)
: mode_(mode), feedback_(feedback) {}
GrowFastElementsMode mode() const { return mode_; }
const VectorSlotPair& feedback() const { return feedback_; }
private:
GrowFastElementsMode mode_;
VectorSlotPair feedback_;
};
bool operator==(const GrowFastElementsParameters&,
const GrowFastElementsParameters&);
inline size_t hash_value(const GrowFastElementsParameters&);
std::ostream& operator<<(std::ostream&, const GrowFastElementsParameters&);
const GrowFastElementsParameters& GrowFastElementsParametersOf(const Operator*)
WARN_UNUSED_RESULT;
// A descriptor for elements kind transitions.
class ElementsTransition final {
@ -542,7 +564,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* EnsureWritableFastElements();
// maybe-grow-fast-elements object, elements, index, length
const Operator* MaybeGrowFastElements(GrowFastElementsMode mode);
const Operator* MaybeGrowFastElements(GrowFastElementsMode mode,
const VectorSlotPair& feedback);
// transition-elements-kind object, from-map, to-map
const Operator* TransitionElementsKind(ElementsTransition transition);

View File

@ -41,3 +41,34 @@
foo([0.34234]);
assertOptimized(foo);
})();
(function test() {
const N = 128 * 1024;
function foo(a) { a.push(1); }
foo(new Array(N));
foo(new Array(N));
%OptimizeFunctionOnNextCall(foo);
foo(new Array(N));
%OptimizeFunctionOnNextCall(foo);
foo(new Array(N));
assertOptimized(foo);
})();
(function test() {
function mkArray() {
const N = 128 * 1024;
let a = [0.1];
a.length = N;
return a;
}
function foo(a) { a.push(0.23441233123); }
foo(mkArray());
foo(mkArray());
%OptimizeFunctionOnNextCall(foo);
foo(mkArray());
%OptimizeFunctionOnNextCall(foo);
foo(mkArray());
assertOptimized(foo);
})();