Reland "[builtins] don't inline calls for common Promise ops in async builtins"

InternalResolvePromise, InternalPromiseReject and
InternalPerformPromiseThen generate quite a lot of code.

This change adds 3 new TF stubs which inline calls to these builtins.
These stubs are invoked rather than inlining those operations listed
above directly. This is done for Async Iteration builtins, as well as
Async Function builtins. Promise builtins are left as they were, and
continue to inline these calls.

This results in a roughly 99kb reduction in snapshot_blob.bin on an x64
release build.

BUG=v8:5855
R=gsathya@chromium.org, jgruber@chromium.org

Change-Id: I83e2f096782db685fe316dd071980cd8d696fe53
Reviewed-on: https://chromium-review.googlesource.com/469927
Reviewed-by: Franziska Hinkelmann <franzih@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44483}
This commit is contained in:
Caitlin Potter 2017-04-06 12:03:26 -04:00
parent 82e3c3ee35
commit e434d11ffe
9 changed files with 142 additions and 21 deletions

View File

@ -27,7 +27,7 @@ Node* AsyncBuiltinsAssembler::Await(
Node* const wrapped_value = AllocateAndInitJSPromise(context);
// Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
InternalResolvePromise(context, wrapped_value, value);
CallBuiltin(Builtins::kResolveNativePromise, context, wrapped_value, value);
Node* const native_context = LoadNativeContext(context);
@ -88,9 +88,8 @@ Node* AsyncBuiltinsAssembler::Await(
Goto(&do_perform_promise_then);
BIND(&do_perform_promise_then);
InternalPerformPromiseThen(context, wrapped_value, on_resolve, on_reject,
throwaway_promise, UndefinedConstant(),
UndefinedConstant());
CallBuiltin(Builtins::kPerformNativePromiseThen, context, wrapped_value,
on_resolve, on_reject, throwaway_promise);
return wrapped_value;
}

View File

@ -184,7 +184,8 @@ void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorEnqueue(
MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context,
CStringConstant(method_name), generator);
InternalPromiseReject(context, promise, error, true);
CallBuiltin(Builtins::kRejectNativePromise, context, promise, error,
TrueConstant());
Return(promise);
}
}
@ -541,18 +542,15 @@ TF_BUILTIN(AsyncGeneratorResolve, AsyncGeneratorBuiltinsAssembler) {
Node* const promise = LoadPromiseFromAsyncGeneratorRequest(next);
Node* const wrapper = AllocateAndInitJSPromise(context);
InternalResolvePromise(context, wrapper, value);
CallBuiltin(Builtins::kResolveNativePromise, context, wrapper, value);
Node* const on_fulfilled =
CreateUnwrapClosure(LoadNativeContext(context), done);
Node* const undefined = UndefinedConstant();
InternalPerformPromiseThen(context, wrapper, on_fulfilled, undefined, promise,
undefined, undefined);
// Per spec, AsyncGeneratorResolve() returns undefined. However, for the
// benefit of %TraceExit(), return the Promise.
Return(promise);
Return(CallBuiltin(Builtins::kPerformNativePromiseThen, context, wrapper,
on_fulfilled, UndefinedConstant(), promise));
}
TF_BUILTIN(AsyncGeneratorReject, AsyncGeneratorBuiltinsAssembler) {
@ -564,8 +562,8 @@ TF_BUILTIN(AsyncGeneratorReject, AsyncGeneratorBuiltinsAssembler) {
Node* const next = TakeFirstAsyncGeneratorRequestFromQueue(generator);
Node* const promise = LoadPromiseFromAsyncGeneratorRequest(next);
InternalPromiseReject(context, promise, value, true);
Return(UndefinedConstant());
Return(CallBuiltin(Builtins::kRejectNativePromise, context, promise, value,
TrueConstant()));
}
} // namespace internal

View File

