Commit 460bf85c authored by peterwmwong's avatar peterwmwong Committed by Commit Bot

[builtins] Port TypedArray ConstructByArrayBuffer to Torque

This is part of an effort to improve the performance of TA#subarray.

Bug: v8:7161
Change-Id: Id110b4bd30fd8f67b9f8f23268e64de22e471c68
Reviewed-on: https://chromium-review.googlesource.com/c/1432596
Commit-Queue: Peter Wong <peter.wm.wong@gmail.com>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59073}
parent 52bcce83
......@@ -288,6 +288,8 @@ const kCalledNonCallable: constexpr MessageTemplate
generates 'MessageTemplate::kCalledNonCallable';
const kCalledOnNullOrUndefined: constexpr MessageTemplate
generates 'MessageTemplate::kCalledOnNullOrUndefined';
const kInvalidOffset: constexpr MessageTemplate
generates 'MessageTemplate::kInvalidOffset';
const kInvalidTypedArrayLength: constexpr MessageTemplate
generates 'MessageTemplate::kInvalidTypedArrayLength';
const kIteratorValueNotAnObject: constexpr MessageTemplate
......@@ -523,8 +525,9 @@ extern macro BranchIfNumberEqual(Number, Number): never
operator '==' macro IsNumberEqual(a: Number, b: Number): bool {
return (BranchIfNumberEqual(a, b)) ? true : false;
}
extern operator '!=' macro BranchIfNumberNotEqual(Number, Number): never
labels Taken, NotTaken;
operator '!=' macro IsNumberNotEqual(a: Number, b: Number): bool {
return (BranchIfNumberEqual(a, b)) ? false : true;
}
extern operator '<' macro BranchIfNumberLessThan(Number, Number): never
labels Taken, NotTaken;
extern operator '<=' macro BranchIfNumberLessThanOrEqual(Number, Number): never
......@@ -536,6 +539,10 @@ extern operator '>=' macro BranchIfNumberGreaterThanOrEqual(
Number, Number): never
labels Taken, NotTaken;
extern builtin Divide(implicit context: Context)(Object, Object): Numeric;
extern builtin Modulus(implicit context: Context)(Object, Object): Numeric;
extern builtin Subtract(implicit context: Context)(Object, Object): Numeric;
extern operator '==' macro WordEqual(Object, Object): bool;
extern operator '!=' macro WordNotEqual(Object, Object): bool;
......@@ -571,6 +578,9 @@ extern operator '&' macro Word32And(int32, int32): int32;
extern operator '&' macro Word32And(uint32, uint32): uint32;
extern operator '==' macro
ConstexprInt31Equal(constexpr int31, constexpr int31): constexpr bool;
extern operator '>=' macro
ConstexprInt31GreaterThanEqual(
constexpr int31, constexpr int31): constexpr bool;
extern operator '==' macro Word32Equal(int32, int32): bool;
extern operator '==' macro Word32Equal(uint32, uint32): bool;
......@@ -937,6 +947,10 @@ FromConstexpr<uintptr, constexpr uintptr>(i: constexpr uintptr): uintptr {
FromConstexpr<Smi, constexpr int31>(i: constexpr int31): Smi {
return %FromConstexpr<Smi>(i);
}
FromConstexpr<PositiveSmi, constexpr int31>(i: constexpr int31): PositiveSmi {
assert(i >= 0);
return %FromConstexpr<PositiveSmi>(i);
}
FromConstexpr<String, constexpr string>(s: constexpr string): String {
return %FromConstexpr<String>(s);
}
......@@ -1114,6 +1128,7 @@ TypedArrayBuiltinsAssembler::LoadTypedArrayBuffer(JSTypedArray): JSArrayBuffer;
extern operator '.data_ptr' macro TypedArrayBuiltinsAssembler::LoadDataPtr(
JSTypedArray): RawPtr;
extern operator '.byte_length' macro LoadByteLength(JSArrayBuffer): uintptr;
extern operator '.elements_kind' macro LoadMapElementsKind(Map): ElementsKind;
extern operator '.elements_kind' macro LoadElementsKind(JSTypedArray):
......
......@@ -318,138 +318,6 @@ TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
Return(UndefinedConstant());
}
// ES6 #sec-typedarray-buffer-byteoffset-length
void TypedArrayBuiltinsAssembler::ConstructByArrayBuffer(
TNode<Context> context, TNode<JSTypedArray> holder,
TNode<JSArrayBuffer> buffer, TNode<Object> byte_offset,
TNode<Object> length, TNode<Smi> element_size) {
CSA_ASSERT(this, TaggedIsPositiveSmi(element_size));
VARIABLE(new_byte_length, MachineRepresentation::kTagged, SmiConstant(0));
VARIABLE(offset, MachineRepresentation::kTagged, SmiConstant(0));
Label start_offset_error(this, Label::kDeferred),
byte_length_error(this, Label::kDeferred),
invalid_offset_error(this, Label::kDeferred);
Label offset_is_smi(this), offset_not_smi(this, Label::kDeferred),
check_length(this), call_init(this), invalid_length(this),
length_undefined(this), length_defined(this), done(this);
GotoIf(IsUndefined(byte_offset), &check_length);
offset.Bind(ToInteger_Inline(context, byte_offset,
CodeStubAssembler::kTruncateMinusZero));
Branch(TaggedIsSmi(offset.value()), &offset_is_smi, &offset_not_smi);
// Check that the offset is a multiple of the element size.
BIND(&offset_is_smi);
{
TNode<Smi> smi_offset = CAST(offset.value());
GotoIf(SmiEqual(smi_offset, SmiConstant(0)), &check_length);
GotoIf(SmiLessThan(smi_offset, SmiConstant(0)), &invalid_length);
TNode<Number> remainder = SmiMod(smi_offset, element_size);
// TODO(ishell): remove <Object, Object>
Branch(WordEqual<Object, Object>(remainder, SmiConstant(0)), &check_length,
&start_offset_error);
}
BIND(&offset_not_smi);
{
GotoIf(IsTrue(CallBuiltin(Builtins::kLessThan, context, offset.value(),
SmiConstant(0))),
&invalid_length);
Node* remainder =
CallBuiltin(Builtins::kModulus, context, offset.value(), element_size);
// Remainder can be a heap number.
Branch(IsTrue(CallBuiltin(Builtins::kEqual, context, remainder,
SmiConstant(0))),
&check_length, &start_offset_error);
}
BIND(&check_length);
Branch(IsUndefined(length), &length_undefined, &length_defined);
BIND(&length_undefined);
{
ThrowIfArrayBufferIsDetached(context, buffer, "Construct");
TNode<Number> buffer_byte_length = ChangeUintPtrToTagged(
LoadObjectField<UintPtrT>(buffer, JSArrayBuffer::kByteLengthOffset));
Node* remainder = CallBuiltin(Builtins::kModulus, context,
buffer_byte_length, element_size);
// Remainder can be a heap number.
GotoIf(IsFalse(CallBuiltin(Builtins::kEqual, context, remainder,
SmiConstant(0))),
&byte_length_error);
new_byte_length.Bind(CallBuiltin(Builtins::kSubtract, context,
buffer_byte_length, offset.value()));
Branch(IsTrue(CallBuiltin(Builtins::kLessThan, context,
new_byte_length.value(), SmiConstant(0))),
&invalid_offset_error, &call_init);
}
BIND(&length_defined);
{
TNode<Smi> new_length = ToSmiIndex(context, length, &invalid_length);
ThrowIfArrayBufferIsDetached(context, buffer, "Construct");
new_byte_length.Bind(SmiMul(new_length, element_size));
// Reading the byte length must come after the ToIndex operation, which
// could cause the buffer to become detached.
TNode<Number> buffer_byte_length = ChangeUintPtrToTagged(
LoadObjectField<UintPtrT>(buffer, JSArrayBuffer::kByteLengthOffset));
Node* end = CallBuiltin(Builtins::kAdd, context, offset.value(),
new_byte_length.value());
Branch(IsTrue(CallBuiltin(Builtins::kGreaterThan, context, end,
buffer_byte_length)),
&invalid_length, &call_init);
}
BIND(&call_init);
{
TNode<Object> raw_length = CallBuiltin(
Builtins::kDivide, context, new_byte_length.value(), element_size);
// Force the result into a Smi, or throw a range error if it doesn't fit.
TNode<Smi> new_length = ToSmiIndex(context, raw_length, &invalid_length);
CallBuiltin(Builtins::kTypedArrayInitializeWithBuffer, context, holder,
new_length, buffer, element_size, offset.value());
Goto(&done);
}
BIND(&invalid_offset_error);
{ ThrowRangeError(context, MessageTemplate::kInvalidOffset, byte_offset); }
BIND(&start_offset_error);
{
Node* holder_map = LoadMap(holder);
Node* problem_string = StringConstant("start offset");
CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
problem_string);
Unreachable();
}
BIND(&byte_length_error);
{
Node* holder_map = LoadMap(holder);
Node* problem_string = StringConstant("byte length");
CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
problem_string);
Unreachable();
}
BIND(&invalid_length);
{
ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength, length);
}
BIND(&done);
}
Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) {
CSA_ASSERT(this, IsJSTypedArray(typed_array));
Node* elements = LoadElements(typed_array);
......@@ -529,8 +397,8 @@ TF_BUILTIN(CreateTypedArray, TypedArrayBuiltinsAssembler) {
// https://tc39.github.io/ecma262/#sec-typedarray-buffer-byteoffset-length
BIND(&if_arg1isbuffer);
{
ConstructByArrayBuffer(context, result, CAST(arg1), arg2, arg3,
element_size);
TypedArrayBuiltinsFromDSLAssembler(state()).ConstructByArrayBuffer(
context, result, CAST(arg1), arg2, arg3, element_size);
Goto(&return_result);
}
......
......@@ -30,12 +30,6 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
const char* method_name,
IterationKind iteration_kind);
void ConstructByArrayBuffer(TNode<Context> context,
TNode<JSTypedArray> holder,
TNode<JSArrayBuffer> buffer,
TNode<Object> byte_offset, TNode<Object> length,
TNode<Smi> element_size);
void SetupTypedArray(TNode<JSTypedArray> holder, TNode<Smi> length,
TNode<UintPtrT> byte_offset,
TNode<UintPtrT> byte_length);
......
......@@ -7,6 +7,8 @@ namespace typed_array {
JSArray;
extern builtin TypedArrayInitialize(implicit context: Context)(
JSTypedArray, PositiveSmi, PositiveSmi, Boolean, JSReceiver): void;
extern builtin TypedArrayInitializeWithBuffer(implicit context: Context)(
JSTypedArray, PositiveSmi, JSArrayBuffer, PositiveSmi, Number): void;
extern macro TypedArrayBuiltinsAssembler::ByteLengthIsValid(Number): bool;
extern macro TypedArrayBuiltinsAssembler::CallCMemcpy(
......@@ -14,6 +16,8 @@ namespace typed_array {
extern macro TypedArrayBuiltinsAssembler::IsSharedArrayBuffer(JSArrayBuffer):
bool;
extern runtime ThrowInvalidTypedArrayAlignment(implicit context: Context)(
Map, String): never;
extern runtime TypedArrayCopyElements(Context, JSTypedArray, Object, Number):
void;
......@@ -107,4 +111,95 @@ namespace typed_array {
ConstructByArrayLike(
typedArray, srcTypedArray, length, elementSize, bufferConstructor);
}
// Determines if `bytes` (byte offset or length) cannot be evenly divded by
// element size.
macro IsUnaligned(implicit context: Context)(bytes: Number, elementSize: Smi):
bool {
const kZero: Smi = 0;
if (bytes == kZero) return false;
const remainder: Number =
Cast<Number>(Modulus(bytes, elementSize)) otherwise unreachable;
return remainder != kZero;
}
// 22.2.4.5 TypedArray ( buffer, byteOffset, length )
// ES #sec-typedarray-buffer-byteoffset-length
macro ConstructByArrayBuffer(implicit context: Context)(
typedArray: JSTypedArray, buffer: JSArrayBuffer, byteOffset: Object,
length: Object, elementSize: Smi): void {
const positiveElementSize: PositiveSmi =
Cast<PositiveSmi>(elementSize) otherwise unreachable;
try {
let offset: Number = FromConstexpr<Smi>(0);
if (byteOffset != Undefined) {
// 6. Let offset be ? ToIndex(byteOffset).
offset = ToInteger_Inline(context, byteOffset, kTruncateMinusZero);
if (offset < 0) goto IfInvalidOffset;
// 7. If offset modulo elementSize ≠ 0, throw a RangeError exception.
if (IsUnaligned(offset, positiveElementSize)) {
goto IfInvalidAlignment('start offset');
}
}
let newLength: PositiveSmi = 0;
// 8. If length is present and length is not undefined, then
if (length != Undefined) {
// a. Let newLength be ? ToIndex(length).
newLength = ToSmiIndex(length) otherwise IfInvalidLength;
}
// 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer)) {
ThrowTypeError(context, kDetachedOperation, 'Construct');
}
// 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
const bufferByteLength: Number = Convert<Number>(buffer.byte_length);
// 11. If length is either not present or undefined, then
if (length == Undefined) {
// a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError
// exception.
if (IsUnaligned(bufferByteLength, positiveElementSize)) {
goto IfInvalidAlignment('byte length');
}
// b. Let newByteLength be bufferByteLength - offset.
// c. If newByteLength < 0, throw a RangeError exception.
if (bufferByteLength < offset) goto IfInvalidOffset;
// Spec step 16 length calculated here to avoid recalculating the length
// in the step 12 branch.
newLength = ToSmiIndex(
Divide((Subtract(bufferByteLength, offset)), positiveElementSize))
otherwise IfInvalidLength;
// 12. Else,
} else {
// a. Let newByteLength be newLength × elementSize.
const newByteLength: Number = SmiMul(newLength, positiveElementSize);
// b. If offset + newByteLength > bufferByteLength, throw a RangeError
// exception.
const difference: Number =
Cast<Number>(Subtract(bufferByteLength, newByteLength))
otherwise unreachable;
if (difference < offset) goto IfInvalidLength;
}
TypedArrayInitializeWithBuffer(
typedArray, newLength, buffer, positiveElementSize, offset);
}
label IfInvalidAlignment(problemString: String) deferred {
ThrowInvalidTypedArrayAlignment(typedArray.map, problemString);
}
label IfInvalidLength deferred {
ThrowRangeError(context, kInvalidTypedArrayLength, length);
}
label IfInvalidOffset deferred {
ThrowRangeError(context, kInvalidOffset, byteOffset);
}
}
}
......@@ -13061,6 +13061,10 @@ TNode<RawPtrT> CodeStubAssembler::LoadJSArrayBufferBackingStore(
JSArrayBuffer::kBackingStoreOffset);
}
TNode<UintPtrT> CodeStubAssembler::LoadByteLength(TNode<JSArrayBuffer> buffer) {
return LoadObjectField<UintPtrT>(buffer, JSArrayBuffer::kByteLengthOffset);
}
TNode<JSArrayBuffer> CodeStubAssembler::LoadJSArrayBufferViewBuffer(
TNode<JSArrayBufferView> array_buffer_view) {
return LoadObjectField<JSArrayBuffer>(array_buffer_view,
......
......@@ -3023,6 +3023,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Uint32T> LoadJSArrayBufferBitField(TNode<JSArrayBuffer> array_buffer);
TNode<RawPtrT> LoadJSArrayBufferBackingStore(
TNode<JSArrayBuffer> array_buffer);
TNode<UintPtrT> LoadByteLength(TNode<JSArrayBuffer> buffer);
Node* IsDetachedBuffer(Node* buffer);
void ThrowIfArrayBufferIsDetached(SloppyTNode<Context> context,
TNode<JSArrayBuffer> array_buffer,
......@@ -3110,6 +3111,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
bool ConstexprBoolNot(bool value) { return !value; }
bool ConstexprInt31Equal(int31_t a, int31_t b) { return a == b; }
bool ConstexprInt31GreaterThanEqual(int31_t a, int31_t b) { return a >= b; }
uint32_t ConstexprUint32Add(uint32_t a, uint32_t b) { return a + b; }
void PerformStackCheck(TNode<Context> context);
......
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