mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-14 07:21:30 +00:00
take a step back
This commit is contained in:
@ -43,10 +43,9 @@
|
||||
"scripts": {
|
||||
"build": "webpack --mode production --display-modules",
|
||||
"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: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:parser": "node tests/parser",
|
||||
"test:compiler": "node tests/compiler",
|
||||
|
@ -1163,8 +1163,7 @@ export enum DecoratorKind {
|
||||
BUILTIN,
|
||||
LAZY,
|
||||
START,
|
||||
UNSAFE,
|
||||
STUB
|
||||
UNSAFE
|
||||
}
|
||||
|
||||
/** 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: {
|
||||
if (nameStr == "sealed") return DecoratorKind.SEALED;
|
||||
if (nameStr == "start") return DecoratorKind.START;
|
||||
if (nameStr == "stub") return DecoratorKind.STUB;
|
||||
break;
|
||||
}
|
||||
case CharCode.u: {
|
||||
|
@ -21,8 +21,7 @@ import {
|
||||
LiteralKind,
|
||||
LiteralExpression,
|
||||
StringLiteralExpression,
|
||||
CallExpression,
|
||||
ElementAccessExpression
|
||||
CallExpression
|
||||
} from "./ast";
|
||||
|
||||
import {
|
||||
@ -100,7 +99,6 @@ export namespace BuiltinSymbols {
|
||||
export const isFunction = "~lib/builtins/isFunction";
|
||||
export const isNullable = "~lib/builtins/isNullable";
|
||||
export const isDefined = "~lib/builtins/isDefined";
|
||||
export const isImplemented = "~lib/builtins/isImplemented";
|
||||
export const isConstant = "~lib/builtins/isConstant";
|
||||
export const isManaged = "~lib/builtins/isManaged";
|
||||
|
||||
@ -472,14 +470,16 @@ export namespace BuiltinSymbols {
|
||||
export const WARNING = "~lib/diagnostics/WARNING";
|
||||
export const INFO = "~lib/diagnostics/INFO";
|
||||
|
||||
// std/runtime.ts
|
||||
export const HEAP_BASE = "~lib/runtime/HEAP_BASE";
|
||||
export const memory_size = "~lib/runtime/memory.size";
|
||||
export const memory_grow = "~lib/runtime/memory.grow";
|
||||
export const memory_copy = "~lib/runtime/memory.copy";
|
||||
export const memory_fill = "~lib/runtime/memory.fill";
|
||||
export const gc_classId = "~lib/runtime/gc.classId";
|
||||
export const gc_iterateRoots = "~lib/runtime/gc.iterateRoots";
|
||||
// std/memory.ts
|
||||
export const HEAP_BASE = "~lib/memory/HEAP_BASE";
|
||||
export const memory_size = "~lib/memory/memory.size";
|
||||
export const memory_grow = "~lib/memory/memory.grow";
|
||||
export const memory_copy = "~lib/memory/memory.copy";
|
||||
export const memory_fill = "~lib/memory/memory.fill";
|
||||
|
||||
// std/gc.ts
|
||||
export const gc_classId = "~lib/gc/gc.classId";
|
||||
export const gc_iterateRoots = "~lib/gc/gc.iterateRoots";
|
||||
|
||||
// std/typedarray.ts
|
||||
export const Int8Array = "~lib/typedarray/Int8Array";
|
||||
@ -621,20 +621,6 @@ export function compileCall(
|
||||
);
|
||||
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
|
||||
compiler.currentType = Type.bool;
|
||||
if (
|
||||
|
@ -930,7 +930,7 @@ export class Program extends DiagnosticEmitter {
|
||||
ensureGlobal(name: string, element: DeclaredElement): DeclaredElement {
|
||||
var elementsByName = this.elementsByName;
|
||||
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
|
||||
// their respective namespaces in std/builtins, but can also trigger when a
|
||||
// user has multiple global elements of the same name in different files,
|
||||
@ -1176,7 +1176,7 @@ export class Program extends DiagnosticEmitter {
|
||||
): void {
|
||||
var name = declaration.name.text;
|
||||
var isStatic = declaration.is(CommonFlags.STATIC);
|
||||
var acceptedFlags = DecoratorFlags.INLINE;
|
||||
var acceptedFlags = DecoratorFlags.INLINE | DecoratorFlags.UNSAFE;
|
||||
if (!declaration.is(CommonFlags.GENERIC)) {
|
||||
acceptedFlags |= DecoratorFlags.OPERATOR_BINARY
|
||||
| DecoratorFlags.OPERATOR_PREFIX
|
||||
@ -1549,7 +1549,7 @@ export class Program extends DiagnosticEmitter {
|
||||
parent: Element
|
||||
): void {
|
||||
var name = declaration.name.text;
|
||||
var validDecorators = DecoratorFlags.UNSAFE | DecoratorFlags.STUB;
|
||||
var validDecorators = DecoratorFlags.UNSAFE;
|
||||
if (declaration.is(CommonFlags.AMBIENT)) {
|
||||
validDecorators |= DecoratorFlags.EXTERNAL;
|
||||
} else {
|
||||
@ -1791,9 +1791,7 @@ export enum DecoratorFlags {
|
||||
/** Is the explicit start function. */
|
||||
START = 1 << 10,
|
||||
/** Is considered unsafe code. */
|
||||
UNSAFE = 1 << 11,
|
||||
/** Is a stub that can be overridden. */
|
||||
STUB = 1 << 12
|
||||
UNSAFE = 1 << 11
|
||||
}
|
||||
|
||||
/** 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.START: return DecoratorFlags.START;
|
||||
case DecoratorKind.UNSAFE: return DecoratorFlags.UNSAFE;
|
||||
case DecoratorKind.STUB: return DecoratorFlags.STUB;
|
||||
default: return DecoratorFlags.NONE;
|
||||
}
|
||||
}
|
||||
@ -1888,8 +1885,8 @@ export abstract class Element {
|
||||
if (!members) this.members = members = new Map();
|
||||
else if (members.has(name)) {
|
||||
let actual = members.get(name)!;
|
||||
if (actual.parent !== this || actual.hasDecorator(DecoratorFlags.STUB)) {
|
||||
// override non-own or stub element
|
||||
if (actual.parent !== this) {
|
||||
// override non-own element
|
||||
} else {
|
||||
let merged = tryMerge(actual, element);
|
||||
if (merged) {
|
||||
|
@ -1,21 +1,10 @@
|
||||
/**
|
||||
* 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 { HEAP_BASE, memory } from "../memory";
|
||||
import { AL_MASK, MAX_SIZE_32 } from "../util/allocator";
|
||||
|
||||
var startOffset: usize = (HEAP_BASE + AL_MASK) & ~AL_MASK;
|
||||
var offset: usize = startOffset;
|
||||
@lazy var startOffset: usize = (HEAP_BASE + AL_MASK) & ~AL_MASK;
|
||||
@lazy var offset: usize = startOffset;
|
||||
|
||||
// Memory allocator implementation
|
||||
@global namespace memory {
|
||||
|
||||
export function allocate(size: usize): usize {
|
||||
@unsafe @global function __memory_allocate(size: usize): usize {
|
||||
if (size > MAX_SIZE_32) unreachable();
|
||||
var ptr = offset;
|
||||
var newPtr = (ptr + max<usize>(size, 1) + AL_MASK) & ~AL_MASK;
|
||||
@ -33,9 +22,9 @@ var offset: usize = startOffset;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
@ -1,24 +1,10 @@
|
||||
/**
|
||||
* Emscripten Memory Allocator.
|
||||
*
|
||||
* 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
|
||||
*//***/
|
||||
@unsafe declare function _malloc(size: usize): usize;
|
||||
@unsafe declare function _free(ptr: usize): void;
|
||||
|
||||
declare function _malloc(size: usize): usize;
|
||||
declare function _free(ptr: usize): void;
|
||||
|
||||
// Memory allocator implementation
|
||||
@global namespace memory {
|
||||
|
||||
@inline export function allocate(size: usize): usize {
|
||||
@unsafe @global function __memory_allocate(size: usize): usize {
|
||||
return _malloc(size);
|
||||
}
|
||||
|
||||
@inline export function free(ptr: usize): void {
|
||||
@unsafe @global function __memory_free(ptr: usize): void {
|
||||
_free(ptr);
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,10 @@
|
||||
/**
|
||||
* System Memory Allocator.
|
||||
*
|
||||
* 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
|
||||
*//***/
|
||||
@unsafe declare function malloc(size: usize): usize;
|
||||
@unsafe declare function free(ptr: usize): void;
|
||||
|
||||
declare function malloc(size: usize): usize;
|
||||
declare function free(ptr: usize): void;
|
||||
|
||||
// Memory allocator interface
|
||||
@global namespace memory {
|
||||
|
||||
@inline export function allocate(size: usize): usize {
|
||||
@unsafe @global function __memory_allocate(size: usize): usize {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
@inline export function free(ptr: usize): void {
|
||||
@unsafe @global function __memory_free(ptr: usize): void {
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
|
@ -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) ═════════════╕
|
||||
// 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
|
||||
@ -16,6 +7,7 @@
|
||||
// FL: first level, SL: second level, AL: alignment, SB: small block
|
||||
|
||||
import { AL_BITS, AL_SIZE, AL_MASK } from "../util/allocator";
|
||||
import { HEAP_BASE, memory } from "../memory";
|
||||
|
||||
const SL_BITS: u32 = 5;
|
||||
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. */
|
||||
var ROOT: Root = changetype<Root>(0);
|
||||
|
||||
// Memory allocator interface
|
||||
@global namespace memory {
|
||||
|
||||
/** Allocates a chunk of memory. */
|
||||
export function allocate(size: usize): usize {
|
||||
@unsafe @global function __memory_allocate(size: usize): usize {
|
||||
// initialize if necessary
|
||||
var root = ROOT;
|
||||
if (!root) {
|
||||
@ -481,7 +470,7 @@ var ROOT: Root = changetype<Root>(0);
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
let root = ROOT;
|
||||
if (root) {
|
||||
@ -493,4 +482,3 @@ var ROOT: Root = changetype<Root>(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ALLOC, REALLOC, REGISTER, LINK, FREE, ArrayBufferView } from "./runtime";
|
||||
import { runtime, ArrayBufferView } from "./runtime";
|
||||
import { ArrayBuffer } from "./arraybuffer";
|
||||
import { COMPARATOR, SORT } from "./util/sort";
|
||||
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>();
|
||||
if (<u32>length > <u32>MAX_LENGTH) throw new RangeError("Invalid array length");
|
||||
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)) {
|
||||
this.data = changetype<ArrayBuffer>(newData); // links
|
||||
this.dataStart = newData;
|
||||
@ -76,7 +76,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
private __set(index: i32, value: T): void {
|
||||
this.resize(index + 1);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
this.resize(newLength);
|
||||
this.length_ = newLength;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -156,14 +156,14 @@ export class Array<T> extends ArrayBufferView {
|
||||
for (let offset: usize = 0; offset < thisSize; offset += sizeof<T>()) {
|
||||
let element = load<T>(thisStart + offset);
|
||||
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 otherSize = <usize>otherLen << alignof<T>();
|
||||
for (let offset: usize = 0; offset < otherSize; offset += sizeof<T>()) {
|
||||
let element = load<T>(otherStart + offset);
|
||||
store<T>(outStart + thisSize + offset, element);
|
||||
LINK(changetype<usize>(element), changetype<usize>(out));
|
||||
runtime.link(changetype<usize>(element), changetype<usize>(out));
|
||||
}
|
||||
} else {
|
||||
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 result = callbackfn(value, index, this);
|
||||
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;
|
||||
}
|
||||
@ -290,7 +290,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
<usize>(newLength - 1) << alignof<T>()
|
||||
);
|
||||
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;
|
||||
return newLength;
|
||||
}
|
||||
@ -307,7 +307,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
let offset = <usize>i << alignof<T>();
|
||||
let element = load<T>(thisBase + offset);
|
||||
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;
|
||||
}
|
||||
@ -323,7 +323,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
for (let i = 0; i < deleteCount; ++i) {
|
||||
let element = load<T>(thisBase + (<usize>i << alignof<T>()));
|
||||
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(
|
||||
splice.dataStart,
|
||||
@ -395,7 +395,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
var sepLen = separator.length;
|
||||
var valueLen = 5; // max possible length of element len("false")
|
||||
var estLen = (valueLen + sepLen) * lastIndex + valueLen;
|
||||
var result = ALLOC(estLen << 1);
|
||||
var result = runtime.alloc(estLen << 1);
|
||||
var offset = 0;
|
||||
var value: bool;
|
||||
for (let i = 0; i < lastIndex; ++i) {
|
||||
@ -427,10 +427,10 @@ export class Array<T> extends ArrayBufferView {
|
||||
|
||||
if (estLen > offset) {
|
||||
let trimmed = changetype<string>(result).substring(0, offset);
|
||||
FREE(result);
|
||||
runtime.free(result);
|
||||
return trimmed; // registered in .substring
|
||||
}
|
||||
return REGISTER<string>(result);
|
||||
return runtime.register<string>(result);
|
||||
}
|
||||
|
||||
private join_int(separator: string = ","): string {
|
||||
@ -442,7 +442,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
var sepLen = separator.length;
|
||||
const valueLen = (sizeof<T>() <= 4 ? 10 : 20) + <i32>isSigned<T>();
|
||||
var estLen = (valueLen + sepLen) * lastIndex + valueLen;
|
||||
var result = ALLOC(estLen << 1);
|
||||
var result = runtime.alloc(estLen << 1);
|
||||
var offset = 0;
|
||||
var value: T;
|
||||
for (let i = 0; i < lastIndex; ++i) {
|
||||
@ -461,10 +461,10 @@ export class Array<T> extends ArrayBufferView {
|
||||
offset += itoa_stream<T>(result, offset, value);
|
||||
if (estLen > offset) {
|
||||
let trimmed = changetype<string>(result).substring(0, offset);
|
||||
FREE(result);
|
||||
runtime.free(result);
|
||||
return trimmed; // registered in .substring
|
||||
}
|
||||
return REGISTER<string>(result);
|
||||
return runtime.register<string>(result);
|
||||
}
|
||||
|
||||
private join_flt(separator: string = ","): string {
|
||||
@ -476,7 +476,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
const valueLen = MAX_DOUBLE_LENGTH;
|
||||
var sepLen = separator.length;
|
||||
var estLen = (valueLen + sepLen) * lastIndex + valueLen;
|
||||
var result = ALLOC(estLen << 1);
|
||||
var result = runtime.alloc(estLen << 1);
|
||||
var offset = 0;
|
||||
var value: T;
|
||||
for (let i = 0; i < lastIndex; ++i) {
|
||||
@ -495,10 +495,10 @@ export class Array<T> extends ArrayBufferView {
|
||||
offset += dtoa_stream(result, offset, value);
|
||||
if (estLen > offset) {
|
||||
let trimmed = changetype<string>(result).substring(0, offset);
|
||||
FREE(result);
|
||||
runtime.free(result);
|
||||
return trimmed; // registered in .substring
|
||||
}
|
||||
return REGISTER<string>(result);
|
||||
return runtime.register<string>(result);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
var offset = 0;
|
||||
var result = ALLOC((estLen + sepLen * lastIndex) << 1);
|
||||
var result = runtime.alloc((estLen + sepLen * lastIndex) << 1);
|
||||
var value: String;
|
||||
for (let i = 0; i < lastIndex; ++i) {
|
||||
value = load<string>(dataStart + (<usize>i << alignof<T>()));
|
||||
@ -544,7 +544,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
<usize>valueLen << 1
|
||||
);
|
||||
}
|
||||
return REGISTER<string>(result);
|
||||
return runtime.register<string>(result);
|
||||
}
|
||||
|
||||
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]")
|
||||
var sepLen = separator.length;
|
||||
var estLen = (valueLen + sepLen) * lastIndex + valueLen;
|
||||
var result = ALLOC(estLen << 1);
|
||||
var result = runtime.alloc(estLen << 1);
|
||||
var offset = 0;
|
||||
var value: T;
|
||||
for (let i = 0; i < lastIndex; ++i) {
|
||||
@ -610,10 +610,10 @@ export class Array<T> extends ArrayBufferView {
|
||||
}
|
||||
if (estLen > offset) {
|
||||
let out = changetype<string>(result).substring(0, offset);
|
||||
FREE(result);
|
||||
runtime.free(result);
|
||||
return out; // registered in .substring
|
||||
}
|
||||
return REGISTER<string>(result);
|
||||
return runtime.register<string>(result);
|
||||
}
|
||||
|
||||
@inline
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { HEADER, ALLOC_RAW, ALLOC, REGISTER, ArrayBufferView } from "./runtime";
|
||||
import { runtime, ArrayBufferView } from "./runtime";
|
||||
|
||||
@sealed export class ArrayBuffer {
|
||||
|
||||
@ -22,11 +22,11 @@ import { HEADER, ALLOC_RAW, ALLOC, REGISTER, ArrayBufferView } from "./runtime";
|
||||
|
||||
constructor(length: i32) {
|
||||
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 {
|
||||
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 {
|
||||
@ -34,9 +34,9 @@ import { HEADER, ALLOC_RAW, ALLOC, REGISTER, ArrayBufferView } from "./runtime";
|
||||
begin = begin < 0 ? max(length + begin, 0) : min(begin, length);
|
||||
end = end < 0 ? max(length + end , 0) : min(end , length);
|
||||
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);
|
||||
return REGISTER<ArrayBuffer>(out);
|
||||
return runtime.register<ArrayBuffer>(out);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
|
@ -13,7 +13,6 @@
|
||||
@builtin export declare function isFunction<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 isImplemented(expression: void): bool;
|
||||
@builtin export declare function isConstant(expression: void): bool;
|
||||
@builtin export declare function isManaged<T>(value?: T): bool;
|
||||
@inline export function isNaN<T>(value: T): bool { return value != value; }
|
||||
|
@ -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
|
||||
|
||||
@inline const TRACE = false;
|
||||
@ -11,8 +5,8 @@
|
||||
/** Size of a managed object header. */
|
||||
@inline export const HEADER_SIZE: usize = (offsetof<ManagedObject>() + AL_MASK) & ~AL_MASK;
|
||||
|
||||
import { AL_MASK, MAX_SIZE_32 } from "../internal/allocator";
|
||||
import { __rt_iterateroots } from "../builtins";
|
||||
import { AL_MASK, MAX_SIZE_32 } from "../util/allocator";
|
||||
import { gc } from "../gc";
|
||||
|
||||
/** Collector states. */
|
||||
const enum State {
|
||||
@ -142,7 +136,7 @@ function step(): void {
|
||||
}
|
||||
case State.IDLE: {
|
||||
if (TRACE) trace("gc~step/IDLE");
|
||||
__rt_iterateroots(__gc_mark);
|
||||
gc.iterateRoots(__gc_mark);
|
||||
state = State.MARK;
|
||||
if (TRACE) trace("gc~state = MARK");
|
||||
break;
|
||||
@ -163,7 +157,7 @@ function step(): void {
|
||||
obj.hookFn(objToRef(obj));
|
||||
} else {
|
||||
if (TRACE) trace("gc~step/MARK finish");
|
||||
__rt_iterateroots(__gc_mark);
|
||||
gc.iterateRoots(__gc_mark);
|
||||
obj = iter.next;
|
||||
if (obj === toSpace) {
|
||||
let from = fromSpace;
|
||||
@ -202,9 +196,7 @@ function step(): void {
|
||||
return changetype<usize>(obj) + HEADER_SIZE;
|
||||
}
|
||||
|
||||
// Garbage collector interface
|
||||
|
||||
@global export function __gc_allocate(
|
||||
@global @unsafe export function __gc_allocate( // TODO: make this register only / reuse header
|
||||
size: usize,
|
||||
markFn: (ref: usize) => void
|
||||
): usize {
|
||||
@ -218,13 +210,13 @@ function step(): void {
|
||||
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);
|
||||
var parent = refToObj(parentRef);
|
||||
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 (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");
|
||||
// begin collecting if not yet collecting
|
||||
switch (state) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ArrayBuffer } from "./arraybuffer";
|
||||
import { ArrayBufferView } from "./runtime";
|
||||
|
||||
export class DataView {
|
||||
|
||||
@ -12,7 +13,7 @@ export class DataView {
|
||||
byteLength: i32 = i32.MIN_VALUE // 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");
|
||||
this.data = buffer; // links
|
||||
var dataStart = changetype<usize>(buffer) + <usize>byteOffset;
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* tslint:disable */
|
||||
|
||||
@builtin export declare function ERROR(message?: void): void;
|
||||
@builtin export declare function WARNING(message?: void): void;
|
||||
@builtin export declare function INFO(message?: void): void;
|
||||
@builtin export declare function ERROR(message?: string): void;
|
||||
@builtin export declare function WARNING(message?: string): void;
|
||||
@builtin export declare function INFO(message?: string): void;
|
||||
|
@ -1,30 +1,37 @@
|
||||
/** Garbage collector interface. */
|
||||
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. */
|
||||
@builtin @unsafe export declare function classId<T>(): u32;
|
||||
@unsafe @builtin export declare function classId<T>(): u32;
|
||||
|
||||
/** 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. */
|
||||
@stub @unsafe export function register(ref: usize): void {
|
||||
ERROR("stub: missing garbage collector");
|
||||
@unsafe export function register(ref: usize): void {
|
||||
if (isDefined(__gc_register)) __gc_register(ref);
|
||||
else ERROR("missing implementation: gc.register");
|
||||
}
|
||||
|
||||
/** Links a registered object with the registered object now referencing it. */
|
||||
@stub @unsafe export function link(ref: usize, parentRef: usize): void {
|
||||
ERROR("stub: missing garbage collector");
|
||||
@unsafe export function link(ref: usize, parentRef: usize): void {
|
||||
if (isDefined(__gc_link)) __gc_link(ref, parentRef);
|
||||
else ERROR("missing implementation: gc.link");
|
||||
}
|
||||
|
||||
/** Marks an object as being reachable. */
|
||||
@stub @unsafe export function mark(ref: usize): void {
|
||||
ERROR("stub: missing garbage collector");
|
||||
@unsafe export function mark(ref: usize): void {
|
||||
if (isDefined(__gc_mark)) __gc_mark(ref);
|
||||
else ERROR("missing implementation: gc.mark");
|
||||
}
|
||||
|
||||
/** Performs a full garbage collection cycle. */
|
||||
@stub export function collect(): void {
|
||||
WARNING("stub: missing garbage collector");
|
||||
export function collect(): void {
|
||||
if (isDefined(__gc_collect)) __gc_collect();
|
||||
else WARNING("missing implementation: gc.collect");
|
||||
}
|
||||
}
|
||||
|
||||
|
9
std/assembly/index.d.ts
vendored
9
std/assembly/index.d.ts
vendored
@ -144,8 +144,6 @@ declare function isFunction<T>(value?: any): value is (...args: any) => any;
|
||||
declare function isNullable<T>(value?: any): bool;
|
||||
/** Tests if the specified expression resolves to a defined element. Compiles to a constant. */
|
||||
declare function isDefined(expression: any): bool;
|
||||
/** Tests if the specified expression 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. */
|
||||
declare function isConstant(expression: any): bool;
|
||||
/** 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>
|
||||
): 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. */
|
||||
declare function external(namespace: string, name: string): (
|
||||
target: any,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { LINK } from "./runtime";
|
||||
import { runtime } from "./runtime";
|
||||
import { HASH } from "./util/hash";
|
||||
|
||||
// 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;
|
||||
entry.taggedNext = load<usize>(bucketPtrBase);
|
||||
store<usize>(bucketPtrBase, changetype<usize>(entry));
|
||||
if (isManaged<K>()) LINK(changetype<usize>(key), changetype<usize>(this));
|
||||
if (isManaged<V>()) LINK(changetype<usize>(value), changetype<usize>(this));
|
||||
if (isManaged<K>()) runtime.link(changetype<usize>(key), changetype<usize>(this));
|
||||
if (isManaged<V>()) runtime.link(changetype<usize>(value), changetype<usize>(this));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { memcmp, memmove, memset } from "./util/memory";
|
||||
|
||||
@builtin export declare const HEAP_BASE: usize;
|
||||
|
||||
/** Memory manager interface. */
|
||||
export namespace memory {
|
||||
|
||||
@ -7,15 +9,15 @@ export namespace memory {
|
||||
@builtin export declare function size(): i32;
|
||||
|
||||
/** 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. */
|
||||
@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
|
||||
}
|
||||
|
||||
/** 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
|
||||
}
|
||||
|
||||
@ -30,24 +32,22 @@ export namespace memory {
|
||||
}
|
||||
|
||||
/** Dynamically allocates a section of memory and returns its address. */
|
||||
@stub @inline export function allocate(size: usize): usize {
|
||||
ERROR("stub: missing memory manager");
|
||||
@unsafe export function allocate(size: usize): usize {
|
||||
if (isDefined(__memory_allocate)) return __memory_allocate(size);
|
||||
else ERROR("missing implementation: memory.allocate");
|
||||
return <usize>unreachable();
|
||||
}
|
||||
|
||||
/** Dynamically frees a section of memory by the previously allocated address. */
|
||||
@stub @unsafe @inline export function free(ptr: usize): void {
|
||||
ERROR("stub: missing memory manager");
|
||||
@unsafe export function free(ptr: usize): void {
|
||||
if (isDefined(__memory_free)) __memory_free(ptr);
|
||||
else ERROR("missing implementation: memory.free");
|
||||
}
|
||||
|
||||
/** Resets the memory to its initial state. Arena allocator only. */
|
||||
@stub @unsafe @inline export function reset(): void {
|
||||
ERROR("stub: not supported by memory manager");
|
||||
}
|
||||
|
||||
/** Compares a section of memory to another. */
|
||||
@inline export function compare(vl: usize, vr: usize, n: usize): i32 {
|
||||
return memcmp(vl, vr, n);
|
||||
@unsafe export function reset(): void {
|
||||
if (isDefined(__memory_reset)) __memory_reset();
|
||||
else ERROR("missing implementation: memory.reset");
|
||||
}
|
||||
|
||||
/** Repeats a section of memory at a specific address. */
|
||||
@ -59,4 +59,9 @@ export namespace memory {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export function bswap<T>(value: T): T {
|
||||
export function bswap<T extends number>(value: T): T {
|
||||
if (isInteger<T>()) {
|
||||
if (sizeof<T>() == 2) {
|
||||
return <T>((value << 8) | ((value >> 8) & <T>0x00FF));
|
||||
@ -25,7 +25,7 @@ export function bswap<T>(value: T): T {
|
||||
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 (sizeof<T>() == 2) {
|
||||
return <T>((value << 8) | ((value >> 8) & <T>0x00FF));
|
||||
|
@ -1,9 +1,21 @@
|
||||
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. */
|
||||
@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.*/
|
||||
classId: u32;
|
||||
/** 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
|
||||
// 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. */
|
||||
export function ADJUST(payloadSize: usize): usize {
|
||||
function adjust(payloadSize: usize): usize {
|
||||
// round up to power of 2, e.g. with HEADER_SIZE=8:
|
||||
// 0 -> 2^3 = 8
|
||||
// 1..8 -> 2^4 = 16
|
||||
// 9..24 -> 2^5 = 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. */
|
||||
@unsafe export function ALLOC_RAW(payloadSize: u32): usize {
|
||||
var header = changetype<HEADER>(memory.allocate(ADJUST(payloadSize)));
|
||||
header.classId = HEADER_MAGIC;
|
||||
@unsafe export function allocRaw(payloadSize: u32): usize {
|
||||
var header = changetype<Header>(memory.allocate(adjust(payloadSize)));
|
||||
header.classId = Header.MAGIC;
|
||||
header.payloadSize = payloadSize;
|
||||
if (GC) {
|
||||
if (gc.implemented) {
|
||||
header.gc1 = 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.*/
|
||||
@unsafe export function ALLOC(payloadSize: u32): usize {
|
||||
var ref = ALLOC_RAW(payloadSize);
|
||||
@unsafe export function alloc(payloadSize: u32): usize {
|
||||
var ref = allocRaw(payloadSize);
|
||||
memory.fill(ref, 0, payloadSize);
|
||||
return ref;
|
||||
}
|
||||
|
||||
/** 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
|
||||
// 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,
|
||||
// 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;
|
||||
if (payloadSize < newPayloadSize) {
|
||||
let newAdjustedSize = ADJUST(newPayloadSize);
|
||||
if (select(ADJUST(payloadSize), 0, ref > HEAP_BASE) < newAdjustedSize) {
|
||||
let newAdjustedSize = adjust(newPayloadSize);
|
||||
if (select(adjust(payloadSize), 0, ref > HEAP_BASE) < newAdjustedSize) {
|
||||
// move if the allocation isn't large enough or not a heap object
|
||||
let newHeader = changetype<HEADER>(memory.allocate(newAdjustedSize));
|
||||
newHeader.classId = HEADER_MAGIC;
|
||||
if (GC) {
|
||||
let newHeader = changetype<Header>(memory.allocate(newAdjustedSize));
|
||||
newHeader.classId = Header.MAGIC;
|
||||
if (gc.implemented) {
|
||||
newHeader.gc1 = 0;
|
||||
newHeader.gc2 = 0;
|
||||
}
|
||||
let newRef = changetype<usize>(newHeader) + HEADER_SIZE;
|
||||
let newRef = changetype<usize>(newHeader) + Header.SIZE;
|
||||
memory.copy(newRef, ref, 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
|
||||
assert(ref > HEAP_BASE); // static objects aren't scratch objects
|
||||
memory.free(changetype<usize>(header));
|
||||
} else if (GC) {
|
||||
} else if (gc.implemented) {
|
||||
// if previously registered, register again
|
||||
gc.register(ref);
|
||||
}
|
||||
@ -102,38 +103,41 @@ export function ADJUST(payloadSize: usize): usize {
|
||||
return ref;
|
||||
}
|
||||
|
||||
function unref(ref: usize): HEADER {
|
||||
assert(ref >= HEAP_BASE + HEADER_SIZE); // must be a heap object
|
||||
var header = changetype<HEADER>(ref - HEADER_SIZE);
|
||||
assert(header.classId == HEADER_MAGIC); // must be unregistered
|
||||
function unref(ref: usize): Header {
|
||||
assert(ref >= HEAP_BASE + Header.SIZE); // must be a heap object
|
||||
var header = changetype<Header>(ref - Header.SIZE);
|
||||
assert(header.classId == Header.MAGIC); // must be unregistered
|
||||
return header;
|
||||
}
|
||||
|
||||
/** 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))));
|
||||
}
|
||||
|
||||
/** 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");
|
||||
// see comment in REALLOC why this is useful. also inline this because
|
||||
// it's generic so we don't get a bunch of functions.
|
||||
unref(ref).classId = gc.classId<T>();
|
||||
if (GC) gc.register(ref);
|
||||
if (gc.implemented) gc.register(ref);
|
||||
return changetype<T>(ref);
|
||||
}
|
||||
|
||||
/** Links a managed object with its managed parent. */
|
||||
@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
|
||||
var header = changetype<HEADER>(changetype<usize>(ref) - HEADER_SIZE);
|
||||
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
|
||||
@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
|
||||
var header = changetype<Header>(changetype<usize>(ref) - Header.SIZE);
|
||||
assert(header.classId != Header.MAGIC && header.gc1 != 0 && header.gc2 != 0); // must be registered
|
||||
if (gc.implemented) gc.link(changetype<usize>(ref), changetype<usize>(parentRef)); // tslint:disable-line
|
||||
}
|
||||
}
|
||||
|
||||
import { ArrayBuffer } from "./arraybuffer";
|
||||
|
||||
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;
|
||||
|
||||
@ -158,7 +162,7 @@ export abstract class ArrayBufferView {
|
||||
}
|
||||
|
||||
get length(): i32 {
|
||||
ERROR("concrete implementation must provide this");
|
||||
ERROR("missing implementation: [T extends ArrayBufferView]#length");
|
||||
return unreachable();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { LINK } from "./runtime";
|
||||
import { runtime } from "./runtime";
|
||||
import { HASH } from "./util/hash";
|
||||
|
||||
// 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;
|
||||
entry.taggedNext = load<usize>(bucketPtrBase);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./util/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 {
|
||||
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
|
||||
static fromCharCode(code: i32): String {
|
||||
var out = ALLOC(2);
|
||||
var out = runtime.allocRaw(2);
|
||||
store<u16>(out, <u16>code);
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
static fromCodePoint(code: i32): String {
|
||||
assert(<u32>code <= 0x10FFFF);
|
||||
var sur = code > 0xFFFF;
|
||||
var out = ALLOC((<i32>sur + 1) << 1);
|
||||
var out = runtime.allocRaw((<i32>sur + 1) << 1);
|
||||
if (!sur) {
|
||||
store<u16>(out, <u16>code);
|
||||
} else {
|
||||
@ -28,15 +28,15 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
let lo: u32 = (code & 0x3FF) + 0xDC00;
|
||||
store<u32>(out, (hi << 16) | lo);
|
||||
}
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
@operator("[]") charAt(pos: i32): String {
|
||||
assert(this !== null);
|
||||
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)));
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
charCodeAt(pos: i32): i32 {
|
||||
@ -67,10 +67,10 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
var otherSize: isize = other.length << 1;
|
||||
var outSize: usize = thisSize + otherSize;
|
||||
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 + thisSize, changetype<usize>(other), otherSize);
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
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);
|
||||
var resultLength = min(max(end, 0), size - intStart);
|
||||
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);
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
substring(start: i32, end: i32 = i32.MAX_VALUE): String {
|
||||
@ -188,9 +188,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
len = toPos - fromPos;
|
||||
if (!len) return changetype<String>("");
|
||||
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);
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
trim(): String {
|
||||
@ -216,9 +216,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
}
|
||||
if (!size) return changetype<String>("");
|
||||
if (!start && size == length << 1) return this;
|
||||
var out = ALLOC(size);
|
||||
var out = runtime.allocRaw(size);
|
||||
memory.copy(out, changetype<usize>(this) + offset, size);
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
@inline
|
||||
@ -246,9 +246,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
if (!offset) return this;
|
||||
size -= offset;
|
||||
if (!size) return changetype<String>("");
|
||||
var out = ALLOC(size);
|
||||
var out = runtime.allocRaw(size);
|
||||
memory.copy(out, changetype<usize>(this) + offset, size);
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
trimEnd(): String {
|
||||
@ -265,9 +265,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
}
|
||||
if (!size) return changetype<String>("");
|
||||
if (size == originalSize) return this;
|
||||
var out = ALLOC(size);
|
||||
var out = runtime.allocRaw(size);
|
||||
memory.copy(out, changetype<usize>(this), size);
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
padStart(targetLength: i32, padString: string = " "): String {
|
||||
@ -277,7 +277,7 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
var padSize = <usize>padString.length << 1;
|
||||
if (targetSize < thisSize || !padSize) return this;
|
||||
var prependSize = targetSize - thisSize;
|
||||
var out = ALLOC(targetSize);
|
||||
var out = runtime.allocRaw(targetSize);
|
||||
if (prependSize > padSize) {
|
||||
let repeatCount = (prependSize - 2) / 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 + prependSize, changetype<usize>(this), thisSize);
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
padEnd(targetLength: i32, padString: string = " "): String {
|
||||
@ -298,7 +298,7 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
var padSize = <usize>padString.length << 1;
|
||||
if (targetSize < thisSize || !padSize) return this;
|
||||
var appendSize = targetSize - thisSize;
|
||||
var out = ALLOC(targetSize);
|
||||
var out = runtime.allocRaw(targetSize);
|
||||
memory.copy(out, changetype<usize>(this), thisSize);
|
||||
if (appendSize > padSize) {
|
||||
let repeatCount = (appendSize - 2) / padSize;
|
||||
@ -309,7 +309,7 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
} else {
|
||||
memory.copy(out + thisSize, changetype<usize>(padString), appendSize);
|
||||
}
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
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 == 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);
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
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);
|
||||
len = end - begin;
|
||||
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);
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
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 = <ArrayBuffer>result.buffer_;
|
||||
for (let i: isize = 0; i < length; ++i) {
|
||||
let char = ALLOC(2);
|
||||
let char = runtime.allocRaw(2);
|
||||
store<u16>(
|
||||
changetype<usize>(char),
|
||||
load<u16>(
|
||||
@ -374,9 +374,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
while ((end = this.indexOf(separator!, start)) != -1) {
|
||||
let len = end - start;
|
||||
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);
|
||||
result.push(REGISTER<String>(out));
|
||||
result.push(runtime.register<String>(out));
|
||||
} else {
|
||||
result.push(changetype<String>(""));
|
||||
}
|
||||
@ -390,9 +390,9 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
}
|
||||
var len = length - start;
|
||||
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);
|
||||
result.push(REGISTER<String>(out));
|
||||
result.push(runtime.register<String>(out));
|
||||
} else {
|
||||
result.push(changetype<String>(""));
|
||||
}
|
||||
@ -464,10 +464,10 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
}
|
||||
}
|
||||
assert(ptrPos == len);
|
||||
var out = ALLOC(bufPos);
|
||||
var out = runtime.allocRaw(bufPos);
|
||||
memory.copy(changetype<usize>(out), buf, bufPos);
|
||||
memory.free(buf);
|
||||
return REGISTER<string>(out);
|
||||
return runtime.register<string>(out);
|
||||
}
|
||||
|
||||
toUTF8(): usize {
|
||||
|
@ -1,16 +1,14 @@
|
||||
export namespace table {
|
||||
|
||||
// export function copy(dst: u32, src: u32, n: u32): void {
|
||||
// __table_copy(dst, src, n);
|
||||
// }
|
||||
|
||||
// Passive elements
|
||||
|
||||
// export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void {
|
||||
// __table_init(elementIndex, srcOffset, dstOffset, n);
|
||||
// }
|
||||
|
||||
// export function drop(elementIndex: u32): void {
|
||||
// __table_drop(elementIndex);
|
||||
// }
|
||||
export function copy(dst: u32, src: u32, n: u32): void {
|
||||
ERROR("not implemented: table.copy");
|
||||
}
|
||||
|
||||
export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void {
|
||||
ERROR("not implemented: table.init");
|
||||
}
|
||||
|
||||
export function drop(elementIndex: u32): void {
|
||||
ERROR("not implemented: table.drop");
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
||||
function clampToByte(value: i32): i32 {
|
||||
@ -720,11 +720,11 @@ export class Float64Array extends ArrayBufferView {
|
||||
else begin = min(begin, length);
|
||||
if (end < 0) end = max(length + end, 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, array.dataStart + (<usize>begin << alignof<T>()) , offsetof<TArray>("dataStart"));
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ALLOC, REGISTER, FREE } from "../runtime";
|
||||
import { runtime } from "../runtime";
|
||||
import { CharCode } from "./string";
|
||||
|
||||
@inline export const MAX_DOUBLE_LENGTH = 28;
|
||||
@ -254,10 +254,10 @@ export function utoa32(value: u32): String {
|
||||
if (!value) return "0";
|
||||
|
||||
var decimals = decimalCount32(value);
|
||||
var out = ALLOC(decimals << 1);
|
||||
var out = runtime.alloc(decimals << 1);
|
||||
|
||||
utoa32_core(changetype<usize>(out), value, decimals);
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
export function itoa32(value: i32): String {
|
||||
@ -267,12 +267,12 @@ export function itoa32(value: i32): String {
|
||||
if (sign) value = -value;
|
||||
|
||||
var decimals = decimalCount32(value) + <u32>sign;
|
||||
var out = ALLOC(decimals << 1);
|
||||
var out = runtime.alloc(decimals << 1);
|
||||
|
||||
utoa32_core(changetype<usize>(out), value, decimals);
|
||||
if (sign) store<u16>(changetype<usize>(out), CharCode.MINUS);
|
||||
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
export function utoa64(value: u64): String {
|
||||
@ -282,14 +282,14 @@ export function utoa64(value: u64): String {
|
||||
if (value <= u32.MAX_VALUE) {
|
||||
let val32 = <u32>value;
|
||||
let decimals = decimalCount32(val32);
|
||||
out = ALLOC(decimals << 1);
|
||||
out = runtime.alloc(decimals << 1);
|
||||
utoa32_core(out, val32, decimals);
|
||||
} else {
|
||||
let decimals = decimalCount64(value);
|
||||
out = ALLOC(decimals << 1);
|
||||
out = runtime.alloc(decimals << 1);
|
||||
utoa64_core(changetype<usize>(out), value, decimals);
|
||||
}
|
||||
return REGISTER<String>(out);
|
||||
return runtime.register<String>(out);
|
||||
}
|
||||
|
||||
export function itoa64(value: i64): String {
|
||||
@ -302,16 +302,16 @@ export function itoa64(value: i64): String {
|
||||
if (<u64>value <= <u64>u32.MAX_VALUE) {
|
||||
let val32 = <u32>value;
|
||||
let decimals = decimalCount32(val32) + <u32>sign;
|
||||
out = ALLOC(decimals << 1);
|
||||
out = runtime.alloc(decimals << 1);
|
||||
utoa32_core(changetype<usize>(out), val32, decimals);
|
||||
} else {
|
||||
let decimals = decimalCount64(value) + <u32>sign;
|
||||
out = ALLOC(decimals << 1);
|
||||
out = runtime.alloc(decimals << 1);
|
||||
utoa64_core(changetype<usize>(out), value, decimals);
|
||||
}
|
||||
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 {
|
||||
@ -594,10 +594,10 @@ export function dtoa(value: f64): String {
|
||||
if (isNaN(value)) return "NaN";
|
||||
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 result = changetype<String>(temp).substring(0, length);
|
||||
FREE(temp);
|
||||
runtime.free(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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 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)
|
||||
(elem (i32.const 0) $std/runtime/gc.collect)
|
||||
(elem (i32.const 0) $null)
|
||||
(global $~lib/allocator/tlsf/ROOT (mut i32) (i32.const 0))
|
||||
(global $std/runtime/register_ref (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))
|
||||
(export "memory" (memory $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)
|
||||
(func $~lib/allocator/tlsf/Root#setSLMap (; 2 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
|
||||
local.get $1
|
||||
@ -45,7 +42,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 140
|
||||
i32.const 132
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -65,7 +62,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 163
|
||||
i32.const 155
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -76,7 +73,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 164
|
||||
i32.const 156
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -102,7 +99,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 85
|
||||
i32.const 77
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -120,7 +117,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 86
|
||||
i32.const 78
|
||||
i32.const 11
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -133,7 +130,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 424
|
||||
i32.const 416
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -150,7 +147,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 154
|
||||
i32.const 146
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -161,7 +158,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 155
|
||||
i32.const 147
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -184,7 +181,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 134
|
||||
i32.const 126
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -210,7 +207,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 254
|
||||
i32.const 246
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -233,7 +230,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 256
|
||||
i32.const 248
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -334,7 +331,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 77
|
||||
i32.const 69
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -348,7 +345,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 78
|
||||
i32.const 70
|
||||
i32.const 11
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -364,7 +361,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 330
|
||||
i32.const 322
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -376,7 +373,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 331
|
||||
i32.const 323
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -389,7 +386,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 332
|
||||
i32.const 324
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -411,7 +408,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 185
|
||||
i32.const 177
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -425,7 +422,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 187
|
||||
i32.const 179
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -449,7 +446,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 189
|
||||
i32.const 181
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -461,7 +458,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 193
|
||||
i32.const 185
|
||||
i32.const 23
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -503,7 +500,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 207
|
||||
i32.const 199
|
||||
i32.const 24
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -517,7 +514,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 209
|
||||
i32.const 201
|
||||
i32.const 6
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -566,7 +563,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 222
|
||||
i32.const 214
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -645,7 +642,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 373
|
||||
i32.const 365
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -656,7 +653,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 374
|
||||
i32.const 366
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -667,7 +664,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 375
|
||||
i32.const 367
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -684,7 +681,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 380
|
||||
i32.const 372
|
||||
i32.const 6
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -712,7 +709,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 389
|
||||
i32.const 381
|
||||
i32.const 6
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -765,7 +762,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 418
|
||||
i32.const 410
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -790,7 +787,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 292
|
||||
i32.const 284
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -870,7 +867,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 319
|
||||
i32.const 311
|
||||
i32.const 16
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -898,7 +895,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 344
|
||||
i32.const 336
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -918,7 +915,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 345
|
||||
i32.const 337
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -929,7 +926,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 346
|
||||
i32.const 338
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -981,7 +978,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 364
|
||||
i32.const 356
|
||||
i32.const 25
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -997,7 +994,7 @@
|
||||
i32.const 8
|
||||
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 $2 i32)
|
||||
(local $3 i32)
|
||||
@ -1146,8 +1143,8 @@
|
||||
else
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 476
|
||||
i32.const 14
|
||||
i32.const 465
|
||||
i32.const 12
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
@ -1163,8 +1160,8 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 479
|
||||
i32.const 4
|
||||
i32.const 468
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
@ -1183,7 +1180,7 @@
|
||||
i32.clz
|
||||
i32.sub
|
||||
i32.shl
|
||||
call $~lib/allocator/tlsf/memory.allocate
|
||||
call $~lib/allocator/tlsf/__memory_allocate
|
||||
local.tee $1
|
||||
i32.const -1520547049
|
||||
i32.store
|
||||
@ -1419,16 +1416,21 @@
|
||||
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.get $0
|
||||
call $~lib/runtime/ALLOC_RAW
|
||||
local.tee $1
|
||||
local.get $0
|
||||
call $~lib/util/memory/memset
|
||||
call $~lib/memory/memory.fill
|
||||
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 $4 i32)
|
||||
(local $5 i32)
|
||||
@ -2325,7 +2327,7 @@
|
||||
i32.store8
|
||||
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 $4 i32)
|
||||
local.get $0
|
||||
@ -2523,7 +2525,7 @@
|
||||
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 $2 i32)
|
||||
(local $3 i32)
|
||||
@ -2543,8 +2545,8 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 490
|
||||
i32.const 8
|
||||
i32.const 479
|
||||
i32.const 6
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
@ -2561,10 +2563,6 @@
|
||||
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)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
@ -2604,7 +2602,7 @@
|
||||
i32.lt_u
|
||||
if
|
||||
local.get $4
|
||||
call $~lib/allocator/tlsf/memory.allocate
|
||||
call $~lib/allocator/tlsf/__memory_allocate
|
||||
local.tee $5
|
||||
i32.const -1520547049
|
||||
i32.store
|
||||
@ -2627,7 +2625,7 @@
|
||||
local.get $1
|
||||
local.get $2
|
||||
i32.sub
|
||||
call $~lib/util/memory/memset
|
||||
call $~lib/memory/memory.fill
|
||||
local.get $3
|
||||
i32.load
|
||||
i32.const -1520547049
|
||||
@ -2639,13 +2637,13 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 85
|
||||
i32.const 83
|
||||
i32.const 8
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
local.get $3
|
||||
call $~lib/allocator/tlsf/memory.free
|
||||
call $~lib/allocator/tlsf/__memory_free
|
||||
else
|
||||
local.get $0
|
||||
global.set $std/runtime/register_ref
|
||||
@ -2661,7 +2659,7 @@
|
||||
local.get $1
|
||||
local.get $2
|
||||
i32.sub
|
||||
call $~lib/util/memory/memset
|
||||
call $~lib/memory/memory.fill
|
||||
end
|
||||
end
|
||||
local.get $3
|
||||
@ -2676,7 +2674,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 106
|
||||
i32.const 104
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2691,7 +2689,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 108
|
||||
i32.const 106
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2715,20 +2713,20 @@
|
||||
i32.clz
|
||||
i32.sub
|
||||
i32.shl
|
||||
local.tee $1
|
||||
local.tee $2
|
||||
i32.const 0
|
||||
i32.ne
|
||||
local.tee $2
|
||||
if (result i32)
|
||||
local.get $1
|
||||
local.tee $1
|
||||
if
|
||||
local.get $2
|
||||
i32.const 1
|
||||
i32.sub
|
||||
local.get $1
|
||||
local.get $2
|
||||
i32.and
|
||||
i32.eqz
|
||||
else
|
||||
local.get $2
|
||||
local.set $1
|
||||
end
|
||||
local.get $1
|
||||
if
|
||||
local.get $0
|
||||
i32.const 1
|
||||
@ -2738,7 +2736,7 @@
|
||||
else
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 38
|
||||
i32.const 36
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2756,6 +2754,7 @@
|
||||
i32.const 1
|
||||
i32.const 32
|
||||
global.get $std/runtime/barrier2
|
||||
local.tee $1
|
||||
i32.const 16
|
||||
i32.add
|
||||
i32.clz
|
||||
@ -2763,7 +2762,7 @@
|
||||
i32.shl
|
||||
i32.const 1
|
||||
i32.const 32
|
||||
global.get $std/runtime/barrier2
|
||||
local.get $1
|
||||
i32.const 15
|
||||
i32.add
|
||||
i32.clz
|
||||
@ -2786,6 +2785,7 @@
|
||||
i32.const 1
|
||||
i32.const 32
|
||||
global.get $std/runtime/barrier3
|
||||
local.tee $1
|
||||
i32.const 16
|
||||
i32.add
|
||||
i32.clz
|
||||
@ -2793,7 +2793,7 @@
|
||||
i32.shl
|
||||
i32.const 1
|
||||
i32.const 32
|
||||
global.get $std/runtime/barrier3
|
||||
local.get $1
|
||||
i32.const 15
|
||||
i32.add
|
||||
i32.clz
|
||||
@ -2849,7 +2849,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 53
|
||||
i32.const 51
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2861,7 +2861,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 54
|
||||
i32.const 52
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2875,7 +2875,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 55
|
||||
i32.const 53
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2887,7 +2887,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 56
|
||||
i32.const 54
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2902,7 +2902,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 58
|
||||
i32.const 56
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2918,14 +2918,14 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 60
|
||||
i32.const 58
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
global.get $std/runtime/ref2
|
||||
call $~lib/runtime/unref
|
||||
call $~lib/allocator/tlsf/memory.free
|
||||
call $~lib/allocator/tlsf/__memory_free
|
||||
global.get $std/runtime/barrier2
|
||||
call $~lib/runtime/ALLOC
|
||||
global.set $std/runtime/ref3
|
||||
@ -2935,7 +2935,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 63
|
||||
i32.const 61
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2956,7 +2956,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 67
|
||||
i32.const 65
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2972,7 +2972,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 69
|
||||
i32.const 67
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2984,7 +2984,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 70
|
||||
i32.const 68
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3001,7 +3001,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 73
|
||||
i32.const 71
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3017,19 +3017,16 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 74
|
||||
i32.const 72
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $std/runtime/gc.link (; 27 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
|
||||
nop
|
||||
)
|
||||
(func $std/runtime/gc.collect (; 28 ;) (type $FUNCSIG$v)
|
||||
nop
|
||||
)
|
||||
(func $start (; 29 ;) (type $FUNCSIG$v)
|
||||
(func $start (; 27 ;) (type $FUNCSIG$v)
|
||||
call $start:std/runtime
|
||||
)
|
||||
(func $null (; 28 ;) (type $FUNCSIG$v)
|
||||
nop
|
||||
)
|
||||
)
|
||||
|
@ -2,28 +2,20 @@ import "allocator/tlsf";
|
||||
|
||||
var register_ref: usize = 0;
|
||||
|
||||
@global namespace gc {
|
||||
export function register(ref: usize): void {
|
||||
@global function __gc_register(ref: usize): void {
|
||||
register_ref = ref;
|
||||
}
|
||||
export function link(ref: usize, parentRef: usize): void {
|
||||
}
|
||||
export function collect(): void {
|
||||
}
|
||||
|
||||
var link_ref: usize = 0;
|
||||
var link_parentRef: usize = 0;
|
||||
|
||||
@global function __gc_link(ref: usize, parentRef: usize): void {
|
||||
link_ref = ref;
|
||||
link_parentRef = parentRef;
|
||||
}
|
||||
|
||||
import {
|
||||
HEADER,
|
||||
HEADER_SIZE,
|
||||
HEADER_MAGIC,
|
||||
ADJUST,
|
||||
ALLOC,
|
||||
REALLOC,
|
||||
FREE,
|
||||
REGISTER,
|
||||
ArrayBufferBase,
|
||||
StringBase
|
||||
} from "runtime";
|
||||
@global function __gc_collect(): void {
|
||||
}
|
||||
|
||||
class A {}
|
||||
class B {}
|
||||
@ -33,42 +25,42 @@ function isPowerOf2(x: i32): bool {
|
||||
return x != 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
assert(ADJUST(0) > 0);
|
||||
assert(runtime.adjust(0) > 0);
|
||||
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;
|
||||
while (ADJUST(barrier2 + 1) == ADJUST(barrier2)) ++barrier2;
|
||||
while (runtime.adjust(barrier2 + 1) == runtime.adjust(barrier2)) ++barrier2;
|
||||
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("barrier2", 1, barrier2);
|
||||
trace("barrier3", 1, barrier3);
|
||||
|
||||
var ref1 = ALLOC(1);
|
||||
var header1 = changetype<HEADER>(ref1 - HEADER_SIZE);
|
||||
assert(header1.classId == HEADER_MAGIC);
|
||||
var ref1 = runtime.alloc(1);
|
||||
var header1 = changetype<runtime.Header>(ref1 - runtime.Header.SIZE);
|
||||
assert(header1.classId == runtime.Header.MAGIC);
|
||||
assert(header1.payloadSize == 1);
|
||||
assert(ref1 == REALLOC(ref1, barrier1)); // same segment
|
||||
assert(ref1 == runtime.realloc(ref1, barrier1)); // same segment
|
||||
assert(header1.payloadSize == barrier1);
|
||||
var ref2 = REALLOC(ref1, barrier2);
|
||||
var ref2 = runtime.realloc(ref1, barrier2);
|
||||
assert(ref1 != ref2); // moves
|
||||
var header2 = changetype<HEADER>(ref2 - HEADER_SIZE);
|
||||
var header2 = changetype<runtime.Header>(ref2 - runtime.Header.SIZE);
|
||||
assert(header2.payloadSize == barrier2);
|
||||
FREE(ref2);
|
||||
var ref3 = ALLOC(barrier2);
|
||||
runtime.free(ref2);
|
||||
var ref3 = runtime.alloc(barrier2);
|
||||
assert(ref1 == ref3); // reuses space of ref1 (free'd in realloc), ref2 (explicitly free'd)
|
||||
|
||||
var ref4 = ALLOC(barrier1);
|
||||
REGISTER<A>(ref4); // should call __REGISTER_IMPL
|
||||
var ref4 = runtime.alloc(barrier1);
|
||||
runtime.register<A>(ref4); // should call __REGISTER_IMPL
|
||||
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.payloadSize == barrier1);
|
||||
|
||||
var ref5 = ALLOC(10);
|
||||
assert(changetype<ArrayBufferBase>(ref5).byteLength == 10);
|
||||
assert(changetype<StringBase>(ref5).length == 5);
|
||||
var ref5 = runtime.alloc(10);
|
||||
assert(changetype<ArrayBuffer>(ref5).byteLength == 10);
|
||||
assert(changetype<String>(ref5).length == 5);
|
||||
|
@ -37,6 +37,7 @@
|
||||
(global $~lib/allocator/tlsf/Root.SIZE i32 (i32.const 2916))
|
||||
(global $~lib/allocator/tlsf/ROOT (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/barrier2 (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/header4 (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 "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)
|
||||
(func $start:~lib/allocator/tlsf (; 2 ;) (type $FUNCSIG$v)
|
||||
i32.const 1
|
||||
@ -65,7 +63,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 118
|
||||
i32.const 110
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -114,7 +112,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 140
|
||||
i32.const 132
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -135,7 +133,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 163
|
||||
i32.const 155
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -147,7 +145,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 164
|
||||
i32.const 156
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -180,7 +178,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 85
|
||||
i32.const 77
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -200,7 +198,7 @@
|
||||
if (result i32)
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 86
|
||||
i32.const 78
|
||||
i32.const 11
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -216,7 +214,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 424
|
||||
i32.const 416
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -234,7 +232,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 154
|
||||
i32.const 146
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -246,7 +244,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 155
|
||||
i32.const 147
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -270,7 +268,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 134
|
||||
i32.const 126
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -300,7 +298,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 254
|
||||
i32.const 246
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -326,7 +324,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 256
|
||||
i32.const 248
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -437,7 +435,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 77
|
||||
i32.const 69
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -451,7 +449,7 @@
|
||||
if (result i32)
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 78
|
||||
i32.const 70
|
||||
i32.const 11
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -468,7 +466,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 330
|
||||
i32.const 322
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -481,7 +479,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 331
|
||||
i32.const 323
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -494,7 +492,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 332
|
||||
i32.const 324
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -520,7 +518,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 185
|
||||
i32.const 177
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -535,7 +533,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 187
|
||||
i32.const 179
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -561,7 +559,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 189
|
||||
i32.const 181
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -573,7 +571,7 @@
|
||||
if (result i32)
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 193
|
||||
i32.const 185
|
||||
i32.const 23
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -621,7 +619,7 @@
|
||||
if (result i32)
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 207
|
||||
i32.const 199
|
||||
i32.const 24
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -639,7 +637,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 209
|
||||
i32.const 201
|
||||
i32.const 6
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -694,7 +692,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 222
|
||||
i32.const 214
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -785,7 +783,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 373
|
||||
i32.const 365
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -798,7 +796,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 374
|
||||
i32.const 366
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -811,7 +809,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 375
|
||||
i32.const 367
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -832,7 +830,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 380
|
||||
i32.const 372
|
||||
i32.const 6
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -861,7 +859,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 389
|
||||
i32.const 381
|
||||
i32.const 6
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -932,7 +930,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 418
|
||||
i32.const 410
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -948,7 +946,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 418
|
||||
i32.const 410
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -978,7 +976,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 292
|
||||
i32.const 284
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -1074,7 +1072,7 @@
|
||||
else
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 319
|
||||
i32.const 311
|
||||
i32.const 16
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -1111,7 +1109,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 344
|
||||
i32.const 336
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -1131,7 +1129,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 345
|
||||
i32.const 337
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -1144,7 +1142,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 346
|
||||
i32.const 338
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -1204,7 +1202,7 @@
|
||||
if (result i32)
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 364
|
||||
i32.const 356
|
||||
i32.const 25
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -1225,7 +1223,7 @@
|
||||
global.get $~lib/allocator/tlsf/Block.INFO
|
||||
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 $2 i32)
|
||||
(local $3 i32)
|
||||
@ -1238,7 +1236,7 @@
|
||||
local.get $1
|
||||
i32.eqz
|
||||
if
|
||||
global.get $~lib/runtime/HEAP_BASE
|
||||
global.get $~lib/memory/HEAP_BASE
|
||||
i32.const 7
|
||||
i32.add
|
||||
i32.const 7
|
||||
@ -1430,8 +1428,8 @@
|
||||
if (result i32)
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 476
|
||||
i32.const 14
|
||||
i32.const 465
|
||||
i32.const 12
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
else
|
||||
@ -1451,8 +1449,8 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 479
|
||||
i32.const 4
|
||||
i32.const 468
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
@ -1461,11 +1459,16 @@
|
||||
local.get $0
|
||||
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.get $0
|
||||
call $~lib/runtime/ADJUST
|
||||
call $~lib/allocator/tlsf/memory.allocate
|
||||
call $~lib/memory/memory.allocate
|
||||
local.set $1
|
||||
local.get $1
|
||||
i32.const -1520547049
|
||||
@ -1483,7 +1486,7 @@
|
||||
i32.const 16
|
||||
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 $4 i32)
|
||||
(local $5 i64)
|
||||
@ -1737,29 +1740,24 @@
|
||||
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 $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
local.get $0
|
||||
call $~lib/runtime/ALLOC_RAW
|
||||
local.set $1
|
||||
block $~lib/runtime/memory.fill|inlined.0
|
||||
local.get $1
|
||||
local.set $2
|
||||
i32.const 0
|
||||
local.set $3
|
||||
local.get $0
|
||||
local.set $4
|
||||
local.get $2
|
||||
local.get $3
|
||||
local.get $4
|
||||
call $~lib/util/memory/memset
|
||||
end
|
||||
call $~lib/memory/memory.fill
|
||||
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 $4 i32)
|
||||
(local $5 i32)
|
||||
@ -2960,7 +2958,7 @@
|
||||
i32.store8
|
||||
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.get $0
|
||||
local.get $1
|
||||
@ -3187,7 +3185,13 @@
|
||||
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 $2 i32)
|
||||
(local $3 i32)
|
||||
@ -3212,8 +3216,8 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 16
|
||||
i32.const 490
|
||||
i32.const 8
|
||||
i32.const 479
|
||||
i32.const 6
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
@ -3230,19 +3234,24 @@
|
||||
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
|
||||
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 $3 i32)
|
||||
(local $4 i32)
|
||||
(local $5 i32)
|
||||
(local $6 i32)
|
||||
(local $7 i32)
|
||||
(local $8 i32)
|
||||
(local $9 i32)
|
||||
local.get $0
|
||||
i32.const 16
|
||||
i32.sub
|
||||
@ -3261,14 +3270,14 @@
|
||||
call $~lib/runtime/ADJUST
|
||||
i32.const 0
|
||||
local.get $0
|
||||
global.get $~lib/runtime/HEAP_BASE
|
||||
global.get $~lib/memory/HEAP_BASE
|
||||
i32.gt_u
|
||||
select
|
||||
local.get $4
|
||||
i32.lt_u
|
||||
if
|
||||
local.get $4
|
||||
call $~lib/allocator/tlsf/memory.allocate
|
||||
call $~lib/memory/memory.allocate
|
||||
local.set $5
|
||||
local.get $5
|
||||
i32.const -1520547049
|
||||
@ -3283,56 +3292,40 @@
|
||||
i32.const 16
|
||||
i32.add
|
||||
local.set $6
|
||||
block $~lib/runtime/memory.copy|inlined.0
|
||||
local.get $6
|
||||
local.set $7
|
||||
local.get $0
|
||||
local.set $8
|
||||
local.get $3
|
||||
local.set $9
|
||||
local.get $7
|
||||
local.get $8
|
||||
local.get $9
|
||||
call $~lib/util/memory/memmove
|
||||
end
|
||||
block $~lib/runtime/memory.fill|inlined.1
|
||||
call $~lib/memory/memory.copy
|
||||
local.get $6
|
||||
local.get $3
|
||||
i32.add
|
||||
local.set $9
|
||||
i32.const 0
|
||||
local.set $8
|
||||
local.get $1
|
||||
local.get $3
|
||||
i32.sub
|
||||
local.set $7
|
||||
local.get $9
|
||||
local.get $8
|
||||
local.get $7
|
||||
call $~lib/util/memory/memset
|
||||
end
|
||||
call $~lib/memory/memory.fill
|
||||
local.get $2
|
||||
i32.load
|
||||
i32.const -1520547049
|
||||
i32.eq
|
||||
if
|
||||
local.get $0
|
||||
global.get $~lib/runtime/HEAP_BASE
|
||||
global.get $~lib/memory/HEAP_BASE
|
||||
i32.gt_u
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 85
|
||||
i32.const 83
|
||||
i32.const 8
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
local.get $2
|
||||
call $~lib/allocator/tlsf/memory.free
|
||||
call $~lib/memory/memory.free
|
||||
else
|
||||
local.get $0
|
||||
call $std/runtime/gc.register
|
||||
call $~lib/gc/gc.register
|
||||
end
|
||||
local.get $5
|
||||
local.set $2
|
||||
@ -3342,17 +3335,11 @@
|
||||
local.get $0
|
||||
local.get $3
|
||||
i32.add
|
||||
local.set $6
|
||||
i32.const 0
|
||||
local.set $5
|
||||
local.get $1
|
||||
local.get $3
|
||||
i32.sub
|
||||
local.set $7
|
||||
local.get $6
|
||||
local.get $5
|
||||
local.get $7
|
||||
call $~lib/util/memory/memset
|
||||
call $~lib/memory/memory.fill
|
||||
end
|
||||
else
|
||||
nop
|
||||
@ -3362,10 +3349,10 @@
|
||||
i32.store offset=4
|
||||
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.get $0
|
||||
global.get $~lib/runtime/HEAP_BASE
|
||||
global.get $~lib/memory/HEAP_BASE
|
||||
i32.const 16
|
||||
i32.add
|
||||
i32.ge_u
|
||||
@ -3373,7 +3360,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 106
|
||||
i32.const 104
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3390,25 +3377,25 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 108
|
||||
i32.const 106
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
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
|
||||
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
|
||||
i32.const 16
|
||||
i32.sub
|
||||
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
|
||||
i32.const 16
|
||||
i32.sub
|
||||
@ -3416,7 +3403,7 @@
|
||||
i32.const 1
|
||||
i32.shr_u
|
||||
)
|
||||
(func $start:std/runtime (; 35 ;) (type $FUNCSIG$v)
|
||||
(func $start:std/runtime (; 40 ;) (type $FUNCSIG$v)
|
||||
(local $0 i32)
|
||||
call $start:~lib/allocator/tlsf
|
||||
i32.const 2
|
||||
@ -3426,7 +3413,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 30
|
||||
i32.const 28
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3439,7 +3426,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 36
|
||||
i32.const 34
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3460,7 +3447,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 38
|
||||
i32.const 36
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3563,7 +3550,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 53
|
||||
i32.const 51
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3576,7 +3563,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 54
|
||||
i32.const 52
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3590,7 +3577,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 55
|
||||
i32.const 53
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3603,7 +3590,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 56
|
||||
i32.const 54
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3619,7 +3606,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 58
|
||||
i32.const 56
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3636,7 +3623,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 60
|
||||
i32.const 58
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3653,7 +3640,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 63
|
||||
i32.const 61
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3669,7 +3656,7 @@
|
||||
i32.const 2
|
||||
i32.store
|
||||
local.get $0
|
||||
call $std/runtime/gc.register
|
||||
call $~lib/gc/gc.register
|
||||
local.get $0
|
||||
end
|
||||
drop
|
||||
@ -3680,7 +3667,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 67
|
||||
i32.const 65
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3697,7 +3684,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 69
|
||||
i32.const 67
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3710,7 +3697,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 70
|
||||
i32.const 68
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3719,41 +3706,35 @@
|
||||
call $~lib/runtime/ALLOC
|
||||
global.set $std/runtime/ref5
|
||||
global.get $std/runtime/ref5
|
||||
call $~lib/runtime/ArrayBufferBase#get:byteLength
|
||||
call $~lib/arraybuffer/ArrayBuffer#get:byteLength
|
||||
i32.const 10
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 73
|
||||
i32.const 71
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
global.get $std/runtime/ref5
|
||||
call $~lib/runtime/StringBase#get:length
|
||||
call $~lib/string/String#get:length
|
||||
i32.const 5
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 72
|
||||
i32.const 74
|
||||
i32.const 72
|
||||
i32.const 0
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $std/runtime/gc.link (; 36 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
|
||||
nop
|
||||
)
|
||||
(func $std/runtime/gc.collect (; 37 ;) (type $FUNCSIG$v)
|
||||
nop
|
||||
)
|
||||
(func $start (; 38 ;) (type $FUNCSIG$v)
|
||||
(func $start (; 41 ;) (type $FUNCSIG$v)
|
||||
call $start:std/runtime
|
||||
)
|
||||
(func $null (; 39 ;) (type $FUNCSIG$v)
|
||||
(func $null (; 42 ;) (type $FUNCSIG$v)
|
||||
)
|
||||
)
|
||||
|
Reference in New Issue
Block a user