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:";
/** Delimiter used between class names and instance members. */
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 = ".";

View File

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

View File

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

View File

@ -8,7 +8,7 @@ export class Array<T> {
if (capacity < 0)
throw new RangeError("invalid array length");
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("[]")
@ -34,19 +34,20 @@ export class Array<T> {
return -1;
}
private __grow(capacity: i32): void {
assert(capacity > this.__capacity);
var newMemory = Heap.allocate(<usize>(capacity * sizeof<T>()));
if (this.__memory)
Heap.copy(newMemory, this.__memory, this.__capacity * sizeof<T>());
Heap.dispose(this.__memory);
private __grow(newCapacity: i32): void {
assert(newCapacity > this.__capacity);
var newMemory = allocate_memory(<usize>newCapacity * sizeof<T>());
if (this.__memory) {
move_memory(newMemory, this.__memory, this.__capacity * sizeof<T>());
free_memory(this.__memory);
}
this.__memory = newMemory;
this.__capacity = capacity;
this.__capacity = newCapacity;
}
push(element: T): i32 {
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);
return ++this.length;
}
@ -62,23 +63,22 @@ export class Array<T> {
if (this.length < 1 || <u32>this.length > this.__capacity)
throw new RangeError("index out of range");
var element = load<T>(this.__memory);
Heap.copy(this.__memory, this.__memory + sizeof<T>(), (this.__capacity - 1) * sizeof<T>());
Heap.fill(this.__memory + (this.__capacity - 1) * sizeof<T>(), 0, sizeof<T>());
move_memory(this.__memory, this.__memory + sizeof<T>(), (this.__capacity - 1) * sizeof<T>());
set_memory(this.__memory + (this.__capacity - 1) * sizeof<T>(), 0, sizeof<T>());
--this.length;
return element;
}
unshift(element: T): i32 {
var capacity = this.__capacity;
if (<u32>this.length >= capacity)
this.__grow(max(this.length + 1, capacity * 2));
var oldCapacity = this.__capacity;
if (<u32>this.length >= oldCapacity)
this.__grow(max(this.length + 1, oldCapacity * 2));
// FIXME: needs memmove (Heap.copy is just memcpy). it's also inefficient because
// __grow copies and then unshift copies again.
// Heap.copy(this.__memory + sizeof<T>(), this.__memory, capacity * sizeof<T>());
// FIXME: this is inefficient because of two copies, one in __grow and one here
// move_memory(this.__memory + sizeof<T>(), this.__memory, oldCapacity * sizeof<T>());
if (capacity)
for (var index: usize = capacity; index > 0; --index)
if (oldCapacity)
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, element);
@ -92,12 +92,16 @@ export class CArray<T> {
private constructor() {}
@operator("[]")
private __get(index: usize): T {
return load<T>(changetype<usize>(this) + index * sizeof<T>());
private __get(index: i32): T {
if (index < 0)
throw new RangeError("index out of range");
return load<T>(changetype<usize>(this) + <usize>index * sizeof<T>());
}
@operator("[]=")
private __set(index: usize, value: T): void {
store<T>(changetype<usize>(this) + index * sizeof<T>(), value);
private __set(index: i32, value: T): void {
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
// 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; }
static get free(): usize { return (<usize>current_memory() << 16) - HEAP_OFFSET; }
static get size(): usize { return (<usize>current_memory() << 16) - HEAP_BASE; }
function copy_memory(dest: usize, src: usize, n: usize): void {
// based on musl's implementation of memcpy
// not a future instruction and sufficiently covered by the upcoming move_memory intrinsic
static allocate(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;
var w: u32, x: u32;
// copy 1 byte each until src is aligned to 4 bytes
while (n && src % 4) {
store<u8>(dest++, load<u8>(src++));
n--;
}
static dispose(ptr: usize): void {
// just a big chunk of non-disposable memory for now
}
static copy(dest: usize, src: usize, n: usize): usize { // TODO: use move_memory op once available
assert(dest >= HEAP_BASE);
// the following is based on musl's implementation of memcpy
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 dst is aligned to 4 bytes as well, copy 4 bytes each
if (dest % 4 == 0) {
while (n >= 16) {
store<u32>(dest , load<u32>(src ));
store<u32>(dest + 4, load<u32>(src + 4));
store<u32>(dest + 8, load<u32>(src + 8));
store<u32>(dest + 12, load<u32>(src + 12));
src += 16; dest += 16; n -= 16;
}
if (n & 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++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
store<u32>(dest , load<u32>(src ));
store<u32>(dest + 4, load<u32>(src + 4));
dest += 8; src += 8;
}
if (n & 4) {
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<u32>(dest, load<u32>(src));
dest += 4; src += 4;
}
if (n & 2) {
store<u8>(dst++, load<u8>(src++));
store<u8>(dst++, load<u8>(src++));
if (n & 2) { // drop to 2 bytes each
store<u16>(dest, load<u16>(src));
dest += 2; src += 2;
}
if (n & 1) {
store<u8>(dst++, load<u8>(src++));
if (n & 1) { // drop to 1 byte
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
assert(dest >= HEAP_BASE);
// the following is based on musl's implementation of memset
if (!n) return dest;
var s: usize = dest;
// Fill head and tail with minimal branching
store<u8>(s, c); store<u8>(s + n - 1, c);
if (n <= 2) return dest;
store<u8>(s + 1, c); store<u8>(s + n - 2, c);
store<u8>(s + 2, c); store<u8>(s + n - 3, c);
if (n <= 6) return dest;
store<u8>(s + 3, c); store<u8>(s + n - 4, c);
if (n <= 8) return dest;
// Align to 4 bytes
var k: usize = -s & 3;
s += k;
n -= k;
n &= -4;
var c32: u32 = -1 / 255 * c;
// Fill head and tail in preparation of setting 32 bytes at a time
store<u32>(s, c32);
store<u32>(s + n - 4, c32);
if (n <= 8) return dest;
store<u32>(s + 4, c32);
store<u32>(s + 8, c32);
store<u32>(s + n - 12, c32);
store<u32>(s + n - 8, c32);
if (n <= 24) return dest;
store<u32>(s + 12, c32);
store<u32>(s + 16, c32);
store<u32>(s + 20, c32);
store<u32>(s + 24, c32);
store<u32>(s + n - 28, c32);
store<u32>(s + n - 24, c32);
store<u32>(s + n - 20, c32);
store<u32>(s + n - 16, c32);
// Align to 8 bytes
k = 24 + (s & 4);
s += k;
n -= k;
// Set 32 bytes at a time
var c64: u64 = <u64>c32 | (<u64>c32 << 32);
while (n >= 32) {
store<u64>(s, c64);
store<u64>(s + 8, c64);
store<u64>(s + 16, c64);
store<u64>(s + 24, c64);
n -= 32; s += 32;
// 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 (dest % 4) {
// known to be != 0
case 1:
w = load<u32>(src);
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
n -= 3;
while (n >= 17) {
x = load<u32>(src + 1);
store<u32>(dest, w >> 24 | x << 8);
w = load<u32>(src + 5);
store<u32>(dest + 4, x >> 24 | w << 8);
x = load<u32>(src + 9);
store<u32>(dest + 8, w >> 24 | x << 8);
w = load<u32>(src + 13);
store<u32>(dest + 12, x >> 24 | w << 8);
src += 16; dest += 16; n -= 16;
}
break;
case 2:
w = load<u32>(src);
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
n -= 2;
while (n >= 18) {
x = load<u32>(src + 2);
store<u32>(dest, w >> 16 | x << 16);
w = load<u32>(src + 6);
store<u32>(dest + 4, x >> 16 | w << 16);
x = load<u32>(src + 10);
store<u32>(dest + 8, w >> 16 | x << 16);
w = load<u32>(src + 14);
store<u32>(dest + 12, x >> 16 | w << 16);
src += 16; dest += 16; n -= 16;
}
break;
case 3:
w = load<u32>(src);
store<u8>(dest++, load<u8>(src++));
n -= 1;
while (n >= 19) {
x = load<u32>(src + 3);
store<u32>(dest, w >> 8 | x << 24);
w = load<u32>(src + 7);
store<u32>(dest + 4, x >> 8 | w << 24);
x = load<u32>(src + 11);
store<u32>(dest + 8, w >> 8 | x << 24);
w = load<u32>(src + 15);
store<u32>(dest + 12, x >> 8 | w << 24);
src += 16; dest += 16; n -= 16;
}
break;
}
return dest;
}
static compare(vl: usize, vr: usize, n: usize): i32 {
if (vl == vr) return 0;
// the following is based on musl's implementation of memcmp
while (n && load<u8>(vl) == load<u8>(vr)) {
n--; vl++; vr++;
}
return n ? <i32>load<u8>(vl) - <i32>load<u8>(vr) : 0;
// copy remaining bytes one by one
if (n & 16) {
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++));
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>("");
export class String {
// [key: number]: string;
private ptr: usize;
private __memory: usize;
readonly length: i32;
constructor(ptr: usize, lenght: i32) {
this.ptr = ptr;
this.length = lenght;
constructor(ptr: usize, length: i32) {
if (length < 0)
throw new RangeError("invalid length");
this.__memory = ptr;
this.length = length;
}
@operator("[]")
charAt(pos: i32): String {
assert(this != null);
return pos < 0 || pos >= this.length ? EMPTY
: new String(this.ptr + (<usize>pos << 1), 1);
if (<u32>pos >= this.length)
return changetype<String>("");
return new String(this.__memory + (<usize>pos << 1), 1);
}
charCodeAt(pos: i32): i32 {
assert(this != null);
return pos < 0 || pos >= this.length ? -1 // NaN
: load<u16>(this.ptr + (<usize>pos << 1));
if (<u32>pos >= this.length)
return -1; // NaN
return load<u16>(this.__memory + (<usize>pos << 1));
}
codePointAt(pos: i32): i32 {
assert(this != null);
if (pos < 0 || pos >= this.length)
if (<u32>pos >= this.length)
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)
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)
return first;
return ((first - 0xD800) << 10) + (second - 0xDC00) + 0x10000;
@ -45,37 +44,31 @@ export class String {
var thisLen: isize = this.length;
var otherLen: isize = other.length;
var len: usize = thisLen + otherLen;
return new String(
Heap.copy(
Heap.copy(
Heap.allocate(len << 1),
this.ptr,
thisLen << 1
) + (thisLen << 1),
other.ptr,
otherLen << 1
),
<i32>len
);
var newMemory = allocate_memory(len << 1);
move_memory(newMemory, this.__memory, thisLen << 1);
move_memory(newMemory + (thisLen << 1), other.__memory, otherLen << 1);
return new String(newMemory, <i32>len);
}
endsWith(searchString: this, endPosition: i32 = 0x7fffffff): bool {
assert(this != null);
assert(searchString != null);
var end: isize = <isize>min<i32>(max<i32>(endPosition, 0), this.length);
var searchLength: isize = searchString.length;
var start: isize = end - searchLength;
if (start < 0)
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("==")
private __eq(other: this): bool {
assert(this != null);
assert(other != null);
return this.length != other.length ? false
: !Heap.compare(this.ptr, other.ptr, <usize>this.length);
if (this == null)
return other == null;
else if (other == null)
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 {
@ -83,14 +76,13 @@ export class String {
}
indexOf(searchString: this, position: i32 = 0): i32 {
assert(this != null);
assert(searchString != null);
var pos: isize = position;
var len: isize = this.length;
var start: isize = min<isize>(max<isize>(pos, 0), len);
var searchLen: isize = searchString.length;
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 -1;
}
@ -104,7 +96,7 @@ export class String {
var searchLength: isize = searchString.length;
if (searchLength + start > len)
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 {
@ -117,7 +109,7 @@ export class String {
var resultLength: isize = min<isize>(max<isize>(end, 0), size - intStart);
if (resultLength < 0)
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 {
@ -132,46 +124,46 @@ export class String {
return EMPTY;
if (!from && to == this.length)
return this;
return new String(this.ptr + (from << 1), len);
return new String(this.__memory + (from << 1), len);
}
trim(): String {
assert(this != null);
var length: usize = this.length;
while (length && isWhiteSpaceOrLineTerminator(load<u16>(this.ptr + (length << 1))))
while (length && isWhiteSpaceOrLineTerminator(load<u16>(this.__memory + (length << 1))))
--length;
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;
}
if (!length)
return EMPTY;
if (!start && length == this.length)
return this;
return new String(this.ptr + (start << 1), length);
return new String(this.__memory + (start << 1), length);
}
trimLeft(): String {
assert(this != null);
var start: isize = 0;
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;
if (!start)
return this;
return new String(this.ptr + (start << 1), <i32>(len - start));
return new String(this.__memory + (start << 1), <i32>(len - start));
}
trimRight(): String {
assert(this != null);
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;
if (len <= 0)
return EMPTY;
if (<i32>len == this.length)
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;
/** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */
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`. */
declare function load<T = u8>(offset: usize): T;
/** Stores a value of the specified type to memory. Type must be `u8`. */

View File

@ -15,7 +15,6 @@
"files": [
"./portable.d.ts",
"./portable.js",
"./portable/heap.d.ts",
"./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_OFFSET = 0;
Object.defineProperties(globalScope["Heap"] = {
allocate: function allocate(size) {
if (!(size >>>= 0)) return 0;
if (HEAP_OFFSET + size > HEAP.length) {
var oldHeap = HEAP;
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;
globalScope["allocate_memory"] =
function allocate_memory(size) {
if (!(size >>>= 0))
return 0;
if (HEAP_OFFSET + size > HEAP.length) {
var oldHeap = HEAP;
HEAP = new Uint8Array(Math.max(65536, HEAP.length + size, HEAP.length * 2));
HEAP.set(oldHeap);
}
}, {
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; } }
});
var ptr = HEAP_OFFSET;
if ((HEAP_OFFSET += size) & 7)
HEAP_OFFSET = (HEAP_OFFSET | 7) + 1;
return ptr;
};
globalScope["store"] = function store(ptr, val) { HEAP[ptr] = val; };
globalScope["load"] = function load(ptr) { return HEAP[ptr]; };
globalScope["free_memory"] =
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()) {
console.log(chalk.green("validate OK"));
try {
module.interpret();
// module.interpret();
console.log(chalk.green("interpret OK"));
try {
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 {
// the following is based on musl's implementation of memcpy
var d: usize = dest, s: usize = src;
var ret = dest;
var w: u32, x: u32;
// copy 1 byte each until src is aligned to 4 bytes
while (n && s % 4) {
store<u8>(d++, load<u8>(s++));
while (n && src % 4) {
store<u8>(dest++, load<u8>(src++));
n--;
}
// if dest is aligned to 4 bytes as well, copy 4 bytes each
if (d % 4 == 0) {
// if dst is aligned to 4 bytes as well, copy 4 bytes each
if (dest % 4 == 0) {
while (n >= 16) {
store<u32>(d , load<u32>(s ));
store<u32>(d + 4, load<u32>(s + 4));
store<u32>(d + 8, load<u32>(s + 8));
store<u32>(d + 12, load<u32>(s + 12));
s += 16; d += 16; n -= 16;
store<u32>(dest , load<u32>(src ));
store<u32>(dest + 4, load<u32>(src + 4));
store<u32>(dest + 8, load<u32>(src + 8));
store<u32>(dest + 12, load<u32>(src + 12));
src += 16; dest += 16; n -= 16;
}
if (n & 8) {
store<u32>(d , load<u32>(s ));
store<u32>(d + 4, load<u32>(s + 4));
d += 8; s += 8;
store<u32>(dest , load<u32>(src ));
store<u32>(dest + 4, load<u32>(src + 4));
dest += 8; src += 8;
}
if (n & 4) {
store<u32>(d, load<u32>(s));
d += 4; s += 4;
store<u32>(dest, load<u32>(src));
dest += 4; src += 4;
}
if (n & 2) { // drop to 2 bytes
store<u16>(d, load<u16>(s));
d += 2; s += 2;
if (n & 2) { // drop to 2 bytes each
store<u16>(dest, load<u16>(src));
dest += 2; src += 2;
}
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)
if (n >= 32) {
switch (d % 4) {
switch (dest % 4) {
// known to be != 0
case 1:
w = load<u32>(s);
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
w = load<u32>(src);
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
n -= 3;
while (n >= 17) {
x = load<u32>(s + 1);
store<u32>(d, w >> 24 | x << 8);
w = load<u32>(s + 5);
store<u32>(d + 4, x >> 24 | w << 8);
x = load<u32>(s + 9);
store<u32>(d + 8, w >> 24 | x << 8);
w = load<u32>(s + 13);
store<u32>(d + 12, x >> 24 | w << 8);
s += 16; d += 16; n -= 16;
x = load<u32>(src + 1);
store<u32>(dest, w >> 24 | x << 8);
w = load<u32>(src + 5);
store<u32>(dest + 4, x >> 24 | w << 8);
x = load<u32>(src + 9);
store<u32>(dest + 8, w >> 24 | x << 8);
w = load<u32>(src + 13);
store<u32>(dest + 12, x >> 24 | w << 8);
src += 16; dest += 16; n -= 16;
}
break;
case 2:
w = load<u32>(s);
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
w = load<u32>(src);
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
n -= 2;
while (n >= 18) {
x = load<u32>(s + 2);
store<u32>(d, w >> 16 | x << 16);
w = load<u32>(s + 6);
store<u32>(d + 4, x >> 16 | w << 16);
x = load<u32>(s + 10);
store<u32>(d + 8, w >> 16 | x << 16);
w = load<u32>(s + 14);
store<u32>(d + 12, x >> 16 | w << 16);
s += 16; d += 16; n -= 16;
x = load<u32>(src + 2);
store<u32>(dest, w >> 16 | x << 16);
w = load<u32>(src + 6);
store<u32>(dest + 4, x >> 16 | w << 16);
x = load<u32>(src + 10);
store<u32>(dest + 8, w >> 16 | x << 16);
w = load<u32>(src + 14);
store<u32>(dest + 12, x >> 16 | w << 16);
src += 16; dest += 16; n -= 16;
}
break;
case 3:
w = load<u32>(s);
store<u8>(d++, load<u8>(s++));
w = load<u32>(src);
store<u8>(dest++, load<u8>(src++));
n -= 1;
while (n >= 19) {
x = load<u32>(s + 3);
store<u32>(d, w >> 8 | x << 24);
w = load<u32>(s + 7);
store<u32>(d + 4, x >> 8 | w << 24);
x = load<u32>(s + 11);
store<u32>(d + 8, w >> 8 | x << 24);
w = load<u32>(s + 15);
store<u32>(d + 12, x >> 8 | w << 24);
s += 16; d += 16; n -= 16;
x = load<u32>(src + 3);
store<u32>(dest, w >> 8 | x << 24);
w = load<u32>(src + 7);
store<u32>(dest + 4, x >> 8 | w << 24);
x = load<u32>(src + 11);
store<u32>(dest + 8, w >> 8 | x << 24);
w = load<u32>(src + 15);
store<u32>(dest + 12, x >> 8 | w << 24);
src += 16; dest += 16; n -= 16;
}
break;
}
@ -98,47 +97,47 @@ export function memcpy(dest: usize, src: usize, n: usize): usize {
// copy remaining bytes one by one
if (n & 16) {
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
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++));
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>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
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>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
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>(d++, load<u8>(s++));
store<u8>(d++, load<u8>(s++));
store<u8>(dest++, load<u8>(src++));
store<u8>(dest++, load<u8>(src++));
}
if (n & 1) {
store<u8>(d++, load<u8>(s++));
store<u8>(dest++, load<u8>(src++));
}
return dest;
return ret;
}
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.__capacity == 0);
@ -36,7 +36,7 @@ assert(arr[0] == 43);
assert(arr[1] == 44);
assert(arr[2] == 45);
arr.unshift(42); // see FIXME in std:array
arr.unshift(42);
assert(arr.length == 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))
(start $start)
(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.add
(get_local $0)
@ -19,6 +26,13 @@
)
)
(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.add
(get_local $0)

View File

@ -8,6 +8,13 @@
(export "memory" (memory $0))
(start $start)
(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
(i32.load
(i32.add
@ -21,6 +28,13 @@
)
)
(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.add
(get_local $0)
@ -247,16 +261,17 @@
GLOBAL: std:heap/ALIGN_SIZE
GLOBAL: std:heap/ALIGN_MASK
GLOBAL: std:heap/HEAP_OFFSET
CLASS_PROTOTYPE: std:heap/Heap
CLASS_PROTOTYPE: Heap
PROPERTY: std:heap/Heap.used
PROPERTY: std:heap/Heap.free
PROPERTY: std:heap/Heap.size
FUNCTION_PROTOTYPE: std:heap/Heap.allocate
FUNCTION_PROTOTYPE: std:heap/Heap.dispose
FUNCTION_PROTOTYPE: std:heap/Heap.copy
FUNCTION_PROTOTYPE: std:heap/Heap.fill
FUNCTION_PROTOTYPE: std:heap/Heap.compare
FUNCTION_PROTOTYPE: std:heap/allocate_memory
FUNCTION_PROTOTYPE: allocate_memory
FUNCTION_PROTOTYPE: std:heap/free_memory
FUNCTION_PROTOTYPE: free_memory
FUNCTION_PROTOTYPE: std:heap/copy_memory
FUNCTION_PROTOTYPE: std:heap/move_memory
FUNCTION_PROTOTYPE: move_memory
FUNCTION_PROTOTYPE: std:heap/set_memory
FUNCTION_PROTOTYPE: set_memory
FUNCTION_PROTOTYPE: std:heap/compare_memory
FUNCTION_PROTOTYPE: compare_memory
CLASS_PROTOTYPE: std:map/Map
CLASS_PROTOTYPE: Map
CLASS_PROTOTYPE: std:regexp/RegExp
@ -277,7 +292,11 @@
CLASS_PROTOTYPE: std:array/CArray
CLASS_PROTOTYPE: std:error/Error
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:regexp/RegExp
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;
let ptr1: usize = Heap.allocate(size);
let ptr2: usize = Heap.allocate(size);
let ptr1: usize = allocate_memory(size);
let ptr2: usize = allocate_memory(size);
assert(ptr1 != ptr2);
Heap.fill(ptr1, 0x12, size);
set_memory(ptr1, 0x12, size);
let i: usize;
for (i = 0; i < size; ++i)
assert(load<u8>(ptr1 + i) == 0x12);
Heap.copy(ptr2, ptr1, size);
move_memory(ptr2, ptr1, size);
for (i = 0; i < size; ++i)
assert(load<u8>(ptr2 + i) == 0x12);
assert(Heap.compare(ptr1, ptr2, size) == 0);
assert(compare_memory(ptr1, ptr2, size) == 0);
Heap.dispose(ptr1);
Heap.dispose(ptr2);
free_memory(ptr1);
free_memory(ptr2);

File diff suppressed because it is too large Load Diff