Skip to content

Commit 5a2ab3d

Browse files
committed
what std/string would look like
1 parent 5c25b0c commit 5a2ab3d

File tree

6 files changed

+302
-285
lines changed

6 files changed

+302
-285
lines changed

std/assembly/runtime/index.ts

+32-16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AL_MASK } from "../internal/allocator";
1+
import { AL_MASK, MAX_SIZE_32 } from "../internal/allocator";
22
import { __rt_classid } from "../builtins";
33

44
/** Common runtime header of all objects. */
@@ -8,18 +8,22 @@ import { __rt_classid } from "../builtins";
88
/** Size of the allocated payload. */
99
payloadSize: u32;
1010
/** Reserved field for use by GC. Only present if GC is. */
11-
reserved1: usize; // itcm: tagged next
11+
gc1: usize; // itcm: tagged next
1212
/** Reserved field for use by GC. Only present if GC is. */
13-
reserved2: usize; // itcm: prev
13+
gc2: usize; // itcm: prev
1414
}
1515

16+
// Note that header data and layout isn't quite optimal depending on which allocator one
17+
// decides to use, but it's done this way for maximum flexibility. Also remember that the
18+
// runtime will most likely change significantly once reftypes and WASM GC are a thing.
19+
1620
/** Whether a GC is present or not. */
17-
@inline export const GC = isDefined(__REGISTER_IMPL);
21+
@inline export const GC = isDefined(gc);
1822

1923
/** Size of the common runtime header. */
2024
@inline export const HEADER_SIZE: usize = GC
21-
? (offsetof<HEADER>( ) + AL_MASK) & ~AL_MASK // full header if GC is present
22-
: (offsetof<HEADER>("reserved1") + AL_MASK) & ~AL_MASK; // half header if GC is absent
25+
? (offsetof<HEADER>( ) + AL_MASK) & ~AL_MASK // full header if GC is present
26+
: (offsetof<HEADER>("gc1") + AL_MASK) & ~AL_MASK; // half header if GC is absent
2327

2428
/** Magic value used to validate common runtime headers. */
2529
@inline export const HEADER_MAGIC: u32 = 0xA55E4B17;
@@ -41,8 +45,8 @@ export function ALLOC(payloadSize: u32): usize {
4145
header.classId = HEADER_MAGIC;
4246
header.payloadSize = payloadSize;
4347
if (GC) {
44-
header.reserved1 = 0;
45-
header.reserved2 = 0;
48+
header.gc1 = 0;
49+
header.gc2 = 0;
4650
}
4751
var ref = changetype<usize>(header) + HEADER_SIZE;
4852
memory.fill(ref, 0, payloadSize);
@@ -60,8 +64,8 @@ export function REALLOC(ref: usize, newPayloadSize: u32): usize {
6064
let newHeader = changetype<HEADER>(memory.allocate(newAlignedSize));
6165
newHeader.classId = HEADER_MAGIC;
6266
if (GC) {
63-
newHeader.reserved1 = 0;
64-
newHeader.reserved2 = 0;
67+
newHeader.gc1 = 0;
68+
newHeader.gc2 = 0;
6569
}
6670
let newRef = changetype<usize>(newHeader) + HEADER_SIZE;
6771
memory.copy(newRef, ref, payloadSize);
@@ -85,7 +89,7 @@ export function REALLOC(ref: usize, newPayloadSize: u32): usize {
8589
return ref;
8690
}
8791

88-
function ensureUnregistered(ref: usize): HEADER {
92+
function unref(ref: usize): HEADER {
8993
assert(ref >= HEAP_BASE + HEADER_SIZE); // must be a heap object
9094
var header = changetype<HEADER>(ref - HEADER_SIZE);
9195
assert(header.classId == HEADER_MAGIC); // must be unregistered
@@ -94,24 +98,36 @@ function ensureUnregistered(ref: usize): HEADER {
9498

9599
/** Frees an object. Must not have been registered with GC yet. */
96100
export function FREE(ref: usize): void {
97-
memory.free(changetype<usize>(ensureUnregistered(ref)));
101+
memory.free(changetype<usize>(unref(ref)));
102+
}
103+
104+
/** Registers a managed object. Cannot be free'd anymore afterwards. */
105+
@inline export function REGISTER<T>(ref: usize): T {
106+
// inline this because it's generic so we don't get a bunch of functions
107+
unref(ref).classId = __rt_classid<T>();
108+
if (GC) gc.register(ref);
109+
return changetype<T>(ref);
98110
}
99111

100-
/** Registers a managed object with GC. Cannot be free'd anymore afterwards. */
101-
@inline export function REGISTER<T>(ref: usize, parentRef: usize): void {
102-
ensureUnregistered(ref).classId = __rt_classid<T>();
103-
if (GC) __REGISTER_IMPL(ref, parentRef); // tslint:disable-line
112+
/** Links a managed object with its managed parent. */
113+
export function LINK(ref: usize, parentRef: usize): void {
114+
assert(ref >= HEAP_BASE + HEADER_SIZE); // must be a heap object
115+
var header = changetype<HEADER>(ref - HEADER_SIZE);
116+
assert(header.classId != HEADER_MAGIC && header.gc1 != 0 && header.gc2 != 0); // must be registered
117+
if (GC) gc.link(ref, parentRef); // tslint:disable-line
104118
}
105119

106120
/** ArrayBuffer base class. */
107121
export abstract class ArrayBufferBase {
122+
static readonly MAX_BYTELENGTH: i32 = MAX_SIZE_32 - HEADER_SIZE;
108123
get byteLength(): i32 {
109124
return changetype<HEADER>(changetype<usize>(this) - HEADER_SIZE).payloadSize;
110125
}
111126
}
112127

113128
/** String base class. */
114129
export abstract class StringBase {
130+
static readonly MAX_LENGTH: i32 = (MAX_SIZE_32 - HEADER_SIZE) >> 1;
115131
get length(): i32 {
116132
return changetype<HEADER>(changetype<usize>(this) - HEADER_SIZE).payloadSize >> 1;
117133
}

std/assembly/runtime/itcm.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@global namespace gc {
2+
@unsafe export function register(ref: usize): void {
3+
}
4+
@unsafe export function link(ref: usize, parentRef: usize): void {
5+
}
6+
export function collect(): void {
7+
}
8+
}

0 commit comments

Comments
 (0)