@asmlift/core 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,6 +24,12 @@ export interface StructureAnalysis {
24
24
  materialize: Set<Op>;
25
25
  /** cached forward reachability (successors-transitive, excluding the start block itself) */
26
26
  reachFrom: (b: Block) => Set<Block>;
27
+ /** where a value's expression ultimately renders — the anchored consumer it inlines into,
28
+ * transitively; null = several places / unresolvable (callers treat conservatively) */
29
+ emitPos: (op: Op) => { blk: Block; idx: number } | null;
30
+ /** may an op `isWrite` accepts execute between `def` and a statement at `render`, on any
31
+ * def-avoiding path — the fold-ordering gate (see the closure's comment) */
32
+ memWriteBetween: (def: Op, render: { blk: Block; idx: number }, isWrite: (x: Op) => boolean) => boolean;
27
33
  }
28
34
 
29
35
  export function analyze(fn: Fn, returnsVoid: boolean): StructureAnalysis {
@@ -406,5 +412,40 @@ export function analyze(fn: Fn, returnsVoid: boolean): StructureAnalysis {
406
412
  }
407
413
  }
408
414
  }
409
- return { useSitesOf, opIndex, opBlock, liveIn, materialize, reachFrom };
415
+ // The def→render path discipline, exported for the bitfield fold's ordering gate
416
+ // (structure.ts): may an op `isWrite` accepts execute between `def` and a statement at
417
+ // `render`, on any def-avoiding path? Same cycle-aware rules as the materialize decisions
418
+ // above — the def block's tail, the render block's head, and every between-block on a path;
419
+ // a path re-crossing the def is the next dynamic instance and does not count.
420
+ const memWriteBetween = (def: Op, render: { blk: Block; idx: number }, isWrite: (x: Op) => boolean): boolean => {
421
+ const b = opBlock.get(def)!;
422
+ const oi = opIndex.get(def)!;
423
+ const wDirty = (list: Op[], from: number, to: number): boolean => {
424
+ for (let k = from; k < to; k++) {
425
+ if (isWrite(list[k])) {
426
+ return true;
427
+ }
428
+ }
429
+ return false;
430
+ };
431
+ if (render.blk === b && oi < render.idx) {
432
+ return wDirty(b.ops, oi + 1, render.idx);
433
+ }
434
+ if (wDirty(b.ops, oi + 1, b.ops.length) || wDirty(render.blk.ops, 0, render.idx)) {
435
+ return true;
436
+ }
437
+ for (const x of reachAvoiding(b, b)) {
438
+ if (x === render.blk && !reachAvoiding(render.blk, b).has(render.blk)) {
439
+ continue; // acyclic render block: head checked
440
+ }
441
+ if (x !== render.blk && !reachAvoiding(x, b).has(render.blk)) {
442
+ continue; // not on a def→render path
443
+ }
444
+ if (wDirty(x.ops, 0, x.ops.length)) {
445
+ return true;
446
+ }
447
+ }
448
+ return false;
449
+ };
450
+ return { useSitesOf, opIndex, opBlock, liveIn, materialize, reachFrom, emitPos, memWriteBetween };
410
451
  }