Commit 69f42d40 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[nci] Add Construct_WithFeedback builtin

This builtin essentially acts like InterpreterAssembler::Construct. It
collects full feedback, then tail-calls either the array constructor
or the construct builtin.

For now, it is only used in generic lowering, if the --turbo-nci flag
is passed. One of the next steps will be to measure performance impact
of feedback collection. If minimal, we may want to enable it
unconditionally in generic lowering.

Bug: v8:8888
Change-Id: I8a460a2b5954c26fa72658045a8423c5eee6b611
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2198775
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Auto-Submit: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67850}
parent e3a7b217
......@@ -36,6 +36,31 @@ void Builtins::Generate_ConstructFunctionForwardVarargs(MacroAssembler* masm) {
BUILTIN_CODE(masm->isolate(), ConstructFunction));
}
TF_BUILTIN(Construct_WithFeedback, CallOrConstructBuiltinsAssembler) {
TNode<Object> target = CAST(Parameter(Descriptor::kTarget));
TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget));
TNode<Int32T> argc =
UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<HeapObject> maybe_feedback_vector =
CAST(Parameter(Descriptor::kMaybeFeedbackVector));
TNode<Int32T> slot = UncheckedCast<Int32T>(Parameter(Descriptor::kSlot));
TVARIABLE(AllocationSite, allocation_site);
Label if_construct_generic(this), if_construct_array(this);
CollectConstructFeedback(context, target, new_target, maybe_feedback_vector,
Unsigned(ChangeInt32ToIntPtr(slot)),
&if_construct_generic, &if_construct_array,
&allocation_site);
BIND(&if_construct_generic);
TailCallBuiltin(Builtins::kConstruct, context, target, new_target, argc);
BIND(&if_construct_array);
TailCallBuiltin(Builtins::kArrayConstructorImpl, context, target, new_target,
argc, allocation_site.value());
}
TF_BUILTIN(ConstructWithArrayLike, CallOrConstructBuiltinsAssembler) {
TNode<Object> target = CAST(Parameter(Descriptor::kTarget));
TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget));
......
......@@ -79,6 +79,7 @@ namespace internal {
TFC(ConstructWithArrayLike, ConstructWithArrayLike) \
ASM(ConstructForwardVarargs, ConstructForwardVarargs) \
ASM(ConstructFunctionForwardVarargs, ConstructForwardVarargs) \
TFC(Construct_WithFeedback, Construct_WithFeedback) \
ASM(JSConstructStubGeneric, Dummy) \
ASM(JSBuiltinsConstructStub, Dummy) \
TFC(FastNewObject, FastNewObject) \
......
......@@ -25,6 +25,12 @@ namespace compiler {
namespace {
// The nci flag is currently used to experiment with feedback collection in
// optimized code produced by generic lowering.
// TODO(jgruber): Remove once we've made a decision whether to collect feedback
// unconditionally.
bool CollectFeedback() { return FLAG_turbo_nci; }
CallDescriptor::Flags FrameStateFlagForCall(Node* node) {
return OperatorProperties::HasFrameStateInput(node->op())
? CallDescriptor::kNeedsFrameState
......@@ -690,19 +696,52 @@ void JSGenericLowering::LowerJSConstruct(Node* node) {
ConstructParameters const& p = ConstructParametersOf(node->op());
int const arg_count = static_cast<int>(p.arity() - 2);
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
Callable callable = CodeFactory::Construct(isolate());
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), arg_count + 1, flags);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* stub_arity = jsgraph()->Int32Constant(arg_count);
Node* new_target = node->InputAt(arg_count + 1);
Node* receiver = jsgraph()->UndefinedConstant();
node->RemoveInput(arg_count + 1); // Drop new target.
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 2, new_target);
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, receiver);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
static constexpr int kReceiver = 1;
static constexpr int kMaybeFeedbackVector = 1;
if (CollectFeedback() && p.feedback().IsValid()) {
const int stack_argument_count =
arg_count + kReceiver + kMaybeFeedbackVector;
Callable callable =
Builtins::CallableFor(isolate(), Builtins::kConstruct_WithFeedback);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), stack_argument_count, flags);
Node* maybe_feedback_vector = jsgraph()->HeapConstant(p.feedback().vector);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* new_target = node->InputAt(arg_count + 1);
Node* stub_arity = jsgraph()->Int32Constant(arg_count);
Node* slot = jsgraph()->Int32Constant(p.feedback().index());
Node* receiver = jsgraph()->UndefinedConstant();
node->RemoveInput(arg_count + 1); // Drop new target.
// Register argument inputs are followed by stack argument inputs (such as
// maybe_feedback_vector). Both are listed in ascending order. Note that
// the receiver is implicitly placed on the stack and is thus inserted
// between explicitly-specified register and stack arguments.
// TODO(jgruber): Implement a simpler way to specify these mutations.
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 2, new_target);
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, slot);
node->InsertInput(zone(), 5, receiver);
node->InsertInput(zone(), 6, maybe_feedback_vector);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
} else {
const int stack_argument_count = arg_count + kReceiver;
Callable callable = Builtins::CallableFor(isolate(), Builtins::kConstruct);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), stack_argument_count, flags);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* stub_arity = jsgraph()->Int32Constant(arg_count);
Node* new_target = node->InputAt(arg_count + 1);
Node* receiver = jsgraph()->UndefinedConstant();
node->RemoveInput(arg_count + 1); // Drop new target.
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 2, new_target);
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, receiver);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
}
void JSGenericLowering::LowerJSConstructWithArrayLike(Node* node) {
......
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