Skip to content
Permalink
Browse files
Allow WASM to use up to 4GB
https://bugs.webkit.org/show_bug.cgi?id=229353
rdar://81603447

Reviewed by Yusuke Suzuki.

JSTests:

The big-wasm-memory/wasm-memory-requested... tests used to simply expect an OOM at 2GB.
They now expect success at 4GB, and failure at 4GB+1.
The exceptions they expect are now more specific, as previously there was a rounding issue that was causing the specific exceptions not to be thrown (resulting in the generic OOM exception later in the code).
Finally I made them only run on 64-bit platforms since we don't support typed arrays >=2GB on 32-bits, and I made them only run in one configuration since they can take a little bit of time.

I also added a few new tests, specifically checking our handling of large typed arrays, and especially of indices above INT32_MAX.

* stress/big-wasm-memory-grow-no-max.js:
(test):
* stress/big-wasm-memory-grow.js:
(test):
* stress/big-wasm-memory.js:
(test):
* stress/typed-array-always-large.js: Added.
(getArrayLength):
(getByVal):
(putByVal):
(test):
* stress/typed-array-eventually-large.js: Added.
(getArrayLength):
(getByVal):
(putByVal):
(test):
* stress/typed-array-large-eventually-oob.js: Added.
(getArrayLength):
(getByVal):
(putByVal):
(test):
* wasm/regress/wasm-memory-requested-more-than-MAX_ARRAY_BUFFER_SIZE-2.js:
* wasm/regress/wasm-memory-requested-more-than-MAX_ARRAY_BUFFER_SIZE.js:

Source/JavaScriptCore:

While increasing MAX_ARRAY_BUFFER_SIZE to 4GB was easy, it was not remotely the only thing required to get this to work:
- 4GB is not representable in a uint32_t, so I changed all length of ArrayBuffer/TypedArray/etc.. to being size_t.
- This also required changing NewTypedArray in all of LLInt/Baseline/DFG/FTL to accept a non-int32 size.
    In order to avoid performance regressions, I had to add speculation in the DFG/FTL, which now have two versions of NewTypedArray (one that takes an Int32 and one that takes a StrictInt52)
- Similarly, GetArrayLength and GetTypedArrayByteOffset now can either return an Int32 or a larger number.
    I also had to split them in the DFG/FTL, see GetTypedArrayLengthAsInt52 and GetTypedArrayByteOffsetAsInt52 for examples
- In turns, I had to add CheckInBoundsInt52 since CheckInBounds could not accept the result of GetTypedArrayLengthAsInt52
- I modified the runtime functions for GetByVal/PutByVal/DataViewGet/DataViewSet/AtomicsXXX to accept non-Int32 indices, since for {Int8/UInt8/UInt8Clamped}Array, a maximum size of 4GB implies indices > 2B.
- I added a "mayBeLargeTypedArray" bit to ArrayProfile/UnlinkedArrayProfile/DFG::ArrayMode to track whether such a non-Int32 index was seen to allow proper speculation and specialization of fast paths in the DFG/FTL.
    I then updated the runtime functions used by the slow paths to correctly update it.

Unfortunately I ran out of time to add all the speculations/update all the fast paths.
So the following will have to wait for a follow-up patch:
- Accepting large indices in the fast path of GetByVal in the LLInt
- Accepting large indices in the fast paths generated by AccessCase/PolymorphicAccess
- Accepting large indices in the fast paths generated by the DFG/FTL for each of GetByVal/PutByVal/DataViewGet/DataViewSet/AtomicsXXX

The current patch is functional, it will just have dreadful performance if trying to use indices >2B in a {Int8/UInt8/UInt8Clamped}Array.

Other minor changes in this patch:
- Fixed an undefined behavior in ArrayBuffer::createInternal where memcpy could be called on nullptr (the spec explicitly bans this even when length is 0)
- Replaced some repetitive and error-prone bounds checking by calls to WTF::isSumSmallerThanOrEqual, which is clearer, shorter, and reuse CheckedArithmetic facilities to avoid overflow issues.
- Fixed a variety of obsolete comments
- Added support for branch64(RelationalCondition cond, RegisterID left, Imm64 right)
    (there was already support for the same but with TrustedImm64)
