Commit c9ef0405 authored by peterwmwong's avatar peterwmwong Committed by Commit Bot

[builtins]: Optimize CreateTypedArray to use element size log 2 for calculations.

TypedArrayElementsInfo now represents an element's size as a log 2 and typed as
uintptr.  This simplifies and speeds up (avoids possible HeapNumber allocations) a
number of calculations:

  - Number of Elements (length) -> Byte Length - is now a WordShl
  - Byte Length -> Number of Elements (length) - is now a WordShr
  - Testing alignment (byte offset or length)  - is now a WordAnd

These element/byte length related calculations are encapsulated in
TypedArrayElementsInfo as struct methods.

This reduces the size of CreateTypedArray by 2.125 KB (24%) on Mac x64.release:
  - Before: 9,088
  - After:  6,896

This improves the performance of the following microbencmarks
  - TypedArrays-ConstructWithBuffer: ~87%
  - TypedArrays-SubarrayNoSpecies:   ~28%

Bug: v8:7161
Change-Id: I2239fd0e0af9d3ad55cd52318088d3c7c913ae44
Reviewed-on: https://chromium-review.googlesource.com/c/1456299
Commit-Queue: Peter Wong <peter.wm.wong@gmail.com>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarSimon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59531}
parent ad5b6d7d
...@@ -340,6 +340,7 @@ const kTypedArrayMaxByteLength: ...@@ -340,6 +340,7 @@ const kTypedArrayMaxByteLength:
const V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP: const V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP:
constexpr int31 generates 'V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP'; constexpr int31 generates 'V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP';
const kMaxSafeInteger: constexpr float64 generates 'kMaxSafeInteger'; const kMaxSafeInteger: constexpr float64 generates 'kMaxSafeInteger';
const kSmiMaxValue: constexpr uintptr generates 'kSmiMaxValue';
const kStringMaxLength: constexpr int31 generates 'String::kMaxLength'; const kStringMaxLength: constexpr int31 generates 'String::kMaxLength';
const kFixedArrayMaxLength: const kFixedArrayMaxLength:
constexpr int31 generates 'FixedArray::kMaxLength'; constexpr int31 generates 'FixedArray::kMaxLength';
...@@ -594,10 +595,6 @@ extern operator '>=' macro BranchIfNumberGreaterThanOrEqual( ...@@ -594,10 +595,6 @@ extern operator '>=' macro BranchIfNumberGreaterThanOrEqual(
Number, Number): never Number, Number): never
labels Taken, NotTaken; 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;
// The type of all tagged values that can safely be compared with WordEqual. // The type of all tagged values that can safely be compared with WordEqual.
type TaggedWithIdentity = type TaggedWithIdentity =
JSReceiver | FixedArrayBase | Oddball | Map | EmptyString; JSReceiver | FixedArrayBase | Oddball | Map | EmptyString;
...@@ -637,6 +634,7 @@ extern operator '|' macro WordOr(intptr, intptr): intptr; ...@@ -637,6 +634,7 @@ extern operator '|' macro WordOr(intptr, intptr): intptr;
extern operator '+' macro UintPtrAdd(uintptr, uintptr): uintptr; extern operator '+' macro UintPtrAdd(uintptr, uintptr): uintptr;
extern operator '-' macro UintPtrSub(uintptr, uintptr): uintptr; extern operator '-' macro UintPtrSub(uintptr, uintptr): uintptr;
extern operator '<<' macro WordShl(uintptr, uintptr): uintptr;
extern operator '>>>' macro WordShr(uintptr, uintptr): uintptr; extern operator '>>>' macro WordShr(uintptr, uintptr): uintptr;
extern operator '&' macro WordAnd(uintptr, uintptr): uintptr; extern operator '&' macro WordAnd(uintptr, uintptr): uintptr;
extern operator '|' macro WordOr(uintptr, uintptr): uintptr; extern operator '|' macro WordOr(uintptr, uintptr): uintptr;
...@@ -988,6 +986,11 @@ extern macro LoadNativeContext(Context): NativeContext; ...@@ -988,6 +986,11 @@ extern macro LoadNativeContext(Context): NativeContext;
extern macro LoadJSArrayElementsMap(constexpr ElementsKind, Context): Map; extern macro LoadJSArrayElementsMap(constexpr ElementsKind, Context): Map;
extern macro LoadJSArrayElementsMap(ElementsKind, Context): Map; extern macro LoadJSArrayElementsMap(ElementsKind, Context): Map;
extern macro ChangeNonnegativeNumberToUintPtr(Number): uintptr; extern macro ChangeNonnegativeNumberToUintPtr(Number): uintptr;
extern macro TryNumberToUintPtr(Number): uintptr labels IfNegative;
macro TryUintPtrToPositiveSmi(ui: uintptr): PositiveSmi labels IfOverflow {
if (ui > kSmiMaxValue) goto IfOverflow;
return %RawDownCast<PositiveSmi>(SmiTag(Signed(ui)));
}
extern macro NumberConstant(constexpr float64): Number; extern macro NumberConstant(constexpr float64): Number;
extern macro NumberConstant(constexpr int32): Number; extern macro NumberConstant(constexpr int32): Number;
...@@ -1138,6 +1141,9 @@ Convert<uint32, uintptr>(ui: uintptr): uint32 { ...@@ -1138,6 +1141,9 @@ Convert<uint32, uintptr>(ui: uintptr): uint32 {
Convert<intptr, Smi>(s: Smi): intptr { Convert<intptr, Smi>(s: Smi): intptr {
return SmiUntag(s); return SmiUntag(s);
} }
Convert<uintptr, PositiveSmi>(ps: PositiveSmi): uintptr {
return Unsigned(SmiUntag(ps));
}
Convert<intptr, uintptr>(ui: uintptr): intptr { Convert<intptr, uintptr>(ui: uintptr): intptr {
const i = Signed(ui); const i = Signed(ui);
assert(i >= 0); assert(i >= 0);
......
...@@ -44,6 +44,15 @@ TNode<Map> TypedArrayBuiltinsAssembler::LoadMapForType( ...@@ -44,6 +44,15 @@ TNode<Map> TypedArrayBuiltinsAssembler::LoadMapForType(
return var_typed_map.value(); return var_typed_map.value();
} }
TNode<BoolT> TypedArrayBuiltinsAssembler::IsMockArrayBufferAllocatorFlag() {
TNode<Word32T> flag_value = UncheckedCast<Word32T>(Load(
MachineType::Uint8(),
ExternalConstant(
ExternalReference::address_of_mock_arraybuffer_allocator_flag())));
return Word32NotEqual(Word32And(flag_value, Int32Constant(0xFF)),
Int32Constant(0));
}
// The byte_offset can be higher than Smi range, in which case to perform the // The byte_offset can be higher than Smi range, in which case to perform the
// pointer arithmetic necessary to calculate external_pointer, converting // pointer arithmetic necessary to calculate external_pointer, converting
// byte_offset to an intptr is more difficult. The max byte_offset is 8 * MaxSmi // byte_offset to an intptr is more difficult. The max byte_offset is 8 * MaxSmi
...@@ -52,9 +61,22 @@ TNode<Map> TypedArrayBuiltinsAssembler::LoadMapForType( ...@@ -52,9 +61,22 @@ TNode<Map> TypedArrayBuiltinsAssembler::LoadMapForType(
// bit platforms could theoretically have an offset up to 2^35 - 1, so we may // bit platforms could theoretically have an offset up to 2^35 - 1, so we may
// need to convert the float heap number to an intptr. // need to convert the float heap number to an intptr.
TNode<UintPtrT> TypedArrayBuiltinsAssembler::CalculateExternalPointer( TNode<UintPtrT> TypedArrayBuiltinsAssembler::CalculateExternalPointer(
TNode<UintPtrT> backing_store, TNode<Number> byte_offset) { TNode<UintPtrT> backing_store, TNode<UintPtrT> byte_offset) {
return Unsigned( TNode<UintPtrT> external_pointer = UintPtrAdd(backing_store, byte_offset);
IntPtrAdd(backing_store, ChangeNonnegativeNumberToUintPtr(byte_offset)));
#ifdef DEBUG
// Assert no overflow has occurred. Only assert if the mock array buffer
// allocator is NOT used. When the mock array buffer is used, impossibly
// large allocations are allowed that would erroneously cause an overflow and
// this assertion to fail.
Label next(this);
GotoIf(IsMockArrayBufferAllocatorFlag(), &next);
CSA_ASSERT(this, UintPtrGreaterThanOrEqual(external_pointer, backing_store));
Goto(&next);
BIND(&next);
#endif // DEBUG
return external_pointer;
} }
// Setup the TypedArray which is under construction. // Setup the TypedArray which is under construction.
...@@ -85,7 +107,7 @@ void TypedArrayBuiltinsAssembler::AttachBuffer(TNode<JSTypedArray> holder, ...@@ -85,7 +107,7 @@ void TypedArrayBuiltinsAssembler::AttachBuffer(TNode<JSTypedArray> holder,
TNode<JSArrayBuffer> buffer, TNode<JSArrayBuffer> buffer,
TNode<Map> map, TNode<Map> map,
TNode<Smi> length, TNode<Smi> length,
TNode<Number> byte_offset) { TNode<UintPtrT> byte_offset) {
CSA_ASSERT(this, TaggedIsPositiveSmi(length)); CSA_ASSERT(this, TaggedIsPositiveSmi(length));
StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer); StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
...@@ -195,29 +217,6 @@ Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) { ...@@ -195,29 +217,6 @@ Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) {
return LoadFixedTypedArrayBackingStore(CAST(elements)); return LoadFixedTypedArrayBackingStore(CAST(elements));
} }
TNode<BoolT> TypedArrayBuiltinsAssembler::ByteLengthIsValid(
TNode<Number> byte_length) {
Label smi(this), done(this);
TVARIABLE(BoolT, is_valid);
GotoIf(TaggedIsSmi(byte_length), &smi);
TNode<Float64T> float_value = LoadHeapNumberValue(CAST(byte_length));
TNode<Float64T> max_byte_length_double =
Float64Constant(FixedTypedArrayBase::kMaxByteLength);
is_valid = Float64LessThanOrEqual(float_value, max_byte_length_double);
Goto(&done);
BIND(&smi);
TNode<IntPtrT> max_byte_length =
IntPtrConstant(FixedTypedArrayBase::kMaxByteLength);
is_valid =
UintPtrLessThanOrEqual(SmiUntag(CAST(byte_length)), max_byte_length);
Goto(&done);
BIND(&done);
return is_valid.value();
}
TF_BUILTIN(TypedArrayBaseConstructor, TypedArrayBuiltinsAssembler) { TF_BUILTIN(TypedArrayBaseConstructor, TypedArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext)); TNode<Context> context = CAST(Parameter(Descriptor::kContext));
ThrowTypeError(context, MessageTemplate::kConstructAbstractClass, ThrowTypeError(context, MessageTemplate::kConstructAbstractClass,
...@@ -337,7 +336,7 @@ TypedArrayBuiltinsFromDSLAssembler::TypedArrayElementsInfo ...@@ -337,7 +336,7 @@ TypedArrayBuiltinsFromDSLAssembler::TypedArrayElementsInfo
TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo( TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
TNode<JSTypedArray> typed_array) { TNode<JSTypedArray> typed_array) {
TNode<Int32T> elements_kind = LoadElementsKind(typed_array); TNode<Int32T> elements_kind = LoadElementsKind(typed_array);
TVARIABLE(Smi, var_element_size); TVARIABLE(UintPtrT, var_size_log2);
TVARIABLE(Map, var_map); TVARIABLE(Map, var_map);
ReadOnlyRoots roots(isolate()); ReadOnlyRoots roots(isolate());
...@@ -345,14 +344,14 @@ TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo( ...@@ -345,14 +344,14 @@ TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
elements_kind, elements_kind,
[&](ElementsKind kind, int size, int typed_array_fun_index) { [&](ElementsKind kind, int size, int typed_array_fun_index) {
DCHECK_GT(size, 0); DCHECK_GT(size, 0);
var_element_size = SmiConstant(size); var_size_log2 = UintPtrConstant(ElementsKindToShiftSize(kind));
Handle<Map> map(roots.MapForFixedTypedArray(kind), isolate()); Handle<Map> map(roots.MapForFixedTypedArray(kind), isolate());
var_map = HeapConstant(map); var_map = HeapConstant(map);
}); });
return TypedArrayBuiltinsFromDSLAssembler::TypedArrayElementsInfo{ return TypedArrayBuiltinsFromDSLAssembler::TypedArrayElementsInfo{
var_element_size.value(), var_map.value(), elements_kind}; var_size_log2.value(), var_map.value(), elements_kind};
} }
TNode<JSFunction> TypedArrayBuiltinsAssembler::GetDefaultConstructor( TNode<JSFunction> TypedArrayBuiltinsAssembler::GetDefaultConstructor(
......
...@@ -38,7 +38,7 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler { ...@@ -38,7 +38,7 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<UintPtrT> byte_length); TNode<UintPtrT> byte_length);
void AttachBuffer(TNode<JSTypedArray> holder, TNode<JSArrayBuffer> buffer, void AttachBuffer(TNode<JSTypedArray> holder, TNode<JSArrayBuffer> buffer,
TNode<Map> map, TNode<Smi> length, TNode<Map> map, TNode<Smi> length,
TNode<Number> byte_offset); TNode<UintPtrT> byte_offset);
TNode<JSArrayBuffer> AllocateEmptyOnHeapBuffer(TNode<Context> context, TNode<JSArrayBuffer> AllocateEmptyOnHeapBuffer(TNode<Context> context,
TNode<JSTypedArray> holder, TNode<JSTypedArray> holder,
...@@ -49,10 +49,10 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler { ...@@ -49,10 +49,10 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<Number> length); TNode<Number> length);
TNode<Map> LoadMapForType(TNode<JSTypedArray> array); TNode<Map> LoadMapForType(TNode<JSTypedArray> array);
TNode<BoolT> IsMockArrayBufferAllocatorFlag();
TNode<UintPtrT> CalculateExternalPointer(TNode<UintPtrT> backing_store, TNode<UintPtrT> CalculateExternalPointer(TNode<UintPtrT> backing_store,
TNode<Number> byte_offset); TNode<UintPtrT> byte_offset);
Node* LoadDataPtr(Node* typed_array); Node* LoadDataPtr(Node* typed_array);
TNode<BoolT> ByteLengthIsValid(TNode<Number> byte_length);
// Returns true if kind is either UINT8_ELEMENTS or UINT8_CLAMPED_ELEMENTS. // Returns true if kind is either UINT8_ELEMENTS or UINT8_CLAMPED_ELEMENTS.
TNode<Word32T> IsUint8ElementsKind(TNode<Word32T> kind); TNode<Word32T> IsUint8ElementsKind(TNode<Word32T> kind);
......
...@@ -14,15 +14,12 @@ namespace typed_array_createtypedarray { ...@@ -14,15 +14,12 @@ namespace typed_array_createtypedarray {
implicit context: Context)(JSTypedArray, uintptr): JSArrayBuffer; implicit context: Context)(JSTypedArray, uintptr): JSArrayBuffer;
extern macro TypedArrayBuiltinsAssembler::AllocateOnHeapElements( extern macro TypedArrayBuiltinsAssembler::AllocateOnHeapElements(
Map, intptr, Number): FixedTypedArrayBase; Map, intptr, Number): FixedTypedArrayBase;
extern macro TypedArrayBuiltinsAssembler::ByteLengthIsValid(Number): bool;
extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
ElementsKind): typed_array::TypedArrayElementsInfo;
extern macro TypedArrayBuiltinsAssembler::IsSharedArrayBuffer(JSArrayBuffer): extern macro TypedArrayBuiltinsAssembler::IsSharedArrayBuffer(JSArrayBuffer):
bool; bool;
extern macro TypedArrayBuiltinsAssembler::SetupTypedArray( extern macro TypedArrayBuiltinsAssembler::SetupTypedArray(
JSTypedArray, Smi, uintptr, uintptr): void; JSTypedArray, Smi, uintptr, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::AttachBuffer( extern macro TypedArrayBuiltinsAssembler::AttachBuffer(
JSTypedArray, JSArrayBuffer, Map, Smi, Number): void; JSTypedArray, JSArrayBuffer, Map, Smi, uintptr): void;
extern runtime ThrowInvalidTypedArrayAlignment(implicit context: Context)( extern runtime ThrowInvalidTypedArrayAlignment(implicit context: Context)(
Map, String): never; Map, String): never;
...@@ -39,10 +36,8 @@ namespace typed_array_createtypedarray { ...@@ -39,10 +36,8 @@ namespace typed_array_createtypedarray {
initialize: constexpr bool, typedArray: JSTypedArray, length: PositiveSmi, initialize: constexpr bool, typedArray: JSTypedArray, length: PositiveSmi,
elementsInfo: typed_array::TypedArrayElementsInfo, elementsInfo: typed_array::TypedArrayElementsInfo,
bufferConstructor: JSReceiver): Object { bufferConstructor: JSReceiver): Object {
assert(Is<PositiveSmi>(length)); const byteLength = elementsInfo.CalculateByteLength(length);
const byteLengthNum = Convert<Number>(byteLength);
const byteLengthNum = SmiMul(length, elementsInfo.size);
const byteLength = Convert<uintptr>(byteLengthNum);
const defaultConstructor = GetArrayBufferFunction(); const defaultConstructor = GetArrayBufferFunction();
try { try {
...@@ -76,7 +71,7 @@ namespace typed_array_createtypedarray { ...@@ -76,7 +71,7 @@ namespace typed_array_createtypedarray {
} }
label AttachBuffer(bufferObj: Object) { label AttachBuffer(bufferObj: Object) {
const buffer = Cast<JSArrayBuffer>(bufferObj) otherwise unreachable; const buffer = Cast<JSArrayBuffer>(bufferObj) otherwise unreachable;
const byteOffset: Smi = 0; const byteOffset: uintptr = 0;
AttachBuffer(typedArray, buffer, elementsInfo.map, length, byteOffset); AttachBuffer(typedArray, buffer, elementsInfo.map, length, byteOffset);
} }
...@@ -86,18 +81,6 @@ namespace typed_array_createtypedarray { ...@@ -86,18 +81,6 @@ namespace typed_array_createtypedarray {
return Undefined; return Undefined;
} }
macro TypedArrayInitializeWithBuffer(
typedArray: JSTypedArray, length: PositiveSmi, buffer: JSArrayBuffer,
elementsInfo: typed_array::TypedArrayElementsInfo, byteOffset: Number) {
const byteLength: Number = SmiMul(length, elementsInfo.size);
SetupTypedArray(
typedArray, length, Convert<uintptr>(byteOffset),
Convert<uintptr>(byteLength));
AttachBuffer(typedArray, buffer, elementsInfo.map, length, byteOffset);
}
// 22.2.4.2 TypedArray ( length ) // 22.2.4.2 TypedArray ( length )
// ES #sec-typedarray-length // ES #sec-typedarray-length
transitioning macro ConstructByLength(implicit context: Context)( transitioning macro ConstructByLength(implicit context: Context)(
...@@ -141,10 +124,9 @@ namespace typed_array_createtypedarray { ...@@ -141,10 +124,9 @@ namespace typed_array_createtypedarray {
goto IfSlow; goto IfSlow;
} else if (length > 0) { } else if (length > 0) {
const byteLength: Number = SmiMul(length, elementsInfo.size); const byteLength = elementsInfo.CalculateByteLength(length);
assert(ByteLengthIsValid(byteLength)); assert(byteLength <= kTypedArrayMaxByteLength);
typed_array::CallCMemcpy( typed_array::CallCMemcpy(typedArray.data_ptr, src.data_ptr, byteLength);
typedArray.data_ptr, src.data_ptr, Convert<uintptr>(byteLength));
} }
} }
label IfSlow deferred { label IfSlow deferred {
...@@ -187,36 +169,27 @@ namespace typed_array_createtypedarray { ...@@ -187,36 +169,27 @@ namespace typed_array_createtypedarray {
goto IfConstructByArrayLike(srcTypedArray, length, bufferConstructor); goto IfConstructByArrayLike(srcTypedArray, length, 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 ) // 22.2.4.5 TypedArray ( buffer, byteOffset, length )
// ES #sec-typedarray-buffer-byteoffset-length // ES #sec-typedarray-buffer-byteoffset-length
macro ConstructByArrayBuffer(implicit context: Context)( macro ConstructByArrayBuffer(implicit context: Context)(
typedArray: JSTypedArray, buffer: JSArrayBuffer, byteOffset: Object, typedArray: JSTypedArray, buffer: JSArrayBuffer, byteOffset: Object,
length: Object, elementsInfo: typed_array::TypedArrayElementsInfo): void { length: Object, elementsInfo: typed_array::TypedArrayElementsInfo): void {
try { try {
let offset: Number = FromConstexpr<Smi>(0); let offset: uintptr = 0;
if (byteOffset != Undefined) { if (byteOffset != Undefined) {
// 6. Let offset be ? ToIndex(byteOffset). // 6. Let offset be ? ToIndex(byteOffset).
offset = ToInteger_Inline(context, byteOffset, kTruncateMinusZero); offset = TryNumberToUintPtr(
if (offset < 0) goto IfInvalidOffset; ToInteger_Inline(context, byteOffset, kTruncateMinusZero))
otherwise goto IfInvalidOffset;
// 7. If offset modulo elementSize ≠ 0, throw a RangeError exception. // 7. If offset modulo elementSize ≠ 0, throw a RangeError exception.
if (IsUnaligned(offset, elementsInfo.size)) { if (elementsInfo.IsUnaligned(offset)) {
goto IfInvalidAlignment('start offset'); goto IfInvalidAlignment('start offset');
} }
} }
let newLength: PositiveSmi = 0; let newLength: PositiveSmi = 0;
let newByteLength: uintptr;
// 8. If length is present and length is not undefined, then // 8. If length is present and length is not undefined, then
if (length != Undefined) { if (length != Undefined) {
// a. Let newLength be ? ToIndex(length). // a. Let newLength be ? ToIndex(length).
...@@ -229,13 +202,13 @@ namespace typed_array_createtypedarray { ...@@ -229,13 +202,13 @@ namespace typed_array_createtypedarray {
} }
// 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. // 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
const bufferByteLength: Number = Convert<Number>(buffer.byte_length); const bufferByteLength: uintptr = buffer.byte_length;
// 11. If length is either not present or undefined, then // 11. If length is either not present or undefined, then
if (length == Undefined) { if (length == Undefined) {
// a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError // a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError
// exception. // exception.
if (IsUnaligned(bufferByteLength, elementsInfo.size)) { if (elementsInfo.IsUnaligned(bufferByteLength)) {
goto IfInvalidAlignment('byte length'); goto IfInvalidAlignment('byte length');
} }
...@@ -245,25 +218,24 @@ namespace typed_array_createtypedarray { ...@@ -245,25 +218,24 @@ namespace typed_array_createtypedarray {
// Spec step 16 length calculated here to avoid recalculating the length // Spec step 16 length calculated here to avoid recalculating the length
// in the step 12 branch. // in the step 12 branch.
newLength = ToSmiIndex( newByteLength = bufferByteLength - offset;
Divide((Subtract(bufferByteLength, offset)), elementsInfo.size)) newLength = elementsInfo.CalculateLength(newByteLength)
otherwise IfInvalidLength; otherwise IfInvalidOffset;
// 12. Else, // 12. Else,
} else { } else {
// a. Let newByteLength be newLength × elementSize. // a. Let newByteLength be newLength × elementSize.
const newByteLength: Number = SmiMul(newLength, elementsInfo.size); newByteLength = elementsInfo.CalculateByteLength(newLength);
// b. If offset + newByteLength > bufferByteLength, throw a RangeError // b. If offset + newByteLength > bufferByteLength, throw a RangeError
// exception. // exception.
const difference: Number = if ((bufferByteLength < newByteLength) ||
Cast<Number>(Subtract(bufferByteLength, newByteLength)) (offset > bufferByteLength - newByteLength))
otherwise unreachable; goto IfInvalidLength;
if (difference < offset) goto IfInvalidLength;
} }
TypedArrayInitializeWithBuffer( SetupTypedArray(typedArray, newLength, offset, newByteLength);
typedArray, newLength, buffer, elementsInfo, offset); AttachBuffer(typedArray, buffer, elementsInfo.map, newLength, offset);
} }
label IfInvalidAlignment(problemString: String) deferred { label IfInvalidAlignment(problemString: String) deferred {
ThrowInvalidTypedArrayAlignment(typedArray.map, problemString); ThrowInvalidTypedArrayAlignment(typedArray.map, problemString);
......
...@@ -6,11 +6,38 @@ ...@@ -6,11 +6,38 @@
namespace typed_array { namespace typed_array {
struct TypedArrayElementsInfo { struct TypedArrayElementsInfo {
size: PositiveSmi; // Calculates the number of bytes required for specified number of elements.
CalculateByteLength(length: uintptr): uintptr {
const byteLength = length << this.sizeLog2;
// Verify no overflow has ocurred.
assert(byteLength >>> this.sizeLog2 == length);
return byteLength;
}
CalculateByteLength(length: PositiveSmi): uintptr {
return this.CalculateByteLength(Convert<uintptr>(length));
}
// Calculates the maximum number of elements supported by a specified number
// of bytes.
CalculateLength(byteLength: uintptr): PositiveSmi labels IfInvalid {
return TryUintPtrToPositiveSmi(byteLength >>> this.sizeLog2)
otherwise IfInvalid;
}
// Determines if `bytes` (byte offset or length) cannot be evenly divided by
// element size.
IsUnaligned(bytes: uintptr): bool {
// Exploits the fact the element size is a power of 2. Determining whether
// there is remainder (not aligned) can be achieved efficiently with bit
// masking. Shift is safe as sizeLog2 can be 3 at most (see
// ElementsKindToShiftSize).
return (bytes & ((1 << this.sizeLog2) - 1)) != 0;
}
sizeLog2: uintptr;
map: Map; map: Map;
kind: ElementsKind; kind: ElementsKind;
} }
extern runtime TypedArraySortFast(Context, Object): JSTypedArray; extern runtime TypedArraySortFast(Context, Object): JSTypedArray;
extern macro TypedArrayBuiltinsAssembler::ValidateTypedArray( extern macro TypedArrayBuiltinsAssembler::ValidateTypedArray(
Context, Object, constexpr string): JSTypedArray; Context, Object, constexpr string): JSTypedArray;
......
...@@ -5701,20 +5701,28 @@ TNode<Float64T> CodeStubAssembler::ChangeNumberToFloat64( ...@@ -5701,20 +5701,28 @@ TNode<Float64T> CodeStubAssembler::ChangeNumberToFloat64(
return result.value(); return result.value();
} }
TNode<UintPtrT> CodeStubAssembler::ChangeNonnegativeNumberToUintPtr( TNode<UintPtrT> CodeStubAssembler::TryNumberToUintPtr(TNode<Number> value,
TNode<Number> value) { Label* if_negative) {
TVARIABLE(UintPtrT, result); TVARIABLE(UintPtrT, result);
Label done(this, &result); Label done(this, &result);
Branch(TaggedIsSmi(value), Branch(TaggedIsSmi(value),
[&] { [&] {
TNode<Smi> value_smi = CAST(value); TNode<Smi> value_smi = CAST(value);
CSA_SLOW_ASSERT(this, SmiLessThan(SmiConstant(-1), value_smi)); if (if_negative == nullptr) {
CSA_SLOW_ASSERT(this, SmiLessThan(SmiConstant(-1), value_smi));
} else {
GotoIfNot(TaggedIsPositiveSmi(value), if_negative);
}
result = UncheckedCast<UintPtrT>(SmiToIntPtr(value_smi)); result = UncheckedCast<UintPtrT>(SmiToIntPtr(value_smi));
Goto(&done); Goto(&done);
}, },
[&] { [&] {
TNode<HeapNumber> value_hn = CAST(value); TNode<HeapNumber> value_hn = CAST(value);
result = ChangeFloat64ToUintPtr(LoadHeapNumberValue(value_hn)); TNode<Float64T> value = LoadHeapNumberValue(value_hn);
if (if_negative != nullptr) {
GotoIf(Float64LessThan(value, Float64Constant(0.0)), if_negative);
}
result = ChangeFloat64ToUintPtr(value);
Goto(&done); Goto(&done);
}); });
......
...@@ -1938,7 +1938,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -1938,7 +1938,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Number> ChangeUintPtrToTagged(TNode<UintPtrT> value); TNode<Number> ChangeUintPtrToTagged(TNode<UintPtrT> value);
TNode<Uint32T> ChangeNumberToUint32(TNode<Number> value); TNode<Uint32T> ChangeNumberToUint32(TNode<Number> value);
TNode<Float64T> ChangeNumberToFloat64(SloppyTNode<Number> value); TNode<Float64T> ChangeNumberToFloat64(SloppyTNode<Number> value);
TNode<UintPtrT> ChangeNonnegativeNumberToUintPtr(TNode<Number> value); TNode<UintPtrT> TryNumberToUintPtr(TNode<Number> value, Label* if_negative);
TNode<UintPtrT> ChangeNonnegativeNumberToUintPtr(TNode<Number> value) {
return TryNumberToUintPtr(value, nullptr);
}
void TaggedToNumeric(Node* context, Node* value, Label* done, void TaggedToNumeric(Node* context, Node* value, Label* done,
Variable* var_numeric); Variable* var_numeric);
......
...@@ -417,6 +417,11 @@ ExternalReference ExternalReference::address_of_min_int() { ...@@ -417,6 +417,11 @@ ExternalReference ExternalReference::address_of_min_int() {
return ExternalReference(reinterpret_cast<Address>(&double_min_int_constant)); return ExternalReference(reinterpret_cast<Address>(&double_min_int_constant));
} }
ExternalReference
ExternalReference::address_of_mock_arraybuffer_allocator_flag() {
return ExternalReference(&FLAG_mock_arraybuffer_allocator);
}
ExternalReference ExternalReference::address_of_runtime_stats_flag() { ExternalReference ExternalReference::address_of_runtime_stats_flag() {
return ExternalReference(&FLAG_runtime_stats); return ExternalReference(&FLAG_runtime_stats);
} }
......
...@@ -92,6 +92,8 @@ class StatsCounter; ...@@ -92,6 +92,8 @@ class StatsCounter;
V(address_of_harmony_await_optimization_flag, \ V(address_of_harmony_await_optimization_flag, \
"FLAG_harmony_await_optimization") \ "FLAG_harmony_await_optimization") \
V(address_of_min_int, "LDoubleConstant::min_int") \ V(address_of_min_int, "LDoubleConstant::min_int") \
V(address_of_mock_arraybuffer_allocator_flag, \
"FLAG_mock_arraybuffer_allocator") \
V(address_of_one_half, "LDoubleConstant::one_half") \ V(address_of_one_half, "LDoubleConstant::one_half") \
V(address_of_runtime_stats_flag, "FLAG_runtime_stats") \ V(address_of_runtime_stats_flag, "FLAG_runtime_stats") \
V(address_of_the_hole_nan, "the_hole_nan") \ V(address_of_the_hole_nan, "the_hole_nan") \
......
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