Rename memory instructions; Rework constant handling

This commit is contained in:
dcodeIO
2018-07-18 23:49:32 +02:00
parent 34e8facfdc
commit a1b75b69b7
170 changed files with 26392 additions and 5185 deletions

View File

@ -1,7 +1,7 @@
/**
* Arena Memory Allocator
*
* Provides a `reset_memory` function to reset the heap to its initial state. A user has to make
* Provides a `memory.reset` function to reset the heap to its initial state. A user has to make
* sure that there are no more references to cleared memory afterwards. Always aligns to 8 bytes.
*
* @module std/assembly/allocator/arena
@ -13,17 +13,17 @@ var startOffset: usize = (HEAP_BASE + AL_MASK) & ~AL_MASK;
var offset: usize = startOffset;
@global
export function allocate_memory(size: usize): usize {
export function __memory_allocate(size: usize): usize {
if (size) {
if (size > MAX_SIZE_32) unreachable();
let ptr = offset;
let newPtr = (ptr + size + AL_MASK) & ~AL_MASK;
let pagesBefore = current_memory();
let pagesBefore = memory.size();
if (newPtr > <usize>pagesBefore << 16) {
let pagesNeeded = ((newPtr - ptr + 0xffff) & ~0xffff) >>> 16;
let pagesWanted = max(pagesBefore, pagesNeeded); // double memory
if (grow_memory(pagesWanted) < 0) {
if (grow_memory(pagesNeeded) < 0) {
if (memory.grow(pagesWanted) < 0) {
if (memory.grow(pagesNeeded) < 0) {
unreachable(); // out of memory
}
}
@ -35,11 +35,11 @@ export function allocate_memory(size: usize): usize {
}
@global
export function free_memory(ptr: usize): void {
export function __memory_free(ptr: usize): void {
// nop
}
@global
export function reset_memory(): void {
export function __memory_reset(): void {
offset = startOffset;
}

View File

@ -176,10 +176,10 @@ function update_max_ptr(new_value: usize): i32 {
// if (brk(new_value)) {
// return 0;
// }
let oldPages = <u32>current_memory();
let newPages = <u32>(((new_value + 0xffff) & ~0xffff) >> 16);
let oldPages = <i32>memory.size();
let newPages = <i32>(((new_value + 0xffff) & ~0xffff) >>> 16);
assert(newPages > oldPages);
if (grow_memory(newPages - oldPages) < 0) {
if (memory.grow(newPages - oldPages) < 0) {
return 0;
}
// max_ptr = new_value;
@ -339,7 +339,7 @@ function lower_bucket_limit(bucket: usize): u32 {
}
@global
export function allocate_memory(request: usize): usize {
export function __memory_allocate(request: usize): usize {
var original_bucket: usize, bucket: usize;
/*
@ -357,7 +357,7 @@ export function allocate_memory(request: usize): usize {
if (base_ptr == 0) {
// base_ptr = max_ptr = (uint8_t *)sbrk(0);
base_ptr = (NODE_IS_SPLIT_END + 7) & ~7; // must be aligned
max_ptr = <usize>current_memory() << 16; // must grow first
max_ptr = <usize>memory.size() << 16; // must grow first
bucket_limit = BUCKET_COUNT - 1;
if (!update_max_ptr(base_ptr + List.SIZE)) {
return 0;
@ -474,7 +474,7 @@ export function allocate_memory(request: usize): usize {
}
@global
export function free_memory(ptr: usize): void {
export function __memory_free(ptr: usize): void {
var bucket: usize, i: usize;
/*
@ -540,6 +540,6 @@ export function free_memory(ptr: usize): void {
}
@global
export function reset_memory(): void {
export function __memory_reset(): void {
unreachable();
}

View File

@ -12,16 +12,16 @@ declare function _malloc(size: usize): usize;
declare function _free(ptr: usize): void;
@global
export function allocate_memory(size: usize): usize {
export function __memory_allocate(size: usize): usize {
return _malloc(size);
}
@global
export function free_memory(ptr: usize): void {
export function __memory_free(ptr: usize): void {
_free(ptr);
}
@global
export function reset_memory(): void {
export function __memory_reset(): void {
unreachable();
}

View File

@ -11,16 +11,16 @@ declare function malloc(size: usize): usize;
declare function free(ptr: usize): void;
@global
export function allocate_memory(size: usize): usize {
export function __memory_allocate(size: usize): usize {
return malloc(size);
}
@global
export function free_memory(ptr: usize): void {
export function __memory_free(ptr: usize): void {
free(ptr);
}
@global
export function reset_memory(): void {
export function __memory_reset(): void {
unreachable();
}

View File

@ -437,15 +437,15 @@ var ROOT: Root = changetype<Root>(0);
/** Allocates a chunk of memory. */
@global
export function allocate_memory(size: usize): usize {
export function __memory_allocate(size: usize): usize {
// initialize if necessary
var root = ROOT;
if (!root) {
let rootOffset = (HEAP_BASE + AL_MASK) & ~AL_MASK;
let pagesBefore = current_memory();
let pagesBefore = memory.size();
let pagesNeeded = <i32>((((rootOffset + Root.SIZE) + 0xffff) & ~0xffff) >>> 16);
if (pagesNeeded > pagesBefore && grow_memory(pagesNeeded - pagesBefore) < 0) unreachable();
if (pagesNeeded > pagesBefore && memory.grow(pagesNeeded - pagesBefore) < 0) unreachable();
ROOT = root = changetype<Root>(rootOffset);
root.tailRef = 0;
root.flMap = 0;
@ -455,7 +455,7 @@ export function allocate_memory(size: usize): usize {
root.setHead(fl, sl, null);
}
}
root.addMemory((rootOffset + Root.SIZE + AL_MASK) & ~AL_MASK, current_memory() << 16);
root.addMemory((rootOffset + Root.SIZE + AL_MASK) & ~AL_MASK, memory.size() << 16);
}
// search for a suitable block
@ -469,15 +469,15 @@ export function allocate_memory(size: usize): usize {
if (!block) {
// request more memory
let pagesBefore = current_memory();
let pagesBefore = memory.size();
let pagesNeeded = <i32>(((size + 0xffff) & ~0xffff) >>> 16);
let pagesWanted = max(pagesBefore, pagesNeeded); // double memory
if (grow_memory(pagesWanted) < 0) {
if (grow_memory(pagesNeeded) < 0) {
if (memory.grow(pagesWanted) < 0) {
if (memory.grow(pagesNeeded) < 0) {
unreachable(); // out of memory
}
}
let pagesAfter = current_memory();
let pagesAfter = memory.size();
root.addMemory(<usize>pagesBefore << 16, <usize>pagesAfter << 16);
block = assert(root.search(size)); // must be found now
}
@ -491,7 +491,7 @@ export function allocate_memory(size: usize): usize {
/** Frees the chunk of memory at the specified address. */
@global
export function free_memory(data: usize): void {
export function __memory_free(data: usize): void {
if (data) {
let root = ROOT;
if (root) {
@ -505,6 +505,6 @@ export function free_memory(data: usize): void {
}
@global
export function reset_memory(): void {
export function __memory_reset(): void {
unreachable();
}

View File

@ -25,7 +25,7 @@ export class Array<T> {
var buffer = allocUnsafe(byteLength);
this.buffer_ = buffer;
this.length_ = length;
set_memory(
memory.fill(
changetype<usize>(buffer) + HEADER_SIZE_AB,
0,
<usize>byteLength
@ -216,7 +216,7 @@ export class Array<T> {
var buffer = this.buffer_;
var element = loadUnsafe<T,T>(buffer, 0);
var lastIndex = length - 1;
move_memory(
memory.copy(
changetype<usize>(buffer) + HEADER_SIZE_AB,
changetype<usize>(buffer) + HEADER_SIZE_AB + sizeof<T>(),
<usize>lastIndex << alignof<T>()
@ -246,7 +246,7 @@ export class Array<T> {
capacity = buffer.byteLength >>> alignof<T>();
this.buffer_ = buffer;
}
move_memory(
memory.copy(
changetype<usize>(buffer) + HEADER_SIZE_AB + sizeof<T>(),
changetype<usize>(buffer) + HEADER_SIZE_AB,
<usize>(capacity - 1) << alignof<T>()
@ -267,7 +267,7 @@ export class Array<T> {
assert(newLength >= 0);
var sliced = new Array<T>(newLength);
if (newLength) {
move_memory(
memory.copy(
changetype<usize>(sliced.buffer_) + HEADER_SIZE_AB,
changetype<usize>(this.buffer_) + HEADER_SIZE_AB + (<usize>begin << alignof<T>()),
<usize>newLength << alignof<T>()
@ -283,7 +283,7 @@ export class Array<T> {
if (start >= length) return;
deleteCount = min(deleteCount, length - start);
var buffer = this.buffer_;
move_memory(
memory.copy(
changetype<usize>(buffer) + HEADER_SIZE_AB + (<usize>start << alignof<T>()),
changetype<usize>(buffer) + HEADER_SIZE_AB + (<usize>(start + deleteCount) << alignof<T>()),
<usize>deleteCount << alignof<T>()

View File

@ -12,7 +12,7 @@ export class ArrayBuffer {
constructor(length: i32, unsafe: bool = false) {
if (<u32>length > <u32>MAX_BLENGTH) throw new RangeError("Invalid array buffer length");
var buffer = allocUnsafe(length);
if (!unsafe) set_memory(changetype<usize>(buffer) + HEADER_SIZE, 0, <usize>length);
if (!unsafe) memory.fill(changetype<usize>(buffer) + HEADER_SIZE, 0, <usize>length);
return buffer;
}
@ -24,7 +24,7 @@ export class ArrayBuffer {
else end = min(end, len);
var newLen = max(end - begin, 0);
var buffer = allocUnsafe(newLen);
move_memory(changetype<usize>(buffer) + HEADER_SIZE, changetype<usize>(this) + HEADER_SIZE + begin, newLen);
memory.copy(changetype<usize>(buffer) + HEADER_SIZE, changetype<usize>(this) + HEADER_SIZE + begin, newLen);
return buffer;
}
}

View File

@ -1,89 +1,43 @@
@inline export const NaN: f64 = 0 / 0;
@inline export const Infinity: f64 = 1 / 0;
export declare const HEAP_BASE: usize;
export declare function isInteger<T>(value?: T): bool;
export declare function isFloat<T>(value?: T): bool;
export declare function isSigned<T>(value?: T): bool;
export declare function isReference<T>(value?: T): bool;
export declare function isString<T>(value?: T): bool;
export declare function isArray<T>(value?: T): bool;
export declare function isDefined(expression: void): bool;
export declare function isConstant(expression: void): bool;
export const NaN: f64 = 0 / 0;
export const Infinity: f64 = 1 / 0;
export function isNaN<T>(value: T): bool {
return value != value;
}
export function isFinite<T>(value: T): bool {
return value - value == 0;
}
export function isNaN<T>(value: T): bool { return value != value; }
export function isFinite<T>(value: T): bool { return value - value == 0; }
export declare function clz<T>(value: T): T;
export declare function ctz<T>(value: T): T;
export declare function popcnt<T>(value: T): T;
export declare function rotl<T>(value: T, shift: T): T;
export declare function rotr<T>(value: T, shift: T): T;
export declare function abs<T>(value: T): T;
export declare function max<T>(left: T, right: T): T;
export declare function min<T>(left: T, right: T): T;
export declare function ceil<T>(value: T): T;
export declare function floor<T>(value: T): T;
export declare function copysign<T>(left: T, right: T): T;
export declare function nearest<T>(value: T): T;
export declare function reinterpret<T>(value: void): T;
export declare function sqrt<T>(value: T): T;
export declare function trunc<T>(value: T): T;
export declare function load<T>(offset: usize, constantOffset?: usize): T;
export declare function store<T>(offset: usize, value: void, constantOffset?: usize): void;
export declare function sizeof<T>(): usize; // | u32 / u64
export declare function alignof<T>(): usize; // | u32 / u64
export declare function offsetof<T>(fieldName?: string): usize; // | u32 / u64
export declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;
export declare function unreachable(): void;
export declare function current_memory(): i32;
export declare function grow_memory(pages: i32): i32;
// export declare function move_memory(dest: usize, src: usize: n: usize): void;
// export declare function set_memory(dest: usize, value: u32, n: usize): void;
export declare function changetype<T>(value: void): T;
export declare function assert<T>(isTrueish: T, message?: string): T;
export declare function unchecked<T>(expr: T): T;
export declare function call_indirect<T>(target: void, ...args: void[]): T;
export declare function i8(value: void): i8;
@ -233,6 +187,15 @@ export namespace f64 {
export declare function trunc(value: f64): f64;
}
export declare const HEAP_BASE: usize;
export declare function start(): void;
export declare function ERROR(message?: void): void;
export declare function WARNING(message?: void): void;
export declare function INFO(message?: void): void;
export declare function __memory_size(): i32;
export declare function __memory_grow(pages: i32): i32;
// export declare function __memory_copy(dest: usize, src: usize: n: usize): void;
// export declare function __memory_fill(dest: usize, value: u32, n: usize): void;
export declare function __gc_iterate_roots(fn: (ref: usize) => void): void;

View File

@ -11,6 +11,10 @@ import {
MAX_SIZE_32
} from "../internal/allocator";
import {
__gc_iterate_roots
} from "../builtins";
// ╒═══════════════ Managed object layout (32-bit) ════════════════╕
// 3 2 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
@ -78,6 +82,11 @@ class ManagedObject {
prev.next = next;
}
clear(): void {
this.nextWithFlags = changetype<usize>(this);
this.prev = this;
}
/** Tests if this object is white, that is unreachable (so far). */
get isWhite(): bool {
return (this.nextWithFlags & Flags.MASK) == Flags.WHITE;
@ -107,7 +116,7 @@ class ManagedObject {
makeGray(): void {
if (this != iter) {
this.remove();
to.insert(this);
set2.insert(this);
} else {
iter = iter.prev;
}
@ -131,8 +140,8 @@ const enum State {
var state = State.INIT;
// From and to spaces
var from: ManagedObject;
var to: ManagedObject;
var set1: ManagedObject;
var set2: ManagedObject;
var iter: ManagedObject;
/** Performs a single step according to the current state. */
@ -140,34 +149,40 @@ function gc_step(): void {
var obj: ManagedObject;
switch (state) {
case State.INIT: {
from = changetype<ManagedObject>(allocate_memory(ManagedObject.SIZE));
from.nextWithFlags = changetype<usize>(from);
from.prev = from;
to = changetype<ManagedObject>(allocate_memory(ManagedObject.SIZE));
to.nextWithFlags = changetype<usize>(to);
to.prev = to;
iter = to;
set1 = changetype<ManagedObject>(memory.allocate(ManagedObject.SIZE));
set1.clear();
set2 = changetype<ManagedObject>(memory.allocate(ManagedObject.SIZE));
set2.clear();
iter = set2;
// fall-through
}
case State.IDLE: {
// start by marking roots
__gc_iterate_roots(function mark_root(ref: usize): void {
if (ref) {
let obj = changetype<ManagedObject>(ref - ManagedObject.SIZE);
obj.makeBlack();
obj.visitFn(ref);
}
});
state = State.MARK;
break;
}
case State.MARK: {
obj = iter.next;
if (obj != to) {
if (obj != set2) {
iter = obj;
obj.makeBlack();
obj.visitFn(changetype<usize>(obj) + ManagedObject.SIZE);
} else {
obj = iter.next;
if (obj == to) {
let temp = from;
from = to;
to = temp;
if (obj == set2) {
let set1_ = set1;
set1 = set2;
set2 = set1_;
Flags.WHITE ^= 1;
Flags.BLACK ^= 1;
iter = from.next;
iter = set1.next;
state = State.SWEEP;
}
}
@ -175,12 +190,11 @@ function gc_step(): void {
}
case State.SWEEP: {
obj = iter;
if (obj != to) {
if (obj !== set2) {
iter = obj.next;
free_memory(changetype<usize>(obj));
memory.free(changetype<usize>(obj));
} else {
to.nextWithFlags = changetype<usize>(to);
to.prev = to;
set2.clear();
state = State.IDLE;
}
break;
@ -188,66 +202,42 @@ function gc_step(): void {
}
}
/** Allocates a managed object. */
/** Garbage collector interface. */
@global
export function gc_allocate(
size: usize,
visitFn: (obj: usize) => void
): usize {
assert(size <= MAX_SIZE_32 - ManagedObject.SIZE);
var obj = changetype<ManagedObject>(allocate_memory(ManagedObject.SIZE + size));
obj.makeWhite();
obj.visitFn = visitFn;
from.insert(obj);
return changetype<usize>(obj) + ManagedObject.SIZE;
}
export namespace gc {
/** Visits a reachable object. Called from the visitFn functions. */
@global
export function gc_visit(obj: ManagedObject): void {
if (state == State.SWEEP) return;
if (obj.isWhite) obj.makeGray();
}
/** Registers a managed child object with its parent object. */
@global
export function gc_register(parent: ManagedObject, child: ManagedObject): void {
if (parent.isBlack && child.isWhite) parent.makeGray();
}
/** Iterates the root set. Provided by the compiler according to the program. */
@global
export declare function gc_roots(): void;
/** Performs a full garbage collection cycle. */
@global
export function gc_collect(): void {
// begin collecting if not yet collecting
switch (state) {
case State.INIT:
case State.IDLE: gc_step();
/** Allocates a managed object. */
export function alloc(
size: usize,
visitFn: (ref: usize) => void
): usize {
assert(size <= MAX_SIZE_32 - ManagedObject.SIZE);
var obj = changetype<ManagedObject>(memory.allocate(ManagedObject.SIZE + size));
obj.makeWhite();
obj.visitFn = visitFn;
set1.insert(obj);
return changetype<usize>(obj) + ManagedObject.SIZE;
}
/** Visits a reachable object. Called from the visitFn functions. */
export function visit(obj: ManagedObject): void {
if (state == State.SWEEP) return;
if (obj.isWhite) obj.makeGray();
}
/** References a managed child object from its parent object. */
export function ref(parent: ManagedObject, child: ManagedObject): void {
if (parent.isBlack && child.isWhite) parent.makeGray();
}
/** Performs a full garbage collection cycle. */
export function collect(): void {
// begin collecting if not yet collecting
switch (state) {
case State.INIT:
case State.IDLE: gc_step();
}
// finish the cycle
while (state != State.IDLE) gc_step();
}
// finish the cycle
while (state != State.IDLE) gc_step();
}
declare function allocate_memory(size: usize): usize;
declare function free_memory(ptr: usize): void;
// Considerations
//
// - An API that consists mostly of just replacing `allocate_memory` would be ideal, possibly taking
// any additional number of parameters that are necessary, like the parent and the visitor.
//
// - Not having to generate a helper function for iterating globals but instead marking specific
// nodes as roots could simplify the embedding, but whether this is feasible or not depends on its
// performance characteristics and the possibility of tracking root status accross assignments.
// For example, root status could be implemented as some sort of referenced-by-globals counting
// and a dedicated list of root objects.
//
// - In 32-bit specifically, there is some free space in TLSF object headers due to alignment that
// could be repurposed to store some GC information, like a class id. Certainly, this somewhat
// depends on the efficiency of the used mechanism to detect this at compile time, including when
// a different allocator is used.
//
// - Think about generations.

View File

@ -34,6 +34,8 @@ declare type f32 = number;
/** A 64-bit float. */
declare type f64 = number;
// Compiler hints
/** Compiler target. 0 = JS, 1 = WASM32, 2 = WASM64. */
declare const ASC_TARGET: i32;
/** Provided noTreeshaking option. */
@ -51,6 +53,100 @@ declare const ASC_FEATURE_MUTABLE_GLOBAL: bool;
/** Whether the sign extension feature is enabled. */
declare const ASC_FEATURE_SIGN_EXTENSION: bool;
// Builtins
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */
declare function clz<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic count tailing zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered trailing if the value is zero. */
declare function ctz<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic count number of one bits operation on a 32-bit or 64-bit integer. */
declare function popcnt<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic rotate left operation on a 32-bit or 64-bit integer. */
declare function rotl<T = i32 | i64>(value: T, shift: T): T;
/** Performs the sign-agnostic rotate right operation on a 32-bit or 64-bit integer. */
declare function rotr<T = i32 | i64>(value: T, shift: T): T;
/** Computes the absolute value of an integer or float. */
declare function abs<T = i32 | i64 | f32 | f64>(value: T): T;
/** Determines the maximum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function max<T = i32 | i64 | f32 | f64>(left: T, right: T): T;
/** Determines the minimum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function min<T = i32 | i64 | f32 | f64>(left: T, right: T): T;
/** Performs the ceiling operation on a 32-bit or 64-bit float. */
declare function ceil<T = f32 | f64>(value: T): T;
/** Composes a 32-bit or 64-bit float from the magnitude of `x` and the sign of `y`. */
declare function copysign<T = f32 | f64>(x: T, y: T): T;
/** Performs the floor operation on a 32-bit or 64-bit float. */
declare function floor<T = f32 | f64>(value: T): T;
/** Rounds to the nearest integer tied to even of a 32-bit or 64-bit float. */
declare function nearest<T = f32 | f64>(value: T): T;
/** Reinterprets the bits of the specified value as type `T`. Valid reinterpretations are u32/i32 to/from f32 and u64/i64 to/from f64. */
declare function reinterpret<T = i32 | i64 | f32 | f64>(value: number): T;
/** Selects one of two pre-evaluated values depending on the condition. */
declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;
/** Calculates the square root of a 32-bit or 64-bit float. */
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;
/** Loads a value of the specified type from memory. Equivalent to dereferncing a pointer in other languages. */
declare function load<T>(ptr: usize, constantOffset?: usize): T;
/** Stores a value of the specified type to memory. Equivalent to dereferencing a pointer in other languages when assigning a value. */
declare function store<T>(ptr: usize, value: any, constantOffset?: 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
/** NaN (not a number) as a 32-bit or 64-bit float depending on context. */
declare const NaN: f32 | f64;
/** Positive infinity as a 32-bit or 64-bit float depending on context. */
declare const Infinity: f32 | f64;
/** Heap base offset. */
declare const HEAP_BASE: usize;
/** Determines the byte size of the specified underlying core type. Compiles to a constant. */
declare function sizeof<T>(): usize;
/** Determines the alignment (log2) of the specified underlying core type. Compiles to a constant. */
declare function alignof<T>(): usize;
/** Determines the offset of the specified field within the given class type. Returns the class type's end offset if field name has been omitted. Compiles to a constant. */
declare function offsetof<T>(fieldName?: string): usize;
/** Changes the type of any value of `usize` kind to another one of `usize` kind. Useful for casting class instances to their pointer values and vice-versa. Beware that this is unsafe.*/
declare function changetype<T>(value: any): T;
/** Explicitly requests no bounds checks on the provided expression. Useful for array accesses. */
declare function unchecked<T>(value: T): T;
/** Emits a `call_indirect` instruction, calling the specified function in the function table by index with the specified arguments. Does result in a runtime error if the arguments do not match the called function. */
declare function call_indirect<T>(target: Function | u32, ...args: any[]): T;
/** Tests if a 32-bit or 64-bit float is `NaN`. */
declare function isNaN<T = f32 | f64>(value: T): bool;
/** Tests if a 32-bit or 64-bit float is finite, that is not `NaN` or +/-`Infinity`. */
declare function isFinite<T = f32 | f64>(value: T): bool;
/** Tests if the specified type *or* expression is of an integer type and not a reference. Compiles to a constant. */
declare function isInteger<T>(value?: any): value is number;
/** Tests if the specified type *or* expression is of a float type. Compiles to a constant. */
declare function isFloat<T>(value?: any): value is number;
/** Tests if the specified type *or* expression can represent negative numbers. Compiles to a constant. */
declare function isSigned<T>(value?: any): value is number;
/** Tests if the specified type *or* expression is of a reference type. Compiles to a constant. */
declare function isReference<T>(value?: any): value is object | string;
/** Tests if the specified type *or* expression can be used as a string. Compiles to a constant. */
declare function isString<T>(value?: any): value is string | String;
/** Tests if the specified type *or* expression can be used as an array. Compiles to a constant. */
declare function isArray<T>(value?: any): value is Array<any>;
/** Tests if the specified expression resolves to a defined element. Compiles to a constant. */
declare function isDefined(expression: any): bool;
/** Tests if the specified expression evaluates to a constant value. Compiles to a constant. */
declare function isConstant(expression: any): bool;
/** Traps if the specified value is not true-ish, otherwise returns the (non-nullable) value. */
declare function assert<T>(isTrueish: T, message?: string): T & object; // any better way to model `: T != null`?
/** Parses an integer string to a 64-bit float. */
declare function parseInt(str: string, radix?: i32): f64;
/** Parses an integer string to a 32-bit integer. */
declare function parseI32(str: string, radix?: i32): i32;
/** Parses an integer string to a 64-bit integer. */
declare function parseI64(str: string, radix?: i32): i64;
/** Parses a string to a 64-bit float. */
declare function parseFloat(str: string): f64;
/** Returns the 64-bit floating-point remainder of `x/y`. */
declare function fmod(x: f64, y: f64): f64;
/** Returns the 32-bit floating-point remainder of `x/y`. */
declare function fmodf(x: f32, y: f32): f32;
/** Converts any other numeric value to an 8-bit signed integer. */
declare function i8(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): i8;
declare namespace i8 {
@ -210,121 +306,58 @@ declare namespace f64 {
export function store(offset: usize, value: f64, constantOffset?: usize): void;
}
// Built-ins
// User-defined diagnostic macros
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */
declare function clz<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic count tailing zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered trailing if the value is zero. */
declare function ctz<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic count number of one bits operation on a 32-bit or 64-bit integer. */
declare function popcnt<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic rotate left operation on a 32-bit or 64-bit integer. */
declare function rotl<T = i32 | i64>(value: T, shift: T): T;
/** Performs the sign-agnostic rotate right operation on a 32-bit or 64-bit integer. */
declare function rotr<T = i32 | i64>(value: T, shift: T): T;
/** Computes the absolute value of an integer or float. */
declare function abs<T = i32 | i64 | f32 | f64>(value: T): T;
/** Determines the maximum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function max<T = i32 | i64 | f32 | f64>(left: T, right: T): T;
/** Determines the minimum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function min<T = i32 | i64 | f32 | f64>(left: T, right: T): T;
/** Performs the ceiling operation on a 32-bit or 64-bit float. */
declare function ceil<T = f32 | f64>(value: T): T;
/** Composes a 32-bit or 64-bit float from the magnitude of `x` and the sign of `y`. */
declare function copysign<T = f32 | f64>(x: T, y: T): T;
/** Performs the floor operation on a 32-bit or 64-bit float. */
declare function floor<T = f32 | f64>(value: T): T;
/** Rounds to the nearest integer tied to even of a 32-bit or 64-bit float. */
declare function nearest<T = f32 | f64>(value: T): T;
/** Reinterprets the bits of the specified value as type `T`. Valid reinterpretations are u32/i32 to/from f32 and u64/i64 to/from f64. */
declare function reinterpret<T = i32 | i64 | f32 | f64>(value: number): T;
/** Selects one of two pre-evaluated values depending on the condition. */
declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;
/** Calculates the square root of a 32-bit or 64-bit float. */
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;
/** Loads a value of the specified type from memory. Equivalent to dereferncing a pointer in other languages. */
declare function load<T>(ptr: usize, constantOffset?: usize): T;
/** Stores a value of the specified type to memory. Equivalent to dereferencing a pointer in other languages when assigning a value. */
declare function store<T>(ptr: usize, value: any, constantOffset?: usize): void;
/** Returns the current memory size in units of pages. One page is 64kb. */
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
/** Emits a user-defined diagnostic error when encountered. */
declare function ERROR(message?: any): void;
/** Emits a user-defined diagnostic warning when encountered. */
declare function WARNING(message?: any): void;
/** Emits a user-defined diagnostic info when encountered. */
declare function INFO(message?: any): void;
/** [Polyfill] Performs the sign-agnostic reverse bytes **/
// Polyfills
/** Performs the sign-agnostic reverse bytes **/
declare function bswap<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64 | isize | usize>(value: T): T;
/** [Polyfill] Performs the sign-agnostic reverse bytes only for last 16-bit **/
/** Performs the sign-agnostic reverse bytes only for last 16-bit **/
declare function bswap16<T = i8 | u8 | i16 | u16 | i32 | u32>(value: T): T;
/** NaN (not a number) as a 32-bit or 64-bit float depending on context. */
declare const NaN: f32 | f64;
/** Positive infinity as a 32-bit or 64-bit float depending on context. */
declare const Infinity: f32 | f64;
/** Heap base offset. */
declare const HEAP_BASE: usize;
/** Determines the byte size of the specified underlying core type. Compiles to a constant. */
declare function sizeof<T>(): usize;
/** Determines the alignment (log2) of the specified underlying core type. Compiles to a constant. */
declare function alignof<T>(): usize;
/** Determines the offset of the specified field within the given class type. Returns the class type's end offset if field name has been omitted. Compiles to a constant. */
declare function offsetof<T>(fieldName?: string): usize;
/** Changes the type of any value of `usize` kind to another one of `usize` kind. Useful for casting class instances to their pointer values and vice-versa. Beware that this is unsafe.*/
declare function changetype<T>(value: any): T;
/** Explicitly requests no bounds checks on the provided expression. Useful for array accesses. */
declare function unchecked<T>(value: T): T;
/** Emits a `call_indirect` instruction, calling the specified function in the function table by index with the specified arguments. Does result in a runtime error if the arguments do not match the called function. */
declare function call_indirect<T>(target: Function | u32, ...args: any[]): T;
/** Tests if a 32-bit or 64-bit float is `NaN`. */
declare function isNaN<T = f32 | f64>(value: T): bool;
/** Tests if a 32-bit or 64-bit float is finite, that is not `NaN` or +/-`Infinity`. */
declare function isFinite<T = f32 | f64>(value: T): bool;
/** Tests if the specified type *or* expression is of an integer type and not a reference. Compiles to a constant. */
declare function isInteger<T>(value?: any): value is number;
/** Tests if the specified type *or* expression is of a float type. Compiles to a constant. */
declare function isFloat<T>(value?: any): value is number;
/** Tests if the specified type *or* expression can represent negative numbers. Compiles to a constant. */
declare function isSigned<T>(value?: any): value is number;
/** Tests if the specified type *or* expression is of a reference type. Compiles to a constant. */
declare function isReference<T>(value?: any): value is object | string;
/** Tests if the specified type *or* expression can be used as a string. Compiles to a constant. */
declare function isString<T>(value?: any): value is string | String;
/** Tests if the specified type *or* expression can be used as an array. Compiles to a constant. */
declare function isArray<T>(value?: any): value is Array<any>;
/** Tests if the specified expression resolves to a defined element. Compiles to a constant. */
declare function isDefined(expression: any): bool;
/** Tests if the specified expression evaluates to a constant value. Compiles to a constant. */
declare function isConstant(expression: any): bool;
/** Traps if the specified value is not true-ish, otherwise returns the (non-nullable) value. */
declare function assert<T>(isTrueish: T, message?: string): T & object; // any better way to model `: T != null`?
/** Parses an integer string to a 64-bit float. */
declare function parseInt(str: string, radix?: i32): f64;
/** Parses an integer string to a 32-bit integer. */
declare function parseI32(str: string, radix?: i32): i32;
/** Parses an integer string to a 64-bit integer. */
declare function parseI64(str: string, radix?: i32): i64;
/** Parses a string to a 64-bit float. */
declare function parseFloat(str: string): f64;
/** Returns the 64-bit floating-point remainder of `x/y`. */
declare function fmod(x: f64, y: f64): f64;
/** Returns the 32-bit floating-point remainder of `x/y`. */
declare function fmodf(x: f32, y: f32): f32;
// Standard library
/** Memory operations. */
declare namespace memory {
/** Returns the current memory size in units of pages. One page is 64kb. */
export function size(): 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. */
export function grow(value: i32): i32;
/** Sets n bytes beginning at the specified destination in memory to the specified byte value. */
export function fill(dst: usize, value: u8, count: usize): void;
/** Copies n bytes from the specified source to the specified destination in memory. These regions may overlap. */
export function copy(dst: usize, src: usize, n: usize): void;
/** Copies elements from a passive element segment to a table. */
// export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void;
/** Prevents further use of a passive element segment. */
// export function drop(segmentIndex: u32): void;
/** Copies elements from one region of a table to another region. */
export function allocate(size: usize): usize;
/** Disposes a chunk of memory by its pointer. */
export function free(ptr: usize): void;
/** Compares two chunks of memory. Returns `0` if equal, otherwise the difference of the first differing bytes. */
export function compare(vl: usize, vr: usize, n: usize): i32;
/** Resets the allocator to its initial state, if supported. */
export function reset(): void;
}
/** Table operations. */
declare namespace table {
/** Copies elements from a passive element segment to a table. */
// export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void;
/** Prevents further use of a passive element segment. */
// export function drop(elementIndex: u32): void;
/** Copies elements from one region of a table to another region. */
// export function copy(dest: u32, src: u32, n: u32): void;
}
/** Class representing a generic, fixed-length raw binary data buffer. */
declare class ArrayBuffer {
/** The size, in bytes, of the array. */
@ -607,7 +640,7 @@ declare const Math: IMath<f64>;
/** Alias of {@link NativeMathf} or {@link JSMath} respectively. Defaults to `NativeMathf`. */
declare const Mathf: IMath<f32>;
// Internal decorators
// Decorators
/** Annotates an element as a program global. */
declare function global(target: Function, propertyKey: string, descriptor: any): void;

View File

@ -36,8 +36,8 @@ export function weakHeapSort<T>(arr: Array<T>, comparator: (a: T, b: T) => i32):
var length = arr.length;
var bitsetSize = (length + 31) >> 5 << shift32;
var bitset = allocate_memory(bitsetSize); // indexed in 32-bit chunks below
set_memory(bitset, 0, bitsetSize);
var bitset = memory.allocate(bitsetSize); // indexed in 32-bit chunks below
memory.fill(bitset, 0, bitsetSize);
// see: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.21.1863&rep=rep1&type=pdf
@ -83,7 +83,7 @@ export function weakHeapSort<T>(arr: Array<T>, comparator: (a: T, b: T) => i32):
}
}
free_memory(bitset);
memory.free(bitset);
var t = loadUnsafe<T,T>(buffer, 1); // t = arr[1]
storeUnsafe<T,T>(buffer, 1, loadUnsafe<T,T>(buffer, 0)); // arr[1] = arr[0]

View File

@ -20,7 +20,7 @@ export function computeSize(byteLength: i32): usize {
/** Allocates a raw ArrayBuffer. Contents remain uninitialized. */
export function allocUnsafe(byteLength: i32): ArrayBuffer {
assert(<u32>byteLength <= <u32>MAX_BLENGTH);
var buffer = allocate_memory(computeSize(byteLength));
var buffer = memory.allocate(computeSize(byteLength));
store<i32>(buffer, byteLength, offsetof<ArrayBuffer>("byteLength"));
return changetype<ArrayBuffer>(buffer);
}
@ -32,19 +32,19 @@ export function reallocUnsafe(buffer: ArrayBuffer, newByteLength: i32): ArrayBuf
assert(newByteLength <= MAX_BLENGTH);
if (newByteLength <= <i32>(computeSize(oldByteLength) - HEADER_SIZE)) { // fast path: zero out additional space
store<i32>(changetype<usize>(buffer), newByteLength, offsetof<ArrayBuffer>("byteLength"));
set_memory(
memory.fill(
changetype<usize>(buffer) + HEADER_SIZE + <usize>oldByteLength,
0,
<usize>(newByteLength - oldByteLength)
);
} else { // slow path: copy to new buffer
let newBuffer = allocUnsafe(newByteLength);
move_memory(
memory.copy(
changetype<usize>(newBuffer) + HEADER_SIZE,
changetype<usize>(buffer) + HEADER_SIZE,
<usize>oldByteLength
);
set_memory(
memory.fill(
changetype<usize>(newBuffer) + HEADER_SIZE + <usize>oldByteLength,
0,
<usize>(newByteLength - oldByteLength)

View File

@ -23,7 +23,7 @@ export function clamp<T>(val: T, lo: T, hi: T): T {
/** Allocates a raw String with uninitialized contents. */
export function allocate(length: i32): String {
assert(length > 0 && length <= MAX_LENGTH);
var buffer = allocate_memory(HEADER_SIZE + (<usize>length << 1));
var buffer = memory.allocate(HEADER_SIZE + (<usize>length << 1));
store<i32>(buffer, length);
return changetype<String>(buffer);
}

View File

@ -18,7 +18,7 @@ export abstract class TypedArray<T,V> {
if (<u32>length > MAX_LENGTH) throw new RangeError("Invalid typed array length");
var byteLength = length << alignof<T>();
var buffer = allocUnsafe(byteLength);
set_memory(changetype<usize>(buffer) + HEADER_SIZE_AB, 0, <usize>byteLength);
memory.fill(changetype<usize>(buffer) + HEADER_SIZE_AB, 0, <usize>byteLength);
this.buffer = buffer;
this.byteOffset = 0;
this.byteLength = byteLength;
@ -64,7 +64,7 @@ export abstract class TypedArray<T,V> {
else begin = min(begin, length);
if (end < 0) end = max(length + end, begin);
else end = max(min(end, length), begin);
var slice = allocate_memory(offsetof<this>());
var slice = memory.allocate(offsetof<this>());
store<usize>(slice, this.buffer, offsetof<this>("buffer"));
store<i32>(slice, begin << alignof<T>(), offsetof<this>("byteOffset"));
store<i32>(slice, end << alignof<T>(), offsetof<this>("byteLength"));

View File

@ -1,7 +1,60 @@
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
export namespace memory {
export function size(): i32 {
return __memory_size(); // tslint:disable-line
}
export function grow(pages: i32): i32 {
return __memory_grow(pages); // tslint:disable-line
}
export function fill(dest: usize, c: u8, n: usize): void { // see: musl/src/string/memset
if (isDefined(__memory_fill)) { __memory_fill(dest, c, n); return; } // tslint:disable-line
memset(dest, c, n);
}
export function copy(dest: usize, src: usize, n: usize): void { // see: musl/src/string/memmove.c
if (isDefined(__memory_copy)) { __memory_copy(dest, src, n); return; } // tslint:disable-line
memmove(dest, src, n);
}
export function compare(vl: usize, vr: usize, n: usize): i32 { // see: musl/src/string/memcmp.c
if (isDefined(__memory_compare)) return __memory_compare(vl, vr, n); // tslint:disable-line
return memcmp(vl, vr, n);
}
// Passive segments
// export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void {
// __memory_init(segmentIndex, srcOffset, dstOffset);
// }
// export function drop(segmentIndex: u32): void {
// __memory_drop(segmentIndex);
// }
// Allocator
export function allocate(size: usize): usize {
if (isDefined(__memory_allocate)) return __memory_allocate(size); // tslint:disable-line
WARNING("Calling 'memory.allocate' requires a memory manager to be present.");
return <usize>unreachable();
}
export function free(ptr: usize): void {
if (isDefined(__memory_free)) { __memory_free(ptr); return; } // tslint:disable-line
WARNING("Calling 'memory.free' requires a memory manager to be present.");
unreachable();
}
export function reset(): void {
if (isDefined(__memory_reset)) { __memory_reset(); return; } // tslint:disable-line
unreachable();
}
}
// this function will go away once `memory.copy` becomes an intrinsic
function memcpy(dest: usize, src: usize, n: usize): void { // see: musl/src/string/memcpy.c
var w: u32, x: u32;
// copy 1 byte each until src is aligned to 4 bytes
@ -144,13 +197,11 @@ function copy_memory(dest: usize, src: usize, n: usize): void {
}
}
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
// this function will go away once `memory.copy` becomes an intrinsic
function memmove(dest: usize, src: usize, n: usize): void { // see: musl/src/string/memmove.c
if (dest == src) return;
if (src + n <= dest || dest + n <= src) {
copy_memory(dest, src, n);
memcpy(dest, src, n);
return;
}
if (dest < src) {
@ -188,9 +239,8 @@ export function move_memory(dest: usize, src: usize, n: usize): void {
}
}
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
// this function will go away once `memory.fill` becomes an intrinsic
function memset(dest: usize, c: u8, n: usize): void { // see: musl/src/string/memset
// fill head and tail with minimal branching
if (!n) return;
@ -250,14 +300,10 @@ export function set_memory(dest: usize, c: u8, n: usize): void {
}
}
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
function memcmp(vl: usize, vr: usize, n: usize): i32 { // see: musl/src/string/memcmp.c
if (vl == vr) return 0;
while (n && load<u8>(vl) == load<u8>(vr)) {
n--;
vl++;
vr++;
while (n != 0 && load<u8>(vl) == load<u8>(vr)) {
n--; vl++; vr++;
}
return n ? <i32>load<u8>(vl) - <i32>load<u8>(vr) : 0;
}

View File

@ -90,13 +90,13 @@ export class String {
if (outLen == 0) return EMPTY;
var out = allocate(outLen);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE,
thisLen << 1
);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE + (thisLen << 1),
changetype<usize>(other) + HEADER_SIZE,
otherLen << 1
@ -112,7 +112,7 @@ export class String {
var searchLength: isize = searchString.length;
var start: isize = end - searchLength;
if (start < 0) return false;
return !compare_memory(
return !memory.compare(
changetype<usize>(this) + HEADER_SIZE + (start << 1),
changetype<usize>(searchString) + HEADER_SIZE,
searchLength << 1
@ -127,7 +127,7 @@ export class String {
var leftLength = left.length;
if (leftLength != right.length) return false;
return !compare_memory(
return !memory.compare(
changetype<usize>(left) + HEADER_SIZE,
changetype<usize>(right) + HEADER_SIZE,
(<usize>leftLength << 1)
@ -150,7 +150,7 @@ export class String {
if (!rightLength) return true;
var length = <usize>min<i32>(leftLength, rightLength);
return compare_memory(
return memory.compare(
changetype<usize>(left) + HEADER_SIZE,
changetype<usize>(right) + HEADER_SIZE,
length << 1
@ -169,7 +169,7 @@ export class String {
if (!rightLength) return true;
var length = <usize>min<i32>(leftLength, rightLength);
return compare_memory(
return memory.compare(
changetype<usize>(left) + HEADER_SIZE,
changetype<usize>(right) + HEADER_SIZE,
length << 1
@ -187,7 +187,7 @@ export class String {
if (!leftLength) return true;
var length = <usize>min<i32>(leftLength, rightLength);
return compare_memory(
return memory.compare(
changetype<usize>(left) + HEADER_SIZE,
changetype<usize>(right) + HEADER_SIZE,
length << 1
@ -206,7 +206,7 @@ export class String {
if (!leftLength) return true;
var length = <usize>min<i32>(leftLength, rightLength);
return compare_memory(
return memory.compare(
changetype<usize>(left) + HEADER_SIZE,
changetype<usize>(right) + HEADER_SIZE,
length << 1
@ -228,7 +228,7 @@ export class String {
len -= searchLen;
// TODO: multiple char codes
for (let k: isize = start; k <= len; ++k) {
if (!compare_memory(
if (!memory.compare(
changetype<usize>(this) + HEADER_SIZE + (k << 1),
changetype<usize>(searchString) + HEADER_SIZE,
searchLen << 1
@ -250,7 +250,7 @@ export class String {
// TODO: multiple char codes
for (let k = start; k >= 0; --k) {
if (!compare_memory(
if (!memory.compare(
changetype<usize>(this) + HEADER_SIZE + (k << 1),
changetype<usize>(searchString) + HEADER_SIZE,
searchLen << 1
@ -272,7 +272,7 @@ export class String {
if (searchLength + start > len) {
return false;
}
return !compare_memory(
return !memory.compare(
changetype<usize>(this) + HEADER_SIZE + (start << 1),
changetype<usize>(searchString) + HEADER_SIZE,
searchLength << 1
@ -292,7 +292,7 @@ export class String {
return EMPTY;
}
var out = allocate(resultLength);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE + (intStart << 1),
<usize>resultLength << 1
@ -315,7 +315,7 @@ export class String {
return this;
}
var out = allocate(len);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE + (from << 1),
len << 1
@ -351,7 +351,7 @@ export class String {
return this;
}
var out = allocate(length);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE + (start << 1),
length << 1
@ -379,7 +379,7 @@ export class String {
return EMPTY;
}
var out = allocate(outLen);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE + (start << 1),
outLen << 1
@ -405,7 +405,7 @@ export class String {
return this;
}
var out = allocate(len);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE,
len << 1
@ -433,7 +433,7 @@ export class String {
* 'a' + 'a' => 'aa' + 'aa' => 'aaaa' + 'aaaa' etc
*/
for (let offset = 0, len = strLen * count; offset < len; offset += strLen) {
move_memory(
memory.copy(
changetype<usize>(result) + HEADER_SIZE + offset,
changetype<usize>(this) + HEADER_SIZE,
strLen
@ -472,7 +472,7 @@ export class String {
}
toUTF8(): usize {
var buf = allocate_memory(<usize>this.lengthUTF8);
var buf = memory.allocate(<usize>this.lengthUTF8);
var pos: usize = 0;
var end = <usize>this.length;
var off: usize = 0;

16
std/assembly/table.ts Normal file
View File

@ -0,0 +1,16 @@
export namespace table {
// export function copy(dst: u32, src: u32, n: u32): void {
// __table_copy(dst, src, n);
// }
// Passive elements
// export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void {
// __table_init(elementIndex, srcOffset, dstOffset, n);
// }
// export function drop(elementIndex: u32): void {
// __table_drop(elementIndex);
// }
}