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 { ...@@ -18,10 +18,16 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
typedef std::function<void(Node* a, Node* pK, Node* value)> typedef std::function<void(Node* a, Node* pK, Node* value)>
CallResultProcessor; CallResultProcessor;
void GenerateArrayIteratingBuiltinBody( void GenerateIteratingArrayBuiltinBody(
const char* name, Node* receiver, Node* callbackfn, Node* this_arg, const char* name, const BuiltinResultGenerator& generator,
Node* context, const BuiltinResultGenerator& generator, const CallResultProcessor& processor,
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)); Variable k(this, MachineRepresentation::kTagged, SmiConstant(0));
Label non_array(this), slow(this, &k), array_changes(this, &k); Label non_array(this), slow(this, &k), array_changes(this, &k);
...@@ -91,8 +97,34 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { ...@@ -91,8 +97,34 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
// Already done above in initialization of the Variable k // Already done above in initialization of the Variable k
Bind(&slow); 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 // 8. Repeat, while k < len
Variable k(this, MachineRepresentation::kTagged, initial_k);
Label loop(this, &k); Label loop(this, &k);
Label after_loop(this); Label after_loop(this);
Goto(&loop); Goto(&loop);
...@@ -106,8 +138,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { ...@@ -106,8 +138,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
// b. Let kPresent be HasProperty(O, Pk). // b. Let kPresent be HasProperty(O, Pk).
// c. ReturnIfAbrupt(kPresent). // c. ReturnIfAbrupt(kPresent).
Node* k_present = Node* k_present = HasProperty(o, p_k, context);
CallStub(CodeFactory::HasProperty(isolate()), context, p_k, o);
// d. If kPresent is true, then // d. If kPresent is true, then
GotoIf(WordNotEqual(k_present, TrueConstant()), &done_element); GotoIf(WordNotEqual(k_present, TrueConstant()), &done_element);
...@@ -132,6 +163,23 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { ...@@ -132,6 +163,23 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
Bind(&after_loop); Bind(&after_loop);
Return(a); 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: private:
...@@ -414,54 +462,53 @@ TF_BUILTIN(FastArrayPush, CodeStubAssembler) { ...@@ -414,54 +462,53 @@ TF_BUILTIN(FastArrayPush, CodeStubAssembler) {
} }
} }
TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) { TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinCodeStubAssembler) {
Node* receiver = Parameter(ForEachDescriptor::kReceiver); GenerateIteratingArrayBuiltinLoopContinuation(
Node* callbackfn = Parameter(ForEachDescriptor::kCallback); [this](Node* a, Node* p_k, Node* value) {
Node* this_arg = Parameter(ForEachDescriptor::kThisArg); ForEachProcessor(a, p_k, value);
Node* context = Parameter(ForEachDescriptor::kContext); });
}
GenerateArrayIteratingBuiltinBody( TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) {
"Array.prototype.forEach", receiver, callbackfn, this_arg, context, GenerateIteratingArrayBuiltinBody(
"Array.prototype.forEach",
[=](Node*, Node*) { return UndefinedConstant(); }, [=](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) { TF_BUILTIN(ArraySomeLoopContinuation, ArrayBuiltinCodeStubAssembler) {
Node* receiver = Parameter(ForEachDescriptor::kReceiver); GenerateIteratingArrayBuiltinLoopContinuation(
Node* callbackfn = Parameter(ForEachDescriptor::kCallback); [this](Node* a, Node* p_k, Node* value) {
Node* this_arg = Parameter(ForEachDescriptor::kThisArg); SomeProcessor(a, p_k, value);
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(ArraySome, ArrayBuiltinCodeStubAssembler) { TF_BUILTIN(ArraySome, ArrayBuiltinCodeStubAssembler) {
Node* receiver = Parameter(ForEachDescriptor::kReceiver); GenerateIteratingArrayBuiltinBody(
Node* callbackfn = Parameter(ForEachDescriptor::kCallback); "Array.prototype.some", [=](Node*, Node*) { return FalseConstant(); },
Node* this_arg = Parameter(ForEachDescriptor::kThisArg); [this](Node* a, Node* p_k, Node* value) { SomeProcessor(a, p_k, value); },
Node* context = Parameter(ForEachDescriptor::kContext); CodeFactory::ArraySomeLoopContinuation(isolate()));
}
GenerateArrayIteratingBuiltinBody(
"Array.prototype.some", receiver, callbackfn, this_arg, context, TF_BUILTIN(ArrayEveryLoopContinuation, ArrayBuiltinCodeStubAssembler) {
[=](Node*, Node*) { return FalseConstant(); }, GenerateIteratingArrayBuiltinLoopContinuation(
[=](Node* a, Node* p_k, Node* value) { [this](Node* a, Node* p_k, Node* value) {
Label false_continue(this), return_true(this); EveryProcessor(a, p_k, value);
BranchIfToBooleanIsTrue(value, &return_true, &false_continue);
Bind(&return_true);
Return(TrueConstant());
Bind(&false_continue);
}); });
} }
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) { TF_BUILTIN(ArrayIsArray, CodeStubAssembler) {
Node* object = Parameter(1); Node* object = Parameter(1);
Node* context = Parameter(4); Node* context = Parameter(4);
......
...@@ -277,6 +277,9 @@ class Isolate; ...@@ -277,6 +277,9 @@ class Isolate;
CPP(ArraySlice) \ CPP(ArraySlice) \
CPP(ArraySplice) \ CPP(ArraySplice) \
CPP(ArrayUnshift) \ CPP(ArrayUnshift) \
TFJ(ArrayForEachLoopContinuation, 6) \
TFJ(ArrayEveryLoopContinuation, 6) \
TFJ(ArraySomeLoopContinuation, 6) \
TFJ(ArrayForEach, 2) \ TFJ(ArrayForEach, 2) \
TFJ(ArrayEvery, 2) \ TFJ(ArrayEvery, 2) \
TFJ(ArraySome, 2) \ TFJ(ArraySome, 2) \
......
...@@ -508,6 +508,24 @@ Callable CodeFactory::ArrayPush(Isolate* isolate) { ...@@ -508,6 +508,24 @@ Callable CodeFactory::ArrayPush(Isolate* isolate) {
return Callable(isolate->builtins()->ArrayPush(), BuiltinDescriptor(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 // static
Callable CodeFactory::FunctionPrototypeBind(Isolate* isolate) { Callable CodeFactory::FunctionPrototypeBind(Isolate* isolate) {
return Callable(isolate->builtins()->FunctionPrototypeBind(), return Callable(isolate->builtins()->FunctionPrototypeBind(),
......
...@@ -180,6 +180,9 @@ class V8_EXPORT_PRIVATE CodeFactory final { ...@@ -180,6 +180,9 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable ArrayConstructor(Isolate* isolate); static Callable ArrayConstructor(Isolate* isolate);
static Callable ArrayPush(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 FunctionPrototypeBind(Isolate* isolate);
static Callable PromiseHandleReject(Isolate* isolate); static Callable PromiseHandleReject(Isolate* isolate);
}; };
......
...@@ -31,6 +31,9 @@ ...@@ -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_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_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_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 v8 {
namespace internal { namespace internal {
...@@ -532,7 +535,7 @@ Node* CodeAssembler::CallRuntime(Runtime::FunctionId function, Node* context, ...@@ -532,7 +535,7 @@ Node* CodeAssembler::CallRuntime(Runtime::FunctionId function, Node* context,
return return_value; return return_value;
} }
// Instantiate CallRuntime() with up to 6 arguments. // Instantiate CallRuntime() for argument counts used by CSA-generated code
#define INSTANTIATE(...) \ #define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::CallRuntime( \ template V8_EXPORT_PRIVATE Node* CodeAssembler::CallRuntime( \
Runtime::FunctionId, __VA_ARGS__); Runtime::FunctionId, __VA_ARGS__);
...@@ -558,7 +561,7 @@ Node* CodeAssembler::TailCallRuntime(Runtime::FunctionId function, ...@@ -558,7 +561,7 @@ Node* CodeAssembler::TailCallRuntime(Runtime::FunctionId function,
return raw_assembler()->TailCallN(desc, arraysize(nodes), nodes); 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(...) \ #define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallRuntime( \ template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallRuntime( \
Runtime::FunctionId, __VA_ARGS__); Runtime::FunctionId, __VA_ARGS__);
...@@ -573,7 +576,7 @@ Node* CodeAssembler::CallStubR(const CallInterfaceDescriptor& descriptor, ...@@ -573,7 +576,7 @@ Node* CodeAssembler::CallStubR(const CallInterfaceDescriptor& descriptor,
return CallStubN(descriptor, result_size, arraysize(nodes), nodes); 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(...) \ #define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::CallStubR( \ template V8_EXPORT_PRIVATE Node* CodeAssembler::CallStubR( \
const CallInterfaceDescriptor& descriptor, size_t, Node*, __VA_ARGS__); const CallInterfaceDescriptor& descriptor, size_t, Node*, __VA_ARGS__);
...@@ -612,15 +615,15 @@ Node* CodeAssembler::TailCallStub(const CallInterfaceDescriptor& descriptor, ...@@ -612,15 +615,15 @@ Node* CodeAssembler::TailCallStub(const CallInterfaceDescriptor& descriptor,
MachineType::AnyTagged(), result_size); MachineType::AnyTagged(), result_size);
Node* nodes[] = {target, args..., context}; Node* nodes[] = {target, args..., context};
CHECK_EQ(descriptor.GetParameterCount() + 2, arraysize(nodes));
return raw_assembler()->TailCallN(desc, arraysize(nodes), 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(...) \ #define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallStub( \ template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallStub( \
const CallInterfaceDescriptor& descriptor, Node*, __VA_ARGS__); const CallInterfaceDescriptor& descriptor, Node*, __VA_ARGS__);
REPEAT_1_TO_7(INSTANTIATE, Node*) REPEAT_1_TO_11(INSTANTIATE, Node*)
#undef INSTANTIATE #undef INSTANTIATE
template <class... TArgs> template <class... TArgs>
...@@ -631,10 +634,12 @@ Node* CodeAssembler::TailCallBytecodeDispatch( ...@@ -631,10 +634,12 @@ Node* CodeAssembler::TailCallBytecodeDispatch(
isolate(), zone(), descriptor, descriptor.GetStackParameterCount()); isolate(), zone(), descriptor, descriptor.GetStackParameterCount());
Node* nodes[] = {target, args...}; Node* nodes[] = {target, args...};
CHECK_EQ(descriptor.GetParameterCount() + 1, arraysize(nodes));
return raw_assembler()->TailCallN(desc, arraysize(nodes), 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( template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallBytecodeDispatch(
const CallInterfaceDescriptor& descriptor, Node* target, Node*, Node*, const CallInterfaceDescriptor& descriptor, Node* target, Node*, Node*,
Node*, Node*); Node*, Node*);
......
...@@ -57,7 +57,8 @@ class PlatformInterfaceDescriptor; ...@@ -57,7 +57,8 @@ class PlatformInterfaceDescriptor;
V(AllocateHeapNumber) \ V(AllocateHeapNumber) \
V(Builtin) \ V(Builtin) \
V(ArrayConstructor) \ V(ArrayConstructor) \
V(ForEach) \ V(IteratingArrayBuiltin) \
V(IteratingArrayBuiltinLoopContinuation) \
V(ArrayNoArgumentConstructor) \ V(ArrayNoArgumentConstructor) \
V(ArraySingleArgumentConstructor) \ V(ArraySingleArgumentConstructor) \
V(ArrayNArgumentsConstructor) \ V(ArrayNArgumentsConstructor) \
...@@ -294,9 +295,8 @@ class V8_EXPORT_PRIVATE CallInterfaceDescriptor { ...@@ -294,9 +295,8 @@ class V8_EXPORT_PRIVATE CallInterfaceDescriptor {
MachineType machine_types[] = {MachineType::AnyTagged(), \ MachineType machine_types[] = {MachineType::AnyTagged(), \
MachineType::AnyTagged(), \ MachineType::AnyTagged(), \
MachineType::Int32()}; \ MachineType::Int32()}; \
int argc = kStackParameterCount + 1 - arraysize(machine_types); \ data->InitializePlatformIndependent(arraysize(machine_types), \
data->InitializePlatformIndependent(arraysize(machine_types), argc, \ kStackParameterCount, machine_types); \
machine_types); \
} \ } \
void InitializePlatformSpecific(CallInterfaceDescriptorData* data) \ void InitializePlatformSpecific(CallInterfaceDescriptorData* data) \
override { \ override { \
...@@ -317,8 +317,8 @@ class V8_EXPORT_PRIVATE CallInterfaceDescriptor { ...@@ -317,8 +317,8 @@ class V8_EXPORT_PRIVATE CallInterfaceDescriptor {
kArgumentsCount, \ kArgumentsCount, \
kContext, /* implicit parameter */ \ kContext, /* implicit parameter */ \
kParameterCount = kContext, \ kParameterCount = kContext, \
kStackParameterCount = \ kArity = kAfterLastStackParameter - kBeforeFirstStackParameter - 1, \
kAfterLastStackParameter - kBeforeFirstStackParameter - 1, \ kStackParameterCount = kArity + 1 \
}; };
class VoidDescriptor : public CallInterfaceDescriptor { class VoidDescriptor : public CallInterfaceDescriptor {
...@@ -702,10 +702,18 @@ class BuiltinDescriptor : public CallInterfaceDescriptor { ...@@ -702,10 +702,18 @@ class BuiltinDescriptor : public CallInterfaceDescriptor {
static const Register TargetRegister(); static const Register TargetRegister();
}; };
class ForEachDescriptor : public BuiltinDescriptor { class IteratingArrayBuiltinDescriptor : public BuiltinDescriptor {
public: public:
DEFINE_BUILTIN_PARAMETERS(kCallback, kThisArg) DEFINE_BUILTIN_PARAMETERS(kCallback, kThisArg)
DECLARE_BUILTIN_DESCRIPTOR(ForEachDescriptor) DECLARE_BUILTIN_DESCRIPTOR(IteratingArrayBuiltinDescriptor)
};
class IteratingArrayBuiltinLoopContinuationDescriptor
: public BuiltinDescriptor {
public:
DEFINE_BUILTIN_PARAMETERS(kCallback, kThisArg, kArray, kObject, kInitialK,
kLength)
DECLARE_BUILTIN_DESCRIPTOR(IteratingArrayBuiltinLoopContinuationDescriptor)
}; };
class ArrayConstructorDescriptor : public CallInterfaceDescriptor { class ArrayConstructorDescriptor : public CallInterfaceDescriptor {
......
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