mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
Give every arm of record unions that share arms a collector header
A shape union's arms all carry a collector header when one of them does. The emitter applied that rule in one pass over the unions in module order, so a header that a later union gave a shared arm did not reach the other arms of an earlier union. With First = A | B and Second = B | C, where only C is cyclic, B got a header through Second, First's trace helper then called A's trace function, and A had none: the module failed LLVM verification with "use of undefined value '@sc_rtrace_r0'". The rule now repeats until no union adds a header. Corpus 5919-record-unions-shared-arms chains Third = D | A, First = A | B and Second = B | C, so the requirement passes from Second through First to Third.
This commit is contained in:
@@ -1395,13 +1395,25 @@ export class LlEmitter {
|
||||
this.tracedUnions = traced.unions;
|
||||
// A shape union's value is whichever record it holds, and the collector
|
||||
// visits it as that record: when one arm carries a collector header,
|
||||
// every arm must (nullable-unions.ts).
|
||||
for (const union of this.nullableUnions.allShapeUnions())
|
||||
if (
|
||||
this.tracedUnions.has(union.unionId) ||
|
||||
union.arms.some((arm) => this.tracedShapes.has(`record:${arm.shapeId}`))
|
||||
)
|
||||
for (const arm of union.arms) this.tracedShapes.add(`record:${arm.shapeId}`);
|
||||
// every arm must (nullable-unions.ts). Unions that share an arm pass a
|
||||
// header on to each other's arms, so repeat until no union adds one.
|
||||
const shapeUnions = this.nullableUnions.allShapeUnions();
|
||||
for (let changed = true; changed;) {
|
||||
changed = false;
|
||||
for (const union of shapeUnions) {
|
||||
if (
|
||||
!this.tracedUnions.has(union.unionId) &&
|
||||
!union.arms.some((arm) => this.tracedShapes.has(`record:${arm.shapeId}`))
|
||||
)
|
||||
continue;
|
||||
for (const arm of union.arms) {
|
||||
const key = `record:${arm.shapeId}`;
|
||||
if (this.tracedShapes.has(key)) continue;
|
||||
this.tracedShapes.add(key);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.shapeHost = {
|
||||
sizeType: this.sizeType,
|
||||
cycleBufOffset: this.cycleBufOffset,
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5919-record-unions-shared-arms.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Record unions that share arms: Third = D | A, First = A | B and
|
||||
// Second = B | C. Only C is cyclic. Every arm of a union whose values the
|
||||
// cycle collector can visit carries a collector header, and the shared
|
||||
// arms pass that requirement from Second back through First to Third.
|
||||
type A = { kind: "a"; v: number };
|
||||
type B = { kind: "b"; w: number };
|
||||
type C = { kind: "c"; kids: C[] };
|
||||
type D = { kind: "d"; label: string };
|
||||
type Third = D | A;
|
||||
type First = A | B;
|
||||
type Second = B | C;
|
||||
|
||||
function third(u: Third): string {
|
||||
return u.kind === "d" ? u.label : `a${u.v}`;
|
||||
}
|
||||
|
||||
function first(u: First): number {
|
||||
return u.kind === "a" ? u.v : u.w;
|
||||
}
|
||||
|
||||
function second(u: Second): number {
|
||||
return u.kind === "b" ? u.w : u.kids.length + u.kids.reduce((n, k) => n + second(k), 0);
|
||||
}
|
||||
|
||||
const zs: Third[] = [
|
||||
{ kind: "d", label: "dee" },
|
||||
{ kind: "a", v: 7 },
|
||||
];
|
||||
console.log(zs.map(third).join(","));
|
||||
|
||||
const xs: First[] = [
|
||||
{ kind: "a", v: 1 },
|
||||
{ kind: "b", w: 2 },
|
||||
];
|
||||
console.log(xs.map(first).join(","));
|
||||
|
||||
const leaf: C = { kind: "c", kids: [] };
|
||||
const ys: Second[] = [
|
||||
{ kind: "b", w: 3 },
|
||||
{ kind: "c", kids: [leaf, { kind: "c", kids: [leaf] }] },
|
||||
];
|
||||
console.log(ys.map(second).join(","));
|
||||
|
||||
// One record held through two unions.
|
||||
const sharedA: A = { kind: "a", v: 5 };
|
||||
const sharedB: B = { kind: "b", w: 6 };
|
||||
const viaThird: Third = sharedA;
|
||||
const viaFirst: First[] = [sharedA, sharedB];
|
||||
const viaSecond: Second = sharedB;
|
||||
console.log(third(viaThird), viaFirst.map(first).join(","), second(viaSecond));
|
||||
|
||||
// A cyclic C reachable only through Second, dropped and rebuilt in a loop.
|
||||
let total = 0;
|
||||
for (let i = 0; i < 200; i++) {
|
||||
const node: C = { kind: "c", kids: [] };
|
||||
node.kids.push(node);
|
||||
const held: Second[] = [node, { kind: "b", w: i }];
|
||||
total += held.length + node.kids.length;
|
||||
}
|
||||
console.log("total", total);
|
||||
@@ -2136,6 +2136,7 @@
|
||||
"5913-prototype-type-error-messages.ts": 3102,
|
||||
"5914-dyn-in-class-prototype-members.ts": 2530,
|
||||
"5915-dyn-error-string-conversions.ts": 3023,
|
||||
"5919-record-unions-shared-arms.ts": 3182,
|
||||
"5920-promise-try-args.ts": 3028,
|
||||
"5921-promise-try-args-js.js": 3179,
|
||||
"5922-matchall-groups-callbacks.ts": 4418,
|
||||
@@ -5154,6 +5155,7 @@
|
||||
"5913-prototype-type-error-messages.ts": 3454,
|
||||
"5914-dyn-in-class-prototype-members.ts": 3792,
|
||||
"5915-dyn-error-string-conversions.ts": 3851,
|
||||
"5919-record-unions-shared-arms.ts": 4941,
|
||||
"5920-promise-try-args.ts": 3195,
|
||||
"5921-promise-try-args-js.js": 3082,
|
||||
"5922-matchall-groups-callbacks.ts": 5275,
|
||||
|
||||
Reference in New Issue
Block a user