Commit 7de21c4d authored by danno's avatar danno Committed by Commit bot

[builtins] Separate Array.prototype.* CSA builtins into two parts

Previous to this CL, CSA-optimized Array builtins--like forEach, some, and
every--were written in a single, monolithic block of CSA code.

This CL teases the code for each of these builtins apart into two chunks, a main
body with optimizations for fast cases, and a "continuation" builtin that
performs a spec-compliant, but slower version of the main loop of the
builtin. The general idea is that when the "fast" main body builtin encounters
an unexpected condition that invalidates assumptions allowing fast-case code, it
tail calls to the slow, correct version of the loop that finishes the builtin
execution.

This separation currently doens't really provide any specific advantage over the
combined version. However, it paves the way to TF-optimized inlined Array
builtins. Inlined Array builtins may trigger deopts during the execution of the
builtin's loop, and those deopt must continue execution from the point at which
they failed. With some massaging of the deoptimizer, it will be possible to make
those deopt points create an extra frame on the top of the stack which resumes
execution in the slow-loop builtin created in this CL.

BUG=v8:1956
LOG=N

Review-Url: https://codereview.chromium.org/2753793002
Cr-Commit-Position: refs/heads/master@{#43867}
parent 947a0437
......@@ -18,10 +18,16 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
typedef std::function<void(Node* a, Node* pK, Node* value)>
CallResultProcessor;
void GenerateArrayIteratingBuiltinBody(
const char* name, Node* receiver, Node* callbackfn, Node* this_arg,
Node* context, const BuiltinResultGenerator& generator,
const CallResultProcessor& processor) {
void GenerateIteratingArrayBuiltinBody(
const char* name, const BuiltinResultGenerator& generator,
const CallResultProcessor& processor,
const Callable& slow_case_continuation) {
Node* receiver = Parameter(IteratingArrayBuiltinDescriptor::kReceiver);
Node* callbackfn = Parameter(IteratingArrayBuiltinDescriptor::kCallback);
Node* this_arg = Parameter(IteratingArrayBuiltinDescriptor::kThisArg);
Node* context = Parameter(IteratingArrayBuiltinDescriptor::kContext);
Node* new_target = Parameter(IteratingArrayBuiltinDescriptor::kNewTarget);
Variable k(this, MachineRepresentation::kTagged, SmiConstant(0));
Label non_array(this), slow(this, &k), array_changes(this, &k);
......@@ -91,47 +97,89 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
// Already done above in initialization of the Variable k
Bind(&slow);
Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
MachineType::TaggedPointer());
TailCallStub(
slow_case_continuation, context, target, new_target,
Int32Constant(IteratingArrayBuiltinLoopContinuationDescriptor::kArity),
receiver, callbackfn, this_arg, a, o, k.value(), len);
}
void GenerateIteratingArrayBuiltinLoopContinuation(
const CallResultProcessor& processor) {
Node* callbackfn =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kCallback);
Node* this_arg =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kThisArg);
Node* a =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kArray);
Node* o =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kObject);
Node* initial_k =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kInitialK);
Node* len =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kLength);
Node* context =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kContext);
// 8. Repeat, while k < len
Variable k(this, MachineRepresentation::kTagged, initial_k);
Label loop(this, &k);
Label after_loop(this);
Goto(&loop);
Bind(&loop);
{
// 8. Repeat, while k < len
Label loop(this, &k);
Label after_loop(this);
Goto(&loop);
Bind(&loop);
{
GotoUnlessNumberLessThan(k.value(), len, &after_loop);
GotoUnlessNumberLessThan(k.value(), len, &after_loop);
Label done_element(this);
// a. Let Pk be ToString(k).
Node* p_k = ToString(context, k.value());
Label done_element(this);
// a. Let Pk be ToString(k).
Node* p_k = ToString(context, k.value());
// b. Let kPresent be HasProperty(O, Pk).
// c. ReturnIfAbrupt(kPresent).
Node* k_present =
CallStub(CodeFactory::HasProperty(isolate()), context, p_k, o);
// b. Let kPresent be HasProperty(O, Pk).
// c. ReturnIfAbrupt(kPresent).
Node* k_present = HasProperty(o, p_k, context);
// d. If kPresent is true, then
GotoIf(WordNotEqual(k_present, TrueConstant()), &done_element);
// d. If kPresent is true, then
GotoIf(WordNotEqual(k_present, TrueConstant()), &done_element);
// i. Let kValue be Get(O, Pk).
// ii. ReturnIfAbrupt(kValue).
Node* k_value = GetProperty(context, o, k.value());
// i. Let kValue be Get(O, Pk).
// ii. ReturnIfAbrupt(kValue).
Node* k_value = GetProperty(context, o, k.value());
// iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»).
// iv. ReturnIfAbrupt(funcResult).
Node* result = CallJS(CodeFactory::Call(isolate()), context, callbackfn,
this_arg, k_value, k.value(), o);
// iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»).
// iv. ReturnIfAbrupt(funcResult).
Node* result = CallJS(CodeFactory::Call(isolate()), context, callbackfn,
this_arg, k_value, k.value(), o);
processor(a, p_k, result);
Goto(&done_element);
Bind(&done_element);
processor(a, p_k, result);
Goto(&done_element);
Bind(&done_element);
// e. Increase k by 1.
k.Bind(NumberInc(k.value()));
Goto(&loop);
}
Bind(&after_loop);
Return(a);
// e. Increase k by 1.
k.Bind(NumberInc(k.value()));
Goto(&loop);
}
Bind(&after_loop);
Return(a);
}
void ForEachProcessor(Node* a, Node* p_k, Node* value) {}
void SomeProcessor(Node* a, Node* p_k, Node* value) {
Label false_continue(this), return_true(this);
BranchIfToBooleanIsTrue(value, &return_true, &false_continue);
Bind(&return_true);
Return(TrueConstant());
Bind(&false_continue);
}
void EveryProcessor(Node* a, Node* p_k, Node* value) {
Label true_continue(this), return_false(this);
BranchIfToBooleanIsTrue(value, &true_continue, &return_false);
Bind(&return_false);
Return(FalseConstant());
Bind(&true_continue);
}
private:
......@@ -414,54 +462,53 @@ TF_BUILTIN(FastArrayPush, CodeStubAssembler) {
}
}
TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) {
Node* receiver = Parameter(ForEachDescriptor::kReceiver);
Node* callbackfn = Parameter(ForEachDescriptor::kCallback);
Node* this_arg = Parameter(ForEachDescriptor::kThisArg);
Node* context = Parameter(ForEachDescriptor::kContext);
TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
[this](Node* a, Node* p_k, Node* value) {
ForEachProcessor(a, p_k, value);
});
}
GenerateArrayIteratingBuiltinBody(
"Array.prototype.forEach", receiver, callbackfn, this_arg, context,
TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.forEach",
[=](Node*, Node*) { return UndefinedConstant(); },
[](Node* a, Node* p_k, Node* value) {});
[this](Node* a, Node* p_k, Node* value) {
ForEachProcessor(a, p_k, value);
},
CodeFactory::ArrayForEachLoopContinuation(isolate()));
}
TF_BUILTIN(ArrayEvery, ArrayBuiltinCodeStubAssembler) {
Node* receiver = Parameter(ForEachDescriptor::kReceiver);
Node* callbackfn = Parameter(ForEachDescriptor::kCallback);
Node* this_arg = Parameter(ForEachDescriptor::kThisArg);
Node* context = Parameter(ForEachDescriptor::kContext);
GenerateArrayIteratingBuiltinBody(
"Array.prototype.every", receiver, callbackfn, this_arg, context,
[=](Node*, Node*) { return TrueConstant(); },
[=](Node* a, Node* p_k, Node* value) {
Label true_continue(this), return_false(this);
BranchIfToBooleanIsTrue(value, &true_continue, &return_false);
Bind(&return_false);
Return(FalseConstant());
Bind(&true_continue);
TF_BUILTIN(ArraySomeLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
[this](Node* a, Node* p_k, Node* value) {
SomeProcessor(a, p_k, value);
});
}
TF_BUILTIN(ArraySome, ArrayBuiltinCodeStubAssembler) {
Node* receiver = Parameter(ForEachDescriptor::kReceiver);
Node* callbackfn = Parameter(ForEachDescriptor::kCallback);
Node* this_arg = Parameter(ForEachDescriptor::kThisArg);
Node* context = Parameter(ForEachDescriptor::kContext);
GenerateArrayIteratingBuiltinBody(
"Array.prototype.some", receiver, callbackfn, this_arg, context,
[=](Node*, Node*) { return FalseConstant(); },
[=](Node* a, Node* p_k, Node* value) {
Label false_continue(this), return_true(this);
BranchIfToBooleanIsTrue(value, &return_true, &false_continue);
Bind(&return_true);
Return(TrueConstant());
Bind(&false_continue);
GenerateIteratingArrayBuiltinBody(
"Array.prototype.some", [=](Node*, Node*) { return FalseConstant(); },
[this](Node* a, Node* p_k, Node* value) { SomeProcessor(a, p_k, value); },
CodeFactory::ArraySomeLoopContinuation(isolate()));
}
TF_BUILTIN(ArrayEveryLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
[this](Node* a, Node* p_k, Node* value) {
EveryProcessor(a, p_k, value);
});
}
TF_BUILTIN(ArrayEvery, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.every", [=](Node*, Node*) { return TrueConstant(); },
[this](Node* a, Node* p_k, Node* value) {
EveryProcessor(a, p_k, value);
},
CodeFactory::ArrayEveryLoopContinuation(isolate()));
}
TF_BUILTIN(ArrayIsArray, CodeStubAssembler) {
Node* object = Parameter(1);
Node* context = Parameter(4);
......
......@@ -277,6 +277,9 @@ class Isolate;
CPP(ArraySlice) \
CPP(ArraySplice) \
CPP(ArrayUnshift) \
TFJ(ArrayForEachLoopContinuation, 6) \
TFJ(ArrayEveryLoopContinuation, 6) \
TFJ(ArraySomeLoopContinuation, 6) \
TFJ(ArrayForEach, 2) \
TFJ(ArrayEvery, 2) \
TFJ(ArraySome, 2) \
......
......@@ -508,6 +508,24 @@ Callable CodeFactory::ArrayPush(Isolate* isolate) {
return Callable(isolate->builtins()->ArrayPush(), BuiltinDescriptor(isolate));
}
// static
Callable CodeFactory::ArrayForEachLoopContinuation(Isolate* isolate) {
return Callable(isolate->builtins()->ArrayForEachLoopContinuation(),
IteratingArrayBuiltinLoopContinuationDescriptor(isolate));
}
// static
Callable CodeFactory::ArraySomeLoopContinuation(Isolate* isolate) {
return Callable(isolate->builtins()->ArraySomeLoopContinuation(),
IteratingArrayBuiltinLoopContinuationDescriptor(isolate));
}
// static
Callable CodeFactory::ArrayEveryLoopContinuation(Isolate* isolate) {
return Callable(isolate->builtins()->ArrayEveryLoopContinuation(),
IteratingArrayBuiltinLoopContinuationDescriptor(isolate));
}
// static
Callable CodeFactory::FunctionPrototypeBind(Isolate* isolate) {
return Callable(isolate->builtins()->FunctionPrototypeBind(),
......
......@@ -180,6 +180,9 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable ArrayConstructor(Isolate* isolate);
static Callable ArrayPush(Isolate* isolate);
static Callable ArrayForEachLoopContinuation(Isolate* isolate);
static Callable ArraySomeLoopContinuation(Isolate* isolate);
static Callable ArrayEveryLoopContinuation(Isolate* isolate);
static Callable FunctionPrototypeBind(Isolate* isolate);
static Callable PromiseHandleReject(Isolate* isolate);
};
......
......@@ -31,6 +31,9 @@
#define REPEAT_1_TO_7(V, T) REPEAT_1_TO_6(V, T) V(T, T, T, T, T, T, T)
#define REPEAT_1_TO_8(V, T) REPEAT_1_TO_7(V, T) V(T, T, T, T, T, T, T, T)
#define REPEAT_1_TO_9(V, T) REPEAT_1_TO_8(V, T) V(T, T, T, T, T, T, T, T, T)
#define REPEAT_1_TO_10(V, T) REPEAT_1_TO_9(V, T) V(T, T, T, T, T, T, T, T, T, T)
#define REPEAT_1_TO_11(V, T) \
REPEAT_1_TO_10(V, T) V(T, T, T, T, T, T, T, T, T, T, T)
namespace v8 {
namespace internal {
......@@ -532,7 +535,7 @@ Node* CodeAssembler::CallRuntime(Runtime::FunctionId function, Node* context,
return return_value;
}
// Instantiate CallRuntime() with up to 6 arguments.
// Instantiate CallRuntime() for argument counts used by CSA-generated code
#define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::CallRuntime( \
Runtime::FunctionId, __VA_ARGS__);
......@@ -558,7 +561,7 @@ Node* CodeAssembler::TailCallRuntime(Runtime::FunctionId function,
return raw_assembler()->TailCallN(desc, arraysize(nodes), nodes);
}
// Instantiate TailCallRuntime() with up to 6 arguments.
// Instantiate TailCallRuntime() for argument counts used by CSA-generated code
#define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallRuntime( \
Runtime::FunctionId, __VA_ARGS__);
......@@ -573,7 +576,7 @@ Node* CodeAssembler::CallStubR(const CallInterfaceDescriptor& descriptor,
return CallStubN(descriptor, result_size, arraysize(nodes), nodes);
}
// Instantiate CallStubR() with up to 6 arguments.
// Instantiate CallStubR() for argument counts used by CSA-generated code.
#define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::CallStubR( \
const CallInterfaceDescriptor& descriptor, size_t, Node*, __VA_ARGS__);
......@@ -612,15 +615,15 @@ Node* CodeAssembler::TailCallStub(const CallInterfaceDescriptor& descriptor,
MachineType::AnyTagged(), result_size);
Node* nodes[] = {target, args..., context};
CHECK_EQ(descriptor.GetParameterCount() + 2, arraysize(nodes));
return raw_assembler()->TailCallN(desc, arraysize(nodes), nodes);
}
// Instantiate TailCallStub() with up to 6 arguments.
// Instantiate TailCallStub() for argument counts used by CSA-generated code
#define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallStub( \
const CallInterfaceDescriptor& descriptor, Node*, __VA_ARGS__);
REPEAT_1_TO_7(INSTANTIATE, Node*)
REPEAT_1_TO_11(INSTANTIATE, Node*)
#undef INSTANTIATE
template <class... TArgs>
......@@ -631,10 +634,12 @@ Node* CodeAssembler::TailCallBytecodeDispatch(
isolate(), zone(), descriptor, descriptor.GetStackParameterCount());
Node* nodes[] = {target, args...};
CHECK_EQ(descriptor.GetParameterCount() + 1, arraysize(nodes));
return raw_assembler()->TailCallN(desc, arraysize(nodes), nodes);
}
// Instantiate TailCallBytecodeDispatch() with 4 arguments.
// Instantiate TailCallBytecodeDispatch() for argument counts used by
// CSA-generated code
template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallBytecodeDispatch(
const CallInterfaceDescriptor& descriptor, Node* target, Node*, Node*,
Node*, Node*);
......
This diff is collapsed.
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