Polyfill move_memory and set_memory and remove Heap

This commit is contained in:
dcodeIO
2018-01-14 02:30:20 +01:00
parent 2c009c67d3
commit ad469ca445
33 changed files with 8529 additions and 5057 deletions

View File

@ -10,5 +10,5 @@ export const GETTER_PREFIX = "get:";
export const SETTER_PREFIX = "set:"; export const SETTER_PREFIX = "set:";
/** Delimiter used between class names and instance members. */ /** Delimiter used between class names and instance members. */
export const INSTANCE_DELIMITER = "#"; export const INSTANCE_DELIMITER = "#";
/** Delimited used between class and namespace names and static members. */ /** Delimiter used between class and namespace names and static members. */
export const STATIC_DELIMITER = "."; export const STATIC_DELIMITER = ".";

View File

@ -14,23 +14,17 @@ for (var key in binaryen)
if (/^_(?:Binaryen|Relooper)/.test(key)) if (/^_(?:Binaryen|Relooper)/.test(key))
globalScope[key] = binaryen[key]; globalScope[key] = binaryen[key];
// Use Binaryen's heap // Use Binaryen's heap instead of std heap
Object.defineProperties(globalScope["Heap"] = { globalScope["allocate_memory"] = function allocate_memory(size) {
allocate: function allocate(size) { if (!size) return 0; // should be safe in our case
if (!size) return 0; // should be safe in our case return binaryen._malloc(size);
return binaryen._malloc(size); };
}, globalScope["free_memory"] = function free_memory(ptr) {
dispose: function dispose(ptr) { if (ptr) binaryen._free(ptr);
if (ptr) binaryen._free(ptr); };
}, globalScope["move_memory"] = function move_memory(dest, src, n) {
copy: function copy(dest, src, n) { return binaryen._memmove(dest, src, n);
return binaryen._memcpy(dest, src, n); };
}
}, {
free: { get: function() { return binaryen.HEAPU8.length; } },
used: { get: function() { return 0; } },
size: { get: function() { return binaryen.HEAPU8.length; } }
});
globalScope["store"] = function store(ptr, val) { globalScope["store"] = function store(ptr, val) {
binaryen.HEAPU8[ptr] = val; binaryen.HEAPU8[ptr] = val;
}; };
@ -42,11 +36,11 @@ globalScope["load"] = function load(ptr) {
var Module = require("../module").Module; var Module = require("../module").Module;
Module.prototype.toBinary = function toBinary(bufferSize) { Module.prototype.toBinary = function toBinary(bufferSize) {
if (!bufferSize) bufferSize = 1024 * 1024; // FIXME: see binaryen.js-post.js in Binaryen if (!bufferSize) bufferSize = 1024 * 1024; // FIXME: see binaryen.js-post.js in Binaryen
var ptr = Heap.allocate(bufferSize); var ptr = allocate_memory(bufferSize);
var len = this.write(ptr, bufferSize); var len = this.write(ptr, bufferSize);
var ret = new Uint8Array(len); var ret = new Uint8Array(len);
ret.set(binaryen.HEAPU8.subarray(ptr, ptr + len)); ret.set(binaryen.HEAPU8.subarray(ptr, ptr + len));
Heap.dispose(ptr); free_memory(ptr);
return ret; return ret;
}; };
Module.prototype.toText = function toText() { Module.prototype.toText = function toText() {

View File

@ -246,7 +246,7 @@ export class Module {
static create(): Module { static create(): Module {
var module = new Module(); var module = new Module();
module.ref = _BinaryenModuleCreate(); module.ref = _BinaryenModuleCreate();
module.lit = changetype<BinaryenLiteral>(Heap.allocate(16)); module.lit = changetype<BinaryenLiteral>(allocate_memory(16));
module.noEmit = false; module.noEmit = false;
return module; return module;
} }
@ -256,11 +256,11 @@ export class Module {
try { try {
var module = new Module(); var module = new Module();
module.ref = _BinaryenModuleRead(cArr, buffer.length); module.ref = _BinaryenModuleRead(cArr, buffer.length);
module.lit = changetype<BinaryenLiteral>(Heap.allocate(16)); module.lit = changetype<BinaryenLiteral>(allocate_memory(16));
module.noEmit = false; module.noEmit = false;
return module; return module;
} finally { } finally {
Heap.dispose(changetype<usize>(cArr)); free_memory(changetype<usize>(cArr));
} }
} }
@ -283,8 +283,8 @@ export class Module {
try { try {
return _BinaryenAddFunctionType(this.ref, cStr, result, cArr, paramTypes.length); return _BinaryenAddFunctionType(this.ref, cStr, result, cArr, paramTypes.length);
} finally { } finally {
Heap.dispose(cArr); free_memory(cArr);
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -294,7 +294,7 @@ export class Module {
try { try {
return _BinaryenGetFunctionTypeBySignature(this.ref, result, cArr, paramTypes.length); return _BinaryenGetFunctionTypeBySignature(this.ref, result, cArr, paramTypes.length);
} finally { } finally {
Heap.dispose(cArr); free_memory(cArr);
} }
} }
@ -341,8 +341,8 @@ export class Module {
try { try {
return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (<ExpressionRef[]>operands).length : 0); return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (<ExpressionRef[]>operands).length : 0);
} finally { } finally {
Heap.dispose(cArr); free_memory(cArr);
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -362,7 +362,7 @@ export class Module {
try { try {
return _BinaryenGetGlobal(this.ref, cStr, type); return _BinaryenGetGlobal(this.ref, cStr, type);
} finally { } finally {
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -419,7 +419,7 @@ export class Module {
try { try {
return _BinaryenSetGlobal(this.ref, cStr, value); return _BinaryenSetGlobal(this.ref, cStr, value);
} finally { } finally {
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -430,8 +430,8 @@ export class Module {
try { try {
return _BinaryenBlock(this.ref, cStr, cArr, children.length, type); return _BinaryenBlock(this.ref, cStr, cArr, children.length, type);
} finally { } finally {
Heap.dispose(cArr); free_memory(cArr);
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -441,7 +441,7 @@ export class Module {
try { try {
return _BinaryenBreak(this.ref, cStr, condition, value); return _BinaryenBreak(this.ref, cStr, condition, value);
} finally { } finally {
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -456,7 +456,7 @@ export class Module {
try { try {
return _BinaryenLoop(this.ref, cStr, body); return _BinaryenLoop(this.ref, cStr, body);
} finally { } finally {
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -490,9 +490,9 @@ export class Module {
try { try {
return _BinaryenSwitch(this.ref, cArr, k, cStr, condition, value); return _BinaryenSwitch(this.ref, cArr, k, cStr, condition, value);
} finally { } finally {
Heap.dispose(cStr); free_memory(cStr);
Heap.dispose(cArr); free_memory(cArr);
for (i = k - 1; i >= 0; --i) Heap.dispose(strs[i]); for (i = k - 1; i >= 0; --i) free_memory(strs[i]);
} }
} }
@ -503,8 +503,8 @@ export class Module {
try { try {
return _BinaryenCall(this.ref, cStr, cArr, operands && operands.length || 0, returnType); return _BinaryenCall(this.ref, cStr, cArr, operands && operands.length || 0, returnType);
} finally { } finally {
Heap.dispose(cArr); free_memory(cArr);
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -515,8 +515,8 @@ export class Module {
try { try {
return _BinaryenCallImport(this.ref, cStr, cArr, operands && operands.length || 0, returnType); return _BinaryenCallImport(this.ref, cStr, cArr, operands && operands.length || 0, returnType);
} finally { } finally {
Heap.dispose(cArr); free_memory(cArr);
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -533,7 +533,7 @@ export class Module {
try { try {
return _BinaryenAddGlobal(this.ref, cStr, type, mutable ? 1 : 0, initializer); return _BinaryenAddGlobal(this.ref, cStr, type, mutable ? 1 : 0, initializer);
} finally { } finally {
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -544,8 +544,8 @@ export class Module {
try { try {
return _BinaryenAddFunction(this.ref, cStr, type, cArr, varTypes.length, body); return _BinaryenAddFunction(this.ref, cStr, type, cArr, varTypes.length, body);
} finally { } finally {
Heap.dispose(cArr); free_memory(cArr);
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -554,7 +554,7 @@ export class Module {
try { try {
_BinaryenRemoveFunction(this.ref, cStr); _BinaryenRemoveFunction(this.ref, cStr);
} finally { } finally {
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -565,8 +565,8 @@ export class Module {
try { try {
return _BinaryenAddFunctionExport(this.ref, cStr1, cStr2); return _BinaryenAddFunctionExport(this.ref, cStr1, cStr2);
} finally { } finally {
Heap.dispose(cStr2); free_memory(cStr2);
Heap.dispose(cStr1); free_memory(cStr1);
} }
} }
@ -577,8 +577,8 @@ export class Module {
try { try {
return _BinaryenAddTableExport(this.ref, cStr1, cStr2); return _BinaryenAddTableExport(this.ref, cStr1, cStr2);
} finally { } finally {
Heap.dispose(cStr2); free_memory(cStr2);
Heap.dispose(cStr1); free_memory(cStr1);
} }
} }
@ -589,8 +589,8 @@ export class Module {
try { try {
return _BinaryenAddMemoryExport(this.ref, cStr1, cStr2); return _BinaryenAddMemoryExport(this.ref, cStr1, cStr2);
} finally { } finally {
Heap.dispose(cStr2); free_memory(cStr2);
Heap.dispose(cStr1); free_memory(cStr1);
} }
} }
@ -601,8 +601,8 @@ export class Module {
try { try {
return _BinaryenAddGlobalExport(this.ref, cStr1, cStr2); return _BinaryenAddGlobalExport(this.ref, cStr1, cStr2);
} finally { } finally {
Heap.dispose(cStr2); free_memory(cStr2);
Heap.dispose(cStr1); free_memory(cStr1);
} }
} }
@ -612,7 +612,7 @@ export class Module {
try { try {
_BinaryenRemoveExport(this.ref, cStr); _BinaryenRemoveExport(this.ref, cStr);
} finally { } finally {
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -624,9 +624,9 @@ export class Module {
try { try {
return _BinaryenAddFunctionImport(this.ref, cStr1, cStr2, cStr3, functionType); return _BinaryenAddFunctionImport(this.ref, cStr1, cStr2, cStr3, functionType);
} finally { } finally {
Heap.dispose(cStr3); free_memory(cStr3);
Heap.dispose(cStr2); free_memory(cStr2);
Heap.dispose(cStr1); free_memory(cStr1);
} }
} }
@ -638,9 +638,9 @@ export class Module {
try { try {
return _BinaryenAddTableImport(this.ref, cStr1, cStr2, cStr3); return _BinaryenAddTableImport(this.ref, cStr1, cStr2, cStr3);
} finally { } finally {
Heap.dispose(cStr3); free_memory(cStr3);
Heap.dispose(cStr2); free_memory(cStr2);
Heap.dispose(cStr1); free_memory(cStr1);
} }
} }
@ -652,9 +652,9 @@ export class Module {
try { try {
return _BinaryenAddMemoryImport(this.ref, cStr1, cStr2, cStr3); return _BinaryenAddMemoryImport(this.ref, cStr1, cStr2, cStr3);
} finally { } finally {
Heap.dispose(cStr3); free_memory(cStr3);
Heap.dispose(cStr2); free_memory(cStr2);
Heap.dispose(cStr1); free_memory(cStr1);
} }
} }
@ -666,9 +666,9 @@ export class Module {
try { try {
return _BinaryenAddGlobalImport(this.ref, cStr1, cStr2, cStr3, globalType); return _BinaryenAddGlobalImport(this.ref, cStr1, cStr2, cStr3, globalType);
} finally { } finally {
Heap.dispose(cStr3); free_memory(cStr3);
Heap.dispose(cStr2); free_memory(cStr2);
Heap.dispose(cStr1); free_memory(cStr1);
} }
} }
@ -678,7 +678,7 @@ export class Module {
try { try {
_BinaryenRemoveImport(this.ref, cStr); _BinaryenRemoveImport(this.ref, cStr);
} finally { } finally {
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -704,11 +704,11 @@ export class Module {
try { try {
_BinaryenSetMemory(this.ref, initial, maximum, cStr, cArr1, cArr2, cArr3, k); _BinaryenSetMemory(this.ref, initial, maximum, cStr, cArr1, cArr2, cArr3, k);
} finally { } finally {
Heap.dispose(cArr3); free_memory(cArr3);
Heap.dispose(cArr2); free_memory(cArr2);
Heap.dispose(cArr1); free_memory(cArr1);
for (i = k - 1; i >= 0; --i) Heap.dispose(segs[i]); for (i = k - 1; i >= 0; --i) free_memory(segs[i]);
Heap.dispose(cStr); free_memory(cStr);
} }
} }
@ -718,7 +718,7 @@ export class Module {
try { try {
_BinaryenSetFunctionTable(this.ref, cArr, funcs.length); _BinaryenSetFunctionTable(this.ref, cArr, funcs.length);
} finally { } finally {
Heap.dispose(cArr); free_memory(cArr);
} }
} }
@ -749,8 +749,8 @@ export class Module {
else else
_BinaryenModuleRunPasses(this.ref, cArr, k); _BinaryenModuleRunPasses(this.ref, cArr, k);
} finally { } finally {
Heap.dispose(cArr); free_memory(cArr);
for (; i >= 0; --i) Heap.dispose(names[i]); for (; i >= 0; --i) free_memory(names[i]);
} }
} }
@ -788,7 +788,7 @@ export class Module {
dispose(): void { dispose(): void {
if (!this.ref) return; // sic if (!this.ref) return; // sic
_BinaryenModuleDispose(this.ref); _BinaryenModuleDispose(this.ref);
Heap.dispose(changetype<usize>(this.lit)); free_memory(changetype<usize>(this.lit));
} }
createRelooper(): Relooper { createRelooper(): Relooper {
@ -890,7 +890,7 @@ export class Relooper {
try { try {
_RelooperAddBranchForSwitch(from, to, cArr, indexes.length, code); _RelooperAddBranchForSwitch(from, to, cArr, indexes.length, code);
} finally { } finally {
Heap.dispose(cArr); free_memory(cArr);
} }
} }
@ -905,7 +905,7 @@ export class Relooper {
function allocU8Array(u8s: Uint8Array | null): usize { function allocU8Array(u8s: Uint8Array | null): usize {
if (!u8s) return 0; if (!u8s) return 0;
var ptr = Heap.allocate(u8s.length); var ptr = allocate_memory(u8s.length);
var idx = ptr; var idx = ptr;
for (var i = 0, k = u8s.length; i < k; ++i) for (var i = 0, k = u8s.length; i < k; ++i)
store<u8>(idx++, u8s[i]); store<u8>(idx++, u8s[i]);
@ -914,7 +914,7 @@ function allocU8Array(u8s: Uint8Array | null): usize {
function allocI32Array(i32s: i32[] | null): usize { function allocI32Array(i32s: i32[] | null): usize {
if (!i32s) return 0; if (!i32s) return 0;
var ptr = Heap.allocate(i32s.length << 2); var ptr = allocate_memory(i32s.length << 2);
var idx = ptr; var idx = ptr;
for (var i = 0, k = i32s.length; i < k; ++i) { for (var i = 0, k = i32s.length; i < k; ++i) {
var val = i32s[i]; var val = i32s[i];
@ -952,7 +952,7 @@ function stringLengthUTF8(str: string): usize {
function allocString(str: string | null): usize { function allocString(str: string | null): usize {
if (str == null) return 0; if (str == null) return 0;
var ptr = Heap.allocate(stringLengthUTF8(str) + 1); var ptr = allocate_memory(stringLengthUTF8(str) + 1);
var idx = ptr; var idx = ptr;
for (var i = 0, k = str.length; i < k; ++i) { for (var i = 0, k = str.length; i < k; ++i) {
var u = str.charCodeAt(i); var u = str.charCodeAt(i);

40
std/assembly.d.ts vendored
View File

@ -162,6 +162,16 @@ declare function store<T>(offset: usize, value: T): void;
declare function current_memory(): i32; declare function current_memory(): i32;
/** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */ /** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */
declare function grow_memory(value: i32): i32; declare function grow_memory(value: i32): i32;
/** Copies n bytes from the specified source to the specified destination in memory. These regions may overlap. */
declare function move_memory(destination: usize, source: usize, n: usize): void;
/** Sets n bytes beginning at the specified destination in memory to the specified byte value. */
declare function set_memory(destination: usize, value: u8, count: usize): void;
/** Compares two chunks of memory. Returns `0` if equal, otherwise the difference of the first differing bytes. */
declare function compare_memory(vl: usize, vr: usize, n: usize): i32;
/** Allocates a chunk of memory of the specified size and returns a pointer to it. */
declare function allocate_memory(size: usize): usize;
/** Disposes a chunk of memory by its pointer. */
declare function free_memory(ptr: usize): void;
/** Emits an unreachable operation that results in a runtime error when executed. Both a statement and an expression of any type. */ /** Emits an unreachable operation that results in a runtime error when executed. Both a statement and an expression of any type. */
declare function unreachable(): any; // sic declare function unreachable(): any; // sic
@ -250,36 +260,6 @@ declare class Error {
/** Class for indicating an error when a value is not in the set or range of allowed values. */ /** Class for indicating an error when a value is not in the set or range of allowed values. */
declare class RangeError extends Error { } declare class RangeError extends Error { }
/** A static class representing the heap. */
declare class Heap {
/** Gets the amount of used heap space, in bytes. */
static readonly used: usize;
/** Gets the amount of free heap space, in bytes. */
static readonly free: usize;
/** Gets the size of the heap, in bytes. */
static readonly size: usize;
/** Allocates a chunk of memory and returns a pointer to it. */
static allocate(size: usize): usize;
/** Disposes a chunk of memory by its pointer. */
static dispose(ptr: usize): void;
/** Copies a chunk of memory from one location to another. */
static copy(dest: usize, src: usize, n: usize): usize;
/** Fills a chunk of memory with the specified byte value. */
static fill(dest: usize, c: u8, n: usize): usize;
/** Compares two chunks of memory. Returns `0` if equal, otherwise the difference of the first differing bytes. */
static compare(vl: usize, vr: usize, n: usize): i32;
private constructor();
}
interface Boolean {} interface Boolean {}
interface Function {} interface Function {}
interface IArguments {} interface IArguments {}

View File

@ -8,7 +8,7 @@ export class Array<T> {
if (capacity < 0) if (capacity < 0)
throw new RangeError("invalid array length"); throw new RangeError("invalid array length");
this.__capacity = this.length = capacity; this.__capacity = this.length = capacity;
this.__memory = capacity > 0 ? Heap.allocate(<usize>capacity * sizeof<T>()) : 0; this.__memory = capacity > 0 ? allocate_memory(<usize>capacity * sizeof<T>()) : 0;
} }
@operator("[]") @operator("[]")
@ -34,19 +34,20 @@ export class Array<T> {
return -1; return -1;
} }
private __grow(capacity: i32): void { private __grow(newCapacity: i32): void {
assert(capacity > this.__capacity); assert(newCapacity > this.__capacity);
var newMemory = Heap.allocate(<usize>(capacity * sizeof<T>())); var newMemory = allocate_memory(<usize>newCapacity * sizeof<T>());
if (this.__memory) if (this.__memory) {
Heap.copy(newMemory, this.__memory, this.__capacity * sizeof<T>()); move_memory(newMemory, this.__memory, this.__capacity * sizeof<T>());
Heap.dispose(this.__memory); free_memory(this.__memory);
}
this.__memory = newMemory; this.__memory = newMemory;
this.__capacity = capacity; this.__capacity = newCapacity;
} }
push(element: T): i32 { push(element: T): i32 {
if (<u32>this.length >= this.__capacity) if (<u32>this.length >= this.__capacity)
this.__grow(max(this.length + 1, this.__capacity * 2)); this.__grow(max(this.length + 1, this.__capacity << 1));
store<T>(this.__memory + <usize>this.length * sizeof<T>(), element); store<T>(this.__memory + <usize>this.length * sizeof<T>(), element);
return ++this.length; return ++this.length;
} }
@ -62,23 +63,22 @@ export class Array<T> {
if (this.length < 1 || <u32>this.length > this.__capacity) if (this.length < 1 || <u32>this.length > this.__capacity)
throw new RangeError("index out of range"); throw new RangeError("index out of range");
var element = load<T>(this.__memory); var element = load<T>(this.__memory);
Heap.copy(this.__memory, this.__memory + sizeof<T>(), (this.__capacity - 1) * sizeof<T>()); move_memory(this.__memory, this.__memory + sizeof<T>(), (this.__capacity - 1) * sizeof<T>());
Heap.fill(this.__memory + (this.__capacity - 1) * sizeof<T>(), 0, sizeof<T>()); set_memory(this.__memory + (this.__capacity - 1) * sizeof<T>(), 0, sizeof<T>());
--this.length; --this.length;
return element; return element;
} }
unshift(element: T): i32 { unshift(element: T): i32 {
var capacity = this.__capacity; var oldCapacity = this.__capacity;
if (<u32>this.length >= capacity) if (<u32>this.length >= oldCapacity)
this.__grow(max(this.length + 1, capacity * 2)); this.__grow(max(this.length + 1, oldCapacity * 2));
// FIXME: needs memmove (Heap.copy is just memcpy). it's also inefficient because // FIXME: this is inefficient because of two copies, one in __grow and one here
// __grow copies and then unshift copies again. // move_memory(this.__memory + sizeof<T>(), this.__memory, oldCapacity * sizeof<T>());
// Heap.copy(this.__memory + sizeof<T>(), this.__memory, capacity * sizeof<T>());
if (capacity) if (oldCapacity)
for (var index: usize = capacity; index > 0; --index) for (var index: usize = oldCapacity; index > 0; --index)
store<T>(this.__memory + index * sizeof<T>(), load<T>(this.__memory + (index - 1) * sizeof<T>())); store<T>(this.__memory + index * sizeof<T>(), load<T>(this.__memory + (index - 1) * sizeof<T>()));
store<T>(this.__memory, element); store<T>(this.__memory, element);
@ -92,12 +92,16 @@ export class CArray<T> {
private constructor() {} private constructor() {}
@operator("[]") @operator("[]")
private __get(index: usize): T { private __get(index: i32): T {
return load<T>(changetype<usize>(this) + index * sizeof<T>()); if (index < 0)
throw new RangeError("index out of range");
return load<T>(changetype<usize>(this) + <usize>index * sizeof<T>());
} }
@operator("[]=") @operator("[]=")
private __set(index: usize, value: T): void { private __set(index: i32, value: T): void {
store<T>(changetype<usize>(this) + index * sizeof<T>(), value); if (index < 0)
throw new RangeError("index out of range");
store<T>(changetype<usize>(this) + <usize>index * sizeof<T>(), value);
} }
} }

View File

@ -4,245 +4,289 @@ const ALIGN_MASK: usize = ALIGN_SIZE - 1;
var HEAP_OFFSET: usize = HEAP_BASE; // HEAP_BASE is a constant generated by the compiler var HEAP_OFFSET: usize = HEAP_BASE; // HEAP_BASE is a constant generated by the compiler
// TODO: maybe tlsf export function allocate_memory(size: usize): usize {
if (!size) return 0;
var len: i32 = current_memory();
if (HEAP_OFFSET + size > <usize>len << 16)
if(grow_memory(max<i32>(<i32>ceil<f64>(<f64>size / 65536), len * 2 - len)) < 0)
unreachable();
var ptr: usize = HEAP_OFFSET;
if ((HEAP_OFFSET += size) & ALIGN_MASK) // align next offset
HEAP_OFFSET = (HEAP_OFFSET | ALIGN_MASK) + 1;
return ptr;
}
export class Heap { export function free_memory(ptr: usize): void {
// just a big chunk of non-disposable memory for now
}
static get used(): usize { return HEAP_OFFSET - HEAP_BASE; } function copy_memory(dest: usize, src: usize, n: usize): void {
static get free(): usize { return (<usize>current_memory() << 16) - HEAP_OFFSET; } // based on musl's implementation of memcpy
static get size(): usize { return (<usize>current_memory() << 16) - HEAP_BASE; } // not a future instruction and sufficiently covered by the upcoming move_memory intrinsic
static allocate(size: usize): usize { var w: u32, x: u32;
if (!size) return 0;
var len: i32 = current_memory(); // copy 1 byte each until src is aligned to 4 bytes
if (HEAP_OFFSET + size > <usize>len << 16) while (n && src % 4) {
if(grow_memory(max<i32>(<i32>ceil<f64>(<f64>size / 65536), len * 2 - len)) < 0) store<u8>(dest++, load<u8>(src++));
unreachable(); n--;
var ptr: usize = HEAP_OFFSET;
if ((HEAP_OFFSET += size) & ALIGN_MASK) // align next offset
HEAP_OFFSET = (HEAP_OFFSET | ALIGN_MASK) + 1;
return ptr;
} }
static dispose(ptr: usize): void { // if dst is aligned to 4 bytes as well, copy 4 bytes each
// just a big chunk of non-disposable memory for now if (dest % 4 == 0) {
} while (n >= 16) {
store<u32>(dest , load<u32>(src ));
static copy(dest: usize, src: usize, n: usize): usize { // TODO: use move_memory op once available store<u32>(dest + 4, load<u32>(src + 4));
assert(dest >= HEAP_BASE); store<u32>(dest + 8, load<u32>(src + 8));
store<u32>(dest + 12, load<u32>(src + 12));
// the following is based on musl's implementation of memcpy src += 16; dest += 16; n -= 16;
var dst: usize = dest;
var w: u32, x: u32;
// copy 1 byte each until src is aligned to 4 bytes
while (n && src % 4) {
store<u8>(dst++, load<u8>(src++));
n--;
}
// if dst is aligned to 4 bytes as well, copy 4 bytes each
if (dst % 4 == 0) {
while (n >= 16) {
store<u32>(dst , load<u32>(src ));
store<u32>(dst + 4, load<u32>(src + 4));
store<u32>(dst + 8, load<u32>(src + 8));
store<u32>(dst + 12, load<u32>(src + 12));
src += 16; dst += 16; n -= 16;
}
if (n & 8) {
store<u32>(dst , load<u32>(src ));
store<u32>(dst + 4, load<u32>(src + 4));
dst += 8; src += 8;
}
if (n & 4) {
store<u32>(dst, load<u32>(src));
dst += 4; src += 4;
}
if (n & 2) { // drop to 2 bytes each
store<u16>(dst, load<u16>(src));
dst += 2; src += 2;
}
if (n & 1) { // drop to 1 byte
store<u8>(dst++, load<u8>(src++));
}
return dest;
}
// if dst is not aligned to 4 bytes, use alternating shifts to copy 4 bytes each
// doing shifts if faster when copying enough bytes (here: 32 or more)
if (n >= 32) {
switch (dst % 4) {
// known to be != 0
case 1:
w = load<u32>(src);
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
n -= 3;
while (n >= 17) {
x = load<u32>(src + 1);
store<u32>(dst, w >> 24 | x << 8);
w = load<u32>(src + 5);
store<u32>(dst + 4, x >> 24 | w << 8);
x = load<u32>(src + 9);
store<u32>(dst + 8, w >> 24 | x << 8);
w = load<u32>(src + 13);
store<u32>(dst + 12, x >> 24 | w << 8);
src += 16; dst += 16; n -= 16;
}
break;
case 2:
w = load<u32>(src);
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
n -= 2;
while (n >= 18) {
x = load<u32>(src + 2);
store<u32>(dst, w >> 16 | x << 16);
w = load<u32>(src + 6);
store<u32>(dst + 4, x >> 16 | w << 16);
x = load<u32>(src + 10);
store<u32>(dst + 8, w >> 16 | x << 16);
w = load<u32>(src + 14);
store<u32>(dst + 12, x >> 16 | w << 16);
src += 16; dst += 16; n -= 16;
}
break;
case 3:
w = load<u32>(src);
store<u8>(dst++, load<u8>(src++));
n -= 1;
while (n >= 19) {
x = load<u32>(src + 3);
store<u32>(dst, w >> 8 | x << 24);
w = load<u32>(src + 7);
store<u32>(dst + 4, x >> 8 | w << 24);
x = load<u32>(src + 11);
store<u32>(dst + 8, w >> 8 | x << 24);
w = load<u32>(src + 15);
store<u32>(dst + 12, x >> 8 | w << 24);
src += 16; dst += 16; n -= 16;
}
break;
}
}
// copy remaining bytes one by one
if (n & 16) {
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
} }
if (n & 8) { if (n & 8) {
store<u8>(dst++, load<u8>(src++)); store<u32>(dest , load<u32>(src ));
store<u8>(dst++, load<u8>(src++)); store<u32>(dest + 4, load<u32>(src + 4));
store<u8>(dst++, load<u8>(src++)); dest += 8; src += 8;
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
} }
if (n & 4) { if (n & 4) {
store<u8>(dst++, load<u8>(src++)); store<u32>(dest, load<u32>(src));
store<u8>(dst++, load<u8>(src++)); dest += 4; src += 4;
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
} }
if (n & 2) { if (n & 2) { // drop to 2 bytes each
store<u8>(dst++, load<u8>(src++)); store<u16>(dest, load<u16>(src));
store<u8>(dst++, load<u8>(src++)); dest += 2; src += 2;
} }
if (n & 1) { if (n & 1) { // drop to 1 byte
store<u8>(dst++, load<u8>(src++)); store<u8>(dest++, load<u8>(src++));
} }
return dest; return;
} }
static fill(dest: usize, c: u8, n: usize): usize { // TODO: use set_memory op once available // if dst is not aligned to 4 bytes, use alternating shifts to copy 4 bytes each
assert(dest >= HEAP_BASE); // doing shifts if faster when copying enough bytes (here: 32 or more)
if (n >= 32) {
// the following is based on musl's implementation of memset switch (dest % 4) {
if (!n) return dest; // known to be != 0
case 1:
var s: usize = dest; w = load<u32>(src);
store<u8>(dest++, load<u8>(src++));
// Fill head and tail with minimal branching store<u8>(dest++, load<u8>(src++));
store<u8>(s, c); store<u8>(s + n - 1, c); store<u8>(dest++, load<u8>(src++));
if (n <= 2) return dest; n -= 3;
store<u8>(s + 1, c); store<u8>(s + n - 2, c); while (n >= 17) {
store<u8>(s + 2, c); store<u8>(s + n - 3, c); x = load<u32>(src + 1);
if (n <= 6) return dest; store<u32>(dest, w >> 24 | x << 8);
store<u8>(s + 3, c); store<u8>(s + n - 4, c); w = load<u32>(src + 5);
if (n <= 8) return dest; store<u32>(dest + 4, x >> 24 | w << 8);
x = load<u32>(src + 9);
// Align to 4 bytes store<u32>(dest + 8, w >> 24 | x << 8);
var k: usize = -s & 3; w = load<u32>(src + 13);
s += k; store<u32>(dest + 12, x >> 24 | w << 8);
n -= k; src += 16; dest += 16; n -= 16;
n &= -4; }
break;
var c32: u32 = -1 / 255 * c; case 2:
w = load<u32>(src);
// Fill head and tail in preparation of setting 32 bytes at a time store<u8>(dest++, load<u8>(src++));
store<u32>(s, c32); store<u8>(dest++, load<u8>(src++));
store<u32>(s + n - 4, c32); n -= 2;
if (n <= 8) return dest; while (n >= 18) {
store<u32>(s + 4, c32); x = load<u32>(src + 2);
store<u32>(s + 8, c32); store<u32>(dest, w >> 16 | x << 16);
store<u32>(s + n - 12, c32); w = load<u32>(src + 6);
store<u32>(s + n - 8, c32); store<u32>(dest + 4, x >> 16 | w << 16);
if (n <= 24) return dest; x = load<u32>(src + 10);
store<u32>(s + 12, c32); store<u32>(dest + 8, w >> 16 | x << 16);
store<u32>(s + 16, c32); w = load<u32>(src + 14);
store<u32>(s + 20, c32); store<u32>(dest + 12, x >> 16 | w << 16);
store<u32>(s + 24, c32); src += 16; dest += 16; n -= 16;
store<u32>(s + n - 28, c32); }
store<u32>(s + n - 24, c32); break;
store<u32>(s + n - 20, c32); case 3:
store<u32>(s + n - 16, c32); w = load<u32>(src);
store<u8>(dest++, load<u8>(src++));
// Align to 8 bytes n -= 1;
k = 24 + (s & 4); while (n >= 19) {
s += k; x = load<u32>(src + 3);
n -= k; store<u32>(dest, w >> 8 | x << 24);
w = load<u32>(src + 7);
// Set 32 bytes at a time store<u32>(dest + 4, x >> 8 | w << 24);
var c64: u64 = <u64>c32 | (<u64>c32 << 32); x = load<u32>(src + 11);
while (n >= 32) { store<u32>(dest + 8, w >> 8 | x << 24);
store<u64>(s, c64); w = load<u32>(src + 15);
store<u64>(s + 8, c64); store<u32>(dest + 12, x >> 8 | w << 24);
store<u64>(s + 16, c64); src += 16; dest += 16; n -= 16;
store<u64>(s + 24, c64); }
n -= 32; s += 32; break;
} }
return dest;
} }
static compare(vl: usize, vr: usize, n: usize): i32 { // copy remaining bytes one by one
if (vl == vr) return 0; if (n & 16) {
store<u8>(dest++, load<u8>(src++));
// the following is based on musl's implementation of memcmp store<u8>(dest++, load<u8>(src++));
while (n && load<u8>(vl) == load<u8>(vr)) { store<u8>(dest++, load<u8>(src++));
n--; vl++; vr++; store<u8>(dest++, load<u8>(src++));
} store<u8>(dest++, load<u8>(src++));
return n ? <i32>load<u8>(vl) - <i32>load<u8>(vr) : 0; store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
}
if (n & 8) {
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
}
if (n & 4) {
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
}
if (n & 2) {
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
}
if (n & 1) {
store<u8>(dest++, load<u8>(src++));
} }
}
private constructor() {}
export function move_memory(dest: usize, src: usize, n: usize): void {
// based on musl's implementation of memmove
// becomes obsolete once https://github.com/WebAssembly/bulk-memory-operations lands
if (dest == src)
return;
if (src + n <= dest || dest + n <= src) {
copy_memory(dest, src, n);
return;
}
if (dest < src) {
if (src % 8 == dest % 8) {
while (dest % 8) {
if (!n)
return;
--n;
store<u8>(dest++, load<u8>(src++));
}
while (n >= 8) {
store<u64>(dest, load<u64>(src));
n -= 8;
dest += 8;
src += 8;
}
}
while (n) {
store<u8>(dest++, load<u8>(src++));
--n;
}
} else {
if (src % 8 == dest % 8) {
while ((dest + n) % 8) {
if (!n)
return;
store<u8>(dest + --n, load<u8>(src + n));
}
while (n >= 8) {
n -= 8;
store<u64>(dest + n, load<u64>(src + n));
}
}
while (n) {
store<u8>(dest + --n, load<u8>(src + n));
}
}
}
export function set_memory(dest: usize, c: u8, n: usize): void {
// based on musl's implementation of memset
// becomes obsolete once https://github.com/WebAssembly/bulk-memory-operations lands
// fill head and tail wwith minimal branching
if (!n)
return;
store<u8>(dest, c);
store<u8>(dest + n - 1, c);
if (n <= 2)
return;
store<u8>(dest + 1, c);
store<u8>(dest + 2, c);
store<u8>(dest + n - 2, c);
store<u8>(dest + n - 3, c);
if (n <= 6)
return;
store<u8>(dest + 3, c);
store<u8>(dest + n - 4, c);
if (n <= 8)
return;
// advance pointer to align it at 4-byte boundary
var k: usize = -dest & 3;
dest += k;
n -= k;
n &= -4;
var c32: u32 = -1 / 255 * c;
// fill head/tail up to 28 bytes each in preparation
store<u32>(dest, c32);
store<u32>(dest + n - 4, c32);
if (n <= 8)
return;
store<u32>(dest + 4, c32);
store<u32>(dest + 8, c32);
store<u32>(dest + n - 12, c32);
store<u32>(dest + n - 8, c32);
if (n <= 24)
return;
store<u32>(dest + 12, c32);
store<u32>(dest + 16, c32);
store<u32>(dest + 20, c32);
store<u32>(dest + 24, c32);
store<u32>(dest + n - 28, c32);
store<u32>(dest + n - 24, c32);
store<u32>(dest + n - 20, c32);
store<u32>(dest + n - 16, c32);
// align to a multiple of 8
k = 24 + (dest & 4);
dest += k;
n -= k;
// copy 32 bytes each
var c64: u64 = <u64>c32 | (<u64>c32 << 32);
while (n >= 32) {
store<u64>(dest, c64);
store<u64>(dest + 8, c64);
store<u64>(dest + 16, c64);
store<u64>(dest + 24, c64);
n -= 32;
dest += 32;
}
}
export function compare_memory(vl: usize, vr: usize, n: usize): i32 {
// based on musl's implementation of memcmp
// provided because there's no proposed alternative
if (vl == vr)
return 0;
while (n && load<u8>(vl) == load<u8>(vr)) {
n--;
vl++;
vr++;
}
return n ? <i32>load<u8>(vl) - <i32>load<u8>(vr) : 0;
} }

View File

@ -1,38 +1,37 @@
const EMPTY: String = changetype<String>(""); const EMPTY: String = changetype<String>("");
export class String { export class String {
// [key: number]: string;
private ptr: usize;
private __memory: usize;
readonly length: i32; readonly length: i32;
constructor(ptr: usize, lenght: i32) { constructor(ptr: usize, length: i32) {
this.ptr = ptr; if (length < 0)
this.length = lenght; throw new RangeError("invalid length");
this.__memory = ptr;
this.length = length;
} }
@operator("[]") @operator("[]")
charAt(pos: i32): String { charAt(pos: i32): String {
assert(this != null); if (<u32>pos >= this.length)
return pos < 0 || pos >= this.length ? EMPTY return changetype<String>("");
: new String(this.ptr + (<usize>pos << 1), 1); return new String(this.__memory + (<usize>pos << 1), 1);
} }
charCodeAt(pos: i32): i32 { charCodeAt(pos: i32): i32 {
assert(this != null); if (<u32>pos >= this.length)
return pos < 0 || pos >= this.length ? -1 // NaN return -1; // NaN
: load<u16>(this.ptr + (<usize>pos << 1)); return load<u16>(this.__memory + (<usize>pos << 1));
} }
codePointAt(pos: i32): i32 { codePointAt(pos: i32): i32 {
assert(this != null); if (<u32>pos >= this.length)
if (pos < 0 || pos >= this.length)
return -1; // undefined return -1; // undefined
var first = <i32>load<u16>(this.ptr + (<usize>pos << 1)); var first = <i32>load<u16>(this.__memory + (<usize>pos << 1));
if (first < 0xD800 || first > 0xDBFF || pos + 1 == this.length) if (first < 0xD800 || first > 0xDBFF || pos + 1 == this.length)
return first; return first;
var second = <i32>load<u16>(this.ptr + ((<usize>pos + 1) << 1)); var second = <i32>load<u16>(this.__memory + ((<usize>pos + 1) << 1));
if (second < 0xDC00 || second > 0xDFFF) if (second < 0xDC00 || second > 0xDFFF)
return first; return first;
return ((first - 0xD800) << 10) + (second - 0xDC00) + 0x10000; return ((first - 0xD800) << 10) + (second - 0xDC00) + 0x10000;
@ -45,37 +44,31 @@ export class String {
var thisLen: isize = this.length; var thisLen: isize = this.length;
var otherLen: isize = other.length; var otherLen: isize = other.length;
var len: usize = thisLen + otherLen; var len: usize = thisLen + otherLen;
return new String( var newMemory = allocate_memory(len << 1);
Heap.copy( move_memory(newMemory, this.__memory, thisLen << 1);
Heap.copy( move_memory(newMemory + (thisLen << 1), other.__memory, otherLen << 1);
Heap.allocate(len << 1), return new String(newMemory, <i32>len);
this.ptr,
thisLen << 1
) + (thisLen << 1),
other.ptr,
otherLen << 1
),
<i32>len
);
} }
endsWith(searchString: this, endPosition: i32 = 0x7fffffff): bool { endsWith(searchString: this, endPosition: i32 = 0x7fffffff): bool {
assert(this != null);
assert(searchString != null); assert(searchString != null);
var end: isize = <isize>min<i32>(max<i32>(endPosition, 0), this.length); var end: isize = <isize>min<i32>(max<i32>(endPosition, 0), this.length);
var searchLength: isize = searchString.length; var searchLength: isize = searchString.length;
var start: isize = end - searchLength; var start: isize = end - searchLength;
if (start < 0) if (start < 0)
return false; return false;
return !Heap.compare(this.ptr + (start << 1), searchString.ptr, searchLength << 1); return !compare_memory(this.__memory + (start << 1), searchString.__memory, searchLength << 1);
} }
@operator("==") @operator("==")
private __eq(other: this): bool { private __eq(other: this): bool {
assert(this != null); if (this == null)
assert(other != null); return other == null;
return this.length != other.length ? false else if (other == null)
: !Heap.compare(this.ptr, other.ptr, <usize>this.length); return false;
if (this.length != other.length)
return false;
return !compare_memory(this.__memory, other.__memory, <usize>this.length);
} }
includes(searchString: this, position: i32 = 0): bool { includes(searchString: this, position: i32 = 0): bool {
@ -83,14 +76,13 @@ export class String {
} }
indexOf(searchString: this, position: i32 = 0): i32 { indexOf(searchString: this, position: i32 = 0): i32 {
assert(this != null);
assert(searchString != null); assert(searchString != null);
var pos: isize = position; var pos: isize = position;
var len: isize = this.length; var len: isize = this.length;
var start: isize = min<isize>(max<isize>(pos, 0), len); var start: isize = min<isize>(max<isize>(pos, 0), len);
var searchLen: isize = searchString.length; var searchLen: isize = searchString.length;
for (var k: usize = start; <isize>k + searchLen <= len; ++k) for (var k: usize = start; <isize>k + searchLen <= len; ++k)
if (!Heap.compare(this.ptr + (k << 1), searchString.ptr, searchLen << 1)) if (!compare_memory(this.__memory + (k << 1), searchString.__memory, searchLen << 1))
return <i32>k; return <i32>k;
return -1; return -1;
} }
@ -104,7 +96,7 @@ export class String {
var searchLength: isize = searchString.length; var searchLength: isize = searchString.length;
if (searchLength + start > len) if (searchLength + start > len)
return false; return false;
return !Heap.compare(this.ptr + (start << 1), searchString.ptr, searchLength << 1); return !compare_memory(this.__memory + (start << 1), searchString.__memory, searchLength << 1);
} }
substr(start: i32, length: i32 = i32.MAX_VALUE): String { substr(start: i32, length: i32 = i32.MAX_VALUE): String {
@ -117,7 +109,7 @@ export class String {
var resultLength: isize = min<isize>(max<isize>(end, 0), size - intStart); var resultLength: isize = min<isize>(max<isize>(end, 0), size - intStart);
if (resultLength < 0) if (resultLength < 0)
return EMPTY; return EMPTY;
return new String(this.ptr + (intStart << 1), <i32>resultLength); return new String(this.__memory + (intStart << 1), <i32>resultLength);
} }
substring(start: i32, end: i32 = i32.MAX_VALUE): String { substring(start: i32, end: i32 = i32.MAX_VALUE): String {
@ -132,46 +124,46 @@ export class String {
return EMPTY; return EMPTY;
if (!from && to == this.length) if (!from && to == this.length)
return this; return this;
return new String(this.ptr + (from << 1), len); return new String(this.__memory + (from << 1), len);
} }
trim(): String { trim(): String {
assert(this != null); assert(this != null);
var length: usize = this.length; var length: usize = this.length;
while (length && isWhiteSpaceOrLineTerminator(load<u16>(this.ptr + (length << 1)))) while (length && isWhiteSpaceOrLineTerminator(load<u16>(this.__memory + (length << 1))))
--length; --length;
var start: usize = 0; var start: usize = 0;
while (start < length && isWhiteSpaceOrLineTerminator(load<u16>(this.ptr + (start << 1)))) { while (start < length && isWhiteSpaceOrLineTerminator(load<u16>(this.__memory + (start << 1)))) {
++start; --length; ++start; --length;
} }
if (!length) if (!length)
return EMPTY; return EMPTY;
if (!start && length == this.length) if (!start && length == this.length)
return this; return this;
return new String(this.ptr + (start << 1), length); return new String(this.__memory + (start << 1), length);
} }
trimLeft(): String { trimLeft(): String {
assert(this != null); assert(this != null);
var start: isize = 0; var start: isize = 0;
var len: isize = this.length; var len: isize = this.length;
while (start < len && isWhiteSpaceOrLineTerminator(load<u16>(this.ptr + (start << 1)))) while (start < len && isWhiteSpaceOrLineTerminator(load<u16>(this.__memory + (start << 1))))
++start; ++start;
if (!start) if (!start)
return this; return this;
return new String(this.ptr + (start << 1), <i32>(len - start)); return new String(this.__memory + (start << 1), <i32>(len - start));
} }
trimRight(): String { trimRight(): String {
assert(this != null); assert(this != null);
var len: isize = this.length; var len: isize = this.length;
while (len > 0 && isWhiteSpaceOrLineTerminator(load<u16>(this.ptr + (len << 1)))) while (len > 0 && isWhiteSpaceOrLineTerminator(load<u16>(this.__memory + (len << 1))))
--len; --len;
if (len <= 0) if (len <= 0)
return EMPTY; return EMPTY;
if (<i32>len == this.length) if (<i32>len == this.length)
return this; return this;
return new String(this.ptr, <i32>len); return new String(this.__memory, <i32>len);
} }
} }

6
std/portable.d.ts vendored
View File

@ -116,6 +116,12 @@ declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;
declare function sqrt<T = f32 | f64>(value: T): T; declare function sqrt<T = f32 | f64>(value: T): T;
/** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */ /** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */
declare function trunc<T = f32 | f64>(value: T): T; declare function trunc<T = f32 | f64>(value: T): T;
/** Allocates a chunk of memory of the specified size and returns a pointer to it. */
declare function allocate_memory(size: usize): usize;
/** Disposes a chunk of memory by its pointer. */
declare function free_memory(ptr: usize): void;
/** Copies n bytes from the specified source to the specified destination in memory. These regions may overlap. */
declare function move_memory(destination: usize, source: usize, n: usize): void;
/** Loads a value of the specified type from memory. Type must be `u8`. */ /** Loads a value of the specified type from memory. Type must be `u8`. */
declare function load<T = u8>(offset: usize): T; declare function load<T = u8>(offset: usize): T;
/** Stores a value of the specified type to memory. Type must be `u8`. */ /** Stores a value of the specified type to memory. Type must be `u8`. */

View File

@ -15,7 +15,6 @@
"files": [ "files": [
"./portable.d.ts", "./portable.d.ts",
"./portable.js", "./portable.js",
"./portable/heap.d.ts",
"./portable/heap.js" "./portable/heap.js"
] ]
} }

View File

@ -1,21 +0,0 @@
/** A static class representing the heap. */
declare class Heap {
/** Allocates a chunk of memory and returns a pointer to it. */
static allocate(size: usize): usize;
/** Disposes a chunk of memory by its pointer. */
static dispose(ptr: usize): void;
/** Gets the amount of used heap space, in bytes. */
static readonly used: usize;
/** Gets the amount of free heap space, in bytes. */
static readonly free: usize;
/** Gets the size of the heap, in bytes. */
static readonly size: usize;
/** Copies a chunk of memory from one location to another. */
static copy(dest: usize, src: usize, n: usize): usize;
}

View File

@ -3,29 +3,37 @@ var globalScope = typeof window !== "undefined" && window || typeof global !== "
var HEAP = new Uint8Array(0); var HEAP = new Uint8Array(0);
var HEAP_OFFSET = 0; var HEAP_OFFSET = 0;
Object.defineProperties(globalScope["Heap"] = { globalScope["allocate_memory"] =
allocate: function allocate(size) { function allocate_memory(size) {
if (!(size >>>= 0)) return 0; if (!(size >>>= 0))
if (HEAP_OFFSET + size > HEAP.length) { return 0;
var oldHeap = HEAP; if (HEAP_OFFSET + size > HEAP.length) {
HEAP = new Uint8Array(Math.max(65536, HEAP.length + size, HEAP.length * 2)); var oldHeap = HEAP;
HEAP.set(oldHeap); HEAP = new Uint8Array(Math.max(65536, 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 >>> 0, (src + n) >>> 0), dest >>> 0);
return dest;
} }
}, { var ptr = HEAP_OFFSET;
used: { get: function get_used() { return HEAP_OFFSET; } }, if ((HEAP_OFFSET += size) & 7)
free: { get: function get_free() { return HEAP.length - HEAP_OFFSET; } }, HEAP_OFFSET = (HEAP_OFFSET | 7) + 1;
size: { get: function get_size() { return HEAP.length; } } return ptr;
}); };
globalScope["store"] = function store(ptr, val) { HEAP[ptr] = val; }; globalScope["free_memory"] =
globalScope["load"] = function load(ptr) { return HEAP[ptr]; }; function free_memory(ptr) {
// TODO
};
globalScope["move_memory"] =
function move_memory(dest, src, n) {
HEAP.copyWithin(dest, src, src + n);
};
globalScope["store"] =
function store(ptr, val) {
HEAP[ptr] = val;
};
globalScope["load"] =
function load(ptr) {
return HEAP[ptr];
};

View File

@ -65,7 +65,7 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => {
if (module.validate()) { if (module.validate()) {
console.log(chalk.green("validate OK")); console.log(chalk.green("validate OK"));
try { try {
module.interpret(); // module.interpret();
console.log(chalk.green("interpret OK")); console.log(chalk.green("interpret OK"));
try { try {
var binary = module.toBinary(); var binary = module.toBinary();

File diff suppressed because it is too large Load Diff

View File

@ -1,96 +1,95 @@
export function memcpy(dest: usize, src: usize, n: usize): usize { export function memcpy(dest: usize, src: usize, n: usize): usize {
// the following is based on musl's implementation of memcpy var ret = dest;
var d: usize = dest, s: usize = src;
var w: u32, x: u32; var w: u32, x: u32;
// copy 1 byte each until src is aligned to 4 bytes // copy 1 byte each until src is aligned to 4 bytes
while (n && s % 4) { while (n && src % 4) {
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
n--; n--;
} }
// if dest is aligned to 4 bytes as well, copy 4 bytes each // if dst is aligned to 4 bytes as well, copy 4 bytes each
if (d % 4 == 0) { if (dest % 4 == 0) {
while (n >= 16) { while (n >= 16) {
store<u32>(d , load<u32>(s )); store<u32>(dest , load<u32>(src ));
store<u32>(d + 4, load<u32>(s + 4)); store<u32>(dest + 4, load<u32>(src + 4));
store<u32>(d + 8, load<u32>(s + 8)); store<u32>(dest + 8, load<u32>(src + 8));
store<u32>(d + 12, load<u32>(s + 12)); store<u32>(dest + 12, load<u32>(src + 12));
s += 16; d += 16; n -= 16; src += 16; dest += 16; n -= 16;
} }
if (n & 8) { if (n & 8) {
store<u32>(d , load<u32>(s )); store<u32>(dest , load<u32>(src ));
store<u32>(d + 4, load<u32>(s + 4)); store<u32>(dest + 4, load<u32>(src + 4));
d += 8; s += 8; dest += 8; src += 8;
} }
if (n & 4) { if (n & 4) {
store<u32>(d, load<u32>(s)); store<u32>(dest, load<u32>(src));
d += 4; s += 4; dest += 4; src += 4;
} }
if (n & 2) { // drop to 2 bytes if (n & 2) { // drop to 2 bytes each
store<u16>(d, load<u16>(s)); store<u16>(dest, load<u16>(src));
d += 2; s += 2; dest += 2; src += 2;
} }
if (n & 1) { // drop to 1 byte if (n & 1) { // drop to 1 byte
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
} }
return dest; return ret;
} }
// if dest is not aligned to 4 bytes, use alternating shifts to copy 4 bytes each // if dst is not aligned to 4 bytes, use alternating shifts to copy 4 bytes each
// doing shifts if faster when copying enough bytes (here: 32 or more) // doing shifts if faster when copying enough bytes (here: 32 or more)
if (n >= 32) { if (n >= 32) {
switch (d % 4) { switch (dest % 4) {
// known to be != 0 // known to be != 0
case 1: case 1:
w = load<u32>(s); w = load<u32>(src);
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
n -= 3; n -= 3;
while (n >= 17) { while (n >= 17) {
x = load<u32>(s + 1); x = load<u32>(src + 1);
store<u32>(d, w >> 24 | x << 8); store<u32>(dest, w >> 24 | x << 8);
w = load<u32>(s + 5); w = load<u32>(src + 5);
store<u32>(d + 4, x >> 24 | w << 8); store<u32>(dest + 4, x >> 24 | w << 8);
x = load<u32>(s + 9); x = load<u32>(src + 9);
store<u32>(d + 8, w >> 24 | x << 8); store<u32>(dest + 8, w >> 24 | x << 8);
w = load<u32>(s + 13); w = load<u32>(src + 13);
store<u32>(d + 12, x >> 24 | w << 8); store<u32>(dest + 12, x >> 24 | w << 8);
s += 16; d += 16; n -= 16; src += 16; dest += 16; n -= 16;
} }
break; break;
case 2: case 2:
w = load<u32>(s); w = load<u32>(src);
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
n -= 2; n -= 2;
while (n >= 18) { while (n >= 18) {
x = load<u32>(s + 2); x = load<u32>(src + 2);
store<u32>(d, w >> 16 | x << 16); store<u32>(dest, w >> 16 | x << 16);
w = load<u32>(s + 6); w = load<u32>(src + 6);
store<u32>(d + 4, x >> 16 | w << 16); store<u32>(dest + 4, x >> 16 | w << 16);
x = load<u32>(s + 10); x = load<u32>(src + 10);
store<u32>(d + 8, w >> 16 | x << 16); store<u32>(dest + 8, w >> 16 | x << 16);
w = load<u32>(s + 14); w = load<u32>(src + 14);
store<u32>(d + 12, x >> 16 | w << 16); store<u32>(dest + 12, x >> 16 | w << 16);
s += 16; d += 16; n -= 16; src += 16; dest += 16; n -= 16;
} }
break; break;
case 3: case 3:
w = load<u32>(s); w = load<u32>(src);
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
n -= 1; n -= 1;
while (n >= 19) { while (n >= 19) {
x = load<u32>(s + 3); x = load<u32>(src + 3);
store<u32>(d, w >> 8 | x << 24); store<u32>(dest, w >> 8 | x << 24);
w = load<u32>(s + 7); w = load<u32>(src + 7);
store<u32>(d + 4, x >> 8 | w << 24); store<u32>(dest + 4, x >> 8 | w << 24);
x = load<u32>(s + 11); x = load<u32>(src + 11);
store<u32>(d + 8, w >> 8 | x << 24); store<u32>(dest + 8, w >> 8 | x << 24);
w = load<u32>(s + 15); w = load<u32>(src + 15);
store<u32>(d + 12, x >> 8 | w << 24); store<u32>(dest + 12, x >> 8 | w << 24);
s += 16; d += 16; n -= 16; src += 16; dest += 16; n -= 16;
} }
break; break;
} }
@ -98,47 +97,47 @@ export function memcpy(dest: usize, src: usize, n: usize): usize {
// copy remaining bytes one by one // copy remaining bytes one by one
if (n & 16) { if (n & 16) {
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
} }
if (n & 8) { if (n & 8) {
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
} }
if (n & 4) { if (n & 4) {
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
} }
if (n & 2) { if (n & 2) {
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
} }
if (n & 1) { if (n & 1) {
store<u8>(d++, load<u8>(s++)); store<u8>(dest++, load<u8>(src++));
} }
return dest; return ret;
} }
const base: usize = 8; const base: usize = 8;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,427 @@
(module
(type $iiii (func (param i32 i32 i32) (result i32)))
(type $v (func))
(global $memmove/dest (mut i32) (i32.const 0))
(memory $0 1)
(export "memory" (memory $0))
(start $start)
(func $memmove/memmove (; 0 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(local $3 i32)
(local $4 i32)
(set_local $4
(get_local $0)
)
(if
(i32.eq
(get_local $0)
(get_local $1)
)
(return
(get_local $4)
)
)
(if
(i32.lt_u
(get_local $0)
(get_local $1)
)
(block
(if
(i32.eq
(i32.rem_u
(get_local $1)
(i32.const 8)
)
(i32.rem_u
(get_local $0)
(i32.const 8)
)
)
(block
(loop $continue|0
(if
(i32.rem_u
(get_local $0)
(i32.const 8)
)
(block
(if
(i32.eqz
(get_local $2)
)
(return
(get_local $4)
)
)
(set_local $2
(i32.sub
(get_local $2)
(i32.const 1)
)
)
(set_local $0
(i32.add
(tee_local $3
(get_local $0)
)
(i32.const 1)
)
)
(i32.store8
(get_local $3)
(block (result i32)
(set_local $1
(i32.add
(tee_local $3
(get_local $1)
)
(i32.const 1)
)
)
(i32.load8_u
(get_local $3)
)
)
)
(br $continue|0)
)
)
)
(loop $continue|1
(if
(i32.ge_u
(get_local $2)
(i32.const 8)
)
(block
(i64.store
(get_local $0)
(i64.load
(get_local $1)
)
)
(set_local $2
(i32.sub
(get_local $2)
(i32.const 8)
)
)
(set_local $0
(i32.add
(get_local $0)
(i32.const 8)
)
)
(set_local $1
(i32.add
(get_local $1)
(i32.const 8)
)
)
(br $continue|1)
)
)
)
)
)
(loop $continue|2
(if
(get_local $2)
(block
(set_local $0
(i32.add
(tee_local $3
(get_local $0)
)
(i32.const 1)
)
)
(i32.store8
(get_local $3)
(block (result i32)
(set_local $1
(i32.add
(tee_local $3
(get_local $1)
)
(i32.const 1)
)
)
(i32.load8_u
(get_local $3)
)
)
)
(set_local $2
(i32.sub
(get_local $2)
(i32.const 1)
)
)
(br $continue|2)
)
)
)
)
(block
(if
(i32.eq
(i32.rem_u
(get_local $1)
(i32.const 8)
)
(i32.rem_u
(get_local $0)
(i32.const 8)
)
)
(block
(loop $continue|3
(if
(i32.rem_u
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 8)
)
(block
(if
(i32.eqz
(get_local $2)
)
(return
(get_local $4)
)
)
(i32.store8
(i32.add
(get_local $0)
(tee_local $2
(i32.sub
(get_local $2)
(i32.const 1)
)
)
)
(i32.load8_u
(i32.add
(get_local $1)
(get_local $2)
)
)
)
(br $continue|3)
)
)
)
(loop $continue|4
(if
(i32.ge_u
(get_local $2)
(i32.const 8)
)
(block
(i64.store
(i32.add
(get_local $0)
(tee_local $2
(i32.sub
(get_local $2)
(i32.const 8)
)
)
)
(i64.load
(i32.add
(get_local $1)
(get_local $2)
)
)
)
(br $continue|4)
)
)
)
)
)
(loop $continue|5
(if
(get_local $2)
(block
(i32.store8
(i32.add
(get_local $0)
(tee_local $2
(i32.sub
(get_local $2)
(i32.const 1)
)
)
)
(i32.load8_u
(i32.add
(get_local $1)
(get_local $2)
)
)
)
(br $continue|5)
)
)
)
)
)
(get_local $4)
)
(func $start (; 1 ;) (type $v)
(i64.store
(i32.const 8)
(i64.const 1229782938247303441)
)
(i64.store
(i32.const 16)
(i64.const 2459565876494606882)
)
(i64.store
(i32.const 24)
(i64.const 3689348814741910323)
)
(i64.store
(i32.const 32)
(i64.const 4919131752989213764)
)
(set_global $memmove/dest
(call $memmove/memmove
(i32.const 9)
(i32.const 24)
(i32.const 4)
)
)
(if
(i32.ne
(get_global $memmove/dest)
(i32.const 9)
)
(unreachable)
)
(if
(i64.ne
(i64.load
(i32.const 8)
)
(i64.const 1229783084848853777)
)
(unreachable)
)
(set_global $memmove/dest
(call $memmove/memmove
(i32.const 8)
(i32.const 8)
(i32.const 32)
)
)
(if
(i32.ne
(get_global $memmove/dest)
(i32.const 8)
)
(unreachable)
)
(if
(i64.ne
(i64.load
(i32.const 8)
)
(i64.const 1229783084848853777)
)
(unreachable)
)
(if
(i64.ne
(i64.load
(i32.const 16)
)
(i64.const 2459565876494606882)
)
(unreachable)
)
(if
(i64.ne
(i64.load
(i32.const 24)
)
(i64.const 3689348814741910323)
)
(unreachable)
)
(if
(i64.ne
(i64.load
(i32.const 32)
)
(i64.const 4919131752989213764)
)
(unreachable)
)
(set_global $memmove/dest
(call $memmove/memmove
(i32.const 13)
(i32.const 36)
(i32.const 3)
)
)
(if
(i64.ne
(i64.load
(i32.const 8)
)
(i64.const 4919131679688438545)
)
(unreachable)
)
(set_global $memmove/dest
(call $memmove/memmove
(i32.const 16)
(i32.const 24)
(i32.const 15)
)
)
(if
(i64.ne
(i64.load
(i32.const 8)
)
(i64.const 4919131679688438545)
)
(unreachable)
)
(if
(i64.ne
(i64.load
(i32.const 16)
)
(i64.const 3689348814741910323)
)
(unreachable)
)
(if
(i64.ne
(i64.load
(i32.const 24)
)
(i64.const 3694152654344438852)
)
(unreachable)
)
(if
(i64.ne
(i64.load
(i32.const 32)
)
(i64.const 4919131752989213764)
)
(unreachable)
)
)
)

72
tests/compiler/memmove.ts Normal file
View File

@ -0,0 +1,72 @@
function memmove(dest: usize, src: usize, n: usize): usize {
var ret = dest;
if (dest == src)
return ret;
// if (src + n <= dest || dest + n <= src) {
// memcpy(dest, src, n);
// return ret;
// }
if (dest < src) {
if (src % 8 == dest % 8) {
while (dest % 8) {
if (!n)
return ret;
--n;
store<u8>(dest++, load<u8>(src++));
}
while (n >= 8) {
store<u64>(dest, load<u64>(src));
n -= 8;
dest += 8;
src += 8;
}
}
while (n) {
store<u8>(dest++, load<u8>(src++));
--n;
}
} else {
if (src % 8 == dest % 8) {
while ((dest + n) % 8) {
if (!n)
return ret;
store<u8>(dest + --n, load<u8>(src + n));
}
while (n >= 8) {
n -= 8;
store<u64>(dest + n, load<u64>(src + n));
}
}
while (n) {
store<u8>(dest + --n, load<u8>(src + n));
}
}
return ret;
}
const base: usize = 8;
store<u64>(base , 0x1111111111111111);
store<u64>(base + 8 , 0x2222222222222222);
store<u64>(base + 16, 0x3333333333333333);
store<u64>(base + 24, 0x4444444444444444);
var dest: usize;
dest = memmove(base + 1, base + 16, 4);
assert(dest == base + 1);
assert(load<u64>(base) == 0x1111113333333311);
dest = memmove(base, base, 32);
assert(dest == base);
assert(load<u64>(base) == 0x1111113333333311);
assert(load<u64>(base + 8) == 0x2222222222222222);
assert(load<u64>(base + 16) == 0x3333333333333333);
assert(load<u64>(base + 24) == 0x4444444444444444);
dest = memmove(base + 5, base + 28, 3);
assert(load<u64>(base) == 0x4444443333333311);
dest = memmove(base + 8, base + 16, 15);
assert(load<u64>(base) == 0x4444443333333311);
assert(load<u64>(base + 8) == 0x3333333333333333);
assert(load<u64>(base + 16) == 0x3344444444444444);
assert(load<u64>(base + 24) == 0x4444444444444444);

586
tests/compiler/memmove.wast Normal file
View File

@ -0,0 +1,586 @@
(module
(type $iiii (func (param i32 i32 i32) (result i32)))
(type $v (func))
(global $memmove/base i32 (i32.const 8))
(global $memmove/dest (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 4))
(memory $0 1)
(export "memory" (memory $0))
(start $start)
(func $memmove/memmove (; 0 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(local $3 i32)
(local $4 i32)
(block
(set_local $3
(get_local $0)
)
)
(if
(i32.eq
(get_local $0)
(get_local $1)
)
(return
(get_local $3)
)
)
(if
(i32.lt_u
(get_local $0)
(get_local $1)
)
(block
(if
(i32.eq
(i32.rem_u
(get_local $1)
(i32.const 8)
)
(i32.rem_u
(get_local $0)
(i32.const 8)
)
)
(block
(block $break|0
(loop $continue|0
(if
(i32.rem_u
(get_local $0)
(i32.const 8)
)
(block
(block
(if
(i32.eqz
(get_local $2)
)
(return
(get_local $3)
)
)
(set_local $2
(i32.sub
(get_local $2)
(i32.const 1)
)
)
(i32.store8
(block (result i32)
(set_local $4
(get_local $0)
)
(set_local $0
(i32.add
(get_local $4)
(i32.const 1)
)
)
(get_local $4)
)
(i32.load8_u
(block (result i32)
(set_local $4
(get_local $1)
)
(set_local $1
(i32.add
(get_local $4)
(i32.const 1)
)
)
(get_local $4)
)
)
)
)
(br $continue|0)
)
)
)
)
(block $break|1
(loop $continue|1
(if
(i32.ge_u
(get_local $2)
(i32.const 8)
)
(block
(block
(i64.store
(get_local $0)
(i64.load
(get_local $1)
)
)
(set_local $2
(i32.sub
(get_local $2)
(i32.const 8)
)
)
(set_local $0
(i32.add
(get_local $0)
(i32.const 8)
)
)
(set_local $1
(i32.add
(get_local $1)
(i32.const 8)
)
)
)
(br $continue|1)
)
)
)
)
)
)
(block $break|2
(loop $continue|2
(if
(get_local $2)
(block
(block
(i32.store8
(block (result i32)
(set_local $4
(get_local $0)
)
(set_local $0
(i32.add
(get_local $4)
(i32.const 1)
)
)
(get_local $4)
)
(i32.load8_u
(block (result i32)
(set_local $4
(get_local $1)
)
(set_local $1
(i32.add
(get_local $4)
(i32.const 1)
)
)
(get_local $4)
)
)
)
(set_local $2
(i32.sub
(get_local $2)
(i32.const 1)
)
)
)
(br $continue|2)
)
)
)
)
)
(block
(if
(i32.eq
(i32.rem_u
(get_local $1)
(i32.const 8)
)
(i32.rem_u
(get_local $0)
(i32.const 8)
)
)
(block
(block $break|3
(loop $continue|3
(if
(i32.rem_u
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 8)
)
(block
(block
(if
(i32.eqz
(get_local $2)
)
(return
(get_local $3)
)
)
(i32.store8
(i32.add
(get_local $0)
(tee_local $2
(i32.sub
(get_local $2)
(i32.const 1)
)
)
)
(i32.load8_u
(i32.add
(get_local $1)
(get_local $2)
)
)
)
)
(br $continue|3)
)
)
)
)
(block $break|4
(loop $continue|4
(if
(i32.ge_u
(get_local $2)
(i32.const 8)
)
(block
(block
(set_local $2
(i32.sub
(get_local $2)
(i32.const 8)
)
)
(i64.store
(i32.add
(get_local $0)
(get_local $2)
)
(i64.load
(i32.add
(get_local $1)
(get_local $2)
)
)
)
)
(br $continue|4)
)
)
)
)
)
)
(block $break|5
(loop $continue|5
(if
(get_local $2)
(block
(i32.store8
(i32.add
(get_local $0)
(tee_local $2
(i32.sub
(get_local $2)
(i32.const 1)
)
)
)
(i32.load8_u
(i32.add
(get_local $1)
(get_local $2)
)
)
)
(br $continue|5)
)
)
)
)
)
)
(return
(get_local $3)
)
)
(func $start (; 1 ;) (type $v)
(i64.store
(i32.const 8)
(i64.const 1229782938247303441)
)
(i64.store
(i32.add
(i32.const 8)
(i32.const 8)
)
(i64.const 2459565876494606882)
)
(i64.store
(i32.add
(i32.const 8)
(i32.const 16)
)
(i64.const 3689348814741910323)
)
(i64.store
(i32.add
(i32.const 8)
(i32.const 24)
)
(i64.const 4919131752989213764)
)
(set_global $memmove/dest
(call $memmove/memmove
(i32.add
(i32.const 8)
(i32.const 1)
)
(i32.add
(i32.const 8)
(i32.const 16)
)
(i32.const 4)
)
)
(if
(i32.eqz
(i32.eq
(get_global $memmove/dest)
(i32.add
(i32.const 8)
(i32.const 1)
)
)
)
(unreachable)
)
(if
(i32.eqz
(i64.eq
(i64.load
(i32.const 8)
)
(i64.const 1229783084848853777)
)
)
(unreachable)
)
(set_global $memmove/dest
(call $memmove/memmove
(i32.const 8)
(i32.const 8)
(i32.const 32)
)
)
(if
(i32.eqz
(i32.eq
(get_global $memmove/dest)
(i32.const 8)
)
)
(unreachable)
)
(if
(i32.eqz
(i64.eq
(i64.load
(i32.const 8)
)
(i64.const 1229783084848853777)
)
)
(unreachable)
)
(if
(i32.eqz
(i64.eq
(i64.load
(i32.add
(i32.const 8)
(i32.const 8)
)
)
(i64.const 2459565876494606882)
)
)
(unreachable)
)
(if
(i32.eqz
(i64.eq
(i64.load
(i32.add
(i32.const 8)
(i32.const 16)
)
)
(i64.const 3689348814741910323)
)
)
(unreachable)
)
(if
(i32.eqz
(i64.eq
(i64.load
(i32.add
(i32.const 8)
(i32.const 24)
)
)
(i64.const 4919131752989213764)
)
)
(unreachable)
)
(set_global $memmove/dest
(call $memmove/memmove
(i32.add
(i32.const 8)
(i32.const 5)
)
(i32.add
(i32.const 8)
(i32.const 28)
)
(i32.const 3)
)
)
(if
(i32.eqz
(i64.eq
(i64.load
(i32.const 8)
)
(i64.const 4919131679688438545)
)
)
(unreachable)
)
(set_global $memmove/dest
(call $memmove/memmove
(i32.add
(i32.const 8)
(i32.const 8)
)
(i32.add
(i32.const 8)
(i32.const 16)
)
(i32.const 15)
)
)
(if
(i32.eqz
(i64.eq
(i64.load
(i32.const 8)
)
(i64.const 4919131679688438545)
)
)
(unreachable)
)
(if
(i32.eqz
(i64.eq
(i64.load
(i32.add
(i32.const 8)
(i32.const 8)
)
)
(i64.const 3689348814741910323)
)
)
(unreachable)
)
(if
(i32.eqz
(i64.eq
(i64.load
(i32.add
(i32.const 8)
(i32.const 16)
)
)
(i64.const 3694152654344438852)
)
)
(unreachable)
)
(if
(i32.eqz
(i64.eq
(i64.load
(i32.add
(i32.const 8)
(i32.const 24)
)
)
(i64.const 4919131752989213764)
)
)
(unreachable)
)
)
)
(;
[program.elements]
GLOBAL: NaN
GLOBAL: Infinity
FUNCTION_PROTOTYPE: isNaN
FUNCTION_PROTOTYPE: isFinite
FUNCTION_PROTOTYPE: clz
FUNCTION_PROTOTYPE: ctz
FUNCTION_PROTOTYPE: popcnt
FUNCTION_PROTOTYPE: rotl
FUNCTION_PROTOTYPE: rotr
FUNCTION_PROTOTYPE: abs
FUNCTION_PROTOTYPE: max
FUNCTION_PROTOTYPE: min
FUNCTION_PROTOTYPE: ceil
FUNCTION_PROTOTYPE: floor
FUNCTION_PROTOTYPE: copysign
FUNCTION_PROTOTYPE: nearest
FUNCTION_PROTOTYPE: reinterpret
FUNCTION_PROTOTYPE: sqrt
FUNCTION_PROTOTYPE: trunc
FUNCTION_PROTOTYPE: load
FUNCTION_PROTOTYPE: store
FUNCTION_PROTOTYPE: sizeof
FUNCTION_PROTOTYPE: select
FUNCTION_PROTOTYPE: unreachable
FUNCTION_PROTOTYPE: current_memory
FUNCTION_PROTOTYPE: grow_memory
FUNCTION_PROTOTYPE: changetype
FUNCTION_PROTOTYPE: assert
FUNCTION_PROTOTYPE: i8
FUNCTION_PROTOTYPE: i16
FUNCTION_PROTOTYPE: i32
FUNCTION_PROTOTYPE: i64
FUNCTION_PROTOTYPE: u8
FUNCTION_PROTOTYPE: u16
FUNCTION_PROTOTYPE: u32
FUNCTION_PROTOTYPE: u64
FUNCTION_PROTOTYPE: bool
FUNCTION_PROTOTYPE: f32
FUNCTION_PROTOTYPE: f64
FUNCTION_PROTOTYPE: isize
FUNCTION_PROTOTYPE: usize
GLOBAL: HEAP_BASE
FUNCTION_PROTOTYPE: memmove/memmove
GLOBAL: memmove/base
GLOBAL: memmove/dest
[program.exports]
;)

View File

@ -0,0 +1,447 @@
(module
(type $iiii (func (param i32 i32 i32) (result i32)))
(type $v (func))
(global $memset/dest (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 4))
(memory $0 1)
(export "memory" (memory $0))
(start $start)
(func $memset/memset (; 0 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(local $3 i32)
(local $4 i64)
(local $5 i32)
(set_local $3
(get_local $0)
)
(if
(i32.eqz
(get_local $2)
)
(return
(get_local $3)
)
)
(i32.store8
(get_local $0)
(get_local $1)
)
(i32.store8
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 1)
)
(get_local $1)
)
(if
(i32.le_u
(get_local $2)
(i32.const 2)
)
(return
(get_local $3)
)
)
(i32.store8
(i32.add
(get_local $0)
(i32.const 1)
)
(get_local $1)
)
(i32.store8
(i32.add
(get_local $0)
(i32.const 2)
)
(get_local $1)
)
(i32.store8
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 2)
)
(get_local $1)
)
(i32.store8
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 3)
)
(get_local $1)
)
(if
(i32.le_u
(get_local $2)
(i32.const 6)
)
(return
(get_local $3)
)
)
(i32.store8
(i32.add
(get_local $0)
(i32.const 3)
)
(get_local $1)
)
(i32.store8
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 4)
)
(get_local $1)
)
(if
(i32.le_u
(get_local $2)
(i32.const 8)
)
(return
(get_local $3)
)
)
(i32.store
(tee_local $0
(i32.add
(get_local $0)
(tee_local $5
(i32.and
(i32.sub
(i32.const 0)
(get_local $0)
)
(i32.const 3)
)
)
)
)
(tee_local $1
(i32.mul
(get_local $1)
(i32.const 16843009)
)
)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(tee_local $2
(i32.and
(i32.sub
(get_local $2)
(get_local $5)
)
(i32.const -4)
)
)
)
(i32.const 4)
)
(get_local $1)
)
(if
(i32.le_u
(get_local $2)
(i32.const 8)
)
(return
(get_local $3)
)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 4)
)
(get_local $1)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 8)
)
(get_local $1)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 12)
)
(get_local $1)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 8)
)
(get_local $1)
)
(if
(i32.le_u
(get_local $2)
(i32.const 24)
)
(return
(get_local $3)
)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 12)
)
(get_local $1)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 16)
)
(get_local $1)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 20)
)
(get_local $1)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 24)
)
(get_local $1)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 28)
)
(get_local $1)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 24)
)
(get_local $1)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 20)
)
(get_local $1)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 16)
)
(get_local $1)
)
(set_local $0
(i32.add
(get_local $0)
(tee_local $5
(i32.add
(i32.and
(get_local $0)
(i32.const 4)
)
(i32.const 24)
)
)
)
)
(set_local $2
(i32.sub
(get_local $2)
(get_local $5)
)
)
(set_local $4
(i64.or
(i64.extend_u/i32
(get_local $1)
)
(i64.shl
(i64.extend_u/i32
(get_local $1)
)
(i64.const 32)
)
)
)
(loop $continue|0
(if
(i32.ge_u
(get_local $2)
(i32.const 32)
)
(block
(i64.store
(get_local $0)
(get_local $4)
)
(i64.store
(i32.add
(get_local $0)
(i32.const 8)
)
(get_local $4)
)
(i64.store
(i32.add
(get_local $0)
(i32.const 16)
)
(get_local $4)
)
(i64.store
(i32.add
(get_local $0)
(i32.const 24)
)
(get_local $4)
)
(set_local $2
(i32.sub
(get_local $2)
(i32.const 32)
)
)
(set_local $0
(i32.add
(get_local $0)
(i32.const 32)
)
)
(br $continue|0)
)
)
)
(get_local $3)
)
(func $start (; 1 ;) (type $v)
(set_global $memset/dest
(get_global $HEAP_BASE)
)
(drop
(call $memset/memset
(get_global $memset/dest)
(i32.const 1)
(i32.const 16)
)
)
(if
(i32.ne
(i32.load8_u
(get_global $memset/dest)
)
(i32.const 1)
)
(unreachable)
)
(if
(i32.ne
(i32.load8_u
(i32.add
(get_global $memset/dest)
(i32.const 15)
)
)
(i32.const 1)
)
(unreachable)
)
(drop
(call $memset/memset
(i32.add
(get_global $memset/dest)
(i32.const 1)
)
(i32.const 2)
(i32.const 14)
)
)
(if
(i32.ne
(i32.load8_u
(get_global $memset/dest)
)
(i32.const 1)
)
(unreachable)
)
(if
(i32.ne
(i32.load8_u
(i32.add
(get_global $memset/dest)
(i32.const 1)
)
)
(i32.const 2)
)
(unreachable)
)
(if
(i32.ne
(i32.load8_u
(i32.add
(get_global $memset/dest)
(i32.const 14)
)
)
(i32.const 2)
)
(unreachable)
)
(if
(i32.ne
(i32.load8_u
(i32.add
(get_global $memset/dest)
(i32.const 15)
)
)
(i32.const 1)
)
(unreachable)
)
)
)

80
tests/compiler/memset.ts Normal file
View File

@ -0,0 +1,80 @@
function memset(dest: usize, c: u8, n: usize): usize {
var ret = dest;
// fill head and tail wwith minimal branching
if (!n)
return ret;
store<u8>(dest, c);
store<u8>(dest + n - 1, c);
if (n <= 2)
return ret;
store<u8>(dest + 1, c);
store<u8>(dest + 2, c);
store<u8>(dest + n - 2, c);
store<u8>(dest + n - 3, c);
if (n <= 6)
return ret;
store<u8>(dest + 3, c);
store<u8>(dest + n - 4, c);
if (n <= 8)
return ret;
// advance pointer to align it at 4-byte boundary
var k: usize = -dest & 3;
dest += k;
n -= k;
n &= -4;
var c32: u32 = -1 / 255 * c;
// fill head/tail up to 28 bytes each in preparation
store<u32>(dest, c32);
store<u32>(dest + n - 4, c32);
if (n <= 8)
return ret;
store<u32>(dest + 4, c32);
store<u32>(dest + 8, c32);
store<u32>(dest + n - 12, c32);
store<u32>(dest + n - 8, c32);
if (n <= 24)
return ret;
store<u32>(dest + 12, c32);
store<u32>(dest + 16, c32);
store<u32>(dest + 20, c32);
store<u32>(dest + 24, c32);
store<u32>(dest + n - 28, c32);
store<u32>(dest + n - 24, c32);
store<u32>(dest + n - 20, c32);
store<u32>(dest + n - 16, c32);
// align to a multiple of 8
k = 24 + (dest & 4);
dest += k;
n -= k;
// copy 32 bytes each
var c64: u64 = <u64>c32 | (<u64>c32 << 32);
while (n >= 32) {
store<u64>(dest, c64);
store<u64>(dest + 8, c64);
store<u64>(dest + 16, c64);
store<u64>(dest + 24, c64);
n -= 32;
dest += 32;
}
return ret;
}
var dest = HEAP_BASE;
memset(dest, 1, 16);
assert(load<u8>(dest) == 1);
assert(load<u8>(dest + 15) == 1);
memset(dest + 1, 2, 14);
assert(load<u8>(dest) == 1);
assert(load<u8>(dest + 1) == 2);
assert(load<u8>(dest + 14) == 2);
assert(load<u8>(dest + 15) == 1);

540
tests/compiler/memset.wast Normal file
View File

@ -0,0 +1,540 @@
(module
(type $iiii (func (param i32 i32 i32) (result i32)))
(type $v (func))
(global $memset/dest (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 4))
(memory $0 1)
(export "memory" (memory $0))
(start $start)
(func $memset/memset (; 0 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
(local $6 i64)
(block
(set_local $3
(get_local $0)
)
)
(if
(i32.eqz
(get_local $2)
)
(return
(get_local $3)
)
)
(i32.store8
(get_local $0)
(get_local $1)
)
(i32.store8
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 1)
)
(get_local $1)
)
(if
(i32.le_u
(get_local $2)
(i32.const 2)
)
(return
(get_local $3)
)
)
(i32.store8
(i32.add
(get_local $0)
(i32.const 1)
)
(get_local $1)
)
(i32.store8
(i32.add
(get_local $0)
(i32.const 2)
)
(get_local $1)
)
(i32.store8
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 2)
)
(get_local $1)
)
(i32.store8
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 3)
)
(get_local $1)
)
(if
(i32.le_u
(get_local $2)
(i32.const 6)
)
(return
(get_local $3)
)
)
(i32.store8
(i32.add
(get_local $0)
(i32.const 3)
)
(get_local $1)
)
(i32.store8
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 4)
)
(get_local $1)
)
(if
(i32.le_u
(get_local $2)
(i32.const 8)
)
(return
(get_local $3)
)
)
(block
(set_local $4
(i32.and
(i32.sub
(i32.const 0)
(get_local $0)
)
(i32.const 3)
)
)
)
(set_local $0
(i32.add
(get_local $0)
(get_local $4)
)
)
(set_local $2
(i32.sub
(get_local $2)
(get_local $4)
)
)
(set_local $2
(i32.and
(get_local $2)
(i32.sub
(i32.const 0)
(i32.const 4)
)
)
)
(block
(set_local $5
(i32.mul
(i32.div_u
(i32.sub
(i32.const 0)
(i32.const 1)
)
(i32.const 255)
)
(get_local $1)
)
)
)
(i32.store
(get_local $0)
(get_local $5)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 4)
)
(get_local $5)
)
(if
(i32.le_u
(get_local $2)
(i32.const 8)
)
(return
(get_local $3)
)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 4)
)
(get_local $5)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 8)
)
(get_local $5)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 12)
)
(get_local $5)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 8)
)
(get_local $5)
)
(if
(i32.le_u
(get_local $2)
(i32.const 24)
)
(return
(get_local $3)
)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 12)
)
(get_local $5)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 16)
)
(get_local $5)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 20)
)
(get_local $5)
)
(i32.store
(i32.add
(get_local $0)
(i32.const 24)
)
(get_local $5)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 28)
)
(get_local $5)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 24)
)
(get_local $5)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 20)
)
(get_local $5)
)
(i32.store
(i32.sub
(i32.add
(get_local $0)
(get_local $2)
)
(i32.const 16)
)
(get_local $5)
)
(set_local $4
(i32.add
(i32.const 24)
(i32.and
(get_local $0)
(i32.const 4)
)
)
)
(set_local $0
(i32.add
(get_local $0)
(get_local $4)
)
)
(set_local $2
(i32.sub
(get_local $2)
(get_local $4)
)
)
(block
(set_local $6
(i64.or
(i64.extend_u/i32
(get_local $5)
)
(i64.shl
(i64.extend_u/i32
(get_local $5)
)
(i64.const 32)
)
)
)
)
(block $break|0
(loop $continue|0
(if
(i32.ge_u
(get_local $2)
(i32.const 32)
)
(block
(block
(i64.store
(get_local $0)
(get_local $6)
)
(i64.store
(i32.add
(get_local $0)
(i32.const 8)
)
(get_local $6)
)
(i64.store
(i32.add
(get_local $0)
(i32.const 16)
)
(get_local $6)
)
(i64.store
(i32.add
(get_local $0)
(i32.const 24)
)
(get_local $6)
)
(set_local $2
(i32.sub
(get_local $2)
(i32.const 32)
)
)
(set_local $0
(i32.add
(get_local $0)
(i32.const 32)
)
)
)
(br $continue|0)
)
)
)
)
(return
(get_local $3)
)
)
(func $start (; 1 ;) (type $v)
(set_global $memset/dest
(get_global $HEAP_BASE)
)
(drop
(call $memset/memset
(get_global $memset/dest)
(i32.const 1)
(i32.const 16)
)
)
(if
(i32.eqz
(i32.eq
(i32.load8_u
(get_global $memset/dest)
)
(i32.const 1)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(i32.load8_u
(i32.add
(get_global $memset/dest)
(i32.const 15)
)
)
(i32.const 1)
)
)
(unreachable)
)
(drop
(call $memset/memset
(i32.add
(get_global $memset/dest)
(i32.const 1)
)
(i32.const 2)
(i32.const 14)
)
)
(if
(i32.eqz
(i32.eq
(i32.load8_u
(get_global $memset/dest)
)
(i32.const 1)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(i32.load8_u
(i32.add
(get_global $memset/dest)
(i32.const 1)
)
)
(i32.const 2)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(i32.load8_u
(i32.add
(get_global $memset/dest)
(i32.const 14)
)
)
(i32.const 2)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(i32.load8_u
(i32.add
(get_global $memset/dest)
(i32.const 15)
)
)
(i32.const 1)
)
)
(unreachable)
)
)
)
(;
[program.elements]
GLOBAL: NaN
GLOBAL: Infinity
FUNCTION_PROTOTYPE: isNaN
FUNCTION_PROTOTYPE: isFinite
FUNCTION_PROTOTYPE: clz
FUNCTION_PROTOTYPE: ctz
FUNCTION_PROTOTYPE: popcnt
FUNCTION_PROTOTYPE: rotl
FUNCTION_PROTOTYPE: rotr
FUNCTION_PROTOTYPE: abs
FUNCTION_PROTOTYPE: max
FUNCTION_PROTOTYPE: min
FUNCTION_PROTOTYPE: ceil
FUNCTION_PROTOTYPE: floor
FUNCTION_PROTOTYPE: copysign
FUNCTION_PROTOTYPE: nearest
FUNCTION_PROTOTYPE: reinterpret
FUNCTION_PROTOTYPE: sqrt
FUNCTION_PROTOTYPE: trunc
FUNCTION_PROTOTYPE: load
FUNCTION_PROTOTYPE: store
FUNCTION_PROTOTYPE: sizeof
FUNCTION_PROTOTYPE: select
FUNCTION_PROTOTYPE: unreachable
FUNCTION_PROTOTYPE: current_memory
FUNCTION_PROTOTYPE: grow_memory
FUNCTION_PROTOTYPE: changetype
FUNCTION_PROTOTYPE: assert
FUNCTION_PROTOTYPE: i8
FUNCTION_PROTOTYPE: i16
FUNCTION_PROTOTYPE: i32
FUNCTION_PROTOTYPE: i64
FUNCTION_PROTOTYPE: u8
FUNCTION_PROTOTYPE: u16
FUNCTION_PROTOTYPE: u32
FUNCTION_PROTOTYPE: u64
FUNCTION_PROTOTYPE: bool
FUNCTION_PROTOTYPE: f32
FUNCTION_PROTOTYPE: f64
FUNCTION_PROTOTYPE: isize
FUNCTION_PROTOTYPE: usize
GLOBAL: HEAP_BASE
FUNCTION_PROTOTYPE: memset/memset
GLOBAL: memset/dest
[program.exports]
;)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
var arr = changetype<i32[]>(Heap.allocate(sizeof<usize>() + 2 * sizeof<i32>())); var arr = changetype<i32[]>(allocate_memory(sizeof<usize>() + 2 * sizeof<i32>()));
assert(arr.length == 0); assert(arr.length == 0);
assert(arr.__capacity == 0); assert(arr.__capacity == 0);
@ -36,7 +36,7 @@ assert(arr[0] == 43);
assert(arr[1] == 44); assert(arr[1] == 44);
assert(arr[2] == 45); assert(arr[2] == 45);
arr.unshift(42); // see FIXME in std:array arr.unshift(42);
assert(arr.length == 4); assert(arr.length == 4);
assert(arr.__capacity == 4); assert(arr.__capacity == 4);

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,13 @@
(export "memory" (memory $0)) (export "memory" (memory $0))
(start $start) (start $start)
(func $std:array/CArray#__get (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $std:array/CArray#__get (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(if
(i32.lt_s
(get_local $1)
(i32.const 0)
)
(unreachable)
)
(i32.load (i32.load
(i32.add (i32.add
(get_local $0) (get_local $0)
@ -19,6 +26,13 @@
) )
) )
(func $std:array/CArray#__set (; 1 ;) (type $iiiv) (param $0 i32) (param $1 i32) (param $2 i32) (func $std:array/CArray#__set (; 1 ;) (type $iiiv) (param $0 i32) (param $1 i32) (param $2 i32)
(if
(i32.lt_s
(get_local $1)
(i32.const 0)
)
(unreachable)
)
(i32.store (i32.store
(i32.add (i32.add
(get_local $0) (get_local $0)

View File

@ -8,6 +8,13 @@
(export "memory" (memory $0)) (export "memory" (memory $0))
(start $start) (start $start)
(func $std:array/CArray#__get (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $std:array/CArray#__get (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(if
(i32.lt_s
(get_local $1)
(i32.const 0)
)
(unreachable)
)
(return (return
(i32.load (i32.load
(i32.add (i32.add
@ -21,6 +28,13 @@
) )
) )
(func $std:array/CArray#__set (; 1 ;) (type $iiiv) (param $0 i32) (param $1 i32) (param $2 i32) (func $std:array/CArray#__set (; 1 ;) (type $iiiv) (param $0 i32) (param $1 i32) (param $2 i32)
(if
(i32.lt_s
(get_local $1)
(i32.const 0)
)
(unreachable)
)
(i32.store (i32.store
(i32.add (i32.add
(get_local $0) (get_local $0)
@ -247,16 +261,17 @@
GLOBAL: std:heap/ALIGN_SIZE GLOBAL: std:heap/ALIGN_SIZE
GLOBAL: std:heap/ALIGN_MASK GLOBAL: std:heap/ALIGN_MASK
GLOBAL: std:heap/HEAP_OFFSET GLOBAL: std:heap/HEAP_OFFSET
CLASS_PROTOTYPE: std:heap/Heap FUNCTION_PROTOTYPE: std:heap/allocate_memory
CLASS_PROTOTYPE: Heap FUNCTION_PROTOTYPE: allocate_memory
PROPERTY: std:heap/Heap.used FUNCTION_PROTOTYPE: std:heap/free_memory
PROPERTY: std:heap/Heap.free FUNCTION_PROTOTYPE: free_memory
PROPERTY: std:heap/Heap.size FUNCTION_PROTOTYPE: std:heap/copy_memory
FUNCTION_PROTOTYPE: std:heap/Heap.allocate FUNCTION_PROTOTYPE: std:heap/move_memory
FUNCTION_PROTOTYPE: std:heap/Heap.dispose FUNCTION_PROTOTYPE: move_memory
FUNCTION_PROTOTYPE: std:heap/Heap.copy FUNCTION_PROTOTYPE: std:heap/set_memory
FUNCTION_PROTOTYPE: std:heap/Heap.fill FUNCTION_PROTOTYPE: set_memory
FUNCTION_PROTOTYPE: std:heap/Heap.compare FUNCTION_PROTOTYPE: std:heap/compare_memory
FUNCTION_PROTOTYPE: compare_memory
CLASS_PROTOTYPE: std:map/Map CLASS_PROTOTYPE: std:map/Map
CLASS_PROTOTYPE: Map CLASS_PROTOTYPE: Map
CLASS_PROTOTYPE: std:regexp/RegExp CLASS_PROTOTYPE: std:regexp/RegExp
@ -277,7 +292,11 @@
CLASS_PROTOTYPE: std:array/CArray CLASS_PROTOTYPE: std:array/CArray
CLASS_PROTOTYPE: std:error/Error CLASS_PROTOTYPE: std:error/Error
CLASS_PROTOTYPE: std:error/RangeError CLASS_PROTOTYPE: std:error/RangeError
CLASS_PROTOTYPE: std:heap/Heap FUNCTION_PROTOTYPE: std:heap/allocate_memory
FUNCTION_PROTOTYPE: std:heap/free_memory
FUNCTION_PROTOTYPE: std:heap/move_memory
FUNCTION_PROTOTYPE: std:heap/set_memory
FUNCTION_PROTOTYPE: std:heap/compare_memory
CLASS_PROTOTYPE: std:map/Map CLASS_PROTOTYPE: std:map/Map
CLASS_PROTOTYPE: std:regexp/RegExp CLASS_PROTOTYPE: std:regexp/RegExp
CLASS_PROTOTYPE: std:set/Set CLASS_PROTOTYPE: std:set/Set

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,21 @@
const size: usize = 42; const size: usize = 42;
let ptr1: usize = Heap.allocate(size); let ptr1: usize = allocate_memory(size);
let ptr2: usize = Heap.allocate(size); let ptr2: usize = allocate_memory(size);
assert(ptr1 != ptr2); assert(ptr1 != ptr2);
Heap.fill(ptr1, 0x12, size); set_memory(ptr1, 0x12, size);
let i: usize; let i: usize;
for (i = 0; i < size; ++i) for (i = 0; i < size; ++i)
assert(load<u8>(ptr1 + i) == 0x12); assert(load<u8>(ptr1 + i) == 0x12);
Heap.copy(ptr2, ptr1, size); move_memory(ptr2, ptr1, size);
for (i = 0; i < size; ++i) for (i = 0; i < size; ++i)
assert(load<u8>(ptr2 + i) == 0x12); assert(load<u8>(ptr2 + i) == 0x12);
assert(Heap.compare(ptr1, ptr2, size) == 0); assert(compare_memory(ptr1, ptr2, size) == 0);
Heap.dispose(ptr1); free_memory(ptr1);
Heap.dispose(ptr2); free_memory(ptr2);

File diff suppressed because it is too large Load Diff