Commit 9fc3c017 authored by gsathya's avatar gsathya Committed by Commit bot

PromiseHandle port to TF

Splits PromiseHandle into two TF builtins to account for catch
prediction. An exception in PromiseHandleReject builtin results in a
"caught" prediction whereas an expception in PromiseHandle results in a
"promise rejection" prediction.

An extra is_exception_caught bit is added to Code to mark this catch
prediction behavior.

BUG=v8:5343

Review-Url: https://codereview.chromium.org/2572623002
Cr-Commit-Position: refs/heads/master@{#41683}
parent 3b1a09f5
...@@ -1926,6 +1926,27 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -1926,6 +1926,27 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Context::PROMISE_RESOLVE_INDEX); Context::PROMISE_RESOLVE_INDEX);
} }
{ // Internal: PromiseHandle
Handle<JSFunction> function = SimpleCreateFunction(
isolate, factory->empty_string(), Builtins::kPromiseHandle, 3, true);
InstallWithIntrinsicDefaultProto(isolate, function,
Context::PROMISE_HANDLE_INDEX);
// Set up catch prediction
Handle<Code> promise_handle = isolate->builtins()->PromiseHandle();
promise_handle->set_is_promise_rejection(true);
}
{ // Internal: PromiseHandleReject
Handle<JSFunction> function =
SimpleCreateFunction(isolate, factory->empty_string(),
Builtins::kPromiseHandleReject, 3, false);
InstallWithIntrinsicDefaultProto(isolate, function,
Context::PROMISE_HANDLE_REJECT_INDEX);
// Set up catch prediction
Handle<Code> promise_handle = isolate->builtins()->PromiseHandleReject();
promise_handle->set_is_exception_caught(true);
}
{ {
Handle<Code> code = Handle<Code> code =
handle(isolate->builtins()->builtin(Builtins::kPromiseResolveClosure), handle(isolate->builtins()->builtin(Builtins::kPromiseResolveClosure),
......
...@@ -867,5 +867,127 @@ void Builtins::Generate_ResolvePromise(compiler::CodeAssemblerState* state) { ...@@ -867,5 +867,127 @@ void Builtins::Generate_ResolvePromise(compiler::CodeAssemblerState* state) {
a.Return(a.UndefinedConstant()); a.Return(a.UndefinedConstant());
} }
void Builtins::Generate_PromiseHandleReject(
compiler::CodeAssemblerState* state) {
CodeStubAssembler a(state);
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
typedef PromiseHandleRejectDescriptor Descriptor;
Node* const promise = a.Parameter(Descriptor::kPromise);
Node* const on_reject = a.Parameter(Descriptor::kOnReject);
Node* const exception = a.Parameter(Descriptor::kException);
Node* const context = a.Parameter(Descriptor::kContext);
Isolate* isolate = a.isolate();
Callable call_callable = CodeFactory::Call(isolate);
Variable var_unused(&a, MachineRepresentation::kTagged);
Label if_internalhandler(&a), if_customhandler(&a, Label::kDeferred);
a.Branch(a.IsUndefined(on_reject), &if_internalhandler, &if_customhandler);
a.Bind(&if_internalhandler);
{
a.CallRuntime(Runtime::kPromiseReject, context, promise, exception,
a.FalseConstant());
a.Return(a.UndefinedConstant());
}
a.Bind(&if_customhandler);
{
a.CallJS(call_callable, context, on_reject, a.UndefinedConstant(),
exception);
a.Return(a.UndefinedConstant());
}
}
void Builtins::Generate_PromiseHandle(compiler::CodeAssemblerState* state) {
CodeStubAssembler a(state);
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
Node* const value = a.Parameter(1);
Node* const handler = a.Parameter(2);
Node* const deferred = a.Parameter(3);
Node* const context = a.Parameter(6);
Isolate* isolate = a.isolate();
// Get promise from deferred
// TODO(gsathya): Remove this lookup by getting rid of the deferred object.
Callable getproperty_callable = CodeFactory::GetProperty(isolate);
Node* const key = a.HeapConstant(isolate->factory()->promise_string());
Node* const promise =
a.CallStub(getproperty_callable, context, deferred, key);
Variable var_reason(&a, MachineRepresentation::kTagged);
Node* const is_debug_active = a.IsDebugActive();
Label run_handler(&a), if_rejectpromise(&a), debug_push(&a, Label::kDeferred),
debug_pop(&a, Label::kDeferred);
a.Branch(is_debug_active, &debug_push, &run_handler);
a.Bind(&debug_push);
{
a.CallRuntime(Runtime::kDebugPushPromise, context, promise);
a.Goto(&run_handler);
}
a.Bind(&run_handler);
{
Callable call_callable = CodeFactory::Call(isolate);
Node* const result =
a.CallJS(call_callable, context, handler, a.UndefinedConstant(), value);
a.GotoIfException(result, &if_rejectpromise, &var_reason);
// TODO(gsathya): Remove this lookup by getting rid of the deferred object.
Node* const key = a.HeapConstant(isolate->factory()->resolve_string());
Node* const on_resolve =
a.CallStub(getproperty_callable, context, deferred, key);
Label if_internalhandler(&a), if_customhandler(&a, Label::kDeferred);
a.Branch(a.IsUndefined(on_resolve), &if_internalhandler, &if_customhandler);
a.Bind(&if_internalhandler);
InternalResolvePromise(&a, context, promise, result, &debug_pop);
a.Bind(&if_customhandler);
{
Node* const maybe_exception = a.CallJS(call_callable, context, on_resolve,
a.UndefinedConstant(), result);
a.GotoIfException(maybe_exception, &if_rejectpromise, &var_reason);
a.Goto(&debug_pop);
}
}
a.Bind(&if_rejectpromise);
{
// TODO(gsathya): Remove this lookup by getting rid of the deferred object.
Node* const key = a.HeapConstant(isolate->factory()->reject_string());
Node* const on_reject =
a.CallStub(getproperty_callable, context, deferred, key);
Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate);
a.CallStub(promise_handle_reject, context, promise, on_reject,
var_reason.value());
a.Goto(&debug_pop);
}
a.Bind(&debug_pop);
{
Label out(&a);
a.GotoUnless(is_debug_active, &out);
a.CallRuntime(Runtime::kDebugPopPromise, context);
a.Goto(&out);
a.Bind(&out);
a.Return(a.UndefinedConstant());
}
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -575,6 +575,8 @@ namespace internal { ...@@ -575,6 +575,8 @@ namespace internal {
TFJ(PromiseCreateAndSet, 2) \ TFJ(PromiseCreateAndSet, 2) \
TFJ(PerformPromiseThen, 4) \ TFJ(PerformPromiseThen, 4) \
TFJ(ResolvePromise, 2) \ TFJ(ResolvePromise, 2) \
TFS(PromiseHandleReject, BUILTIN, kNoExtraICState, PromiseHandleReject) \
TFJ(PromiseHandle, 3) \
\ \
/* Proxy */ \ /* Proxy */ \
CPP(ProxyConstructor) \ CPP(ProxyConstructor) \
......
...@@ -244,6 +244,7 @@ TFS_BUILTIN(OrdinaryHasInstance) ...@@ -244,6 +244,7 @@ TFS_BUILTIN(OrdinaryHasInstance)
TFS_BUILTIN(ForInFilter) TFS_BUILTIN(ForInFilter)
TFS_BUILTIN(NewUnmappedArgumentsElements) TFS_BUILTIN(NewUnmappedArgumentsElements)
TFS_BUILTIN(NewRestParameterElements) TFS_BUILTIN(NewRestParameterElements)
TFS_BUILTIN(PromiseHandleReject)
#undef TFS_BUILTIN #undef TFS_BUILTIN
......
...@@ -172,6 +172,7 @@ class V8_EXPORT_PRIVATE CodeFactory final { ...@@ -172,6 +172,7 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable ArrayPush(Isolate* isolate); static Callable ArrayPush(Isolate* isolate);
static Callable FunctionPrototypeBind(Isolate* isolate); static Callable FunctionPrototypeBind(Isolate* isolate);
static Callable PromiseHandleReject(Isolate* isolate);
}; };
} // namespace internal } // namespace internal
......
...@@ -67,7 +67,9 @@ enum ContextLookupFlags { ...@@ -67,7 +67,9 @@ enum ContextLookupFlags {
V(PERFORM_PROMISE_THEN_INDEX, JSFunction, perform_promise_then) \ V(PERFORM_PROMISE_THEN_INDEX, JSFunction, perform_promise_then) \
V(PROMISE_CREATE_AND_SET_INDEX, JSFunction, promise_create_and_set) \ V(PROMISE_CREATE_AND_SET_INDEX, JSFunction, promise_create_and_set) \
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \ V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
V(PROMISE_THEN_INDEX, JSFunction, promise_then) V(PROMISE_THEN_INDEX, JSFunction, promise_then) \
V(PROMISE_HANDLE_INDEX, JSFunction, promise_handle) \
V(PROMISE_HANDLE_REJECT_INDEX, JSFunction, promise_handle_reject)
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \ #define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
V(ARRAY_CONCAT_INDEX, JSFunction, array_concat) \ V(ARRAY_CONCAT_INDEX, JSFunction, array_concat) \
...@@ -102,7 +104,6 @@ enum ContextLookupFlags { ...@@ -102,7 +104,6 @@ enum ContextLookupFlags {
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \ V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \ V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \ V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
V(PROMISE_HANDLE_INDEX, JSFunction, promise_handle) \
V(PROMISE_HAS_USER_DEFINED_REJECT_HANDLER_INDEX, JSFunction, \ V(PROMISE_HAS_USER_DEFINED_REJECT_HANDLER_INDEX, JSFunction, \
promise_has_user_defined_reject_handler) \ promise_has_user_defined_reject_handler) \
V(PROMISE_DEBUG_GET_INFO_INDEX, JSFunction, promise_debug_get_info) \ V(PROMISE_DEBUG_GET_INFO_INDEX, JSFunction, promise_debug_get_info) \
......
...@@ -125,6 +125,7 @@ ...@@ -125,6 +125,7 @@
V(preventExtensions_string, "preventExtensions") \ V(preventExtensions_string, "preventExtensions") \
V(Promise_string, "Promise") \ V(Promise_string, "Promise") \
V(PromiseResolveThenableJob_string, "PromiseResolveThenableJob") \ V(PromiseResolveThenableJob_string, "PromiseResolveThenableJob") \
V(promise_string, "promise") \
V(proto_string, "__proto__") \ V(proto_string, "__proto__") \
V(prototype_string, "prototype") \ V(prototype_string, "prototype") \
V(Proxy_string, "Proxy") \ V(Proxy_string, "Proxy") \
...@@ -132,6 +133,8 @@ ...@@ -132,6 +133,8 @@
V(RangeError_string, "RangeError") \ V(RangeError_string, "RangeError") \
V(ReferenceError_string, "ReferenceError") \ V(ReferenceError_string, "ReferenceError") \
V(RegExp_string, "RegExp") \ V(RegExp_string, "RegExp") \
V(reject_string, "reject") \
V(resolve_string, "resolve") \
V(script_string, "script") \ V(script_string, "script") \
V(second_string, "second") \ V(second_string, "second") \
V(setPrototypeOf_string, "setPrototypeOf") \ V(setPrototypeOf_string, "setPrototypeOf") \
......
...@@ -93,7 +93,8 @@ class PlatformInterfaceDescriptor; ...@@ -93,7 +93,8 @@ class PlatformInterfaceDescriptor;
V(InterpreterPushArgsAndConstruct) \ V(InterpreterPushArgsAndConstruct) \
V(InterpreterPushArgsAndConstructArray) \ V(InterpreterPushArgsAndConstructArray) \
V(InterpreterCEntry) \ V(InterpreterCEntry) \
V(ResumeGenerator) V(ResumeGenerator) \
V(PromiseHandleReject)
class V8_EXPORT_PRIVATE CallInterfaceDescriptorData { class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
public: public:
...@@ -835,6 +836,13 @@ class ResumeGeneratorDescriptor final : public CallInterfaceDescriptor { ...@@ -835,6 +836,13 @@ class ResumeGeneratorDescriptor final : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR(ResumeGeneratorDescriptor, CallInterfaceDescriptor) DECLARE_DESCRIPTOR(ResumeGeneratorDescriptor, CallInterfaceDescriptor)
}; };
class PromiseHandleRejectDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kPromise, kOnReject, kException)
DECLARE_DEFAULT_DESCRIPTOR(PromiseHandleRejectDescriptor,
CallInterfaceDescriptor, kParameterCount)
};
#undef DECLARE_DESCRIPTOR_WITH_BASE #undef DECLARE_DESCRIPTOR_WITH_BASE
#undef DECLARE_DESCRIPTOR #undef DECLARE_DESCRIPTOR
#undef DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE #undef DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE
......
...@@ -1342,10 +1342,18 @@ HandlerTable::CatchPrediction PredictException(JavaScriptFrame* frame) { ...@@ -1342,10 +1342,18 @@ HandlerTable::CatchPrediction PredictException(JavaScriptFrame* frame) {
frame->Summarize(&summaries); frame->Summarize(&summaries);
for (const FrameSummary& summary : summaries) { for (const FrameSummary& summary : summaries) {
Handle<AbstractCode> code = summary.abstract_code(); Handle<AbstractCode> code = summary.abstract_code();
if (code->IsCode() && code->kind() == AbstractCode::BUILTIN && if (code->IsCode() && code->kind() == AbstractCode::BUILTIN) {
code->GetCode()->is_promise_rejection()) { if (code->GetCode()->is_promise_rejection()) {
return HandlerTable::PROMISE; return HandlerTable::PROMISE;
}
// This the exception throw in PromiseHandle which doesn't
// cause a promise rejection.
if (code->GetCode()->is_exception_caught()) {
return HandlerTable::CAUGHT;
}
} }
if (code->kind() == AbstractCode::OPTIMIZED_FUNCTION) { if (code->kind() == AbstractCode::OPTIMIZED_FUNCTION) {
DCHECK(summary.function()->shared()->asm_function()); DCHECK(summary.function()->shared()->asm_function());
// asm code cannot contain try-catch. // asm code cannot contain try-catch.
......
...@@ -39,31 +39,6 @@ utils.Import(function(from) { ...@@ -39,31 +39,6 @@ utils.Import(function(from) {
// Core functionality. // Core functionality.
function PromiseHandle(value, handler, deferred) {
var debug_is_active = DEBUG_IS_ACTIVE;
try {
if (debug_is_active) %DebugPushPromise(deferred.promise);
var result = handler(value);
if (IS_UNDEFINED(deferred.resolve)) {
%promise_resolve(deferred.promise, result);
} else {
%_Call(deferred.resolve, UNDEFINED, result);
}
} %catch (exception) { // Natives syntax to mark this catch block.
try {
if (IS_UNDEFINED(deferred.reject)) {
// Pass false for debugEvent so .then chaining or throwaway promises
// in async functions do not trigger redundant ExceptionEvents.
%PromiseReject(deferred.promise, exception, false);
} else {
%_Call(deferred.reject, UNDEFINED, exception);
}
} catch (e) { }
} finally {
if (debug_is_active) %DebugPopPromise();
}
}
function PromiseDebugGetInfo(deferreds, status) { function PromiseDebugGetInfo(deferreds, status) {
var id, name, instrumenting = DEBUG_IS_ACTIVE; var id, name, instrumenting = DEBUG_IS_ACTIVE;
...@@ -394,7 +369,6 @@ utils.InstallGetter(GlobalPromise, speciesSymbol, PromiseSpecies); ...@@ -394,7 +369,6 @@ utils.InstallGetter(GlobalPromise, speciesSymbol, PromiseSpecies);
"promise_reject", DoRejectPromise, "promise_reject", DoRejectPromise,
// TODO(gsathya): Remove this once we update the promise builtin. // TODO(gsathya): Remove this once we update the promise builtin.
"promise_internal_reject", RejectPromise, "promise_internal_reject", RejectPromise,
"promise_handle", PromiseHandle,
"promise_debug_get_info", PromiseDebugGetInfo, "promise_debug_get_info", PromiseDebugGetInfo,
"new_promise_capability", NewPromiseCapability, "new_promise_capability", NewPromiseCapability,
"internal_promise_capability", CreateInternalPromiseCapability, "internal_promise_capability", CreateInternalPromiseCapability,
......
...@@ -5145,6 +5145,19 @@ inline void Code::set_is_promise_rejection(bool value) { ...@@ -5145,6 +5145,19 @@ inline void Code::set_is_promise_rejection(bool value) {
WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated); WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated);
} }
inline bool Code::is_exception_caught() {
DCHECK(kind() == BUILTIN);
return IsExceptionCaughtField::decode(
READ_UINT32_FIELD(this, kKindSpecificFlags1Offset));
}
inline void Code::set_is_exception_caught(bool value) {
DCHECK(kind() == BUILTIN);
int previous = READ_UINT32_FIELD(this, kKindSpecificFlags1Offset);
int updated = IsExceptionCaughtField::update(previous, value);
WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated);
}
bool Code::has_deoptimization_support() { bool Code::has_deoptimization_support() {
DCHECK_EQ(FUNCTION, kind()); DCHECK_EQ(FUNCTION, kind());
unsigned flags = READ_UINT32_FIELD(this, kFullCodeFlags); unsigned flags = READ_UINT32_FIELD(this, kFullCodeFlags);
......
...@@ -5579,6 +5579,11 @@ class Code: public HeapObject { ...@@ -5579,6 +5579,11 @@ class Code: public HeapObject {
inline bool is_promise_rejection(); inline bool is_promise_rejection();
inline void set_is_promise_rejection(bool flag); inline void set_is_promise_rejection(bool flag);
// [is_exception_caught]: For kind BUILTIN tells whether the exception
// thrown by the code will be caught internally.
inline bool is_exception_caught();
inline void set_is_exception_caught(bool flag);
// [constant_pool]: The constant pool for this function. // [constant_pool]: The constant pool for this function.
inline Address constant_pool(); inline Address constant_pool();
...@@ -5856,9 +5861,10 @@ class Code: public HeapObject { ...@@ -5856,9 +5861,10 @@ class Code: public HeapObject {
// Could be moved to overlap previous bits when we need more space. // Could be moved to overlap previous bits when we need more space.
static const int kIsConstructStub = kCanHaveWeakObjects + 1; static const int kIsConstructStub = kCanHaveWeakObjects + 1;
static const int kIsPromiseRejection = kIsConstructStub + 1; static const int kIsPromiseRejection = kIsConstructStub + 1;
static const int kIsExceptionCaught = kIsPromiseRejection + 1;
STATIC_ASSERT(kStackSlotsFirstBit + kStackSlotsBitCount <= 32); STATIC_ASSERT(kStackSlotsFirstBit + kStackSlotsBitCount <= 32);
STATIC_ASSERT(kIsPromiseRejection + 1 <= 32); STATIC_ASSERT(kIsExceptionCaught + 1 <= 32);
class StackSlotsField: public BitField<int, class StackSlotsField: public BitField<int,
kStackSlotsFirstBit, kStackSlotsBitCount> {}; // NOLINT kStackSlotsFirstBit, kStackSlotsBitCount> {}; // NOLINT
...@@ -5872,6 +5878,8 @@ class Code: public HeapObject { ...@@ -5872,6 +5878,8 @@ class Code: public HeapObject {
}; // NOLINT }; // NOLINT
class IsPromiseRejectionField class IsPromiseRejectionField
: public BitField<bool, kIsPromiseRejection, 1> {}; // NOLINT : public BitField<bool, kIsPromiseRejection, 1> {}; // NOLINT
class IsExceptionCaughtField : public BitField<bool, kIsExceptionCaught, 1> {
}; // NOLINT
// KindSpecificFlags2 layout (ALL) // KindSpecificFlags2 layout (ALL)
static const int kIsCrankshaftedBit = 0; static const int kIsCrankshaftedBit = 0;
......
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