Commit 0cde253c authored by jarin's avatar jarin Committed by Commit bot

[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=bmeurer@chromium.org

Review URL: https://codereview.chromium.org/997983004

Cr-Commit-Position: refs/heads/master@{#27285}
parent f2429190
...@@ -1829,10 +1829,14 @@ void AstGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { ...@@ -1829,10 +1829,14 @@ void AstGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
VisitForValue(subexpr); VisitForValue(subexpr);
Node* frame_state_before = environment()->Checkpoint(
subexpr->id(), OutputFrameStateCombine::PokeAt(0));
Node* value = environment()->Pop(); Node* value = environment()->Pop();
Node* index = jsgraph()->Constant(i); Node* index = jsgraph()->Constant(i);
Node* store = BuildKeyedStore(literal, index, value); 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. environment()->Pop(); // Array literal index.
...@@ -1873,7 +1877,10 @@ void AstGraphBuilder::VisitForInAssignment(Expression* expr, Node* value, ...@@ -1873,7 +1877,10 @@ void AstGraphBuilder::VisitForInAssignment(Expression* expr, Node* value,
Node* object = environment()->Pop(); Node* object = environment()->Pop();
value = environment()->Pop(); value = environment()->Pop();
Node* store = BuildKeyedStore(object, key, value); 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; break;
} }
} }
...@@ -1904,6 +1911,8 @@ void AstGraphBuilder::VisitAssignment(Assignment* expr) { ...@@ -1904,6 +1911,8 @@ void AstGraphBuilder::VisitAssignment(Assignment* expr) {
// Evaluate the value and potentially handle compound assignments by loading // Evaluate the value and potentially handle compound assignments by loading
// the left-hand side value and performing a binary operation. // 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()) { if (expr->is_compound()) {
Node* old_value = NULL; Node* old_value = NULL;
switch (assign_type) { switch (assign_type) {
...@@ -1945,8 +1954,21 @@ void AstGraphBuilder::VisitAssignment(Assignment* expr) { ...@@ -1945,8 +1954,21 @@ void AstGraphBuilder::VisitAssignment(Assignment* expr) {
OutputFrameStateCombine::Push(), OutputFrameStateCombine::Push(),
frame_state_before); frame_state_before);
environment()->Push(value); environment()->Push(value);
if (needs_frame_state_before) {
frame_state_before_store = environment()->Checkpoint(
expr->binary_operation()->id(), OutputFrameStateCombine::PokeAt(0));
}
} else { } else {
VisitForValue(expr->value()); 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. // Store the value.
...@@ -1969,7 +1991,9 @@ void AstGraphBuilder::VisitAssignment(Assignment* expr) { ...@@ -1969,7 +1991,9 @@ void AstGraphBuilder::VisitAssignment(Assignment* expr) {
Node* key = environment()->Pop(); Node* key = environment()->Pop();
Node* object = environment()->Pop(); Node* object = environment()->Pop();
Node* store = BuildKeyedStore(object, key, value); 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; break;
} }
} }
...@@ -2268,6 +2292,11 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) { ...@@ -2268,6 +2292,11 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
PrepareFrameState(old_value, expr->ToNumberId(), PrepareFrameState(old_value, expr->ToNumberId(),
OutputFrameStateCombine::Push()); 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. // Save result for postfix expressions at correct stack depth.
if (is_postfix) environment()->Poke(stack_depth, old_value); if (is_postfix) environment()->Poke(stack_depth, old_value);
...@@ -2304,7 +2333,9 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) { ...@@ -2304,7 +2333,9 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
Node* object = environment()->Pop(); Node* object = environment()->Pop();
Node* store = BuildKeyedStore(object, key, value); Node* store = BuildKeyedStore(object, key, value);
environment()->Push(value); environment()->Push(value);
PrepareFrameState(store, expr->AssignmentId()); PrepareFrameStateAfterAndBefore(store, expr->AssignmentId(),
OutputFrameStateCombine::Ignore(),
frame_state_before_store);
environment()->Pop(); environment()->Pop();
break; break;
} }
......
...@@ -803,9 +803,11 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) { ...@@ -803,9 +803,11 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
(OperatorProperties::GetFrameStateInputCount( (OperatorProperties::GetFrameStateInputCount(
javascript()->ToNumber()) == 1)); javascript()->ToNumber()) == 1));
if (FLAG_turbo_deoptimization) { if (FLAG_turbo_deoptimization) {
Node* frame_state_for_to_number =
NodeProperties::GetFrameStateInput(node, 1);
value = effect = value = effect =
graph()->NewNode(javascript()->ToNumber(), value, context, graph()->NewNode(javascript()->ToNumber(), value, context,
jsgraph()->EmptyFrameState(), effect, control); frame_state_for_to_number, effect, control);
} else { } else {
value = effect = graph()->NewNode(javascript()->ToNumber(), value, value = effect = graph()->NewNode(javascript()->ToNumber(), value,
context, effect, control); context, effect, control);
......
...@@ -64,12 +64,17 @@ int OperatorProperties::GetFrameStateInputCount(const Operator* op) { ...@@ -64,12 +64,17 @@ int OperatorProperties::GetFrameStateInputCount(const Operator* op) {
// Properties // Properties
case IrOpcode::kJSLoadNamed: case IrOpcode::kJSLoadNamed:
case IrOpcode::kJSLoadProperty:
case IrOpcode::kJSStoreNamed: case IrOpcode::kJSStoreNamed:
case IrOpcode::kJSStoreProperty: case IrOpcode::kJSLoadProperty:
case IrOpcode::kJSDeleteProperty: case IrOpcode::kJSDeleteProperty:
return 1; 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., // 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 // as a result of lazy deopt in ToNumber conversion) need a second frame
// state so that we can resume before the operation. // state so that we can resume before the operation.
......
// 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);
...@@ -171,7 +171,7 @@ TEST_P(JSStorePropertyOperatorTest, NumberOfInputsAndOutputs) { ...@@ -171,7 +171,7 @@ TEST_P(JSStorePropertyOperatorTest, NumberOfInputsAndOutputs) {
const Operator* op = javascript.StoreProperty(mode); const Operator* op = javascript.StoreProperty(mode);
// TODO(jarin): Get rid of this hack. // 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(3, op->ValueInputCount());
EXPECT_EQ(1, OperatorProperties::GetContextInputCount(op)); EXPECT_EQ(1, OperatorProperties::GetContextInputCount(op));
EXPECT_EQ(frame_state_input_count, EXPECT_EQ(frame_state_input_count,
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "src/compiler/js-typed-lowering.h" #include "src/compiler/js-typed-lowering.h"
#include "src/compiler/machine-operator.h" #include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties.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/compiler-test-utils.h"
#include "test/unittests/compiler/graph-unittest.h" #include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h" #include "test/unittests/compiler/node-test-utils.h"
...@@ -691,8 +692,9 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArray) { ...@@ -691,8 +692,9 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArray) {
Node* control = graph()->start(); Node* control = graph()->start();
Node* node = graph()->NewNode(javascript()->StoreProperty(language_mode), Node* node = graph()->NewNode(javascript()->StoreProperty(language_mode),
base, key, value, context); base, key, value, context);
if (FLAG_turbo_deoptimization) { for (int i = 0;
node->AppendInput(zone(), UndefinedConstant()); i < OperatorProperties::GetFrameStateInputCount(node->op()); i++) {
node->AppendInput(zone(), EmptyFrameState());
} }
node->AppendInput(zone(), effect); node->AppendInput(zone(), effect);
node->AppendInput(zone(), control); node->AppendInput(zone(), control);
...@@ -736,8 +738,9 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithConversion) { ...@@ -736,8 +738,9 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithConversion) {
Node* control = graph()->start(); Node* control = graph()->start();
Node* node = graph()->NewNode(javascript()->StoreProperty(language_mode), Node* node = graph()->NewNode(javascript()->StoreProperty(language_mode),
base, key, value, context); base, key, value, context);
if (FLAG_turbo_deoptimization) { for (int i = 0;
node->AppendInput(zone(), UndefinedConstant()); i < OperatorProperties::GetFrameStateInputCount(node->op()); i++) {
node->AppendInput(zone(), EmptyFrameState());
} }
node->AppendInput(zone(), effect); node->AppendInput(zone(), effect);
node->AppendInput(zone(), control); node->AppendInput(zone(), control);
...@@ -794,8 +797,9 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithSafeKey) { ...@@ -794,8 +797,9 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithSafeKey) {
Node* control = graph()->start(); Node* control = graph()->start();
Node* node = graph()->NewNode(javascript()->StoreProperty(language_mode), Node* node = graph()->NewNode(javascript()->StoreProperty(language_mode),
base, key, value, context); base, key, value, context);
if (FLAG_turbo_deoptimization) { for (int i = 0;
node->AppendInput(zone(), UndefinedConstant()); i < OperatorProperties::GetFrameStateInputCount(node->op()); i++) {
node->AppendInput(zone(), EmptyFrameState());
} }
node->AppendInput(zone(), effect); node->AppendInput(zone(), effect);
node->AppendInput(zone(), control); node->AppendInput(zone(), control);
......
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