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) {
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.
......@@ -1873,7 +1877,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;
}
}
......@@ -1904,6 +1911,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) {
......@@ -1945,8 +1954,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.
......@@ -1969,7 +1991,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;
}
}
......@@ -2268,6 +2292,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);
......@@ -2304,7 +2333,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;
}
......
......@@ -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);
......
......@@ -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.
......
// 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) {
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,
......
......@@ -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);
......
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