take a step back

This commit is contained in:
dcode
2019-03-14 04:33:58 +01:00
parent e38f627c8b
commit 6163a73ab5
30 changed files with 645 additions and 1287 deletions

View File

@ -43,10 +43,9 @@
"scripts": { "scripts": {
"build": "webpack --mode production --display-modules", "build": "webpack --mode production --display-modules",
"clean": "node scripts/clean", "clean": "node scripts/clean",
"check": "npm run check:config && npm run check:compiler && npm run check:library", "check": "npm run check:config && npm run check:compiler",
"check:config": "tsc --noEmit -p src --diagnostics --listFiles", "check:config": "tsc --noEmit -p src --diagnostics --listFiles",
"check:compiler": "tslint -c tslint.json --project src --formatters-dir lib/lint/formatters --format as", "check:compiler": "tslint -c tslint.json --project src --formatters-dir lib/lint/formatters --format as",
"check:library": "tslint -c tslint.json --project std/assembly --formatters-dir lib/lint/formatters --format as",
"test": "npm run test:parser && npm run test:compiler", "test": "npm run test:parser && npm run test:compiler",
"test:parser": "node tests/parser", "test:parser": "node tests/parser",
"test:compiler": "node tests/compiler", "test:compiler": "node tests/compiler",

View File

@ -1163,8 +1163,7 @@ export enum DecoratorKind {
BUILTIN, BUILTIN,
LAZY, LAZY,
START, START,
UNSAFE, UNSAFE
STUB
} }
/** Returns the kind of the specified decorator. Defaults to {@link DecoratorKind.CUSTOM}. */ /** Returns the kind of the specified decorator. Defaults to {@link DecoratorKind.CUSTOM}. */
@ -1201,7 +1200,6 @@ export function decoratorNameToKind(name: Expression): DecoratorKind {
case CharCode.s: { case CharCode.s: {
if (nameStr == "sealed") return DecoratorKind.SEALED; if (nameStr == "sealed") return DecoratorKind.SEALED;
if (nameStr == "start") return DecoratorKind.START; if (nameStr == "start") return DecoratorKind.START;
if (nameStr == "stub") return DecoratorKind.STUB;
break; break;
} }
case CharCode.u: { case CharCode.u: {

View File

@ -21,8 +21,7 @@ import {
LiteralKind, LiteralKind,
LiteralExpression, LiteralExpression,
StringLiteralExpression, StringLiteralExpression,
CallExpression, CallExpression
ElementAccessExpression
} from "./ast"; } from "./ast";
import { import {
@ -100,7 +99,6 @@ export namespace BuiltinSymbols {
export const isFunction = "~lib/builtins/isFunction"; export const isFunction = "~lib/builtins/isFunction";
export const isNullable = "~lib/builtins/isNullable"; export const isNullable = "~lib/builtins/isNullable";
export const isDefined = "~lib/builtins/isDefined"; export const isDefined = "~lib/builtins/isDefined";
export const isImplemented = "~lib/builtins/isImplemented";
export const isConstant = "~lib/builtins/isConstant"; export const isConstant = "~lib/builtins/isConstant";
export const isManaged = "~lib/builtins/isManaged"; export const isManaged = "~lib/builtins/isManaged";
@ -472,14 +470,16 @@ export namespace BuiltinSymbols {
export const WARNING = "~lib/diagnostics/WARNING"; export const WARNING = "~lib/diagnostics/WARNING";
export const INFO = "~lib/diagnostics/INFO"; export const INFO = "~lib/diagnostics/INFO";
// std/runtime.ts // std/memory.ts
export const HEAP_BASE = "~lib/runtime/HEAP_BASE"; export const HEAP_BASE = "~lib/memory/HEAP_BASE";
export const memory_size = "~lib/runtime/memory.size"; export const memory_size = "~lib/memory/memory.size";
export const memory_grow = "~lib/runtime/memory.grow"; export const memory_grow = "~lib/memory/memory.grow";
export const memory_copy = "~lib/runtime/memory.copy"; export const memory_copy = "~lib/memory/memory.copy";
export const memory_fill = "~lib/runtime/memory.fill"; export const memory_fill = "~lib/memory/memory.fill";
export const gc_classId = "~lib/runtime/gc.classId";
export const gc_iterateRoots = "~lib/runtime/gc.iterateRoots"; // std/gc.ts
export const gc_classId = "~lib/gc/gc.classId";
export const gc_iterateRoots = "~lib/gc/gc.iterateRoots";
// std/typedarray.ts // std/typedarray.ts
export const Int8Array = "~lib/typedarray/Int8Array"; export const Int8Array = "~lib/typedarray/Int8Array";
@ -621,20 +621,6 @@ export function compileCall(
); );
return module.createI32(element ? 1 : 0); return module.createI32(element ? 1 : 0);
} }
case BuiltinSymbols.isImplemented: { // isImplemented(expression) -> bool
compiler.currentType = Type.bool;
if (
checkTypeAbsent(typeArguments, reportNode, prototype) |
checkArgsRequired(operands, 1, reportNode, compiler)
) return module.createUnreachable();
let element = compiler.resolver.resolveExpression(
operands[0],
compiler.currentFlow,
Type.void,
ReportMode.SWALLOW
);
return module.createI32(element && !element.hasDecorator(DecoratorFlags.STUB) ? 1 : 0);
}
case BuiltinSymbols.isConstant: { // isConstant(expression) -> bool case BuiltinSymbols.isConstant: { // isConstant(expression) -> bool
compiler.currentType = Type.bool; compiler.currentType = Type.bool;
if ( if (

View File

@ -930,7 +930,7 @@ export class Program extends DiagnosticEmitter {
ensureGlobal(name: string, element: DeclaredElement): DeclaredElement { ensureGlobal(name: string, element: DeclaredElement): DeclaredElement {
var elementsByName = this.elementsByName; var elementsByName = this.elementsByName;
if (elementsByName.has(name)) { if (elementsByName.has(name)) {
let actual = elementsByName.get(name); let actual = elementsByName.get(name)!;
// NOTE: this is effectively only performed when merging native types with // NOTE: this is effectively only performed when merging native types with
// their respective namespaces in std/builtins, but can also trigger when a // their respective namespaces in std/builtins, but can also trigger when a
// user has multiple global elements of the same name in different files, // user has multiple global elements of the same name in different files,
@ -1176,7 +1176,7 @@ export class Program extends DiagnosticEmitter {
): void { ): void {
var name = declaration.name.text; var name = declaration.name.text;
var isStatic = declaration.is(CommonFlags.STATIC); var isStatic = declaration.is(CommonFlags.STATIC);
var acceptedFlags = DecoratorFlags.INLINE; var acceptedFlags = DecoratorFlags.INLINE | DecoratorFlags.UNSAFE;
if (!declaration.is(CommonFlags.GENERIC)) { if (!declaration.is(CommonFlags.GENERIC)) {
acceptedFlags |= DecoratorFlags.OPERATOR_BINARY acceptedFlags |= DecoratorFlags.OPERATOR_BINARY
| DecoratorFlags.OPERATOR_PREFIX | DecoratorFlags.OPERATOR_PREFIX
@ -1549,7 +1549,7 @@ export class Program extends DiagnosticEmitter {
parent: Element parent: Element
): void { ): void {
var name = declaration.name.text; var name = declaration.name.text;
var validDecorators = DecoratorFlags.UNSAFE | DecoratorFlags.STUB; var validDecorators = DecoratorFlags.UNSAFE;
if (declaration.is(CommonFlags.AMBIENT)) { if (declaration.is(CommonFlags.AMBIENT)) {
validDecorators |= DecoratorFlags.EXTERNAL; validDecorators |= DecoratorFlags.EXTERNAL;
} else { } else {
@ -1791,9 +1791,7 @@ export enum DecoratorFlags {
/** Is the explicit start function. */ /** Is the explicit start function. */
START = 1 << 10, START = 1 << 10,
/** Is considered unsafe code. */ /** Is considered unsafe code. */
UNSAFE = 1 << 11, UNSAFE = 1 << 11
/** Is a stub that can be overridden. */
STUB = 1 << 12
} }
/** Translates a decorator kind to the respective decorator flag. */ /** Translates a decorator kind to the respective decorator flag. */
@ -1812,7 +1810,6 @@ export function decoratorKindToFlag(kind: DecoratorKind): DecoratorFlags {
case DecoratorKind.LAZY: return DecoratorFlags.LAZY; case DecoratorKind.LAZY: return DecoratorFlags.LAZY;
case DecoratorKind.START: return DecoratorFlags.START; case DecoratorKind.START: return DecoratorFlags.START;
case DecoratorKind.UNSAFE: return DecoratorFlags.UNSAFE; case DecoratorKind.UNSAFE: return DecoratorFlags.UNSAFE;
case DecoratorKind.STUB: return DecoratorFlags.STUB;
default: return DecoratorFlags.NONE; default: return DecoratorFlags.NONE;
} }
} }
@ -1888,8 +1885,8 @@ export abstract class Element {
if (!members) this.members = members = new Map(); if (!members) this.members = members = new Map();
else if (members.has(name)) { else if (members.has(name)) {
let actual = members.get(name)!; let actual = members.get(name)!;
if (actual.parent !== this || actual.hasDecorator(DecoratorFlags.STUB)) { if (actual.parent !== this) {
// override non-own or stub element // override non-own element
} else { } else {
let merged = tryMerge(actual, element); let merged = tryMerge(actual, element);
if (merged) { if (merged) {

View File

@ -1,21 +1,10 @@
/** import { HEAP_BASE, memory } from "../memory";
* Arena Memory Allocator
*
* 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
*//***/
import { AL_MASK, MAX_SIZE_32 } from "../util/allocator"; import { AL_MASK, MAX_SIZE_32 } from "../util/allocator";
var startOffset: usize = (HEAP_BASE + AL_MASK) & ~AL_MASK; @lazy var startOffset: usize = (HEAP_BASE + AL_MASK) & ~AL_MASK;
var offset: usize = startOffset; @lazy var offset: usize = startOffset;
// Memory allocator implementation @unsafe @global function __memory_allocate(size: usize): usize {
@global namespace memory {
export function allocate(size: usize): usize {
if (size > MAX_SIZE_32) unreachable(); if (size > MAX_SIZE_32) unreachable();
var ptr = offset; var ptr = offset;
var newPtr = (ptr + max<usize>(size, 1) + AL_MASK) & ~AL_MASK; var newPtr = (ptr + max<usize>(size, 1) + AL_MASK) & ~AL_MASK;
@ -33,9 +22,9 @@ var offset: usize = startOffset;
return ptr; return ptr;
} }
export function free(ptr: usize): void { /* nop */ } @unsafe @global function __memory_free(ptr: usize): void {
}
export function reset(): void { @unsafe @global function __memory_reset(): void {
offset = startOffset; offset = startOffset;
} }
}

View File

@ -1,542 +0,0 @@
/**
* Buddy Memory Allocator.
* @module std/assembly/allocator/buddy
*//***/
/*
Copyright 2018 Evan Wallace
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/// see: https://github.com/evanw/buddy-malloc
/*
* This file implements a buddy memory allocator, which is an allocator that
* allocates memory within a fixed linear address range. It spans the address
* range with a binary tree that tracks free space. Both "malloc" and "free"
* are O(log N) time where N is the maximum possible number of allocations.
*
* The "buddy" term comes from how the tree is used. When memory is allocated,
* nodes in the tree are split recursively until a node of the appropriate size
* is reached. Every split results in two child nodes, each of which is the
* buddy of the other. When a node is freed, the node and its buddy can be
* merged again if the buddy is also free. This makes the memory available
* for larger allocations again.
*/
/*
* Every allocation needs an 8-byte header to store the allocation size while
* staying 8-byte aligned. The address returned by "malloc" is the address
* right after this header (i.e. the size occupies the 8 bytes before the
* returned address).
*/
const HEADER_SIZE: usize = 8;
/*
* The minimum allocation size is 16 bytes because we have an 8-byte header and
* we need to stay 8-byte aligned.
*/
const MIN_ALLOC_LOG2: usize = 4;
const MIN_ALLOC: usize = 1 << MIN_ALLOC_LOG2;
/*
* The maximum allocation size is currently set to 2gb. This is the total size
* of the heap. It's technically also the maximum allocation size because the
* heap could consist of a single allocation of this size. But of course real
* heaps will have multiple allocations, so the real maximum allocation limit
* is at most 1gb.
*/
const MAX_ALLOC_LOG2: usize = 30; // 31;
const MAX_ALLOC: usize = 1 << MAX_ALLOC_LOG2;
/*
* Allocations are done in powers of two starting from MIN_ALLOC and ending at
* MAX_ALLOC inclusive. Each allocation size has a bucket that stores the free
* list for that allocation size.
*
* Given a bucket index, the size of the allocations in that bucket can be
* found with "(size_t)1 << (MAX_ALLOC_LOG2 - bucket)".
*/
const BUCKET_COUNT: usize = MAX_ALLOC_LOG2 - MIN_ALLOC_LOG2 + 1;
/*
* Free lists are stored as circular doubly-linked lists. Every possible
* allocation size has an associated free list that is threaded through all
* currently free blocks of that size. That means MIN_ALLOC must be at least
* "sizeof(list_t)". MIN_ALLOC is currently 16 bytes, so this will be true for
* both 32-bit and 64-bit.
*/
@unmanaged
class List {
prev: List;
next: List;
static readonly SIZE: usize = 2 * sizeof<usize>();
}
/*
* Each bucket corresponds to a certain allocation size and stores a free list
* for that size. The bucket at index 0 corresponds to an allocation size of
* MAX_ALLOC (i.e. the whole address space).
*/
var BUCKETS_START: usize = HEAP_BASE;
var BUCKETS_END: usize = BUCKETS_START + BUCKET_COUNT * List.SIZE;
function buckets$get(index: usize): List {
assert(index < BUCKET_COUNT);
return changetype<List>(BUCKETS_START + index * List.SIZE);
}
/*
* We could initialize the allocator by giving it one free block the size of
* the entire address space. However, this would cause us to instantly reserve
* half of the entire address space on the first allocation, since the first
* split would store a free list entry at the start of the right child of the
* root. Instead, we have the tree start out small and grow the size of the
* tree as we use more memory. The size of the tree is tracked by this value.
*/
var bucket_limit: usize;
/*
* This array represents a linearized binary tree of bits. Every possible
* allocation larger than MIN_ALLOC has a node in this tree (and therefore a
* bit in this array).
*
* Given the index for a node, lineraized binary trees allow you to traverse to
* the parent node or the child nodes just by doing simple arithmetic on the
* index:
*
* - Move to parent: index = (index - 1) / 2;
* - Move to left child: index = index * 2 + 1;
* - Move to right child: index = index * 2 + 2;
* - Move to sibling: index = ((index - 1) ^ 1) + 1;
*
* Each node in this tree can be in one of several states:
*
* - UNUSED (both children are UNUSED)
* - SPLIT (one child is UNUSED and the other child isn't)
* - USED (neither children are UNUSED)
*
* These states take two bits to store. However, it turns out we have enough
* information to distinguish between UNUSED and USED from context, so we only
* need to store SPLIT or not, which only takes a single bit.
*
* Note that we don't need to store any nodes for allocations of size MIN_ALLOC
* since we only ever care about parent nodes.
*/
const SPLIT_COUNT: usize = (1 << (BUCKET_COUNT - 1)) / 8;
var NODE_IS_SPLIT_START: usize = BUCKETS_END;
var NODE_IS_SPLIT_END: usize = NODE_IS_SPLIT_START + SPLIT_COUNT * sizeof<u8>();
function node_is_split$get(index: usize): i32 {
assert(index < SPLIT_COUNT);
return load<u8>(NODE_IS_SPLIT_START + index);
}
function node_is_split$set(index: usize, state: i32): void {
assert(index < SPLIT_COUNT);
store<u8>(NODE_IS_SPLIT_START + index, state);
}
/*
* This is the starting address of the address range for this allocator. Every
* returned allocation will be an offset of this pointer from 0 to MAX_ALLOC.
*/
var base_ptr: usize;
/*
* This is the maximum address that has ever been used by the allocator. It's
* used to know when to call "brk" to request more memory from the kernel.
*/
var max_ptr: usize;
/*
* Make sure all addresses before "new_value" are valid and can be used. Memory
* is allocated in a 2gb address range but that memory is not reserved up
* front. It's only reserved when it's needed by calling this function. This
* will return false if the memory could not be reserved.
*/
function update_max_ptr(new_value: usize): i32 {
if (new_value > max_ptr) {
// if (brk(new_value)) {
// return 0;
// }
let oldPages = <i32>memory.size();
let newPages = <i32>(((new_value + 0xffff) & ~0xffff) >>> 16);
assert(newPages > oldPages);
if (memory.grow(newPages - oldPages) < 0) {
return 0;
}
// max_ptr = new_value;
max_ptr = <usize>newPages << 16;
}
return 1;
}
/*
* Initialize a list to empty. Because these are circular lists, an "empty"
* list is an entry where both links point to itself. This makes insertion
* and removal simpler because they don't need any branches.
*/
function list_init(list: List): void {
list.prev = list;
list.next = list;
}
/*
* Append the provided entry to the end of the list. This assumes the entry
* isn't in a list already because it overwrites the linked list pointers.
*/
function list_push(list: List, entry: List): void {
var prev = list.prev;
entry.prev = prev;
entry.next = list;
prev.next = entry;
list.prev = entry;
}
/*
* Remove the provided entry from whichever list it's currently in. This
* assumes that the entry is in a list. You don't need to provide the list
* because the lists are circular, so the list's pointers will automatically
* be updated if the first or last entries are removed.
*/
function list_remove(entry: List): void {
var prev = entry.prev;
var next = entry.next;
prev.next = next;
next.prev = prev;
}
/*
* Remove and return the first entry in the list or NULL if the list is empty.
*/
function list_pop(list: List): List | null {
var back = list.prev;
if (back == list) return null;
list_remove(back);
return back;
}
/*
* This maps from the index of a node to the address of memory that node
* represents. The bucket can be derived from the index using a loop but is
* required to be provided here since having them means we can avoid the loop
* and have this function return in constant time.
*/
function ptr_for_node(index: usize, bucket: usize): usize {
return base_ptr + ((index - (1 << bucket) + 1) << (MAX_ALLOC_LOG2 - bucket));
}
/*
* This maps from an address of memory to the node that represents that
* address. There are often many nodes that all map to the same address, so
* the bucket is needed to uniquely identify a node.
*/
function node_for_ptr(ptr: usize, bucket: usize): usize {
return ((ptr - base_ptr) >> (MAX_ALLOC_LOG2 - bucket)) + (1 << bucket) - 1;
}
/*
* Given the index of a node, this returns the "is split" flag of the parent.
*/
function parent_is_split(index: usize): bool {
index = (index - 1) / 2;
return ((node_is_split$get(index / 8) >>> <i32>(index % 8)) & 1) == 1;
}
/*
* Given the index of a node, this flips the "is split" flag of the parent.
*/
function flip_parent_is_split(index: usize): void {
index = (index - 1) / 2;
var indexDiv8 = index / 8;
node_is_split$set(indexDiv8,
node_is_split$get(indexDiv8) ^ <i32>(1 << (index % 8))
);
}
/*
* Given the requested size passed to "malloc", this function returns the index
* of the smallest bucket that can fit that size.
*/
function bucket_for_request(request: usize): usize {
var bucket = BUCKET_COUNT - 1;
var size = MIN_ALLOC;
while (size < request) {
bucket--;
size *= 2;
}
return bucket;
}
/*
* The tree is always rooted at the current bucket limit. This call grows the
* tree by repeatedly doubling it in size until the root lies at the provided
* bucket index. Each doubling lowers the bucket limit by 1.
*/
function lower_bucket_limit(bucket: usize): u32 {
while (bucket < bucket_limit) {
let root = node_for_ptr(base_ptr, bucket_limit);
let right_child: usize;
/*
* If the parent isn't SPLIT, that means the node at the current bucket
* limit is UNUSED and our address space is entirely free. In that case,
* clear the root free list, increase the bucket limit, and add a single
* block with the newly-expanded address space to the new root free list.
*/
if (!parent_is_split(root)) {
list_remove(changetype<List>(base_ptr));
list_init(buckets$get(--bucket_limit));
list_push(buckets$get(bucket_limit), changetype<List>(base_ptr));
continue;
}
/*
* Otherwise, the tree is currently in use. Create a parent node for the
* current root node in the SPLIT state with a right child on the free
* list. Make sure to reserve the memory for the free list entry before
* writing to it. Note that we do not need to flip the "is split" flag for
* our current parent because it's already on (we know because we just
* checked it above).
*/
right_child = ptr_for_node(root + 1, bucket_limit);
if (!update_max_ptr(right_child + List.SIZE)) {
return 0;
}
list_push(buckets$get(bucket_limit), changetype<List>(right_child));
list_init(buckets$get(--bucket_limit));
/*
* Set the grandparent's SPLIT flag so if we need to lower the bucket limit
* again, we'll know that the new root node we just added is in use.
*/
root = (root - 1) / 2;
if (root != 0) {
flip_parent_is_split(root);
}
}
return 1;
}
// Memory allocator implementation
@global namespace memory {
export function allocate(request: usize): usize {
var original_bucket: usize, bucket: usize;
/*
* Make sure it's possible for an allocation of this size to succeed. There's
* a hard-coded limit on the maximum allocation size because of the way this
* allocator works.
*/
if (request > MAX_ALLOC - HEADER_SIZE) unreachable();
/*
* Initialize our global state if this is the first call to "malloc". At the
* beginning, the tree has a single node that represents the smallest
* possible allocation size. More memory will be reserved later as needed.
*/
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>memory.size() << 16; // must grow first
bucket_limit = BUCKET_COUNT - 1;
if (!update_max_ptr(base_ptr + List.SIZE)) {
return 0;
}
list_init(buckets$get(BUCKET_COUNT - 1));
list_push(buckets$get(BUCKET_COUNT - 1), changetype<List>(base_ptr));
}
/*
* Find the smallest bucket that will fit this request. This doesn't check
* that there's space for the request yet.
*/
bucket = bucket_for_request(request + HEADER_SIZE);
original_bucket = bucket;
/*
* Search for a bucket with a non-empty free list that's as large or larger
* than what we need. If there isn't an exact match, we'll need to split a
* larger one to get a match.
*/
while (bucket + 1 != 0) {
let size: usize, bytes_needed: usize, i: usize;
let ptr: usize;
/*
* We may need to grow the tree to be able to fit an allocation of this
* size. Try to grow the tree and stop here if we can't.
*/
if (!lower_bucket_limit(bucket)) {
return 0;
}
/*
* Try to pop a block off the free list for this bucket. If the free list
* is empty, we're going to have to split a larger block instead.
*/
ptr = changetype<usize>(list_pop(buckets$get(bucket)));
if (!ptr) {
/*
* If we're not at the root of the tree or it's impossible to grow the
* tree any more, continue on to the next bucket.
*/
if (bucket != bucket_limit || bucket == 0) {
bucket--;
continue;
}
/*
* Otherwise, grow the tree one more level and then pop a block off the
* free list again. Since we know the root of the tree is used (because
* the free list was empty), this will add a parent above this node in
* the SPLIT state and then add the new right child node to the free list
* for this bucket. Popping the free list will give us this right child.
*/
if (!lower_bucket_limit(bucket - 1)) {
return 0;
}
ptr = changetype<usize>(list_pop(buckets$get(bucket)));
}
/*
* Try to expand the address space first before going any further. If we
* have run out of space, put this block back on the free list and fail.
*/
size = 1 << (MAX_ALLOC_LOG2 - bucket);
bytes_needed = bucket < original_bucket ? size / 2 + List.SIZE : size;
if (!update_max_ptr(ptr + bytes_needed)) {
list_push(buckets$get(bucket), changetype<List>(ptr));
return 0;
}
/*
* If we got a node off the free list, change the node from UNUSED to USED.
* This involves flipping our parent's "is split" bit because that bit is
* the exclusive-or of the UNUSED flags of both children, and our UNUSED
* flag (which isn't ever stored explicitly) has just changed.
*
* Note that we shouldn't ever need to flip the "is split" bit of our
* grandparent because we know our buddy is USED so it's impossible for our
* grandparent to be UNUSED (if our buddy chunk was UNUSED, our parent
* wouldn't ever have been split in the first place).
*/
i = node_for_ptr(ptr, bucket);
if (i != 0) {
flip_parent_is_split(i);
}
/*
* If the node we got is larger than we need, split it down to the correct
* size and put the new unused child nodes on the free list in the
* corresponding bucket. This is done by repeatedly moving to the left
* child, splitting the parent, and then adding the right child to the free
* list.
*/
while (bucket < original_bucket) {
i = i * 2 + 1;
bucket++;
flip_parent_is_split(i);
list_push(
buckets$get(bucket),
changetype<List>(ptr_for_node(i + 1, bucket))
);
}
/*
* Now that we have a memory address, write the block header (just the size
* of the allocation) and return the address immediately after the header.
*/
store<usize>(ptr, request);
return ptr + HEADER_SIZE;
}
return 0;
}
export function free(ptr: usize): void {
var bucket: usize, i: usize;
/*
* Ignore any attempts to free a NULL pointer.
*/
if (!ptr) {
return;
}
/*
* We were given the address returned by "malloc" so get back to the actual
* address of the node by subtracting off the size of the block header. Then
* look up the index of the node corresponding to this address.
*/
ptr = ptr - HEADER_SIZE;
bucket = bucket_for_request(load<usize>(ptr) + HEADER_SIZE);
i = node_for_ptr(ptr, bucket);
/*
* Traverse up to the root node, flipping USED blocks to UNUSED and merging
* UNUSED buddies together into a single UNUSED parent.
*/
while (i != 0) {
/*
* Change this node from UNUSED to USED. This involves flipping our
* parent's "is split" bit because that bit is the exclusive-or of the
* UNUSED flags of both children, and our UNUSED flag (which isn't ever
* stored explicitly) has just changed.
*/
flip_parent_is_split(i);
/*
* If the parent is now SPLIT, that means our buddy is USED, so don't merge
* with it. Instead, stop the iteration here and add ourselves to the free
* list for our bucket.
*
* Also stop here if we're at the current root node, even if that root node
* is now UNUSED. Root nodes don't have a buddy so we can't merge with one.
*/
if (parent_is_split(i) || bucket == bucket_limit) {
break;
}
/*
* If we get here, we know our buddy is UNUSED. In this case we should
* merge with that buddy and continue traversing up to the root node. We
* need to remove the buddy from its free list here but we don't need to
* add the merged parent to its free list yet. That will be done once after
* this loop is finished.
*/
list_remove(changetype<List>(ptr_for_node(((i - 1) ^ 1) + 1, bucket)));
i = (i - 1) / 2;
bucket--;
}
/*
* Add ourselves to the free list for our bucket. We add to the back of the
* list because "malloc" takes from the back of the list and we want a "free"
* followed by a "malloc" of the same size to ideally use the same address
* for better memory locality.
*/
list_push(buckets$get(bucket), changetype<List>(ptr_for_node(i, bucket)));
}
}

View File

@ -1,24 +1,10 @@
/** @unsafe declare function _malloc(size: usize): usize;
* Emscripten Memory Allocator. @unsafe declare function _free(ptr: usize): void;
*
* Uses Emscripten's exported _malloc and _free implementations, i.e., when linking with
* Emscripten-compiled programs that already provide these. Differs from 'system' in that their
* names are prefixed with an underscore.
*
* @module std/assembly/allocator/emscripten
*//***/
declare function _malloc(size: usize): usize; @unsafe @global function __memory_allocate(size: usize): usize {
declare function _free(ptr: usize): void;
// Memory allocator implementation
@global namespace memory {
@inline export function allocate(size: usize): usize {
return _malloc(size); return _malloc(size);
} }
@inline export function free(ptr: usize): void { @unsafe @global function __memory_free(ptr: usize): void {
_free(ptr); _free(ptr);
} }
}

View File

@ -1,23 +1,10 @@
/** @unsafe declare function malloc(size: usize): usize;
* System Memory Allocator. @unsafe declare function free(ptr: usize): void;
*
* Uses the environment's malloc and free implementations, i.e., when linking with other C-like
* programs that already provide these.
*
* @module std/assembly/allocator/system
*//***/
declare function malloc(size: usize): usize; @unsafe @global function __memory_allocate(size: usize): usize {
declare function free(ptr: usize): void;
// Memory allocator interface
@global namespace memory {
@inline export function allocate(size: usize): usize {
return malloc(size); return malloc(size);
} }
@inline export function free(ptr: usize): void { @unsafe @global function __memory_free(ptr: usize): void {
free(ptr); free(ptr);
} }
}

View File

@ -1,12 +1,3 @@
/**
* Two-Level Segregate Fit Memory Allocator.
*
* A general purpose dynamic memory allocator specifically designed to meet real-time requirements.
* Always aligns to 8 bytes.
*
* @module std/assembly/allocator/tlsf
*//***/
// ╒══════════════ Block size interpretation (32-bit) ═════════════╕ // ╒══════════════ Block size interpretation (32-bit) ═════════════╕
// 3 2 1 // 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 // 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
@ -16,6 +7,7 @@
// FL: first level, SL: second level, AL: alignment, SB: small block // FL: first level, SL: second level, AL: alignment, SB: small block
import { AL_BITS, AL_SIZE, AL_MASK } from "../util/allocator"; import { AL_BITS, AL_SIZE, AL_MASK } from "../util/allocator";
import { HEAP_BASE, memory } from "../memory";
const SL_BITS: u32 = 5; const SL_BITS: u32 = 5;
const SL_SIZE: usize = 1 << <usize>SL_BITS; const SL_SIZE: usize = 1 << <usize>SL_BITS;
@ -429,11 +421,8 @@ function fls<T>(word: T): T {
/** Reference to the initialized {@link Root} structure, once initialized. */ /** Reference to the initialized {@link Root} structure, once initialized. */
var ROOT: Root = changetype<Root>(0); var ROOT: Root = changetype<Root>(0);
// Memory allocator interface
@global namespace memory {
/** Allocates a chunk of memory. */ /** Allocates a chunk of memory. */
export function allocate(size: usize): usize { @unsafe @global function __memory_allocate(size: usize): usize {
// initialize if necessary // initialize if necessary
var root = ROOT; var root = ROOT;
if (!root) { if (!root) {
@ -481,7 +470,7 @@ var ROOT: Root = changetype<Root>(0);
} }
/** Frees the chunk of memory at the specified address. */ /** Frees the chunk of memory at the specified address. */
export function free(data: usize): void { @unsafe @global function __memory_free(data: usize): void {
if (data) { if (data) {
let root = ROOT; let root = ROOT;
if (root) { if (root) {
@ -493,4 +482,3 @@ var ROOT: Root = changetype<Root>(0);
} }
} }
} }
}

View File

@ -1,4 +1,4 @@
import { ALLOC, REALLOC, REGISTER, LINK, FREE, ArrayBufferView } from "./runtime"; import { runtime, ArrayBufferView } from "./runtime";
import { ArrayBuffer } from "./arraybuffer"; import { ArrayBuffer } from "./arraybuffer";
import { COMPARATOR, SORT } from "./util/sort"; import { COMPARATOR, SORT } from "./util/sort";
import { itoa, dtoa, itoa_stream, dtoa_stream, MAX_DOUBLE_LENGTH } from "./util/number"; import { itoa, dtoa, itoa_stream, dtoa_stream, MAX_DOUBLE_LENGTH } from "./util/number";
@ -36,7 +36,7 @@ export class Array<T> extends ArrayBufferView {
const MAX_LENGTH = ArrayBufferView.MAX_BYTELENGTH >>> alignof<T>(); const MAX_LENGTH = ArrayBufferView.MAX_BYTELENGTH >>> alignof<T>();
if (<u32>length > <u32>MAX_LENGTH) throw new RangeError("Invalid array length"); if (<u32>length > <u32>MAX_LENGTH) throw new RangeError("Invalid array length");
let newCapacity = <usize>length << alignof<T>(); let newCapacity = <usize>length << alignof<T>();
let newData = REALLOC(changetype<usize>(oldData), newCapacity); // registers on move let newData = runtime.realloc(changetype<usize>(oldData), newCapacity); // registers on move
if (newData !== changetype<usize>(oldData)) { if (newData !== changetype<usize>(oldData)) {
this.data = changetype<ArrayBuffer>(newData); // links this.data = changetype<ArrayBuffer>(newData); // links
this.dataStart = newData; this.dataStart = newData;
@ -76,7 +76,7 @@ export class Array<T> extends ArrayBufferView {
private __set(index: i32, value: T): void { private __set(index: i32, value: T): void {
this.resize(index + 1); this.resize(index + 1);
store<T>(this.dataStart + (<usize>index << alignof<T>()), value); store<T>(this.dataStart + (<usize>index << alignof<T>()), value);
if (isManaged<T>()) LINK(changetype<usize>(value), changetype<usize>(this)); if (isManaged<T>()) runtime.link(changetype<usize>(value), changetype<usize>(this));
if (index >= this.length_) this.length_ = index + 1; if (index >= this.length_) this.length_ = index + 1;
} }
@ -141,7 +141,7 @@ export class Array<T> extends ArrayBufferView {
this.resize(newLength); this.resize(newLength);
this.length_ = newLength; this.length_ = newLength;
store<T>(this.dataStart + (<usize>(newLength - 1) << alignof<T>()), element); store<T>(this.dataStart + (<usize>(newLength - 1) << alignof<T>()), element);
if (isManaged<T>()) LINK(changetype<usize>(element), changetype<usize>(this)); if (isManaged<T>()) runtime.link(changetype<usize>(element), changetype<usize>(this));
return newLength; return newLength;
} }
@ -156,14 +156,14 @@ export class Array<T> extends ArrayBufferView {
for (let offset: usize = 0; offset < thisSize; offset += sizeof<T>()) { for (let offset: usize = 0; offset < thisSize; offset += sizeof<T>()) {
let element = load<T>(thisStart + offset); let element = load<T>(thisStart + offset);
store<T>(outStart + offset, element); store<T>(outStart + offset, element);
LINK(changetype<usize>(element), changetype<usize>(out)); runtime.link(changetype<usize>(element), changetype<usize>(out));
} }
let otherStart = other.dataStart; let otherStart = other.dataStart;
let otherSize = <usize>otherLen << alignof<T>(); let otherSize = <usize>otherLen << alignof<T>();
for (let offset: usize = 0; offset < otherSize; offset += sizeof<T>()) { for (let offset: usize = 0; offset < otherSize; offset += sizeof<T>()) {
let element = load<T>(otherStart + offset); let element = load<T>(otherStart + offset);
store<T>(outStart + thisSize + offset, element); store<T>(outStart + thisSize + offset, element);
LINK(changetype<usize>(element), changetype<usize>(out)); runtime.link(changetype<usize>(element), changetype<usize>(out));
} }
} else { } else {
memory.copy(outStart, this.dataStart, thisSize); memory.copy(outStart, this.dataStart, thisSize);
@ -221,7 +221,7 @@ export class Array<T> extends ArrayBufferView {
let value = load<T>(this.dataStart + (<usize>index << alignof<T>())); let value = load<T>(this.dataStart + (<usize>index << alignof<T>()));
let result = callbackfn(value, index, this); let result = callbackfn(value, index, this);
store<U>(outStart + (<usize>index << alignof<U>()), result); store<U>(outStart + (<usize>index << alignof<U>()), result);
if (isManaged<U>()) LINK(changetype<usize>(result), changetype<usize>(out)); if (isManaged<U>()) runtime.link(changetype<usize>(result), changetype<usize>(out));
} }
return out; return out;
} }
@ -290,7 +290,7 @@ export class Array<T> extends ArrayBufferView {
<usize>(newLength - 1) << alignof<T>() <usize>(newLength - 1) << alignof<T>()
); );
store<T>(base, element); store<T>(base, element);
if (isManaged<T>()) LINK(changetype<usize>(element), changetype<usize>(this)); if (isManaged<T>()) runtime.link(changetype<usize>(element), changetype<usize>(this));
this.length_ = newLength; this.length_ = newLength;
return newLength; return newLength;
} }
@ -307,7 +307,7 @@ export class Array<T> extends ArrayBufferView {
let offset = <usize>i << alignof<T>(); let offset = <usize>i << alignof<T>();
let element = load<T>(thisBase + offset); let element = load<T>(thisBase + offset);
store<T>(sliceBase + offset, element); store<T>(sliceBase + offset, element);
if (isManaged<T>()) LINK(changetype<usize>(element), changetype<usize>(slice)); if (isManaged<T>()) runtime.link(changetype<usize>(element), changetype<usize>(slice));
} }
return slice; return slice;
} }
@ -323,7 +323,7 @@ export class Array<T> extends ArrayBufferView {
for (let i = 0; i < deleteCount; ++i) { for (let i = 0; i < deleteCount; ++i) {
let element = load<T>(thisBase + (<usize>i << alignof<T>())); let element = load<T>(thisBase + (<usize>i << alignof<T>()));
store<T>(spliceStart + (<usize>i << alignof<T>()), element); store<T>(spliceStart + (<usize>i << alignof<T>()), element);
if (isManaged<T>()) LINK(changetype<usize>(element), changetype<usize>(splice)); if (isManaged<T>()) runtime.link(changetype<usize>(element), changetype<usize>(splice));
} }
memory.copy( memory.copy(
splice.dataStart, splice.dataStart,
@ -395,7 +395,7 @@ export class Array<T> extends ArrayBufferView {
var sepLen = separator.length; var sepLen = separator.length;
var valueLen = 5; // max possible length of element len("false") var valueLen = 5; // max possible length of element len("false")
var estLen = (valueLen + sepLen) * lastIndex + valueLen; var estLen = (valueLen + sepLen) * lastIndex + valueLen;
var result = ALLOC(estLen << 1); var result = runtime.alloc(estLen << 1);
var offset = 0; var offset = 0;
var value: bool; var value: bool;
for (let i = 0; i < lastIndex; ++i) { for (let i = 0; i < lastIndex; ++i) {
@ -427,10 +427,10 @@ export class Array<T> extends ArrayBufferView {
if (estLen > offset) { if (estLen > offset) {
let trimmed = changetype<string>(result).substring(0, offset); let trimmed = changetype<string>(result).substring(0, offset);
FREE(result); runtime.free(result);
return trimmed; // registered in .substring return trimmed; // registered in .substring
} }
return REGISTER<string>(result); return runtime.register<string>(result);
} }
private join_int(separator: string = ","): string { private join_int(separator: string = ","): string {
@ -442,7 +442,7 @@ export class Array<T> extends ArrayBufferView {
var sepLen = separator.length; var sepLen = separator.length;
const valueLen = (sizeof<T>() <= 4 ? 10 : 20) + <i32>isSigned<T>(); const valueLen = (sizeof<T>() <= 4 ? 10 : 20) + <i32>isSigned<T>();
var estLen = (valueLen + sepLen) * lastIndex + valueLen; var estLen = (valueLen + sepLen) * lastIndex + valueLen;
var result = ALLOC(estLen << 1); var result = runtime.alloc(estLen << 1);
var offset = 0; var offset = 0;
var value: T; var value: T;
for (let i = 0; i < lastIndex; ++i) { for (let i = 0; i < lastIndex; ++i) {
@ -461,10 +461,10 @@ export class Array<T> extends ArrayBufferView {
offset += itoa_stream<T>(result, offset, value); offset += itoa_stream<T>(result, offset, value);
if (estLen > offset) { if (estLen > offset) {
let trimmed = changetype<string>(result).substring(0, offset); let trimmed = changetype<string>(result).substring(0, offset);
FREE(result); runtime.free(result);
return trimmed; // registered in .substring return trimmed; // registered in .substring
} }
return REGISTER<string>(result); return runtime.register<string>(result);
} }
private join_flt(separator: string = ","): string { private join_flt(separator: string = ","): string {
@ -476,7 +476,7 @@ export class Array<T> extends ArrayBufferView {
const valueLen = MAX_DOUBLE_LENGTH; const valueLen = MAX_DOUBLE_LENGTH;
var sepLen = separator.length; var sepLen = separator.length;
var estLen = (valueLen + sepLen) * lastIndex + valueLen; var estLen = (valueLen + sepLen) * lastIndex + valueLen;
var result = ALLOC(estLen << 1); var result = runtime.alloc(estLen << 1);
var offset = 0; var offset = 0;
var value: T; var value: T;
for (let i = 0; i < lastIndex; ++i) { for (let i = 0; i < lastIndex; ++i) {
@ -495,10 +495,10 @@ export class Array<T> extends ArrayBufferView {
offset += dtoa_stream(result, offset, value); offset += dtoa_stream(result, offset, value);
if (estLen > offset) { if (estLen > offset) {
let trimmed = changetype<string>(result).substring(0, offset); let trimmed = changetype<string>(result).substring(0, offset);
FREE(result); runtime.free(result);
return trimmed; // registered in .substring return trimmed; // registered in .substring
} }
return REGISTER<string>(result); return runtime.register<string>(result);
} }
private join_str(separator: string = ","): string { private join_str(separator: string = ","): string {
@ -513,7 +513,7 @@ export class Array<T> extends ArrayBufferView {
estLen += load<string>(dataStart + (<usize>i << alignof<T>())).length; estLen += load<string>(dataStart + (<usize>i << alignof<T>())).length;
} }
var offset = 0; var offset = 0;
var result = ALLOC((estLen + sepLen * lastIndex) << 1); var result = runtime.alloc((estLen + sepLen * lastIndex) << 1);
var value: String; var value: String;
for (let i = 0; i < lastIndex; ++i) { for (let i = 0; i < lastIndex; ++i) {
value = load<string>(dataStart + (<usize>i << alignof<T>())); value = load<string>(dataStart + (<usize>i << alignof<T>()));
@ -544,7 +544,7 @@ export class Array<T> extends ArrayBufferView {
<usize>valueLen << 1 <usize>valueLen << 1
); );
} }
return REGISTER<string>(result); return runtime.register<string>(result);
} }
private join_arr(separator: string = ","): string { private join_arr(separator: string = ","): string {
@ -578,7 +578,7 @@ export class Array<T> extends ArrayBufferView {
const valueLen = 15; // max possible length of element len("[object Object]") const valueLen = 15; // max possible length of element len("[object Object]")
var sepLen = separator.length; var sepLen = separator.length;
var estLen = (valueLen + sepLen) * lastIndex + valueLen; var estLen = (valueLen + sepLen) * lastIndex + valueLen;
var result = ALLOC(estLen << 1); var result = runtime.alloc(estLen << 1);
var offset = 0; var offset = 0;
var value: T; var value: T;
for (let i = 0; i < lastIndex; ++i) { for (let i = 0; i < lastIndex; ++i) {
@ -610,10 +610,10 @@ export class Array<T> extends ArrayBufferView {
} }
if (estLen > offset) { if (estLen > offset) {
let out = changetype<string>(result).substring(0, offset); let out = changetype<string>(result).substring(0, offset);
FREE(result); runtime.free(result);
return out; // registered in .substring return out; // registered in .substring
} }
return REGISTER<string>(result); return runtime.register<string>(result);
} }
@inline @inline

View File

@ -1,4 +1,4 @@
import { HEADER, ALLOC_RAW, ALLOC, REGISTER, ArrayBufferView } from "./runtime"; import { runtime, ArrayBufferView } from "./runtime";
@sealed export class ArrayBuffer { @sealed export class ArrayBuffer {
@ -22,11 +22,11 @@ import { HEADER, ALLOC_RAW, ALLOC, REGISTER, ArrayBufferView } from "./runtime";
constructor(length: i32) { constructor(length: i32) {
if (<u32>length > <u32>ArrayBufferView.MAX_BYTELENGTH) throw new RangeError("Invalid array buffer length"); if (<u32>length > <u32>ArrayBufferView.MAX_BYTELENGTH) throw new RangeError("Invalid array buffer length");
return REGISTER<ArrayBuffer>(ALLOC(<usize>length)); return runtime.register<ArrayBuffer>(runtime.alloc(<usize>length));
} }
get byteLength(): i32 { get byteLength(): i32 {
return changetype<HEADER>(changetype<usize>(this) - HEADER_SIZE).payloadSize; return changetype<runtime.Header>(changetype<usize>(this) - runtime.Header.SIZE).payloadSize;
} }
slice(begin: i32 = 0, end: i32 = ArrayBufferView.MAX_BYTELENGTH): ArrayBuffer { slice(begin: i32 = 0, end: i32 = ArrayBufferView.MAX_BYTELENGTH): ArrayBuffer {
@ -34,9 +34,9 @@ import { HEADER, ALLOC_RAW, ALLOC, REGISTER, ArrayBufferView } from "./runtime";
begin = begin < 0 ? max(length + begin, 0) : min(begin, length); begin = begin < 0 ? max(length + begin, 0) : min(begin, length);
end = end < 0 ? max(length + end , 0) : min(end , length); end = end < 0 ? max(length + end , 0) : min(end , length);
var outSize = <usize>max(end - begin, 0); var outSize = <usize>max(end - begin, 0);
var out = ALLOC_RAW(outSize); var out = runtime.allocRaw(outSize);
memory.copy(out, changetype<usize>(this) + <usize>begin, outSize); memory.copy(out, changetype<usize>(this) + <usize>begin, outSize);
return REGISTER<ArrayBuffer>(out); return runtime.register<ArrayBuffer>(out);
} }
toString(): string { toString(): string {

View File

@ -13,7 +13,6 @@
@builtin export declare function isFunction<T>(value?: T): bool; @builtin export declare function isFunction<T>(value?: T): bool;
@builtin export declare function isNullable<T>(value?: T): bool; @builtin export declare function isNullable<T>(value?: T): bool;
@builtin export declare function isDefined(expression: void): bool; @builtin export declare function isDefined(expression: void): bool;
@builtin export declare function isImplemented(expression: void): bool;
@builtin export declare function isConstant(expression: void): bool; @builtin export declare function isConstant(expression: void): bool;
@builtin export declare function isManaged<T>(value?: T): bool; @builtin export declare function isManaged<T>(value?: T): bool;
@inline export function isNaN<T>(value: T): bool { return value != value; } @inline export function isNaN<T>(value: T): bool { return value != value; }

View File

@ -1,9 +1,3 @@
/**
* Incremental Tri-Color-Marking Garbage Collector.
*
* @module std/assembly/collector/itcm
*//***/
// Largely based on Bach Le's μgc, see: https://github.com/bullno1/ugc // Largely based on Bach Le's μgc, see: https://github.com/bullno1/ugc
@inline const TRACE = false; @inline const TRACE = false;
@ -11,8 +5,8 @@
/** Size of a managed object header. */ /** Size of a managed object header. */
@inline export const HEADER_SIZE: usize = (offsetof<ManagedObject>() + AL_MASK) & ~AL_MASK; @inline export const HEADER_SIZE: usize = (offsetof<ManagedObject>() + AL_MASK) & ~AL_MASK;
import { AL_MASK, MAX_SIZE_32 } from "../internal/allocator"; import { AL_MASK, MAX_SIZE_32 } from "../util/allocator";
import { __rt_iterateroots } from "../builtins"; import { gc } from "../gc";
/** Collector states. */ /** Collector states. */
const enum State { const enum State {
@ -142,7 +136,7 @@ function step(): void {
} }
case State.IDLE: { case State.IDLE: {
if (TRACE) trace("gc~step/IDLE"); if (TRACE) trace("gc~step/IDLE");
__rt_iterateroots(__gc_mark); gc.iterateRoots(__gc_mark);
state = State.MARK; state = State.MARK;
if (TRACE) trace("gc~state = MARK"); if (TRACE) trace("gc~state = MARK");
break; break;
@ -163,7 +157,7 @@ function step(): void {
obj.hookFn(objToRef(obj)); obj.hookFn(objToRef(obj));
} else { } else {
if (TRACE) trace("gc~step/MARK finish"); if (TRACE) trace("gc~step/MARK finish");
__rt_iterateroots(__gc_mark); gc.iterateRoots(__gc_mark);
obj = iter.next; obj = iter.next;
if (obj === toSpace) { if (obj === toSpace) {
let from = fromSpace; let from = fromSpace;
@ -202,9 +196,7 @@ function step(): void {
return changetype<usize>(obj) + HEADER_SIZE; return changetype<usize>(obj) + HEADER_SIZE;
} }
// Garbage collector interface @global @unsafe export function __gc_allocate( // TODO: make this register only / reuse header
@global export function __gc_allocate(
size: usize, size: usize,
markFn: (ref: usize) => void markFn: (ref: usize) => void
): usize { ): usize {
@ -218,13 +210,13 @@ function step(): void {
return objToRef(obj); return objToRef(obj);
} }
@global export function __gc_link(parentRef: usize, childRef: usize): void { @global @unsafe export function __gc_link(parentRef: usize, childRef: usize): void {
if (TRACE) trace("gc.link", 2, parentRef, childRef); if (TRACE) trace("gc.link", 2, parentRef, childRef);
var parent = refToObj(parentRef); var parent = refToObj(parentRef);
if (parent.color == <i32>!white && refToObj(childRef).color == white) parent.makeGray(); if (parent.color == <i32>!white && refToObj(childRef).color == white) parent.makeGray();
} }
@global export function __gc_mark(ref: usize): void { @global @unsafe export function __gc_mark(ref: usize): void {
if (TRACE) trace("gc.mark", 1, ref); if (TRACE) trace("gc.mark", 1, ref);
if (ref) { if (ref) {
let obj = refToObj(ref); let obj = refToObj(ref);
@ -232,7 +224,7 @@ function step(): void {
} }
} }
@global export function __gc_collect(): void { @global @unsafe export function __gc_collect(): void {
if (TRACE) trace("gc.collect"); if (TRACE) trace("gc.collect");
// begin collecting if not yet collecting // begin collecting if not yet collecting
switch (state) { switch (state) {

View File

@ -1,4 +1,5 @@
import { ArrayBuffer } from "./arraybuffer"; import { ArrayBuffer } from "./arraybuffer";
import { ArrayBufferView } from "./runtime";
export class DataView { export class DataView {
@ -12,7 +13,7 @@ export class DataView {
byteLength: i32 = i32.MIN_VALUE // FIXME byteLength: i32 = i32.MIN_VALUE // FIXME
) { ) {
if (byteLength === i32.MIN_VALUE) byteLength = buffer.byteLength - byteOffset; // FIXME if (byteLength === i32.MIN_VALUE) byteLength = buffer.byteLength - byteOffset; // FIXME
if (<u32>byteLength > <u32>ArrayBuffer.MAX_BYTELENGTH) throw new RangeError("Invalid byteLength"); if (<u32>byteLength > <u32>ArrayBufferView.MAX_BYTELENGTH) throw new RangeError("Invalid byteLength");
if (<u32>byteOffset + byteLength > <u32>buffer.byteLength) throw new RangeError("Invalid length"); if (<u32>byteOffset + byteLength > <u32>buffer.byteLength) throw new RangeError("Invalid length");
this.data = buffer; // links this.data = buffer; // links
var dataStart = changetype<usize>(buffer) + <usize>byteOffset; var dataStart = changetype<usize>(buffer) + <usize>byteOffset;

View File

@ -1,5 +1,3 @@
/* tslint:disable */ @builtin export declare function ERROR(message?: string): void;
@builtin export declare function WARNING(message?: string): void;
@builtin export declare function ERROR(message?: void): void; @builtin export declare function INFO(message?: string): void;
@builtin export declare function WARNING(message?: void): void;
@builtin export declare function INFO(message?: void): void;

View File

@ -1,30 +1,37 @@
/** Garbage collector interface. */ /** Garbage collector interface. */
export namespace gc { export namespace gc {
/** Whether the garbage collector interface is implemented. */
@lazy export const implemented: bool = isDefined(__gc_register);
/** Gets the computed unique class id of a class type. */ /** Gets the computed unique class id of a class type. */
@builtin @unsafe export declare function classId<T>(): u32; @unsafe @builtin export declare function classId<T>(): u32;
/** Iterates reference root objects. */ /** Iterates reference root objects. */
@builtin @unsafe export declare function iterateRoots(fn: (ref: usize) => void): void; @unsafe @builtin export declare function iterateRoots(fn: (ref: usize) => void): void;
/** Registers a managed object to be tracked by the garbage collector. */ /** Registers a managed object to be tracked by the garbage collector. */
@stub @unsafe export function register(ref: usize): void { @unsafe export function register(ref: usize): void {
ERROR("stub: missing garbage collector"); if (isDefined(__gc_register)) __gc_register(ref);
else ERROR("missing implementation: gc.register");
} }
/** Links a registered object with the registered object now referencing it. */ /** Links a registered object with the registered object now referencing it. */
@stub @unsafe export function link(ref: usize, parentRef: usize): void { @unsafe export function link(ref: usize, parentRef: usize): void {
ERROR("stub: missing garbage collector"); if (isDefined(__gc_link)) __gc_link(ref, parentRef);
else ERROR("missing implementation: gc.link");
} }
/** Marks an object as being reachable. */ /** Marks an object as being reachable. */
@stub @unsafe export function mark(ref: usize): void { @unsafe export function mark(ref: usize): void {
ERROR("stub: missing garbage collector"); if (isDefined(__gc_mark)) __gc_mark(ref);
else ERROR("missing implementation: gc.mark");
} }
/** Performs a full garbage collection cycle. */ /** Performs a full garbage collection cycle. */
@stub export function collect(): void { export function collect(): void {
WARNING("stub: missing garbage collector"); if (isDefined(__gc_collect)) __gc_collect();
else WARNING("missing implementation: gc.collect");
} }
} }

View File

@ -144,8 +144,6 @@ declare function isFunction<T>(value?: any): value is (...args: any) => any;
declare function isNullable<T>(value?: any): bool; declare function isNullable<T>(value?: any): bool;
/** Tests if the specified expression resolves to a defined element. Compiles to a constant. */ /** Tests if the specified expression resolves to a defined element. Compiles to a constant. */
declare function isDefined(expression: any): bool; declare function isDefined(expression: any): bool;
/** Tests if the specified expression resolves to an implemented (non-stub) element. Compiles to a constant. */
declare function isImplemented(expression: any): bool;
/** Tests if the specified expression evaluates to a constant value. Compiles to a constant. */ /** Tests if the specified expression evaluates to a constant value. Compiles to a constant. */
declare function isConstant(expression: any): bool; declare function isConstant(expression: any): bool;
/** Tests if the specified type *or* expression is of a managed type. Compiles to a constant. */ /** Tests if the specified type *or* expression is of a managed type. Compiles to a constant. */
@ -1519,6 +1517,13 @@ declare function inline(
descriptor: TypedPropertyDescriptor<any> descriptor: TypedPropertyDescriptor<any>
): TypedPropertyDescriptor<any> | void; ): TypedPropertyDescriptor<any> | void;
/** Annotates a method, function or constant global as unsafe. */
declare function unsafe(
target: any,
propertyKey: string,
descriptor: TypedPropertyDescriptor<any>
): TypedPropertyDescriptor<any> | void;
/** Annotates an explicit external name of a function or global. */ /** Annotates an explicit external name of a function or global. */
declare function external(namespace: string, name: string): ( declare function external(namespace: string, name: string): (
target: any, target: any,

View File

@ -1,4 +1,4 @@
import { LINK } from "./runtime"; import { runtime } from "./runtime";
import { HASH } from "./util/hash"; import { HASH } from "./util/hash";
// A deterministic hash map based on CloseTable from https://github.com/jorendorff/dht // A deterministic hash map based on CloseTable from https://github.com/jorendorff/dht
@ -108,8 +108,8 @@ export class Map<K,V> {
let bucketPtrBase = changetype<usize>(this.buckets) + <usize>(hashCode & this.bucketsMask) * BUCKET_SIZE; let bucketPtrBase = changetype<usize>(this.buckets) + <usize>(hashCode & this.bucketsMask) * BUCKET_SIZE;
entry.taggedNext = load<usize>(bucketPtrBase); entry.taggedNext = load<usize>(bucketPtrBase);
store<usize>(bucketPtrBase, changetype<usize>(entry)); store<usize>(bucketPtrBase, changetype<usize>(entry));
if (isManaged<K>()) LINK(changetype<usize>(key), changetype<usize>(this)); if (isManaged<K>()) runtime.link(changetype<usize>(key), changetype<usize>(this));
if (isManaged<V>()) LINK(changetype<usize>(value), changetype<usize>(this)); if (isManaged<V>()) runtime.link(changetype<usize>(value), changetype<usize>(this));
} }
} }

View File

@ -1,5 +1,7 @@
import { memcmp, memmove, memset } from "./util/memory"; import { memcmp, memmove, memset } from "./util/memory";
@builtin export declare const HEAP_BASE: usize;
/** Memory manager interface. */ /** Memory manager interface. */
export namespace memory { export namespace memory {
@ -7,15 +9,15 @@ export namespace memory {
@builtin export declare function size(): i32; @builtin export declare function size(): i32;
/** Grows the memory by the given size in pages and returns the previous size in pages. */ /** Grows the memory by the given size in pages and returns the previous size in pages. */
@builtin @unsafe export declare function grow(pages: i32): i32; @unsafe @builtin export declare function grow(pages: i32): i32;
/** Fills a section in memory with the specified byte value. */ /** Fills a section in memory with the specified byte value. */
@builtin @unsafe @inline export function fill(dst: usize, c: u8, n: usize): void { @unsafe @builtin export function fill(dst: usize, c: u8, n: usize): void {
memset(dst, c, n); // fallback if "bulk-memory" isn't enabled memset(dst, c, n); // fallback if "bulk-memory" isn't enabled
} }
/** Copies a section of memory to another. Has move semantics. */ /** Copies a section of memory to another. Has move semantics. */
@builtin @unsafe @inline export function copy(dst: usize, src: usize, n: usize): void { @unsafe @builtin export function copy(dst: usize, src: usize, n: usize): void {
memmove(dst, src, n); // fallback if "bulk-memory" isn't enabled memmove(dst, src, n); // fallback if "bulk-memory" isn't enabled
} }
@ -30,24 +32,22 @@ export namespace memory {
} }
/** Dynamically allocates a section of memory and returns its address. */ /** Dynamically allocates a section of memory and returns its address. */
@stub @inline export function allocate(size: usize): usize { @unsafe export function allocate(size: usize): usize {
ERROR("stub: missing memory manager"); if (isDefined(__memory_allocate)) return __memory_allocate(size);
else ERROR("missing implementation: memory.allocate");
return <usize>unreachable(); return <usize>unreachable();
} }
/** Dynamically frees a section of memory by the previously allocated address. */ /** Dynamically frees a section of memory by the previously allocated address. */
@stub @unsafe @inline export function free(ptr: usize): void { @unsafe export function free(ptr: usize): void {
ERROR("stub: missing memory manager"); if (isDefined(__memory_free)) __memory_free(ptr);
else ERROR("missing implementation: memory.free");
} }
/** Resets the memory to its initial state. Arena allocator only. */ /** Resets the memory to its initial state. Arena allocator only. */
@stub @unsafe @inline export function reset(): void { @unsafe export function reset(): void {
ERROR("stub: not supported by memory manager"); if (isDefined(__memory_reset)) __memory_reset();
} else ERROR("missing implementation: memory.reset");
/** Compares a section of memory to another. */
@inline export function compare(vl: usize, vr: usize, n: usize): i32 {
return memcmp(vl, vr, n);
} }
/** Repeats a section of memory at a specific address. */ /** Repeats a section of memory at a specific address. */
@ -59,4 +59,9 @@ export namespace memory {
index += srcLength; index += srcLength;
} }
} }
/** Compares a section of memory to another. */
@inline export function compare(vl: usize, vr: usize, n: usize): i32 {
return memcmp(vl, vr, n);
}
} }

View File

@ -1,4 +1,4 @@
export function bswap<T>(value: T): T { export function bswap<T extends number>(value: T): T {
if (isInteger<T>()) { if (isInteger<T>()) {
if (sizeof<T>() == 2) { if (sizeof<T>() == 2) {
return <T>((value << 8) | ((value >> 8) & <T>0x00FF)); return <T>((value << 8) | ((value >> 8) & <T>0x00FF));
@ -25,7 +25,7 @@ export function bswap<T>(value: T): T {
return value; return value;
} }
@inline export function bswap16<T>(value: T): T { @inline export function bswap16<T extends number>(value: T): T {
if (isInteger<T>() && sizeof<T>() <= 4) { if (isInteger<T>() && sizeof<T>() <= 4) {
if (sizeof<T>() == 2) { if (sizeof<T>() == 2) {
return <T>((value << 8) | ((value >> 8) & <T>0x00FF)); return <T>((value << 8) | ((value >> 8) & <T>0x00FF));

View File

@ -1,9 +1,21 @@
import { AL_MASK, MAX_SIZE_32 } from "./util/allocator"; import { AL_MASK, MAX_SIZE_32 } from "./util/allocator";
import { HEAP_BASE, memory } from "./memory";
import { gc } from "./gc";
@builtin export declare const HEAP_BASE: usize; /** Common runtime. */
export namespace runtime {
/** Common runtime header of all objects. */ /** Common runtime header of all objects. */
@unmanaged export class HEADER { @unmanaged export class Header {
/** Size of a runtime header. */
@lazy @inline static readonly SIZE: usize = gc.implemented
? (offsetof<Header>( ) + AL_MASK) & ~AL_MASK // full header if GC is present
: (offsetof<Header>("gc1") + AL_MASK) & ~AL_MASK; // half header if GC is absent
/** Magic value used to validate runtime headers. */
@lazy @inline static readonly MAGIC: u32 = 0xA55E4B17;
/** Unique id of the respective class or a magic value if not yet registered.*/ /** Unique id of the respective class or a magic value if not yet registered.*/
classId: u32; classId: u32;
/** Size of the allocated payload. */ /** Size of the allocated payload. */
@ -18,73 +30,62 @@ import { AL_MASK, MAX_SIZE_32 } from "./util/allocator";
// decides to use, but it's done this way for maximum flexibility. Also remember that the // decides to use, but it's done this way for maximum flexibility. Also remember that the
// runtime will most likely change significantly once reftypes and WASM GC are a thing. // runtime will most likely change significantly once reftypes and WASM GC are a thing.
/** Whether a GC is present or not. */
@inline export const GC = isImplemented(gc.register);
/** Size of the common runtime header. */
@inline export const HEADER_SIZE: usize = GC
? (offsetof<HEADER>( ) + AL_MASK) & ~AL_MASK // full header if GC is present
: (offsetof<HEADER>("gc1") + AL_MASK) & ~AL_MASK; // half header if GC is absent
/** Magic value used to validate common runtime headers. */
@inline export const HEADER_MAGIC: u32 = 0xA55E4B17;
/** Adjusts an allocation to actual block size. Primarily targets TLSF. */ /** Adjusts an allocation to actual block size. Primarily targets TLSF. */
export function ADJUST(payloadSize: usize): usize { function adjust(payloadSize: usize): usize {
// round up to power of 2, e.g. with HEADER_SIZE=8: // round up to power of 2, e.g. with HEADER_SIZE=8:
// 0 -> 2^3 = 8 // 0 -> 2^3 = 8
// 1..8 -> 2^4 = 16 // 1..8 -> 2^4 = 16
// 9..24 -> 2^5 = 32 // 9..24 -> 2^5 = 32
// ... // ...
// MAX_LENGTH -> 2^30 = 0x40000000 (MAX_SIZE_32) // MAX_LENGTH -> 2^30 = 0x40000000 (MAX_SIZE_32)
return <usize>1 << <usize>(<u32>32 - clz<u32>(payloadSize + HEADER_SIZE - 1)); return <usize>1 << <usize>(<u32>32 - clz<u32>(payloadSize + Header.SIZE - 1));
} }
/** Allocates a new object and returns a pointer to its payload. Does not fill. */ /** Allocates a new object and returns a pointer to its payload. Does not fill. */
@unsafe export function ALLOC_RAW(payloadSize: u32): usize { @unsafe export function allocRaw(payloadSize: u32): usize {
var header = changetype<HEADER>(memory.allocate(ADJUST(payloadSize))); var header = changetype<Header>(memory.allocate(adjust(payloadSize)));
header.classId = HEADER_MAGIC; header.classId = Header.MAGIC;
header.payloadSize = payloadSize; header.payloadSize = payloadSize;
if (GC) { if (gc.implemented) {
header.gc1 = 0; header.gc1 = 0;
header.gc2 = 0; header.gc2 = 0;
} }
return changetype<usize>(header) + HEADER_SIZE; return changetype<usize>(header) + Header.SIZE;
} }
/** Allocates a new object and returns a pointer to its payload. Fills with zeroes.*/ /** Allocates a new object and returns a pointer to its payload. Fills with zeroes.*/
@unsafe export function ALLOC(payloadSize: u32): usize { @unsafe export function alloc(payloadSize: u32): usize {
var ref = ALLOC_RAW(payloadSize); var ref = allocRaw(payloadSize);
memory.fill(ref, 0, payloadSize); memory.fill(ref, 0, payloadSize);
return ref; return ref;
} }
/** Reallocates an object if necessary. Returns a pointer to its (moved) payload. */ /** Reallocates an object if necessary. Returns a pointer to its (moved) payload. */
@unsafe export function REALLOC(ref: usize, newPayloadSize: u32): usize { @unsafe export function realloc(ref: usize, newPayloadSize: u32): usize {
// Background: When managed objects are allocated these aren't immediately registered with GC // Background: When managed objects are allocated these aren't immediately registered with GC
// but can be used as scratch objects while unregistered. This is useful in situations where // but can be used as scratch objects while unregistered. This is useful in situations where
// the object must be reallocated multiple times because its final size isn't known beforehand, // the object must be reallocated multiple times because its final size isn't known beforehand,
// e.g. in Array#filter, with only the final object making it into GC'ed userland. // e.g. in Array#filter, with only the final object making it into GC'ed userland.
var header = changetype<HEADER>(ref - HEADER_SIZE); var header = changetype<Header>(ref - Header.SIZE);
var payloadSize = header.payloadSize; var payloadSize = header.payloadSize;
if (payloadSize < newPayloadSize) { if (payloadSize < newPayloadSize) {
let newAdjustedSize = ADJUST(newPayloadSize); let newAdjustedSize = adjust(newPayloadSize);
if (select(ADJUST(payloadSize), 0, ref > HEAP_BASE) < newAdjustedSize) { if (select(adjust(payloadSize), 0, ref > HEAP_BASE) < newAdjustedSize) {
// move if the allocation isn't large enough or not a heap object // move if the allocation isn't large enough or not a heap object
let newHeader = changetype<HEADER>(memory.allocate(newAdjustedSize)); let newHeader = changetype<Header>(memory.allocate(newAdjustedSize));
newHeader.classId = HEADER_MAGIC; newHeader.classId = Header.MAGIC;
if (GC) { if (gc.implemented) {
newHeader.gc1 = 0; newHeader.gc1 = 0;
newHeader.gc2 = 0; newHeader.gc2 = 0;
} }
let newRef = changetype<usize>(newHeader) + HEADER_SIZE; let newRef = changetype<usize>(newHeader) + Header.SIZE;
memory.copy(newRef, ref, payloadSize); memory.copy(newRef, ref, payloadSize);
memory.fill(newRef + payloadSize, 0, newPayloadSize - payloadSize); memory.fill(newRef + payloadSize, 0, newPayloadSize - payloadSize);
if (header.classId == HEADER_MAGIC) { if (header.classId == Header.MAGIC) {
// free right away if not registered yet // free right away if not registered yet
assert(ref > HEAP_BASE); // static objects aren't scratch objects assert(ref > HEAP_BASE); // static objects aren't scratch objects
memory.free(changetype<usize>(header)); memory.free(changetype<usize>(header));
} else if (GC) { } else if (gc.implemented) {
// if previously registered, register again // if previously registered, register again
gc.register(ref); gc.register(ref);
} }
@ -102,38 +103,41 @@ export function ADJUST(payloadSize: usize): usize {
return ref; return ref;
} }
function unref(ref: usize): HEADER { function unref(ref: usize): Header {
assert(ref >= HEAP_BASE + HEADER_SIZE); // must be a heap object assert(ref >= HEAP_BASE + Header.SIZE); // must be a heap object
var header = changetype<HEADER>(ref - HEADER_SIZE); var header = changetype<Header>(ref - Header.SIZE);
assert(header.classId == HEADER_MAGIC); // must be unregistered assert(header.classId == Header.MAGIC); // must be unregistered
return header; return header;
} }
/** Frees an object. Must not have been registered with GC yet. */ /** Frees an object. Must not have been registered with GC yet. */
@unsafe @inline export function FREE<T>(ref: T): void { @unsafe @inline export function free<T>(ref: T): void {
memory.free(changetype<usize>(unref(changetype<usize>(ref)))); memory.free(changetype<usize>(unref(changetype<usize>(ref))));
} }
/** Registers a managed object. Cannot be free'd anymore afterwards. */ /** Registers a managed object. Cannot be free'd anymore afterwards. */
@unsafe @inline export function REGISTER<T>(ref: usize): T { @unsafe @inline export function register<T>(ref: usize): T {
if (!isReference<T>()) ERROR("reference expected"); if (!isReference<T>()) ERROR("reference expected");
// see comment in REALLOC why this is useful. also inline this because // see comment in REALLOC why this is useful. also inline this because
// it's generic so we don't get a bunch of functions. // it's generic so we don't get a bunch of functions.
unref(ref).classId = gc.classId<T>(); unref(ref).classId = gc.classId<T>();
if (GC) gc.register(ref); if (gc.implemented) gc.register(ref);
return changetype<T>(ref); return changetype<T>(ref);
} }
/** Links a managed object with its managed parent. */ /** Links a managed object with its managed parent. */
@unsafe @inline export function LINK<T, TParent>(ref: T, parentRef: TParent): void { @unsafe @inline export function link<T, TParent>(ref: T, parentRef: TParent): void {
assert(changetype<usize>(ref) >= HEAP_BASE + HEADER_SIZE); // must be a heap object assert(changetype<usize>(ref) >= HEAP_BASE + Header.SIZE); // must be a heap object
var header = changetype<HEADER>(changetype<usize>(ref) - HEADER_SIZE); var header = changetype<Header>(changetype<usize>(ref) - Header.SIZE);
assert(header.classId != HEADER_MAGIC && header.gc1 != 0 && header.gc2 != 0); // must be registered assert(header.classId != Header.MAGIC && header.gc1 != 0 && header.gc2 != 0); // must be registered
if (GC) gc.link(changetype<usize>(ref), changetype<usize>(parentRef)); // tslint:disable-line if (gc.implemented) gc.link(changetype<usize>(ref), changetype<usize>(parentRef)); // tslint:disable-line
}
} }
import { ArrayBuffer } from "./arraybuffer";
export abstract class ArrayBufferView { export abstract class ArrayBufferView {
@lazy static readonly MAX_BYTELENGTH: i32 = MAX_SIZE_32 - HEADER_SIZE; @lazy static readonly MAX_BYTELENGTH: i32 = MAX_SIZE_32 - runtime.Header.SIZE;
[key: number]: number; [key: number]: number;
@ -158,7 +162,7 @@ export abstract class ArrayBufferView {
} }
get length(): i32 { get length(): i32 {
ERROR("concrete implementation must provide this"); ERROR("missing implementation: [T extends ArrayBufferView]#length");
return unreachable(); return unreachable();
} }
} }

View File

@ -1,4 +1,4 @@
import { LINK } from "./runtime"; import { runtime } from "./runtime";
import { HASH } from "./util/hash"; import { HASH } from "./util/hash";
// A deterministic hash set based on CloseTable from https://github.com/jorendorff/dht // A deterministic hash set based on CloseTable from https://github.com/jorendorff/dht
@ -98,7 +98,7 @@ export class Set<K> {
let bucketPtrBase = changetype<usize>(this.buckets) + <usize>(hashCode & this.bucketsMask) * BUCKET_SIZE; let bucketPtrBase = changetype<usize>(this.buckets) + <usize>(hashCode & this.bucketsMask) * BUCKET_SIZE;
entry.taggedNext = load<usize>(bucketPtrBase); entry.taggedNext = load<usize>(bucketPtrBase);
store<usize>(bucketPtrBase, changetype<usize>(entry)); store<usize>(bucketPtrBase, changetype<usize>(entry));
if (isManaged<K>()) LINK(changetype<usize>(key), changetype<usize>(this)); // tslint:disable-line if (isManaged<K>()) runtime.link(changetype<usize>(key), changetype<usize>(this)); // tslint:disable-line
} }
} }

View File

@ -1,25 +1,25 @@
import { HEADER, HEADER_SIZE, ALLOC, REGISTER, ArrayBufferView } from "./runtime"; import { runtime } from "./runtime";
import { MAX_SIZE_32 } from "./util/allocator"; import { MAX_SIZE_32 } from "./util/allocator";
import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./util/string"; import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./util/string";
@sealed export abstract class String { @sealed export abstract class String {
@lazy static readonly MAX_LENGTH: i32 = (MAX_SIZE_32 - HEADER_SIZE) >> alignof<u16>(); @lazy static readonly MAX_LENGTH: i32 = (MAX_SIZE_32 - runtime.Header.SIZE) >> alignof<u16>();
get length(): i32 { get length(): i32 {
return changetype<HEADER>(changetype<usize>(this) - HEADER_SIZE).payloadSize >> 1; return changetype<runtime.Header>(changetype<usize>(this) - runtime.Header.SIZE).payloadSize >> 1;
} }
// TODO Add and handle second argument // TODO Add and handle second argument
static fromCharCode(code: i32): String { static fromCharCode(code: i32): String {
var out = ALLOC(2); var out = runtime.allocRaw(2);
store<u16>(out, <u16>code); store<u16>(out, <u16>code);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
static fromCodePoint(code: i32): String { static fromCodePoint(code: i32): String {
assert(<u32>code <= 0x10FFFF); assert(<u32>code <= 0x10FFFF);
var sur = code > 0xFFFF; var sur = code > 0xFFFF;
var out = ALLOC((<i32>sur + 1) << 1); var out = runtime.allocRaw((<i32>sur + 1) << 1);
if (!sur) { if (!sur) {
store<u16>(out, <u16>code); store<u16>(out, <u16>code);
} else { } else {
@ -28,15 +28,15 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
let lo: u32 = (code & 0x3FF) + 0xDC00; let lo: u32 = (code & 0x3FF) + 0xDC00;
store<u32>(out, (hi << 16) | lo); store<u32>(out, (hi << 16) | lo);
} }
return REGISTER<String>(out); return runtime.register<String>(out);
} }
@operator("[]") charAt(pos: i32): String { @operator("[]") charAt(pos: i32): String {
assert(this !== null); assert(this !== null);
if (<u32>pos >= <u32>this.length) return changetype<String>(""); if (<u32>pos >= <u32>this.length) return changetype<String>("");
var out = ALLOC(2); var out = runtime.allocRaw(2);
store<u16>(out, load<u16>(changetype<usize>(this) + (<usize>pos << 1))); store<u16>(out, load<u16>(changetype<usize>(this) + (<usize>pos << 1)));
return REGISTER<String>(out); return runtime.register<String>(out);
} }
charCodeAt(pos: i32): i32 { charCodeAt(pos: i32): i32 {
@ -67,10 +67,10 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
var otherSize: isize = other.length << 1; var otherSize: isize = other.length << 1;
var outSize: usize = thisSize + otherSize; var outSize: usize = thisSize + otherSize;
if (outSize == 0) return changetype<String>(""); if (outSize == 0) return changetype<String>("");
var out = ALLOC(outSize); var out = runtime.allocRaw(outSize);
memory.copy(out, changetype<usize>(this), thisSize); memory.copy(out, changetype<usize>(this), thisSize);
memory.copy(out + thisSize, changetype<usize>(other), otherSize); memory.copy(out + thisSize, changetype<usize>(other), otherSize);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
endsWith(searchString: String, endPosition: i32 = String.MAX_LENGTH): bool { endsWith(searchString: String, endPosition: i32 = String.MAX_LENGTH): bool {
@ -173,9 +173,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
if (intStart < 0) intStart = max(size + intStart, 0); if (intStart < 0) intStart = max(size + intStart, 0);
var resultLength = min(max(end, 0), size - intStart); var resultLength = min(max(end, 0), size - intStart);
if (resultLength <= 0) return changetype<String>(""); if (resultLength <= 0) return changetype<String>("");
var out = ALLOC(resultLength << 1); var out = runtime.allocRaw(resultLength << 1);
memory.copy(out, changetype<usize>(this) + intStart, resultLength); memory.copy(out, changetype<usize>(this) + intStart, resultLength);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
substring(start: i32, end: i32 = i32.MAX_VALUE): String { substring(start: i32, end: i32 = i32.MAX_VALUE): String {
@ -188,9 +188,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
len = toPos - fromPos; len = toPos - fromPos;
if (!len) return changetype<String>(""); if (!len) return changetype<String>("");
if (!fromPos && toPos == this.length << 1) return this; if (!fromPos && toPos == this.length << 1) return this;
var out = ALLOC(len); var out = runtime.allocRaw(len);
memory.copy(out, changetype<usize>(this) + fromPos, len); memory.copy(out, changetype<usize>(this) + fromPos, len);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
trim(): String { trim(): String {
@ -216,9 +216,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
} }
if (!size) return changetype<String>(""); if (!size) return changetype<String>("");
if (!start && size == length << 1) return this; if (!start && size == length << 1) return this;
var out = ALLOC(size); var out = runtime.allocRaw(size);
memory.copy(out, changetype<usize>(this) + offset, size); memory.copy(out, changetype<usize>(this) + offset, size);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
@inline @inline
@ -246,9 +246,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
if (!offset) return this; if (!offset) return this;
size -= offset; size -= offset;
if (!size) return changetype<String>(""); if (!size) return changetype<String>("");
var out = ALLOC(size); var out = runtime.allocRaw(size);
memory.copy(out, changetype<usize>(this) + offset, size); memory.copy(out, changetype<usize>(this) + offset, size);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
trimEnd(): String { trimEnd(): String {
@ -265,9 +265,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
} }
if (!size) return changetype<String>(""); if (!size) return changetype<String>("");
if (size == originalSize) return this; if (size == originalSize) return this;
var out = ALLOC(size); var out = runtime.allocRaw(size);
memory.copy(out, changetype<usize>(this), size); memory.copy(out, changetype<usize>(this), size);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
padStart(targetLength: i32, padString: string = " "): String { padStart(targetLength: i32, padString: string = " "): String {
@ -277,7 +277,7 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
var padSize = <usize>padString.length << 1; var padSize = <usize>padString.length << 1;
if (targetSize < thisSize || !padSize) return this; if (targetSize < thisSize || !padSize) return this;
var prependSize = targetSize - thisSize; var prependSize = targetSize - thisSize;
var out = ALLOC(targetSize); var out = runtime.allocRaw(targetSize);
if (prependSize > padSize) { if (prependSize > padSize) {
let repeatCount = (prependSize - 2) / padSize; let repeatCount = (prependSize - 2) / padSize;
let restBase = repeatCount * padSize; let restBase = repeatCount * padSize;
@ -288,7 +288,7 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
memory.copy(out, changetype<usize>(padString), prependSize); memory.copy(out, changetype<usize>(padString), prependSize);
} }
memory.copy(out + prependSize, changetype<usize>(this), thisSize); memory.copy(out + prependSize, changetype<usize>(this), thisSize);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
padEnd(targetLength: i32, padString: string = " "): String { padEnd(targetLength: i32, padString: string = " "): String {
@ -298,7 +298,7 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
var padSize = <usize>padString.length << 1; var padSize = <usize>padString.length << 1;
if (targetSize < thisSize || !padSize) return this; if (targetSize < thisSize || !padSize) return this;
var appendSize = targetSize - thisSize; var appendSize = targetSize - thisSize;
var out = ALLOC(targetSize); var out = runtime.allocRaw(targetSize);
memory.copy(out, changetype<usize>(this), thisSize); memory.copy(out, changetype<usize>(this), thisSize);
if (appendSize > padSize) { if (appendSize > padSize) {
let repeatCount = (appendSize - 2) / padSize; let repeatCount = (appendSize - 2) / padSize;
@ -309,7 +309,7 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
} else { } else {
memory.copy(out + thisSize, changetype<usize>(padString), appendSize); memory.copy(out + thisSize, changetype<usize>(padString), appendSize);
} }
return REGISTER<String>(out); return runtime.register<String>(out);
} }
repeat(count: i32 = 0): String { repeat(count: i32 = 0): String {
@ -323,9 +323,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
if (count == 0 || !length) return changetype<String>(""); if (count == 0 || !length) return changetype<String>("");
if (count == 1) return this; if (count == 1) return this;
var out = ALLOC((length * count) << 1); var out = runtime.allocRaw((length * count) << 1);
memory.repeat(out, changetype<usize>(this), <usize>length << 1, count); memory.repeat(out, changetype<usize>(this), <usize>length << 1, count);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
slice(beginIndex: i32, endIndex: i32 = i32.MAX_VALUE): String { slice(beginIndex: i32, endIndex: i32 = i32.MAX_VALUE): String {
@ -334,9 +334,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
var end = endIndex < 0 ? max(endIndex + len, 0) : min(endIndex, len); var end = endIndex < 0 ? max(endIndex + len, 0) : min(endIndex, len);
len = end - begin; len = end - begin;
if (len <= 0) return changetype<String>(""); if (len <= 0) return changetype<String>("");
var out = ALLOC(len << 1); var out = runtime.allocRaw(len << 1);
memory.copy(out, changetype<usize>(this) + (<usize>begin << 1), <usize>len << 1); memory.copy(out, changetype<usize>(this) + (<usize>begin << 1), <usize>len << 1);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
split(separator: String | null = null, limit: i32 = i32.MAX_VALUE): String[] { split(separator: String | null = null, limit: i32 = i32.MAX_VALUE): String[] {
@ -354,7 +354,7 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
let buffer = unreachable(); // TODO let buffer = unreachable(); // TODO
// let buffer = <ArrayBuffer>result.buffer_; // let buffer = <ArrayBuffer>result.buffer_;
for (let i: isize = 0; i < length; ++i) { for (let i: isize = 0; i < length; ++i) {
let char = ALLOC(2); let char = runtime.allocRaw(2);
store<u16>( store<u16>(
changetype<usize>(char), changetype<usize>(char),
load<u16>( load<u16>(
@ -374,9 +374,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
while ((end = this.indexOf(separator!, start)) != -1) { while ((end = this.indexOf(separator!, start)) != -1) {
let len = end - start; let len = end - start;
if (len > 0) { if (len > 0) {
let out = ALLOC(<usize>len << 1); let out = runtime.allocRaw(<usize>len << 1);
memory.copy(out, changetype<usize>(this) + (<usize>start << 1), <usize>len << 1); memory.copy(out, changetype<usize>(this) + (<usize>start << 1), <usize>len << 1);
result.push(REGISTER<String>(out)); result.push(runtime.register<String>(out));
} else { } else {
result.push(changetype<String>("")); result.push(changetype<String>(""));
} }
@ -390,9 +390,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
} }
var len = length - start; var len = length - start;
if (len > 0) { if (len > 0) {
let out = ALLOC(<usize>len << 1); let out = runtime.allocRaw(<usize>len << 1);
memory.copy(out, changetype<usize>(this) + (<usize>start << 1), <usize>len << 1); memory.copy(out, changetype<usize>(this) + (<usize>start << 1), <usize>len << 1);
result.push(REGISTER<String>(out)); result.push(runtime.register<String>(out));
} else { } else {
result.push(changetype<String>("")); result.push(changetype<String>(""));
} }
@ -464,10 +464,10 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
} }
} }
assert(ptrPos == len); assert(ptrPos == len);
var out = ALLOC(bufPos); var out = runtime.allocRaw(bufPos);
memory.copy(changetype<usize>(out), buf, bufPos); memory.copy(changetype<usize>(out), buf, bufPos);
memory.free(buf); memory.free(buf);
return REGISTER<string>(out); return runtime.register<string>(out);
} }
toUTF8(): usize { toUTF8(): usize {

View File

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

View File

@ -1,4 +1,4 @@
import { ALLOC, REGISTER, LINK, ArrayBufferView } from "./runtime"; import { runtime, ArrayBufferView } from "./runtime";
import { COMPARATOR, SORT as SORT_IMPL } from "./util/sort"; import { COMPARATOR, SORT as SORT_IMPL } from "./util/sort";
function clampToByte(value: i32): i32 { function clampToByte(value: i32): i32 {
@ -720,11 +720,11 @@ export class Float64Array extends ArrayBufferView {
else begin = min(begin, length); else begin = min(begin, length);
if (end < 0) end = max(length + end, begin); if (end < 0) end = max(length + end, begin);
else end = max(min(end, length), begin); else end = max(min(end, length), begin);
var out = ALLOC(offsetof<TArray>()); var out = runtime.allocRaw(offsetof<TArray>());
store<usize>(out, buffer, offsetof<TArray>("buffer")); store<usize>(out, buffer, offsetof<TArray>("buffer"));
store<usize>(out, array.dataStart + (<usize>begin << alignof<T>()) , offsetof<TArray>("dataStart")); store<usize>(out, array.dataStart + (<usize>begin << alignof<T>()) , offsetof<TArray>("dataStart"));
store<usize>(out, array.dataEnd + (<usize>(end - begin) << alignof<T>()), offsetof<TArray>("dataEnd")); store<usize>(out, array.dataEnd + (<usize>(end - begin) << alignof<T>()), offsetof<TArray>("dataEnd"));
LINK(buffer, REGISTER<TArray>(out)); // register first, then link runtime.link(buffer, runtime.register<TArray>(out)); // register first, then link
return changetype<TArray>(out); return changetype<TArray>(out);
} }

View File

@ -1,4 +1,4 @@
import { ALLOC, REGISTER, FREE } from "../runtime"; import { runtime } from "../runtime";
import { CharCode } from "./string"; import { CharCode } from "./string";
@inline export const MAX_DOUBLE_LENGTH = 28; @inline export const MAX_DOUBLE_LENGTH = 28;
@ -254,10 +254,10 @@ export function utoa32(value: u32): String {
if (!value) return "0"; if (!value) return "0";
var decimals = decimalCount32(value); var decimals = decimalCount32(value);
var out = ALLOC(decimals << 1); var out = runtime.alloc(decimals << 1);
utoa32_core(changetype<usize>(out), value, decimals); utoa32_core(changetype<usize>(out), value, decimals);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
export function itoa32(value: i32): String { export function itoa32(value: i32): String {
@ -267,12 +267,12 @@ export function itoa32(value: i32): String {
if (sign) value = -value; if (sign) value = -value;
var decimals = decimalCount32(value) + <u32>sign; var decimals = decimalCount32(value) + <u32>sign;
var out = ALLOC(decimals << 1); var out = runtime.alloc(decimals << 1);
utoa32_core(changetype<usize>(out), value, decimals); utoa32_core(changetype<usize>(out), value, decimals);
if (sign) store<u16>(changetype<usize>(out), CharCode.MINUS); if (sign) store<u16>(changetype<usize>(out), CharCode.MINUS);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
export function utoa64(value: u64): String { export function utoa64(value: u64): String {
@ -282,14 +282,14 @@ export function utoa64(value: u64): String {
if (value <= u32.MAX_VALUE) { if (value <= u32.MAX_VALUE) {
let val32 = <u32>value; let val32 = <u32>value;
let decimals = decimalCount32(val32); let decimals = decimalCount32(val32);
out = ALLOC(decimals << 1); out = runtime.alloc(decimals << 1);
utoa32_core(out, val32, decimals); utoa32_core(out, val32, decimals);
} else { } else {
let decimals = decimalCount64(value); let decimals = decimalCount64(value);
out = ALLOC(decimals << 1); out = runtime.alloc(decimals << 1);
utoa64_core(changetype<usize>(out), value, decimals); utoa64_core(changetype<usize>(out), value, decimals);
} }
return REGISTER<String>(out); return runtime.register<String>(out);
} }
export function itoa64(value: i64): String { export function itoa64(value: i64): String {
@ -302,16 +302,16 @@ export function itoa64(value: i64): String {
if (<u64>value <= <u64>u32.MAX_VALUE) { if (<u64>value <= <u64>u32.MAX_VALUE) {
let val32 = <u32>value; let val32 = <u32>value;
let decimals = decimalCount32(val32) + <u32>sign; let decimals = decimalCount32(val32) + <u32>sign;
out = ALLOC(decimals << 1); out = runtime.alloc(decimals << 1);
utoa32_core(changetype<usize>(out), val32, decimals); utoa32_core(changetype<usize>(out), val32, decimals);
} else { } else {
let decimals = decimalCount64(value) + <u32>sign; let decimals = decimalCount64(value) + <u32>sign;
out = ALLOC(decimals << 1); out = runtime.alloc(decimals << 1);
utoa64_core(changetype<usize>(out), value, decimals); utoa64_core(changetype<usize>(out), value, decimals);
} }
if (sign) store<u16>(changetype<usize>(out), CharCode.MINUS); if (sign) store<u16>(changetype<usize>(out), CharCode.MINUS);
return REGISTER<String>(out); return runtime.register<String>(out);
} }
export function itoa<T>(value: T): String { export function itoa<T>(value: T): String {
@ -594,10 +594,10 @@ export function dtoa(value: f64): String {
if (isNaN(value)) return "NaN"; if (isNaN(value)) return "NaN";
return select<String>("-Infinity", "Infinity", value < 0); return select<String>("-Infinity", "Infinity", value < 0);
} }
var temp = ALLOC(MAX_DOUBLE_LENGTH << 1); var temp = runtime.alloc(MAX_DOUBLE_LENGTH << 1);
var length = dtoa_core(temp, value); var length = dtoa_core(temp, value);
var result = changetype<String>(temp).substring(0, length); var result = changetype<String>(temp).substring(0, length);
FREE(temp); runtime.free(temp);
return result; return result;
} }

View File

@ -1,9 +0,0 @@
import "allocator/arena";
var arr = new Uint8Array(8);
arr[0] = 10;
arr[4] = 5;
var arr2 = arr.sort();
arr2.forEach(x => {
trace("", 1, x);
});

View File

@ -18,7 +18,7 @@
(data (i32.const 152) "\01\00\00\00\10\00\00\00b\00a\00r\00r\00i\00e\00r\003") (data (i32.const 152) "\01\00\00\00\10\00\00\00b\00a\00r\00r\00i\00e\00r\003")
(data (i32.const 176) "\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00u\00n\00t\00i\00m\00e\00.\00t\00s") (data (i32.const 176) "\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00u\00n\00t\00i\00m\00e\00.\00t\00s")
(table $0 1 funcref) (table $0 1 funcref)
(elem (i32.const 0) $std/runtime/gc.collect) (elem (i32.const 0) $null)
(global $~lib/allocator/tlsf/ROOT (mut i32) (i32.const 0)) (global $~lib/allocator/tlsf/ROOT (mut i32) (i32.const 0))
(global $std/runtime/register_ref (mut i32) (i32.const 0)) (global $std/runtime/register_ref (mut i32) (i32.const 0))
(global $std/runtime/barrier1 (mut i32) (i32.const 0)) (global $std/runtime/barrier1 (mut i32) (i32.const 0))
@ -34,9 +34,6 @@
(global $std/runtime/ref5 (mut i32) (i32.const 0)) (global $std/runtime/ref5 (mut i32) (i32.const 0))
(export "memory" (memory $0)) (export "memory" (memory $0))
(export "table" (table $0)) (export "table" (table $0))
(export "gc.register" (func $std/runtime/gc.register))
(export "gc.link" (func $std/runtime/gc.link))
(export "gc.collect" (func $std/runtime/gc.collect))
(start $start) (start $start)
(func $~lib/allocator/tlsf/Root#setSLMap (; 2 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (func $~lib/allocator/tlsf/Root#setSLMap (; 2 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
local.get $1 local.get $1
@ -45,7 +42,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 140 i32.const 132
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -65,7 +62,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 163 i32.const 155
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -76,7 +73,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 164 i32.const 156
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -102,7 +99,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 85 i32.const 77
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -120,7 +117,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 86 i32.const 78
i32.const 11 i32.const 11
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -133,7 +130,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 424 i32.const 416
i32.const 2 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -150,7 +147,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 154 i32.const 146
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -161,7 +158,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 155 i32.const 147
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -184,7 +181,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 134 i32.const 126
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -210,7 +207,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 254 i32.const 246
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -233,7 +230,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 256 i32.const 248
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -334,7 +331,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 77 i32.const 69
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -348,7 +345,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 78 i32.const 70
i32.const 11 i32.const 11
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -364,7 +361,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 330 i32.const 322
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -376,7 +373,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 331 i32.const 323
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -389,7 +386,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 332 i32.const 324
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -411,7 +408,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 185 i32.const 177
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -425,7 +422,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 187 i32.const 179
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -449,7 +446,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 189 i32.const 181
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -461,7 +458,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 193 i32.const 185
i32.const 23 i32.const 23
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -503,7 +500,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 207 i32.const 199
i32.const 24 i32.const 24
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -517,7 +514,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 209 i32.const 201
i32.const 6 i32.const 6
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -566,7 +563,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 222 i32.const 214
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -645,7 +642,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 373 i32.const 365
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -656,7 +653,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 374 i32.const 366
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -667,7 +664,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 375 i32.const 367
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -684,7 +681,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 380 i32.const 372
i32.const 6 i32.const 6
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -712,7 +709,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 389 i32.const 381
i32.const 6 i32.const 6
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -765,7 +762,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 418 i32.const 410
i32.const 2 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -790,7 +787,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 292 i32.const 284
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -870,7 +867,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 319 i32.const 311
i32.const 16 i32.const 16
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -898,7 +895,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 344 i32.const 336
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -918,7 +915,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 345 i32.const 337
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -929,7 +926,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 346 i32.const 338
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -981,7 +978,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 364 i32.const 356
i32.const 25 i32.const 25
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -997,7 +994,7 @@
i32.const 8 i32.const 8
i32.add i32.add
) )
(func $~lib/allocator/tlsf/memory.allocate (; 16 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $~lib/allocator/tlsf/__memory_allocate (; 16 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
@ -1146,8 +1143,8 @@
else else
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 476 i32.const 465
i32.const 14 i32.const 12
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
@ -1163,8 +1160,8 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 479 i32.const 468
i32.const 4 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
@ -1183,7 +1180,7 @@
i32.clz i32.clz
i32.sub i32.sub
i32.shl i32.shl
call $~lib/allocator/tlsf/memory.allocate call $~lib/allocator/tlsf/__memory_allocate
local.tee $1 local.tee $1
i32.const -1520547049 i32.const -1520547049
i32.store i32.store
@ -1419,16 +1416,21 @@
end end
end end
) )
(func $~lib/runtime/ALLOC (; 19 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $~lib/memory/memory.fill (; 19 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
local.get $0
local.get $1
call $~lib/util/memory/memset
)
(func $~lib/runtime/ALLOC (; 20 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)
local.get $0 local.get $0
call $~lib/runtime/ALLOC_RAW call $~lib/runtime/ALLOC_RAW
local.tee $1 local.tee $1
local.get $0 local.get $0
call $~lib/util/memory/memset call $~lib/memory/memory.fill
local.get $1 local.get $1
) )
(func $~lib/util/memory/memcpy (; 20 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (func $~lib/util/memory/memcpy (; 21 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
(local $3 i32) (local $3 i32)
(local $4 i32) (local $4 i32)
(local $5 i32) (local $5 i32)
@ -2325,7 +2327,7 @@
i32.store8 i32.store8
end end
) )
(func $~lib/util/memory/memmove (; 21 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (func $~lib/util/memory/memmove (; 22 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
(local $3 i32) (local $3 i32)
(local $4 i32) (local $4 i32)
local.get $0 local.get $0
@ -2523,7 +2525,7 @@
end end
end end
) )
(func $~lib/allocator/tlsf/memory.free (; 22 ;) (type $FUNCSIG$vi) (param $0 i32) (func $~lib/allocator/tlsf/__memory_free (; 23 ;) (type $FUNCSIG$vi) (param $0 i32)
(local $1 i32) (local $1 i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
@ -2543,8 +2545,8 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 490 i32.const 479
i32.const 8 i32.const 6
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
@ -2561,10 +2563,6 @@
end end
end end
) )
(func $std/runtime/gc.register (; 23 ;) (type $FUNCSIG$vi) (param $0 i32)
local.get $0
global.set $std/runtime/register_ref
)
(func $~lib/runtime/REALLOC (; 24 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/runtime/REALLOC (; 24 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
@ -2604,7 +2602,7 @@
i32.lt_u i32.lt_u
if if
local.get $4 local.get $4
call $~lib/allocator/tlsf/memory.allocate call $~lib/allocator/tlsf/__memory_allocate
local.tee $5 local.tee $5
i32.const -1520547049 i32.const -1520547049
i32.store i32.store
@ -2627,7 +2625,7 @@
local.get $1 local.get $1
local.get $2 local.get $2
i32.sub i32.sub
call $~lib/util/memory/memset call $~lib/memory/memory.fill
local.get $3 local.get $3
i32.load i32.load
i32.const -1520547049 i32.const -1520547049
@ -2639,13 +2637,13 @@
if if
i32.const 0 i32.const 0
i32.const 184 i32.const 184
i32.const 85 i32.const 83
i32.const 8 i32.const 8
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
local.get $3 local.get $3
call $~lib/allocator/tlsf/memory.free call $~lib/allocator/tlsf/__memory_free
else else
local.get $0 local.get $0
global.set $std/runtime/register_ref global.set $std/runtime/register_ref
@ -2661,7 +2659,7 @@
local.get $1 local.get $1
local.get $2 local.get $2
i32.sub i32.sub
call $~lib/util/memory/memset call $~lib/memory/memory.fill
end end
end end
local.get $3 local.get $3
@ -2676,7 +2674,7 @@
if if
i32.const 0 i32.const 0
i32.const 184 i32.const 184
i32.const 106 i32.const 104
i32.const 2 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -2691,7 +2689,7 @@
if if
i32.const 0 i32.const 0
i32.const 184 i32.const 184
i32.const 108 i32.const 106
i32.const 2 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -2715,20 +2713,20 @@
i32.clz i32.clz
i32.sub i32.sub
i32.shl i32.shl
local.tee $1 local.tee $2
i32.const 0 i32.const 0
i32.ne i32.ne
local.tee $2 local.tee $1
if (result i32) if
local.get $1 local.get $2
i32.const 1 i32.const 1
i32.sub i32.sub
local.get $1 local.get $2
i32.and i32.and
i32.eqz i32.eqz
else local.set $1
local.get $2
end end
local.get $1
if if
local.get $0 local.get $0
i32.const 1 i32.const 1
@ -2738,7 +2736,7 @@
else else
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 38 i32.const 36
i32.const 2 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -2756,6 +2754,7 @@
i32.const 1 i32.const 1
i32.const 32 i32.const 32
global.get $std/runtime/barrier2 global.get $std/runtime/barrier2
local.tee $1
i32.const 16 i32.const 16
i32.add i32.add
i32.clz i32.clz
@ -2763,7 +2762,7 @@
i32.shl i32.shl
i32.const 1 i32.const 1
i32.const 32 i32.const 32
global.get $std/runtime/barrier2 local.get $1
i32.const 15 i32.const 15
i32.add i32.add
i32.clz i32.clz
@ -2786,6 +2785,7 @@
i32.const 1 i32.const 1
i32.const 32 i32.const 32
global.get $std/runtime/barrier3 global.get $std/runtime/barrier3
local.tee $1
i32.const 16 i32.const 16
i32.add i32.add
i32.clz i32.clz
@ -2793,7 +2793,7 @@
i32.shl i32.shl
i32.const 1 i32.const 1
i32.const 32 i32.const 32
global.get $std/runtime/barrier3 local.get $1
i32.const 15 i32.const 15
i32.add i32.add
i32.clz i32.clz
@ -2849,7 +2849,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 53 i32.const 51
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -2861,7 +2861,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 54 i32.const 52
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -2875,7 +2875,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 55 i32.const 53
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -2887,7 +2887,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 56 i32.const 54
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -2902,7 +2902,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 58 i32.const 56
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -2918,14 +2918,14 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 60 i32.const 58
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
global.get $std/runtime/ref2 global.get $std/runtime/ref2
call $~lib/runtime/unref call $~lib/runtime/unref
call $~lib/allocator/tlsf/memory.free call $~lib/allocator/tlsf/__memory_free
global.get $std/runtime/barrier2 global.get $std/runtime/barrier2
call $~lib/runtime/ALLOC call $~lib/runtime/ALLOC
global.set $std/runtime/ref3 global.set $std/runtime/ref3
@ -2935,7 +2935,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 63 i32.const 61
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -2956,7 +2956,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 67 i32.const 65
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -2972,7 +2972,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 69 i32.const 67
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -2984,7 +2984,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 70 i32.const 68
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3001,7 +3001,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 73 i32.const 71
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3017,19 +3017,16 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 74 i32.const 72
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
) )
(func $std/runtime/gc.link (; 27 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (func $start (; 27 ;) (type $FUNCSIG$v)
nop
)
(func $std/runtime/gc.collect (; 28 ;) (type $FUNCSIG$v)
nop
)
(func $start (; 29 ;) (type $FUNCSIG$v)
call $start:std/runtime call $start:std/runtime
) )
(func $null (; 28 ;) (type $FUNCSIG$v)
nop
)
) )

View File

@ -2,28 +2,20 @@ import "allocator/tlsf";
var register_ref: usize = 0; var register_ref: usize = 0;
@global namespace gc { @global function __gc_register(ref: usize): void {
export function register(ref: usize): void {
register_ref = ref; register_ref = ref;
} }
export function link(ref: usize, parentRef: usize): void {
} var link_ref: usize = 0;
export function collect(): void { var link_parentRef: usize = 0;
}
@global function __gc_link(ref: usize, parentRef: usize): void {
link_ref = ref;
link_parentRef = parentRef;
} }
import { @global function __gc_collect(): void {
HEADER, }
HEADER_SIZE,
HEADER_MAGIC,
ADJUST,
ALLOC,
REALLOC,
FREE,
REGISTER,
ArrayBufferBase,
StringBase
} from "runtime";
class A {} class A {}
class B {} class B {}
@ -33,42 +25,42 @@ function isPowerOf2(x: i32): bool {
return x != 0 && (x & (x - 1)) == 0; return x != 0 && (x & (x - 1)) == 0;
} }
assert(ADJUST(0) > 0); assert(runtime.adjust(0) > 0);
for (let i = 0; i < 9000; ++i) { for (let i = 0; i < 9000; ++i) {
assert(isPowerOf2(ADJUST(i))); assert(isPowerOf2(runtime.adjust(i)));
} }
var barrier1 = ADJUST(0); var barrier1 = runtime.adjust(0);
var barrier2 = barrier1 + 1; var barrier2 = barrier1 + 1;
while (ADJUST(barrier2 + 1) == ADJUST(barrier2)) ++barrier2; while (runtime.adjust(barrier2 + 1) == runtime.adjust(barrier2)) ++barrier2;
var barrier3 = barrier2 + 1; var barrier3 = barrier2 + 1;
while (ADJUST(barrier3 + 1) == ADJUST(barrier3)) ++barrier3; while (runtime.adjust(barrier3 + 1) == runtime.adjust(barrier3)) ++barrier3;
trace("barrier1", 1, barrier1); trace("barrier1", 1, barrier1);
trace("barrier2", 1, barrier2); trace("barrier2", 1, barrier2);
trace("barrier3", 1, barrier3); trace("barrier3", 1, barrier3);
var ref1 = ALLOC(1); var ref1 = runtime.alloc(1);
var header1 = changetype<HEADER>(ref1 - HEADER_SIZE); var header1 = changetype<runtime.Header>(ref1 - runtime.Header.SIZE);
assert(header1.classId == HEADER_MAGIC); assert(header1.classId == runtime.Header.MAGIC);
assert(header1.payloadSize == 1); assert(header1.payloadSize == 1);
assert(ref1 == REALLOC(ref1, barrier1)); // same segment assert(ref1 == runtime.realloc(ref1, barrier1)); // same segment
assert(header1.payloadSize == barrier1); assert(header1.payloadSize == barrier1);
var ref2 = REALLOC(ref1, barrier2); var ref2 = runtime.realloc(ref1, barrier2);
assert(ref1 != ref2); // moves assert(ref1 != ref2); // moves
var header2 = changetype<HEADER>(ref2 - HEADER_SIZE); var header2 = changetype<runtime.Header>(ref2 - runtime.Header.SIZE);
assert(header2.payloadSize == barrier2); assert(header2.payloadSize == barrier2);
FREE(ref2); runtime.free(ref2);
var ref3 = ALLOC(barrier2); var ref3 = runtime.alloc(barrier2);
assert(ref1 == ref3); // reuses space of ref1 (free'd in realloc), ref2 (explicitly free'd) assert(ref1 == ref3); // reuses space of ref1 (free'd in realloc), ref2 (explicitly free'd)
var ref4 = ALLOC(barrier1); var ref4 = runtime.alloc(barrier1);
REGISTER<A>(ref4); // should call __REGISTER_IMPL runtime.register<A>(ref4); // should call __REGISTER_IMPL
assert(register_ref == ref4); assert(register_ref == ref4);
var header4 = changetype<HEADER>(register_ref - HEADER_SIZE); var header4 = changetype<runtime.Header>(register_ref - runtime.Header.SIZE);
assert(header4.classId == gc.classId<A>()); assert(header4.classId == gc.classId<A>());
assert(header4.payloadSize == barrier1); assert(header4.payloadSize == barrier1);
var ref5 = ALLOC(10); var ref5 = runtime.alloc(10);
assert(changetype<ArrayBufferBase>(ref5).byteLength == 10); assert(changetype<ArrayBuffer>(ref5).byteLength == 10);
assert(changetype<StringBase>(ref5).length == 5); assert(changetype<String>(ref5).length == 5);

View File

@ -37,6 +37,7 @@
(global $~lib/allocator/tlsf/Root.SIZE i32 (i32.const 2916)) (global $~lib/allocator/tlsf/Root.SIZE i32 (i32.const 2916))
(global $~lib/allocator/tlsf/ROOT (mut i32) (i32.const 0)) (global $~lib/allocator/tlsf/ROOT (mut i32) (i32.const 0))
(global $std/runtime/register_ref (mut i32) (i32.const 0)) (global $std/runtime/register_ref (mut i32) (i32.const 0))
(global $~lib/gc/gc.implemented i32 (i32.const 1))
(global $std/runtime/barrier1 (mut i32) (i32.const 0)) (global $std/runtime/barrier1 (mut i32) (i32.const 0))
(global $std/runtime/barrier2 (mut i32) (i32.const 0)) (global $std/runtime/barrier2 (mut i32) (i32.const 0))
(global $std/runtime/barrier3 (mut i32) (i32.const 0)) (global $std/runtime/barrier3 (mut i32) (i32.const 0))
@ -48,12 +49,9 @@
(global $std/runtime/ref4 (mut i32) (i32.const 0)) (global $std/runtime/ref4 (mut i32) (i32.const 0))
(global $std/runtime/header4 (mut i32) (i32.const 0)) (global $std/runtime/header4 (mut i32) (i32.const 0))
(global $std/runtime/ref5 (mut i32) (i32.const 0)) (global $std/runtime/ref5 (mut i32) (i32.const 0))
(global $~lib/runtime/HEAP_BASE i32 (i32.const 216)) (global $~lib/memory/HEAP_BASE i32 (i32.const 216))
(export "memory" (memory $0)) (export "memory" (memory $0))
(export "table" (table $0)) (export "table" (table $0))
(export "gc.register" (func $std/runtime/gc.register))
(export "gc.link" (func $std/runtime/gc.link))
(export "gc.collect" (func $std/runtime/gc.collect))
(start $start) (start $start)
(func $start:~lib/allocator/tlsf (; 2 ;) (type $FUNCSIG$v) (func $start:~lib/allocator/tlsf (; 2 ;) (type $FUNCSIG$v)
i32.const 1 i32.const 1
@ -65,7 +63,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 118 i32.const 110
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -114,7 +112,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 140 i32.const 132
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -135,7 +133,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 163 i32.const 155
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -147,7 +145,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 164 i32.const 156
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -180,7 +178,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 85 i32.const 77
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -200,7 +198,7 @@
if (result i32) if (result i32)
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 86 i32.const 78
i32.const 11 i32.const 11
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -216,7 +214,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 424 i32.const 416
i32.const 2 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -234,7 +232,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 154 i32.const 146
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -246,7 +244,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 155 i32.const 147
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -270,7 +268,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 134 i32.const 126
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -300,7 +298,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 254 i32.const 246
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -326,7 +324,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 256 i32.const 248
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -437,7 +435,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 77 i32.const 69
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -451,7 +449,7 @@
if (result i32) if (result i32)
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 78 i32.const 70
i32.const 11 i32.const 11
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -468,7 +466,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 330 i32.const 322
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -481,7 +479,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 331 i32.const 323
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -494,7 +492,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 332 i32.const 324
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -520,7 +518,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 185 i32.const 177
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -535,7 +533,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 187 i32.const 179
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -561,7 +559,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 189 i32.const 181
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -573,7 +571,7 @@
if (result i32) if (result i32)
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 193 i32.const 185
i32.const 23 i32.const 23
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -621,7 +619,7 @@
if (result i32) if (result i32)
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 207 i32.const 199
i32.const 24 i32.const 24
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -639,7 +637,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 209 i32.const 201
i32.const 6 i32.const 6
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -694,7 +692,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 222 i32.const 214
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -785,7 +783,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 373 i32.const 365
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -798,7 +796,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 374 i32.const 366
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -811,7 +809,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 375 i32.const 367
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -832,7 +830,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 380 i32.const 372
i32.const 6 i32.const 6
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -861,7 +859,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 389 i32.const 381
i32.const 6 i32.const 6
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -932,7 +930,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 418 i32.const 410
i32.const 2 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -948,7 +946,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 418 i32.const 410
i32.const 2 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -978,7 +976,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 292 i32.const 284
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -1074,7 +1072,7 @@
else else
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 319 i32.const 311
i32.const 16 i32.const 16
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -1111,7 +1109,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 344 i32.const 336
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -1131,7 +1129,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 345 i32.const 337
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -1144,7 +1142,7 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 346 i32.const 338
i32.const 4 i32.const 4
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -1204,7 +1202,7 @@
if (result i32) if (result i32)
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 364 i32.const 356
i32.const 25 i32.const 25
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -1225,7 +1223,7 @@
global.get $~lib/allocator/tlsf/Block.INFO global.get $~lib/allocator/tlsf/Block.INFO
i32.add i32.add
) )
(func $~lib/allocator/tlsf/memory.allocate (; 22 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $~lib/allocator/tlsf/__memory_allocate (; 22 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
@ -1238,7 +1236,7 @@
local.get $1 local.get $1
i32.eqz i32.eqz
if if
global.get $~lib/runtime/HEAP_BASE global.get $~lib/memory/HEAP_BASE
i32.const 7 i32.const 7
i32.add i32.add
i32.const 7 i32.const 7
@ -1430,8 +1428,8 @@
if (result i32) if (result i32)
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 476 i32.const 465
i32.const 14 i32.const 12
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
else else
@ -1451,8 +1449,8 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 479 i32.const 468
i32.const 4 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
@ -1461,11 +1459,16 @@
local.get $0 local.get $0
call $~lib/allocator/tlsf/Root#use call $~lib/allocator/tlsf/Root#use
) )
(func $~lib/runtime/ALLOC_RAW (; 23 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $~lib/memory/memory.allocate (; 23 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
local.get $0
call $~lib/allocator/tlsf/__memory_allocate
return
)
(func $~lib/runtime/ALLOC_RAW (; 24 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)
local.get $0 local.get $0
call $~lib/runtime/ADJUST call $~lib/runtime/ADJUST
call $~lib/allocator/tlsf/memory.allocate call $~lib/memory/memory.allocate
local.set $1 local.set $1
local.get $1 local.get $1
i32.const -1520547049 i32.const -1520547049
@ -1483,7 +1486,7 @@
i32.const 16 i32.const 16
i32.add i32.add
) )
(func $~lib/util/memory/memset (; 24 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (func $~lib/util/memory/memset (; 25 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
(local $3 i32) (local $3 i32)
(local $4 i32) (local $4 i32)
(local $5 i64) (local $5 i64)
@ -1737,29 +1740,24 @@
end end
end end
) )
(func $~lib/runtime/ALLOC (; 25 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $~lib/memory/memory.fill (; 26 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
local.get $0
local.get $1
local.get $2
call $~lib/util/memory/memset
)
(func $~lib/runtime/ALLOC (; 27 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
local.get $0 local.get $0
call $~lib/runtime/ALLOC_RAW call $~lib/runtime/ALLOC_RAW
local.set $1 local.set $1
block $~lib/runtime/memory.fill|inlined.0
local.get $1 local.get $1
local.set $2
i32.const 0 i32.const 0
local.set $3
local.get $0 local.get $0
local.set $4 call $~lib/memory/memory.fill
local.get $2
local.get $3
local.get $4
call $~lib/util/memory/memset
end
local.get $1 local.get $1
) )
(func $~lib/util/memory/memcpy (; 26 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (func $~lib/util/memory/memcpy (; 28 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
(local $3 i32) (local $3 i32)
(local $4 i32) (local $4 i32)
(local $5 i32) (local $5 i32)
@ -2960,7 +2958,7 @@
i32.store8 i32.store8
end end
) )
(func $~lib/util/memory/memmove (; 27 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (func $~lib/util/memory/memmove (; 29 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
(local $3 i32) (local $3 i32)
local.get $0 local.get $0
local.get $1 local.get $1
@ -3187,7 +3185,13 @@
end end
end end
) )
(func $~lib/allocator/tlsf/memory.free (; 28 ;) (type $FUNCSIG$vi) (param $0 i32) (func $~lib/memory/memory.copy (; 30 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
local.get $0
local.get $1
local.get $2
call $~lib/util/memory/memmove
)
(func $~lib/allocator/tlsf/__memory_free (; 31 ;) (type $FUNCSIG$vi) (param $0 i32)
(local $1 i32) (local $1 i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
@ -3212,8 +3216,8 @@
if if
i32.const 0 i32.const 0
i32.const 16 i32.const 16
i32.const 490 i32.const 479
i32.const 8 i32.const 6
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
@ -3230,19 +3234,24 @@
end end
end end
) )
(func $std/runtime/gc.register (; 29 ;) (type $FUNCSIG$vi) (param $0 i32) (func $~lib/memory/memory.free (; 32 ;) (type $FUNCSIG$vi) (param $0 i32)
local.get $0
call $~lib/allocator/tlsf/__memory_free
)
(func $std/runtime/__gc_register (; 33 ;) (type $FUNCSIG$vi) (param $0 i32)
local.get $0 local.get $0
global.set $std/runtime/register_ref global.set $std/runtime/register_ref
) )
(func $~lib/runtime/REALLOC (; 30 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/gc/gc.register (; 34 ;) (type $FUNCSIG$vi) (param $0 i32)
local.get $0
call $std/runtime/__gc_register
)
(func $~lib/runtime/REALLOC (; 35 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
(local $4 i32) (local $4 i32)
(local $5 i32) (local $5 i32)
(local $6 i32) (local $6 i32)
(local $7 i32)
(local $8 i32)
(local $9 i32)
local.get $0 local.get $0
i32.const 16 i32.const 16
i32.sub i32.sub
@ -3261,14 +3270,14 @@
call $~lib/runtime/ADJUST call $~lib/runtime/ADJUST
i32.const 0 i32.const 0
local.get $0 local.get $0
global.get $~lib/runtime/HEAP_BASE global.get $~lib/memory/HEAP_BASE
i32.gt_u i32.gt_u
select select
local.get $4 local.get $4
i32.lt_u i32.lt_u
if if
local.get $4 local.get $4
call $~lib/allocator/tlsf/memory.allocate call $~lib/memory/memory.allocate
local.set $5 local.set $5
local.get $5 local.get $5
i32.const -1520547049 i32.const -1520547049
@ -3283,56 +3292,40 @@
i32.const 16 i32.const 16
i32.add i32.add
local.set $6 local.set $6
block $~lib/runtime/memory.copy|inlined.0
local.get $6 local.get $6
local.set $7
local.get $0 local.get $0
local.set $8
local.get $3 local.get $3
local.set $9 call $~lib/memory/memory.copy
local.get $7
local.get $8
local.get $9
call $~lib/util/memory/memmove
end
block $~lib/runtime/memory.fill|inlined.1
local.get $6 local.get $6
local.get $3 local.get $3
i32.add i32.add
local.set $9
i32.const 0 i32.const 0
local.set $8
local.get $1 local.get $1
local.get $3 local.get $3
i32.sub i32.sub
local.set $7 call $~lib/memory/memory.fill
local.get $9
local.get $8
local.get $7
call $~lib/util/memory/memset
end
local.get $2 local.get $2
i32.load i32.load
i32.const -1520547049 i32.const -1520547049
i32.eq i32.eq
if if
local.get $0 local.get $0
global.get $~lib/runtime/HEAP_BASE global.get $~lib/memory/HEAP_BASE
i32.gt_u i32.gt_u
i32.eqz i32.eqz
if if
i32.const 0 i32.const 0
i32.const 184 i32.const 184
i32.const 85 i32.const 83
i32.const 8 i32.const 8
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
local.get $2 local.get $2
call $~lib/allocator/tlsf/memory.free call $~lib/memory/memory.free
else else
local.get $0 local.get $0
call $std/runtime/gc.register call $~lib/gc/gc.register
end end
local.get $5 local.get $5
local.set $2 local.set $2
@ -3342,17 +3335,11 @@
local.get $0 local.get $0
local.get $3 local.get $3
i32.add i32.add
local.set $6
i32.const 0 i32.const 0
local.set $5
local.get $1 local.get $1
local.get $3 local.get $3
i32.sub i32.sub
local.set $7 call $~lib/memory/memory.fill
local.get $6
local.get $5
local.get $7
call $~lib/util/memory/memset
end end
else else
nop nop
@ -3362,10 +3349,10 @@
i32.store offset=4 i32.store offset=4
local.get $0 local.get $0
) )
(func $~lib/runtime/unref (; 31 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $~lib/runtime/unref (; 36 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)
local.get $0 local.get $0
global.get $~lib/runtime/HEAP_BASE global.get $~lib/memory/HEAP_BASE
i32.const 16 i32.const 16
i32.add i32.add
i32.ge_u i32.ge_u
@ -3373,7 +3360,7 @@
if if
i32.const 0 i32.const 0
i32.const 184 i32.const 184
i32.const 106 i32.const 104
i32.const 2 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3390,25 +3377,25 @@
if if
i32.const 0 i32.const 0
i32.const 184 i32.const 184
i32.const 108 i32.const 106
i32.const 2 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
local.get $1 local.get $1
) )
(func $~lib/runtime/FREE<usize> (; 32 ;) (type $FUNCSIG$vi) (param $0 i32) (func $~lib/runtime/FREE<usize> (; 37 ;) (type $FUNCSIG$vi) (param $0 i32)
local.get $0 local.get $0
call $~lib/runtime/unref call $~lib/runtime/unref
call $~lib/allocator/tlsf/memory.free call $~lib/memory/memory.free
) )
(func $~lib/runtime/ArrayBufferBase#get:byteLength (; 33 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $~lib/arraybuffer/ArrayBuffer#get:byteLength (; 38 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
local.get $0 local.get $0
i32.const 16 i32.const 16
i32.sub i32.sub
i32.load offset=4 i32.load offset=4
) )
(func $~lib/runtime/StringBase#get:length (; 34 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $~lib/string/String#get:length (; 39 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
local.get $0 local.get $0
i32.const 16 i32.const 16
i32.sub i32.sub
@ -3416,7 +3403,7 @@
i32.const 1 i32.const 1
i32.shr_u i32.shr_u
) )
(func $start:std/runtime (; 35 ;) (type $FUNCSIG$v) (func $start:std/runtime (; 40 ;) (type $FUNCSIG$v)
(local $0 i32) (local $0 i32)
call $start:~lib/allocator/tlsf call $start:~lib/allocator/tlsf
i32.const 2 i32.const 2
@ -3426,7 +3413,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 30 i32.const 28
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3439,7 +3426,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 36 i32.const 34
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3460,7 +3447,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 38 i32.const 36
i32.const 2 i32.const 2
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3563,7 +3550,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 53 i32.const 51
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3576,7 +3563,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 54 i32.const 52
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3590,7 +3577,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 55 i32.const 53
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3603,7 +3590,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 56 i32.const 54
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3619,7 +3606,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 58 i32.const 56
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3636,7 +3623,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 60 i32.const 58
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3653,7 +3640,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 63 i32.const 61
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3669,7 +3656,7 @@
i32.const 2 i32.const 2
i32.store i32.store
local.get $0 local.get $0
call $std/runtime/gc.register call $~lib/gc/gc.register
local.get $0 local.get $0
end end
drop drop
@ -3680,7 +3667,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 67 i32.const 65
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3697,7 +3684,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 69 i32.const 67
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3710,7 +3697,7 @@
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 70 i32.const 68
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
@ -3719,41 +3706,35 @@
call $~lib/runtime/ALLOC call $~lib/runtime/ALLOC
global.set $std/runtime/ref5 global.set $std/runtime/ref5
global.get $std/runtime/ref5 global.get $std/runtime/ref5
call $~lib/runtime/ArrayBufferBase#get:byteLength call $~lib/arraybuffer/ArrayBuffer#get:byteLength
i32.const 10 i32.const 10
i32.eq i32.eq
i32.eqz i32.eqz
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 73 i32.const 71
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
global.get $std/runtime/ref5 global.get $std/runtime/ref5
call $~lib/runtime/StringBase#get:length call $~lib/string/String#get:length
i32.const 5 i32.const 5
i32.eq i32.eq
i32.eqz i32.eqz
if if
i32.const 0 i32.const 0
i32.const 72 i32.const 72
i32.const 74 i32.const 72
i32.const 0 i32.const 0
call $~lib/env/abort call $~lib/env/abort
unreachable unreachable
end end
) )
(func $std/runtime/gc.link (; 36 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (func $start (; 41 ;) (type $FUNCSIG$v)
nop
)
(func $std/runtime/gc.collect (; 37 ;) (type $FUNCSIG$v)
nop
)
(func $start (; 38 ;) (type $FUNCSIG$v)
call $start:std/runtime call $start:std/runtime
) )
(func $null (; 39 ;) (type $FUNCSIG$v) (func $null (; 42 ;) (type $FUNCSIG$v)
) )
) )