diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj
index 38d6f035e6cf3..09123abe3d19b 100644
--- a/src/mono/browser/browser.proj
+++ b/src/mono/browser/browser.proj
@@ -200,6 +200,7 @@
+
diff --git a/src/mono/browser/runtime/assets.ts b/src/mono/browser/runtime/assets.ts
index 7cdff19d8a84c..318750ad67258 100644
--- a/src/mono/browser/runtime/assets.ts
+++ b/src/mono/browser/runtime/assets.ts
@@ -7,7 +7,7 @@ import cwraps from "./cwraps";
import { mono_wasm_load_icu_data } from "./icu";
import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
import { mono_log_info, mono_log_debug, parseSymbolMapFile } from "./logging";
-import { mono_wasm_load_bytes_into_heap } from "./memory";
+import { mono_wasm_load_bytes_into_heap_persistent } from "./memory";
import { endMeasure, MeasuredBlock, startMeasure } from "./profiler";
import { AssetEntry } from "./types";
import { VoidPtr } from "./types/emscripten";
@@ -37,7 +37,7 @@ export function instantiate_asset (asset: AssetEntry, url: string, bytes: Uint8A
// falls through
case "heap":
case "icu":
- offset = mono_wasm_load_bytes_into_heap(bytes);
+ offset = mono_wasm_load_bytes_into_heap_persistent(bytes);
break;
case "vfs": {
diff --git a/src/mono/browser/runtime/dotnet.d.ts b/src/mono/browser/runtime/dotnet.d.ts
index e0d644558b46b..07a21e06dc7f0 100644
--- a/src/mono/browser/runtime/dotnet.d.ts
+++ b/src/mono/browser/runtime/dotnet.d.ts
@@ -20,6 +20,7 @@ declare interface Int32Ptr extends NativePointer {
declare interface EmscriptenModule {
_malloc(size: number): VoidPtr;
_free(ptr: VoidPtr): void;
+ _sbrk(size: number): VoidPtr;
out(message: string): void;
err(message: string): void;
ccall(ident: string, returnType?: string | null, argTypes?: string[], args?: any[], opts?: any): T;
diff --git a/src/mono/browser/runtime/memory.ts b/src/mono/browser/runtime/memory.ts
index 8205e6fd8098f..5a0ac71d3b329 100644
--- a/src/mono/browser/runtime/memory.ts
+++ b/src/mono/browser/runtime/memory.ts
@@ -325,7 +325,24 @@ export function withStackAlloc (bytesWanted: number, f: (pt
// @bytes must be a typed array. space is allocated for it in the native heap
// and it is copied to that location. returns the address of the allocation.
export function mono_wasm_load_bytes_into_heap (bytes: Uint8Array): VoidPtr {
- const memoryOffset = Module._malloc(bytes.length);
+ // pad sizes by 16 bytes for simd
+ const memoryOffset = Module._malloc(bytes.length + 16);
+ const heapBytes = new Uint8Array(localHeapViewU8().buffer, memoryOffset, bytes.length);
+ heapBytes.set(bytes);
+ return memoryOffset;
+}
+
+// @bytes must be a typed array. space is allocated for it in memory
+// and it is copied to that location. returns the address of the data.
+// the result pointer *cannot* be freed because malloc is bypassed for speed.
+export function mono_wasm_load_bytes_into_heap_persistent (bytes: Uint8Array): VoidPtr {
+ // pad sizes by 16 bytes for simd
+ const desiredSize = bytes.length + 16;
+ // wasm memory page size is 64kb. allocations smaller than that are probably best
+ // serviced by malloc
+ const memoryOffset = (desiredSize < (64 * 1024))
+ ? Module._malloc(desiredSize)
+ : Module._sbrk(desiredSize);
const heapBytes = new Uint8Array(localHeapViewU8().buffer, memoryOffset, bytes.length);
heapBytes.set(bytes);
return memoryOffset;
diff --git a/src/mono/browser/runtime/types/emscripten.ts b/src/mono/browser/runtime/types/emscripten.ts
index a9691cde49061..e2cc8e7c73ce6 100644
--- a/src/mono/browser/runtime/types/emscripten.ts
+++ b/src/mono/browser/runtime/types/emscripten.ts
@@ -26,6 +26,7 @@ export declare interface EmscriptenModule {
// this should match emcc -s EXPORTED_FUNCTIONS
_malloc(size: number): VoidPtr;
_free(ptr: VoidPtr): void;
+ _sbrk(size: number): VoidPtr;
// this should match emcc -s EXPORTED_RUNTIME_METHODS
out(message: string): void;