Commit c7bf46ea authored by Marja Hölttä's avatar Marja Hölttä Committed by V8 LUCI CQ

[baseline] Omit calling default ctors

I.e., implement a baseline handler for the FindNonDefaultConstructor
bytecode.

Bug: v8:13091
Change-Id: If1b119ae0479e54d2a89143bf8f40faeadb1abaf
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3871206Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83076}
parent 9459c27b
......@@ -1161,8 +1161,10 @@ void BaselineCompiler::VisitGetSuperConstructor() {
}
void BaselineCompiler::VisitFindNonDefaultConstructor() {
// TODO(v8:13091): Implement.
CHECK(false);
CallBuiltin<Builtin::kFindNonDefaultConstructor>(RegisterOperand(0),
RegisterOperand(1));
StoreRegister(2, kReturnRegister1);
StoreRegister(3, kReturnRegister2);
}
namespace {
......
......@@ -1094,6 +1094,7 @@ namespace internal {
TFS(CreateDataProperty, kReceiver, kKey, kValue) \
ASM(MemCopyUint8Uint8, CCall) \
ASM(MemMove, CCall) \
TFC(FindNonDefaultConstructor, FindNonDefaultConstructor) \
\
/* Trace */ \
CPP(IsTraceCategoryEnabled) \
......
......@@ -16,6 +16,7 @@
#include "src/ic/keyed-store-generic.h"
#include "src/logging/counters.h"
#include "src/objects/debug-objects.h"
#include "src/objects/scope-info.h"
#include "src/objects/shared-function-info.h"
#include "src/runtime/runtime.h"
......@@ -1501,5 +1502,32 @@ TF_BUILTIN(InstantiateAsmJs, CodeStubAssembler) {
TailCallJSCode(code, context, function, new_target, arg_count);
}
TF_BUILTIN(FindNonDefaultConstructor, CodeStubAssembler) {
auto this_function = Parameter<JSFunction>(Descriptor::kThisFunction);
auto new_target = Parameter<Object>(Descriptor::kNewTarget);
auto context = Parameter<Context>(Descriptor::kContext);
TVARIABLE(Object, constructor);
Label found_default_base_ctor(this, &constructor),
found_something_else(this, &constructor);
FindNonDefaultConstructor(context, this_function, constructor,
&found_default_base_ctor, &found_something_else);
BIND(&found_default_base_ctor);
{
// Create an object directly, without calling the default base ctor.
TNode<Object> instance = CallBuiltin(Builtin::kFastNewObject, context,
constructor.value(), new_target);
Return(TrueConstant(), constructor.value(), instance);
}
BIND(&found_something_else);
{
// Not a base ctor (or bailed out).
Return(FalseConstant(), constructor.value(), UndefinedConstant());
}
}
} // namespace internal
} // namespace v8
......@@ -13711,6 +13711,72 @@ TNode<HeapObject> CodeStubAssembler::GetSuperConstructor(
return LoadMapPrototype(map);
}
void CodeStubAssembler::FindNonDefaultConstructor(
TNode<Context> context, TNode<JSFunction> this_function,
TVariable<Object>& constructor, Label* found_default_base_ctor,
Label* found_something_else) {
Label loop(this, &constructor);
constructor = GetSuperConstructor(this_function);
// Disable the optimization if the debugger is active, so that we can still
// put breakpoints into default constructors.
GotoIf(IsDebugActive(), found_something_else);
// Disable the optimization if the array iterator has been changed. V8 uses
// the array iterator for the spread in default ctors, even though it
// shouldn't, according to the spec. This ensures that omitting default ctors
// doesn't change the behavior. See crbug.com/v8/13249.
GotoIf(IsArrayIteratorProtectorCellInvalid(), found_something_else);
Goto(&loop);
BIND(&loop);
{
// We know constructor can't be a SMI, since it's a prototype. If it's not a
// JSFunction, the error will be thrown by the ThrowIfNotSuperConstructor
// which follows this bytecode.
GotoIfNot(IsJSFunction(CAST(constructor.value())), found_something_else);
// If there are class fields, bail out. TODO(v8:13091): Handle them here.
TNode<Oddball> has_class_fields =
HasProperty(context, constructor.value(), ClassFieldsSymbolConstant(),
kHasProperty);
GotoIf(IsTrue(has_class_fields), found_something_else);
// If there are private methods, bail out. TODO(v8:13091): Handle them here.
TNode<Context> function_context =
LoadJSFunctionContext(CAST(constructor.value()));
TNode<ScopeInfo> scope_info = LoadScopeInfo(function_context);
GotoIf(LoadScopeInfoClassScopeHasPrivateBrand(scope_info),
found_something_else);
const TNode<Uint32T> function_kind =
LoadFunctionKind(CAST(constructor.value()));
// A default base ctor -> stop the search.
GotoIf(Word32Equal(
function_kind,
static_cast<uint32_t>(FunctionKind::kDefaultBaseConstructor)),
found_default_base_ctor);
// Something else than a default derived ctor (e.g., a non-default base
// ctor, a non-default derived ctor, or a normal function) -> stop the
// search.
GotoIfNot(Word32Equal(function_kind,
static_cast<uint32_t>(
FunctionKind::kDefaultDerivedConstructor)),
found_something_else);
constructor = GetSuperConstructor(CAST(constructor.value()));
Goto(&loop);
}
// We don't need to re-check the proctector, since the loop cannot call into
// user code. Even if GetSuperConstructor returns a Proxy, we will throw since
// it's not a constructor, and not invoke [[GetPrototypeOf]] on it.
// TODO(v8:13091): make sure this is still valid after we handle class fields.
}
TNode<JSReceiver> CodeStubAssembler::SpeciesConstructor(
TNode<Context> context, TNode<Object> object,
TNode<JSReceiver> default_constructor) {
......
......@@ -2043,6 +2043,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
Label* if_bailout);
TNode<Object> GetConstructor(TNode<Map> map);
void FindNonDefaultConstructor(TNode<Context> context,
TNode<JSFunction> this_function,
TVariable<Object>& constructor,
Label* found_default_base_ctor,
Label* found_something_else);
TNode<Map> GetInstanceTypeMap(InstanceType instance_type);
TNode<FixedArray> AllocateUninitializedFixedArray(intptr_t capacity) {
......
......@@ -72,6 +72,7 @@ namespace internal {
V(CopyDataPropertiesWithExcludedPropertiesOnStack) \
V(CppBuiltinAdaptor) \
V(FastNewObject) \
V(FindNonDefaultConstructor) \
V(ForInPrepare) \
V(GetIteratorStackParameter) \
V(GetProperty) \
......@@ -1816,6 +1817,20 @@ class InterpreterCEntry2Descriptor
static constexpr auto registers();
};
class FindNonDefaultConstructorDescriptor
: public StaticCallInterfaceDescriptor<
FindNonDefaultConstructorDescriptor> {
public:
DEFINE_RESULT_AND_PARAMETERS(3, kThisFunction, kNewTarget)
DEFINE_RESULT_AND_PARAMETER_TYPES(
MachineType::AnyTagged(), // result 1 (true / false)
MachineType::AnyTagged(), // result 2 (constructor)
MachineType::AnyTagged(), // result 3 (instance)
MachineType::AnyTagged(), // kThisFunction
MachineType::AnyTagged()) // kNewTarget
DECLARE_DESCRIPTOR(FindNonDefaultConstructorDescriptor)
};
class ForInPrepareDescriptor
: public StaticCallInterfaceDescriptor<ForInPrepareDescriptor> {
public:
......
......@@ -5683,11 +5683,7 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
Register new_target = register_allocator()->NewRegister();
VisitForRegisterValue(super->new_target_var(), new_target);
// Use the same register for storing the 'constructor' or the 'instance',
// they won't both be needed at the same time. If we jump to super_ctor_call
// done, only 'instance' is used, and if we don't, only 'constructor' is
// used.
Register& constructor = instance;
Register constructor = register_allocator()->NewRegister();
if (omit_super_ctor) {
BuildSuperCallOptimization(this_function, new_target, constructor,
instance, &super_ctor_call_done);
......
......@@ -2792,73 +2792,18 @@ IGNITION_HANDLER(ThrowIfNotSuperConstructor, InterpreterAssembler) {
IGNITION_HANDLER(FindNonDefaultConstructor, InterpreterAssembler) {
TNode<Context> context = GetContext();
TVARIABLE(Object, constructor);
Label loop(this, &constructor), found_default_base_ctor(this, &constructor),
Label found_default_base_ctor(this, &constructor),
found_something_else(this, &constructor);
TNode<JSFunction> this_function = CAST(LoadRegisterAtOperandIndex(0));
constructor = GetSuperConstructor(this_function);
// Disable the optimization if the debugger is active, so that we can still
// put breakpoints into default constructors.
GotoIf(IsDebugActive(), &found_something_else);
// Disable the optimization if the array iterator has been changed. V8 uses
// the array iterator for the spread in default ctors, even though it
// shouldn't, according to the spec. This ensures that omitting default ctors
// doesn't change the behavior. See crbug.com/v8/13249.
GotoIf(IsArrayIteratorProtectorCellInvalid(), &found_something_else);
TNode<Object> new_target = LoadRegisterAtOperandIndex(1);
Goto(&loop);
BIND(&loop);
{
// We know constructor can't be a SMI, since it's a prototype. If it's not a
// JSFunction, the error will be thrown by the ThrowIfNotSuperConstructor
// which follows this bytecode.
GotoIfNot(IsJSFunction(CAST(constructor.value())), &found_something_else);
// If there are class fields, bail out. TODO(v8:13091): Handle them here.
TNode<Oddball> has_class_fields =
HasProperty(context, constructor.value(), ClassFieldsSymbolConstant(),
kHasProperty);
GotoIf(IsTrue(has_class_fields), &found_something_else);
// If there are private methods, bail out. TODO(v8:13091): Handle them here.
TNode<Context> function_context =
LoadJSFunctionContext(CAST(constructor.value()));
TNode<ScopeInfo> scope_info = LoadScopeInfo(function_context);
GotoIf(LoadScopeInfoClassScopeHasPrivateBrand(scope_info),
&found_something_else);
const TNode<Uint32T> function_kind =
LoadFunctionKind(CAST(constructor.value()));
// A default base ctor -> stop the search.
GotoIf(Word32Equal(
function_kind,
static_cast<uint32_t>(FunctionKind::kDefaultBaseConstructor)),
&found_default_base_ctor);
// Something else than a default derived ctor (e.g., a non-default base
// ctor, a non-default derived ctor, or a normal function) -> stop the
// search.
GotoIfNot(Word32Equal(function_kind,
static_cast<uint32_t>(
FunctionKind::kDefaultDerivedConstructor)),
&found_something_else);
constructor = GetSuperConstructor(CAST(constructor.value()));
Goto(&loop);
}
// We don't need to re-check the proctector, since the loop cannot call into
// user code. Even if GetSuperConstructor returns a Proxy, we will throw since
// it's not a constructor, and not invoke [[GetPrototypeOf]] on it.
// TODO(v8:13091): make sure this is still valid after we handle class fields.
FindNonDefaultConstructor(context, this_function, constructor,
&found_default_base_ctor, &found_something_else);
BIND(&found_default_base_ctor);
{
// Create an object directly, without calling the default base ctor.
TNode<Object> new_target = LoadRegisterAtOperandIndex(1);
TNode<Object> instance = CallBuiltin(Builtin::kFastNewObject, context,
constructor.value(), new_target);
StoreRegisterAtOperandIndex(instance, 3);
......
......@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --omit-default-ctors --no-sparkplug --no-turbofan --no-always-turbofan
// Flags: --omit-default-ctors --no-turbofan --no-always-turbofan
// TODO(v8:13091): Enable Sparkplug + TurboFan.
// TODO(v8:13091): Enable TurboFan.
// This behavior is not spec compliant, see crbug.com/v8/13249.
(function ArrayIteratorMonkeyPatched() {
......
......@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --omit-default-ctors --no-sparkplug --no-turbofan --no-always-turbofan
// Flags: --omit-default-ctors --no-turbofan --no-always-turbofan
// TODO(v8:13091): Enable Sparkplug + TurboFan.
// TODO(v8:13091): Enable TurboFan.
(function OmitDefaultBaseCtor() {
class A {} // default base ctor -> will be omitted
......
......@@ -87,7 +87,7 @@ snippet: "
test = new B().constructor;
})();
"
frame size: 5
frame size: 7
parameter count: 1
bytecode array length: 39
bytecodes: [
......@@ -95,10 +95,10 @@ bytecodes: [
/* 118 S> */ B(LdaSmi), I8(1),
B(Star4),
B(Ldar), R(1),
/* 118 E> */ B(GetSuperConstructor), R(3),
B(ThrowIfNotSuperConstructor), R(3),
/* 118 E> */ B(GetSuperConstructor), R(6),
B(ThrowIfNotSuperConstructor), R(6),
B(Ldar), R(0),
/* 118 E> */ B(Construct), R(3), R(4), U8(1), U8(0),
/* 118 E> */ B(Construct), R(6), R(4), U8(1), U8(0),
B(Star3),
B(Ldar), R(this),
B(ThrowSuperAlreadyCalledIfNotHole),
......@@ -130,16 +130,16 @@ snippet: "
test = new B().constructor;
})();
"
frame size: 4
frame size: 6
parameter count: 1
bytecode array length: 36
bytecodes: [
B(Mov), R(closure), R(1),
/* 117 S> */ B(Ldar), R(1),
/* 117 E> */ B(GetSuperConstructor), R(3),
B(ThrowIfNotSuperConstructor), R(3),
/* 117 E> */ B(GetSuperConstructor), R(5),
B(ThrowIfNotSuperConstructor), R(5),
B(Ldar), R(0),
/* 117 E> */ B(Construct), R(3), R(0), U8(0), U8(0),
/* 117 E> */ B(Construct), R(5), R(0), U8(0), U8(0),
B(Star3),
B(Ldar), R(this),
B(ThrowSuperAlreadyCalledIfNotHole),
......
......@@ -157,7 +157,7 @@ snippet: "
};
new F;
"
frame size: 9
frame size: 10
parameter count: 1
bytecode array length: 63
bytecodes: [
......@@ -166,27 +166,27 @@ bytecodes: [
B(LdaImmutableCurrentContextSlot), U8(3),
B(Star2),
B(Ldar), R(0),
/* 89 E> */ B(GetSuperConstructor), R(1),
B(ThrowIfNotSuperConstructor), R(1),
/* 89 E> */ B(GetSuperConstructor), R(3),
B(ThrowIfNotSuperConstructor), R(3),
B(Ldar), R(2),
/* 89 E> */ B(Construct), R(1), R(0), U8(0), U8(0),
/* 89 E> */ B(Construct), R(3), R(0), U8(0), U8(0),
B(Star1),
B(LdaCurrentContextSlot), U8(2),
B(ThrowSuperAlreadyCalledIfNotHole),
B(Ldar), R(1),
B(StaCurrentContextSlot), U8(2),
B(LdaImmutableContextSlot), R(context), U8(3), U8(1),
B(Star4),
B(Star5),
B(LdaSmi), I8(1),
B(Star6),
B(Mov), R(1), R(3),
B(Mov), R(context), R(5),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(3), U8(4),
B(Star7),
B(Mov), R(1), R(4),
B(Mov), R(context), R(6),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(4), U8(4),
B(GetNamedProperty), R(0), U8(0), U8(2),
B(JumpIfUndefined), U8(10),
B(Star8),
B(CallProperty0), R(8), R(1), U8(4),
B(Mov), R(1), R(7),
B(Star9),
B(CallProperty0), R(9), R(1), U8(4),
B(Mov), R(1), R(8),
B(Ldar), R(1),
/* 96 S> */ B(Return),
]
......@@ -209,7 +209,7 @@ snippet: "
};
new G();
"
frame size: 9
frame size: 10
parameter count: 1
bytecode array length: 63
bytecodes: [
......@@ -218,27 +218,27 @@ bytecodes: [
B(LdaImmutableCurrentContextSlot), U8(3),
B(Star2),
B(Ldar), R(0),
/* 88 E> */ B(GetSuperConstructor), R(1),
B(ThrowIfNotSuperConstructor), R(1),
/* 88 E> */ B(GetSuperConstructor), R(3),
B(ThrowIfNotSuperConstructor), R(3),
B(Ldar), R(2),
/* 88 E> */ B(Construct), R(1), R(0), U8(0), U8(0),
/* 88 E> */ B(Construct), R(3), R(0), U8(0), U8(0),
B(Star1),
B(LdaCurrentContextSlot), U8(2),
B(ThrowSuperAlreadyCalledIfNotHole),
B(Ldar), R(1),
B(StaCurrentContextSlot), U8(2),
B(LdaImmutableContextSlot), R(context), U8(3), U8(1),
B(Star4),
B(Star5),
B(LdaSmi), I8(1),
B(Star6),
B(Mov), R(1), R(3),
B(Mov), R(context), R(5),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(3), U8(4),
B(Star7),
B(Mov), R(1), R(4),
B(Mov), R(context), R(6),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(4), U8(4),
B(GetNamedProperty), R(0), U8(0), U8(2),
B(JumpIfUndefined), U8(10),
B(Star8),
B(CallProperty0), R(8), R(1), U8(4),
B(Mov), R(1), R(7),
B(Star9),
B(CallProperty0), R(9), R(1), U8(4),
B(Mov), R(1), R(8),
B(Ldar), R(1),
/* 95 S> */ B(Return),
]
......@@ -260,7 +260,7 @@ snippet: "
};
new test('test = () => super(); test()');
"
frame size: 9
frame size: 10
parameter count: 1
bytecode array length: 63
bytecodes: [
......@@ -269,27 +269,27 @@ bytecodes: [
B(LdaImmutableCurrentContextSlot), U8(3),
B(Star2),
B(Ldar), R(0),
/* 88 E> */ B(GetSuperConstructor), R(1),
B(ThrowIfNotSuperConstructor), R(1),
/* 88 E> */ B(GetSuperConstructor), R(3),
B(ThrowIfNotSuperConstructor), R(3),
B(Ldar), R(2),
/* 88 E> */ B(Construct), R(1), R(0), U8(0), U8(0),
/* 88 E> */ B(Construct), R(3), R(0), U8(0), U8(0),
B(Star1),
B(LdaCurrentContextSlot), U8(2),
B(ThrowSuperAlreadyCalledIfNotHole),
B(Ldar), R(1),
B(StaCurrentContextSlot), U8(2),
B(LdaImmutableContextSlot), R(context), U8(3), U8(1),
B(Star4),
B(Star5),
B(LdaSmi), I8(1),
B(Star6),
B(Mov), R(1), R(3),
B(Mov), R(context), R(5),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(3), U8(4),
B(Star7),
B(Mov), R(1), R(4),
B(Mov), R(context), R(6),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(4), U8(4),
B(GetNamedProperty), R(0), U8(0), U8(2),
B(JumpIfUndefined), U8(10),
B(Star8),
B(CallProperty0), R(8), R(1), U8(4),
B(Mov), R(1), R(7),
B(Star9),
B(CallProperty0), R(9), R(1), U8(4),
B(Mov), R(1), R(8),
B(Ldar), R(1),
/* 95 S> */ B(Return),
]
......
......@@ -17,7 +17,7 @@ snippet: "
test = new B(1, 2, 3).constructor;
})();
"
frame size: 5
frame size: 8
parameter count: 1
bytecode array length: 19
bytecodes: [
......@@ -25,10 +25,10 @@ bytecodes: [
B(Star2),
B(Mov), R(closure), R(1),
/* 93 S> */ B(Ldar), R(1),
/* 93 E> */ B(GetSuperConstructor), R(4),
B(ThrowIfNotSuperConstructor), R(4),
/* 93 E> */ B(GetSuperConstructor), R(7),
B(ThrowIfNotSuperConstructor), R(7),
B(Ldar), R(0),
/* 93 E> */ B(ConstructWithSpread), R(4), R(2), U8(1), U8(0),
/* 93 E> */ B(ConstructWithSpread), R(7), R(2), U8(1), U8(0),
/* 93 S> */ B(Return),
]
constant pool: [
......@@ -49,7 +49,7 @@ snippet: "
test = new B(1, 2, 3).constructor;
})();
"
frame size: 8
frame size: 10
parameter count: 1
bytecode array length: 38
bytecodes: [
......@@ -60,11 +60,11 @@ bytecodes: [
/* 140 S> */ B(LdaSmi), I8(1),
B(Star6),
B(Ldar), R(closure),
/* 140 E> */ B(GetSuperConstructor), R(5),
B(ThrowIfNotSuperConstructor), R(5),
/* 140 E> */ B(GetSuperConstructor), R(9),
B(ThrowIfNotSuperConstructor), R(9),
B(Ldar), R(0),
B(Mov), R(3), R(7),
/* 140 E> */ B(ConstructWithSpread), R(5), R(6), U8(2), U8(0),
/* 140 E> */ B(ConstructWithSpread), R(9), R(6), U8(2), U8(0),
B(Star5),
B(Ldar), R(this),
B(ThrowSuperAlreadyCalledIfNotHole),
......
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