Commit e2544f6c authored by danno's avatar danno Committed by Commit Bot

Fix deoptmization of inlined TF instanceOf to call ToBoolean

This CL leverages and extends the deopt-to-stub mechanisms previously
introduced to support deopting from CSA-built builtins (e.g. Array.prototype.forEach).

BUG=v8:6373
LOG=N

Review-Url: https://codereview.chromium.org/2890363002
Cr-Commit-Position: refs/heads/master@{#46144}
parent 731d1b73
...@@ -259,6 +259,22 @@ TF_BUILTIN(ToBoolean, CodeStubAssembler) { ...@@ -259,6 +259,22 @@ TF_BUILTIN(ToBoolean, CodeStubAssembler) {
Return(BooleanConstant(false)); Return(BooleanConstant(false));
} }
// ES6 section 7.1.2 ToBoolean ( argument )
// Requires parameter on stack so that it can be used as a continuation from a
// LAZY deopt.
TF_BUILTIN(ToBooleanLazyDeoptContinuation, CodeStubAssembler) {
Node* value = Parameter(Descriptor::kArgument);
Label return_true(this), return_false(this);
BranchIfToBooleanIsTrue(value, &return_true, &return_false);
BIND(&return_true);
Return(BooleanConstant(true));
BIND(&return_false);
Return(BooleanConstant(false));
}
TF_BUILTIN(ToLength, CodeStubAssembler) { TF_BUILTIN(ToLength, CodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext); Node* context = Parameter(Descriptor::kContext);
......
...@@ -228,6 +228,9 @@ namespace internal { ...@@ -228,6 +228,9 @@ namespace internal {
TFC(Typeof, Typeof, 1) \ TFC(Typeof, Typeof, 1) \
TFC(GetSuperConstructor, Typeof, 1) \ TFC(GetSuperConstructor, Typeof, 1) \
\ \
/* Type conversions continuations */ \
TFC(ToBooleanLazyDeoptContinuation, TypeConversionStackParameter, 1) \
\
/* Handlers */ \ /* Handlers */ \
TFH(LoadICProtoArray, BUILTIN, kNoExtraICState, LoadICProtoArray) \ TFH(LoadICProtoArray, BUILTIN, kNoExtraICState, LoadICProtoArray) \
TFH(LoadICProtoArrayThrowIfNonexistent, BUILTIN, kNoExtraICState, \ TFH(LoadICProtoArrayThrowIfNonexistent, BUILTIN, kNoExtraICState, \
......
...@@ -110,11 +110,11 @@ Node* CreateBuiltinContinuationFrameStateCommon( ...@@ -110,11 +110,11 @@ Node* CreateBuiltinContinuationFrameStateCommon(
FrameStateType frame_type = FrameStateType frame_type =
function.is_null() ? FrameStateType::kBuiltinContinuation function.is_null() ? FrameStateType::kBuiltinContinuation
: FrameStateType::kJavaScriptBuiltinContinuation; : FrameStateType::kJavaScriptBuiltinContinuation;
Handle<SharedFunctionInfo> shared(
Handle<SharedFunctionInfo>(function->shared()));
const FrameStateFunctionInfo* state_info = const FrameStateFunctionInfo* state_info =
common->CreateFrameStateFunctionInfo(frame_type, parameter_count, 0, common->CreateFrameStateFunctionInfo(
shared); frame_type, parameter_count, 0,
function.is_null() ? Handle<SharedFunctionInfo>()
: Handle<SharedFunctionInfo>(function->shared()));
const Operator* op = common->FrameState( const Operator* op = common->FrameState(
bailout_id, OutputFrameStateCombine::Ignore(), state_info); bailout_id, OutputFrameStateCombine::Ignore(), state_info);
...@@ -140,8 +140,12 @@ Node* CreateStubBuiltinContinuationFrameState(JSGraph* js_graph, ...@@ -140,8 +140,12 @@ Node* CreateStubBuiltinContinuationFrameState(JSGraph* js_graph,
CallInterfaceDescriptor descriptor = callable.descriptor(); CallInterfaceDescriptor descriptor = callable.descriptor();
std::vector<Node*> actual_parameters; std::vector<Node*> actual_parameters;
// Stack parameters first // Stack parameters first. If the deoptimization is LAZY, the final parameter
for (int i = 0; i < descriptor.GetStackParameterCount(); ++i) { // is added by the deoptimizer and isn't explicitly passed in the frame state.
int stack_parameter_count =
descriptor.GetRegisterParameterCount() -
(mode == ContinuationFrameStateMode::LAZY ? 1 : 0);
for (int i = 0; i < stack_parameter_count; ++i) {
actual_parameters.push_back( actual_parameters.push_back(
parameters[descriptor.GetRegisterParameterCount() + i]); parameters[descriptor.GetRegisterParameterCount() + i]);
} }
...@@ -152,7 +156,7 @@ Node* CreateStubBuiltinContinuationFrameState(JSGraph* js_graph, ...@@ -152,7 +156,7 @@ Node* CreateStubBuiltinContinuationFrameState(JSGraph* js_graph,
} }
return CreateBuiltinContinuationFrameStateCommon( return CreateBuiltinContinuationFrameStateCommon(
js_graph, name, context, &actual_parameters[0], js_graph, name, context, actual_parameters.data(),
static_cast<int>(actual_parameters.size()), outer_frame_state, static_cast<int>(actual_parameters.size()), outer_frame_state,
Handle<JSFunction>()); Handle<JSFunction>());
} }
......
...@@ -161,6 +161,7 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -161,6 +161,7 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
Node* constructor = NodeProperties::GetValueInput(node, 1); Node* constructor = NodeProperties::GetValueInput(node, 1);
Node* context = NodeProperties::GetContextInput(node); Node* context = NodeProperties::GetContextInput(node);
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
// Check if the right hand side is a known {receiver}. // Check if the right hand side is a known {receiver}.
...@@ -234,11 +235,22 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -234,11 +235,22 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
access_builder.BuildCheckMaps(constructor, &effect, control, access_builder.BuildCheckMaps(constructor, &effect, control,
access_info.receiver_maps()); access_info.receiver_maps());
// Create a nested frame state inside the current method's most-recent frame
// state that will ensure that deopts that happen after this point will not
// fallback to the last Checkpoint--which would completely re-execute the
// instanceof logic--but rather create an activation of a version of the
// ToBoolean stub that finishes the remaining work of instanceof and returns
// to the caller without duplicating side-effects upon a lazy deopt.
Node* continuation_frame_state = CreateStubBuiltinContinuationFrameState(
jsgraph(), Builtins::kToBooleanLazyDeoptContinuation, context, nullptr,
0, frame_state, ContinuationFrameStateMode::LAZY);
// Call the @@hasInstance handler. // Call the @@hasInstance handler.
Node* target = jsgraph()->Constant(constant); Node* target = jsgraph()->Constant(constant);
node->InsertInput(graph()->zone(), 0, target); node->InsertInput(graph()->zone(), 0, target);
node->ReplaceInput(1, constructor); node->ReplaceInput(1, constructor);
node->ReplaceInput(2, object); node->ReplaceInput(2, object);
node->ReplaceInput(4, continuation_frame_state);
node->ReplaceInput(5, effect); node->ReplaceInput(5, effect);
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, javascript()->Call(3, CallFrequency(), VectorSlotPair(), node, javascript()->Call(3, CallFrequency(), VectorSlotPair(),
......
...@@ -1981,8 +1981,11 @@ void Deoptimizer::DoComputeBuiltinContinuation( ...@@ -1981,8 +1981,11 @@ void Deoptimizer::DoComputeBuiltinContinuation(
if (trace_scope_ != NULL) { if (trace_scope_ != NULL) {
PrintF(trace_scope_->file(), PrintF(trace_scope_->file(),
" translating BuiltinContinuation to %s, stack param count %d\n", " translating BuiltinContinuation to %s,"
Builtins::name(builtin_name), stack_param_count); " register param count %d,"
" stack param count %d\n",
Builtins::name(builtin_name), register_parameter_count,
stack_param_count);
} }
unsigned output_frame_offset = output_frame_size; unsigned output_frame_offset = output_frame_size;
...@@ -3203,7 +3206,6 @@ TranslatedFrame TranslatedFrame::ConstructStubFrame( ...@@ -3203,7 +3206,6 @@ TranslatedFrame TranslatedFrame::ConstructStubFrame(
TranslatedFrame TranslatedFrame::BuiltinContinuationFrame( TranslatedFrame TranslatedFrame::BuiltinContinuationFrame(
BailoutId bailout_id, SharedFunctionInfo* shared_info, int height) { BailoutId bailout_id, SharedFunctionInfo* shared_info, int height) {
base::OS::DebugBreak();
TranslatedFrame frame(kBuiltinContinuation, shared_info->GetIsolate(), TranslatedFrame frame(kBuiltinContinuation, shared_info->GetIsolate(),
shared_info, height); shared_info, height);
frame.node_id_ = bailout_id; frame.node_id_ = bailout_id;
...@@ -3337,8 +3339,11 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame( ...@@ -3337,8 +3339,11 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame(
PrintF(trace_file, " => bailout_id=%d, height=%d; inputs:\n", PrintF(trace_file, " => bailout_id=%d, height=%d; inputs:\n",
bailout_id.ToInt(), height); bailout_id.ToInt(), height);
} }
// Add one to the height to account for the context which was implicitly
// added to the translation during code generation.
int height_with_context = height + 1;
return TranslatedFrame::BuiltinContinuationFrame(bailout_id, shared_info, return TranslatedFrame::BuiltinContinuationFrame(bailout_id, shared_info,
height); height_with_context);
} }
case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME: { case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME: {
...@@ -3353,8 +3358,11 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame( ...@@ -3353,8 +3358,11 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame(
PrintF(trace_file, " => bailout_id=%d, height=%d; inputs:\n", PrintF(trace_file, " => bailout_id=%d, height=%d; inputs:\n",
bailout_id.ToInt(), height); bailout_id.ToInt(), height);
} }
// Add one to the height to account for the context which was implicitly
// added to the translation during code generation.
int height_with_context = height + 1;
return TranslatedFrame::JavaScriptBuiltinContinuationFrame( return TranslatedFrame::JavaScriptBuiltinContinuationFrame(
bailout_id, shared_info, height + 1); bailout_id, shared_info, height_with_context);
} }
case Translation::GETTER_STUB_FRAME: { case Translation::GETTER_STUB_FRAME: {
......
...@@ -278,6 +278,16 @@ void TypeConversionDescriptor::InitializePlatformSpecific( ...@@ -278,6 +278,16 @@ void TypeConversionDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers); data->InitializePlatformSpecific(arraysize(registers), registers);
} }
void TypeConversionStackParameterDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
data->InitializePlatformSpecific(0, nullptr);
}
void TypeConversionStackParameterDescriptor::InitializePlatformIndependent(
CallInterfaceDescriptorData* data) {
data->InitializePlatformIndependent(data->register_param_count(), 1, NULL);
}
void MathPowTaggedDescriptor::InitializePlatformSpecific( void MathPowTaggedDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = {exponent()}; Register registers[] = {exponent()};
......
...@@ -35,6 +35,7 @@ class PlatformInterfaceDescriptor; ...@@ -35,6 +35,7 @@ class PlatformInterfaceDescriptor;
V(FastNewObject) \ V(FastNewObject) \
V(FastNewArguments) \ V(FastNewArguments) \
V(TypeConversion) \ V(TypeConversion) \
V(TypeConversionStackParameter) \
V(Typeof) \ V(Typeof) \
V(FastCloneRegExp) \ V(FastCloneRegExp) \
V(FastCloneShallowArray) \ V(FastCloneShallowArray) \
...@@ -513,6 +514,14 @@ class TypeConversionDescriptor final : public CallInterfaceDescriptor { ...@@ -513,6 +514,14 @@ class TypeConversionDescriptor final : public CallInterfaceDescriptor {
static const Register ArgumentRegister(); static const Register ArgumentRegister();
}; };
class TypeConversionStackParameterDescriptor final
: public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kArgument)
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(
TypeConversionStackParameterDescriptor, CallInterfaceDescriptor)
};
class ForInPrepareDescriptor final : public CallInterfaceDescriptor { class ForInPrepareDescriptor final : public CallInterfaceDescriptor {
public: public:
DEFINE_PARAMETERS(kObject) DEFINE_PARAMETERS(kObject)
......
// Copyright 2017 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
var A = {}
A[Symbol.hasInstance] = function(x) {
%DeoptimizeFunction(foo);
return 1;
}
var a = {}
function foo(o) {
return o instanceof A;
}
foo(a);
foo(a);
assertTrue(foo(a) !== 1);
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(a) !== 1);
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