- Made various AbstractMacroAssembler function constexpr as part of the previous point

* assembler/AbstractMacroAssembler.cpp:
* assembler/AbstractMacroAssembler.h:
(JSC::AbstractMacroAssembler::TrustedImmPtr::TrustedImmPtr):
(JSC::AbstractMacroAssembler::TrustedImmPtr::asIntptr):
(JSC::AbstractMacroAssembler::TrustedImmPtr::asPtr):
(JSC::AbstractMacroAssembler::ImmPtr::ImmPtr):
(JSC::AbstractMacroAssembler::ImmPtr::asTrustedImmPtr):
(JSC::AbstractMacroAssembler::TrustedImm32::TrustedImm32):
(JSC::AbstractMacroAssembler::Imm32::Imm32):
(JSC::AbstractMacroAssembler::Imm32::asTrustedImm32 const):
(JSC::AbstractMacroAssembler::TrustedImm64::TrustedImm64):
(JSC::AbstractMacroAssembler::Imm64::Imm64):
(JSC::AbstractMacroAssembler::Imm64::asTrustedImm64 const):
(JSC::AbstractMacroAssembler::canBlind):
(JSC::AbstractMacroAssembler::shouldBlindForSpecificArch):
* assembler/MacroAssembler.h:
(JSC::MacroAssembler::branch64):
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::shouldBlindForSpecificArch):
(JSC::MacroAssemblerARM64::branch64):
* assembler/MacroAssemblerARM64E.h:
(JSC::MacroAssemblerARM64E::untagArrayPtrLength64):
(JSC::MacroAssemblerARM64E::untagArrayPtrLength32): Deleted.
* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::canBlind):
(JSC::MacroAssemblerX86Common::shouldBlindForSpecificArch):
* bytecode/AccessCase.cpp:
(JSC::AccessCase::needsScratchFPR const):
(JSC::AccessCase::generateWithGuard):
* bytecode/ArrayProfile.h:
(JSC::ArrayProfile::setMayBeLargeTypedArray):
(JSC::ArrayProfile::mayBeLargeTypedArray const):
(JSC::UnlinkedArrayProfile::UnlinkedArrayProfile):
(JSC::UnlinkedArrayProfile::update):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGArgumentsEliminationPhase.cpp:
* dfg/DFGArrayMode.cpp:
(JSC::DFG::ArrayMode::refine const):
* dfg/DFGArrayMode.h:
(JSC::DFG::ArrayMode::ArrayMode):
(JSC::DFG::ArrayMode::mayBeLargeTypedArray const):
(JSC::DFG::ArrayMode::withType const):
(JSC::DFG::ArrayMode::withSpeculation const):
(JSC::DFG::ArrayMode::withConversion const):
(JSC::DFG::ArrayMode::withTypeAndConversion const):
(JSC::DFG::ArrayMode::withArrayClassAndSpeculationAndMayBeLargeTypedArray const):
(JSC::DFG::ArrayMode::speculationFromProfile):
(JSC::DFG::ArrayMode::withSpeculationFromProfile const):
(JSC::DFG::ArrayMode::withProfile const):
(JSC::DFG::ArrayMode::operator== const):
(JSC::DFG::ArrayMode::withArrayClass const): Deleted.
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicGetter):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGCommon.h:
(JSC::DFG::enableInt52):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::convertToGetArrayLength):
(JSC::DFG::FixupPhase::prependGetArrayLength): Deleted.
* dfg/DFGGenerationInfo.h:
* dfg/DFGHeapLocation.cpp:
(WTF::printInternal):
* dfg/DFGHeapLocation.h:
* dfg/DFGIntegerRangeOptimizationPhase.cpp:
* dfg/DFGNode.h:
(JSC::DFG::Node::hasStorageChild const):
(JSC::DFG::Node::storageChildIndex):
(JSC::DFG::Node::hasArrayMode):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
(JSC::DFG::putByVal):
(JSC::DFG::newTypedArrayWithSize):
(JSC::DFG::JSC_DEFINE_JIT_OPERATION):
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSSALoweringPhase.cpp:
(JSC::DFG::SSALoweringPhase::handleNode):
(JSC::DFG::SSALoweringPhase::lowerBoundsCheck):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::jumpForTypedArrayOutOfBounds):
(JSC::DFG::SpeculativeJIT::emitTypedArrayBoundsCheck):
(JSC::DFG::SpeculativeJIT::compileGetByValOnIntTypedArray):
(JSC::DFG::SpeculativeJIT::compilePutByValForIntTypedArray):
(JSC::DFG::SpeculativeJIT::compileGetByValOnFloatTypedArray):
(JSC::DFG::SpeculativeJIT::compilePutByValForFloatTypedArray):
(JSC::DFG::SpeculativeJIT::cageTypedArrayStorage):
(JSC::DFG::SpeculativeJIT::compileGetTypedArrayByteOffset):
(JSC::DFG::SpeculativeJIT::compileGetArrayLength):
(JSC::DFG::SpeculativeJIT::compileNewTypedArrayWithSize):
(JSC::DFG::SpeculativeJIT::emitNewTypedArrayWithSizeInRegister):
(JSC::DFG::SpeculativeJIT::compileNewTypedArray):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compileGetByVal):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compileNewTypedArrayWithInt52Size):
(JSC::DFG::SpeculativeJIT::compileGetTypedArrayLengthAsInt52):
(JSC::DFG::SpeculativeJIT::compileGetTypedArrayByteOffsetAsInt52):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGTypeCheckHoistingPhase.cpp:
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
* dfg/DFGValidate.cpp:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::validateAIState):
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::emitGetTypedArrayByteOffsetExceptSettingResult):
(JSC::FTL::DFG::LowerDFGToB3::compileGetTypedArrayByteOffset):
(JSC::FTL::DFG::LowerDFGToB3::compileGetTypedArrayByteOffsetAsInt52):
(JSC::FTL::DFG::LowerDFGToB3::compileGetArrayLength):
(JSC::FTL::DFG::LowerDFGToB3::compileGetTypedArrayLengthAsInt52):
(JSC::FTL::DFG::LowerDFGToB3::compileCheckInBoundsInt52):
(JSC::FTL::DFG::LowerDFGToB3::compilePutByVal):
(JSC::FTL::DFG::LowerDFGToB3::compileNewTypedArray):
(JSC::FTL::DFG::LowerDFGToB3::emitNewTypedArrayWithSize):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
* ftl/FTLOutput.h:
(JSC::FTL::Output::load64NonNegative):
* jit/IntrinsicEmitter.cpp:
(JSC::IntrinsicGetterAccessCase::emitIntrinsicGetter):
* jit/JITOperations.cpp:
(JSC::putByVal):
(JSC::getByVal):
* llint/LLIntOfflineAsmConfig.h:
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::getByVal):
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/ArrayBuffer.cpp:
(JSC::SharedArrayBufferContents::SharedArrayBufferContents):
(JSC::ArrayBufferContents::ArrayBufferContents):
(JSC::ArrayBufferContents::tryAllocate):
(JSC::ArrayBuffer::create):
(JSC::ArrayBuffer::createAdopted):
(JSC::ArrayBuffer::createFromBytes):
(JSC::ArrayBuffer::tryCreate):
(JSC::ArrayBuffer::createUninitialized):
(JSC::ArrayBuffer::tryCreateUninitialized):
(JSC::ArrayBuffer::createInternal):
(JSC::ArrayBuffer::clampValue):
(JSC::ArrayBuffer::clampIndex const):
(JSC::ArrayBuffer::sliceWithClampedIndex const):
* runtime/ArrayBuffer.h:
(JSC::ArrayBufferContents::sizeInBytes const):
(JSC::ArrayBuffer::byteLength const):
(JSC::ArrayBuffer::gcSizeEstimateInBytes const):
* runtime/ArrayBufferView.cpp:
(JSC::ArrayBufferView::ArrayBufferView):
* runtime/ArrayBufferView.h:
(JSC::ArrayBufferView::byteOffset const):
(JSC::ArrayBufferView::byteLength const):
(JSC::ArrayBufferView::verifyByteOffsetAlignment):
(JSC::ArrayBufferView::verifySubRangeLength):
(JSC::ArrayBufferView::clampOffsetAndNumElements):
(JSC::ArrayBufferView::setImpl):
(JSC::ArrayBufferView::setRangeImpl):
(JSC::ArrayBufferView::getRangeImpl):
(JSC::ArrayBufferView::zeroRangeImpl):
(JSC::ArrayBufferView::calculateOffsetAndLength): Deleted.
* runtime/AtomicsObject.cpp:
* runtime/DataView.cpp:
(JSC::DataView::DataView):
(JSC::DataView::create):
* runtime/DataView.h:
* runtime/GenericTypedArrayView.h:
* runtime/GenericTypedArrayViewInlines.h:
(JSC::GenericTypedArrayView<Adaptor>::GenericTypedArrayView):
(JSC::GenericTypedArrayView<Adaptor>::create):
(JSC::GenericTypedArrayView<Adaptor>::tryCreate):
(JSC::GenericTypedArrayView<Adaptor>::createUninitialized):
(JSC::GenericTypedArrayView<Adaptor>::tryCreateUninitialized):
(JSC::GenericTypedArrayView<Adaptor>::subarray const): Deleted.
* runtime/JSArrayBufferView.cpp:
(JSC::JSArrayBufferView::ConstructionContext::ConstructionContext):
(JSC::JSArrayBufferView::byteLength const):
(JSC::JSArrayBufferView::slowDownAndWasteMemory):
(JSC::JSArrayBufferView::possiblySharedImpl):
* runtime/JSArrayBufferView.h:
(JSC::JSArrayBufferView::sizeOf):
(JSC::JSArrayBufferView::ConstructionContext::length const):
(JSC::JSArrayBufferView::length const):
* runtime/JSArrayBufferViewInlines.h:
(JSC::JSArrayBufferView::byteOffsetImpl):
(JSC::JSArrayBufferView::byteOffset):
(JSC::JSArrayBufferView::byteOffsetConcurrently):
* runtime/JSCJSValue.h:
* runtime/JSCJSValueInlines.h:
(JSC::JSValue::toIndex const):
(JSC::JSValue::toTypedArrayIndex const):
* runtime/JSDataView.cpp:
(JSC::JSDataView::create):
(JSC::JSDataView::createUninitialized):
(JSC::JSDataView::set):
(JSC::JSDataView::setIndex):
* runtime/JSDataView.h:
* runtime/JSDataViewPrototype.cpp:
(JSC::getData):
(JSC::setData):
* runtime/JSGenericTypedArrayView.h:
* runtime/JSGenericTypedArrayViewConstructorInlines.h:
(JSC::constructGenericTypedArrayViewWithArguments):
(JSC::constructGenericTypedArrayViewImpl):
* runtime/JSGenericTypedArrayViewInlines.h:
(JSC::JSGenericTypedArrayView<Adaptor>::create):
(JSC::JSGenericTypedArrayView<Adaptor>::createWithFastVector):
(JSC::JSGenericTypedArrayView<Adaptor>::createUninitialized):
(JSC::JSGenericTypedArrayView<Adaptor>::validateRange):
(JSC::JSGenericTypedArrayView<Adaptor>::setWithSpecificType):
(JSC::JSGenericTypedArrayView<Adaptor>::set):
* runtime/JSObject.h:
(JSC::JSObject::putByIndexInline):
(JSC::JSObject::tryGetIndexQuickly const):
(JSC::JSObject::trySetIndexQuickly):
(JSC::JSObject::canSetIndexQuickly): Deleted.
* runtime/JSObjectInlines.h:
(JSC::JSObject::getIndexQuicklyForTypedArray const):
(JSC::JSObject::setIndexQuicklyForArrayStorageIndexingType):
(JSC::JSObject::trySetIndexQuicklyForTypedArray):
(JSC::JSObject::canSetIndexQuicklyForTypedArray const): Deleted.
* runtime/Operations.h:
(JSC::getByValWithIndex):
* wasm/WasmPageCount.h:

