Commit 90c3a2d5 authored by danno's avatar danno Committed by Commit Bot

Inline Array.prototype.forEach in TurboFan

This CL contains a few pieces:

- A new mechanism to create "BuiltinContinuation" checkpoints in TurboFan
  graphs, which--when triggered--swizzle the values in the the FrameState to be
  parameters to a typically TF-generated builtin that resumes execution to finish
  the slow-case functionality.
- Continuation builtins that have special handling in the deoptimizer and their own
  new frame type to ensure that the values they need to begin executing can be stashed
  away and restored immediately before the builtin is called via a trampoline that runs
  when the continuation builtin's frame execution resumes.
- An implementation of Array.prototype.forEach in TurboFan that can be used to
  inline it. The inlined forEach implementation uses the checkpoints mechanism
  described above to deopt in the middle of the forEach in the cases that optimization
  invariants are violated. There is a slightly different continuation stub for each
  deopt point in the forEach implementation to ensure the correct side-effects, i.e.
  that the deopt of the builtin isn't programmatically observable.

Review-Url: https://codereview.chromium.org/2803853005
Cr-Commit-Position: refs/heads/master@{#45764}
parent cf8f7bdc
......@@ -1654,6 +1654,69 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs);
}
void Builtins::Generate_NotifyBuiltinContinuation(MacroAssembler* masm) {
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
// Preserve possible return result from lazy deopt.
__ push(r0);
// Pass the function and deoptimization type to the runtime system.
__ CallRuntime(Runtime::kNotifyStubFailure, false);
__ pop(r0);
}
__ add(sp, sp, Operand(kPointerSize)); // Ignore state
__ mov(pc, lr); // Jump to ContinueToBuiltin stub
}
namespace {
void Generate_ContinueToBuiltinHelper(MacroAssembler* masm,
bool java_script_builtin,
bool with_result) {
const RegisterConfiguration* config(RegisterConfiguration::Turbofan());
int allocatable_register_count = config->num_allocatable_general_registers();
if (with_result) {
// Overwrite the hole inserted by the deoptimizer with the return value from
// the LAZY deopt point.
__ str(r0,
MemOperand(
sp, config->num_allocatable_general_registers() * kPointerSize +
BuiltinContinuationFrameConstants::kFixedFrameSize));
}
for (int i = allocatable_register_count - 1; i >= 0; --i) {
int code = config->GetAllocatableGeneralCode(i);
__ Pop(Register::from_code(code));
if (java_script_builtin && code == kJavaScriptCallArgCountRegister.code()) {
__ SmiUntag(Register::from_code(code));
}
}
__ ldr(fp, MemOperand(
sp, BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp));
__ Pop(ip);
__ add(sp, sp,
Operand(BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp));
__ Pop(lr);
__ add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
}
} // namespace
void Builtins::Generate_ContinueToCodeStubBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, false);
}
void Builtins::Generate_ContinueToCodeStubBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, true);
}
void Builtins::Generate_ContinueToJavaScriptBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, false);
}
void Builtins::Generate_ContinueToJavaScriptBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, true);
}
static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) {
{
......
......@@ -1694,6 +1694,75 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs);
}
void Builtins::Generate_NotifyBuiltinContinuation(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve possible return result from lazy deopt.
__ Push(x0);
// Pass the function and deoptimization type to the runtime system.
__ CallRuntime(Runtime::kNotifyStubFailure, false);
__ Pop(x0);
}
// Ignore state (pushed by Deoptimizer::EntryGenerator::Generate).
__ Drop(1);
// Jump to the ContinueToBuiltin stub. Deoptimizer::EntryGenerator::Generate
// loads this into lr before it jumps here.
__ Br(lr);
}
namespace {
void Generate_ContinueToBuiltinHelper(MacroAssembler* masm,
bool java_script_builtin,
bool with_result) {
const RegisterConfiguration* config(RegisterConfiguration::Turbofan());
int allocatable_register_count = config->num_allocatable_general_registers();
if (with_result) {
// Overwrite the hole inserted by the deoptimizer with the return value from
// the LAZY deopt point.
__ Str(x0, MemOperand(
jssp,
config->num_allocatable_general_registers() * kPointerSize +
BuiltinContinuationFrameConstants::kFixedFrameSize));
}
for (int i = allocatable_register_count - 1; i >= 0; --i) {
int code = config->GetAllocatableGeneralCode(i);
__ Pop(Register::from_code(code));
if (java_script_builtin && code == kJavaScriptCallArgCountRegister.code()) {
__ SmiUntag(Register::from_code(code));
}
}
__ ldr(fp,
MemOperand(jssp,
BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp));
__ Pop(ip0);
__ Add(jssp, jssp,
Operand(BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp));
__ Pop(lr);
__ Add(ip0, ip0, Operand(Code::kHeaderSize - kHeapObjectTag));
__ Br(ip0);
}
} // namespace
void Builtins::Generate_ContinueToCodeStubBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, false);
}
void Builtins::Generate_ContinueToCodeStubBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, true);
}
void Builtins::Generate_ContinueToJavaScriptBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, false);
}
void Builtins::Generate_ContinueToJavaScriptBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, true);
}
static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) {
{
......
......@@ -1200,6 +1200,38 @@ TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinCodeStubAssembler) {
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction);
}
TF_BUILTIN(ArrayForEachLoopEagerDeoptContinuation,
ArrayBuiltinCodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* initial_k = Parameter(Descriptor::kInitialK);
Node* len = Parameter(Descriptor::kLength);
Callable stub(Builtins::CallableFor(isolate(),
Builtins::kArrayForEachLoopContinuation));
Return(CallStub(stub, context, receiver, callbackfn, this_arg,
UndefinedConstant(), receiver, initial_k, len,
UndefinedConstant()));
}
TF_BUILTIN(ArrayForEachLoopLazyDeoptContinuation,
ArrayBuiltinCodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* initial_k = Parameter(Descriptor::kInitialK);
Node* len = Parameter(Descriptor::kLength);
Callable stub(Builtins::CallableFor(isolate(),
Builtins::kArrayForEachLoopContinuation));
Return(CallStub(stub, context, receiver, callbackfn, this_arg,
UndefinedConstant(), receiver, initial_k, len,
UndefinedConstant()));
}
TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) {
Node* argc =
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
......
......@@ -158,6 +158,33 @@ namespace internal {
ASM(NotifyLazyDeoptimized) \
ASM(NotifyStubFailure) \
ASM(NotifyStubFailureSaveDoubles) \
ASM(NotifyBuiltinContinuation) \
\
/* Trampolines called when returning from a deoptimization that expects */ \
/* to continue in a JavaScript builtin to finish the functionality of a */ \
/* an TF-inlined version of builtin that has side-effects. */ \
/* */ \
/* The trampolines work as follows: */ \
/* 1. Trampoline restores input register values that */ \
/* the builtin expects from a BuiltinContinuationFrame. */ \
/* 2. Trampoline tears down BuiltinContinuationFrame. */ \
/* 3. Trampoline jumps to the builtin's address. */ \
/* 4. Builtin executes as if invoked by the frame above it. */ \
/* 5. When the builtin returns, execution resumes normally in the */ \
/* calling frame, processing any return result from the JavaScript */ \
/* builtin as if it had called the builtin directly. */ \
/* */ \
/* There are two variants of the stub that differ in their handling of a */ \
/* value returned by the next frame deeper on the stack. For LAZY deopts, */ \
/* the return value (e.g. rax on x64) is explicitly passed as an extra */ \
/* stack parameter to the JavaScript builtin by the "WithResult" */ \
/* trampoline variant. The plain variant is used in EAGER deopt contexts */ \
/* and has no such special handling. */ \
ASM(ContinueToCodeStubBuiltin) \
ASM(ContinueToCodeStubBuiltinWithResult) \
ASM(ContinueToJavaScriptBuiltin) \
ASM(ContinueToJavaScriptBuiltinWithResult) \
\
ASM(OnStackReplacement) \
\
/* API callback handling */ \
......@@ -270,6 +297,10 @@ namespace internal {
/* ES6 #sec-array.prototype.foreach */ \
TFS(ArrayForEachLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
kObject, kInitialK, kLength, kTo) \
TFJ(ArrayForEachLoopEagerDeoptContinuation, 4, kCallbackFn, kThisArg, \
kInitialK, kLength) \
TFJ(ArrayForEachLoopLazyDeoptContinuation, 5, kCallbackFn, kThisArg, \
kInitialK, kLength, kResult) \
TFJ(ArrayForEach, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.every */ \
TFS(ArrayEveryLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
......@@ -1077,6 +1108,14 @@ namespace internal {
BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, \
V, IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN)
#define BUILTIN_LIST_TFJ(V) \
BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, V, IGNORE_BUILTIN, \
IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN)
#define BUILTIN_LIST_TFC(V) \
BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, V, \
IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN)
#define BUILTINS_WITH_UNTAGGED_PARAMS(V) V(WasmCompileLazy)
} // namespace internal
......
......@@ -26,6 +26,32 @@ Builtins::Builtins() : initialized_(false) {
Builtins::~Builtins() {}
BailoutId Builtins::GetContinuationBailoutId(Name name) {
switch (name) {
#define BAILOUT_ID(NAME, ...) \
case k##NAME: \
return BailoutId(BailoutId::kFirstBuiltinContinuationId + name);
BUILTIN_LIST_TFJ(BAILOUT_ID);
BUILTIN_LIST_TFC(BAILOUT_ID);
#undef BAILOUT_ID
default:
UNREACHABLE();
}
}
Builtins::Name Builtins::GetBuiltinFromBailoutId(BailoutId id) {
switch (id.ToInt()) {
#define BAILOUT_ID(NAME, ...) \
case BailoutId::kFirstBuiltinContinuationId + k##NAME: \
return k##NAME;
BUILTIN_LIST_TFJ(BAILOUT_ID)
BUILTIN_LIST_TFC(BAILOUT_ID)
#undef BAILOUT_ID
default:
UNREACHABLE();
}
}
void Builtins::TearDown() { initialized_ = false; }
void Builtins::IterateBuiltins(RootVisitor* v) {
......@@ -118,17 +144,31 @@ Callable Builtins::CallableFor(Isolate* isolate, Name name) {
switch (name) {
// This macro is deliberately crafted so as to emit very little code,
// in order to keep binary size of this function under control.
#define CASE(Name, ...) \
#define CASE_OTHER(Name, ...) \
case k##Name: { \
key = Builtin_##Name##_InterfaceDescriptor::key(); \
break; \
}
BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, CASE, CASE,
CASE, IGNORE_BUILTIN, IGNORE_BUILTIN)
#undef CASE
BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, CASE_OTHER,
CASE_OTHER, CASE_OTHER, IGNORE_BUILTIN, IGNORE_BUILTIN)
#undef CASE_OTHER
case kConsoleAssert: {
return Callable(code, BuiltinDescriptor(isolate));
}
case kArrayForEach: {
Handle<Code> code = isolate->builtins()->ArrayForEach();
return Callable(code, BuiltinDescriptor(isolate));
}
case kArrayForEachLoopEagerDeoptContinuation: {
Handle<Code> code =
isolate->builtins()->ArrayForEachLoopEagerDeoptContinuation();
return Callable(code, BuiltinDescriptor(isolate));
}
case kArrayForEachLoopLazyDeoptContinuation: {
Handle<Code> code =
isolate->builtins()->ArrayForEachLoopLazyDeoptContinuation();
return Callable(code, BuiltinDescriptor(isolate));
}
default:
UNREACHABLE();
}
......@@ -136,6 +176,21 @@ Callable Builtins::CallableFor(Isolate* isolate, Name name) {
return Callable(code, descriptor);
}
// static
int Builtins::GetStackParameterCount(Isolate* isolate, Name name) {
switch (name) {
#define CASE(Name, Count, ...) \
case k##Name: { \
return Count; \
}
BUILTIN_LIST_TFJ(CASE)
#undef CASE
default:
UNREACHABLE();
return 0;
}
}
// static
const char* Builtins::name(int index) {
switch (index) {
......
......@@ -18,6 +18,7 @@ class Handle;
class Isolate;
// Forward declarations.
class BailoutId;
class RootVisitor;
enum class InterpreterPushArgsMode : unsigned;
namespace compiler {
......@@ -43,6 +44,9 @@ class Builtins {
builtin_count
};
static BailoutId GetContinuationBailoutId(Name name);
static Name GetBuiltinFromBailoutId(BailoutId);
#define DECLARE_BUILTIN_ACCESSOR(Name, ...) \
V8_EXPORT_PRIVATE Handle<Code> Name();
BUILTIN_LIST_ALL(DECLARE_BUILTIN_ACCESSOR)
......@@ -82,6 +86,8 @@ class Builtins {
V8_EXPORT_PRIVATE static Callable CallableFor(Isolate* isolate, Name name);
static int GetStackParameterCount(Isolate* isolate, Name name);
static const char* name(int index);
// Returns the C++ entry point for builtins implemented in C++, and the null
......
......@@ -1467,6 +1467,72 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs);
}
void Builtins::Generate_NotifyBuiltinContinuation(MacroAssembler* masm) {
// Enter an internal frame.
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve possible return result from lazy deopt.
__ push(eax);
__ CallRuntime(Runtime::kNotifyStubFailure, false);
__ pop(eax);
// Tear down internal frame.
}
__ pop(MemOperand(esp, 0)); // Ignore state offset
__ ret(0); // Return to ContinueToBuiltin stub still on stack.
}
namespace {
void Generate_ContinueToBuiltinHelper(MacroAssembler* masm,
bool java_script_builtin,
bool with_result) {
const RegisterConfiguration* config(RegisterConfiguration::Turbofan());
int allocatable_register_count = config->num_allocatable_general_registers();
if (with_result) {
// Overwrite the hole inserted by the deoptimizer with the return value from
// the LAZY deopt point.
__ mov(Operand(esp,
config->num_allocatable_general_registers() * kPointerSize +
BuiltinContinuationFrameConstants::kFixedFrameSize),
eax);
}
for (int i = allocatable_register_count - 1; i >= 0; --i) {
int code = config->GetAllocatableGeneralCode(i);
__ pop(Register::from_code(code));
if (java_script_builtin && code == kJavaScriptCallArgCountRegister.code()) {
__ SmiUntag(Register::from_code(code));
}
}
__ mov(
ebp,
Operand(esp, BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp));
const int offsetToPC =
BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp - kPointerSize;
__ pop(Operand(esp, offsetToPC));
__ Drop(offsetToPC / kPointerSize);
__ add(Operand(esp, 0), Immediate(Code::kHeaderSize - kHeapObjectTag));
__ ret(0);
}
} // namespace
void Builtins::Generate_ContinueToCodeStubBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, false);
}
void Builtins::Generate_ContinueToCodeStubBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, true);
}
void Builtins::Generate_ContinueToJavaScriptBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, false);
}
void Builtins::Generate_ContinueToJavaScriptBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, true);
}
static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) {
{
......
......@@ -1646,6 +1646,70 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs);
}
void Builtins::Generate_NotifyBuiltinContinuation(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve possible return result from lazy deopt.
__ Push(v0);
// Pass the function and deoptimization type to the runtime system.
__ CallRuntime(Runtime::kNotifyStubFailure, false);
__ Pop(v0);
}
__ Addu(sp, sp, Operand(kPointerSize)); // Ignore state
__ Jump(ra); // Jump to the ContinueToBuiltin stub
}
namespace {
void Generate_ContinueToBuiltinHelper(MacroAssembler* masm,
bool java_script_builtin,
bool with_result) {
const RegisterConfiguration* config(RegisterConfiguration::Turbofan());
int allocatable_register_count = config->num_allocatable_general_registers();
if (with_result) {
// Overwrite the hole inserted by the deoptimizer with the return value from
// the LAZY deopt point.
__ sw(v0,
MemOperand(
sp, config->num_allocatable_general_registers() * kPointerSize +
BuiltinContinuationFrameConstants::kFixedFrameSize));
}
for (int i = allocatable_register_count - 1; i >= 0; --i) {
int code = config->GetAllocatableGeneralCode(i);
__ Pop(Register::from_code(code));
if (java_script_builtin && code == kJavaScriptCallArgCountRegister.code()) {
__ SmiUntag(Register::from_code(code));
}
}
__ lw(fp, MemOperand(
sp, BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp));
__ Pop(t0);
__ Addu(sp, sp,
Operand(BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp));
__ Pop(ra);
__ Addu(t0, t0, Operand(Code::kHeaderSize - kHeapObjectTag));
__ Jump(t0);
}
} // namespace
void Builtins::Generate_ContinueToCodeStubBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, false);
}
void Builtins::Generate_ContinueToCodeStubBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, true);
}
void Builtins::Generate_ContinueToJavaScriptBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, false);
}
void Builtins::Generate_ContinueToJavaScriptBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, true);
}
static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) {
{
......
......@@ -1647,6 +1647,70 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs);
}
void Builtins::Generate_NotifyBuiltinContinuation(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve possible return result from lazy deopt.
__ push(v0);
// Pass the function and deoptimization type to the runtime system.
__ CallRuntime(Runtime::kNotifyStubFailure, false);
__ pop(v0);
}
__ Daddu(sp, sp, Operand(kPointerSize)); // Ignore state
__ Jump(ra); // Jump to the ContinueToBuiltin stub
}
namespace {
void Generate_ContinueToBuiltinHelper(MacroAssembler* masm,
bool java_script_builtin,
bool with_result) {
const RegisterConfiguration* config(RegisterConfiguration::Turbofan());
int allocatable_register_count = config->num_allocatable_general_registers();
if (with_result) {
// Overwrite the hole inserted by the deoptimizer with the return value from
// the LAZY deopt point.
__ Sd(v0,
MemOperand(
sp, config->num_allocatable_general_registers() * kPointerSize +
BuiltinContinuationFrameConstants::kFixedFrameSize));
}
for (int i = allocatable_register_count - 1; i >= 0; --i) {
int code = config->GetAllocatableGeneralCode(i);
__ Pop(Register::from_code(code));
if (java_script_builtin && code == kJavaScriptCallArgCountRegister.code()) {
__ SmiUntag(Register::from_code(code));
}
}
__ Ld(fp, MemOperand(
sp, BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp));
__ Pop(t0);
__ Daddu(sp, sp,
Operand(BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp));
__ Pop(ra);
__ Daddu(t0, t0, Operand(Code::kHeaderSize - kHeapObjectTag));
__ Jump(t0);
}
} // namespace
void Builtins::Generate_ContinueToCodeStubBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, false);
}
void Builtins::Generate_ContinueToCodeStubBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, true);
}
void Builtins::Generate_ContinueToJavaScriptBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, false);
}
void Builtins::Generate_ContinueToJavaScriptBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, true);
}
static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) {
{
......
......@@ -1430,6 +1430,72 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs);
}
void Builtins::Generate_NotifyBuiltinContinuation(MacroAssembler* masm) {
// Enter an internal frame.
{
FrameScope scope(masm, StackFrame::INTERNAL);
// Preserve possible return result from lazy deopt.
__ pushq(rax);
__ CallRuntime(Runtime::kNotifyStubFailure, false);
__ popq(rax);
// Tear down internal frame.
}
__ DropUnderReturnAddress(1); // Ignore state offset
__ ret(0); // Return to ContinueToBuiltin stub still on stack.
}
namespace {
void Generate_ContinueToBuiltinHelper(MacroAssembler* masm,
bool java_script_builtin,
bool with_result) {
const RegisterConfiguration* config(RegisterConfiguration::Turbofan());
int allocatable_register_count = config->num_allocatable_general_registers();
if (with_result) {
// Overwrite the hole inserted by the deoptimizer with the return value from
// the LAZY deopt point.
__ movq(Operand(rsp,
config->num_allocatable_general_registers() * kPointerSize +
BuiltinContinuationFrameConstants::kFixedFrameSize),
rax);
}
for (int i = allocatable_register_count - 1; i >= 0; --i) {
int code = config->GetAllocatableGeneralCode(i);
__ popq(Register::from_code(code));
if (java_script_builtin && code == kJavaScriptCallArgCountRegister.code()) {
__ SmiToInteger32(Register::from_code(code), Register::from_code(code));
}
}
__ movq(
rbp,
Operand(rsp, BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp));
const int offsetToPC =
BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp - kPointerSize;
__ popq(Operand(rsp, offsetToPC));
__ Drop(offsetToPC / kPointerSize);
__ addq(Operand(rsp, 0), Immediate(Code::kHeaderSize - kHeapObjectTag));
__ Ret();
}
} // namespace
void Builtins::Generate_ContinueToCodeStubBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, false);
}
void Builtins::Generate_ContinueToCodeStubBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, false, true);
}
void Builtins::Generate_ContinueToJavaScriptBuiltin(MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, false);
}
void Builtins::Generate_ContinueToJavaScriptBuiltinWithResult(
MacroAssembler* masm) {
Generate_ContinueToBuiltinHelper(masm, true, true);
}
static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) {
// Enter an internal frame.
......
......@@ -813,6 +813,22 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
descriptor->bailout_id(), shared_info_id,
static_cast<unsigned int>(descriptor->parameters_count()));
break;
case FrameStateType::kBuiltinContinuation: {
BailoutId bailout_id = descriptor->bailout_id();
int parameter_count =
static_cast<unsigned int>(descriptor->parameters_count());
translation->BeginBuiltinContinuationFrame(bailout_id, shared_info_id,
parameter_count);
break;
}
case FrameStateType::kJavaScriptBuiltinContinuation: {
BailoutId bailout_id = descriptor->bailout_id();
int parameter_count =
static_cast<unsigned int>(descriptor->parameters_count());
translation->BeginJavaScriptBuiltinContinuationFrame(
bailout_id, shared_info_id, parameter_count);
break;
}
case FrameStateType::kGetterStub:
translation->BeginGetterStubFrame(shared_info_id);
break;
......
......@@ -5,6 +5,10 @@
#include "src/compiler/frame-states.h"
#include "src/base/functional.h"
#include "src/callable.h"
#include "src/compiler/graph.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node.h"
#include "src/handles-inl.h"
#include "src/objects-inl.h"
......@@ -64,6 +68,12 @@ std::ostream& operator<<(std::ostream& os, FrameStateType type) {
case FrameStateType::kConstructStub:
os << "CONSTRUCT_STUB";
break;
case FrameStateType::kBuiltinContinuation:
os << "BUILTIN_CONTINUATION_FRAME";
break;
case FrameStateType::kJavaScriptBuiltinContinuation:
os << "JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME";
break;
case FrameStateType::kGetterStub:
os << "GETTER_STUB";
break;
......@@ -85,6 +95,112 @@ std::ostream& operator<<(std::ostream& os, FrameStateInfo const& info) {
return os;
}
namespace {
Node* CreateBuiltinContinuationFrameStateCommon(
JSGraph* js_graph, Builtins::Name name, Node* context, Node** parameters,
int parameter_count, Node* outer_frame_state, Handle<JSFunction> function) {
Isolate* isolate = js_graph->isolate();
Graph* graph = js_graph->graph();
CommonOperatorBuilder* common = js_graph->common();
BailoutId bailout_id = Builtins::GetContinuationBailoutId(name);
Callable callable = Builtins::CallableFor(isolate, name);
const Operator* op_param =
common->StateValues(parameter_count, SparseInputMask::Dense());
Node* params_node = graph->NewNode(op_param, parameter_count, parameters);
FrameStateType frame_type =
function.is_null() ? FrameStateType::kBuiltinContinuation
: FrameStateType::kJavaScriptBuiltinContinuation;
Handle<SharedFunctionInfo> shared(
Handle<SharedFunctionInfo>(function->shared()));
const FrameStateFunctionInfo* state_info =
common->CreateFrameStateFunctionInfo(frame_type, parameter_count, 0,
shared);
const Operator* op = common->FrameState(
bailout_id, OutputFrameStateCombine::Ignore(), state_info);
Node* function_node = function.is_null() ? js_graph->UndefinedConstant()
: js_graph->HeapConstant(function);
Node* frame_state = graph->NewNode(
op, params_node, js_graph->EmptyStateValues(),
js_graph->EmptyStateValues(), context, function_node, outer_frame_state);
return frame_state;
}
} // namespace
Node* CreateStubBuiltinContinuationFrameState(JSGraph* js_graph,
Builtins::Name name,
Node* context, Node** parameters,
int parameter_count,
Node* outer_frame_state,
ContinuationFrameStateMode mode) {
Isolate* isolate = js_graph->isolate();
Callable callable = Builtins::CallableFor(isolate, name);
CallInterfaceDescriptor descriptor = callable.descriptor();
std::vector<Node*> actual_parameters;
// Stack parameters first
for (int i = 0; i < descriptor.GetStackParameterCount(); ++i) {
actual_parameters.push_back(
parameters[descriptor.GetRegisterParameterCount() + i]);
}
// Register parameters follow, context will be added by instruction selector
// during FrameState translation.
for (int i = 0; i < descriptor.GetRegisterParameterCount(); ++i) {
actual_parameters.push_back(parameters[i]);
}
return CreateBuiltinContinuationFrameStateCommon(
js_graph, name, context, &actual_parameters[0],
static_cast<int>(actual_parameters.size()), outer_frame_state,
Handle<JSFunction>());
}
Node* CreateJavaScriptBuiltinContinuationFrameState(
JSGraph* js_graph, Handle<JSFunction> function, Builtins::Name name,
Node* target, Node* context, Node** stack_parameters,
int stack_parameter_count, Node* outer_frame_state,
ContinuationFrameStateMode mode) {
Isolate* isolate = js_graph->isolate();
Callable callable = Builtins::CallableFor(isolate, name);
// Lazy deopt points where the frame state is assocated with a call get an
// additional parameter for the return result from the call that's added by
// the deoptimizer and not explicitly specified in the frame state. Check that
// there is not a mismatch between the number of frame state parameters and
// the stack parameters required by the builtin taking this into account.
DCHECK_EQ(
Builtins::GetStackParameterCount(isolate, name) + 1, // add receiver
stack_parameter_count +
(mode == ContinuationFrameStateMode::EAGER ? 0 : 1));
Node* argc =
js_graph->Constant(stack_parameter_count -
(mode == ContinuationFrameStateMode::EAGER ? 1 : 0));
// Stack parameters first. They must be first because the receiver is expected
// to be the second value in the translation when creating stack crawls
// (e.g. Error.stack) of optimized JavaScript frames.
std::vector<Node*> actual_parameters;
for (int i = 0; i < stack_parameter_count; ++i) {
actual_parameters.push_back(stack_parameters[i]);
}
// Register parameters follow stack paraemters. The context will be added by
// instruction selector during FrameState translation.
actual_parameters.push_back(target);
actual_parameters.push_back(js_graph->UndefinedConstant());
actual_parameters.push_back(argc);
return CreateBuiltinContinuationFrameStateCommon(
js_graph, name, context, &actual_parameters[0],
static_cast<int>(actual_parameters.size()), outer_frame_state, function);
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -5,6 +5,7 @@
#ifndef V8_COMPILER_FRAME_STATES_H_
#define V8_COMPILER_FRAME_STATES_H_
#include "src/builtins/builtins.h"
#include "src/handles.h"
#include "src/objects/shared-function-info.h"
#include "src/utils.h"
......@@ -14,6 +15,9 @@ namespace internal {
namespace compiler {
class JSGraph;
class Node;
// Flag that describes how to combine the current environment with
// the output of a node to obtain a framestate for lazy bailout.
class OutputFrameStateCombine {
......@@ -80,7 +84,10 @@ enum class FrameStateType {
kTailCallerFunction, // Represents a frame removed by tail call elimination.
kConstructStub, // Represents a ConstructStubFrame.
kGetterStub, // Represents a GetterStubFrame.
kSetterStub // Represents a SetterStubFrame.
kSetterStub, // Represents a SetterStubFrame.
kBuiltinContinuation, // Represents a continuation to a stub.
kJavaScriptBuiltinContinuation // Represents a continuation to a JavaScipt
// builtin.
};
class FrameStateFunctionInfo {
......@@ -100,7 +107,8 @@ class FrameStateFunctionInfo {
static bool IsJSFunctionType(FrameStateType type) {
return type == FrameStateType::kJavaScriptFunction ||
type == FrameStateType::kInterpretedFunction;
type == FrameStateType::kInterpretedFunction ||
type == FrameStateType::kJavaScriptBuiltinContinuation;
}
private:
......@@ -158,6 +166,21 @@ static const int kFrameStateFunctionInput = 4;
static const int kFrameStateOuterStateInput = 5;
static const int kFrameStateInputCount = kFrameStateOuterStateInput + 1;
enum class ContinuationFrameStateMode { EAGER, LAZY };
Node* CreateStubBuiltinContinuationFrameState(JSGraph* graph,
Builtins::Name name,
Node* context, Node** parameters,
int parameter_count,
Node* outer_frame_state,
ContinuationFrameStateMode mode);
Node* CreateJavaScriptBuiltinContinuationFrameState(
JSGraph* graph, Handle<JSFunction> function, Builtins::Name name,
Node* target, Node* context, Node** stack_parameters,
int stack_parameter_count, Node* outer_frame_state,
ContinuationFrameStateMode mode);
} // namespace compiler
} // namespace internal
} // namespace v8
......
......@@ -1300,7 +1300,8 @@ class FrameStateDescriptor : public ZoneObject {
MaybeHandle<SharedFunctionInfo> shared_info() const { return shared_info_; }
FrameStateDescriptor* outer_state() const { return outer_state_; }
bool HasContext() const {
return FrameStateFunctionInfo::IsJSFunctionType(type_);
return FrameStateFunctionInfo::IsJSFunctionType(type_) ||
type_ == FrameStateType::kBuiltinContinuation;
}
size_t GetSize(OutputFrameStateCombine combine =
......
......@@ -7,6 +7,7 @@
#include "src/code-factory.h"
#include "src/code-stubs.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
......@@ -350,6 +351,132 @@ Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) {
return ReduceObjectGetPrototype(node, target);
}
Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
Node* node) {
if (!FLAG_turbo_inline_array_builtins) return NoChange();
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
CallParameters const& p = CallParametersOf(node->op());
// Try to determine the {receiver} map.
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* fncallback = node->op()->ValueInputCount() > 2
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
Node* this_arg = node->op()->ValueInputCount() > 3
? NodeProperties::GetValueInput(node, 3)
: jsgraph()->UndefinedConstant();
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
if (result != NodeProperties::kReliableReceiverMaps) {
return NoChange();
}
if (receiver_maps.size() != 1) return NoChange();
Handle<Map> receiver_map(receiver_maps[0]);
ElementsKind kind = receiver_map->elements_kind();
// TODO(danno): Handle holey Smi and Object fast elements kinds and double
// packed.
if (!IsFastPackedElementsKind(kind) || IsFastDoubleElementsKind(kind)) {
return NoChange();
}
// TODO(danno): forEach can throw. Hook up exceptional edges.
if (NodeProperties::IsExceptionalCall(node)) return NoChange();
Node* k = jsgraph()->ZeroConstant();
Node* original_length = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayLength(FAST_ELEMENTS)),
receiver, effect, control);
Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
Node* eloop = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* vloop = k = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
control = loop;
effect = eloop;
Node* continue_test =
graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
continue_test, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
control = if_true;
std::vector<Node*> checkpoint_params(
{receiver, fncallback, this_arg, k, original_length});
const int stack_parameters = static_cast<int>(checkpoint_params.size());
Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), function, Builtins::kArrayForEachLoopEagerDeoptContinuation,
node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
outer_frame_state, ContinuationFrameStateMode::EAGER);
effect =
graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
// Make sure the map hasn't changed during the iteration
Node* orig_map = jsgraph()->HeapConstant(receiver_map);
Node* array_map = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
receiver, effect, control);
Node* check_map =
graph()->NewNode(simplified()->ReferenceEqual(), array_map, orig_map);
effect =
graph()->NewNode(simplified()->CheckIf(), check_map, effect, control);
// Make sure that the access is still in bounds, since the callback could have
// changed the array's size.
Node* length = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayLength(FAST_ELEMENTS)),
receiver, effect, control);
k = effect =
graph()->NewNode(simplified()->CheckBounds(), k, length, effect, control);
// Reload the elements pointer before calling the callback, since the previous
// callback might have resized the array causing the elements buffer to be
// re-allocated.
Node* elements = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
effect, control);
Node* element = graph()->NewNode(
simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
elements, k, effect, control);
Node* next_k =
graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->Constant(1));
checkpoint_params[3] = next_k;
frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), function, Builtins::kArrayForEachLoopLazyDeoptContinuation,
node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
outer_frame_state, ContinuationFrameStateMode::LAZY);
control = effect = graph()->NewNode(
javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
receiver, context, frame_state, effect, control);
k = next_k;
loop->ReplaceInput(1, control);
vloop->ReplaceInput(1, k);
eloop->ReplaceInput(1, effect);
control = if_false;
effect = eloop;
ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect, control);
return Replace(jsgraph()->UndefinedConstant());
}
Reduction JSCallReducer::ReduceCallApiFunction(
Node* node, Handle<FunctionTemplateInfo> function_template_info) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
......@@ -616,6 +743,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceObjectPrototypeGetProto(node);
case Builtins::kReflectGetPrototypeOf:
return ReduceReflectGetPrototypeOf(node);
case Builtins::kArrayForEach:
return ReduceArrayForEach(function, node);
default:
break;
}
......
......@@ -50,6 +50,7 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceObjectGetPrototypeOf(Node* node);
Reduction ReduceObjectPrototypeGetProto(Node* node);
Reduction ReduceReflectGetPrototypeOf(Node* node);
Reduction ReduceArrayForEach(Handle<JSFunction> function, Node* node);
Reduction ReduceSpreadCall(Node* node, int arity);
Reduction ReduceJSConstruct(Node* node);
Reduction ReduceJSConstructWithSpread(Node* node);
......
This diff is collapsed.
......@@ -159,6 +159,8 @@ class TranslatedFrame {
kArgumentsAdaptor,
kConstructStub,
kCompiledStub,
kBuiltinContinuation,
kJavaScriptBuiltinContinuation,
kInvalid
};
......@@ -230,6 +232,10 @@ class TranslatedFrame {
static TranslatedFrame ConstructStubFrame(BailoutId bailout_id,
SharedFunctionInfo* shared_info,
int height);
static TranslatedFrame BuiltinContinuationFrame(
BailoutId bailout_id, SharedFunctionInfo* shared_info, int height);
static TranslatedFrame JavaScriptBuiltinContinuationFrame(
BailoutId bailout_id, SharedFunctionInfo* shared_info, int height);
static TranslatedFrame CompiledStubFrame(int height, Isolate* isolate) {
return TranslatedFrame(kCompiledStub, isolate, nullptr, height);
}
......@@ -568,6 +574,8 @@ class Deoptimizer : public Malloced {
int frame_index, bool is_setter_stub_frame);
void DoComputeCompiledStubFrame(TranslatedFrame* translated_frame,
int frame_index);
void DoComputeBuiltinContinuation(TranslatedFrame* translated_frame,
int frame_index, bool java_script_frame);
void WriteTranslatedValueToOutput(
TranslatedFrame::iterator* iterator, int* input_index, int frame_index,
......@@ -921,33 +929,35 @@ class TranslationIterator BASE_EMBEDDED {
int index_;
};
#define TRANSLATION_OPCODE_LIST(V) \
V(BEGIN) \
V(JS_FRAME) \
V(INTERPRETED_FRAME) \
V(CONSTRUCT_STUB_FRAME) \
V(GETTER_STUB_FRAME) \
V(SETTER_STUB_FRAME) \
V(ARGUMENTS_ADAPTOR_FRAME) \
V(TAIL_CALLER_FRAME) \
V(COMPILED_STUB_FRAME) \
V(DUPLICATED_OBJECT) \
V(ARGUMENTS_OBJECT) \
V(ARGUMENTS_ELEMENTS) \
V(ARGUMENTS_LENGTH) \
V(CAPTURED_OBJECT) \
V(REGISTER) \
V(INT32_REGISTER) \
V(UINT32_REGISTER) \
V(BOOL_REGISTER) \
V(FLOAT_REGISTER) \
V(DOUBLE_REGISTER) \
V(STACK_SLOT) \
V(INT32_STACK_SLOT) \
V(UINT32_STACK_SLOT) \
V(BOOL_STACK_SLOT) \
V(FLOAT_STACK_SLOT) \
V(DOUBLE_STACK_SLOT) \
#define TRANSLATION_OPCODE_LIST(V) \
V(BEGIN) \
V(JS_FRAME) \
V(INTERPRETED_FRAME) \
V(BUILTIN_CONTINUATION_FRAME) \
V(JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME) \
V(CONSTRUCT_STUB_FRAME) \
V(GETTER_STUB_FRAME) \
V(SETTER_STUB_FRAME) \
V(ARGUMENTS_ADAPTOR_FRAME) \
V(TAIL_CALLER_FRAME) \
V(COMPILED_STUB_FRAME) \
V(DUPLICATED_OBJECT) \
V(ARGUMENTS_OBJECT) \
V(ARGUMENTS_ELEMENTS) \
V(ARGUMENTS_LENGTH) \
V(CAPTURED_OBJECT) \
V(REGISTER) \
V(INT32_REGISTER) \
V(UINT32_REGISTER) \
V(BOOL_REGISTER) \
V(FLOAT_REGISTER) \
V(DOUBLE_REGISTER) \
V(STACK_SLOT) \
V(INT32_STACK_SLOT) \
V(UINT32_STACK_SLOT) \
V(BOOL_STACK_SLOT) \
V(FLOAT_STACK_SLOT) \
V(DOUBLE_STACK_SLOT) \
V(LITERAL)
class Translation BASE_EMBEDDED {
......@@ -980,6 +990,10 @@ class Translation BASE_EMBEDDED {
void BeginTailCallerFrame(int literal_id);
void BeginConstructStubFrame(BailoutId bailout_id, int literal_id,
unsigned height);
void BeginBuiltinContinuationFrame(BailoutId bailout_id, int literal_id,
unsigned height);
void BeginJavaScriptBuiltinContinuationFrame(BailoutId bailout_id,
int literal_id, unsigned height);
void BeginGetterStubFrame(int literal_id);
void BeginSetterStubFrame(int literal_id);
void BeginArgumentsObject(int args_length);
......
......@@ -504,6 +504,8 @@ DEFINE_BOOL(function_context_specialization, false,
"enable function context specialization in TurboFan")
DEFINE_BOOL(turbo_inlining, true, "enable inlining in TurboFan")
DEFINE_BOOL(trace_turbo_inlining, false, "trace TurboFan inlining")
DEFINE_BOOL(turbo_inline_array_builtins, false,
"inline array builtins in TurboFan code")
DEFINE_BOOL(turbo_load_elimination, true, "enable load elimination in TurboFan")
DEFINE_BOOL(trace_turbo_load_elimination, false,
"trace TurboFan load elimination")
......
......@@ -183,7 +183,9 @@ inline JavaScriptFrame::JavaScriptFrame(StackFrameIteratorBase* iterator)
Address JavaScriptFrame::GetParameterSlot(int index) const {
int param_count = ComputeParametersCount();
DCHECK(-1 <= index && index < param_count);
DCHECK(-1 <= index &&
(index < param_count ||
param_count == SharedFunctionInfo::kDontAdaptArgumentsSentinel));
int parameter_offset = (param_count - index - 1) * kPointerSize;
return caller_sp() + parameter_offset;
}
......@@ -279,6 +281,14 @@ inline ConstructFrame::ConstructFrame(StackFrameIteratorBase* iterator)
: InternalFrame(iterator) {
}
inline BuiltinContinuationFrame::BuiltinContinuationFrame(
StackFrameIteratorBase* iterator)
: InternalFrame(iterator) {}
inline JavaScriptBuiltinContinuationFrame::JavaScriptBuiltinContinuationFrame(
StackFrameIteratorBase* iterator)
: JavaScriptFrame(iterator) {}
inline JavaScriptFrameIterator::JavaScriptFrameIterator(
Isolate* isolate)
: iterator_(isolate) {
......
......@@ -504,6 +504,8 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator,
case ENTRY:
case ENTRY_CONSTRUCT:
case EXIT:
case BUILTIN_CONTINUATION:
case JAVA_SCRIPT_BUILTIN_CONTINUATION:
case BUILTIN_EXIT:
case STUB:
case STUB_FAILURE_TRAMPOLINE:
......@@ -802,6 +804,8 @@ void StandardFrame::IterateCompiledFrame(RootVisitor* v) const {
case ENTRY:
case ENTRY_CONSTRUCT:
case EXIT:
case BUILTIN_CONTINUATION:
case JAVA_SCRIPT_BUILTIN_CONTINUATION:
case BUILTIN_EXIT:
case STUB_FAILURE_TRAMPOLINE:
case ARGUMENTS_ADAPTOR:
......@@ -1143,6 +1147,12 @@ int JavaScriptFrame::ComputeParametersCount() const {
return GetNumberOfIncomingArguments();
}
int JavaScriptBuiltinContinuationFrame::ComputeParametersCount() const {
Object* argc_object =
Memory::Object_at(fp() + BuiltinContinuationFrameConstants::kArgCOffset);
return Smi::cast(argc_object)->value();
}
namespace {
bool CannotDeoptFromAsmCode(Code* code, JSFunction* function) {
......@@ -1377,7 +1387,8 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames,
while (jsframe_count != 0) {
frame_opcode = static_cast<Translation::Opcode>(it.Next());
if (frame_opcode == Translation::JS_FRAME ||
frame_opcode == Translation::INTERPRETED_FRAME) {
frame_opcode == Translation::INTERPRETED_FRAME ||
frame_opcode == Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME) {
jsframe_count--;
BailoutId const bailout_id = BailoutId(it.Next());
SharedFunctionInfo* const shared_info =
......@@ -1429,6 +1440,11 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames,
Deoptimizer::GetOutputInfo(output_data, bailout_id, shared_info);
code_offset = FullCodeGenerator::PcField::decode(entry);
abstract_code = AbstractCode::cast(code);
} else if (frame_opcode ==
Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME) {
code_offset = 0;
abstract_code = AbstractCode::cast(isolate()->builtins()->builtin(
Builtins::GetBuiltinFromBailoutId(bailout_id)));
} else {
DCHECK_EQ(frame_opcode, Translation::INTERPRETED_FRAME);
code_offset = bailout_id.ToInt(); // Points to current bytecode.
......@@ -1537,7 +1553,8 @@ void OptimizedFrame::GetFunctions(List<SharedFunctionInfo*>* functions) const {
while (jsframe_count != 0) {
opcode = static_cast<Translation::Opcode>(it.Next());
if (opcode == Translation::JS_FRAME ||
opcode == Translation::INTERPRETED_FRAME) {
opcode == Translation::INTERPRETED_FRAME ||
opcode == Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME) {
it.Next(); // Skip bailout id.
jsframe_count--;
......
......@@ -98,23 +98,25 @@ class StackHandler BASE_EMBEDDED {
DISALLOW_IMPLICIT_CONSTRUCTORS(StackHandler);
};
#define STACK_FRAME_TYPE_LIST(V) \
V(ENTRY, EntryFrame) \
V(ENTRY_CONSTRUCT, EntryConstructFrame) \
V(EXIT, ExitFrame) \
V(JAVA_SCRIPT, JavaScriptFrame) \
V(OPTIMIZED, OptimizedFrame) \
V(WASM_COMPILED, WasmCompiledFrame) \
V(WASM_TO_JS, WasmToJsFrame) \
V(JS_TO_WASM, JsToWasmFrame) \
V(WASM_INTERPRETER_ENTRY, WasmInterpreterEntryFrame) \
V(INTERPRETED, InterpretedFrame) \
V(STUB, StubFrame) \
V(STUB_FAILURE_TRAMPOLINE, StubFailureTrampolineFrame) \
V(INTERNAL, InternalFrame) \
V(CONSTRUCT, ConstructFrame) \
V(ARGUMENTS_ADAPTOR, ArgumentsAdaptorFrame) \
V(BUILTIN, BuiltinFrame) \
#define STACK_FRAME_TYPE_LIST(V) \
V(ENTRY, EntryFrame) \
V(ENTRY_CONSTRUCT, EntryConstructFrame) \
V(EXIT, ExitFrame) \
V(JAVA_SCRIPT, JavaScriptFrame) \
V(OPTIMIZED, OptimizedFrame) \
V(WASM_COMPILED, WasmCompiledFrame) \
V(WASM_TO_JS, WasmToJsFrame) \
V(JS_TO_WASM, JsToWasmFrame) \
V(WASM_INTERPRETER_ENTRY, WasmInterpreterEntryFrame) \
V(INTERPRETED, InterpretedFrame) \
V(STUB, StubFrame) \
V(STUB_FAILURE_TRAMPOLINE, StubFailureTrampolineFrame) \
V(BUILTIN_CONTINUATION, BuiltinContinuationFrame) \
V(JAVA_SCRIPT_BUILTIN_CONTINUATION, JavaScriptBuiltinContinuationFrame) \
V(INTERNAL, InternalFrame) \
V(CONSTRUCT, ConstructFrame) \
V(ARGUMENTS_ADAPTOR, ArgumentsAdaptorFrame) \
V(BUILTIN, BuiltinFrame) \
V(BUILTIN_EXIT, BuiltinExitFrame)
// Every pointer in a frame has a slot id. On 32-bit platforms, doubles consume
......@@ -359,6 +361,15 @@ class ConstructFrameConstants : public TypedFrameConstants {
DEFINE_TYPED_FRAME_SIZES(4);
};
class BuiltinContinuationFrameConstants : public TypedFrameConstants {
public:
// FP-relative.
static const int kFunctionOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
static const int kBuiltinOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
static const int kArgCOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(2);
DEFINE_TYPED_FRAME_SIZES(2);
};
class StubFailureTrampolineFrameConstants : public InternalFrameConstants {
public:
static const int kArgumentsArgumentsOffset =
......@@ -525,6 +536,12 @@ class StackFrame BASE_EMBEDDED {
bool is_stub_failure_trampoline() const {
return type() == STUB_FAILURE_TRAMPOLINE;
}
bool is_builtin_continuation() const {
return type() == BUILTIN_CONTINUATION;
}
bool is_java_script_builtin_continuation() const {
return type() == JAVA_SCRIPT_BUILTIN_CONTINUATION;
}
bool is_construct() const { return type() == CONSTRUCT; }
bool is_builtin_exit() const { return type() == BUILTIN_EXIT; }
virtual bool is_standard() const { return false; }
......@@ -532,7 +549,8 @@ class StackFrame BASE_EMBEDDED {
bool is_java_script() const {
Type type = this->type();
return (type == JAVA_SCRIPT) || (type == OPTIMIZED) ||
(type == INTERPRETED) || (type == BUILTIN);
(type == INTERPRETED) || (type == BUILTIN) ||
(type == JAVA_SCRIPT_BUILTIN_CONTINUATION);
}
bool is_wasm() const {
Type type = this->type();
......@@ -1460,6 +1478,40 @@ class ConstructFrame: public InternalFrame {
friend class StackFrameIteratorBase;
};
class BuiltinContinuationFrame : public InternalFrame {
public:
Type type() const override { return BUILTIN_CONTINUATION; }
static BuiltinContinuationFrame* cast(StackFrame* frame) {
DCHECK(frame->is_builtin_continuation());
return static_cast<BuiltinContinuationFrame*>(frame);
}
protected:
inline explicit BuiltinContinuationFrame(StackFrameIteratorBase* iterator);
private:
friend class StackFrameIteratorBase;
};
class JavaScriptBuiltinContinuationFrame : public JavaScriptFrame {
public:
Type type() const override { return JAVA_SCRIPT_BUILTIN_CONTINUATION; }
static JavaScriptBuiltinContinuationFrame* cast(StackFrame* frame) {
DCHECK(frame->is_java_script_builtin_continuation());
return static_cast<JavaScriptBuiltinContinuationFrame*>(frame);
}
int ComputeParametersCount() const override;
protected:
inline explicit JavaScriptBuiltinContinuationFrame(
StackFrameIteratorBase* iterator);
private:
friend class StackFrameIteratorBase;
};
class StackFrameIteratorBase BASE_EMBEDDED {
public:
......
......@@ -496,6 +496,7 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
switch (frame->type()) {
case StackFrame::JAVA_SCRIPT:
case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION:
case StackFrame::OPTIMIZED:
case StackFrame::INTERPRETED:
case StackFrame::BUILTIN: {
......
......@@ -14395,6 +14395,18 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(
break;
}
case Translation::BUILTIN_CONTINUATION_FRAME:
case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME: {
int bailout_id = iterator.Next();
int shared_info_id = iterator.Next();
Object* shared_info = LiteralArray()->get(shared_info_id);
unsigned height = iterator.Next();
os << "{bailout_id=" << bailout_id << ", function="
<< Brief(SharedFunctionInfo::cast(shared_info)->DebugName())
<< ", height=" << height << "}";
break;
}
case Translation::COMPILED_STUB_FRAME: {
Code::Kind stub_kind = static_cast<Code::Kind>(iterator.Next());
os << "{kind=" << stub_kind << "}";
......
......@@ -922,6 +922,8 @@ class BailoutId {
V8_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream&, BailoutId);
private:
friend class Builtins;
static const int kNoneId = -1;
// Using 0 could disguise errors.
......@@ -940,6 +942,11 @@ class BailoutId {
// Every compiled stub starts with this id.
static const int kStubEntryId = 6;
// Builtin continuations bailout ids start here. If you need to add a
// non-builtin BailoutId, add it before this id so that this Id has the
// highest number.
static const int kFirstBuiltinContinuationId = 7;
int id_;
};
......
// Copyright 2017 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: --allow-natives-syntax --expose-gc --turbo-inline-array-builtins
var a = [0, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,0,0];
var b = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];
var c = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];
// Unknown field access leads to soft-deopt unrelated to forEach, should still
// lead to correct result.
(function() {
var result = 0;
var eagerDeoptInCalled = function(deopt) {
var sum = function(v,i,o) {
result += v;
if (i == 13 && deopt) {
a.abc = 25;
}
}
a.forEach(sum);
}
eagerDeoptInCalled();
eagerDeoptInCalled();
%OptimizeFunctionOnNextCall(eagerDeoptInCalled);
eagerDeoptInCalled();
eagerDeoptInCalled(true);
eagerDeoptInCalled();
assertEquals(1500, result);
})();
// Length change detected during loop, must cause properly handled eager deopt.
(function() {
var result = 0;
var eagerDeoptInCalled = function(deopt) {
var sum = function(v,i,o) {
result += v;
a.length = (i == 13 && deopt) ? 25 : 27;
}
a.forEach(sum);
}
eagerDeoptInCalled();
eagerDeoptInCalled();
%OptimizeFunctionOnNextCall(eagerDeoptInCalled);
eagerDeoptInCalled();
eagerDeoptInCalled(true);
eagerDeoptInCalled();
assertEquals(1500, result);
})();
// Escape analyzed array
(function() {
var result = 0;
var eagerDeoptInCalled = function(deopt) {
var a_noescape = [0,1,2,3,4,5];
var sum = function(v,i,o) {
result += v;
if (i == 13 && deopt) {
a_noescape.length = 25;
}
}
a_noescape.forEach(sum);
}
eagerDeoptInCalled();
eagerDeoptInCalled();
%OptimizeFunctionOnNextCall(eagerDeoptInCalled);
eagerDeoptInCalled();
eagerDeoptInCalled(true);
eagerDeoptInCalled();
assertEquals(75, result);
})();
// Escape analyzed array where sum function isn't inlined, forcing a lazy deopt
// with GC that relies on the stashed-away return result fro the lazy deopt
// being properly stored in a place on the stack that gets GC'ed.
(function() {
var result = 0;
var lazyDeopt = function(deopt) {
var b = [1,2,3];
var sum = function(v,i,o) {
result += i;
if (i == 1 && deopt) {
%DeoptimizeFunction(lazyDeopt);
}
gc(); gc();
};
%NeverOptimizeFunction(sum);
b.forEach(sum);
}
lazyDeopt();
lazyDeopt();
%OptimizeFunctionOnNextCall(lazyDeopt);
lazyDeopt();
lazyDeopt(true);
lazyDeopt();
})();
// Lazy deopt from runtime call from inlined callback function.
(function() {
var result = 0;
var lazyDeopt = function(deopt) {
var sum = function(v,i,o) {
result += i;
if (i == 13 && deopt) {
%DeoptimizeNow();
}
}
b.forEach(sum);
}
lazyDeopt();
lazyDeopt();
%OptimizeFunctionOnNextCall(lazyDeopt);
lazyDeopt();
lazyDeopt(true);
lazyDeopt();
assertEquals(1500, result);
})();
// Lazy deopt from runtime call from non-inline callback function.
(function() {
var result = 0;
var lazyDeopt = function(deopt) {
var sum = function(v,i,o) {
result += i;
if (i == 13 && deopt) {
%DeoptimizeNow();
}
};
%NeverOptimizeFunction(sum);
b.forEach(sum);
}
lazyDeopt();
lazyDeopt();
%OptimizeFunctionOnNextCall(lazyDeopt);
lazyDeopt();
lazyDeopt(true);
lazyDeopt();
assertEquals(1500, result);
})();
(function() {
var result = 0;
var lazyDeopt = function(deopt) {
var sum = function(v,i,o) {
result += i;
if (i == 13 && deopt) {
%DeoptimizeNow();
gc();
gc();
gc();
}
}
c.forEach(sum);
}
lazyDeopt();
lazyDeopt();
%OptimizeFunctionOnNextCall(lazyDeopt);
lazyDeopt();
lazyDeopt(true);
lazyDeopt();
assertEquals(1500, result);
})();
// Call to a.forEach is done inside a try-catch block and the callback function
// being called actually throws.
(function() {
var caught = false;
var result = 0;
var lazyDeopt = function(deopt) {
var sum = function(v,i,o) {
result += i;
if (i == 1 && deopt) {
throw("a");
}
}
try {
c.forEach(sum);
} catch (e) {
caught = true;
}
}
lazyDeopt();
lazyDeopt();
%OptimizeFunctionOnNextCall(lazyDeopt);
lazyDeopt();
assertDoesNotThrow(lazyDeopt.bind(this, true));
assertTrue(caught);
lazyDeopt();
})();
// Call to a.forEach is done inside a try-catch block and the callback function
// being called actually throws, but the callback is not inlined.
(function() {
var caught = false;
var result = 0;
var lazyDeopt = function(deopt) {
var sum = function(v,i,o) {
result += i;
if (i == 1 && deopt) {
throw("a");
}
};
%NeverOptimizeFunction(sum);
try {
c.forEach(sum);
} catch (e) {
caught = true;
}
}
lazyDeopt();
lazyDeopt();
%OptimizeFunctionOnNextCall(lazyDeopt);
lazyDeopt();
assertDoesNotThrow(lazyDeopt.bind(this, true));
assertTrue(caught);
lazyDeopt();
})();
(function() {
var re = /Array\.forEach/;
var lazyDeopt = function(deopt) {
var b = [1,2,3];
var result = 0;
var sum = function(v,i,o) {
result += v;
if (i == 1) {
var e = new Error();
assertTrue(re.exec(e.stack) !== null);
}
};
var o = [1,2,3];
b.forEach(sum);
}
lazyDeopt();
lazyDeopt();
%OptimizeFunctionOnNextCall(lazyDeopt);
lazyDeopt();
})();
(function() {
var re = /Array\.forEach/;
var lazyDeopt = function(deopt) {
var b = [1,2,3];
var result = 0;
var sum = function(v,i,o) {
result += v;
if (i == 1) {
var e = new Error();
assertTrue(re.exec(e.stack) !== null);
}
};
%NeverOptimizeFunction(sum);
var o = [1,2,3];
b.forEach(sum);
}
lazyDeopt();
lazyDeopt();
%OptimizeFunctionOnNextCall(lazyDeopt);
lazyDeopt();
})();
(function() {
var re = /Array\.forEach/;
var lazyDeopt = function(deopt) {
var b = [1,2,3];
var result = 0;
var sum = function(v,i,o) {
result += v;
if (i == 1) {
%DeoptimizeNow();
} else if (i == 2) {
var e = new Error();
assertTrue(re.exec(e.stack) !== null);
}
};
var o = [1,2,3];
b.forEach(sum);
}
lazyDeopt();
lazyDeopt();
%OptimizeFunctionOnNextCall(lazyDeopt);
lazyDeopt();
})();
(function() {
var re = /Array\.forEach/;
var a = [1,2,3];
var result = 0;
var lazyDeopt = function() {
var sum = function(v,i,o) {
result += i;
if (i == 1) {
%DeoptimizeFunction(lazyDeopt);
throw new Error();
}
};
a.forEach(sum);
}
assertThrows(() => lazyDeopt());
assertThrows(() => lazyDeopt());
try {
lazyDeopt();
} catch (e) {
assertTrue(re.exec(e.stack) !== null);
}
%OptimizeFunctionOnNextCall(lazyDeopt);
try {
lazyDeopt();
} catch (e) {
assertTrue(re.exec(e.stack) !== null);
}
})();
......@@ -323,6 +323,8 @@ FRAME_MARKERS = (
"INTERPRETED",
"STUB",
"STUB_FAILURE_TRAMPOLINE",
"BUILTIN_CONTINUATION",
"JAVA_SCRIPT_BUILTIN_CONTINUATION",
"INTERNAL",
"CONSTRUCT",
"ARGUMENTS_ADAPTOR",
......
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