Skip to content

Lift the #-shaped length foreigns to a unary length primop #247

Description

@Unisay

Problem

The length exports across the forks are one-line wrappers around the unary # operator: Data.Array.length = (function(xs) return #xs end), with the same shape behind Data.Array.ST.lengthImpl and Data.String.CodeUnits.length. To the IR these are opaque foreigns, so a saturated length xs call site pays a field read off the foreign table plus a call frame for what the VM executes as a single length opcode. Unlike the show-formatting shims, length sits on hot paths: array traversals and bounds logic call it per iteration, and in a LuaJIT trace the wrapper is a call where #xs would be a TLEN.

ForeignLift (#178) cannot lift these bodies today because the translatable subset has no representation of #: the sole unary primop is PrimNot, and the lifter's unary case handles LogicalNot only.

Approach

Add a unary PrimLen node mirroring PrimNot: the node is the Lua operator, so lowering maps it onto the existing HashOp and lifting maps HashOp back onto the node, both directions correct by construction (Note [IR primops]). Extend the lifter's unary case and allowlist Data.Array.length, Data.Array.ST.lengthImpl, and Data.String.CodeUnits.length. Nothing else is needed: # is an operator rather than a name, so the global-registry and shadowing questions that make lifting globals like tostring expensive do not arise. No constant folding over PrimLen ships with this; carrying Lua's length semantics (string bytes, table borders) in the folder buys nothing the lift needs.

While touching the module header, repoint its allowlist follow-up cross-reference at this issue; it currently points at #187, which is the closing audit of the optimisation series, and has since 9da225a.

Verification / Measurement

Focused lifter tests mirroring the runFn ones: the three length exports lift, and a # body with extra statements declines. Golden churn where lengths are called: saturated sites collapse to #xs after inlining and beta reduction, and the lifted rows drop out of the emitted FFI tables like the uncurried wrappers did. Eval goldens unchanged. The per-iteration call-frame effect becomes measurable through the #172 harness once it exists.

Prerequisites / Relations

Independent. Extends the #178 lifter the same way #198 and #227 did for the uncurried wrappers. Complements the fork-side audit #186.

Metadata

Metadata

Assignees

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