Source/WebCore:

Some parts of WebCore use TypedArrays, and would not build after I made the length() function on typed arrays return UCPURegister instead of uint32_t.
Most fixes were trivial, the only exception is SerializedScriptValue.cpp, where I had to increment the version number, as ArrayBuffer (and ArrayBufferViews) now write/read their length in a 64-bit field (and same for the byteOffset of ArrayBufferView).

I also had to add a test in PixelBuffer.cpp that the size of the ArrayBuffer it will try to allocate is < INT32_MAX.
Otherwise, the test LayoutTests/fast/shapes/shape-outside-floats/shape-outside-imagedata-overflow.html which checks that very large images are properly rejected without crashing, would timeout.

No new tests, as the code is already extensively covered by existing tests, and I implemented no new features.

* Modules/webaudio/AudioBuffer.cpp:
(WebCore::AudioBuffer::copyFromChannel):
(WebCore::AudioBuffer::copyToChannel):
* Modules/webaudio/RealtimeAnalyser.cpp:
(WebCore::RealtimeAnalyser::getByteFrequencyData):
(WebCore::RealtimeAnalyser::getFloatTimeDomainData):
(WebCore::RealtimeAnalyser::getByteTimeDomainData):
* bindings/js/SerializedScriptValue.cpp:
(WebCore::CloneSerializer::dumpArrayBufferView):
(WebCore::CloneSerializer::dumpImageBitmap):
(WebCore::CloneSerializer::dumpIfTerminal):
(WebCore::CloneDeserializer::readArrayBufferImpl):
(WebCore::CloneDeserializer::readArrayBuffer):
(WebCore::CloneDeserializer::readArrayBufferViewImpl):
(WebCore::CloneDeserializer::readArrayBufferView):
* bindings/js/SerializedScriptValue.h:
(WebCore::SerializedScriptValue::encode const):
(WebCore::SerializedScriptValue::decode):
* fileapi/NetworkSendQueue.cpp:
(WebCore::NetworkSendQueue::enqueue):
* platform/graphics/PixelBuffer.cpp:
(WebCore::PixelBuffer::tryCreate):
* platform/graphics/iso/ISOBox.h:

