Reviewers: Benedikt Meurer,
Message:
Could you take a look, please?
Description:
[turbofan] Support lazy deopt for truncating store to a typed array.
The change introduces a second frame state (for the state before
the operation) for the StoreProperty nodes. If the store writes
into a typed array, the frame state is used for lazy deopt from
the to-number conversion that is performed by the store.
BUG=v8:3963
LOG=n
R=bmeu...@chromium.org
Please review this at https://codereview.chromium.org/997983004/
Base URL: https://chromium.googlesource.com/v8/v8.git@master
Affected files (+84, -14 lines):
M src/compiler/ast-graph-builder.cc
M src/compiler/js-typed-lowering.cc
M src/compiler/operator-properties.cc
A test/mjsunit/compiler/truncating-store-deopt.js
M test/unittests/compiler/js-operator-unittest.cc
M test/unittests/compiler/js-typed-lowering-unittest.cc
Index: src/compiler/ast-graph-builder.cc
diff --git a/src/compiler/ast-graph-builder.cc
b/src/compiler/ast-graph-builder.cc
index
63696e45cd2e7f2dbe932a2ac415e1c76e30053e..b5a89dac947678a997380821fd959c11415f6482
100644
--- a/src/compiler/ast-graph-builder.cc
+++ b/src/compiler/ast-graph-builder.cc
@@ -1737,10 +1737,14 @@ void
AstGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
VisitForValue(subexpr);
+ Node* frame_state_before = environment()->Checkpoint(
+ subexpr->id(), OutputFrameStateCombine::PokeAt(0));
Node* value = environment()->Pop();
Node* index = jsgraph()->Constant(i);
Node* store = BuildKeyedStore(literal, index, value);
- PrepareFrameState(store, expr->GetIdForElement(i));
+ PrepareFrameStateAfterAndBefore(store, expr->GetIdForElement(i),
+ OutputFrameStateCombine::Ignore(),
+ frame_state_before);
}
environment()->Pop(); // Array literal index.
@@ -1781,7 +1785,10 @@ void
AstGraphBuilder::VisitForInAssignment(Expression* expr, Node* value,
Node* object = environment()->Pop();
value = environment()->Pop();
Node* store = BuildKeyedStore(object, key, value);
- PrepareFrameState(store, bailout_id);
+ // TODO(jarin) Provide a real frame state before.
+ PrepareFrameStateAfterAndBefore(store, bailout_id,
+ OutputFrameStateCombine::Ignore(),
+ jsgraph()->EmptyFrameState());
break;
}
}
@@ -1812,6 +1819,8 @@ void AstGraphBuilder::VisitAssignment(Assignment*
expr) {
// Evaluate the value and potentially handle compound assignments by
loading
// the left-hand side value and performing a binary operation.
+ Node* frame_state_before_store = nullptr;
+ bool needs_frame_state_before = (assign_type == KEYED_PROPERTY);
if (expr->is_compound()) {
Node* old_value = NULL;
switch (assign_type) {
@@ -1853,8 +1862,21 @@ void AstGraphBuilder::VisitAssignment(Assignment*
expr) {
OutputFrameStateCombine::Push(),
frame_state_before);
environment()->Push(value);
+ if (needs_frame_state_before) {
+ frame_state_before_store = environment()->Checkpoint(
+ expr->binary_operation()->id(),
OutputFrameStateCombine::PokeAt(0));
+ }
} else {
VisitForValue(expr->value());
+ if (needs_frame_state_before) {
+ // This frame state can be used for lazy-deopting from a to-number
+ // conversion if we are storing into a typed array. It is important
+ // that the frame state is usable for such lazy deopt (i.e., it has
+ // to specify how to override the value before the conversion, in
this
+ // case, it overwrites the stack top).
+ frame_state_before_store = environment()->Checkpoint(
+ expr->value()->id(), OutputFrameStateCombine::PokeAt(0));
+ }
}
// Store the value.
@@ -1877,7 +1899,9 @@ void AstGraphBuilder::VisitAssignment(Assignment*
expr) {
Node* key = environment()->Pop();
Node* object = environment()->Pop();
Node* store = BuildKeyedStore(object, key, value);
- PrepareFrameState(store, expr->id(),
ast_context()->GetStateCombine());
+ PrepareFrameStateAfterAndBefore(store, expr->id(),
+ ast_context()->GetStateCombine(),
+ frame_state_before_store);
break;
}
}
@@ -2176,6 +2200,11 @@ void
AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
PrepareFrameState(old_value, expr->ToNumberId(),
OutputFrameStateCombine::Push());
+ Node* frame_state_before_store =
+ assign_type == KEYED_PROPERTY
+ ? environment()->Checkpoint(expr->ToNumberId())
+ : nullptr;
+
// Save result for postfix expressions at correct stack depth.
if (is_postfix) environment()->Poke(stack_depth, old_value);
@@ -2212,7 +2241,9 @@ void
AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
Node* object = environment()->Pop();
Node* store = BuildKeyedStore(object, key, value);
environment()->Push(value);
- PrepareFrameState(store, expr->AssignmentId());
+ PrepareFrameStateAfterAndBefore(store, expr->AssignmentId(),
+ OutputFrameStateCombine::Ignore(),
+ frame_state_before_store);
environment()->Pop();
break;
}
Index: src/compiler/js-typed-lowering.cc
diff --git a/src/compiler/js-typed-lowering.cc
b/src/compiler/js-typed-lowering.cc
index
386518e9bd934f9b3ddfdbb46f3fb659e8409938..05363f288103f747fd0f4f171c608ed9a3e20d04
100644
--- a/src/compiler/js-typed-lowering.cc
+++ b/src/compiler/js-typed-lowering.cc
@@ -803,9 +803,11 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node*
node) {
(OperatorProperties::GetFrameStateInputCount(
javascript()->ToNumber()) == 1));
if (FLAG_turbo_deoptimization) {
+ Node* frame_state_for_to_number =
+ NodeProperties::GetFrameStateInput(node, 1);
value = effect =
graph()->NewNode(javascript()->ToNumber(), value, context,
- jsgraph()->EmptyFrameState(), effect,
control);
+ frame_state_for_to_number, effect,
control);
} else {
value = effect = graph()->NewNode(javascript()->ToNumber(),
value,
context, effect, control);
Index: src/compiler/operator-properties.cc
diff --git a/src/compiler/operator-properties.cc
b/src/compiler/operator-properties.cc
index
05891e1a743275dcd081c4cfe791e62775538007..3a91fd6b581b5e6a94e68f36df95efb4fd09ff0a
100644
--- a/src/compiler/operator-properties.cc
+++ b/src/compiler/operator-properties.cc
@@ -64,12 +64,17 @@ int OperatorProperties::GetFrameStateInputCount(const
Operator* op) {
// Properties
case IrOpcode::kJSLoadNamed:
- case IrOpcode::kJSLoadProperty:
case IrOpcode::kJSStoreNamed:
- case IrOpcode::kJSStoreProperty:
+ case IrOpcode::kJSLoadProperty:
case IrOpcode::kJSDeleteProperty:
return 1;
+ // StoreProperty provides a second frame state just before
+ // the operation. This is used to lazy-deoptimize a to-number
+ // conversion for typed arrays.
+ case IrOpcode::kJSStoreProperty:
+ return 2;
+
// Binary operators that can deopt in the middle the operation (e.g.,
// as a result of lazy deopt in ToNumber conversion) need a second
frame
// state so that we can resume before the operation.
Index: test/mjsunit/compiler/truncating-store-deopt.js
diff --git a/test/mjsunit/compiler/truncating-store-deopt.js
b/test/mjsunit/compiler/truncating-store-deopt.js
new file mode 100644
index
0000000000000000000000000000000000000000..a640caf58347d1341e4aa0f2d26d8c9aa373be0a
--- /dev/null
+++ b/test/mjsunit/compiler/truncating-store-deopt.js
@@ -0,0 +1,28 @@
+// Copyright 2015 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 g(a, b, c) {
+ return a + b + c;
+}
+
+var asm = (function Module(global, env, buffer) {
+ "use asm";
+
+ var i32 = new global.Int32Array(buffer);
+
+ // This is not valid asm.js, but we should still generate correct code.
+ function store(x) {
+ return g(1, i32[0] = x, 2);
+ }
+
+ return { store: store };
+})({
+ "Int32Array": Int32Array
+}, {}, new ArrayBuffer(64 * 1024));
+
+var o = { toString : function() { %DeoptimizeFunction(asm.store);
return "1"; } }
+
+asm.store(o);
Index: test/unittests/compiler/js-operator-unittest.cc
diff --git a/test/unittests/compiler/js-operator-unittest.cc
b/test/unittests/compiler/js-operator-unittest.cc
index
2cc3bfadd02b43dfc449868f04e61936a6697c16..f8ef7fbbb223516e1ac35a27991c5cff98002aba
100644
--- a/test/unittests/compiler/js-operator-unittest.cc
+++ b/test/unittests/compiler/js-operator-unittest.cc
@@ -171,7 +171,7 @@ TEST_P(JSStorePropertyOperatorTest,
NumberOfInputsAndOutputs) {
const Operator* op = javascript.StoreProperty(mode);
// TODO(jarin): Get rid of this hack.
- const int frame_state_input_count = FLAG_turbo_deoptimization ? 1 : 0;
+ const int frame_state_input_count = FLAG_turbo_deoptimization ? 2 : 0;
EXPECT_EQ(3, op->ValueInputCount());
EXPECT_EQ(1, OperatorProperties::GetContextInputCount(op));
EXPECT_EQ(frame_state_input_count,
Index: test/unittests/compiler/js-typed-lowering-unittest.cc
diff --git a/test/unittests/compiler/js-typed-lowering-unittest.cc
b/test/unittests/compiler/js-typed-lowering-unittest.cc
index
4fb4b2422be33b31d4f81e08d8d8c3d1c0711fb6..781cf4c2c65fdad1350258e64e470833e47100a9
100644
--- a/test/unittests/compiler/js-typed-lowering-unittest.cc
+++ b/test/unittests/compiler/js-typed-lowering-unittest.cc
@@ -8,6 +8,7 @@
#include "src/compiler/js-typed-lowering.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties.h"
+#include "src/compiler/operator-properties.h"
#include "test/unittests/compiler/compiler-test-utils.h"
#include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h"
@@ -691,8 +692,9 @@ TEST_F(JSTypedLoweringTest,
JSStorePropertyToExternalTypedArray) {
Node* control = graph()->start();
Node* node =
graph()->NewNode(javascript()->StoreProperty(language_mode),
base, key, value, context);
- if (FLAG_turbo_deoptimization) {
- node->AppendInput(zone(), UndefinedConstant());
+ for (int i = 0;
+ i < OperatorProperties::GetFrameStateInputCount(node->op());
i++) {
+ node->AppendInput(zone(), EmptyFrameState());
}
node->AppendInput(zone(), effect);
node->AppendInput(zone(), control);
@@ -736,8 +738,9 @@ TEST_F(JSTypedLoweringTest,
JSStorePropertyToExternalTypedArrayWithConversion) {
Node* control = graph()->start();
Node* node =
graph()->NewNode(javascript()->StoreProperty(language_mode),
base, key, value, context);
- if (FLAG_turbo_deoptimization) {
- node->AppendInput(zone(), UndefinedConstant());
+ for (int i = 0;
+ i < OperatorProperties::GetFrameStateInputCount(node->op());
i++) {
+ node->AppendInput(zone(), EmptyFrameState());
}
node->AppendInput(zone(), effect);
node->AppendInput(zone(), control);
@@ -794,8 +797,9 @@ TEST_F(JSTypedLoweringTest,
JSStorePropertyToExternalTypedArrayWithSafeKey) {
Node* control = graph()->start();
Node* node =
graph()->NewNode(javascript()->StoreProperty(language_mode),
base, key, value, context);
- if (FLAG_turbo_deoptimization) {
- node->AppendInput(zone(), UndefinedConstant());
+ for (int i = 0;
+ i < OperatorProperties::GetFrameStateInputCount(node->op());
i++) {
+ node->AppendInput(zone(), EmptyFrameState());
}
node->AppendInput(zone(), effect);
node->AppendInput(zone(), control);
--
--
v8-dev mailing list
v8-dev@googlegroups.com
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to v8-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.