Skip to content

Commit 2fa7d66

Browse files
committed
Make sure all roots are iterated by delaying builtin generation; Cleanup
1 parent 41ad2f8 commit 2fa7d66

11 files changed

+382
-842
lines changed

src/builtins.ts

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2629,8 +2629,7 @@ export function compileCall(
26292629

26302630
// gc
26312631

2632-
case "gc.iterateRoots": {
2633-
// TOOD: make it so that this can only be called from a library file?
2632+
case "iterateRoots": {
26342633
if (typeArguments) {
26352634
compiler.error(
26362635
DiagnosticCode.Type_0_is_not_generic,
@@ -2661,49 +2660,10 @@ export function compileCall(
26612660
);
26622661
return module.createUnreachable();
26632662
}
2664-
let exprs = new Array<ExpressionRef>();
2665-
for (let element of compiler.program.elementsLookup.values()) {
2666-
if (element.kind != ElementKind.GLOBAL) continue;
2667-
let global = <Global>element;
2668-
let classReference = global.type.classReference;
2669-
if (
2670-
global.is(CommonFlags.COMPILED) &&
2671-
classReference !== null &&
2672-
!classReference.hasDecorator(DecoratorFlags.UNMANAGED)
2673-
) {
2674-
if (global.is(CommonFlags.INLINED)) {
2675-
let value = global.constantIntegerValue;
2676-
exprs.push(
2677-
module.createCallIndirect(
2678-
expr,
2679-
[
2680-
compiler.options.isWasm64
2681-
? module.createI64(i64_low(value), i64_high(value))
2682-
: module.createI32(i64_low(value))
2683-
],
2684-
signatureReference.toSignatureString()
2685-
)
2686-
);
2687-
} else {
2688-
exprs.push(
2689-
module.createCallIndirect(
2690-
expr,
2691-
[
2692-
module.createGetGlobal(
2693-
global.internalName,
2694-
compiler.options.nativeSizeType
2695-
)
2696-
],
2697-
signatureReference.toSignatureString()
2698-
)
2699-
);
2700-
}
2701-
}
2702-
}
27032663
compiler.currentType = Type.void;
2704-
return exprs.length
2705-
? module.createBlock(null, exprs)
2706-
: module.createNop();
2664+
// just emit a call even if the function doesn't yet exist
2665+
compiler.needsIterateRoots = true;
2666+
return module.createCall("~iterateRoots", [ expr ], NativeType.None);
27072667
}
27082668
}
27092669
var expr = deferASMCall(compiler, prototype, operands, contextualType, reportNode);
@@ -2977,3 +2937,54 @@ export function compileAbort(
29772937
module.createUnreachable()
29782938
]);
29792939
}
2940+
2941+
/** Compiles the iterateRoots function if requires. */
2942+
export function compileIterateRoots(compiler: Compiler): void {
2943+
var module = compiler.module;
2944+
var exprs = new Array<ExpressionRef>();
2945+
2946+
for (let element of compiler.program.elementsLookup.values()) {
2947+
if (element.kind != ElementKind.GLOBAL) continue;
2948+
let global = <Global>element;
2949+
let classReference = global.type.classReference;
2950+
if (
2951+
global.is(CommonFlags.COMPILED) &&
2952+
classReference !== null &&
2953+
!classReference.hasDecorator(DecoratorFlags.UNMANAGED)
2954+
) {
2955+
if (global.is(CommonFlags.INLINED)) {
2956+
let value = global.constantIntegerValue;
2957+
exprs.push(
2958+
module.createCallIndirect(
2959+
module.createGetLocal(0, NativeType.I32),
2960+
[
2961+
compiler.options.isWasm64
2962+
? module.createI64(i64_low(value), i64_high(value))
2963+
: module.createI32(i64_low(value))
2964+
],
2965+
"iv"
2966+
)
2967+
);
2968+
} else {
2969+
exprs.push(
2970+
module.createCallIndirect(
2971+
module.createGetLocal(0, NativeType.I32),
2972+
[
2973+
module.createGetGlobal(
2974+
global.internalName,
2975+
compiler.options.nativeSizeType
2976+
)
2977+
],
2978+
"iv"
2979+
)
2980+
);
2981+
}
2982+
}
2983+
}
2984+
var typeRef = compiler.ensureFunctionType([ Type.i32 ], Type.void);
2985+
module.addFunction("~iterateRoots", typeRef, [],
2986+
exprs.length
2987+
? module.createBlock(null, exprs)
2988+
: module.createNop()
2989+
);
2990+
}