Source/WTF:

Made some of CheckedArithmetic constexpr, and added isSumSmallerThanOrEqual since it is a commonly used test in ArrayBuffer and easy to get wrong in terms of overflow.

* wtf/CheckedArithmetic.h:
(WTF::isInBounds):
(WTF::convertSafely):
(WTF::isSumSmallerThanOrEqual):

LayoutTests:

Rebaselined three following tests as different or fewer error messages appear on the console.
Also changed currentVersion in serialized-script-value.html from 9 to 10.

* fast/canvas/canvas-getImageData-invalid-result-buffer-crash-expected.txt:
* fast/storage/serialized-script-value.html:
* webaudio/OfflineAudioContext-bad-buffer-crash-expected.txt:
* webaudio/OfflineAudioContext/bad-buffer-length-expected.txt:


Canonical link: https://commits.webkit.org/243125@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@284330 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Robin Morisset committed Oct 17, 2021
1 parent 205e4ef commit a233fa74da11aa6011bb8a1c021eeecd3d0cad37
Show file tree
Hide file tree
Showing 94 changed files with 2,017 additions and 732 deletions.
@@ -1,3 +1,42 @@
2021-10-16 Robin Morisset <rmorisset@apple.com>

Allow WASM to use up to 4GB
https://bugs.webkit.org/show_bug.cgi?id=229353
rdar://81603447

