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) { ...@@ -1654,6 +1654,69 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs); 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, static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) { Deoptimizer::BailoutType type) {
{ {
......
...@@ -1694,6 +1694,75 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) { ...@@ -1694,6 +1694,75 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs); 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, static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) { Deoptimizer::BailoutType type) {
{ {
......
...@@ -1200,6 +1200,38 @@ TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinCodeStubAssembler) { ...@@ -1200,6 +1200,38 @@ TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinCodeStubAssembler) {
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction); &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) { TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) {
Node* argc = Node* argc =
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
......
...@@ -158,6 +158,33 @@ namespace internal { ...@@ -158,6 +158,33 @@ namespace internal {
ASM(NotifyLazyDeoptimized) \ ASM(NotifyLazyDeoptimized) \
ASM(NotifyStubFailure) \ ASM(NotifyStubFailure) \
ASM(NotifyStubFailureSaveDoubles) \ 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) \ ASM(OnStackReplacement) \
\ \
/* API callback handling */ \ /* API callback handling */ \
...@@ -270,6 +297,10 @@ namespace internal { ...@@ -270,6 +297,10 @@ namespace internal {
/* ES6 #sec-array.prototype.foreach */ \ /* ES6 #sec-array.prototype.foreach */ \
TFS(ArrayForEachLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \ TFS(ArrayForEachLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
kObject, kInitialK, kLength, kTo) \ kObject, kInitialK, kLength, kTo) \
TFJ(ArrayForEachLoopEagerDeoptContinuation, 4, kCallbackFn, kThisArg, \
kInitialK, kLength) \
TFJ(ArrayForEachLoopLazyDeoptContinuation, 5, kCallbackFn, kThisArg, \
kInitialK, kLength, kResult) \
TFJ(ArrayForEach, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ TFJ(ArrayForEach, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.every */ \ /* ES6 #sec-array.prototype.every */ \
TFS(ArrayEveryLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \ TFS(ArrayEveryLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
...@@ -1077,6 +1108,14 @@ namespace internal { ...@@ -1077,6 +1108,14 @@ namespace internal {
BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, \ BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, \
V, 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) #define BUILTINS_WITH_UNTAGGED_PARAMS(V) V(WasmCompileLazy)
} // namespace internal } // namespace internal
......
...@@ -26,6 +26,32 @@ Builtins::Builtins() : initialized_(false) { ...@@ -26,6 +26,32 @@ Builtins::Builtins() : initialized_(false) {
Builtins::~Builtins() {} 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::TearDown() { initialized_ = false; }
void Builtins::IterateBuiltins(RootVisitor* v) { void Builtins::IterateBuiltins(RootVisitor* v) {
...@@ -118,17 +144,31 @@ Callable Builtins::CallableFor(Isolate* isolate, Name name) { ...@@ -118,17 +144,31 @@ Callable Builtins::CallableFor(Isolate* isolate, Name name) {
switch (name) { switch (name) {
// This macro is deliberately crafted so as to emit very little code, // This macro is deliberately crafted so as to emit very little code,
// in order to keep binary size of this function under control. // in order to keep binary size of this function under control.
#define CASE(Name, ...) \ #define CASE_OTHER(Name, ...) \
case k##Name: { \ case k##Name: { \
key = Builtin_##Name##_InterfaceDescriptor::key(); \ key = Builtin_##Name##_InterfaceDescriptor::key(); \
break; \ break; \
} }
BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, CASE, CASE, BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, CASE_OTHER,
CASE, IGNORE_BUILTIN, IGNORE_BUILTIN) CASE_OTHER, CASE_OTHER, IGNORE_BUILTIN, IGNORE_BUILTIN)
#undef CASE #undef CASE_OTHER
case kConsoleAssert: { case kConsoleAssert: {
return Callable(code, BuiltinDescriptor(isolate)); 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: default:
UNREACHABLE(); UNREACHABLE();
} }
...@@ -136,6 +176,21 @@ Callable Builtins::CallableFor(Isolate* isolate, Name name) { ...@@ -136,6 +176,21 @@ Callable Builtins::CallableFor(Isolate* isolate, Name name) {
return Callable(code, descriptor); 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 // static
const char* Builtins::name(int index) { const char* Builtins::name(int index) {
switch (index) { switch (index) {
......
...@@ -18,6 +18,7 @@ class Handle; ...@@ -18,6 +18,7 @@ class Handle;
class Isolate; class Isolate;
// Forward declarations. // Forward declarations.
class BailoutId;
class RootVisitor; class RootVisitor;
enum class InterpreterPushArgsMode : unsigned; enum class InterpreterPushArgsMode : unsigned;
namespace compiler { namespace compiler {
...@@ -43,6 +44,9 @@ class Builtins { ...@@ -43,6 +44,9 @@ class Builtins {
builtin_count builtin_count
}; };
static BailoutId GetContinuationBailoutId(Name name);
static Name GetBuiltinFromBailoutId(BailoutId);
#define DECLARE_BUILTIN_ACCESSOR(Name, ...) \ #define DECLARE_BUILTIN_ACCESSOR(Name, ...) \
V8_EXPORT_PRIVATE Handle<Code> Name(); V8_EXPORT_PRIVATE Handle<Code> Name();
BUILTIN_LIST_ALL(DECLARE_BUILTIN_ACCESSOR) BUILTIN_LIST_ALL(DECLARE_BUILTIN_ACCESSOR)
...@@ -82,6 +86,8 @@ class Builtins { ...@@ -82,6 +86,8 @@ class Builtins {
V8_EXPORT_PRIVATE static Callable CallableFor(Isolate* isolate, Name name); V8_EXPORT_PRIVATE static Callable CallableFor(Isolate* isolate, Name name);
static int GetStackParameterCount(Isolate* isolate, Name name);
static const char* name(int index); static const char* name(int index);
// Returns the C++ entry point for builtins implemented in C++, and the null // Returns the C++ entry point for builtins implemented in C++, and the null
......
...@@ -1467,6 +1467,72 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) { ...@@ -1467,6 +1467,72 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs); 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, static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) { Deoptimizer::BailoutType type) {
{ {
......
...@@ -1646,6 +1646,70 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) { ...@@ -1646,6 +1646,70 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs); 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, static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) { Deoptimizer::BailoutType type) {
{ {
......
...@@ -1647,6 +1647,70 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) { ...@@ -1647,6 +1647,70 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs); 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, static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) { Deoptimizer::BailoutType type) {
{ {
......
...@@ -1430,6 +1430,72 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) { ...@@ -1430,6 +1430,72 @@ void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
Generate_NotifyStubFailureHelper(masm, kSaveFPRegs); 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, static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
Deoptimizer::BailoutType type) { Deoptimizer::BailoutType type) {
// Enter an internal frame. // Enter an internal frame.
......
...@@ -813,6 +813,22 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor( ...@@ -813,6 +813,22 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
descriptor->bailout_id(), shared_info_id, descriptor->bailout_id(), shared_info_id,
static_cast<unsigned int>(descriptor->parameters_count())); static_cast<unsigned int>(descriptor->parameters_count()));
break; 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: case FrameStateType::kGetterStub:
translation->BeginGetterStubFrame(shared_info_id); translation->BeginGetterStubFrame(shared_info_id);
break; break;
......
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
#include "src/compiler/frame-states.h" #include "src/compiler/frame-states.h"
#include "src/base/functional.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/handles-inl.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
...@@ -64,6 +68,12 @@ std::ostream& operator<<(std::ostream& os, FrameStateType type) { ...@@ -64,6 +68,12 @@ std::ostream& operator<<(std::ostream& os, FrameStateType type) {
case FrameStateType::kConstructStub: case FrameStateType::kConstructStub:
os << "CONSTRUCT_STUB"; os << "CONSTRUCT_STUB";
break; break;
case FrameStateType::kBuiltinContinuation:
os << "BUILTIN_CONTINUATION_FRAME";
break;
case FrameStateType::kJavaScriptBuiltinContinuation:
os << "JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME";
break;
case FrameStateType::kGetterStub: case FrameStateType::kGetterStub:
os << "GETTER_STUB"; os << "GETTER_STUB";
break; break;
...@@ -85,6 +95,112 @@ std::ostream& operator<<(std::ostream& os, FrameStateInfo const& info) { ...@@ -85,6 +95,112 @@ std::ostream& operator<<(std::ostream& os, FrameStateInfo const& info) {
return os; 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 compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef V8_COMPILER_FRAME_STATES_H_ #ifndef V8_COMPILER_FRAME_STATES_H_
#define V8_COMPILER_FRAME_STATES_H_ #define V8_COMPILER_FRAME_STATES_H_
#include "src/builtins/builtins.h"
#include "src/handles.h" #include "src/handles.h"
#include "src/objects/shared-function-info.h" #include "src/objects/shared-function-info.h"
#include "src/utils.h" #include "src/utils.h"
...@@ -14,6 +15,9 @@ namespace internal { ...@@ -14,6 +15,9 @@ namespace internal {
namespace compiler { namespace compiler {
class JSGraph;
class Node;
// Flag that describes how to combine the current environment with // Flag that describes how to combine the current environment with
// the output of a node to obtain a framestate for lazy bailout. // the output of a node to obtain a framestate for lazy bailout.
class OutputFrameStateCombine { class OutputFrameStateCombine {
...@@ -80,7 +84,10 @@ enum class FrameStateType { ...@@ -80,7 +84,10 @@ enum class FrameStateType {
kTailCallerFunction, // Represents a frame removed by tail call elimination. kTailCallerFunction, // Represents a frame removed by tail call elimination.
kConstructStub, // Represents a ConstructStubFrame. kConstructStub, // Represents a ConstructStubFrame.
kGetterStub, // Represents a GetterStubFrame. 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 { class FrameStateFunctionInfo {
...@@ -100,7 +107,8 @@ class FrameStateFunctionInfo { ...@@ -100,7 +107,8 @@ class FrameStateFunctionInfo {
static bool IsJSFunctionType(FrameStateType type) { static bool IsJSFunctionType(FrameStateType type) {
return type == FrameStateType::kJavaScriptFunction || return type == FrameStateType::kJavaScriptFunction ||
type == FrameStateType::kInterpretedFunction; type == FrameStateType::kInterpretedFunction ||
type == FrameStateType::kJavaScriptBuiltinContinuation;
} }
private: private:
...@@ -158,6 +166,21 @@ static const int kFrameStateFunctionInput = 4; ...@@ -158,6 +166,21 @@ static const int kFrameStateFunctionInput = 4;
static const int kFrameStateOuterStateInput = 5; static const int kFrameStateOuterStateInput = 5;
static const int kFrameStateInputCount = kFrameStateOuterStateInput + 1; 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 compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -1300,7 +1300,8 @@ class FrameStateDescriptor : public ZoneObject { ...@@ -1300,7 +1300,8 @@ class FrameStateDescriptor : public ZoneObject {
MaybeHandle<SharedFunctionInfo> shared_info() const { return shared_info_; } MaybeHandle<SharedFunctionInfo> shared_info() const { return shared_info_; }
FrameStateDescriptor* outer_state() const { return outer_state_; } FrameStateDescriptor* outer_state() const { return outer_state_; }
bool HasContext() const { bool HasContext() const {
return FrameStateFunctionInfo::IsJSFunctionType(type_); return FrameStateFunctionInfo::IsJSFunctionType(type_) ||
type_ == FrameStateType::kBuiltinContinuation;
} }
size_t GetSize(OutputFrameStateCombine combine = size_t GetSize(OutputFrameStateCombine combine =
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "src/code-factory.h" #include "src/code-factory.h"
#include "src/code-stubs.h" #include "src/code-stubs.h"
#include "src/compilation-dependencies.h" #include "src/compilation-dependencies.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.h" #include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h" #include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h" #include "src/compiler/node-matchers.h"
...@@ -350,6 +351,132 @@ Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) { ...@@ -350,6 +351,132 @@ Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) {
return ReduceObjectGetPrototype(node, target); 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( Reduction JSCallReducer::ReduceCallApiFunction(
Node* node, Handle<FunctionTemplateInfo> function_template_info) { Node* node, Handle<FunctionTemplateInfo> function_template_info) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
...@@ -616,6 +743,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { ...@@ -616,6 +743,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceObjectPrototypeGetProto(node); return ReduceObjectPrototypeGetProto(node);
case Builtins::kReflectGetPrototypeOf: case Builtins::kReflectGetPrototypeOf:
return ReduceReflectGetPrototypeOf(node); return ReduceReflectGetPrototypeOf(node);
case Builtins::kArrayForEach:
return ReduceArrayForEach(function, node);
default: default:
break; break;
} }
......
...@@ -50,6 +50,7 @@ class JSCallReducer final : public AdvancedReducer { ...@@ -50,6 +50,7 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceObjectGetPrototypeOf(Node* node); Reduction ReduceObjectGetPrototypeOf(Node* node);
Reduction ReduceObjectPrototypeGetProto(Node* node); Reduction ReduceObjectPrototypeGetProto(Node* node);
Reduction ReduceReflectGetPrototypeOf(Node* node); Reduction ReduceReflectGetPrototypeOf(Node* node);
Reduction ReduceArrayForEach(Handle<JSFunction> function, Node* node);
Reduction ReduceSpreadCall(Node* node, int arity); Reduction ReduceSpreadCall(Node* node, int arity);
Reduction ReduceJSConstruct(Node* node); Reduction ReduceJSConstruct(Node* node);
Reduction ReduceJSConstructWithSpread(Node* node); Reduction ReduceJSConstructWithSpread(Node* node);
......
This diff is collapsed.
...@@ -159,6 +159,8 @@ class TranslatedFrame { ...@@ -159,6 +159,8 @@ class TranslatedFrame {
kArgumentsAdaptor, kArgumentsAdaptor,
kConstructStub, kConstructStub,
kCompiledStub, kCompiledStub,
kBuiltinContinuation,
kJavaScriptBuiltinContinuation,
kInvalid kInvalid
}; };
...@@ -230,6 +232,10 @@ class TranslatedFrame { ...@@ -230,6 +232,10 @@ class TranslatedFrame {
static TranslatedFrame ConstructStubFrame(BailoutId bailout_id, static TranslatedFrame ConstructStubFrame(BailoutId bailout_id,
SharedFunctionInfo* shared_info, SharedFunctionInfo* shared_info,
int height); 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) { static TranslatedFrame CompiledStubFrame(int height, Isolate* isolate) {
return TranslatedFrame(kCompiledStub, isolate, nullptr, height); return TranslatedFrame(kCompiledStub, isolate, nullptr, height);
} }
...@@ -568,6 +574,8 @@ class Deoptimizer : public Malloced { ...@@ -568,6 +574,8 @@ class Deoptimizer : public Malloced {
int frame_index, bool is_setter_stub_frame); int frame_index, bool is_setter_stub_frame);
void DoComputeCompiledStubFrame(TranslatedFrame* translated_frame, void DoComputeCompiledStubFrame(TranslatedFrame* translated_frame,
int frame_index); int frame_index);
void DoComputeBuiltinContinuation(TranslatedFrame* translated_frame,
int frame_index, bool java_script_frame);
void WriteTranslatedValueToOutput( void WriteTranslatedValueToOutput(
TranslatedFrame::iterator* iterator, int* input_index, int frame_index, TranslatedFrame::iterator* iterator, int* input_index, int frame_index,
...@@ -921,33 +929,35 @@ class TranslationIterator BASE_EMBEDDED { ...@@ -921,33 +929,35 @@ class TranslationIterator BASE_EMBEDDED {
int index_; int index_;
}; };
#define TRANSLATION_OPCODE_LIST(V) \ #define TRANSLATION_OPCODE_LIST(V) \
V(BEGIN) \ V(BEGIN) \
V(JS_FRAME) \ V(JS_FRAME) \
V(INTERPRETED_FRAME) \ V(INTERPRETED_FRAME) \
V(CONSTRUCT_STUB_FRAME) \ V(BUILTIN_CONTINUATION_FRAME) \
V(GETTER_STUB_FRAME) \ V(JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME) \
V(SETTER_STUB_FRAME) \ V(CONSTRUCT_STUB_FRAME) \
V(ARGUMENTS_ADAPTOR_FRAME) \ V(GETTER_STUB_FRAME) \
V(TAIL_CALLER_FRAME) \ V(SETTER_STUB_FRAME) \
V(COMPILED_STUB_FRAME) \ V(ARGUMENTS_ADAPTOR_FRAME) \
V(DUPLICATED_OBJECT) \ V(TAIL_CALLER_FRAME) \
V(ARGUMENTS_OBJECT) \ V(COMPILED_STUB_FRAME) \
V(ARGUMENTS_ELEMENTS) \ V(DUPLICATED_OBJECT) \
V(ARGUMENTS_LENGTH) \ V(ARGUMENTS_OBJECT) \
V(CAPTURED_OBJECT) \ V(ARGUMENTS_ELEMENTS) \
V(REGISTER) \ V(ARGUMENTS_LENGTH) \
V(INT32_REGISTER) \ V(CAPTURED_OBJECT) \
V(UINT32_REGISTER) \ V(REGISTER) \
V(BOOL_REGISTER) \ V(INT32_REGISTER) \
V(FLOAT_REGISTER) \ V(UINT32_REGISTER) \
V(DOUBLE_REGISTER) \ V(BOOL_REGISTER) \
V(STACK_SLOT) \ V(FLOAT_REGISTER) \
V(INT32_STACK_SLOT) \ V(DOUBLE_REGISTER) \
V(UINT32_STACK_SLOT) \ V(STACK_SLOT) \
V(BOOL_STACK_SLOT) \ V(INT32_STACK_SLOT) \
V(FLOAT_STACK_SLOT) \ V(UINT32_STACK_SLOT) \
V(DOUBLE_STACK_SLOT) \ V(BOOL_STACK_SLOT) \
V(FLOAT_STACK_SLOT) \
V(DOUBLE_STACK_SLOT) \
V(LITERAL) V(LITERAL)
class Translation BASE_EMBEDDED { class Translation BASE_EMBEDDED {
...@@ -980,6 +990,10 @@ class Translation BASE_EMBEDDED { ...@@ -980,6 +990,10 @@ class Translation BASE_EMBEDDED {
void BeginTailCallerFrame(int literal_id); void BeginTailCallerFrame(int literal_id);
void BeginConstructStubFrame(BailoutId bailout_id, int literal_id, void BeginConstructStubFrame(BailoutId bailout_id, int literal_id,
unsigned height); 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 BeginGetterStubFrame(int literal_id);
void BeginSetterStubFrame(int literal_id); void BeginSetterStubFrame(int literal_id);
void BeginArgumentsObject(int args_length); void BeginArgumentsObject(int args_length);
......
...@@ -504,6 +504,8 @@ DEFINE_BOOL(function_context_specialization, false, ...@@ -504,6 +504,8 @@ DEFINE_BOOL(function_context_specialization, false,
"enable function context specialization in TurboFan") "enable function context specialization in TurboFan")
DEFINE_BOOL(turbo_inlining, true, "enable inlining in TurboFan") DEFINE_BOOL(turbo_inlining, true, "enable inlining in TurboFan")
DEFINE_BOOL(trace_turbo_inlining, false, "trace TurboFan inlining") 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(turbo_load_elimination, true, "enable load elimination in TurboFan")
DEFINE_BOOL(trace_turbo_load_elimination, false, DEFINE_BOOL(trace_turbo_load_elimination, false,
"trace TurboFan load elimination") "trace TurboFan load elimination")
......
...@@ -183,7 +183,9 @@ inline JavaScriptFrame::JavaScriptFrame(StackFrameIteratorBase* iterator) ...@@ -183,7 +183,9 @@ inline JavaScriptFrame::JavaScriptFrame(StackFrameIteratorBase* iterator)
Address JavaScriptFrame::GetParameterSlot(int index) const { Address JavaScriptFrame::GetParameterSlot(int index) const {
int param_count = ComputeParametersCount(); 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; int parameter_offset = (param_count - index - 1) * kPointerSize;
return caller_sp() + parameter_offset; return caller_sp() + parameter_offset;
} }
...@@ -279,6 +281,14 @@ inline ConstructFrame::ConstructFrame(StackFrameIteratorBase* iterator) ...@@ -279,6 +281,14 @@ inline ConstructFrame::ConstructFrame(StackFrameIteratorBase* iterator)
: InternalFrame(iterator) { : InternalFrame(iterator) {
} }
inline BuiltinContinuationFrame::BuiltinContinuationFrame(
StackFrameIteratorBase* iterator)
: InternalFrame(iterator) {}
inline JavaScriptBuiltinContinuationFrame::JavaScriptBuiltinContinuationFrame(
StackFrameIteratorBase* iterator)
: JavaScriptFrame(iterator) {}
inline JavaScriptFrameIterator::JavaScriptFrameIterator( inline JavaScriptFrameIterator::JavaScriptFrameIterator(
Isolate* isolate) Isolate* isolate)
: iterator_(isolate) { : iterator_(isolate) {
......
...@@ -504,6 +504,8 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator, ...@@ -504,6 +504,8 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator,
case ENTRY: case ENTRY:
case ENTRY_CONSTRUCT: case ENTRY_CONSTRUCT:
case EXIT: case EXIT:
case BUILTIN_CONTINUATION:
case JAVA_SCRIPT_BUILTIN_CONTINUATION:
case BUILTIN_EXIT: case BUILTIN_EXIT:
case STUB: case STUB:
case STUB_FAILURE_TRAMPOLINE: case STUB_FAILURE_TRAMPOLINE:
...@@ -802,6 +804,8 @@ void StandardFrame::IterateCompiledFrame(RootVisitor* v) const { ...@@ -802,6 +804,8 @@ void StandardFrame::IterateCompiledFrame(RootVisitor* v) const {
case ENTRY: case ENTRY:
case ENTRY_CONSTRUCT: case ENTRY_CONSTRUCT:
case EXIT: case EXIT:
case BUILTIN_CONTINUATION:
case JAVA_SCRIPT_BUILTIN_CONTINUATION:
case BUILTIN_EXIT: case BUILTIN_EXIT:
case STUB_FAILURE_TRAMPOLINE: case STUB_FAILURE_TRAMPOLINE:
case ARGUMENTS_ADAPTOR: case ARGUMENTS_ADAPTOR:
...@@ -1143,6 +1147,12 @@ int JavaScriptFrame::ComputeParametersCount() const { ...@@ -1143,6 +1147,12 @@ int JavaScriptFrame::ComputeParametersCount() const {
return GetNumberOfIncomingArguments(); return GetNumberOfIncomingArguments();
} }
int JavaScriptBuiltinContinuationFrame::ComputeParametersCount() const {
Object* argc_object =
Memory::Object_at(fp() + BuiltinContinuationFrameConstants::kArgCOffset);
return Smi::cast(argc_object)->value();
}
namespace { namespace {
bool CannotDeoptFromAsmCode(Code* code, JSFunction* function) { bool CannotDeoptFromAsmCode(Code* code, JSFunction* function) {
...@@ -1377,7 +1387,8 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames, ...@@ -1377,7 +1387,8 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames,
while (jsframe_count != 0) { while (jsframe_count != 0) {
frame_opcode = static_cast<Translation::Opcode>(it.Next()); frame_opcode = static_cast<Translation::Opcode>(it.Next());
if (frame_opcode == Translation::JS_FRAME || 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--; jsframe_count--;
BailoutId const bailout_id = BailoutId(it.Next()); BailoutId const bailout_id = BailoutId(it.Next());
SharedFunctionInfo* const shared_info = SharedFunctionInfo* const shared_info =
...@@ -1429,6 +1440,11 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames, ...@@ -1429,6 +1440,11 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames,
Deoptimizer::GetOutputInfo(output_data, bailout_id, shared_info); Deoptimizer::GetOutputInfo(output_data, bailout_id, shared_info);
code_offset = FullCodeGenerator::PcField::decode(entry); code_offset = FullCodeGenerator::PcField::decode(entry);
abstract_code = AbstractCode::cast(code); 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 { } else {
DCHECK_EQ(frame_opcode, Translation::INTERPRETED_FRAME); DCHECK_EQ(frame_opcode, Translation::INTERPRETED_FRAME);
code_offset = bailout_id.ToInt(); // Points to current bytecode. code_offset = bailout_id.ToInt(); // Points to current bytecode.
...@@ -1537,7 +1553,8 @@ void OptimizedFrame::GetFunctions(List<SharedFunctionInfo*>* functions) const { ...@@ -1537,7 +1553,8 @@ void OptimizedFrame::GetFunctions(List<SharedFunctionInfo*>* functions) const {
while (jsframe_count != 0) { while (jsframe_count != 0) {
opcode = static_cast<Translation::Opcode>(it.Next()); opcode = static_cast<Translation::Opcode>(it.Next());
if (opcode == Translation::JS_FRAME || 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. it.Next(); // Skip bailout id.
jsframe_count--; jsframe_count--;
......
...@@ -98,23 +98,25 @@ class StackHandler BASE_EMBEDDED { ...@@ -98,23 +98,25 @@ class StackHandler BASE_EMBEDDED {
DISALLOW_IMPLICIT_CONSTRUCTORS(StackHandler); DISALLOW_IMPLICIT_CONSTRUCTORS(StackHandler);
}; };
#define STACK_FRAME_TYPE_LIST(V) \ #define STACK_FRAME_TYPE_LIST(V) \
V(ENTRY, EntryFrame) \ V(ENTRY, EntryFrame) \
V(ENTRY_CONSTRUCT, EntryConstructFrame) \ V(ENTRY_CONSTRUCT, EntryConstructFrame) \
V(EXIT, ExitFrame) \ V(EXIT, ExitFrame) \
V(JAVA_SCRIPT, JavaScriptFrame) \ V(JAVA_SCRIPT, JavaScriptFrame) \
V(OPTIMIZED, OptimizedFrame) \ V(OPTIMIZED, OptimizedFrame) \
V(WASM_COMPILED, WasmCompiledFrame) \ V(WASM_COMPILED, WasmCompiledFrame) \
V(WASM_TO_JS, WasmToJsFrame) \ V(WASM_TO_JS, WasmToJsFrame) \
V(JS_TO_WASM, JsToWasmFrame) \ V(JS_TO_WASM, JsToWasmFrame) \
V(WASM_INTERPRETER_ENTRY, WasmInterpreterEntryFrame) \ V(WASM_INTERPRETER_ENTRY, WasmInterpreterEntryFrame) \
V(INTERPRETED, InterpretedFrame) \ V(INTERPRETED, InterpretedFrame) \
V(STUB, StubFrame) \ V(STUB, StubFrame) \
V(STUB_FAILURE_TRAMPOLINE, StubFailureTrampolineFrame) \ V(STUB_FAILURE_TRAMPOLINE, StubFailureTrampolineFrame) \
V(INTERNAL, InternalFrame) \ V(BUILTIN_CONTINUATION, BuiltinContinuationFrame) \
V(CONSTRUCT, ConstructFrame) \ V(JAVA_SCRIPT_BUILTIN_CONTINUATION, JavaScriptBuiltinContinuationFrame) \
V(ARGUMENTS_ADAPTOR, ArgumentsAdaptorFrame) \ V(INTERNAL, InternalFrame) \
V(BUILTIN, BuiltinFrame) \ V(CONSTRUCT, ConstructFrame) \
V(ARGUMENTS_ADAPTOR, ArgumentsAdaptorFrame) \
V(BUILTIN, BuiltinFrame) \
V(BUILTIN_EXIT, BuiltinExitFrame) V(BUILTIN_EXIT, BuiltinExitFrame)
// Every pointer in a frame has a slot id. On 32-bit platforms, doubles consume // Every pointer in a frame has a slot id. On 32-bit platforms, doubles consume
...@@ -359,6 +361,15 @@ class ConstructFrameConstants : public TypedFrameConstants { ...@@ -359,6 +361,15 @@ class ConstructFrameConstants : public TypedFrameConstants {
DEFINE_TYPED_FRAME_SIZES(4); 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 { class StubFailureTrampolineFrameConstants : public InternalFrameConstants {
public: public:
static const int kArgumentsArgumentsOffset = static const int kArgumentsArgumentsOffset =
...@@ -525,6 +536,12 @@ class StackFrame BASE_EMBEDDED { ...@@ -525,6 +536,12 @@ class StackFrame BASE_EMBEDDED {
bool is_stub_failure_trampoline() const { bool is_stub_failure_trampoline() const {
return type() == STUB_FAILURE_TRAMPOLINE; 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_construct() const { return type() == CONSTRUCT; }
bool is_builtin_exit() const { return type() == BUILTIN_EXIT; } bool is_builtin_exit() const { return type() == BUILTIN_EXIT; }
virtual bool is_standard() const { return false; } virtual bool is_standard() const { return false; }
...@@ -532,7 +549,8 @@ class StackFrame BASE_EMBEDDED { ...@@ -532,7 +549,8 @@ class StackFrame BASE_EMBEDDED {
bool is_java_script() const { bool is_java_script() const {
Type type = this->type(); Type type = this->type();
return (type == JAVA_SCRIPT) || (type == OPTIMIZED) || return (type == JAVA_SCRIPT) || (type == OPTIMIZED) ||
(type == INTERPRETED) || (type == BUILTIN); (type == INTERPRETED) || (type == BUILTIN) ||
(type == JAVA_SCRIPT_BUILTIN_CONTINUATION);
} }
bool is_wasm() const { bool is_wasm() const {
Type type = this->type(); Type type = this->type();
...@@ -1460,6 +1478,40 @@ class ConstructFrame: public InternalFrame { ...@@ -1460,6 +1478,40 @@ class ConstructFrame: public InternalFrame {
friend class StackFrameIteratorBase; 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 { class StackFrameIteratorBase BASE_EMBEDDED {
public: public:
......
...@@ -496,6 +496,7 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object, ...@@ -496,6 +496,7 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
switch (frame->type()) { switch (frame->type()) {
case StackFrame::JAVA_SCRIPT: case StackFrame::JAVA_SCRIPT:
case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION:
case StackFrame::OPTIMIZED: case StackFrame::OPTIMIZED:
case StackFrame::INTERPRETED: case StackFrame::INTERPRETED:
case StackFrame::BUILTIN: { case StackFrame::BUILTIN: {
......
...@@ -14395,6 +14395,18 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint( ...@@ -14395,6 +14395,18 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(
break; 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: { case Translation::COMPILED_STUB_FRAME: {
Code::Kind stub_kind = static_cast<Code::Kind>(iterator.Next()); Code::Kind stub_kind = static_cast<Code::Kind>(iterator.Next());
os << "{kind=" << stub_kind << "}"; os << "{kind=" << stub_kind << "}";
......
...@@ -922,6 +922,8 @@ class BailoutId { ...@@ -922,6 +922,8 @@ class BailoutId {
V8_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream&, BailoutId); V8_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream&, BailoutId);
private: private:
friend class Builtins;
static const int kNoneId = -1; static const int kNoneId = -1;
// Using 0 could disguise errors. // Using 0 could disguise errors.
...@@ -940,6 +942,11 @@ class BailoutId { ...@@ -940,6 +942,11 @@ class BailoutId {
// Every compiled stub starts with this id. // Every compiled stub starts with this id.
static const int kStubEntryId = 6; 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_; 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 = ( ...@@ -323,6 +323,8 @@ FRAME_MARKERS = (
"INTERPRETED", "INTERPRETED",
"STUB", "STUB",
"STUB_FAILURE_TRAMPOLINE", "STUB_FAILURE_TRAMPOLINE",
"BUILTIN_CONTINUATION",
"JAVA_SCRIPT_BUILTIN_CONTINUATION",
"INTERNAL", "INTERNAL",
"CONSTRUCT", "CONSTRUCT",
"ARGUMENTS_ADAPTOR", "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