@ -119,7 +119,7 @@ void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
// Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, «
// throwValue »).
InternalResolvePromise(context, wrapper, value);
CallBuiltin(Builtins::kResolveNativePromise, context, wrapper, value);
// Let onFulfilled be a new built-in function object as defined in
// Async Iterator Value Unwrap Functions.
@ -128,16 +128,14 @@ void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
// Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]],
// onFulfilled, undefined, promiseCapability).
Node* const undefined = UndefinedConstant();
InternalPerformPromiseThen(context, wrapper, on_fulfilled, undefined, promise,
undefined, undefined);
Return(promise);
Return(CallBuiltin(Builtins::kPerformNativePromiseThen, context, wrapper,
on_fulfilled, UndefinedConstant(), promise));
BIND(&reject_promise);
{
Node* const exception = var_exception.value();
InternalPromiseReject(context, promise, exception, TrueConstant());
CallBuiltin(Builtins::kRejectNativePromise, context, promise, exception,
TrueConstant());
Return(promise);
}
}

View File

@ -224,6 +224,10 @@ namespace internal {
TFH(StoreIC_Uninitialized, BUILTIN, kNoExtraICState, StoreWithVector) \
TFH(StoreICStrict_Uninitialized, BUILTIN, kNoExtraICState, StoreWithVector) \
\
TFS(ResolveNativePromise, ResolveNativePromise, 1) \
TFS(RejectNativePromise, RejectNativePromise, 1) \
TFS(PerformNativePromiseThen, PerformNativePromiseThen, 1) \
\
/* Built-in functions for Javascript */ \
/* Special internal builtins */ \
CPP(EmptyFunction) \
@ -985,10 +989,13 @@ namespace internal {
V(AsyncGeneratorResolve) \
V(AsyncGeneratorAwaitCaught) \
V(AsyncGeneratorAwaitUncaught) \
V(PerformNativePromiseThen) \
V(PromiseConstructor) \
V(PromiseHandle) \
V(PromiseResolve) \
V(PromiseResolveClosure) \
V(RejectNativePromise) \
V(ResolveNativePromise) \
V(ResolvePromise)
#define BUILTIN_EXCEPTION_CAUGHT_PREDICTION_LIST(V) V(PromiseHandleReject)

View File

@ -1762,5 +1762,42 @@ TF_BUILTIN(PromiseFinally, PromiseBuiltinsAssembler) {
}
}
TF_BUILTIN(ResolveNativePromise, PromiseBuiltinsAssembler) {
Node* const promise = Parameter(Descriptor::kPromise);
Node* const value = Parameter(Descriptor::kValue);
Node* const context = Parameter(Descriptor::kContext);
CSA_ASSERT(this, HasInstanceType(promise, JS_PROMISE_TYPE));
InternalResolvePromise(context, promise, value);
Return(UndefinedConstant());
}
TF_BUILTIN(RejectNativePromise, PromiseBuiltinsAssembler) {
Node* const promise = Parameter(Descriptor::kPromise);
Node* const value = Parameter(Descriptor::kValue);
Node* const debug_event = Parameter(Descriptor::kDebugEvent);
Node* const context = Parameter(Descriptor::kContext);
CSA_ASSERT(this, HasInstanceType(promise, JS_PROMISE_TYPE));
CSA_ASSERT(this, IsBoolean(debug_event));
InternalPromiseReject(context, promise, value, debug_event);
Return(UndefinedConstant());
}
TF_BUILTIN(PerformNativePromiseThen, PromiseBuiltinsAssembler) {
Node* const promise = Parameter(Descriptor::kPromise);
Node* const resolve_reaction = Parameter(Descriptor::kResolveReaction);
Node* const reject_reaction = Parameter(Descriptor::kRejectReaction);
Node* const result_promise = Parameter(Descriptor::kResultPromise);
Node* const context = Parameter(Descriptor::kContext);
CSA_ASSERT(this, HasInstanceType(result_promise, JS_PROMISE_TYPE));
InternalPerformPromiseThen(context, promise, resolve_reaction,
reject_reaction, result_promise,
UndefinedConstant(), UndefinedConstant());
Return(result_promise);
}
} // namespace internal
} // namespace v8

View File

