Skip to content

MagicDo: recognize Effect/ST chains by qualified name at translation instead of by shape (removes the post-DCE ordering constraint) #182

Description

@Unisay

Problem

Two layers.

  1. Fragility: magicDo recognizes Effect/ST chains by syntactic shape, late in the pipeline. Any new rewrite that changes chain shapes — and the Lift the pure subset of foreign values into the IR: primop nodes and an allowlist-driven lifter #178Budgeted call-site inlining of dictionary methods to collapse non-Effect/ST monadic chains #180 series changes them massively — can silently starve it. A miss is not an error but a quiet fallback to thunk-nested closures (the 63x cost class) that only a benchmark would notice.
  2. Ordering constraint: the pipeline comment states magicDo must run after DCE (the statements it introduces for discard look like dead let _ = bindings) and after the optimize fixpoint (the shapes must survive it). Consequently no optimize+dce round can legally run after magicDo — a tax on pipeline evolution that grows with every new pass.

Approach

Recognize bind/pure/discard applications of Effect/ST by qualified name (the Effect.bindEffect dictionary, the pureE/bindE foreigns, their ST counterparts) at CoreFn→IR translation — the same QName-keyed technique as the #178 allowlist — and mark them with dedicated annotations (or normalize to a canonical form). magicDo then consumes marks instead of guessing shapes: a mark lives on the node and survives any rewrite, a "dead" let _ = carrying an effect mark stops being a DCE candidate, and the ordering constraint dissolves — running a fixpoint after magicDo becomes legal.

Design record: first-class Effect in the IR — considered and deferred

The alternative is making Effect structural: EffectBind/EffectPure IR constructors, as purs-backend-es does (together with its UncurriedEffectApp). Deferred because: recognition is symbol-driven either way (CoreFn carries no reliable types, so "this is Effect" is a QName question regardless of representation); the current thunk encoding keeps Effect values pure values, so every optimizer pass stays sound on plain lambda calculus without effect-awareness; and new IR constructors tax every traversal (uniquify, countFreeRefs, alphaEq, linter, DCE, the rewrite driver, lowering).

Revisit conditions: (a) effect-loop fusion becomes a goal — compiling forE/whileE/tailRecM-over-Effect into native loops on top of #181 is shorter with structural effect nodes; (b) magicDo keeps missing chains systematically despite marks, as visible in #172.

Prerequisites / Relations

Independent in the dependency graph — no hard prerequisites. It is an enabler: recognising Effect/ST by qualified name removes the post-DCE / post-fixpoint ordering constraint, so an optimize+dce fixpoint may legally run after magicDo. That restored pipeline freedom is what the #178#180 inlining series benefits from. Reuses the same QName-keyed recognition as the #178 allowlist. Relates to #172, which would carry the miss counter (and is also the trigger for revisit condition (b) above).

Verification / Measurement

#172 tracks a magicDo-miss counter on the goldens as a smoke check: zero Effect chains left in thunk form.

Metadata

Metadata

Assignees

No one assigned

    Labels

    OptimisationA Compiler Optimisationarea: irIR / optimizer / DCE / inliner

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions