Skip to content

Budgeted call-site inlining of dictionary methods to collapse non-Effect/ST monadic chains #180

Description

@Unisay

Problem

Monadic code outside Effect/ST — Writer/State/Either/Maybe chains — compiles to nested closure chains: measured 63x against the equivalent statement sequence (1e6 iterations of a 3-bind chain, PUC 5.1). magicDo covers Effect/ST only, and flattenDeepBinds fixes nesting depth (the parser limit), not cost. The cure is specialization: inline the concrete dictionary's bind/apply/map into the call site, beta-reduce, and let case-of-known-constructor (#177) collapse the constructor matches into straight-line code.

The current heuristic refuses by construction: isInlinableExpr accepts refs, literals, annotations, and used-once bindings — a dictionary method is a multi-use non-trivial lambda, so it is never inlined into call sites.

Approach

GHC-style budgeted call-site inlining: inline a multi-use binding into a specific saturated application site when (a) the head resolves to a lambda, including through ObjectProp of a known top-level dictionary literal, (b) the application is saturated with respect to manifest arity, and (c) the inlinee's size is within a tunable budget (the analogue of GHC's unfolding-use-threshold), with growth accounted per fixpoint round — the change-flag infrastructure from #144 is in place. Separately, projection through a Ref: ObjectProp (Ref dict) "bind" resolves to the method expression by looking at the binding's RHS without inlining the whole dictionary.

Prerequisites / Relations

The prerequisites that complete the cascade are #177 (known-constructor folds) and #178 (primops give arithmetic inside chains a bottom); with #177 alone the Maybe/Either case is already useful, collapsing into if-chains. Several open issues interact: #167 is a discipline prerequisite (without let-binding non-trivial arguments, aggressive inlining multiplies work); #143 must be fixed with or before this, since the heuristic stands on reference counts that go stale during inlining; #142 becomes visible because more inlining means more fixpoint pressure on optimizer compile time; and the budget must interact predictably with the @inline annotations of #169 and #171.

Verification / Measurement

The win is measured by #172's bind-chain macrobenchmark — the 63x case — with the LongWriterBind/LongStateBind goldens as ready-made material. Code growth is the main risk, bounded by the size budget and watched via golden.lua sizes. Because the transform is behaviour-preserving, runtime output must stay identical: the eval goldens must not move.

Metadata

Metadata

Assignees

Labels

OptimisationA Compiler Optimisationarea: irIR / optimizer / DCE / inliner

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions