Commit 06fef85b authored by Peter Marshall's avatar Peter Marshall Committed by Commit Bot

[builtins] Port TypedArrayConstructByArrayBuffer to CodeStubAssembler.

Part of the performance and refactoring work to move the TypedArray
constructors into CSA. This CL moves ConstructByArrayBuffer from JS
to CSA.

BUG=v8:5977

Change-Id: I0a200e6b3f6261ea2372ea9c3d3ca98e313cf2c5
Reviewed-on: https://chromium-review.googlesource.com/451620
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43747}
parent d9bcc067
......@@ -2647,6 +2647,15 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
InstallWithIntrinsicDefaultProto(
isolate, construct_by_length,
Context::TYPED_ARRAY_CONSTRUCT_BY_LENGTH_INDEX);
// %typed_array_construct_by_array_buffer
Handle<JSFunction> construct_by_buffer = SimpleCreateFunction(
isolate,
factory->NewStringFromAsciiChecked("typedArrayConstructByArrayBuffer"),
Builtins::kTypedArrayConstructByArrayBuffer, 5, false);
InstallWithIntrinsicDefaultProto(
isolate, construct_by_buffer,
Context::TYPED_ARRAY_CONSTRUCT_BY_ARRAY_BUFFER_INDEX);
}
{ // -- D a t a V i e w
......
This diff is collapsed.
......@@ -808,6 +808,7 @@ class Isolate;
TFJ(SymbolPrototypeValueOf, 0) \
\
/* TypedArray */ \
TFJ(TypedArrayConstructByArrayBuffer, 5) \
TFJ(TypedArrayConstructByLength, 3) \
TFJ(TypedArrayInitialize, 6) \
CPP(TypedArrayPrototypeBuffer) \
......
......@@ -4213,6 +4213,35 @@ Node* CodeStubAssembler::JSReceiverToPrimitive(Node* context, Node* input) {
return result.value();
}
Node* CodeStubAssembler::ToSmiIndex(Node* const input, Node* const context,
Label* range_error) {
Variable result(this, MachineRepresentation::kTagged, input);
Label check_undefined(this), undefined(this), defined(this),
negative_check(this), done(this);
Branch(TaggedIsSmi(result.value()), &negative_check, &check_undefined);
Bind(&check_undefined);
Branch(IsUndefined(result.value()), &undefined, &defined);
Bind(&defined);
result.Bind(ToInteger(context, result.value(),
CodeStubAssembler::kTruncateMinusZero));
GotoIfNot(TaggedIsSmi(result.value()), range_error);
CSA_ASSERT(this, TaggedIsSmi(result.value()));
Goto(&negative_check);
Bind(&negative_check);
GotoIf(SmiLessThan(result.value(), SmiConstant(0)), range_error);
Goto(&done);
Bind(&undefined);
result.Bind(SmiConstant(0));
Goto(&done);
Bind(&done);
return result.value();
}
Node* CodeStubAssembler::ToInteger(Node* context, Node* input,
ToIntegerTruncationMode mode) {
// We might need to loop once for ToNumber conversion.
......
......@@ -782,6 +782,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
kTruncateMinusZero,
};
// ES6 7.1.17 ToIndex, but jumps to range_error if the result is not a Smi.
Node* ToSmiIndex(Node* const input, Node* const context, Label* range_error);
// Convert any object to an Integer.
Node* ToInteger(Node* context, Node* input,
ToIntegerTruncationMode mode = kNoTruncation);
......
......@@ -1258,6 +1258,7 @@ void InstructionSelector::VisitWord64Ror(Node* node) {
V(ChangeFloat64ToInt32, kArm64Float64ToInt32) \
V(TruncateFloat32ToUint32, kArm64Float32ToUint32) \
V(ChangeFloat64ToUint32, kArm64Float64ToUint32) \
V(ChangeFloat64ToUint64, kArm64Float64ToUint64) \
V(TruncateFloat64ToUint32, kArm64Float64ToUint32) \
V(TruncateFloat64ToFloat32, kArm64Float64ToFloat32) \
V(TruncateFloat64ToWord32, kArchTruncateDoubleToI) \
......
......@@ -391,6 +391,13 @@ Node* CodeAssembler::ChangeInt32ToIntPtr(Node* value) {
return value;
}
Node* CodeAssembler::ChangeFloat64ToUintPtr(Node* value) {
if (raw_assembler()->machine()->Is64()) {
return raw_assembler()->ChangeFloat64ToUint64(value);
}
return raw_assembler()->ChangeFloat64ToUint32(value);
}
Node* CodeAssembler::RoundIntPtrToFloat64(Node* value) {
if (raw_assembler()->machine()->Is64()) {
return raw_assembler()->RoundInt64ToFloat64(value);
......
......@@ -153,6 +153,7 @@ typedef std::function<void()> CodeAssemblerCallback;
V(TruncateInt64ToInt32) \
V(ChangeFloat32ToFloat64) \
V(ChangeFloat64ToUint32) \
V(ChangeFloat64ToUint64) \
V(ChangeInt32ToFloat64) \
V(ChangeInt32ToInt64) \
V(ChangeUint32ToFloat64) \
......@@ -297,6 +298,10 @@ class V8_EXPORT_PRIVATE CodeAssembler {
CODE_ASSEMBLER_UNARY_OP_LIST(DECLARE_CODE_ASSEMBLER_UNARY_OP)
#undef DECLARE_CODE_ASSEMBLER_UNARY_OP
// Changes a double to an inptr_t for pointer arithmetic outside of Smi range.
// Assumes that the double can be exactly represented as an int.
Node* ChangeFloat64ToUintPtr(Node* value);
// Changes an intptr_t to a double, e.g. for storing an element index
// outside Smi range in a HeapNumber. Lossless on 32-bit,
// rounds on 64-bit (which doesn't affect valid element indices).
......
......@@ -1229,6 +1229,8 @@ void InstructionSelector::VisitNode(Node* node) {
return MarkAsWord32(node), VisitChangeFloat64ToInt32(node);
case IrOpcode::kChangeFloat64ToUint32:
return MarkAsWord32(node), VisitChangeFloat64ToUint32(node);
case IrOpcode::kChangeFloat64ToUint64:
return MarkAsWord64(node), VisitChangeFloat64ToUint64(node);
case IrOpcode::kFloat64SilenceNaN:
MarkAsFloat64(node);
if (CanProduceSignalingNaN(node->InputAt(0))) {
......@@ -1961,6 +1963,9 @@ void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
UNIMPLEMENTED();
}
void InstructionSelector::VisitChangeFloat64ToUint64(Node* node) {
UNIMPLEMENTED();
}
void InstructionSelector::VisitTryTruncateFloat32ToInt64(Node* node) {
UNIMPLEMENTED();
......
......@@ -57,7 +57,6 @@ class MachineRepresentationInferrer {
case IrOpcode::kTryTruncateFloat32ToInt64:
case IrOpcode::kTryTruncateFloat64ToInt64:
case IrOpcode::kTryTruncateFloat32ToUint64:
case IrOpcode::kTryTruncateFloat64ToUint64:
CHECK_LE(index, static_cast<size_t>(1));
return index == 0 ? MachineRepresentation::kWord64
: MachineRepresentation::kBit;
......@@ -234,6 +233,7 @@ class MachineRepresentationInferrer {
case IrOpcode::kInt64Constant:
case IrOpcode::kRelocatableInt64Constant:
case IrOpcode::kBitcastFloat64ToInt64:
case IrOpcode::kChangeFloat64ToUint64:
MACHINE_BINOP_64_LIST(LABEL) {
representation_vector_[node->id()] =
MachineRepresentation::kWord64;
......@@ -335,6 +335,7 @@ class MachineRepresentationChecker {
case IrOpcode::kFloat64ExtractLowWord32:
case IrOpcode::kFloat64ExtractHighWord32:
case IrOpcode::kBitcastFloat64ToInt64:
case IrOpcode::kTryTruncateFloat64ToInt64:
CheckValueInputForFloat64Op(node, 0);
break;
case IrOpcode::kWord64Equal:
......@@ -427,6 +428,7 @@ class MachineRepresentationChecker {
}
break;
case IrOpcode::kFloat64SilenceNaN:
case IrOpcode::kChangeFloat64ToUint64:
MACHINE_FLOAT64_UNOP_LIST(LABEL) {
CheckValueInputForFloat64Op(node, 0);
}
......
......@@ -140,6 +140,7 @@ MachineType AtomicExchangeRepresentationOf(Operator const* op) {
V(ChangeFloat32ToFloat64, Operator::kNoProperties, 1, 0, 1) \
V(ChangeFloat64ToInt32, Operator::kNoProperties, 1, 0, 1) \
V(ChangeFloat64ToUint32, Operator::kNoProperties, 1, 0, 1) \
V(ChangeFloat64ToUint64, Operator::kNoProperties, 1, 0, 1) \
V(TruncateFloat64ToUint32, Operator::kNoProperties, 1, 0, 1) \
V(TruncateFloat32ToInt32, Operator::kNoProperties, 1, 0, 1) \
V(TruncateFloat32ToUint32, Operator::kNoProperties, 1, 0, 1) \
......
......@@ -301,6 +301,7 @@ class V8_EXPORT_PRIVATE MachineOperatorBuilder final
const Operator* ChangeFloat32ToFloat64();
const Operator* ChangeFloat64ToInt32(); // narrowing
const Operator* ChangeFloat64ToUint32(); // narrowing
const Operator* ChangeFloat64ToUint64();
const Operator* TruncateFloat64ToUint32();
const Operator* TruncateFloat32ToInt32();
const Operator* TruncateFloat32ToUint32();
......
......@@ -1231,6 +1231,10 @@ void InstructionSelector::VisitChangeFloat64ToUint32(Node* node) {
VisitRR(this, kMips64TruncUwD, node);
}
void InstructionSelector::VisitChangeFloat64ToUint64(Node* node) {
VisitRR(this, kMips64TruncUlD, node);
}
void InstructionSelector::VisitTruncateFloat64ToUint32(Node* node) {
VisitRR(this, kMips64TruncUwD, node);
}
......
......@@ -505,6 +505,7 @@
V(ChangeFloat32ToFloat64) \
V(ChangeFloat64ToInt32) \
V(ChangeFloat64ToUint32) \
V(ChangeFloat64ToUint64) \
V(Float64SilenceNaN) \
V(TruncateFloat64ToUint32) \
V(TruncateFloat32ToInt32) \
......
......@@ -572,6 +572,9 @@ class V8_EXPORT_PRIVATE RawMachineAssembler {
Node* ChangeFloat64ToUint32(Node* a) {
return AddNode(machine()->ChangeFloat64ToUint32(), a);
}
Node* ChangeFloat64ToUint64(Node* a) {
return AddNode(machine()->ChangeFloat64ToUint64(), a);
}
Node* TruncateFloat64ToUint32(Node* a) {
return AddNode(machine()->TruncateFloat64ToUint32(), a);
}
......
......@@ -1370,6 +1370,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kChangeFloat32ToFloat64:
case IrOpcode::kChangeFloat64ToInt32:
case IrOpcode::kChangeFloat64ToUint32:
case IrOpcode::kChangeFloat64ToUint64:
case IrOpcode::kFloat64SilenceNaN:
case IrOpcode::kTruncateFloat64ToUint32:
case IrOpcode::kTruncateFloat32ToInt32:
......
......@@ -1332,6 +1332,7 @@ void VisitFloatUnop(InstructionSelector* selector, Node* node, Node* input,
V(ChangeFloat64ToInt32, kSSEFloat64ToInt32) \
V(ChangeFloat64ToUint32, kSSEFloat64ToUint32 | MiscField::encode(1)) \
V(TruncateFloat64ToUint32, kSSEFloat64ToUint32 | MiscField::encode(0)) \
V(ChangeFloat64ToUint64, kSSEFloat64ToUint64) \
V(TruncateFloat64ToFloat32, kSSEFloat64ToFloat32) \
V(ChangeFloat32ToFloat64, kSSEFloat32ToFloat64) \
V(TruncateFloat32ToInt32, kSSEFloat32ToInt32) \
......
......@@ -68,6 +68,8 @@ 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_CONSTRUCT_BY_ARRAY_BUFFER_INDEX, JSFunction, \
typed_array_construct_by_array_buffer) \
V(TYPED_ARRAY_CONSTRUCT_BY_LENGTH_INDEX, JSFunction, \
typed_array_construct_by_length) \
V(TYPED_ARRAY_INITIALIZE_INDEX, JSFunction, typed_array_initialize) \
......
......@@ -127,51 +127,6 @@ function TypedArraySpeciesCreate(exemplar, arg0, arg1, arg2, conservative) {
}
macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE)
function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) {
var offset;
if (!IS_UNDEFINED(byteOffset)) {
offset = ToIndex(byteOffset, kInvalidTypedArrayLength);
if (offset % ELEMENT_SIZE !== 0) {
throw %make_range_error(kInvalidTypedArrayAlignment,
"start offset", "NAME", ELEMENT_SIZE);
}
} else {
offset = 0;
}
if (!IS_UNDEFINED(length)) {
length = ToIndex(length, kInvalidTypedArrayLength);
}
if (length > %_MaxSmi()) {
// Note: this is not per spec, but rather a constraint of our current
// representation (which uses smi's).
throw %make_range_error(kInvalidTypedArrayLength);
}
var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
var newByteLength;
if (IS_UNDEFINED(length)) {
if (bufferByteLength % ELEMENT_SIZE !== 0) {
throw %make_range_error(kInvalidTypedArrayAlignment,
"byte length", "NAME", ELEMENT_SIZE);
}
newByteLength = bufferByteLength - offset;
if (newByteLength < 0) {
throw %make_range_error(kInvalidOffset, offset);
}
} else {
newByteLength = length * ELEMENT_SIZE;
if (offset + newByteLength > bufferByteLength) {
throw %make_range_error(kInvalidTypedArrayLength);
}
}
var newLength = newByteLength / ELEMENT_SIZE;
if (newLength > %_MaxSmi()) {
throw %make_range_error(kInvalidTypedArrayLength);
}
%typed_array_initialize(obj, newLength, buffer, offset, newByteLength, true);
}
function NAMEConstructByArrayLike(obj, arrayLike, length) {
var l = ToPositiveInteger(length, kInvalidTypedArrayLength);
......@@ -234,7 +189,8 @@ function NAMEConstructByTypedArray(obj, typedArray) {
function NAMEConstructor(arg1, arg2, arg3) {
if (!IS_UNDEFINED(new.target)) {
if (IS_ARRAYBUFFER(arg1) || IS_SHAREDARRAYBUFFER(arg1)) {
NAMEConstructByArrayBuffer(this, arg1, arg2, arg3);
%typed_array_construct_by_array_buffer(
this, arg1, arg2, arg3, ELEMENT_SIZE);
} else if (IS_TYPEDARRAY(arg1)) {
NAMEConstructByTypedArray(this, arg1);
} else if (IS_RECEIVER(arg1)) {
......
......@@ -120,6 +120,28 @@ RUNTIME_FUNCTION(Runtime_ThrowTypeError) {
#undef THROW_ERROR
RUNTIME_FUNCTION(Runtime_ThrowInvalidTypedArrayAlignment) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Map, map, 0);
CONVERT_ARG_HANDLE_CHECKED(String, problem_string, 1);
ElementsKind kind = map->elements_kind();
Handle<String> type = isolate->factory()->NewStringFromAsciiChecked(
Runtime::ElementsKindToType(kind));
ExternalArrayType external_type =
isolate->factory()->GetArrayTypeFromElementsKind(kind);
size_t size = isolate->factory()->GetExternalArrayElementSize(external_type);
Handle<Object> element_size =
handle(Smi::FromInt(static_cast<int>(size)), isolate);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidTypedArrayAlignment,
problem_string, type, element_size));
}
RUNTIME_FUNCTION(Runtime_UnwindAndFindExceptionHandler) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
......
......@@ -61,6 +61,20 @@ void Runtime::ArrayIdToTypeAndSize(int arrayId, ExternalArrayType* array_type,
}
}
const char* Runtime::ElementsKindToType(ElementsKind fixed_elements_kind) {
switch (fixed_elements_kind) {
#define ELEMENTS_KIND_CASE(Type, type, TYPE, ctype, size) \
case TYPE##_ELEMENTS: \
return #Type "Array";
TYPED_ARRAYS(ELEMENTS_KIND_CASE)
#undef ELEMENTS_KIND_CASE
default:
UNREACHABLE();
return "";
}
}
RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) {
HandleScope scope(isolate);
......
......@@ -317,6 +317,7 @@ namespace internal {
F(ThrowIncompatibleMethodReceiver, 2, 1) \
F(ThrowInvalidHint, 1, 1) \
F(ThrowInvalidStringLength, 0, 1) \
F(ThrowInvalidTypedArrayAlignment, 2, 1) \
F(ThrowIteratorResultNotAnObject, 1, 1) \
F(ThrowSymbolIteratorInvalid, 0, 1) \
F(ThrowNonCallableInInstanceOfCheck, 0, 1) \
......@@ -797,6 +798,8 @@ class Runtime : public AllStatic {
ElementsKind* fixed_elements_kind,
size_t* element_size);
static const char* ElementsKindToType(ElementsKind fixed_elements_kind);
static MaybeHandle<JSArray> GetInternalProperties(Isolate* isolate,
Handle<Object>);
};
......
......@@ -29,3 +29,32 @@
}, RangeError);
assertEquals(expected, actual);
})();
(function TestConstructByBufferToPrimitiveOrdering() {
var expected = ["offset.toPrimitive", "length.toPrimitive"];
var actual = [];
var offset = {};
offset[Symbol.toPrimitive] = function() {
actual.push("offset.toPrimitive");
return 1;
};
var length = {};
length[Symbol.toPrimitive] = function() {
actual.push("length.toPrimitive");
return 1;
};
var buffer = new ArrayBuffer(16);
var arr = new Uint8Array(buffer, offset, length);
assertEquals(expected, actual);
assertEquals(1, arr.length);
})();
(function TestByteOffsetToIndexThrowsForNegative() {
var buffer = new ArrayBuffer(16);
assertThrows(function() {
new Uint8Array(buffer, -1);
}, RangeError);
})();
// Copyright 2017 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.
// Flags: --allow-natives-syntax
(function TestBufferByteLengthNonSmi() {
var non_smi_byte_length = %_MaxSmi() + 1;
try {
var buffer = new ArrayBuffer(non_smi_byte_length);
} catch (e) {
// The ArrayBuffer allocation can fail on 32-bit archs, so no need to try to
// construct the typed array.
return;
}
var arr = new Uint16Array(buffer);
assertEquals(non_smi_byte_length, arr.byteLength);
assertEquals(non_smi_byte_length / 2, arr.length);
arr = new Uint32Array(buffer);
assertEquals(non_smi_byte_length, arr.byteLength);
assertEquals(non_smi_byte_length / 4, arr.length);
})();
(function TestByteOffsetNonSmi() {
var non_smi_byte_length = %_MaxSmi() + 11;
try {
var buffer = new ArrayBuffer(non_smi_byte_length);
} catch (e) {
// The ArrayBuffer allocation can fail on 32-bit archs, so no need to try to
// construct the typed array.
return;
}
print(buffer.byteLength);
var whole = new Uint16Array(buffer);
whole[non_smi_byte_length / 2 - 1] = 1;
whole[non_smi_byte_length / 2 - 2] = 2;
whole[non_smi_byte_length / 2 - 3] = 3;
whole[non_smi_byte_length / 2 - 4] = 4;
whole[non_smi_byte_length / 2 - 5] = 5;
assertEquals(non_smi_byte_length / 2, whole.length);
assertEquals(1, whole[non_smi_byte_length / 2 - 1]);
var arr = new Uint16Array(buffer, non_smi_byte_length - 10, 5);
assertEquals(non_smi_byte_length, arr.buffer.byteLength);
assertEquals(10, arr.byteLength);
assertEquals(5, arr.length);
assertEquals(5, arr[0]);
assertEquals(4, arr[1]);
assertEquals(3, arr[2]);
assertEquals(2, arr[3]);
assertEquals(1, arr[4]);
})();
......@@ -837,11 +837,21 @@ for(i = 0; i < typedArrayConstructors.length; i++) {
(function TestBufferLengthTooLong() {
try {
var buf = new ArrayBuffer(2147483648);
assertThrows(function() {
new Int8Array(buf);
}, RangeError);
} catch (e) {
// The ArrayBuffer allocation fails on 32-bit archs, so no need to try to
// construct the typed array.
return;
}
assertThrows(function() {
new Int8Array(buf);
}, RangeError);
})();
(function TestByteLengthErrorMessage() {
try {
new Uint32Array(new ArrayBuffer(17));
} catch (e) {
assertEquals("byte length of Uint32Array should be a multiple of 4",
e.message);
}
})();
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