@ -916,6 +916,15 @@ int StubFrame::GetNumberOfIncomingArguments() const {
return 0;
}
int StubFrame::LookupExceptionHandlerInTable(int* stack_slots) {
Code* code = LookupCode();
DCHECK(code->is_turbofanned());
DCHECK_EQ(code->kind(), Code::BUILTIN);
HandlerTable* table = HandlerTable::cast(code->handler_table());
int pc_offset = static_cast<int>(pc() - code->entry());
*stack_slots = code->stack_slots();
return table->LookupReturn(pc_offset);
}
void OptimizedFrame::Iterate(ObjectVisitor* v) const {
IterateCompiledFrame(v);

View File

@ -1130,6 +1130,12 @@ class StubFrame : public StandardFrame {
// Determine the code for the frame.
Code* unchecked_code() const override;
// Lookup exception handler for current {pc}, returns -1 if none found. Only
// TurboFan stub frames are supported. Also returns data associated with the
// handler site:
// - TurboFan stub: Data is the stack slot count of the entire frame.
int LookupExceptionHandlerInTable(int* data);
protected:
inline explicit StubFrame(StackFrameIteratorBase* iterator);

View File

@ -101,7 +101,10 @@ class PlatformInterfaceDescriptor;
V(AsyncGeneratorResolve) \
V(AsyncGeneratorReject) \
V(AsyncGeneratorResumeNext) \
V(WasmRuntimeCall)
V(WasmRuntimeCall) \
V(ResolveNativePromise) \
V(RejectNativePromise) \
V(PerformNativePromiseThen)
class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
public:
@ -1008,6 +1011,28 @@ class AsyncGeneratorResumeNextDescriptor final
CallInterfaceDescriptor, kParameterCount)
};
class ResolveNativePromiseDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kPromise, kValue)
DECLARE_DEFAULT_DESCRIPTOR(ResolveNativePromiseDescriptor,
CallInterfaceDescriptor, kParameterCount)
};
class RejectNativePromiseDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kPromise, kValue, kDebugEvent)
DECLARE_DEFAULT_DESCRIPTOR(RejectNativePromiseDescriptor,
CallInterfaceDescriptor, kParameterCount)
};
class PerformNativePromiseThenDescriptor final
: public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kPromise, kResolveReaction, kRejectReaction, kResultPromise)
DECLARE_DEFAULT_DESCRIPTOR(PerformNativePromiseThenDescriptor,
CallInterfaceDescriptor, kParameterCount)
};
class WasmRuntimeCallDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_EMPTY_PARAMETERS()

View File

@ -1241,6 +1241,29 @@ Object* Isolate::UnwindAndFindHandler() {
return FoundHandler(nullptr, code, offset, return_sp, frame->fp());
}
case StackFrame::STUB: {
// Some stubs are able to handle exceptions.
if (!catchable_by_js) break;
StubFrame* stub_frame = static_cast<StubFrame*>(frame);
Code* code = stub_frame->LookupCode();
if (!code->IsCode() || code->kind() != Code::BUILTIN ||
!code->handler_table()->length() || !code->is_turbofanned()) {
break;
}
int stack_slots = 0; // Will contain stack slot count of frame.
int offset = stub_frame->LookupExceptionHandlerInTable(&stack_slots);
if (offset < 0) break;
// Compute the stack pointer from the frame pointer. This ensures
// that argument slots on the stack are dropped as returning would.
Address return_sp = frame->fp() +
StandardFrameConstants::kFixedFrameSizeAboveFp -
stack_slots * kPointerSize;
return FoundHandler(nullptr, code, offset, return_sp, frame->fp());
}
case StackFrame::INTERPRETED: {
// For interpreted frame we perform a range lookup in the handler table.
if (!catchable_by_js) break;
@ -1401,6 +1424,25 @@ Isolate::CatchType Isolate::PredictExceptionCatcher() {
}
} break;
case StackFrame::STUB: {
Handle<Code> code(frame->LookupCode());
if (code->kind() == Code::BUILTIN && code->is_turbofanned() &&
code->handler_table()->length()) {
if (code->is_promise_rejection()) {
return CAUGHT_BY_PROMISE;
}
// This the exception throw in PromiseHandle which doesn't
// cause a promise rejection.
if (code->is_exception_caught()) {
return CAUGHT_BY_JAVASCRIPT;
}
// The built-in must be marked with an exception prediction.
UNREACHABLE();
}
} break;
default:
// All other types can not handle exception.
break;