[builtins] Reland of Port TypedArrayInitialize to CodeStubAssembler.

Turbofan is a lot slower than Crankshaft at constructing TypedArrays,
because we always go to the C++ builtin. Port the builtin to CSA
to improve performance, and to clean up the implementation, which is
split across multiple files and pieces at the moment.

This CL increases the performance with --future to roughly the same
as with crankshaft.

BUG=v8:5977

Change-Id: Id0d91a4592de41a3a308846d79bd44a608931762
Reviewed-on: https://chromium-review.googlesource.com/448537
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43548}
This commit is contained in:
Peter Marshall 2017-03-02 14:35:39 +01:00 committed by Commit Bot
parent d7cb3cfc91
commit ff8b1abb1a
11 changed files with 286 additions and 34 deletions

View File

@ -1546,6 +1546,14 @@ ExternalReference ExternalReference::libc_memchr_function(Isolate* isolate) {
return ExternalReference(Redirect(isolate, FUNCTION_ADDR(libc_memchr)));
}
void* libc_memset(void* string, int character, size_t n) {
return memset(string, character, n);
}
ExternalReference ExternalReference::libc_memset_function(Isolate* isolate) {
return ExternalReference(Redirect(isolate, FUNCTION_ADDR(libc_memset)));
}
ExternalReference ExternalReference::page_flags(Page* page) {
return ExternalReference(reinterpret_cast<Address>(page) +
MemoryChunk::kFlagsOffset);

View File

@ -978,6 +978,7 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference ieee754_tanh_function(Isolate* isolate);
static ExternalReference libc_memchr_function(Isolate* isolate);
static ExternalReference libc_memset_function(Isolate* isolate);
static ExternalReference page_flags(Page* page);

View File

@ -2620,6 +2620,13 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
}
TYPED_ARRAYS(INSTALL_TYPED_ARRAY)
#undef INSTALL_TYPED_ARRAY
// %typed_array_initialize
Handle<JSFunction> typed_array_initialize = SimpleCreateFunction(
isolate, factory->NewStringFromAsciiChecked("typedArrayInitialize"),
Builtins::kTypedArrayInitialize, 6, false);
InstallWithIntrinsicDefaultProto(isolate, typed_array_initialize,
Context::TYPED_ARRAY_INITIALIZE_INDEX);
}
{ // -- D a t a V i e w

View File

@ -21,8 +21,231 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
int object_offset);
template <IterationKind kIterationKind>
void GenerateTypedArrayPrototypeIterationMethod(const char* method_name);
void LoadMapAndElementsSize(Node* array, Variable* typed_map, Variable* size);
};
void TypedArrayBuiltinsAssembler::LoadMapAndElementsSize(Node* array,
Variable* typed_map,
Variable* size) {
Label unreachable(this), done(this);
Label uint8_elements(this), uint8_clamped_elements(this), int8_elements(this),
uint16_elements(this), int16_elements(this), uint32_elements(this),
int32_elements(this), float32_elements(this), float64_elements(this);
Label* elements_kind_labels[] = {
&uint8_elements, &uint8_clamped_elements, &int8_elements,
&uint16_elements, &int16_elements, &uint32_elements,
&int32_elements, &float32_elements, &float64_elements};
int32_t elements_kinds[] = {
UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS};
const size_t kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
1;
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
Node* array_map = LoadMap(array);
Node* elements_kind = LoadMapElementsKind(array_map);
Switch(elements_kind, &unreachable, elements_kinds, elements_kind_labels,
kTypedElementsKindCount);
for (int i = 0; i < static_cast<int>(kTypedElementsKindCount); i++) {
Bind(elements_kind_labels[i]);
{
ElementsKind kind = static_cast<ElementsKind>(elements_kinds[i]);
ExternalArrayType type =
isolate()->factory()->GetArrayTypeFromElementsKind(kind);
Handle<Map> map(isolate()->heap()->MapForFixedTypedArray(type));
typed_map->Bind(HeapConstant(map));
size->Bind(SmiConstant(static_cast<int>(
isolate()->factory()->GetExternalArrayElementSize(type))));
Goto(&done);
}
}
Bind(&unreachable);
{ Unreachable(); }
Bind(&done);
}
TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
Node* holder = Parameter(1);
Node* length = Parameter(2);
Node* maybe_buffer = Parameter(3);
Node* byte_offset = Parameter(4);
Node* byte_length = Parameter(5);
Node* initialize = Parameter(6);
Node* context = Parameter(9);
static const int32_t fta_base_data_offset =
FixedTypedArrayBase::kDataOffset - kHeapObjectTag;
Label setup_holder(this), alloc_array_buffer(this), aligned(this),
allocate_elements(this), attach_buffer(this), done(this);
Variable fixed_typed_map(this, MachineRepresentation::kTagged);
Variable element_size(this, MachineRepresentation::kTagged);
Variable total_size(this, MachineType::PointerRepresentation());
// Make sure length is a Smi. The caller guarantees this is the case.
length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero);
CSA_ASSERT(this, TaggedIsSmi(length));
byte_offset =
ToInteger(context, byte_offset, CodeStubAssembler::kTruncateMinusZero);
CSA_ASSERT(this, TaggedIsSmi(byte_offset));
// byte_length can be -0, get rid of it.
byte_length =
ToInteger(context, byte_length, CodeStubAssembler::kTruncateMinusZero);
GotoIfNot(IsNull(maybe_buffer), &setup_holder);
// If the buffer is null, then we need a Smi byte_length. The caller
// guarantees this is the case, because when byte_length >
// TypedArrayMaxSizeInHeap, a buffer is allocated and passed in here.
CSA_ASSERT(this, TaggedIsSmi(byte_length));
Goto(&setup_holder);
Bind(&setup_holder);
{
LoadMapAndElementsSize(holder, &fixed_typed_map, &element_size);
// Setup the holder (JSArrayBufferView).
// - Set the length.
// - Set the byte_offset.
// - Set the byte_length.
// - Set InternalFields to 0.
StoreObjectField(holder, JSTypedArray::kLengthOffset, length);
StoreObjectField(holder, JSArrayBufferView::kByteOffsetOffset, byte_offset);
StoreObjectField(holder, JSArrayBufferView::kByteLengthOffset, byte_length);
for (int offset = JSTypedArray::kSize;
offset < JSTypedArray::kSizeWithInternalFields;
offset += kPointerSize) {
StoreObjectField(holder, offset, SmiConstant(Smi::kZero));
}
Branch(IsNull(maybe_buffer), &alloc_array_buffer, &attach_buffer);
}
Bind(&alloc_array_buffer);
{
// Allocate a new ArrayBuffer and initialize it with empty properties and
// elements.
Node* const native_context = LoadNativeContext(context);
Node* const map =
LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX);
Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
Node* buffer = Allocate(JSArrayBuffer::kSizeWithInternalFields);
StoreMapNoWriteBarrier(buffer, map);
StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOffset,
empty_fixed_array);
StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset,
empty_fixed_array);
// Setup the ArrayBuffer.
// - Set BitField to 0.
// - Set IsExternal and IsNeuterable bits of BitFieldSlot.
// - Set the byte_length field to byte_length.
// - Set backing_store to null/Smi(0).
// - Set all internal fields to Smi(0).
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldSlot,
SmiConstant(Smi::kZero));
int32_t bitfield_value = (1 << JSArrayBuffer::IsExternal::kShift) |
(1 << JSArrayBuffer::IsNeuterable::kShift);
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
Int32Constant(bitfield_value),
MachineRepresentation::kWord32);
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
byte_length);
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
SmiConstant(Smi::kZero));
for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) {
int offset = JSArrayBuffer::kSize + i * kPointerSize;
StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(Smi::kZero));
}
StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
// Check the alignment.
GotoIf(SmiEqual(SmiMod(element_size.value(), SmiConstant(kObjectAlignment)),
SmiConstant(0)),
&aligned);
// Fix alignment if needed.
DCHECK_EQ(0, FixedTypedArrayBase::kHeaderSize & kObjectAlignmentMask);
Node* aligned_header_size =
IntPtrConstant(FixedTypedArrayBase::kHeaderSize + kObjectAlignmentMask);
Node* size = IntPtrAdd(SmiToWord(byte_length), aligned_header_size);
total_size.Bind(WordAnd(size, IntPtrConstant(~kObjectAlignmentMask)));
Goto(&allocate_elements);
}
Bind(&aligned);
{
Node* header_size = IntPtrConstant(FixedTypedArrayBase::kHeaderSize);
total_size.Bind(IntPtrAdd(SmiToWord(byte_length), header_size));
Goto(&allocate_elements);
}
Bind(&allocate_elements);
{
// Allocate a FixedTypedArray and set the length, base pointer and external
// pointer.
CSA_ASSERT(this, IsRegularHeapObjectSize(total_size.value()));
Node* elements = Allocate(total_size.value());
StoreMapNoWriteBarrier(elements, fixed_typed_map.value());
StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
StoreObjectFieldNoWriteBarrier(
elements, FixedTypedArrayBase::kBasePointerOffset, elements);
StoreObjectFieldNoWriteBarrier(elements,
FixedTypedArrayBase::kExternalPointerOffset,
IntPtrConstant(fta_base_data_offset),
MachineType::PointerRepresentation());
StoreObjectField(holder, JSObject::kElementsOffset, elements);
GotoIf(IsFalse(initialize), &done);
// Initialize the backing store by filling it with 0s.
Node* backing_store = IntPtrAdd(BitcastTaggedToWord(elements),
IntPtrConstant(fta_base_data_offset));
// Call out to memset to perform initialization.
Node* memset =
ExternalConstant(ExternalReference::libc_memset_function(isolate()));
CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
MachineType::IntPtr(), MachineType::UintPtr(), memset,
backing_store, IntPtrConstant(0), SmiToWord(byte_length));
Goto(&done);
}
Bind(&attach_buffer);
{
StoreObjectField(holder, JSArrayBufferView::kBufferOffset, maybe_buffer);
Node* elements = Allocate(FixedTypedArrayBase::kHeaderSize);
StoreMapNoWriteBarrier(elements, fixed_typed_map.value());
StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
StoreObjectFieldNoWriteBarrier(
elements, FixedTypedArrayBase::kBasePointerOffset, SmiConstant(0));
Node* backing_store =
LoadObjectField(maybe_buffer, JSArrayBuffer::kBackingStoreOffset,
MachineType::Pointer());
Node* external_pointer = IntPtrAdd(backing_store, SmiToWord(byte_offset));
StoreObjectFieldNoWriteBarrier(
elements, FixedTypedArrayBase::kExternalPointerOffset, external_pointer,
MachineType::PointerRepresentation());
StoreObjectField(holder, JSObject::kElementsOffset, elements);
Goto(&done);
}
Bind(&done);
{ Return(UndefinedConstant()); }
}
// -----------------------------------------------------------------------------
// ES6 section 22.2 TypedArray Objects

