Commit cc7e0b0e authored by gsathya's avatar gsathya Committed by Commit bot

[promises] Port CreateResolvingFunctions to TF

2% improvement on benchmarks over 5 runs.

BUG=v8:5343

Review-Url: https://codereview.chromium.org/2567033003
Cr-Commit-Position: refs/heads/master@{#41827}
parent 48a36c7d
......@@ -16,6 +16,52 @@ typedef compiler::Node Node;
typedef CodeStubAssembler::ParameterMode ParameterMode;
typedef compiler::CodeAssemblerState CodeAssemblerState;
Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext(
Node* promise, Node* debug_event, Node* native_context) {
Node* const context =
Allocate(FixedArray::SizeFor(PromiseUtils::kPromiseContextLength));
StoreMapNoWriteBarrier(context, Heap::kFunctionContextMapRootIndex);
StoreObjectFieldNoWriteBarrier(
context, FixedArray::kLengthOffset,
SmiConstant(PromiseUtils::kPromiseContextLength));
Node* const empty_fn =
LoadContextElement(native_context, Context::CLOSURE_INDEX);
StoreContextElementNoWriteBarrier(context, Context::CLOSURE_INDEX, empty_fn);
StoreContextElementNoWriteBarrier(context, Context::PREVIOUS_INDEX,
UndefinedConstant());
StoreContextElementNoWriteBarrier(context, Context::EXTENSION_INDEX,
TheHoleConstant());
StoreContextElementNoWriteBarrier(context, Context::NATIVE_CONTEXT_INDEX,
native_context);
StoreContextElementNoWriteBarrier(context, PromiseUtils::kAlreadyVisitedSlot,
SmiConstant(0));
StoreContextElementNoWriteBarrier(context, PromiseUtils::kPromiseSlot,
promise);
StoreContextElementNoWriteBarrier(context, PromiseUtils::kDebugEventSlot,
debug_event);
return context;
}
std::pair<Node*, Node*>
PromiseBuiltinsAssembler::CreatePromiseResolvingFunctions(
Node* promise, Node* debug_event, Node* native_context) {
Node* const promise_context = CreatePromiseResolvingFunctionsContext(
promise, debug_event, native_context);
Node* const map = LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
Node* const resolve_info =
LoadContextElement(native_context, Context::PROMISE_RESOLVE_SHARED_FUN);
Node* const resolve =
AllocateFunctionWithMapAndContext(map, resolve_info, promise_context);
Node* const reject_info =
LoadContextElement(native_context, Context::PROMISE_REJECT_SHARED_FUN);
Node* const reject =
AllocateFunctionWithMapAndContext(map, reject_info, promise_context);
return std::make_pair(resolve, reject);
}
Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver(
Node* context, Node* value, MessageTemplate::Template msg_template) {
Label out(this), throw_exception(this, Label::kDeferred);
......@@ -513,23 +559,33 @@ BUILTIN(PromiseRejectClosure) {
// ES#sec-createresolvingfunctions
// CreateResolvingFunctions ( promise )
BUILTIN(CreateResolvingFunctions) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
Handle<JSObject> promise = args.at<JSObject>(1);
Handle<Object> debug_event = args.at<Object>(2);
Handle<JSFunction> resolve, reject;
TF_BUILTIN(CreateResolvingFunctions, PromiseBuiltinsAssembler) {
Node* const promise = Parameter(1);
Node* const debug_event = Parameter(2);
Node* const context = Parameter(5);
Node* const native_context = LoadNativeContext(context);
PromiseUtils::CreateResolvingFunctions(isolate, promise, debug_event,
&resolve, &reject);
Node* resolve = nullptr;
Node* reject = nullptr;
Handle<FixedArray> result = isolate->factory()->NewFixedArray(2);
result->set(0, *resolve);
result->set(1, *reject);
std::tie(resolve, reject) =
CreatePromiseResolvingFunctions(promise, debug_event, native_context);
return *isolate->factory()->NewJSArrayWithElements(result, FAST_ELEMENTS, 2,
NOT_TENURED);
Node* const kSize = IntPtrConstant(2);
const ElementsKind kind = FAST_ELEMENTS;
const WriteBarrierMode barrier_mode = SKIP_WRITE_BARRIER;
const ParameterMode parameter_mode = INTPTR_PARAMETERS;
Node* const arr = AllocateFixedArray(kind, kSize, parameter_mode);
StoreFixedArrayElement(arr, 0, resolve, barrier_mode);
StoreFixedArrayElement(arr, 1, reject, barrier_mode);
Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
Node* const length = SmiTag(kSize);
Node* const result = AllocateUninitializedJSArrayWithoutElements(
kind, array_map, length, nullptr);
StoreObjectField(result, JSObject::kElementsOffset, arr);
Return(result);
}
TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) {
......@@ -600,13 +656,9 @@ TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) {
{
Label out(this), if_rejectpromise(this), debug_pop(this, Label::kDeferred);
// TODO(gsathya): Move this to TF.
Node* const resolving_functions = CallRuntime(
Runtime::kCreateResolvingFunctions, context, var_result.value());
Node* const resolve =
LoadFixedArrayElement(resolving_functions, IntPtrConstant(0));
Node* const reject =
LoadFixedArrayElement(resolving_functions, IntPtrConstant(1));
Node *resolve, *reject;
std::tie(resolve, reject) = CreatePromiseResolvingFunctions(
var_result.value(), TrueConstant(), native_context);
Callable call_callable = CodeFactory::Call(isolate);
Node* const maybe_exception = CallJS(call_callable, context, executor,
......
......@@ -38,6 +38,12 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
void BranchIfFastPath(Node* context, Node* promise, Label* if_isunmodified,
Label* if_ismodified);
Node* CreatePromiseResolvingFunctionsContext(Node* promise, Node* debug_event,
Node* native_context);
std::pair<Node*, Node*> CreatePromiseResolvingFunctions(
Node* promise, Node* native_context, Node* promise_context);
};
} // namespace internal
......
......@@ -569,7 +569,7 @@ namespace internal {
TFJ(PromiseConstructor, 1) \
TFJ(PromiseInternalConstructor, 1) \
TFJ(IsPromise, 1) \
CPP(CreateResolvingFunctions) \
TFJ(CreateResolvingFunctions, 2) \
TFJ(PromiseResolveClosure, 1) \
CPP(PromiseRejectClosure) \
TFJ(PromiseThen, 2) \
......
......@@ -1382,6 +1382,14 @@ Node* CodeStubAssembler::StoreContextElement(Node* context, Node* slot_index,
return Store(context, offset, value);
}
Node* CodeStubAssembler::StoreContextElementNoWriteBarrier(Node* context,
int slot_index,
Node* value) {
int offset = Context::SlotOffset(slot_index);
return StoreNoWriteBarrier(MachineRepresentation::kTagged, context,
IntPtrConstant(offset), value);
}
Node* CodeStubAssembler::LoadNativeContext(Node* context) {
return LoadContextElement(context, Context::NATIVE_CONTEXT_INDEX);
}
......@@ -8290,6 +8298,35 @@ Node* CodeStubAssembler::IsPromiseHookEnabled() {
return WordNotEqual(promise_hook, IntPtrConstant(0));
}
Node* CodeStubAssembler::AllocateFunctionWithMapAndContext(Node* map,
Node* shared_info,
Node* context) {
Node* const code = BitcastTaggedToWord(
LoadObjectField(shared_info, SharedFunctionInfo::kCodeOffset));
Node* const code_entry =
IntPtrAdd(code, IntPtrConstant(Code::kHeaderSize - kHeapObjectTag));
Node* const fun = Allocate(JSFunction::kSize);
StoreMapNoWriteBarrier(fun, map);
StoreObjectFieldRoot(fun, JSObject::kPropertiesOffset,
Heap::kEmptyFixedArrayRootIndex);
StoreObjectFieldRoot(fun, JSObject::kElementsOffset,
Heap::kEmptyFixedArrayRootIndex);
StoreObjectFieldRoot(fun, JSFunction::kLiteralsOffset,
Heap::kEmptyLiteralsArrayRootIndex);
StoreObjectFieldRoot(fun, JSFunction::kPrototypeOrInitialMapOffset,
Heap::kTheHoleValueRootIndex);
StoreObjectFieldNoWriteBarrier(fun, JSFunction::kSharedFunctionInfoOffset,
shared_info);
StoreObjectFieldNoWriteBarrier(fun, JSFunction::kContextOffset, context);
StoreObjectFieldNoWriteBarrier(fun, JSFunction::kCodeEntryOffset, code_entry,
MachineType::PointerRepresentation());
StoreObjectFieldRoot(fun, JSFunction::kNextFunctionLinkOffset,
Heap::kUndefinedValueRootIndex);
return fun;
}
Node* CodeStubAssembler::AllocateJSPromise(Node* context) {
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
......
......@@ -402,6 +402,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* LoadContextElement(Node* context, Node* slot_index);
Node* StoreContextElement(Node* context, int slot_index, Node* value);
Node* StoreContextElement(Node* context, Node* slot_index, Node* value);
Node* StoreContextElementNoWriteBarrier(Node* context, int slot_index,
Node* value);
Node* LoadNativeContext(Node* context);
Node* LoadJSArrayElementsMap(ElementsKind kind, Node* native_context);
......@@ -1093,6 +1095,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* ElementOffsetFromIndex(Node* index, ElementsKind kind,
ParameterMode mode, int base_size = 0);
Node* AllocateFunctionWithMapAndContext(Node* map, Node* shared_info,
Node* context);
// Promise helpers
Node* IsPromiseHookEnabled();
......
......@@ -8,6 +8,7 @@
#include "src/code-stub-assembler.h"
#include "src/compiler/node.h"
#include "src/isolate.h"
#include "src/promise-utils.h"
#include "test/cctest/compiler/code-assembler-tester.h"
#include "test/cctest/compiler/function-tester.h"
......@@ -1996,5 +1997,107 @@ TEST(PromiseHasHandler) {
CHECK_EQ(isolate->heap()->false_value(), *result);
}
TEST(CreatePromiseResolvingFunctionsContext) {
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 1;
CodeAssemblerTester data(isolate, kNumParams);
PromiseBuiltinsAssembler m(data.state());
Node* const context = m.Parameter(kNumParams + 2);
Node* const native_context = m.LoadNativeContext(context);
Node* const promise = m.AllocateJSPromise(context);
m.PromiseSet(promise, m.SmiConstant(kPromisePending), m.SmiConstant(1));
Node* const promise_context = m.CreatePromiseResolvingFunctionsContext(
promise, m.BooleanConstant(false), native_context);
m.Return(promise_context);
Handle<Code> code = data.GenerateCode();
CHECK(!code.is_null());
FunctionTester ft(code, kNumParams);
Handle<Object> result =
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK(result->IsContext());
Handle<Context> context_js = Handle<Context>::cast(result);
CHECK_EQ(isolate->native_context()->closure(), context_js->closure());
CHECK_EQ(isolate->heap()->the_hole_value(), context_js->extension());
CHECK_EQ(*isolate->native_context(), context_js->native_context());
CHECK_EQ(Smi::FromInt(0), context_js->get(PromiseUtils::kAlreadyVisitedSlot));
CHECK(context_js->get(PromiseUtils::kPromiseSlot)->IsJSPromise());
CHECK_EQ(isolate->heap()->false_value(),
context_js->get(PromiseUtils::kDebugEventSlot));
}
TEST(CreatePromiseResolvingFunctions) {
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 1;
CodeAssemblerTester data(isolate, kNumParams);
PromiseBuiltinsAssembler m(data.state());
Node* const context = m.Parameter(kNumParams + 2);
Node* const native_context = m.LoadNativeContext(context);
Node* const promise = m.AllocateJSPromise(context);
m.PromiseSet(promise, m.SmiConstant(kPromisePending), m.SmiConstant(1));
Node *resolve, *reject;
std::tie(resolve, reject) = m.CreatePromiseResolvingFunctions(
promise, m.BooleanConstant(false), native_context);
Node* const kSize = m.IntPtrConstant(2);
Node* const arr = m.AllocateFixedArray(FAST_ELEMENTS, kSize);
m.StoreFixedArrayElement(arr, 0, resolve);
m.StoreFixedArrayElement(arr, 1, reject);
m.Return(arr);
Handle<Code> code = data.GenerateCode();
CHECK(!code.is_null());
FunctionTester ft(code, kNumParams);
Handle<Object> result_obj =
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK(result_obj->IsFixedArray());
Handle<FixedArray> result_arr = Handle<FixedArray>::cast(result_obj);
CHECK(result_arr->get(0)->IsJSFunction());
CHECK(result_arr->get(1)->IsJSFunction());
}
TEST(AllocateFunctionWithMapAndContext) {
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 1;
CodeAssemblerTester data(isolate, kNumParams);
PromiseBuiltinsAssembler m(data.state());
Node* const context = m.Parameter(kNumParams + 2);
Node* const native_context = m.LoadNativeContext(context);
Node* const promise = m.AllocateJSPromise(context);
m.PromiseSet(promise, m.SmiConstant(kPromisePending), m.SmiConstant(1));
Node* promise_context = m.CreatePromiseResolvingFunctionsContext(
promise, m.BooleanConstant(false), native_context);
Node* resolve_info =
m.LoadContextElement(native_context, Context::PROMISE_RESOLVE_SHARED_FUN);
Node* const map = m.LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
Node* const resolve =
m.AllocateFunctionWithMapAndContext(map, resolve_info, promise_context);
m.Return(resolve);
Handle<Code> code = data.GenerateCode();
CHECK(!code.is_null());
FunctionTester ft(code, kNumParams);
Handle<Object> result_obj =
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK(result_obj->IsJSFunction());
Handle<JSFunction> fun = Handle<JSFunction>::cast(result_obj);
CHECK_EQ(isolate->heap()->empty_fixed_array(), fun->properties());
CHECK_EQ(isolate->heap()->empty_fixed_array(), fun->elements());
CHECK_EQ(isolate->heap()->empty_literals_array(), fun->literals());
CHECK_EQ(isolate->heap()->the_hole_value(), fun->prototype_or_initial_map());
CHECK_EQ(*isolate->promise_resolve_shared_fun(), fun->shared());
CHECK_EQ(isolate->promise_resolve_shared_fun()->code(), fun->code());
CHECK_EQ(isolate->heap()->undefined_value(), fun->next_function_link());
}
} // namespace internal
} // namespace v8
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