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:
parent
82e3c3ee35
commit
e434d11ffe
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user