src/compiler.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55

66
import {
77
compileCall as compileBuiltinCall,
8-
compileAllocate as compileBuiltinAllocate,
9-
compileAbort as compileBuiltinAbort
8+
compileAllocate,
9+
compileAbort,
10+
compileIterateRoots
1011
} from "./builtins";
1112

1213
import {
@@ -286,6 +287,8 @@ export class Compiler extends DiagnosticEmitter {
286287
argcVar: GlobalRef = 0;
287288
/** Argument count helper setter. */
288289
argcSet: FunctionRef = 0;
290+
/** Indicates whether the iterateRoots function must be generated. */
291+
needsIterateRoots: bool = false;
289292

290293
/** Compiles a {@link Program} to a {@link Module} using the specified options. */
291294
static compile(program: Program, options: Options | null = null): Module {
@@ -408,6 +411,9 @@ export class Compiler extends DiagnosticEmitter {
408411
this.makeModuleExport(name, moduleExport.element);
409412
}
410413

414+
// set up gc
415+
if (this.needsIterateRoots) compileIterateRoots(this);
416+
411417
return module;
412418
}
413419

@@ -974,7 +980,7 @@ export class Compiler extends DiagnosticEmitter {
974980
}
975981

976982
/** Either reuses or creates the function type matching the specified signature. */
977-
private ensureFunctionType(
983+
ensureFunctionType(
978984
parameterTypes: Type[] | null,
979985
returnType: Type,
980986
thisType: Type | null = null
@@ -2022,7 +2028,7 @@ export class Compiler extends DiagnosticEmitter {
20222028
flow.set(FlowFlags.RETURNS);
20232029

20242030
// TODO: requires exception-handling spec.
2025-
return compileBuiltinAbort(this, null, statement);
2031+
return compileAbort(this, null, statement);
20262032
}
20272033

20282034
compileTryStatement(statement: TryStatement): ExpressionRef {
@@ -6524,7 +6530,7 @@ export class Compiler extends DiagnosticEmitter {
65246530
// allocate a new instance first and assign 'this' to the temp. local
65256531
exprs[0] = module.createSetLocal(
65266532
tempLocal.index,
6527-
compileBuiltinAllocate(this, classReference, expression)
6533+
compileAllocate(this, classReference, expression)
65286534
);
65296535

65306536
// once all field values have been set, return 'this'
@@ -7497,7 +7503,7 @@ export class Compiler extends DiagnosticEmitter {
74977503
var initializers = new Array<ExpressionRef>();
74987504
initializers.push(
74997505
module.createSetLocal(tempLocal.index,
7500-
compileBuiltinAllocate(this, classInstance, reportNode)
7506+
compileAllocate(this, classInstance, reportNode)
75017507
)
75027508
);
75037509

std/assembly/collector/itcm.ts

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66

77
// Largely based on the Bach Le's μgc, see: https://github.com/bullno1/ugc
88

9-
const TRACE = true;
9+
const TRACE = false;
1010

1111
import {
1212
AL_MASK,
1313
MAX_SIZE_32
1414
} from "../internal/allocator";
1515

16+
import {
17+
iterateRoots
18+
} from "../gc";
19+
1620
/** Collector states. */
1721
const enum State {
1822
/** Not yet initialized. */
@@ -31,8 +35,8 @@ var state = State.INIT;
3135
var white = 0;
3236

3337
// From and to spaces
34-
var from: ManagedObject;
35-
var to: ManagedObject;
38+
var from: ManagedObjectSet;
39+
var to: ManagedObjectSet;
3640
var iter: ManagedObject;
3741

3842
// ╒═══════════════ Managed object layout (32-bit) ════════════════╕
@@ -83,16 +87,6 @@ class ManagedObject {
8387
this.nextWithColor = (this.nextWithColor & ~3) | color;
8488
}
8589

86-
/** Inserts an object to this list. */
87-
push(obj: ManagedObject): void {
88-
var prev = this.prev;
89-
trace(" push", 3, objToRef(prev), objToRef(obj), objToRef(this));
90-
obj.next = this;
91-
obj.prev = prev;
92-
prev.next = obj;
93-
this.prev = obj;
94-
}
95-
9690
/** Unlinks this object from its list. */
9791
unlink(): void {
9892
var next = this.next;
@@ -102,12 +96,6 @@ class ManagedObject {
10296
prev.next = next;
10397
}
10498

105-
clear(): void {
106-
if (TRACE) trace(" clear", 1, objToRef(this));
107-
this.nextWithColor = changetype<usize>(this);
108-
this.prev = this;
109-
}
110-
11199
/** Marks this object as gray, that is reachable with unscanned children. */
112100
makeGray(): void {
113101
if (TRACE) trace(" makeGray", 1, objToRef(this));
@@ -119,24 +107,38 @@ class ManagedObject {
119107
}
120108
}
121109

122-
function markRoots(): void {
123-
if (TRACE) trace(" markRoots");
124-
gc.iterateRoots(function markRoot(ref: usize): void {
125-
if (TRACE) trace(" markRoot", 1, ref);
126-
if (ref) __gc_mark(ref);
127-
});
110+
/** A set of managed objects. Used for the from and to spaces. */
111+
@unmanaged
112+
class ManagedObjectSet extends ManagedObject {
113+
114+
/** Inserts an object. */
115+
push(obj: ManagedObject): void {
116+
var prev = this.prev;
117+
if (TRACE) trace(" push", 3, objToRef(prev), objToRef(obj), objToRef(this));
118+
obj.next = this;
119+
obj.prev = prev;
120+
prev.next = obj;
121+
this.prev = obj;
122+
}
123+
124+
/** Clears this list. */
125+
clear(): void {
126+
if (TRACE) trace(" clear", 1, objToRef(this));
127+
this.nextWithColor = changetype<usize>(this);
128+
this.prev = this;
129+
}
128130
}
129131

130132
/** Performs a single step according to the current state. */
131-
function step(): void {
133+
export function step(): bool {
132134
var obj: ManagedObject;
133135
switch (state) {
134136
case State.INIT: {
135137
if (TRACE) trace("gc~step/INIT");
136-
from = changetype<ManagedObject>(memory.allocate(ManagedObject.SIZE));
138+
from = changetype<ManagedObjectSet>(memory.allocate(ManagedObject.SIZE));
137139
from.visitFn = changetype<(ref: usize) => void>(<u32>-1); // would error
138140
from.clear();
139-
to = changetype<ManagedObject>(memory.allocate(ManagedObject.SIZE));
141+
to = changetype<ManagedObjectSet>(memory.allocate(ManagedObject.SIZE));
140142
to.visitFn = changetype<(ref: usize) => void>(<u32>-1); // would error
141143
to.clear();
142144
iter = to;
@@ -146,7 +148,7 @@ function step(): void {
146148
}
147149
case State.IDLE: {
148150
if (TRACE) trace("gc~step/IDLE");
149-
markRoots();
151+
iterateRoots(__gc_mark);
150152
state = State.MARK;
151153
if (TRACE) trace("gc~state = MARK");
152154
break;
@@ -160,7 +162,7 @@ function step(): void {
160162
obj.visitFn(objToRef(obj));
161163
} else {
162164
if (TRACE) trace("gc~step/MARK finish");
163-
markRoots();
165+
iterateRoots(__gc_mark);
164166
obj = iter.next;
165167
if (obj === to) {
166168
let prevFrom = from;
@@ -189,6 +191,7 @@ function step(): void {
189191
break;
190192
}
191193
}
194+
return state != State.IDLE;
192195
}
193196

194197
@inline function refToObj(ref: usize): ManagedObject {
@@ -201,7 +204,6 @@ function step(): void {
201204

202205
// Garbage collector interface
203206

204-
/** Allocates a managed object. */
205207
@global export function __gc_allocate(
206208
size: usize,
207209
visitFn: (ref: usize) => void
@@ -216,21 +218,20 @@ function step(): void {
216218
return objToRef(obj);
217219
}
218220

219-
/** Marks a reachable object. Called from the visitFn functions. */
220-
@global export function __gc_mark(ref: usize): void {
221-
if (TRACE) trace("gc.mark", 1, ref);
222-
var obj = refToObj(ref);
223-
if (obj.color == white) obj.makeGray();
224-
}
225-
226-
/** Links a managed child object to its parent object. */
227221
@global export function __gc_link(parentRef: usize, childRef: usize): void {
228222
if (TRACE) trace("gc.link", 2, parentRef, childRef);
229223
var parent = refToObj(parentRef);
230224
if (parent.color == <i32>!white && refToObj(childRef).color == white) parent.makeGray();
231225
}
232226

233-
/** Performs a full garbage collection cycle. */
227+
@global export function __gc_mark(ref: usize): void {
228+
if (TRACE) trace("gc.mark", 1, ref);
229+
if (ref) {
230+
let obj = refToObj(ref);
231+
if (obj.color == white) obj.makeGray();
232+
}
233+
}
234+
234235
@global export function __gc_collect(): void {
235236
if (TRACE) trace("gc.collect");
236237
// begin collecting if not yet collecting

std/assembly/gc.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
export namespace gc {
1+
@builtin export declare function iterateRoots(fn: (ref: usize) => void): void; // tslint:disable-line
22

3-
@builtin export declare function iterateRoots(fn: (ref: usize) => void): void; // tslint:disable-line
3+
export namespace gc {
44

55
export function allocate(size: usize, visitFn: (ref: usize) => void): usize {
66
if (isDefined(__gc_allocate)) return __gc_allocate(size, visitFn); // tslint:disable-line
77
WARNING("Calling 'gc.allocate' requires a garbage collector to be present.");
88
return <usize>unreachable();
99
}
1010

11-
export function mark(ref: usize): void {
12-
if (isDefined(__gc_mark)) { __gc_mark(ref); return; } // tslint:disable-line
13-
WARNING("Calling 'gc.mark' requires a garbage collector to be present.");
11+
export function collect(): void {
12+
if (isDefined(__gc_collect)) { __gc_collect(); return; } // tslint:disable-line
13+
WARNING("Calling 'gc.collect' requires a garbage collector to be present.");
1414
unreachable();
1515
}
1616

@@ -20,9 +20,9 @@ export namespace gc {
2020
unreachable();
2121
}
2222

23-
export function collect(): void {
24-
if (isDefined(__gc_collect)) { __gc_collect(); return; } // tslint:disable-line
25-
WARNING("Calling 'gc.collect' requires a garbage collector to be present.");
23+
export function mark(ref: usize): void {
24+
if (isDefined(__gc_mark)) { __gc_mark(ref); return; } // tslint:disable-line
25+
WARNING("Calling 'gc.mark' requires a garbage collector to be present.");
2626
unreachable();
2727
}
2828
}

0 commit comments

Comments
 (0)