Commit c59fbf13 authored by Joshua Litt's avatar Joshua Litt Committed by Commit Bot

[promises] Port promiseAll ResolveElementClosure to Torque.

Bug: v8:9838
Change-Id: Ib7af793218d005883b0ab5423714fdf43664cbc4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1972611
Commit-Queue: Joshua Litt <joshualitt@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65818}
parent dabc6eb5
This diff is collapsed.
......@@ -837,6 +837,7 @@ extern macro ChangeFloat32ToFloat64(float32): float64;
extern macro ChangeNumberToFloat64(Number): float64;
extern macro ChangeFloat64ToTagged(float64): Number;
extern macro ChangeFloat64ToUintPtr(float64): uintptr;
extern macro ChangeFloat64ToIntPtr(float64): intptr;
extern macro ChangeInt32ToIntPtr(int32): intptr; // Sign-extends.
extern macro ChangeUint32ToWord(uint32): uintptr; // Doesn't sign-extend.
extern macro LoadNativeContext(Context): NativeContext;
......
......@@ -712,11 +712,8 @@ namespace internal {
CPP(IsPromise) \
/* ES #sec-promise.all */ \
TFJ(PromiseAll, 1, kReceiver, kIterable) \
TFJ(PromiseAllResolveElementClosure, 1, kReceiver, kValue) \
/* ES #sec-promise.allsettled */ \
TFJ(PromiseAllSettled, 1, kReceiver, kIterable) \
TFJ(PromiseAllSettledResolveElementClosure, 1, kReceiver, kValue) \
TFJ(PromiseAllSettledRejectElementClosure, 1, kReceiver, kValue) \
\
/* Reflect */ \
ASM(ReflectApply, JSTrampoline) \
......
......@@ -740,193 +740,5 @@ TF_BUILTIN(PromiseAllSettled, PromiseBuiltinsAssembler) {
});
}
void PromiseBuiltinsAssembler::Generate_PromiseAllResolveElementClosure(
TNode<Context> context, TNode<Object> value, TNode<JSFunction> function,
const CreatePromiseAllResolveElementFunctionValue& callback) {
Label already_called(this, Label::kDeferred), resolve_promise(this);
// We use the {function}s context as the marker to remember whether this
// resolve element closure was already called. It points to the resolve
// element context (which is a FunctionContext) until it was called the
// first time, in which case we make it point to the native context here
// to mark this resolve element closure as done.
GotoIf(IsNativeContext(context), &already_called);
CSA_ASSERT(
this,
SmiEqual(LoadObjectField<Smi>(context, Context::kLengthOffset),
SmiConstant(PromiseBuiltins::kPromiseAllResolveElementLength)));
TNode<NativeContext> native_context = LoadNativeContext(context);
StoreObjectField(function, JSFunction::kContextOffset, native_context);
// Update the value depending on whether Promise.all or
// Promise.allSettled is called.
value = callback(context, native_context, value);
// Determine the index from the {function}.
Label unreachable(this, Label::kDeferred);
STATIC_ASSERT(PropertyArray::kNoHashSentinel == 0);
TNode<IntPtrT> identity_hash =
LoadJSReceiverIdentityHash(function, &unreachable);
CSA_ASSERT(this, IntPtrGreaterThan(identity_hash, IntPtrConstant(0)));
TNode<IntPtrT> index = IntPtrSub(identity_hash, IntPtrConstant(1));
// Check if we need to grow the [[ValuesArray]] to store {value} at {index}.
TNode<JSArray> values_array = CAST(LoadContextElement(
context, PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot));
TNode<FixedArray> elements = CAST(LoadElements(values_array));
TNode<IntPtrT> values_length =
LoadAndUntagObjectField(values_array, JSArray::kLengthOffset);
Label if_inbounds(this), if_outofbounds(this), done(this);
Branch(IntPtrLessThan(index, values_length), &if_inbounds, &if_outofbounds);
BIND(&if_outofbounds);
{
// Check if we need to grow the backing store.
TNode<IntPtrT> new_length = IntPtrAdd(index, IntPtrConstant(1));
TNode<IntPtrT> elements_length =
LoadAndUntagObjectField(elements, FixedArray::kLengthOffset);
Label if_grow(this, Label::kDeferred), if_nogrow(this);
Branch(IntPtrLessThan(index, elements_length), &if_nogrow, &if_grow);
BIND(&if_grow);
{
// We need to grow the backing store to fit the {index} as well.
TNode<IntPtrT> new_elements_length =
IntPtrMin(CalculateNewElementsCapacity(new_length),
IntPtrConstant(PropertyArray::HashField::kMax + 1));
CSA_ASSERT(this, IntPtrLessThan(index, new_elements_length));
CSA_ASSERT(this, IntPtrLessThan(elements_length, new_elements_length));
TNode<FixedArray> new_elements =
CAST(AllocateFixedArray(PACKED_ELEMENTS, new_elements_length,
AllocationFlag::kAllowLargeObjectAllocation));
CopyFixedArrayElements(PACKED_ELEMENTS, elements, PACKED_ELEMENTS,
new_elements, elements_length,
new_elements_length);
StoreFixedArrayElement(new_elements, index, value);
// Update backing store and "length" on {values_array}.
StoreObjectField(values_array, JSArray::kElementsOffset, new_elements);
StoreObjectFieldNoWriteBarrier(values_array, JSArray::kLengthOffset,
SmiTag(new_length));
Goto(&done);
}
BIND(&if_nogrow);
{
// The {index} is within bounds of the {elements} backing store, so
// just store the {value} and update the "length" of the {values_array}.
StoreObjectFieldNoWriteBarrier(values_array, JSArray::kLengthOffset,
SmiTag(new_length));
StoreFixedArrayElement(elements, index, value);
Goto(&done);
}
}
BIND(&if_inbounds);
{
// The {index} is in bounds of the {values_array},
// just store the {value} and continue.
StoreFixedArrayElement(elements, index, value);
Goto(&done);
}
BIND(&done);
TNode<Smi> remaining_elements_count = CAST(LoadContextElement(
context, PromiseBuiltins::kPromiseAllResolveElementRemainingSlot));
remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1));
StoreContextElement(context,
PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
remaining_elements_count);
GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)), &resolve_promise);
Return(UndefinedConstant());
BIND(&resolve_promise);
TNode<PromiseCapability> capability = CAST(LoadContextElement(
context, PromiseBuiltins::kPromiseAllResolveElementCapabilitySlot));
TNode<Object> resolve =
LoadObjectField(capability, PromiseCapability::kResolveOffset);
CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
context, resolve, UndefinedConstant(), values_array);
Return(UndefinedConstant());
BIND(&already_called);
Return(UndefinedConstant());
BIND(&unreachable);
Unreachable();
}
TF_BUILTIN(PromiseAllResolveElementClosure, PromiseBuiltinsAssembler) {
TNode<Object> value = CAST(Parameter(Descriptor::kValue));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<JSFunction> function = CAST(Parameter(Descriptor::kJSTarget));
Generate_PromiseAllResolveElementClosure(
context, value, function,
[](TNode<Object>, TNode<NativeContext>, TNode<Object> value) {
return value;
});
}
TF_BUILTIN(PromiseAllSettledResolveElementClosure, PromiseBuiltinsAssembler) {
TNode<Object> value = CAST(Parameter(Descriptor::kValue));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<JSFunction> function = CAST(Parameter(Descriptor::kJSTarget));
Generate_PromiseAllResolveElementClosure(
context, value, function,
[this](TNode<Context> context, TNode<NativeContext> native_context,
TNode<Object> value) {
// TODO(gsathya): Optimize the creation using a cached map to
// prevent transitions here.
// 9. Let obj be ! ObjectCreate(%ObjectPrototype%).
TNode<HeapObject> object_function = Cast(
LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX));
TNode<Map> object_function_map = Cast(LoadObjectField(
object_function, JSFunction::kPrototypeOrInitialMapOffset));
TNode<JSObject> obj = AllocateJSObjectFromMap(object_function_map);
// 10. Perform ! CreateDataProperty(obj, "status", "fulfilled").
CallBuiltin(Builtins::kFastCreateDataProperty, context, obj,
StringConstant("status"), StringConstant("fulfilled"));
// 11. Perform ! CreateDataProperty(obj, "value", x).
CallBuiltin(Builtins::kFastCreateDataProperty, context, obj,
StringConstant("value"), value);
return obj;
});
}
TF_BUILTIN(PromiseAllSettledRejectElementClosure, PromiseBuiltinsAssembler) {
TNode<Object> value = CAST(Parameter(Descriptor::kValue));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<JSFunction> function = CAST(Parameter(Descriptor::kJSTarget));
Generate_PromiseAllResolveElementClosure(
context, value, function,
[this](TNode<Context> context, TNode<NativeContext> native_context,
TNode<Object> value) {
// TODO(gsathya): Optimize the creation using a cached map to
// prevent transitions here.
// 9. Let obj be ! ObjectCreate(%ObjectPrototype%).
TNode<HeapObject> object_function = Cast(
LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX));
TNode<Map> object_function_map = Cast(LoadObjectField(
object_function, JSFunction::kPrototypeOrInitialMapOffset));
TNode<JSObject> obj = AllocateJSObjectFromMap(object_function_map);
// 10. Perform ! CreateDataProperty(obj, "status", "rejected").
CallBuiltin(Builtins::kFastCreateDataProperty, context, obj,
StringConstant("status"), StringConstant("rejected"));
// 11. Perform ! CreateDataProperty(obj, "reason", x).
CallBuiltin(Builtins::kFastCreateDataProperty, context, obj,
StringConstant("reason"), value);
return obj;
});
}
} // namespace internal
} // namespace v8
......@@ -167,15 +167,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
TNode<Context> context, TNode<Object> receiver, TNode<Object> iterable,
const PromiseAllResolvingElementFunction& create_resolve_element_function,
const PromiseAllResolvingElementFunction& create_reject_element_function);
using CreatePromiseAllResolveElementFunctionValue =
std::function<TNode<Object>(TNode<Context> context,
TNode<NativeContext> native_context,
TNode<Object> value)>;
void Generate_PromiseAllResolveElementClosure(
TNode<Context> context, TNode<Object> value, TNode<JSFunction> function,
const CreatePromiseAllResolveElementFunctionValue& callback);
};
} // namespace internal
......
......@@ -227,6 +227,9 @@ Convert<float64, uintptr>(ui: uintptr): float64 {
Convert<Number, uintptr>(ui: uintptr): Number {
return ChangeUintPtrToTagged(ui);
}
Convert<Number, intptr>(i: intptr): Number {
return ChangeUintPtrToTagged(Unsigned(i));
}
Convert<uintptr, float64>(d: float64): uintptr {
return ChangeFloat64ToUintPtr(d);
}
......@@ -239,6 +242,9 @@ Convert<uintptr, RawPtr>(r: RawPtr): uintptr {
Convert<intptr, RawPtr>(r: RawPtr): intptr {
return Signed(r);
}
Convert<intptr, Number>(n: Number): intptr {
return ChangeFloat64ToIntPtr(ChangeNumberToFloat64(n));
}
Convert<bint, int32>(v: int32): bint {
return IntPtrToBInt(Convert<intptr>(v));
}
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include 'src/builtins/builtins-promise.h'
#include 'src/builtins/builtins-promise-gen.h'
#include 'src/objects/property-array.h'
namespace promise {
struct PromiseAllWrapResultAsFulfilledFunctor {
macro Call(_nativeContext: NativeContext, value: JSAny): JSAny {
return value;
}
}
struct PromiseAllSettledWrapResultAsFulfilledFunctor {
transitioning
macro Call(implicit context: Context)(
nativeContext: NativeContext, value: JSAny): JSAny {
// TODO(gsathya): Optimize the creation using a cached map to
// prevent transitions here.
// 9. Let obj be ! ObjectCreate(%ObjectPrototype%).
const objectFunction = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]);
const objectFunctionMap =
UnsafeCast<Map>(objectFunction.prototype_or_initial_map);
const obj = AllocateJSObjectFromMap(objectFunctionMap);
// 10. Perform ! CreateDataProperty(obj, "status", "fulfilled").
FastCreateDataProperty(
obj, StringConstant('status'), StringConstant('fulfilled'));
// 11. Perform ! CreateDataProperty(obj, "value", x).
FastCreateDataProperty(obj, StringConstant('value'), value);
return obj;
}
}
struct PromiseAllSettledWrapResultAsRejectedFunctor {
transitioning
macro Call(implicit context: Context)(
nativeContext: NativeContext, value: JSAny): JSAny {
// TODO(gsathya): Optimize the creation using a cached map to
// prevent transitions here.
// 9. Let obj be ! ObjectCreate(%ObjectPrototype%).
const objectFunction = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]);
const objectFunctionMap =
UnsafeCast<Map>(objectFunction.prototype_or_initial_map);
const obj = AllocateJSObjectFromMap(objectFunctionMap);
// 10. Perform ! CreateDataProperty(obj, "status", "rejected").
FastCreateDataProperty(
obj, StringConstant('status'), StringConstant('rejected'));
// 11. Perform ! CreateDataProperty(obj, "reason", x).
FastCreateDataProperty(obj, StringConstant('reason'), value);
return obj;
}
}
extern macro LoadJSReceiverIdentityHash(Object): intptr labels IfNoHash;
extern enum PromiseAllResolveElementContextSlots extends int31
constexpr 'PromiseBuiltins::PromiseAllResolveElementContextSlots' {
kPromiseAllResolveElementRemainingSlot,
kPromiseAllResolveElementCapabilitySlot,
kPromiseAllResolveElementValuesArraySlot,
kPromiseAllResolveElementLength
}
extern operator '[]=' macro StoreContextElement(
Context, constexpr PromiseAllResolveElementContextSlots, Object): void;
extern operator '[]' macro LoadContextElement(
Context, constexpr PromiseAllResolveElementContextSlots): Object;
const kPropertyArrayNoHashSentinel: constexpr int31
generates 'PropertyArray::kNoHashSentinel';
const kPropertyArrayHashFieldMax: constexpr int31
generates 'PropertyArray::HashField::kMax';
transitioning macro PromiseAllResolveElementClosure<F: type>(
implicit context:
Context)(value: JSAny, function: JSFunction, wrapResultFunctor: F):
JSAny {
// We use the {function}s context as the marker to remember whether this
// resolve element closure was already called. It points to the resolve
// element context (which is a FunctionContext) until it was called the
// first time, in which case we make it point to the native context here
// to mark this resolve element closure as done.
if (IsNativeContext(context)) deferred {
return Undefined;
}
assert(
context.length ==
PromiseAllResolveElementContextSlots::kPromiseAllResolveElementLength);
const nativeContext = LoadNativeContext(context);
function.context = nativeContext;
// Update the value depending on whether Promise.all or
// Promise.allSettled is called.
const updatedValue = wrapResultFunctor.Call(nativeContext, value);
// Determine the index from the {function}.
assert(kPropertyArrayNoHashSentinel == 0);
const identityHash =
LoadJSReceiverIdentityHash(function) otherwise unreachable;
assert(identityHash > 0);
const index = identityHash - 1;
// Check if we need to grow the [[ValuesArray]] to store {value} at {index}.
const valuesArray = UnsafeCast<JSArray>(
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementValuesArraySlot]);
const elements = UnsafeCast<FixedArray>(valuesArray.elements);
const valuesLength = Convert<intptr>(valuesArray.length);
if (index < valuesLength) {
// The {index} is in bounds of the {values_array},
// just store the {value} and continue.
elements.objects[index] = updatedValue;
} else {
// Check if we need to grow the backing store.
const newLength = index + 1;
const elementsLength = elements.length_intptr;
if (index < elementsLength) {
// The {index} is within bounds of the {elements} backing store, so
// just store the {value} and update the "length" of the {values_array}.
valuesArray.length = Convert<Smi>(newLength);
elements.objects[index] = updatedValue;
} else
deferred {
// We need to grow the backing store to fit the {index} as well.
const newElementsLength = IntPtrMin(
CalculateNewElementsCapacity(newLength),
kPropertyArrayHashFieldMax + 1);
assert(index < newElementsLength);
assert(elementsLength < newElementsLength);
const newElements =
ExtractFixedArray(elements, 0, elementsLength, newElementsLength);
newElements.objects[index] = updatedValue;
// Update backing store and "length" on {values_array}.
valuesArray.elements = newElements;
valuesArray.length = Convert<Smi>(newLength);
}
}
let remainingElementsCount =
UnsafeCast<Smi>(context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot]);
remainingElementsCount = remainingElementsCount - 1;
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot] =
remainingElementsCount;
if (remainingElementsCount == 0) {
const capability = UnsafeCast<PromiseCapability>(
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementCapabilitySlot]);
const resolve = UnsafeCast<JSAny>(capability.resolve);
Call(context, resolve, Undefined, valuesArray);
}
return Undefined;
}
transitioning javascript builtin
PromiseAllResolveElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllWrapResultAsFulfilledFunctor{});
}
transitioning javascript builtin
PromiseAllSettledResolveElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{});
}
transitioning javascript builtin
PromiseAllSettledRejectElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllSettledWrapResultAsRejectedFunctor{});
}
}
......@@ -4978,8 +4978,10 @@ TNode<FixedArrayBase> CodeStubAssembler::GrowElementsCapacity(
// Copy the elements from the old elements store to the new.
// The size-check above guarantees that the |new_elements| is allocated
// in new space so we can skip the write barrier.
CopyFixedArrayElements(from_kind, elements, to_kind, new_elements, capacity,
new_capacity, SKIP_WRITE_BARRIER, mode);
CopyFixedArrayElements(from_kind, CAST(elements), to_kind, new_elements,
UncheckedCast<IntPtrT>(capacity),
UncheckedCast<IntPtrT>(new_capacity),
SKIP_WRITE_BARRIER, mode);
StoreObjectField(CAST(object), JSObject::kElementsOffset, new_elements);
Comment("] GrowElementsCapacity");
......
......@@ -2064,8 +2064,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// zero to |to_array| of |capacity| size respecting both array's elements
// kinds.
void CopyFixedArrayElements(
ElementsKind from_kind, Node* from_array, ElementsKind to_kind,
Node* to_array, Node* element_count, Node* capacity,
ElementsKind from_kind, TNode<Object> from_array, ElementsKind to_kind,
TNode<Object> to_array, TNode<IntPtrT> element_count,
TNode<IntPtrT> capacity,
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
ParameterMode mode = INTPTR_PARAMETERS) {
CopyFixedArrayElements(from_kind, from_array, to_kind, to_array,
......
......@@ -94,10 +94,10 @@ extern macro AllocateFixedDoubleArrayWithHoles(
intptr, constexpr AllocationFlag): FixedDoubleArray;
extern macro CopyFixedArrayElements(
constexpr ElementsKind, FixedArray, constexpr ElementsKind, FixedArray,
intptr, intptr, intptr): void;
intptr, intptr): void;
extern macro CopyFixedArrayElements(
constexpr ElementsKind, FixedArray, constexpr ElementsKind, FixedArray, Smi,
Smi, Smi): void;
constexpr ElementsKind, FixedArray, constexpr ElementsKind, FixedArray,
intptr, intptr, intptr): void;
macro ExtractFixedArray(
source: FixedArray, first: intptr, count: intptr,
......
......@@ -74,6 +74,7 @@ void ImplementationVisitor::BeginCSAFiles() {
UnderlinifyPath(SourceFileMap::PathFromV8Root(file)) + "_H_";
header << "#ifndef " << headerDefine << "\n";
header << "#define " << headerDefine << "\n\n";
header << "#include \"src/builtins/builtins-promise.h\"\n";
header << "#include \"src/compiler/code-assembler.h\"\n";
header << "#include \"src/codegen/code-stub-assembler.h\"\n";
header << "#include \"src/utils/utils.h\"\n";
......
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