Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment: Memory/thread-safe runtime #1276

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 38 additions & 0 deletions std/assembly/rt/common.ts
Expand Up @@ -12,6 +12,44 @@
// @ts-ignore: decorator
@inline export const DEBUG = true;

// Common root and block structure

// @ts-ignore: decorator
@inline export const INITIALIZED: u32 = 1 << 0;
// @ts-ignore: decorator
@inline export const COLLECTING: u32 = 1 << 1;

// ╒═════════════════ Common root layout (32-bit) ═════════════════╕
// 3 2 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤
// │ state │
// ├───────────────────────────────────────────────────────────────┤
// │ data0 │
// ├───────────────────────────────────────────────────────────────┤
// │ ... │
// ╞═══════════════════════════════════════════════════════════════╡
// │ ... │
@unmanaged export class ROOT {
/** Runtime state, i.e. INITIALIZED, COLLECTING. */
state: u32;
/** Runtime lock. */
lock: u32;
/** Additional data, either MM or GC. */
data0: usize; // MM flMap
/** Additional data, either MM or GC. */
data1: usize; // GC roots
/** Additional data, either MM or GC. */
data2: usize; // GC roots_cur
/** Additional data, either MM or GC. */
data3: usize; // GC roots_end
}

// @ts-ignore: decorator
@lazy export const root = changetype<ROOT>((__heap_base + AL_MASK) & ~AL_MASK);
// @ts-ignore: decorator
@lazy export var main = false;

// ╒════════════════ Common block layout (32-bit) ═════════════════╕
// 3 2 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
Expand Down
50 changes: 22 additions & 28 deletions std/assembly/rt/pure.ts
@@ -1,5 +1,5 @@
import { DEBUG, BLOCK_OVERHEAD } from "rt/common";
import { Block, freeBlock, ROOT } from "rt/tlsf";
import { DEBUG, BLOCK_OVERHEAD, root } from "rt/common";
import { Block, freeBlock } from "rt/tlsf";
import { TypeinfoFlags } from "shared/typeinfo";
import { onincrement, ondecrement, onfree, onalloc } from "./rtrace";

Expand Down Expand Up @@ -124,10 +124,10 @@ function decrement(s: Block): void {
__visit_members(changetype<usize>(s) + BLOCK_OVERHEAD, VISIT_DECREMENT);
if (isDefined(__GC_ALL_ACYCLIC)) {
if (DEBUG) assert(!(info & BUFFERED_MASK));
freeBlock(ROOT, s);
freeBlock(s);
} else {
if (!(info & BUFFERED_MASK)) {
freeBlock(ROOT, s);
freeBlock(s);
} else {
s.gcInfo = BUFFERED_MASK | COLOR_BLACK | 0;
}
Expand All @@ -149,31 +149,25 @@ function decrement(s: Block): void {
}
}

/** Buffer of possible roots. */
// @ts-ignore: decorator
@lazy var ROOTS: usize;
/** Current absolute offset into the `ROOTS` buffer. */
// @ts-ignore: decorator
@lazy var CUR: usize = 0;
/** Current absolute end offset into the `ROOTS` buffer. */
// @ts-ignore: decorator
@lazy var END: usize = 0;
// root.data1 : Buffer of possible roots
// root.data2 : Current absolute offset into the buffer
// root.data3 : Current absolute end offset into the buffer

/** Appends a block to possible roots. */
function appendRoot(s: Block): void {
var cur = CUR;
if (cur >= END) {
var cur = root.data2;
if (cur >= root.data3) {
growRoots(); // TBD: either that or pick a default and force collection on overflow
cur = CUR;
cur = root.data2;
}
store<Block>(cur, s);
CUR = cur + sizeof<usize>();
root.data2 = cur + sizeof<usize>();
}

/** Grows the roots buffer if it ran full. */
function growRoots(): void {
var oldRoots = ROOTS;
var oldSize = CUR - oldRoots;
var oldRoots = root.data1;
var oldSize = root.data2 - oldRoots;
var newSize = max(oldSize * 2, 64 << alignof<usize>());
var newRoots = __alloc(newSize, 0);
if (isDefined(ASC_RTRACE)) onfree(changetype<Block>(newRoots - BLOCK_OVERHEAD)); // neglect unmanaged
Expand All @@ -182,9 +176,9 @@ function growRoots(): void {
if (isDefined(ASC_RTRACE)) onalloc(changetype<Block>(oldRoots - BLOCK_OVERHEAD)); // neglect unmanaged
__free(oldRoots);
}
ROOTS = newRoots;
CUR = newRoots + oldSize;
END = newRoots + newSize;
root.data1 = newRoots;
root.data2 = newRoots + oldSize;
root.data3 = newRoots + newSize;
}

/** Collects cyclic garbage. */
Expand All @@ -194,9 +188,9 @@ export function __collect(): void {
if (isDefined(__GC_ALL_ACYCLIC)) return;

// markRoots
var roots = ROOTS;
var roots = root.data1;
var cur = roots;
for (let pos = cur, end = CUR; pos < end; pos += sizeof<usize>()) {
for (let pos = cur, end = root.data2; pos < end; pos += sizeof<usize>()) {
let s = load<Block>(pos);
let info = s.gcInfo;
if ((info & COLOR_MASK) == COLOR_PURPLE && (info & REFCOUNT_MASK) > 0) {
Expand All @@ -205,13 +199,13 @@ export function __collect(): void {
cur += sizeof<usize>();
} else {
if ((info & COLOR_MASK) == COLOR_BLACK && !(info & REFCOUNT_MASK)) {
freeBlock(ROOT, s);
freeBlock(s);
} else {
s.gcInfo = info & ~BUFFERED_MASK;
}
}
}
CUR = cur;
root.data2 = cur;

// scanRoots
for (let pos = roots; pos < cur; pos += sizeof<usize>()) {
Expand All @@ -224,7 +218,7 @@ export function __collect(): void {
s.gcInfo = s.gcInfo & ~BUFFERED_MASK;
collectWhite(s);
}
CUR = roots;
root.data2 = roots;
}

/** Marks a block as gray (possible member of cycle) during the collection phase. */
Expand Down Expand Up @@ -261,7 +255,7 @@ function collectWhite(s: Block): void {
if ((info & COLOR_MASK) == COLOR_WHITE && !(info & BUFFERED_MASK)) {
s.gcInfo = (info & ~COLOR_MASK) | COLOR_BLACK;
__visit_members(changetype<usize>(s) + BLOCK_OVERHEAD, VISIT_COLLECTWHITE);
freeBlock(ROOT, s);
freeBlock(s);
}
}

Expand Down