mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-12 06:21:29 +00:00
Rename memory instructions; Rework constant handling
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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>()
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
251
std/assembly/index.d.ts
vendored
251
std/assembly/index.d.ts
vendored
@ -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;
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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"));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
16
std/assembly/table.ts
Normal 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);
|
||||
// }
|
||||
}
|
Reference in New Issue
Block a user