Commit 2026d5cb authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[turbofan] [builtins] Unify construct builtins for JS functions and classes...

[turbofan] [builtins] Unify construct builtins for JS functions and classes and add inlining and deoptimizer support

BUG=v8:6180
R=mstarzinger@chromium.org

Change-Id: Iac5782a0f6b0ff92293421656d907073cfc3f5dd
Reviewed-on: https://chromium-review.googlesource.com/489525
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45232}
parent 6bfee50e
This diff is collapsed.
This diff is collapsed.
...@@ -91,10 +91,9 @@ namespace internal { ...@@ -91,10 +91,9 @@ namespace internal {
ASM(Construct) \ ASM(Construct) \
ASM(ConstructWithSpread) \ ASM(ConstructWithSpread) \
ASM(JSConstructStubApi) \ ASM(JSConstructStubApi) \
ASM(JSConstructStubGeneric) \ ASM(JSConstructStubGenericRestrictedReturn) \
ASM(JSConstructStubGenericUnrestrictedReturn) \
ASM(JSBuiltinsConstructStub) \ ASM(JSBuiltinsConstructStub) \
ASM(JSBuiltinsConstructStubForBase) \
ASM(JSBuiltinsConstructStubForDerived) \
TFC(FastNewClosure, FastNewClosure, 1) \ TFC(FastNewClosure, FastNewClosure, 1) \
TFC(FastNewFunctionContextEval, FastNewFunctionContext, 1) \ TFC(FastNewFunctionContextEval, FastNewFunctionContext, 1) \
TFC(FastNewFunctionContextFunction, FastNewFunctionContext, 1) \ TFC(FastNewFunctionContextFunction, FastNewFunctionContext, 1) \
......
...@@ -249,6 +249,12 @@ bool Builtins::HasCppImplementation(int index) { ...@@ -249,6 +249,12 @@ bool Builtins::HasCppImplementation(int index) {
BUILTIN_LIST_ALL(DEFINE_BUILTIN_ACCESSOR) BUILTIN_LIST_ALL(DEFINE_BUILTIN_ACCESSOR)
#undef DEFINE_BUILTIN_ACCESSOR #undef DEFINE_BUILTIN_ACCESSOR
Handle<Code> Builtins::JSConstructStubGeneric() {
return FLAG_harmony_restrict_constructor_return
? JSConstructStubGenericRestrictedReturn()
: JSConstructStubGenericUnrestrictedReturn();
}
// static // static
bool Builtins::AllowDynamicFunction(Isolate* isolate, Handle<JSFunction> target, bool Builtins::AllowDynamicFunction(Isolate* isolate, Handle<JSFunction> target,
Handle<JSObject> target_global_proxy) { Handle<JSObject> target_global_proxy) {
......
...@@ -65,6 +65,7 @@ class Builtins { ...@@ -65,6 +65,7 @@ class Builtins {
Handle<Code> NewFunctionContext(ScopeType scope_type); Handle<Code> NewFunctionContext(ScopeType scope_type);
Handle<Code> NewCloneShallowArray(AllocationSiteMode allocation_mode); Handle<Code> NewCloneShallowArray(AllocationSiteMode allocation_mode);
Handle<Code> NewCloneShallowObject(int length); Handle<Code> NewCloneShallowObject(int length);
Handle<Code> JSConstructStubGeneric();
Code* builtin(Name name) { Code* builtin(Name name) {
// Code::cast cannot be used here since we access builtins // Code::cast cannot be used here since we access builtins
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -335,10 +335,11 @@ bool NeedsImplicitReceiver(Handle<SharedFunctionInfo> shared_info) { ...@@ -335,10 +335,11 @@ bool NeedsImplicitReceiver(Handle<SharedFunctionInfo> shared_info) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
Isolate* const isolate = shared_info->GetIsolate(); Isolate* const isolate = shared_info->GetIsolate();
Code* const construct_stub = shared_info->construct_stub(); Code* const construct_stub = shared_info->construct_stub();
return construct_stub != *isolate->builtins()->JSBuiltinsConstructStub() && if (construct_stub == *isolate->builtins()->JSConstructStubGeneric()) {
construct_stub != return !IsDerivedConstructor(shared_info->kind());
*isolate->builtins()->JSBuiltinsConstructStubForDerived() && } else {
construct_stub != *isolate->builtins()->JSConstructStubApi(); return false;
}
} }
bool IsNonConstructible(Handle<SharedFunctionInfo> shared_info) { bool IsNonConstructible(Handle<SharedFunctionInfo> shared_info) {
...@@ -486,32 +487,6 @@ Reduction JSInliner::ReduceJSCall(Node* node) { ...@@ -486,32 +487,6 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
return NoChange(); return NoChange();
} }
// TODO(6180): Don't inline class constructors for now, as the
// inlining logic doesn't deal properly with class constructors
// that return a primitive.
if (FLAG_harmony_restrict_constructor_return &&
node->opcode() == IrOpcode::kJSConstruct &&
IsClassConstructor(shared_info->kind())) {
TRACE(
"Not inlining %s into %s because class constructor inlining is"
"not supported.\n",
shared_info->DebugName()->ToCString().get(),
info_->shared_info()->DebugName()->ToCString().get());
return NoChange();
}
// TODO(706642): Don't inline derived class constructors for now, as the
// inlining logic doesn't deal properly with derived class constructors
// that return a primitive, i.e. it's not in sync with what the Parser
// and the JSConstructSub does.
if (node->opcode() == IrOpcode::kJSConstruct &&
IsDerivedConstructor(shared_info->kind())) {
TRACE("Not inlining %s into %s because constructor is derived.\n",
shared_info->DebugName()->ToCString().get(),
info_->shared_info()->DebugName()->ToCString().get());
return NoChange();
}
// Class constructors are callable, but [[Call]] will raise an exception. // Class constructors are callable, but [[Call]] will raise an exception.
// See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
if (node->opcode() == IrOpcode::kJSCall && if (node->opcode() == IrOpcode::kJSCall &&
...@@ -669,21 +644,94 @@ Reduction JSInliner::ReduceJSCall(Node* node) { ...@@ -669,21 +644,94 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
uncaught_subcalls.push_back(create); // Adds {IfSuccess} & {IfException}. uncaught_subcalls.push_back(create); // Adds {IfSuccess} & {IfException}.
NodeProperties::ReplaceControlInput(node, create); NodeProperties::ReplaceControlInput(node, create);
NodeProperties::ReplaceEffectInput(node, create); NodeProperties::ReplaceEffectInput(node, create);
// Insert a check of the return value to determine whether the return Node* node_success =
// value or the implicit receiver should be selected as a result of the NodeProperties::FindSuccessfulControlProjection(node);
// call. // Placeholder to hold {node}'s value dependencies while {node} is
Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), node); // replaced.
Node* select = Node* dummy = graph()->NewNode(common()->Dead());
graph()->NewNode(common()->Select(MachineRepresentation::kTagged), NodeProperties::ReplaceUses(node, dummy, node, node, node);
check, node, create); Node* result;
NodeProperties::ReplaceUses(node, select, node, node, node); if (FLAG_harmony_restrict_constructor_return &&
// Fix-up inputs that have been mangled by the {ReplaceUses} call above. IsClassConstructor(shared_info->kind())) {
NodeProperties::ReplaceValueInput(select, node, 1); // Fix-up input. Node* is_undefined =
NodeProperties::ReplaceValueInput(check, node, 0); // Fix-up input. graph()->NewNode(simplified()->ReferenceEqual(), node,
jsgraph()->UndefinedConstant());
Node* branch_is_undefined =
graph()->NewNode(common()->Branch(), is_undefined, node_success);
Node* branch_is_undefined_true =
graph()->NewNode(common()->IfTrue(), branch_is_undefined);
Node* branch_is_undefined_false =
graph()->NewNode(common()->IfFalse(), branch_is_undefined);
Node* is_receiver =
graph()->NewNode(simplified()->ObjectIsReceiver(), node);
Node* branch_is_receiver = graph()->NewNode(
common()->Branch(), is_receiver, branch_is_undefined_false);
Node* branch_is_receiver_true =
graph()->NewNode(common()->IfTrue(), branch_is_receiver);
Node* branch_is_receiver_false =
graph()->NewNode(common()->IfFalse(), branch_is_receiver);
branch_is_receiver_false =
graph()->NewNode(javascript()->CallRuntime(
Runtime::kThrowConstructorReturnedNonObject),
context, NodeProperties::GetFrameStateInput(node),
node, branch_is_receiver_false);
uncaught_subcalls.push_back(branch_is_receiver_false);
branch_is_receiver_false =
graph()->NewNode(common()->Throw(), branch_is_receiver_false,
branch_is_receiver_false);
NodeProperties::MergeControlToEnd(graph(), common(),
branch_is_receiver_false);
Node* merge =
graph()->NewNode(common()->Merge(2), branch_is_undefined_true,
branch_is_receiver_true);
result =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
create, node, merge);
NodeProperties::ReplaceUses(node_success, node_success, node_success,
merge);
// Fix input destroyed by the above {ReplaceUses} call.
NodeProperties::ReplaceControlInput(branch_is_undefined, node_success,
0);
} else {
// Insert a check of the return value to determine whether the return
// value or the implicit receiver should be selected as a result of the
// call.
Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), node);
result =
graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
check, node, create);
}
receiver = create; // The implicit receiver. receiver = create; // The implicit receiver.
NodeProperties::ReplaceUses(dummy, result);
} else if (IsDerivedConstructor(shared_info->kind())) {
Node* node_success =
NodeProperties::FindSuccessfulControlProjection(node);
Node* is_receiver =
graph()->NewNode(simplified()->ObjectIsReceiver(), node);
Node* branch_is_receiver =
graph()->NewNode(common()->Branch(), is_receiver, node_success);
Node* branch_is_receiver_true =
graph()->NewNode(common()->IfTrue(), branch_is_receiver);
Node* branch_is_receiver_false =
graph()->NewNode(common()->IfFalse(), branch_is_receiver);
branch_is_receiver_false =
graph()->NewNode(javascript()->CallRuntime(
Runtime::kThrowConstructorReturnedNonObject),
context, NodeProperties::GetFrameStateInput(node),
node, branch_is_receiver_false);
uncaught_subcalls.push_back(branch_is_receiver_false);
branch_is_receiver_false =
graph()->NewNode(common()->Throw(), branch_is_receiver_false,
branch_is_receiver_false);
NodeProperties::MergeControlToEnd(graph(), common(),
branch_is_receiver_false);
NodeProperties::ReplaceUses(node_success, node_success, node_success,
branch_is_receiver_true);
// Fix input destroyed by the above {ReplaceUses} call.
NodeProperties::ReplaceControlInput(branch_is_receiver, node_success, 0);
} }
node->ReplaceInput(1, receiver); node->ReplaceInput(1, receiver);
// Insert a construct stub frame into the chain of frame states. This will // Insert a construct stub frame into the chain of frame states. This will
// reconstruct the proper frame when deoptimizing within the constructor. // reconstruct the proper frame when deoptimizing within the constructor.
frame_state = CreateArtificialFrameState( frame_state = CreateArtificialFrameState(
......
...@@ -138,6 +138,18 @@ bool NodeProperties::IsExceptionalCall(Node* node, Node** out_exception) { ...@@ -138,6 +138,18 @@ bool NodeProperties::IsExceptionalCall(Node* node, Node** out_exception) {
return false; return false;
} }
// static
Node* NodeProperties::FindSuccessfulControlProjection(Node* node) {
DCHECK_GT(node->op()->ControlOutputCount(), 0);
if (node->op()->HasProperty(Operator::kNoThrow)) return node;
for (Edge const edge : node->use_edges()) {
if (!NodeProperties::IsControlEdge(edge)) continue;
if (edge.from()->opcode() == IrOpcode::kIfSuccess) {
return edge.from();
}
}
return node;
}
// static // static
void NodeProperties::ReplaceValueInput(Node* node, Node* value, int index) { void NodeProperties::ReplaceValueInput(Node* node, Node* value, int index) {
......
...@@ -79,6 +79,10 @@ class V8_EXPORT_PRIVATE NodeProperties final { ...@@ -79,6 +79,10 @@ class V8_EXPORT_PRIVATE NodeProperties final {
// the present IfException projection is returned via {out_exception}. // the present IfException projection is returned via {out_exception}.
static bool IsExceptionalCall(Node* node, Node** out_exception = nullptr); static bool IsExceptionalCall(Node* node, Node** out_exception = nullptr);
// Returns the node producing the successful control output of {node}. This is
// the IfSuccess projection of {node} if present and {node} itself otherwise.
static Node* FindSuccessfulControlProjection(Node* node);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Miscellaneous mutators. // Miscellaneous mutators.
......
...@@ -9538,14 +9538,14 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) { ...@@ -9538,14 +9538,14 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
expr->IsMonomorphic() && expr->IsMonomorphic() &&
IsAllocationInlineable(expr->target())) { IsAllocationInlineable(expr->target())) {
Handle<JSFunction> constructor = expr->target(); Handle<JSFunction> constructor = expr->target();
DCHECK( DCHECK(constructor->shared()->construct_stub() ==
constructor->shared()->construct_stub() == isolate()->builtins()->builtin(
isolate()->builtins()->builtin(Builtins::kJSConstructStubGeneric) || Builtins::kJSConstructStubGenericRestrictedReturn) ||
constructor->shared()->construct_stub() == constructor->shared()->construct_stub() ==
isolate()->builtins()->builtin(Builtins::kJSConstructStubApi) || isolate()->builtins()->builtin(
constructor->shared()->construct_stub() == Builtins::kJSConstructStubGenericUnrestrictedReturn) ||
isolate()->builtins()->builtin( constructor->shared()->construct_stub() ==
Builtins::kJSBuiltinsConstructStubForBase)); isolate()->builtins()->builtin(Builtins::kJSConstructStubApi));
HValue* check = Add<HCheckValue>(function, constructor); HValue* check = Add<HCheckValue>(function, constructor);
// Force completion of inobject slack tracking before generating // Force completion of inobject slack tracking before generating
......
...@@ -1556,7 +1556,10 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame, ...@@ -1556,7 +1556,10 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
int input_index = 0; int input_index = 0;
Builtins* builtins = isolate_->builtins(); Builtins* builtins = isolate_->builtins();
Code* construct_stub = builtins->builtin(Builtins::kJSConstructStubGeneric); Code* construct_stub = builtins->builtin(
FLAG_harmony_restrict_constructor_return
? Builtins::kJSConstructStubGenericRestrictedReturn
: Builtins::kJSConstructStubGenericUnrestrictedReturn);
BailoutId bailout_id = translated_frame->node_id(); BailoutId bailout_id = translated_frame->node_id();
unsigned height = translated_frame->height(); unsigned height = translated_frame->height();
unsigned height_in_bytes = height * kPointerSize; unsigned height_in_bytes = height * kPointerSize;
...@@ -1662,18 +1665,22 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame, ...@@ -1662,18 +1665,22 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
PrintF(trace_scope_->file(), "(%d)\n", height - 1); PrintF(trace_scope_->file(), "(%d)\n", height - 1);
} }
// The constructor function was mentioned explicitly in the
// CONSTRUCT_STUB_FRAME.
output_offset -= kPointerSize;
value = reinterpret_cast<intptr_t>(function);
WriteValueToOutput(function, 0, frame_index, output_offset,
"constructor function ");
// The deopt info contains the implicit receiver or the new target at the
// position of the receiver. Copy it to the top of stack.
output_offset -= kPointerSize;
value = output_frame->GetFrameSlot(output_frame_size - kPointerSize);
output_frame->SetFrameSlot(output_offset, value);
if (bailout_id == BailoutId::ConstructStubCreate()) { if (bailout_id == BailoutId::ConstructStubCreate()) {
// The function was mentioned explicitly in the CONSTRUCT_STUB_FRAME. DebugPrintOutputSlot(value, frame_index, output_offset, "new target\n");
output_offset -= kPointerSize;
value = reinterpret_cast<intptr_t>(function);
WriteValueToOutput(function, 0, frame_index, output_offset, "function ");
} else { } else {
DCHECK(bailout_id == BailoutId::ConstructStubInvoke()); CHECK(bailout_id == BailoutId::ConstructStubInvoke());
// The newly allocated object was passed as receiver in the artificial
// constructor stub environment created by HEnvironment::CopyForInlining().
output_offset -= kPointerSize;
value = output_frame->GetFrameSlot(output_frame_size - kPointerSize);
output_frame->SetFrameSlot(output_offset, value);
DebugPrintOutputSlot(value, frame_index, output_offset, DebugPrintOutputSlot(value, frame_index, output_offset,
"allocated receiver\n"); "allocated receiver\n");
} }
...@@ -1684,8 +1691,7 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame, ...@@ -1684,8 +1691,7 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
Register result_reg = FullCodeGenerator::result_register(); Register result_reg = FullCodeGenerator::result_register();
value = input_->GetRegister(result_reg.code()); value = input_->GetRegister(result_reg.code());
output_frame->SetFrameSlot(output_offset, value); output_frame->SetFrameSlot(output_offset, value);
DebugPrintOutputSlot(value, frame_index, output_offset, DebugPrintOutputSlot(value, frame_index, output_offset, "subcall result\n");
"constructor result\n");
output_frame->SetState( output_frame->SetState(
Smi::FromInt(static_cast<int>(BailoutState::TOS_REGISTER))); Smi::FromInt(static_cast<int>(BailoutState::TOS_REGISTER)));
......
...@@ -353,8 +353,10 @@ class ConstructFrameConstants : public TypedFrameConstants { ...@@ -353,8 +353,10 @@ class ConstructFrameConstants : public TypedFrameConstants {
// FP-relative. // FP-relative.
static const int kContextOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0); static const int kContextOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
static const int kLengthOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1); static const int kLengthOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
static const int kImplicitReceiverOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(2); static const int kConstructorOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(2);
DEFINE_TYPED_FRAME_SIZES(3); static const int kNewTargetOrImplicitReceiverOffset =
TYPED_FRAME_PUSHED_VALUE_OFFSET(3);
DEFINE_TYPED_FRAME_SIZES(4);
}; };
class StubFailureTrampolineFrameConstants : public InternalFrameConstants { class StubFailureTrampolineFrameConstants : public InternalFrameConstants {
......
...@@ -791,12 +791,18 @@ void Heap::SetArgumentsAdaptorDeoptPCOffset(int pc_offset) { ...@@ -791,12 +791,18 @@ void Heap::SetArgumentsAdaptorDeoptPCOffset(int pc_offset) {
} }
void Heap::SetConstructStubCreateDeoptPCOffset(int pc_offset) { void Heap::SetConstructStubCreateDeoptPCOffset(int pc_offset) {
DCHECK(construct_stub_create_deopt_pc_offset() == Smi::kZero); // TODO(tebbi): Remove second half of DCHECK once
// FLAG_harmony_restrict_constructor_return is gone.
DCHECK(construct_stub_create_deopt_pc_offset() == Smi::kZero ||
construct_stub_create_deopt_pc_offset() == Smi::FromInt(pc_offset));
set_construct_stub_create_deopt_pc_offset(Smi::FromInt(pc_offset)); set_construct_stub_create_deopt_pc_offset(Smi::FromInt(pc_offset));
} }
void Heap::SetConstructStubInvokeDeoptPCOffset(int pc_offset) { void Heap::SetConstructStubInvokeDeoptPCOffset(int pc_offset) {
DCHECK(construct_stub_invoke_deopt_pc_offset() == Smi::kZero); // TODO(tebbi): Remove second half of DCHECK once
// FLAG_harmony_restrict_constructor_return is gone.
DCHECK(construct_stub_invoke_deopt_pc_offset() == Smi::kZero ||
construct_stub_invoke_deopt_pc_offset() == Smi::FromInt(pc_offset));
set_construct_stub_invoke_deopt_pc_offset(Smi::FromInt(pc_offset)); set_construct_stub_invoke_deopt_pc_offset(Smi::FromInt(pc_offset));
} }
......
...@@ -6526,6 +6526,10 @@ class SharedFunctionInfo: public HeapObject { ...@@ -6526,6 +6526,10 @@ class SharedFunctionInfo: public HeapObject {
FunctionKind::kClassConstructor << kCompilerHintsSmiTagSize; FunctionKind::kClassConstructor << kCompilerHintsSmiTagSize;
STATIC_ASSERT(kClassConstructorBitsWithinByte < (1 << kBitsPerByte)); STATIC_ASSERT(kClassConstructorBitsWithinByte < (1 << kBitsPerByte));
static const int kDerivedConstructorBitsWithinByte =
FunctionKind::kDerivedConstructor << kCompilerHintsSmiTagSize;
STATIC_ASSERT(kDerivedConstructorBitsWithinByte < (1 << kBitsPerByte));
static const int kMarkedForTierUpBitWithinByte = static const int kMarkedForTierUpBitWithinByte =
kMarkedForTierUpBit % kBitsPerByte; kMarkedForTierUpBit % kBitsPerByte;
......
...@@ -149,20 +149,6 @@ static MaybeHandle<Object> DefineClass(Isolate* isolate, ...@@ -149,20 +149,6 @@ static MaybeHandle<Object> DefineClass(Isolate* isolate,
map->SetConstructor(*constructor); map->SetConstructor(*constructor);
Handle<JSObject> prototype = isolate->factory()->NewJSObjectFromMap(map); Handle<JSObject> prototype = isolate->factory()->NewJSObjectFromMap(map);
if (!super_class->IsTheHole(isolate)) {
// Derived classes, just like builtins, don't create implicit receivers in
// [[construct]]. Instead they just set up new.target and call into the
// constructor. Hence we can reuse the builtins construct stub for derived
// classes.
Handle<Code> stub =
isolate->builtins()->JSBuiltinsConstructStubForDerived();
constructor->shared()->SetConstructStub(*stub);
} else if (FLAG_harmony_restrict_constructor_return) {
DCHECK(super_class->IsTheHole(isolate));
Handle<Code> stub = isolate->builtins()->JSBuiltinsConstructStubForBase();
constructor->shared()->SetConstructStub(*stub);
}
JSFunction::SetPrototype(constructor, prototype); JSFunction::SetPrototype(constructor, prototype);
PropertyAttributes attribs = PropertyAttributes attribs =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY); static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
......
// 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 --no-harmony-restrict-constructor-return --max-deopt-count 200
this.FLAG_harmony_restrict_constructor_return = false;
try {
load('mjsunit/compiler/constructor-inlining.js');
} catch(e) {
load('test/mjsunit/compiler/constructor-inlining.js');
}
// 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 --harmony-restrict-constructor-return --max-deopt-count 200
if (this.FLAG_harmony_restrict_constructor_return === undefined)
this.FLAG_harmony_restrict_constructor_return = true;
var counter = 0;
var deopt_at = -1;
class Base {
constructor(use, x){
if (deopt_at-- == 0) {
%_DeoptimizeNow();
%DeoptimizeFunction(testConstructorInlining);
}
counter++;
this.x = x;
if (use) {
return x;
}
}
}
class Derived extends Base {
constructor(use, x, y, deopt = false) {
super(use, x);
counter++;
if (deopt_at-- == 0) %_DeoptimizeNow();
this.y = y;
if (use) {
return y;
}
}
}
var DerivedDeoptCreate = new Proxy(Derived, {
get: function(target, name) {
if (name=='prototype') {
counter++;
if (deopt_at-- == 0) %DeoptimizeFunction(Derived);
}
return target[name];
}
});
function Constr(use, x){
counter++;
if (deopt_at-- == 0) %_DeoptimizeNow();
this.x = x;
if (use) {
return x;
}
}
%SetForceInlineFlag(Base);
%SetForceInlineFlag(Derived);
%SetForceInlineFlag(Constr);
var a = {};
var b = {};
function testConstructorInlining(){
assertEquals(a, new Constr(true, a));
assertEquals(7, new Constr(false, 7).x);
assertEquals(5, new Constr(true, 5).x);
assertEquals(a, new Base(true, a));
assertEquals(7, new Base(false, 7).x);
if (FLAG_harmony_restrict_constructor_return) {
// not using assertThrows to ensure proper inlining
try {
new Base(true, 5);
assertTrue(false);
} catch (e) {
if (!(e instanceof TypeError)) throw e;
}
} else {
assertEquals(5, new Base(true, 5).x);
}
assertEquals(b, new Derived(true, a, b));
assertEquals(a, new Derived(true, a, undefined));
assertEquals(5, new Derived(false, 5, 7).x);
assertEquals(7, new Derived(false, 5, 7).y);
try {
new Derived(true, a, 7)
assertTrue(false);
} catch (e) {
if (!(e instanceof TypeError)) throw e;
}
if (FLAG_harmony_restrict_constructor_return) {
try {
new Derived(true, 5, a)
assertTrue(false);
} catch (e) {
if (!(e instanceof TypeError)) throw e;
}
} else {
assertEquals(a, new Derived(true, 5, a));
}
%OptimizeFunctionOnNextCall(Derived);
assertEquals(b, new DerivedDeoptCreate(true, a, b));
%OptimizeFunctionOnNextCall(Derived);
assertEquals(a, new DerivedDeoptCreate(true, a, undefined));
%OptimizeFunctionOnNextCall(Derived);
assertEquals(5, new DerivedDeoptCreate(false, 5, 7).x);
%OptimizeFunctionOnNextCall(Derived);
assertEquals(7, new DerivedDeoptCreate(false, 5, 7).y);
}
testConstructorInlining();
%OptimizeFunctionOnNextCall(testConstructorInlining);
testConstructorInlining();
var last = undefined;
for(var i = 0; deopt_at < 0; ++i) {
deopt_at = i;
counter = 0;
%OptimizeFunctionOnNextCall(testConstructorInlining);
testConstructorInlining();
if (last !== undefined) {
assertEquals(counter, last)
}
last = counter;
}
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