Reviewed by Yusuke Suzuki.

The big-wasm-memory/wasm-memory-requested... tests used to simply expect an OOM at 2GB.
They now expect success at 4GB, and failure at 4GB+1.
The exceptions they expect are now more specific, as previously there was a rounding issue that was causing the specific exceptions not to be thrown (resulting in the generic OOM exception later in the code).
Finally I made them only run on 64-bit platforms since we don't support typed arrays >=2GB on 32-bits, and I made them only run in one configuration since they can take a little bit of time.

I also added a few new tests, specifically checking our handling of large typed arrays, and especially of indices above INT32_MAX.

* stress/big-wasm-memory-grow-no-max.js:
(test):
* stress/big-wasm-memory-grow.js:
(test):
* stress/big-wasm-memory.js:
(test):
* stress/typed-array-always-large.js: Added.
(getArrayLength):
(getByVal):
(putByVal):
(test):
* stress/typed-array-eventually-large.js: Added.
(getArrayLength):
(getByVal):
(putByVal):
(test):
* stress/typed-array-large-eventually-oob.js: Added.
(getArrayLength):
(getByVal):
(putByVal):
(test):
* wasm/regress/wasm-memory-requested-more-than-MAX_ARRAY_BUFFER_SIZE-2.js:
* wasm/regress/wasm-memory-requested-more-than-MAX_ARRAY_BUFFER_SIZE.js:

