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

This reverts commit c9ef0405c7.

Reason for revert: https://crbug.com/932034

Original change's description:
> [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: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Simon Zünd <szuend@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#59531}

TBR=peter.wm.wong@gmail.com,jgruber@chromium.org,petermarshall@chromium.org,szuend@chromium.org

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: v8:7161, chromium:932034
Change-Id: I3da95447ce34f84d01629d2791868f3adcdfb387
Reviewed-on: https://chromium-review.googlesource.com/c/1475764
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59645}
This commit is contained in:
Jakob Gruber 2019-02-18 08:18:11 +00:00 committed by Commit Bot
parent 61c344e600
commit ced2e4eec5
9 changed files with 96 additions and 118 deletions

View File

@ -340,7 +340,6 @@ const kTypedArrayMaxByteLength:
const V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP:
constexpr int31 generates 'V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP';
const kMaxSafeInteger: constexpr float64 generates 'kMaxSafeInteger';
const kSmiMaxValue: constexpr uintptr generates 'kSmiMaxValue';
const kStringMaxLength: constexpr int31 generates 'String::kMaxLength';
const kFixedArrayMaxLength:
constexpr int31 generates 'FixedArray::kMaxLength';
@ -594,6 +593,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;
// The type of all tagged values that can safely be compared with WordEqual.
type TaggedWithIdentity =
JSReceiver | FixedArrayBase | Oddball | Map | EmptyString;
@ -633,7 +636,6 @@ extern operator '|' macro WordOr(intptr, intptr): intptr;
extern operator '+' macro UintPtrAdd(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 WordAnd(uintptr, uintptr): uintptr;
extern operator '|' macro WordOr(uintptr, uintptr): uintptr;
@ -985,11 +987,6 @@ extern macro LoadNativeContext(Context): NativeContext;
extern macro LoadJSArrayElementsMap(constexpr ElementsKind, Context): Map;
extern macro LoadJSArrayElementsMap(ElementsKind, Context): Map;
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 int32): Number;
@ -1144,9 +1141,6 @@ Convert<uint32, uintptr>(ui: uintptr): uint32 {
Convert<intptr, Smi>(s: Smi): intptr {
return SmiUntag(s);
}
Convert<uintptr, PositiveSmi>(ps: PositiveSmi): uintptr {
return Unsigned(SmiUntag(ps));
}
Convert<intptr, uintptr>(ui: uintptr): intptr {
const i = Signed(ui);
assert(i >= 0);

View File

@ -44,15 +44,6 @@ TNode<Map> TypedArrayBuiltinsAssembler::LoadMapForType(
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
// pointer arithmetic necessary to calculate external_pointer, converting
// byte_offset to an intptr is more difficult. The max byte_offset is 8 * MaxSmi
@ -61,22 +52,9 @@ TNode<BoolT> TypedArrayBuiltinsAssembler::IsMockArrayBufferAllocatorFlag() {
// 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.
TNode<UintPtrT> TypedArrayBuiltinsAssembler::CalculateExternalPointer(
TNode<UintPtrT> backing_store, TNode<UintPtrT> byte_offset) {
TNode<UintPtrT> external_pointer = UintPtrAdd(backing_store, 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;
TNode<UintPtrT> backing_store, TNode<Number> byte_offset) {
return Unsigned(
IntPtrAdd(backing_store, ChangeNonnegativeNumberToUintPtr(byte_offset)));
}
// Setup the TypedArray which is under construction.
@ -107,7 +85,7 @@ void TypedArrayBuiltinsAssembler::AttachBuffer(TNode<JSTypedArray> holder,
TNode<JSArrayBuffer> buffer,
TNode<Map> map,
TNode<Smi> length,
TNode<UintPtrT> byte_offset) {
TNode<Number> byte_offset) {
CSA_ASSERT(this, TaggedIsPositiveSmi(length));
StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
@ -217,6 +195,29 @@ Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) {
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) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
ThrowTypeError(context, MessageTemplate::kConstructAbstractClass,
@ -336,7 +337,7 @@ TypedArrayBuiltinsFromDSLAssembler::TypedArrayElementsInfo
TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
TNode<JSTypedArray> typed_array) {
TNode<Int32T> elements_kind = LoadElementsKind(typed_array);
TVARIABLE(UintPtrT, var_size_log2);
TVARIABLE(Smi, var_element_size);
TVARIABLE(Map, var_map);
ReadOnlyRoots roots(isolate());
@ -344,14 +345,14 @@ TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
elements_kind,
[&](ElementsKind kind, int size, int typed_array_fun_index) {
DCHECK_GT(size, 0);
var_size_log2 = UintPtrConstant(ElementsKindToShiftSize(kind));
var_element_size = SmiConstant(size);
Handle<Map> map(roots.MapForFixedTypedArray(kind), isolate());
var_map = HeapConstant(map);
});
return TypedArrayBuiltinsFromDSLAssembler::TypedArrayElementsInfo{
var_size_log2.value(), var_map.value(), elements_kind};
var_element_size.value(), var_map.value(), elements_kind};
}
TNode<JSFunction> TypedArrayBuiltinsAssembler::GetDefaultConstructor(

View File

@ -38,7 +38,7 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<UintPtrT> byte_length);
void AttachBuffer(TNode<JSTypedArray> holder, TNode<JSArrayBuffer> buffer,
TNode<Map> map, TNode<Smi> length,
TNode<UintPtrT> byte_offset);
TNode<Number> byte_offset);
TNode<JSArrayBuffer> AllocateEmptyOnHeapBuffer(TNode<Context> context,
TNode<JSTypedArray> holder,
@ -49,10 +49,10 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<Number> length);
TNode<Map> LoadMapForType(TNode<JSTypedArray> array);
TNode<BoolT> IsMockArrayBufferAllocatorFlag();
TNode<UintPtrT> CalculateExternalPointer(TNode<UintPtrT> backing_store,
TNode<UintPtrT> byte_offset);
TNode<Number> byte_offset);
Node* LoadDataPtr(Node* typed_array);
TNode<BoolT> ByteLengthIsValid(TNode<Number> byte_length);
// Returns true if kind is either UINT8_ELEMENTS or UINT8_CLAMPED_ELEMENTS.
TNode<Word32T> IsUint8ElementsKind(TNode<Word32T> kind);

View File

@ -14,12 +14,15 @@ namespace typed_array_createtypedarray {
implicit context: Context)(JSTypedArray, uintptr): JSArrayBuffer;
extern macro TypedArrayBuiltinsAssembler::AllocateOnHeapElements(
Map, intptr, Number): FixedTypedArrayBase;
extern macro TypedArrayBuiltinsAssembler::ByteLengthIsValid(Number): bool;
extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
ElementsKind): typed_array::TypedArrayElementsInfo;
extern macro TypedArrayBuiltinsAssembler::IsSharedArrayBuffer(JSArrayBuffer):
bool;
extern macro TypedArrayBuiltinsAssembler::SetupTypedArray(
JSTypedArray, Smi, uintptr, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::AttachBuffer(
JSTypedArray, JSArrayBuffer, Map, Smi, uintptr): void;
JSTypedArray, JSArrayBuffer, Map, Smi, Number): void;
extern runtime ThrowInvalidTypedArrayAlignment(implicit context: Context)(
Map, String): never;
@ -36,8 +39,10 @@ namespace typed_array_createtypedarray {
initialize: constexpr bool, typedArray: JSTypedArray, length: PositiveSmi,
elementsInfo: typed_array::TypedArrayElementsInfo,
bufferConstructor: JSReceiver): Object {
const byteLength = elementsInfo.CalculateByteLength(length);
const byteLengthNum = Convert<Number>(byteLength);
assert(Is<PositiveSmi>(length));
const byteLengthNum = SmiMul(length, elementsInfo.size);
const byteLength = Convert<uintptr>(byteLengthNum);
const defaultConstructor = GetArrayBufferFunction();
try {
@ -71,7 +76,7 @@ namespace typed_array_createtypedarray {
}
label AttachBuffer(bufferObj: Object) {
const buffer = Cast<JSArrayBuffer>(bufferObj) otherwise unreachable;
const byteOffset: uintptr = 0;
const byteOffset: Smi = 0;
AttachBuffer(typedArray, buffer, elementsInfo.map, length, byteOffset);
}
@ -81,6 +86,18 @@ namespace typed_array_createtypedarray {
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 )
// ES #sec-typedarray-length
transitioning macro ConstructByLength(implicit context: Context)(
@ -124,9 +141,10 @@ namespace typed_array_createtypedarray {
goto IfSlow;
} else if (length > 0) {
const byteLength = elementsInfo.CalculateByteLength(length);
assert(byteLength <= kTypedArrayMaxByteLength);
typed_array::CallCMemcpy(typedArray.data_ptr, src.data_ptr, byteLength);
const byteLength: Number = SmiMul(length, elementsInfo.size);
assert(ByteLengthIsValid(byteLength));
typed_array::CallCMemcpy(
typedArray.data_ptr, src.data_ptr, Convert<uintptr>(byteLength));
}
}
label IfSlow deferred {
@ -169,27 +187,36 @@ namespace typed_array_createtypedarray {
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 )
// ES #sec-typedarray-buffer-byteoffset-length
macro ConstructByArrayBuffer(implicit context: Context)(
typedArray: JSTypedArray, buffer: JSArrayBuffer, byteOffset: Object,
length: Object, elementsInfo: typed_array::TypedArrayElementsInfo): void {
try {
let offset: uintptr = 0;
let offset: Number = FromConstexpr<Smi>(0);
if (byteOffset != Undefined) {
// 6. Let offset be ? ToIndex(byteOffset).
offset = TryNumberToUintPtr(
ToInteger_Inline(context, byteOffset, kTruncateMinusZero))
otherwise goto IfInvalidOffset;
offset = ToInteger_Inline(context, byteOffset, kTruncateMinusZero);
if (offset < 0) goto IfInvalidOffset;
// 7. If offset modulo elementSize ≠ 0, throw a RangeError exception.
if (elementsInfo.IsUnaligned(offset)) {
if (IsUnaligned(offset, elementsInfo.size)) {
goto IfInvalidAlignment('start offset');
}
}
let newLength: PositiveSmi = 0;
let newByteLength: uintptr;
// 8. If length is present and length is not undefined, then
if (length != Undefined) {
// a. Let newLength be ? ToIndex(length).
@ -202,13 +229,13 @@ namespace typed_array_createtypedarray {
}
// 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
const bufferByteLength: uintptr = buffer.byte_length;
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 (elementsInfo.IsUnaligned(bufferByteLength)) {
if (IsUnaligned(bufferByteLength, elementsInfo.size)) {
goto IfInvalidAlignment('byte length');
}
@ -218,24 +245,25 @@ namespace typed_array_createtypedarray {
// Spec step 16 length calculated here to avoid recalculating the length
// in the step 12 branch.
newByteLength = bufferByteLength - offset;
newLength = elementsInfo.CalculateLength(newByteLength)
otherwise IfInvalidOffset;
newLength = ToSmiIndex(
Divide((Subtract(bufferByteLength, offset)), elementsInfo.size))
otherwise IfInvalidLength;
// 12. Else,
} else {
// a. Let newByteLength be newLength × elementSize.
newByteLength = elementsInfo.CalculateByteLength(newLength);
const newByteLength: Number = SmiMul(newLength, elementsInfo.size);
// b. If offset + newByteLength > bufferByteLength, throw a RangeError
// exception.
if ((bufferByteLength < newByteLength) ||
(offset > bufferByteLength - newByteLength))
goto IfInvalidLength;
const difference: Number =
Cast<Number>(Subtract(bufferByteLength, newByteLength))
otherwise unreachable;
if (difference < offset) goto IfInvalidLength;
}
SetupTypedArray(typedArray, newLength, offset, newByteLength);
AttachBuffer(typedArray, buffer, elementsInfo.map, newLength, offset);
TypedArrayInitializeWithBuffer(
typedArray, newLength, buffer, elementsInfo, offset);
}
label IfInvalidAlignment(problemString: String) deferred {
ThrowInvalidTypedArrayAlignment(typedArray.map, problemString);

View File

@ -6,38 +6,11 @@
namespace typed_array {
struct TypedArrayElementsInfo {
// 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;
size: PositiveSmi;
map: Map;
kind: ElementsKind;
}
extern runtime TypedArraySortFast(Context, Object): JSTypedArray;
extern macro TypedArrayBuiltinsAssembler::ValidateTypedArray(
Context, Object, constexpr string): JSTypedArray;

View File

@ -5691,28 +5691,20 @@ TNode<Float64T> CodeStubAssembler::ChangeNumberToFloat64(
return result.value();
}
TNode<UintPtrT> CodeStubAssembler::TryNumberToUintPtr(TNode<Number> value,
Label* if_negative) {
TNode<UintPtrT> CodeStubAssembler::ChangeNonnegativeNumberToUintPtr(
TNode<Number> value) {
TVARIABLE(UintPtrT, result);
Label done(this, &result);
Branch(TaggedIsSmi(value),
[&] {
TNode<Smi> value_smi = CAST(value);
if (if_negative == nullptr) {
CSA_SLOW_ASSERT(this, SmiLessThan(SmiConstant(-1), value_smi));
} else {
GotoIfNot(TaggedIsPositiveSmi(value), if_negative);
}
CSA_SLOW_ASSERT(this, SmiLessThan(SmiConstant(-1), value_smi));
result = UncheckedCast<UintPtrT>(SmiToIntPtr(value_smi));
Goto(&done);
},
[&] {
TNode<HeapNumber> value_hn = CAST(value);
TNode<Float64T> value = LoadHeapNumberValue(value_hn);
if (if_negative != nullptr) {
GotoIf(Float64LessThan(value, Float64Constant(0.0)), if_negative);
}
result = ChangeFloat64ToUintPtr(value);
result = ChangeFloat64ToUintPtr(LoadHeapNumberValue(value_hn));
Goto(&done);
});

View File

@ -1988,10 +1988,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Number> ChangeUintPtrToTagged(TNode<UintPtrT> value);
TNode<Uint32T> ChangeNumberToUint32(TNode<Number> value);
TNode<Float64T> ChangeNumberToFloat64(SloppyTNode<Number> value);
TNode<UintPtrT> TryNumberToUintPtr(TNode<Number> value, Label* if_negative);
TNode<UintPtrT> ChangeNonnegativeNumberToUintPtr(TNode<Number> value) {
return TryNumberToUintPtr(value, nullptr);
}
TNode<UintPtrT> ChangeNonnegativeNumberToUintPtr(TNode<Number> value);
void TaggedToNumeric(Node* context, Node* value, Label* done,
Variable* var_numeric);

View File

@ -420,11 +420,6 @@ ExternalReference ExternalReference::address_of_min_int() {
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() {
return ExternalReference(&FLAG_runtime_stats);
}

View File

@ -92,8 +92,6 @@ class StatsCounter;
V(address_of_harmony_await_optimization_flag, \
"FLAG_harmony_await_optimization") \
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_runtime_stats_flag, "FLAG_runtime_stats") \
V(address_of_the_hole_nan, "the_hole_nan") \