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

[compiler] Omit calling default ctors

I.e., implement the TurboFan handler for the FindNonDefaultConstructor
bytecode.

Bug: v8:13091
Change-Id: I021b5d24817b47e3ce86cc1ac1377056cfd5e2a5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3885892
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarJakob Linke <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83426}
parent 5a7977a3
......@@ -13848,10 +13848,16 @@ void CodeStubAssembler::FindNonDefaultConstructor(
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);
const TNode<SharedFunctionInfo> shared_function_info =
LoadObjectField<SharedFunctionInfo>(
CAST(constructor.value()), JSFunction::kSharedFunctionInfoOffset);
const TNode<Uint32T> has_class_fields =
DecodeWord32<SharedFunctionInfo::RequiresInstanceMembersInitializerBit>(
LoadObjectField<Uint32T>(shared_function_info,
SharedFunctionInfo::kFlagsOffset));
GotoIf(Word32NotEqual(has_class_fields, Int32Constant(0)),
found_something_else);
// If there are private methods, bail out. TODO(v8:13091): Handle them here.
TNode<Context> function_context =
......
......@@ -3230,8 +3230,17 @@ void BytecodeGraphBuilder::VisitGetSuperConstructor() {
}
void BytecodeGraphBuilder::VisitFindNonDefaultConstructor() {
// TODO(v8:13091): Implement.
CHECK(false);
Node* this_function =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* new_target =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
Node* node = NewNode(javascript()->FindNonDefaultConstructor(), this_function,
new_target);
environment()->BindRegistersToProjections(
bytecode_iterator().GetRegisterOperand(2), node,
Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::BuildCompareOp(const Operator* op) {
......
......@@ -1561,6 +1561,7 @@ ObjectRef CallHandlerInfoRef::data() const {
HEAP_ACCESSOR_C(ScopeInfo, int, ContextLength)
HEAP_ACCESSOR_C(ScopeInfo, bool, HasContextExtensionSlot)
HEAP_ACCESSOR_C(ScopeInfo, bool, HasOuterScopeInfo)
HEAP_ACCESSOR_C(ScopeInfo, bool, ClassScopeHasPrivateBrand)
ScopeInfoRef ScopeInfoRef::OuterScopeInfo() const {
return MakeRefAssumeMemoryFence(broker(), object()->OuterScopeInfo());
......@@ -1701,7 +1702,7 @@ ZoneVector<const CFunctionInfo*> FunctionTemplateInfoRef::c_signatures() const {
bool StringRef::IsSeqString() const { return object()->IsSeqString(); }
ScopeInfoRef NativeContextRef::scope_info() const {
ScopeInfoRef ContextRef::scope_info() const {
// The scope_info is immutable after initialization.
return MakeRefAssumeMemoryFence(broker(), object()->scope_info());
}
......
......@@ -527,6 +527,8 @@ class ContextRef : public HeapObjectRef {
// Only returns a value if the index is valid for this ContextRef.
base::Optional<ObjectRef> get(int index) const;
ScopeInfoRef scope_info() const;
};
#define BROKER_NATIVE_CONTEXT_FIELDS(V) \
......@@ -584,7 +586,6 @@ class NativeContextRef : public ContextRef {
BROKER_NATIVE_CONTEXT_FIELDS(DECL_ACCESSOR)
#undef DECL_ACCESSOR
ScopeInfoRef scope_info() const;
MapRef GetFunctionMapFromIndex(int index) const;
MapRef GetInitialJSArrayMap(ElementsKind kind) const;
base::Optional<JSFunctionRef> GetConstructorFunction(const MapRef& map) const;
......@@ -879,6 +880,7 @@ class ScopeInfoRef : public HeapObjectRef {
int ContextLength() const;
bool HasOuterScopeInfo() const;
bool HasContextExtensionSlot() const;
bool ClassScopeHasPrivateBrand() const;
ScopeInfoRef OuterScopeInfo() const;
};
......@@ -899,6 +901,7 @@ class ScopeInfoRef : public HeapObjectRef {
V(int, StartPosition) \
V(bool, is_compiled) \
V(bool, IsUserJavaScript) \
V(bool, requires_instance_members_initializer) \
IF_WASM(V, const wasm::WasmModule*, wasm_module) \
IF_WASM(V, const wasm::FunctionSig*, wasm_function_signature)
......
......@@ -552,6 +552,10 @@ void JSGenericLowering::LowerJSGetSuperConstructor(Node* node) {
AccessBuilder::ForMapPrototype()));
}
void JSGenericLowering::LowerJSFindNonDefaultConstructor(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kFindNonDefaultConstructor);
}
void JSGenericLowering::LowerJSHasInPrototypeChain(Node* node) {
ReplaceWithRuntimeCall(node, Runtime::kHasInPrototypeChain);
}
......
......@@ -80,6 +80,8 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
return ReduceJSAsyncFunctionResolve(node);
case IrOpcode::kJSGetSuperConstructor:
return ReduceJSGetSuperConstructor(node);
case IrOpcode::kJSFindNonDefaultConstructor:
return ReduceJSFindNonDefaultConstructor(node);
case IrOpcode::kJSInstanceOf:
return ReduceJSInstanceOf(node);
case IrOpcode::kJSHasInPrototypeChain:
......@@ -540,6 +542,108 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
return NoChange();
}
Reduction JSNativeContextSpecialization::ReduceJSFindNonDefaultConstructor(
Node* node) {
JSFindNonDefaultConstructorNode n(node);
Node* this_function = n.this_function();
Node* new_target = n.new_target();
Node* effect = n.effect();
Control control = n.control();
// TODO(v8:13091): Don't produce incomplete stack traces when debug is active.
// We already deopt when a breakpoint is set. But it would be even nicer to
// avoid producting incomplete stack traces when when debug is active, even if
// there are no breakpoints - then a user inspecting stack traces via Dev
// Tools would always see the full stack trace.
// Check if the input is a known JSFunction.
HeapObjectMatcher m(this_function);
if (!m.HasResolvedValue() || !m.Ref(broker()).IsJSFunction()) {
return NoChange();
}
JSFunctionRef this_function_ref = m.Ref(broker()).AsJSFunction();
MapRef function_map = this_function_ref.map();
HeapObjectRef current = function_map.prototype();
Node* return_value;
Node* ctor_or_instance;
// Walk the class inheritance tree until we find a ctor which is not a default
// derived ctor.
while (true) {
if (!current.IsJSFunction()) {
return NoChange();
}
JSFunctionRef current_function = current.AsJSFunction();
// If there are class fields, bail out. TODO(v8:13091): Handle them here.
if (current_function.shared().requires_instance_members_initializer()) {
return NoChange();
}
// If there are private methods, bail out. TODO(v8:13091): Handle them here.
if (current_function.context().scope_info().ClassScopeHasPrivateBrand()) {
return NoChange();
}
FunctionKind kind = current_function.shared().kind();
if (kind != FunctionKind::kDefaultDerivedConstructor) {
// The hierarchy walk will end here; this is the last change to bail out
// before creating new nodes.
if (!dependencies()->DependOnArrayIteratorProtector()) {
return NoChange();
}
if (kind == FunctionKind::kDefaultBaseConstructor) {
return_value = jsgraph()->BooleanConstant(true);
// Generate a builtin call for creating the instance.
Node* constructor = jsgraph()->Constant(current_function);
effect = ctor_or_instance = graph()->NewNode(
jsgraph()->javascript()->Create(), constructor, new_target,
n.context(), n.frame_state(), effect, control);
} else {
return_value = jsgraph()->BooleanConstant(false);
ctor_or_instance = jsgraph()->Constant(current_function);
}
break;
}
// Keep walking up the class tree.
current = current_function.map().prototype();
}
dependencies()->DependOnStablePrototypeChain(function_map,
WhereToStart::kStartAtReceiver);
// Update the uses of {node}.
for (Edge edge : node->use_edges()) {
Node* const user = edge.from();
if (NodeProperties::IsEffectEdge(edge)) {
edge.UpdateTo(effect);
} else if (NodeProperties::IsControlEdge(edge)) {
edge.UpdateTo(control);
} else {
DCHECK(NodeProperties::IsValueEdge(edge));
switch (ProjectionIndexOf(user->op())) {
case 0:
Replace(user, return_value);
break;
case 1:
Replace(user, ctor_or_instance);
break;
default:
UNREACHABLE();
}
}
}
node->Kill();
return Replace(return_value);
}
Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
JSInstanceOfNode n(node);
FeedbackParameter const& p = n.Parameters();
......
......@@ -75,6 +75,7 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
Reduction ReduceJSAsyncFunctionReject(Node* node);
Reduction ReduceJSAsyncFunctionResolve(Node* node);
Reduction ReduceJSGetSuperConstructor(Node* node);
Reduction ReduceJSFindNonDefaultConstructor(Node* node);
Reduction ReduceJSInstanceOf(Node* node);
Reduction ReduceJSHasInPrototypeChain(Node* node);
Reduction ReduceJSOrdinaryHasInstance(Node* node);
......
......@@ -770,6 +770,7 @@ Type JSWasmCallNode::TypeForWasmReturnType(const wasm::ValueType& type) {
V(RejectPromise, Operator::kNoDeopt | Operator::kNoThrow, 3, 1) \
V(ResolvePromise, Operator::kNoDeopt | Operator::kNoThrow, 2, 1) \
V(GetSuperConstructor, Operator::kNoWrite | Operator::kNoThrow, 1, 1) \
V(FindNonDefaultConstructor, Operator::kNoProperties, 2, 2) \
V(ParseInt, Operator::kNoProperties, 2, 1) \
V(RegExpTest, Operator::kNoProperties, 2, 1)
......
......@@ -1040,6 +1040,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* GetSuperConstructor();
const Operator* FindNonDefaultConstructor();
const Operator* CreateGeneratorObject();
const Operator* LoadGlobal(const NameRef& name,
......@@ -1757,6 +1759,20 @@ class JSForInNextNode final : public JSNodeWrapperBase {
#undef INPUTS
};
class JSFindNonDefaultConstructorNode final : public JSNodeWrapperBase {
public:
explicit constexpr JSFindNonDefaultConstructorNode(Node* node)
: JSNodeWrapperBase(node) {
DCHECK_EQ(IrOpcode::kJSFindNonDefaultConstructor, node->opcode());
}
#define INPUTS(V) \
V(ThisFunction, this_function, 0, Object) \
V(NewTarget, new_target, 1, Object)
INPUTS(DEFINE_INPUT_ACCESSORS)
#undef INPUTS
};
#undef DEFINE_INPUT_ACCESSORS
} // namespace compiler
......
......@@ -183,7 +183,8 @@
V(JSStoreInArrayLiteral) \
V(JSDeleteProperty) \
V(JSHasProperty) \
V(JSGetSuperConstructor)
V(JSGetSuperConstructor) \
V(JSFindNonDefaultConstructor)
#define JS_CONTEXT_OP_LIST(V) \
V(JSHasContextExtension) \
......
......@@ -91,6 +91,7 @@ bool OperatorProperties::NeedsExactContext(const Operator* op) {
case IrOpcode::kJSSetNamedProperty:
case IrOpcode::kJSDefineNamedOwnProperty:
case IrOpcode::kJSSetKeyedProperty:
case IrOpcode::kJSFindNonDefaultConstructor:
return true;
case IrOpcode::kJSAsyncFunctionEnter:
......@@ -239,6 +240,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSStackCheck:
case IrOpcode::kJSDebugger:
case IrOpcode::kJSGetSuperConstructor:
case IrOpcode::kJSFindNonDefaultConstructor:
case IrOpcode::kJSBitwiseNot:
case IrOpcode::kJSDecrement:
case IrOpcode::kJSIncrement:
......
......@@ -1470,6 +1470,10 @@ Type Typer::Visitor::TypeJSGetSuperConstructor(Node* node) {
return Type::NonInternal();
}
Type Typer::Visitor::TypeJSFindNonDefaultConstructor(Node* node) {
return Type::Tuple(Type::Boolean(), Type::Object(), zone());
}
// JS context operators.
Type Typer::Visitor::TypeJSHasContextExtension(Node* node) {
return Type::Boolean();
......
......@@ -1121,6 +1121,13 @@ Type Type::Tuple(Type first, Type second, Type third, Zone* zone) {
return FromTypeBase(tuple);
}
Type Type::Tuple(Type first, Type second, Zone* zone) {
TupleType* tuple = TupleType::New(2, zone);
tuple->InitElement(0, first);
tuple->InitElement(1, second);
return FromTypeBase(tuple);
}
// static
Type Type::OtherNumberConstant(double value, Zone* zone) {
return FromTypeBase(OtherNumberConstantType::New(value, zone));
......
......@@ -423,6 +423,7 @@ class V8_EXPORT_PRIVATE Type {
static Type Constant(double value, Zone* zone);
static Type Range(double min, double max, Zone* zone);
static Type Tuple(Type first, Type second, Type third, Zone* zone);
static Type Tuple(Type first, Type second, Zone* zone);
static Type Union(Type type1, Type type2, Zone* zone);
static Type Intersect(Type type1, Type type2, Zone* zone);
......
......@@ -790,7 +790,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::NonInternal());
break;
case IrOpcode::kJSFindNonDefaultConstructor:
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Any());
break;
case IrOpcode::kJSHasContextExtension:
CheckTypeIs(node, Type::Boolean());
break;
......
// Copyright 2022 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: --omit-default-ctors --allow-natives-syntax --no-maglev --turbofan
// Flags: --no-always-turbofan
// TODO(v8:13091): Enable Maglev.
// This behavior is not spec compliant, see crbug.com/v8/13249.
(function ArrayIteratorMonkeyPatched() {
let iterationCount = 0;
const oldIterator = Array.prototype[Symbol.iterator];
Array.prototype[Symbol.iterator] =
function () { ++iterationCount; return oldIterator.call(this); };
class A {}
class B extends A {}
class C extends B {}
%PrepareFunctionForOptimization(C);
new C();
%OptimizeFunctionOnNextCall(C);
// C default ctor doing "...args" and B default ctor doing "...args".
assertEquals(2, iterationCount);
new C();
// C default ctor doing "...args" and B default ctor doing "...args".
assertEquals(4, iterationCount);
assertTrue(isTurboFanned(C)); // No deopt.
Array.prototype[Symbol.iterator] = oldIterator;
})();
This diff is collapsed.
......@@ -2,9 +2,8 @@
// 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-turbofan --no-always-turbofan --no-maglev
// Flags: --omit-default-ctors --no-maglev
// TODO(v8:13091): Enable TurboFan.
// TODO(v8:13091): Enable Maglev.
// This behavior is not spec compliant, see crbug.com/v8/13249.
......
......@@ -2,9 +2,8 @@
// 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-turbofan --no-always-turbofan --no-maglev
// Flags: --omit-default-ctors --no-maglev
// TODO(v8:13091): Enable TurboFan.
// TODO(v8:13091): Enable Maglev.
(function OmitDefaultBaseCtor() {
......
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