2021-10-15 Robin Morisset <rmorisset@apple.com>

Revert r284230, my last fixes to the watch build make it break tests
@@ -1,4 +1,5 @@
//@ skip if $memoryLimited
//@ skip if $memoryLimited or ($architecture != "arm64" && $architecture != "x86-64")
//@ runDefault()

function test() {

@@ -24,19 +25,25 @@ function test() {
for (var i = 0; i < 10000; ++i)
foo(okArray);

var ok = false;
try {
let maxSize = 0x10000;
{
var memory = new WebAssembly.Memory({ initial: 0x1000 });
memory.grow(0x7000);
memory.grow(maxSize - 0x1000);
var result = foo(new Uint8Array(memory.buffer));
if (result !== void 0)
throw "Error: bad result at end: " + result;
}

var ok = false;
try {
var memory = new WebAssembly.Memory({ initial: 0x1000 });
memory.grow(maxSize - 0x1000 + 1);
var result = foo(new Uint8Array(memory.buffer));
ok = true;
} catch (e) {
if (e.toString() != "RangeError: Out of memory")
if (e.toString() != "RangeError: WebAssembly.Memory.grow expects the grown size to be a valid page count")
throw e;
}

if (ok)
throw "Error: did not throw error";

@@ -1,4 +1,5 @@
//@ skip if $memoryLimited
//@ skip if $memoryLimited or ($architecture != "arm64" && $architecture != "x86-64")
//@ runDefault()

function test() {

@@ -24,19 +25,26 @@ function test() {
for (var i = 0; i < 10000; ++i)
foo(okArray);

var ok = false;
try {
var memory = new WebAssembly.Memory({ initial: 0x1000, maximum: 0x8000 });
memory.grow(0x7000);

let maxSize = 0x10000;
{
var memory = new WebAssembly.Memory({ initial: 0x1000, maximum: maxSize });
memory.grow(maxSize - 0x1000);
var result = foo(new Uint8Array(memory.buffer));
if (result !== void 0)
throw "Error: bad result at end: " + result;
}

var ok = false;
try {
var memory = new WebAssembly.Memory({ initial: 0x1000, maximum: maxSize+1 });
memory.grow(maxSize + 1 - 0x1000);
var result = foo(new Uint8Array(memory.buffer));
ok = true;
} catch (e) {
if (e.toString() != "RangeError: Out of memory")
if (e.toString() != "RangeError: WebAssembly.Memory 'maximum' page count is too large")
throw e;
}

if (ok)
throw "Error: did not throw error";

@@ -1,4 +1,5 @@
//@ skip if $memoryLimited
//@ skip if $memoryLimited or ($architecture != "arm64" && $architecture != "x86-64")
//@ runDefault()

function test() {

@@ -24,17 +25,21 @@ function test() {
for (var i = 0; i < 10000; ++i)
foo(okArray);

var ok = false;
try {
var result = foo(new Uint8Array(new WebAssembly.Memory({ initial: 0x8000, maximum: 0x8000 }).buffer));
let maxSize = 0x10000;
{
var result = foo(new Uint8Array(new WebAssembly.Memory({ initial: maxSize, maximum: maxSize }).buffer));
if (result !== void 0)
throw "Error: bad result at end: " + result;
}

var ok = false;
try {
var result = foo(new Uint8Array(new WebAssembly.Memory({ initial: maxSize+1, maximum: maxSize+1 }).buffer));
ok = true;
} catch (e) {
if (e.toString() != "RangeError: Out of memory")
if (e.toString() != "RangeError: WebAssembly.Memory 'initial' page count is too large")
throw e;
}

if (ok)
throw "Error: did not throw error";

@@ -0,0 +1,42 @@
//@ skip if $memoryLimited or ($architecture != "arm64" && $architecture != "x86-64")
//@ runDefault()

function getArrayLength(array)
{
return array.length;
}
noInline(getArrayLength);
function getByVal(array, index)
{
return array[index];
}
noInline(getByVal);
function putByVal(array, index, value)
{
array[index] = value;
}
noInline(putByVal);

let oneGiga = 1024 * 1024 * 1024;

function test(array, actualLength, string)
{
for (var i = 0; i < 100000; ++i) {
var l = getArrayLength(array);
if (l != actualLength)
throw ("Wrong array length: " + l + " instead of the expected " + actualLength + " in case " + string);
var index = (2 * oneGiga) + i;
var value = i % 100;
putByVal(array, index, value);
var result = getByVal(array, index);
if (result != value)
throw ("Expected " + value + " but got " + result + " in case " + string);
}
}

let threeGigs = 3 * oneGiga;
let fourGigs = 4 * oneGiga;

test(new Int8Array(threeGigs), threeGigs, "Int8Array/3GB");
test(new Uint8Array(fourGigs), fourGigs, "Uint8Array/4GB");
test(new Uint8ClampedArray(threeGigs), threeGigs, "Uint8ClampedArray/3GB");
@@ -0,0 +1,48 @@
//@ skip if $memoryLimited or ($architecture != "arm64" && $architecture != "x86-64")
//@ runDefault()

function getArrayLength(array)
{
return array.length;
}
noInline(getArrayLength);
function getByVal(array, index)
{
return array[index];
}
noInline(getByVal);
function putByVal(array, index, value)
{
array[index] = value;
}
noInline(putByVal);

let oneGiga = 1024 * 1024 * 1024;

function test(array, actualLength, string)
{
for (var i = 0; i < 100000; ++i) {
var l = getArrayLength(array);
if (l != actualLength)
throw ("Wrong array length: " + l + " instead of the expected " + actualLength + " in case " + string);
var index = i;
var value = i % 100;
putByVal(array, index, value);
var result = getByVal(array, index);
if (result != value)
throw ("Expected " + value + " but got " + result + " in case " + string);
}
var value = 42;
var index = 2 * oneGiga + 10;
putByVal(array, index, value);
var result = getByVal(array, index);
if (result != value)
throw ("Expected " + value + " but got " + result + " in case " + string);
}

let threeGigs = 3 * oneGiga;
let fourGigs = 4 * oneGiga;

test(new Int8Array(threeGigs), threeGigs, "Int8Array/3GB");
test(new Uint8Array(fourGigs), fourGigs, "Uint8Array/4GB");
test(new Uint8ClampedArray(threeGigs), threeGigs, "Uint8ClampedArray/3GB");
@@ -0,0 +1,48 @@
//@ skip if $memoryLimited or ($architecture != "arm64" && $architecture != "x86-64")
//@ runDefault()
function getArrayLength(array)
{
return array.length;
}
noInline(getArrayLength);
function getByVal(array, index)
{
return array[index];
}
noInline(getByVal);
function putByVal(array, index, value)
{
'use strict';
array[index] = value;
}
noInline(putByVal);

let oneGiga = 1024 * 1024 * 1024;

function test(array, actualLength, string)
{
for (var i = 0; i < 100000; ++i) {
var l = getArrayLength(array);
if (l != actualLength)
throw ("Wrong array length: " + l + " instead of the expected " + actualLength + " in case " + string);
var index = i;
var value = i % 100;
putByVal(array, index, value);
var result = getByVal(array, index);
if (result != value)
throw ("Expected " + value + " but got " + result + " in case " + string);
}
var value = 42;
var index = actualLength + 10;
putByVal(array, index, value);
var result = getByVal(array, index);
if (result != undefined)
throw ("In out-of-bounds case, expected undefined but got " + result + " in case " + string);
}

let threeGigs = 3 * oneGiga;
let fourGigs = 4 * oneGiga;

test(new Int8Array(threeGigs), threeGigs, "Int8Array/3GB");
test(new Uint8Array(fourGigs), fourGigs, "Uint8Array/4GB");
test(new Uint8ClampedArray(threeGigs), threeGigs, "Uint8ClampedArray/3GB");
@@ -76,13 +76,13 @@ class WasmModuleBuilder {
var exception;
try {
var module = new WasmModuleBuilder();
module.addMemory(32768);
module.addMemory((2**16) + 1);
module.instantiate();
} catch (e) {
exception = e;
}

if (exception != "RangeError: Out of memory") {
if (exception != "CompileError: WebAssembly.Module doesn't parse at byte 15: Memory's initial page count of 65537 is invalid (evaluating 'new WebAssembly.Module(this.toBuffer())')") {
print(exception);
throw "FAILED";
}
@@ -1,10 +1,10 @@
var exception;

try {
new WebAssembly.Memory({ initial: 0x8000, maximum: 0x8000 }).buffer;
new WebAssembly.Memory({ initial: 0x10001, maximum: 0x10001 }).buffer;
} catch (e) {
exception = e;
}

if (exception != "RangeError: Out of memory")
throw "FAILED";
if (exception != "RangeError: WebAssembly.Memory 'initial' page count is too large")
throw "FAILED, exception was: " + exception;
@@ -1,3 +1,19 @@
2021-10-16 Robin Morisset <rmorisset@apple.com>

Allow WASM to use up to 4GB
https://bugs.webkit.org/show_bug.cgi?id=229353
rdar://81603447

Reviewed by Yusuke Suzuki.

Rebaselined three following tests as different or fewer error messages appear on the console.
Also changed currentVersion in serialized-script-value.html from 9 to 10.

* fast/canvas/canvas-getImageData-invalid-result-buffer-crash-expected.txt:
* fast/storage/serialized-script-value.html:
* webaudio/OfflineAudioContext-bad-buffer-crash-expected.txt:
* webaudio/OfflineAudioContext/bad-buffer-length-expected.txt:

2021-10-16 Tim Horton <timothy_horton@apple.com>

Unreviewed, revert r284181
@@ -6,7 +6,7 @@
<body>
<script>

const currentVersion = 0x09;
const currentVersion = 0x0a;

// Here's a little Q&D helper for future adventurers needing to rebaseline this.

@@ -1,4 +1,3 @@
CONSOLE MESSAGE: Failed to construct internal AudioBuffer with 1 channel(s), a sample rate of 44000 and a length of 536870912.
This test passes if it does not crash.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -1,4 +1,3 @@
CONSOLE MESSAGE: Failed to construct internal AudioBuffer with 1 channel(s), a sample rate of 44000 and a length of 536870912.
Make sure that the length returned by OfflineAudioContext even if we failed to construct the rendering buffer.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".

0 comments on commit a233fa7

Please sign in to comment.