StaticArgumentTransformation

scalus.compiler.sir.transform.StaticArgumentTransformation

Static-argument transformation (T1 of docs/internal/CODEGEN_IMPROVEMENT_PLAN.md).

A parameter of a single-binding recursive Let is static when every self-call passes exactly that parameter's own variable in the same position. Static parameters are bound once by a wrapper lambda, and the inner recursion re-passes only the changing ones:

 let rec f = λp1...pn. body                     // some pi static in every self-call
 -->
 let f = λp1...pn.
     let rec f$sat = λq1...qk. body[ f e1...en := f$sat e_q1...e_qk ]
     in f$sat q1...qk                           // q = changing params, in source order

When the static parameters form a prefix (the common case, e.g. go f n acc with an invariant f) a leaner shape is used instead - the wrapper binds only the static prefix and returns the fixpoint itself, so the changing parameters are consumed by f$sat directly:

 let f = λp1...pj. let rec f$sat = λq1...qk. body' in f$sat

The wrapper keeps the original name, arity and type, so external uses of f (partial applications, higher-order uses, eta-lets) are unaffected. The fixpoint is built once per entry into f instead of once per iteration, and each iteration saves one Apply per lifted argument.

The transform is skipped (input returned unchanged) when it cannot be proven safe:

  • multi-binding recursive Lets (mutual-recursion groups) and lazy lets;
  • a rhs that is not a lambda, or has duplicate parameter names;
  • any self-reference that is not the head of a self-call saturated to full arity (a bare reference or a partial application could observe the original arity);
  • no static parameter at all.

If every parameter is static the last one is demoted to changing, because a nullary strict letrec would diverge.

Runs only when optimization is enabled - see scalus.compiler.sir.lowering.UplcPipeline.run, which is the single place this pass is invoked from. It runs after MutualRecursionElimination, so the peers-as-params static arguments that pass introduces (a $mutrec peer re-passing f1 .. f(i-1) unchanged on every self-call) are lifted here too.

Attributes

Graph
Supertypes
class Object
trait Matchable
class Any
Self type

Members list

Value members

Concrete methods

def apply(sir: SIR): SIR

Concrete fields

val SatSuffix: String

Suffix of the generated inner recursive binding.

Suffix of the generated inner recursive binding.

Attributes