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}
This commit is contained in:
gsathya 2016-12-13 15:54:04 -08:00 committed by Commit bot
parent 3b1a09f56d
commit 9fc3c017e1
12 changed files with 195 additions and 33 deletions

View File

@ -1926,6 +1926,27 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
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(isolate->builtins()->builtin(Builtins::kPromiseResolveClosure),

View File

@ -867,5 +867,127 @@ void Builtins::Generate_ResolvePromise(compiler::CodeAssemblerState* state) {
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 v8

View File

@ -575,6 +575,8 @@ namespace internal {
TFJ(PromiseCreateAndSet, 2) \
TFJ(PerformPromiseThen, 4) \
TFJ(ResolvePromise, 2) \
TFS(PromiseHandleReject, BUILTIN, kNoExtraICState, PromiseHandleReject) \
TFJ(PromiseHandle, 3) \
\
/* Proxy */ \
CPP(ProxyConstructor) \

View File

@ -244,6 +244,7 @@ TFS_BUILTIN(OrdinaryHasInstance)
TFS_BUILTIN(ForInFilter)
TFS_BUILTIN(NewUnmappedArgumentsElements)
TFS_BUILTIN(NewRestParameterElements)
TFS_BUILTIN(PromiseHandleReject)
#undef TFS_BUILTIN

View File

@ -172,6 +172,7 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable ArrayPush(Isolate* isolate);
static Callable FunctionPrototypeBind(Isolate* isolate);
static Callable PromiseHandleReject(Isolate* isolate);
};
} // namespace internal

View File

@ -67,7 +67,9 @@ enum ContextLookupFlags {
V(PERFORM_PROMISE_THEN_INDEX, JSFunction, perform_promise_then) \
V(PROMISE_CREATE_AND_SET_INDEX, JSFunction, promise_create_and_set) \
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) \
V(ARRAY_CONCAT_INDEX, JSFunction, array_concat) \
@ -102,7 +104,6 @@ enum ContextLookupFlags {
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
V(PROMISE_HANDLE_INDEX, JSFunction, promise_handle) \
V(PROMISE_HAS_USER_DEFINED_REJECT_HANDLER_INDEX, JSFunction, \
promise_has_user_defined_reject_handler) \
V(PROMISE_DEBUG_GET_INFO_INDEX, JSFunction, promise_debug_get_info) \

View File

@ -125,6 +125,7 @@
V(preventExtensions_string, "preventExtensions") \
V(Promise_string, "Promise") \
V(PromiseResolveThenableJob_string, "PromiseResolveThenableJob") \
V(promise_string, "promise") \
V(proto_string, "__proto__") \
V(prototype_string, "prototype") \
V(Proxy_string, "Proxy") \
@ -132,6 +133,8 @@
V(RangeError_string, "RangeError") \
V(ReferenceError_string, "ReferenceError") \
V(RegExp_string, "RegExp") \
V(reject_string, "reject") \
V(resolve_string, "resolve") \
V(script_string, "script") \
V(second_string, "second") \
V(setPrototypeOf_string, "setPrototypeOf") \

View File

@ -93,7 +93,8 @@ class PlatformInterfaceDescriptor;
V(InterpreterPushArgsAndConstruct) \
V(InterpreterPushArgsAndConstructArray) \
V(InterpreterCEntry) \
V(ResumeGenerator)
V(ResumeGenerator) \
V(PromiseHandleReject)
class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
public:
@ -835,6 +836,13 @@ class ResumeGeneratorDescriptor final : public 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
#undef DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE

View File

@ -1342,10 +1342,18 @@ HandlerTable::CatchPrediction PredictException(JavaScriptFrame* frame) {
frame->Summarize(&summaries);
for (const FrameSummary& summary : summaries) {
Handle<AbstractCode> code = summary.abstract_code();
if (code->IsCode() && code->kind() == AbstractCode::BUILTIN &&
code->GetCode()->is_promise_rejection()) {
return HandlerTable::PROMISE;
if (code->IsCode() && code->kind() == AbstractCode::BUILTIN) {
if (code->GetCode()->is_promise_rejection()) {
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) {
DCHECK(summary.function()->shared()->asm_function());
// asm code cannot contain try-catch.

View File

@ -39,31 +39,6 @@ utils.Import(function(from) {
// 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) {
var id, name, instrumenting = DEBUG_IS_ACTIVE;
@ -394,7 +369,6 @@ utils.InstallGetter(GlobalPromise, speciesSymbol, PromiseSpecies);
"promise_reject", DoRejectPromise,
// TODO(gsathya): Remove this once we update the promise builtin.
"promise_internal_reject", RejectPromise,
"promise_handle", PromiseHandle,
"promise_debug_get_info", PromiseDebugGetInfo,
"new_promise_capability", NewPromiseCapability,
"internal_promise_capability", CreateInternalPromiseCapability,

View File

@ -5145,6 +5145,19 @@ inline void Code::set_is_promise_rejection(bool value) {
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() {
DCHECK_EQ(FUNCTION, kind());
unsigned flags = READ_UINT32_FIELD(this, kFullCodeFlags);

View File

@ -5579,6 +5579,11 @@ class Code: public HeapObject {
inline bool is_promise_rejection();
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.
inline Address constant_pool();
@ -5856,9 +5861,10 @@ class Code: public HeapObject {
// Could be moved to overlap previous bits when we need more space.
static const int kIsConstructStub = kCanHaveWeakObjects + 1;
static const int kIsPromiseRejection = kIsConstructStub + 1;
static const int kIsExceptionCaught = kIsPromiseRejection + 1;
STATIC_ASSERT(kStackSlotsFirstBit + kStackSlotsBitCount <= 32);
STATIC_ASSERT(kIsPromiseRejection + 1 <= 32);
STATIC_ASSERT(kIsExceptionCaught + 1 <= 32);
class StackSlotsField: public BitField<int,
kStackSlotsFirstBit, kStackSlotsBitCount> {}; // NOLINT
@ -5872,6 +5878,8 @@ class Code: public HeapObject {
}; // NOLINT
class IsPromiseRejectionField
: public BitField<bool, kIsPromiseRejection, 1> {}; // NOLINT
class IsExceptionCaughtField : public BitField<bool, kIsExceptionCaught, 1> {
}; // NOLINT
// KindSpecificFlags2 layout (ALL)
static const int kIsCrankshaftedBit = 0;