Commit 6ff3b370 authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[builtins] Allow 2Gb TypedArrays on 64-bit architectures

... even with ptr-compr.

Although full uintptr-sized TypedArrays are not supported yet
we may already start using uint32-sized typed arrays as we no
longer rely on TypedArray length to be a Smi.

Bug: v8:4153
Change-Id: If179541ad4f02c4ec7de9d1f3836138fe526d8a5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1905847
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64897}
parent 1a1a9cca
...@@ -5226,7 +5226,9 @@ class V8_EXPORT TypedArray : public ArrayBufferView { ...@@ -5226,7 +5226,9 @@ class V8_EXPORT TypedArray : public ArrayBufferView {
/* /*
* The largest typed array size that can be constructed using New. * The largest typed array size that can be constructed using New.
*/ */
static constexpr size_t kMaxLength = internal::kSmiMaxValue; static constexpr size_t kMaxLength = internal::kApiSystemPointerSize == 4
? internal::kSmiMaxValue
: 0x7FFFFFFF; // kMaxInt32
/** /**
* Number of elements in this typed array * Number of elements in this typed array
......
...@@ -7633,8 +7633,9 @@ size_t v8::TypedArray::Length() { ...@@ -7633,8 +7633,9 @@ size_t v8::TypedArray::Length() {
return obj->WasDetached() ? 0 : obj->length(); return obj->WasDetached() ? 0 : obj->length();
} }
static_assert(v8::TypedArray::kMaxLength == i::Smi::kMaxValue, static_assert(
"v8::TypedArray::kMaxLength must match i::Smi::kMaxValue"); v8::TypedArray::kMaxLength == i::JSTypedArray::kMaxLength,
"v8::TypedArray::kMaxLength must match i::JSTypedArray::kMaxLength");
#define TYPED_ARRAY_NEW(Type, type, TYPE, ctype) \ #define TYPED_ARRAY_NEW(Type, type, TYPE, ctype) \
Local<Type##Array> Type##Array::New(Local<ArrayBuffer> array_buffer, \ Local<Type##Array> Type##Array::New(Local<ArrayBuffer> array_buffer, \
......
...@@ -315,6 +315,8 @@ const kMaxArrayIndex: ...@@ -315,6 +315,8 @@ const kMaxArrayIndex:
constexpr uint32 generates 'JSArray::kMaxArrayIndex'; constexpr uint32 generates 'JSArray::kMaxArrayIndex';
const kArrayBufferMaxByteLength: const kArrayBufferMaxByteLength:
constexpr uintptr generates 'JSArrayBuffer::kMaxByteLength'; constexpr uintptr generates 'JSArrayBuffer::kMaxByteLength';
const kTypedArrayMaxLength:
constexpr uintptr generates 'JSTypedArray::kMaxLength';
const kMaxTypedArrayInHeap: const kMaxTypedArrayInHeap:
constexpr int31 generates 'JSTypedArray::kMaxSizeInHeap'; constexpr int31 generates 'JSTypedArray::kMaxSizeInHeap';
// CSA does not support 64-bit types on 32-bit platforms so as a workaround the // CSA does not support 64-bit types on 32-bit platforms so as a workaround the
......
...@@ -24,9 +24,8 @@ namespace typed_array { ...@@ -24,9 +24,8 @@ namespace typed_array {
struct TypedArrayElementsInfo { struct TypedArrayElementsInfo {
// Calculates the number of bytes required for specified number of elements. // Calculates the number of bytes required for specified number of elements.
CalculateByteLength(length: uintptr): uintptr labels IfInvalid { CalculateByteLength(length: uintptr): uintptr labels IfInvalid {
// TODO(v8:4153): use kArrayBufferMaxByteLength instead of kSmiMaxValue if (length > kTypedArrayMaxLength) goto IfInvalid;
// to allow creation of huge TypedArrays. const maxArrayLength = kArrayBufferMaxByteLength >>> this.sizeLog2;
const maxArrayLength = kSmiMaxValue >>> this.sizeLog2;
if (length > maxArrayLength) goto IfInvalid; if (length > maxArrayLength) goto IfInvalid;
const byteLength = length << this.sizeLog2; const byteLength = length << this.sizeLog2;
return byteLength; return byteLength;
...@@ -36,9 +35,7 @@ namespace typed_array { ...@@ -36,9 +35,7 @@ namespace typed_array {
// of bytes. // of bytes.
CalculateLength(byteLength: uintptr): uintptr labels IfInvalid { CalculateLength(byteLength: uintptr): uintptr labels IfInvalid {
const length = byteLength >>> this.sizeLog2; const length = byteLength >>> this.sizeLog2;
// TODO(v8:4153): use kArrayBufferMaxByteLength instead of kSmiMaxValue if (length > kTypedArrayMaxLength) goto IfInvalid;
// to allow creation of huge TypedArrays.
if (length > kSmiMaxValue) goto IfInvalid;
return length; return length;
} }
......
...@@ -129,19 +129,12 @@ class ShellArrayBufferAllocator : public ArrayBufferAllocatorBase { ...@@ -129,19 +129,12 @@ class ShellArrayBufferAllocator : public ArrayBufferAllocatorBase {
private: private:
static constexpr size_t kVMThreshold = 65536; static constexpr size_t kVMThreshold = 65536;
static constexpr size_t kTwoGB = 2u * 1024u * 1024u * 1024u;
void* AllocateVM(size_t length) { void* AllocateVM(size_t length) {
DCHECK_LE(kVMThreshold, length); DCHECK_LE(kVMThreshold, length);
// TODO(titzer): allocations should fail if >= 2gb because array buffers
// store their lengths as a SMI internally.
if (length >= kTwoGB) return nullptr;
v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator(); v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
size_t page_size = page_allocator->AllocatePageSize(); size_t page_size = page_allocator->AllocatePageSize();
size_t allocated = RoundUp(length, page_size); size_t allocated = RoundUp(length, page_size);
// Rounding up could go over the limit.
if (allocated >= kTwoGB) return nullptr;
return i::AllocatePages(page_allocator, nullptr, allocated, page_size, return i::AllocatePages(page_allocator, nullptr, allocated, page_size,
PageAllocator::kReadWrite); PageAllocator::kReadWrite);
} }
......
...@@ -1273,6 +1273,18 @@ RUNTIME_FUNCTION(Runtime_HeapObjectVerify) { ...@@ -1273,6 +1273,18 @@ RUNTIME_FUNCTION(Runtime_HeapObjectVerify) {
return isolate->heap()->ToBoolean(true); return isolate->heap()->ToBoolean(true);
} }
RUNTIME_FUNCTION(Runtime_ArrayBufferMaxByteLength) {
HandleScope shs(isolate);
DCHECK_EQ(0, args.length());
return *isolate->factory()->NewNumber(JSArrayBuffer::kMaxByteLength);
}
RUNTIME_FUNCTION(Runtime_TypedArrayMaxLength) {
HandleScope shs(isolate);
DCHECK_EQ(0, args.length());
return *isolate->factory()->NewNumber(JSTypedArray::kMaxLength);
}
RUNTIME_FUNCTION(Runtime_WasmGetNumberOfInstances) { RUNTIME_FUNCTION(Runtime_WasmGetNumberOfInstances) {
SealHandleScope shs(isolate); SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
......
...@@ -265,6 +265,7 @@ namespace internal { ...@@ -265,6 +265,7 @@ namespace internal {
F(GetModuleNamespace, 1, 1) F(GetModuleNamespace, 1, 1)
#define FOR_EACH_INTRINSIC_NUMBERS(F, I) \ #define FOR_EACH_INTRINSIC_NUMBERS(F, I) \
F(ArrayBufferMaxByteLength, 0, 1) \
F(GetHoleNaNLower, 0, 1) \ F(GetHoleNaNLower, 0, 1) \
F(GetHoleNaNUpper, 0, 1) \ F(GetHoleNaNUpper, 0, 1) \
I(IsSmi, 1, 1) \ I(IsSmi, 1, 1) \
...@@ -273,7 +274,8 @@ namespace internal { ...@@ -273,7 +274,8 @@ namespace internal {
F(NumberToString, 1, 1) \ F(NumberToString, 1, 1) \
F(StringParseFloat, 1, 1) \ F(StringParseFloat, 1, 1) \
F(StringParseInt, 2, 1) \ F(StringParseInt, 2, 1) \
F(StringToNumber, 1, 1) F(StringToNumber, 1, 1) \
F(TypedArrayMaxLength, 0, 1)
#define FOR_EACH_INTRINSIC_OBJECT(F, I) \ #define FOR_EACH_INTRINSIC_OBJECT(F, I) \
F(AddDictionaryProperty, 3, 1) \ F(AddDictionaryProperty, 3, 1) \
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// Flags: --allow-natives-syntax // Flags: --allow-natives-syntax
const limit = %MaxSmi() + 1; const limit = %TypedArrayMaxLength() + 1;
(function() { (function() {
function foo() { function foo() {
......
...@@ -211,8 +211,8 @@ tests.push(function TestFromTypedArraySpeciesDetachsBuffer(constr) { ...@@ -211,8 +211,8 @@ tests.push(function TestFromTypedArraySpeciesDetachsBuffer(constr) {
assertThrows(() => new constr(a1)); assertThrows(() => new constr(a1));
}); });
tests.push(function TestLengthIsMaxSmi(constr) { tests.push(function TestTypedArrayMaxLength(constr) {
var myObject = { 0: 5, 1: 6, length: %MaxSmi() + 1 }; var myObject = { 0: 5, 1: 6, length: %TypedArrayMaxLength() + 1 };
assertThrows(function() { assertThrows(function() {
new constr(myObject); new constr(myObject);
......
...@@ -26,32 +26,36 @@ ...@@ -26,32 +26,36 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --nostress-opt --allow-natives-syntax --mock-arraybuffer-allocator // Flags: --nostress-opt --allow-natives-syntax --mock-arraybuffer-allocator
var maxSize = %MaxSmi() + 1;
var ab; let kArrayBufferByteLengthLimit = %ArrayBufferMaxByteLength() + 1;
let kTypedArrayLengthLimit = %TypedArrayMaxLength() + 1;
// Allocate the largest ArrayBuffer we can on this architecture. // Allocate the largest ArrayBuffer we can on this architecture.
for (k = 8; k >= 1 && ab == null; k = k/2) { let ab = new ArrayBuffer(kArrayBufferByteLengthLimit - 1);
try {
ab = new ArrayBuffer(maxSize * k);
} catch (e) {
ab = null;
}
}
assertTrue(ab != null); function TestArray(constr, elementSize) {
assertEquals(kArrayBufferByteLengthLimit % elementSize, 0);
let bufferSizeLength = kArrayBufferByteLengthLimit / elementSize;
let minUnallocatableSize =
kTypedArrayLengthLimit < bufferSizeLength
? kTypedArrayLengthLimit
: bufferSizeLength;
function TestArray(constr) {
assertThrows(function() { assertThrows(function() {
new constr(ab, 0, maxSize); new constr(ab, 0, minUnallocatableSize);
}, RangeError); }, RangeError);
// This one must succeed.
new constr(ab, 0, minUnallocatableSize - 1);
} }
TestArray(Uint8Array); TestArray(Uint8Array, 1);
TestArray(Int8Array); TestArray(Int8Array, 1);
TestArray(Uint16Array); TestArray(Uint16Array, 2);
TestArray(Int16Array); TestArray(Int16Array, 2);
TestArray(Uint32Array); TestArray(Uint32Array, 4);
TestArray(Int32Array); TestArray(Int32Array, 4);
TestArray(Float32Array); TestArray(Float32Array, 4);
TestArray(Float64Array); TestArray(Float64Array, 8);
TestArray(Uint8ClampedArray); TestArray(Uint8ClampedArray, 1);
// Copyright 2013 the V8 project authors. All rights reserved. // Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
...@@ -26,20 +25,34 @@ ...@@ -26,20 +25,34 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --nostress-opt --allow-natives-syntax // Flags: --nostress-opt --allow-natives-syntax --mock-arraybuffer-allocator
var maxSize = %MaxSmi() + 1;
function TestArray(constr) { let kArrayBufferByteLengthLimit = %ArrayBufferMaxByteLength() + 1;
let kTypedArrayLengthLimit = %TypedArrayMaxLength() + 1;
function TestArray(constr, elementSize) {
assertEquals(kArrayBufferByteLengthLimit % elementSize, 0);
let bufferSizeLength = kArrayBufferByteLengthLimit / elementSize;
let minUnallocatableSize =
kTypedArrayLengthLimit < bufferSizeLength
? kTypedArrayLengthLimit
: bufferSizeLength;
assertThrows(function() { assertThrows(function() {
new constr(maxSize); new constr(minUnallocatableSize);
}, RangeError); }, RangeError);
// This one must succeed.
new constr(minUnallocatableSize - 1);
} }
TestArray(Uint8Array); TestArray(Uint8Array, 1);
TestArray(Int8Array); TestArray(Int8Array, 1);
TestArray(Uint16Array); TestArray(Uint16Array, 2);
TestArray(Int16Array); TestArray(Int16Array, 2);
TestArray(Uint32Array); TestArray(Uint32Array, 4);
TestArray(Int32Array); TestArray(Int32Array, 4);
TestArray(Float32Array); TestArray(Float32Array, 4);
TestArray(Float64Array); TestArray(Float64Array, 8);
TestArray(Uint8ClampedArray); TestArray(Uint8ClampedArray, 1);
...@@ -18,9 +18,11 @@ try { ...@@ -18,9 +18,11 @@ try {
var __v_12 = new ArrayBuffer(2147483648); var __v_12 = new ArrayBuffer(2147483648);
ok = true; ok = true;
} catch (e) { } catch (e) {
// Can happen on 32 bit systems. // Can happen if the allocation fails.
} }
if (ok) { if (ok) {
var module = __f_61(this, null, __v_12); var module = __f_61(this, null, __v_12);
assertTrue(%IsAsmWasmCode(__f_61)); // This fails AsmJS memory size validation because of kV8MaxWasmMemoryPages
// value.
assertFalse(%IsAsmWasmCode(__f_61));
} }
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