View File

@ -805,6 +805,7 @@ class Isolate;
TFJ(SymbolPrototypeValueOf, 0) \
\
/* TypedArray */ \
TFJ(TypedArrayInitialize, 6) \
CPP(TypedArrayPrototypeBuffer) \
/* ES6 section 22.2.3.2 get %TypedArray%.prototype.byteLength */ \
TFJ(TypedArrayPrototypeByteLength, 0) \

View File

@ -68,6 +68,7 @@ enum ContextLookupFlags {
V(REFLECT_DELETE_PROPERTY_INDEX, JSFunction, reflect_delete_property) \
V(SPREAD_ARGUMENTS_INDEX, JSFunction, spread_arguments) \
V(SPREAD_ITERABLE_INDEX, JSFunction, spread_iterable) \
V(TYPED_ARRAY_INITIALIZE_INDEX, JSFunction, typed_array_initialize) \
V(MATH_FLOOR_INDEX, JSFunction, math_floor) \
V(MATH_POW_INDEX, JSFunction, math_pow) \
V(NEW_PROMISE_CAPABILITY_INDEX, JSFunction, new_promise_capability) \

View File

@ -233,6 +233,8 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
"wasm::call_trap_callback_for_testing");
Add(ExternalReference::libc_memchr_function(isolate).address(),
"libc_memchr");
Add(ExternalReference::libc_memset_function(isolate).address(),
"libc_memset");
Add(ExternalReference::log_enter_external_function(isolate).address(),
"Logger::EnterExternal");
Add(ExternalReference::log_leave_external_function(isolate).address(),

View File

@ -2017,6 +2017,31 @@ Handle<JSSetIterator> Factory::NewJSSetIterator() {
JSSetIterator);
}
ExternalArrayType Factory::GetArrayTypeFromElementsKind(ElementsKind kind) {
switch (kind) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case TYPE##_ELEMENTS: \
return kExternal##Type##Array;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
default:
UNREACHABLE();
return kExternalInt8Array;
}
#undef TYPED_ARRAY_CASE
}
size_t Factory::GetExternalArrayElementSize(ExternalArrayType type) {
switch (type) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: \
return size;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
default:
UNREACHABLE();
return 0;
}
#undef TYPED_ARRAY_CASE
}
namespace {
@ -2032,21 +2057,6 @@ ElementsKind GetExternalArrayElementsKind(ExternalArrayType type) {
#undef TYPED_ARRAY_CASE
}
size_t GetExternalArrayElementSize(ExternalArrayType type) {
switch (type) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: \
return size;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
default:
UNREACHABLE();
return 0;
}
#undef TYPED_ARRAY_CASE
}
size_t GetFixedTypedArraysElementSize(ElementsKind kind) {
switch (kind) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
@ -2061,20 +2071,6 @@ size_t GetFixedTypedArraysElementSize(ElementsKind kind) {
}
ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) {
switch (kind) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case TYPE##_ELEMENTS: \
return kExternal##Type##Array;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
default:
UNREACHABLE();
return kExternalInt8Array;
}
#undef TYPED_ARRAY_CASE
}
JSFunction* GetTypedArrayFun(ExternalArrayType type, Isolate* isolate) {
Context* native_context = isolate->context()->native_context();
switch (type) {

View File

@ -536,6 +536,9 @@ class V8_EXPORT_PRIVATE Factory final {
SharedFlag shared = SharedFlag::kNotShared,
PretenureFlag pretenure = NOT_TENURED);
ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind);
size_t GetExternalArrayElementSize(ExternalArrayType type);
Handle<JSTypedArray> NewJSTypedArray(ExternalArrayType type,
PretenureFlag pretenure = NOT_TENURED);

View File

@ -169,13 +169,14 @@ function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) {
throw %make_range_error(kInvalidTypedArrayLength);
}
}
%_TypedArrayInitialize(obj, ARRAY_ID, buffer, offset, newByteLength, true);
var newLength = newByteLength / ELEMENT_SIZE;
%typed_array_initialize(obj, newLength, buffer, offset, newByteLength, true);
}
function NAMEConstructByLength(obj, length) {
var l = IS_UNDEFINED(length) ?
0 : ToIndex(length, kInvalidTypedArrayLength);
if (length > %_MaxSmi()) {
if (l > %_MaxSmi()) {
// Note: this is not per spec, but rather a constraint of our current
// representation (which uses smi's).
throw %make_range_error(kInvalidTypedArrayLength);
@ -183,9 +184,9 @@ function NAMEConstructByLength(obj, length) {
var byteLength = l * ELEMENT_SIZE;
if (byteLength > %_TypedArrayMaxSizeInHeap()) {
var buffer = new GlobalArrayBuffer(byteLength);
%_TypedArrayInitialize(obj, ARRAY_ID, buffer, 0, byteLength, true);
%typed_array_initialize(obj, l, buffer, 0, byteLength, true);
} else {
%_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, true);
%typed_array_initialize(obj, l, null, 0, byteLength, true);
}
}
@ -198,7 +199,7 @@ function NAMEConstructByArrayLike(obj, arrayLike, length) {
var initialized = false;
var byteLength = l * ELEMENT_SIZE;
if (byteLength <= %_TypedArrayMaxSizeInHeap()) {
%_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, false);
%typed_array_initialize(obj, l, null, 0, byteLength, false);
} else {
initialized =
%TypedArrayInitializeFromArrayLike(obj, ARRAY_ID, arrayLike, l);

View File

@ -822,3 +822,12 @@ function TestNonConfigurableProperties(constructor) {
for(i = 0; i < typedArrayConstructors.length; i++) {
TestNonConfigurableProperties(typedArrayConstructors[i]);
}
(function TestInitialization() {
for (var i = 0; i <= 128; i++) {
var arr = new Uint8Array(i);
for (var j = 0; j < i; j++) {
assertEquals(0, arr[j]);
}
}
})();