Skip to content

Fold a let-bound conditional of known constructors into its eliminating reads #336

Description

@Unisay

Problem

The constructor folds fire on a constructor that is syntactically in place: reduceKnownConstructor matches ReflectCtor (Ctor …) and DataArgumentByIndex i (Ctor …), and propagateKnownCtorThroughLet (#214) extends this through a Standalone Let whose right-hand side is a saturated Ctor read only through constructor-eliminating reads. A conditional of known constructors slips through both. case (if b then Just x else Just y) of … let-binds the scrutinee (it is read more than once: a tag test plus a field read), so the eliminating reads see ReflectCtor (Ref v) and DataArgumentByIndex 0 (Ref v) with v = IfThenElse b (Ctor Just [x]) (Ctor Just [y]) — a Ref, never the conditional of Ctors — and nothing folds. The #243 distribution (pushEliminatorIntoIfBranches) does not reach it either: it fires on an eliminator whose scrutinee is directly an IfThenElse, not one hidden behind a Let binder. The runtime cost is the un-folded shape: both arms allocate a constructor table only for the tag to be read back off and the payload extracted.

Approach

An if-of-ctors sibling of propagateKnownCtorThroughLet: when a Let binder's RHS is an IfThenElse whose arms are saturated Ctor applications (of the same algebraic type) and the binder is read only through constructor-eliminating reads, distribute the reads arm-wise — e.g. rewrite the body's ReflectCtor v / DataArgumentByIndex i v occurrences under a per-arm binding, or equivalently push the whole Let body into the conditional when the duplication is admissible. The duplication concern is the same one #243 sidesteps: the body appears once per arm, so the safe subset needs either a cheapness gate on the body or the join points of #234, which is what makes the unrestricted version out of scope today.

Prerequisites / Relations

Sharpest after #234 (join points would remove the duplication constraint entirely); a gated subset (trivial bodies, or single eliminating read) can ship without it. Relates to #243 (the direct-scrutinee distribution), #214 (the Let-propagation machinery this would extend), and #206/#208 (whose specializations produce exactly such conditional-of-constructor RHSs at StateT-style boundaries).

Verification / Measurement

A focused unit test: let v = if b then Just x else Just y in (tag test + field read of v) folds to a conditional over the extracted payloads with no Ctor allocation left; a non-eliminating extra read of v declines. A golden showing the allocation disappear from the .lua, with eval output unchanged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    OptimisationA Compiler Optimisationarea: irIR / optimizer / DCE / inlinerenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions