mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +00:00
Polyfill move_memory and set_memory and remove Heap
This commit is contained in:
parent
2c009c67d3
commit
ad469ca445
@ -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 = ".";
|
||||
|
@ -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() {
|
||||
|
120
src/module.ts
120
src/module.ts
@ -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
40
std/assembly.d.ts
vendored
@ -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 {}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
6
std/portable.d.ts
vendored
@ -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`. */
|
||||
|
@ -15,7 +15,6 @@
|
||||
"files": [
|
||||
"./portable.d.ts",
|
||||
"./portable.js",
|
||||
"./portable/heap.d.ts",
|
||||
"./portable/heap.js"
|
||||
]
|
||||
}
|
||||
|
21
std/portable/heap.d.ts
vendored
21
std/portable/heap.d.ts
vendored
@ -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;
|
||||
}
|
@ -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];
|
||||
};
|
||||
|
@ -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
@ -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
427
tests/compiler/memmove.optimized.wast
Normal file
427
tests/compiler/memmove.optimized.wast
Normal 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
72
tests/compiler/memmove.ts
Normal 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
586
tests/compiler/memmove.wast
Normal 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]
|
||||
|
||||
;)
|
447
tests/compiler/memset.optimized.wast
Normal file
447
tests/compiler/memset.optimized.wast
Normal 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
80
tests/compiler/memset.ts
Normal 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
540
tests/compiler/memset.wast
Normal 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
@ -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
@ -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)
|
||||
|
@ -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
@ -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
Loading…
x
Reference in New Issue
Block a user