Commit e2ebb2be authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by Commit Bot

[promises] Reduce size of JSPromise by 1 word

Instead of using a word to store the status of the promise, this
patch uses 2 bit on flags.

Bug: v8:5046
Change-Id: Ic651338230dbe1704c68de8652676f236a3298f0
Reviewed-on: https://chromium-review.googlesource.com/634623
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47628}
parent 70881da0
......@@ -229,9 +229,8 @@ void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwaitResumeClosure(
#if defined(DEBUG) && defined(ENABLE_SLOW_DCHECKS)
Node* const awaited_promise = LoadGeneratorAwaitedPromise(generator);
CSA_SLOW_ASSERT(this, HasInstanceType(awaited_promise, JS_PROMISE_TYPE));
CSA_SLOW_ASSERT(this, SmiNotEqual(LoadObjectField(awaited_promise,
JSPromise::kStatusOffset),
SmiConstant(v8::Promise::kPending)));
CSA_SLOW_ASSERT(this, Word32NotEqual(PromiseStatus(awaited_promise),
Int32Constant(v8::Promise::kPending)));
#endif
ClearAwaitedPromise(generator);
......
......@@ -28,8 +28,7 @@ Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) {
}
void PromiseBuiltinsAssembler::PromiseInit(Node* promise) {
StoreObjectFieldNoWriteBarrier(promise, JSPromise::kStatusOffset,
SmiConstant(v8::Promise::kPending));
STATIC_ASSERT(v8::Promise::kPending == 0);
StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset,
SmiConstant(0));
for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) {
......@@ -56,17 +55,14 @@ Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context,
return instance;
}
Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise(Node* context,
Node* status,
Node* result) {
CSA_ASSERT(this, TaggedIsSmi(status));
Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise(
Node* context, v8::Promise::PromiseState status, Node* result) {
Node* const instance = AllocateJSPromise(context);
StoreObjectFieldNoWriteBarrier(instance, JSPromise::kStatusOffset, status);
StoreObjectFieldNoWriteBarrier(instance, JSPromise::kResultOffset, result);
STATIC_ASSERT(JSPromise::kStatusShift == 0);
StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset,
SmiConstant(0));
SmiConstant(status));
for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) {
int offset = JSPromise::kSize + i * kPointerSize;
StoreObjectFieldNoWriteBarrier(instance, offset, SmiConstant(0));
......@@ -281,6 +277,29 @@ void PromiseBuiltinsAssembler::PromiseSetHasHandler(Node* promise) {
StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags);
}
Node* PromiseBuiltinsAssembler::IsPromiseStatus(
Node* actual, v8::Promise::PromiseState expected) {
return Word32Equal(actual, Int32Constant(expected));
}
Node* PromiseBuiltinsAssembler::PromiseStatus(Node* promise) {
STATIC_ASSERT(JSPromise::kStatusShift == 0);
Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset);
return Word32And(SmiToWord32(flags), Int32Constant(JSPromise::kStatusMask));
}
void PromiseBuiltinsAssembler::PromiseSetStatus(
Node* promise, v8::Promise::PromiseState const status) {
CSA_ASSERT(this,
IsPromiseStatus(PromiseStatus(promise), v8::Promise::kPending));
CHECK(status != v8::Promise::kPending);
Node* mask = SmiConstant(status);
Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset);
StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset,
SmiOr(flags, mask));
}
void PromiseBuiltinsAssembler::PromiseSetHandledHint(Node* promise) {
Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset);
Node* const new_flags =
......@@ -461,9 +480,8 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(
BIND(&append_callbacks);
{
Label fulfilled_check(this);
Node* const status = LoadObjectField(promise, JSPromise::kStatusOffset);
GotoIfNot(SmiEqual(status, SmiConstant(v8::Promise::kPending)),
&fulfilled_check);
Node* const status = PromiseStatus(promise);
GotoIfNot(IsPromiseStatus(status, v8::Promise::kPending), &fulfilled_check);
Node* const existing_deferred_promise =
LoadObjectField(promise, JSPromise::kDeferredPromiseOffset);
......@@ -566,8 +584,7 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(
{
Label reject(this);
Node* const result = LoadObjectField(promise, JSPromise::kResultOffset);
GotoIfNot(WordEqual(status, SmiConstant(v8::Promise::kFulfilled)),
&reject);
GotoIfNot(IsPromiseStatus(status, v8::Promise::kFulfilled), &reject);
Node* info = AllocatePromiseReactionJobInfo(
result, var_on_resolve.value(), deferred_promise, deferred_on_resolve,
......@@ -578,6 +595,7 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(
BIND(&reject);
{
CSA_ASSERT(this, IsPromiseStatus(status, v8::Promise::kRejected));
Node* const has_handler = PromiseHasHandler(promise);
Label enqueue(this);
......@@ -700,13 +718,12 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
// reusing the value from the promise.
BIND(&if_nativepromise);
{
Node* const thenable_status =
LoadObjectField(result, JSPromise::kStatusOffset);
Node* const thenable_status = PromiseStatus(result);
Node* const thenable_value =
LoadObjectField(result, JSPromise::kResultOffset);
Label if_isnotpending(this);
GotoIfNot(SmiEqual(SmiConstant(v8::Promise::kPending), thenable_status),
GotoIfNot(IsPromiseStatus(thenable_status, v8::Promise::kPending),
&if_isnotpending);
// TODO(gsathya): Use a marker here instead of the actual then
......@@ -720,7 +737,7 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
BIND(&if_isnotpending);
{
Label if_fulfilled(this), if_rejected(this);
Branch(SmiEqual(SmiConstant(v8::Promise::kFulfilled), thenable_status),
Branch(IsPromiseStatus(thenable_status, v8::Promise::kFulfilled),
&if_fulfilled, &if_rejected);
BIND(&if_fulfilled);
......@@ -837,7 +854,6 @@ void PromiseBuiltinsAssembler::PromiseFulfill(
v8::Promise::PromiseState status) {
Label do_promisereset(this), debug_async_event_enqueue_recurring(this);
Node* const status_smi = SmiConstant(static_cast<int>(status));
Node* const deferred_promise =
LoadObjectField(promise, JSPromise::kDeferredPromiseOffset);
......@@ -864,13 +880,13 @@ void PromiseBuiltinsAssembler::PromiseFulfill(
{
GotoIfNot(IsDebugActive(), &do_promisereset);
CallRuntime(Runtime::kDebugAsyncEventEnqueueRecurring, context, promise,
status_smi);
SmiConstant(status));
Goto(&do_promisereset);
}
BIND(&do_promisereset);
{
StoreObjectField(promise, JSPromise::kStatusOffset, status_smi);
PromiseSetStatus(promise, status);
StoreObjectField(promise, JSPromise::kResultOffset, result);
StoreObjectFieldRoot(promise, JSPromise::kDeferredPromiseOffset,
Heap::kUndefinedValueRootIndex);
......@@ -1517,8 +1533,8 @@ TF_BUILTIN(PromiseReject, PromiseBuiltinsAssembler) {
BIND(&if_nativepromise);
{
Node* const promise = AllocateAndSetJSPromise(
context, SmiConstant(v8::Promise::kRejected), reason);
Node* const promise =
AllocateAndSetJSPromise(context, v8::Promise::kRejected, reason);
CallRuntime(Runtime::kPromiseRejectEventFromStack, context, promise,
reason);
Return(promise);
......
......@@ -88,7 +88,8 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
// This allocates and initializes a promise with the given state and
// fields.
Node* AllocateAndSetJSPromise(Node* context, Node* status, Node* result);
Node* AllocateAndSetJSPromise(Node* context, v8::Promise::PromiseState status,
Node* result);
Node* AllocatePromiseResolveThenableJobInfo(Node* result, Node* then,
Node* resolve, Node* reject,
......@@ -177,7 +178,12 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
void SetPromiseHandledByIfTrue(Node* context, Node* condition, Node* promise,
const NodeGenerator& handled_by);
Node* PromiseStatus(Node* promise);
private:
Node* IsPromiseStatus(Node* actual, v8::Promise::PromiseState expected);
void PromiseSetStatus(Node* promise, v8::Promise::PromiseState status);
Node* AllocateJSPromise(Node* context);
};
......
......@@ -1004,7 +1004,6 @@ void JSPromise::JSPromiseVerify() {
CHECK(IsJSPromise());
JSObjectVerify();
Isolate* isolate = GetIsolate();
VerifySmiField(kStatusOffset);
CHECK(result()->IsUndefined(isolate) || result()->IsObject());
CHECK(deferred_promise()->IsUndefined(isolate) ||
deferred_promise()->IsJSReceiver() ||
......
......@@ -5250,7 +5250,6 @@ ACCESSORS(JSPromiseCapability, promise, Object, kPromiseOffset)
ACCESSORS(JSPromiseCapability, resolve, Object, kResolveOffset)
ACCESSORS(JSPromiseCapability, reject, Object, kRejectOffset)
SMI_ACCESSORS(JSPromise, status, kStatusOffset)
ACCESSORS(JSPromise, result, Object, kResultOffset)
ACCESSORS(JSPromise, deferred_promise, Object, kDeferredPromiseOffset)
ACCESSORS(JSPromise, deferred_on_resolve, Object, kDeferredOnResolveOffset)
......
......@@ -15918,8 +15918,14 @@ class StringSharedKey : public HashTableKey {
int position_;
};
v8::Promise::PromiseState JSPromise::status() const {
int value = flags() & kStatusMask;
DCHECK(value == 0 || value == 1 || value == 2);
return static_cast<v8::Promise::PromiseState>(value);
}
// static
const char* JSPromise::Status(int status) {
const char* JSPromise::Status(v8::Promise::PromiseState status) {
switch (status) {
case v8::Promise::kFulfilled:
return "resolved";
......
......@@ -5357,7 +5357,6 @@ class JSPromiseCapability : public JSObject {
class JSPromise : public JSObject {
public:
DECL_INT_ACCESSORS(status)
DECL_ACCESSORS(result, Object)
// There are 3 possible states for these fields --
......@@ -5388,7 +5387,8 @@ class JSPromise : public JSObject {
// block in an async function.
DECL_BOOLEAN_ACCESSORS(handled_hint)
static const char* Status(int status);
static const char* Status(v8::Promise::PromiseState status);
v8::Promise::PromiseState status() const;
DECL_CAST(JSPromise)
......@@ -5397,8 +5397,7 @@ class JSPromise : public JSObject {
DECL_VERIFIER(JSPromise)
// Layout description.
static const int kStatusOffset = JSObject::kHeaderSize;
static const int kResultOffset = kStatusOffset + kPointerSize;
static const int kResultOffset = JSObject::kHeaderSize;
static const int kDeferredPromiseOffset = kResultOffset + kPointerSize;
static const int kDeferredOnResolveOffset =
kDeferredPromiseOffset + kPointerSize;
......@@ -5414,8 +5413,16 @@ class JSPromise : public JSObject {
kSize + v8::Promise::kEmbedderFieldCount * kPointerSize;
// Flags layout.
static const int kHasHandlerBit = 0;
static const int kHandledHintBit = 1;
// The first two bits store the v8::Promise::PromiseState.
static const int kStatusBits = 2;
static const int kHasHandlerBit = 2;
static const int kHandledHintBit = 3;
static const int kStatusShift = 0;
static const int kStatusMask = 0x3;
STATIC_ASSERT(v8::Promise::kPending == 0);
STATIC_ASSERT(v8::Promise::kFulfilled == 1);
STATIC_ASSERT(v8::Promise::kRejected == 2);
};
// Regular expressions
......
......@@ -1972,7 +1972,7 @@ TEST(AllocateAndSetJSPromise) {
Node* const context = m.Parameter(kNumParams + 2);
Node* const promise = m.AllocateAndSetJSPromise(
context, m.SmiConstant(v8::Promise::kPending), m.SmiConstant(1));
context, v8::Promise::kRejected, m.SmiConstant(1));
m.Return(promise);
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
......@@ -1980,7 +1980,7 @@ TEST(AllocateAndSetJSPromise) {
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK(result->IsJSPromise());
Handle<JSPromise> js_promise = Handle<JSPromise>::cast(result);
CHECK_EQ(v8::Promise::kPending, js_promise->status());
CHECK_EQ(v8::Promise::kRejected, js_promise->status());
CHECK_EQ(Smi::FromInt(1), js_promise->result());
CHECK(!js_promise->has_handler());
}
......
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