From b5ffaf36cd5d17d325605d18cbaac6fb25f60e06 Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Mon, 11 Dec 2017 03:31:35 +0100 Subject: [PATCH] Portable not-so-smart Heap --- portable-assembly.d.ts | 4 ++++ src/glue/js.js | 18 +++++++++++++----- std/assembly/heap.ts | 8 ++++++-- std/portable/heap.js | 37 +++++++++++++++++++++++++++++++------ 4 files changed, 54 insertions(+), 13 deletions(-) diff --git a/portable-assembly.d.ts b/portable-assembly.d.ts index 709ab089..1e624a11 100644 --- a/portable-assembly.d.ts +++ b/portable-assembly.d.ts @@ -41,6 +41,10 @@ declare function select(ifTrue: T, ifFalse: T, condition: bool): T; declare function sqrt(value: T): T; /** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */ declare function trunc(value: T): T; +/** Loads a value of the specified type from memory. Type must be `u8`. */ +declare function load(offset: usize): T; +/** Stores a value of the specified type to memory. Type must be `u8`. */ +declare function store(offset: usize, value: T): void; /** Emits an unreachable operation that results in a runtime error when executed. */ declare function unreachable(): any; // sic diff --git a/src/glue/js.js b/src/glue/js.js index b7d415a2..921f3c96 100644 --- a/src/glue/js.js +++ b/src/glue/js.js @@ -1,4 +1,4 @@ -require("../../portable-assembly"); // not inherited from tsconfig by ts-node otherwise :( +require("../../portable-assembly"); // Copy Binaryen exports to global scope var globalScope = typeof window !== "undefined" && window || typeof global !== "undefined" && global || self; @@ -13,9 +13,17 @@ for (var key in binaryen) globalScope[key] = binaryen[key]; // Use Binaryen's heap -Object.defineProperties(globalScope['Heap'] = { - allocate: binaryen._malloc, - dispose: binaryen._free +Object.defineProperties(globalScope["Heap"] = { + allocate: function allocate(size) { + if (!size) return 0; // should be safe in our case + return binaryen._malloc(size); + }, + dispose: function dispose(ptr) { + if (ptr) binaryen._free(ptr); + }, + copy: function copy(dest, src, n) { + return binaryen._memcpy(dest, src, n); + } }, { free: { get: function() { return binaryen.HEAPU8.length; } }, used: { get: function() { return 0; } }, @@ -24,7 +32,7 @@ Object.defineProperties(globalScope['Heap'] = { globalScope["store"] = function store(ptr, val) { binaryen.HEAPU8[ptr] = val; }; -globalScope["load"] = function load_u8(ptr) { +globalScope["load"] = function load(ptr) { return binaryen.HEAPU8[ptr]; }; diff --git a/std/assembly/heap.ts b/std/assembly/heap.ts index 0771f92c..32aae957 100644 --- a/std/assembly/heap.ts +++ b/std/assembly/heap.ts @@ -9,9 +9,13 @@ let HEAP_OFFSET: usize = HEAP_START; // HEAP_START is a constant generated by th class Heap { static allocate(size: usize): usize { + if (!size) return 0; + const len: i32 = current_memory(); + if (HEAP_OFFSET + size > len << 16) + if(grow_memory(max(ceil(size / 65536), len * 2 - len)) < 0) + unreachable(); const ptr: usize = HEAP_OFFSET; - assert(ptr + size <= (current_memory() << 16)); - if (((HEAP_OFFSET += size) & ALIGN_MASK) != 0) // align next offset + if ((HEAP_OFFSET += size) & ALIGN_MASK) // align next offset HEAP_OFFSET = (HEAP_OFFSET | ALIGN_MASK) + 1; return ptr; } diff --git a/std/portable/heap.js b/std/portable/heap.js index 7d1597ae..2c91c7ab 100644 --- a/std/portable/heap.js +++ b/std/portable/heap.js @@ -1,9 +1,34 @@ var globalScope = typeof window !== "undefined" && window || typeof global !== "undefined" && global || self; -globalScope["Heap"] = { - allocate: function() { throw new Error("not implemented"); }, - dispose: function() { throw new Error("not implemented"); }, - used: 0, - free: 0, - size: 0 +var HEAP = new Uint8Array(65536); +var HEAP_OFFSET = 0; + +Object.defineProperties(globalScope["Heap"] = { + allocate: function allocate(size) { + if (!size) return 0; + if (HEAP_OFFSET + size > HEAP.length) { + var oldHeap = HEAP; + HEAP = new Uint8Array(Math.max(HEAP.length + size, HEAP.length * 2)); + HEAP.set(oldHeap); + } + var ptr = HEAP_OFFSET; + if ((HEAP_OFFSET += size) & 7) + HEAP_OFFSET = (HEAP_OFFSET | 7) + 1; + return ptr; + }, + dispose: function dispose() { }, + copy: function copy(dest, src, n) { + HEAP.set(HEAP.subarray(src, src + n), dest); + return dest; + } +}, { + used: { get: function get_used() { return HEAP_OFFSET; } }, + free: { get: function get_free() { return HEAP.length - HEAP_OFFSET; } }, + size: { get: function get_size() { return HEAP.length; } } +}); +globalScope["store"] = function store(ptr, val) { + binaryen.HEAPU8[ptr] = val; +}; +globalScope["load"] = function load(ptr) { + return binaryen.HEAPU8[ptr]; };