Skip to content

Perf: low-risk codegen cleanups (dead box/unbox elision + loop-invariant hoisting) #859

Description

@nickna

Part of #856 (perf epic). Lowest-risk, start-today candidate — correctness-neutral codegen cleanups that help every numeric path. Good for validating the cost model (#856) before the larger refactors.

Summary

The emitted IL contains pervasive redundant work that a small peephole / loop-invariant pass would remove. Two independent items, grouped because both are local IL-cleanup passes:

(a) Dead box → unbox round-trips and discarded boxed results

A value already typed float64 on the stack is boxed and then immediately coerced back via a $Runtime call:

// stringWork scan — s.length:
conv.r8
box Double
call $Runtime::ConvertToNumber(object)   // <-- box then immediately unbox

// stringWork — charCodeAt result, and again for the index arg:
box Double → $Runtime::ToNumber(object)
box Double → $Runtime::ConvertToNumber(object)

And boxed values computed only to be thrown away — the push return (array length) is computed, boxed, and popped:

// countPrimes / arrayMethodWork after ArrayPush:
callvirt List`1::get_Count()
conv.r8
box Double
pop                                       // <-- boxed for nothing

Fix: when the static stack type already matches the consumer, skip the box + $Runtime coercion; when a boxed result is unused, don't compute/box it.

(b) Loop-invariant recomputation (LICM/CSE)

stringWork's scan recomputes UnwrapStringReceiver(s) + get_Length() every iteration although s is not mutated in that loop; array loops recompute .Count similarly.

IL_005a: call string $Runtime::UnwrapStringReceiver(object)  // loop-invariant
IL_005f: call instance int32 String::get_Length()            // loop-invariant

Fix: hoist provably loop-invariant receiver-unwrap / length / count out of the loop header.

Notes

Scope / priority

Low risk, broad benefit. Good first perf PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    performanceRuntime/codegen performance work

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions