IL: reuse the cached ILTypeRef in ILTypeInfo.FromType - #20255
Conversation
For a TILObjectRepr entity, Entity.CompiledRepresentation already caches exactly 'mkRefForNestedILTypeDef scoref (enc, tdef)'. Take the ILTypeRef from there instead of rebuilding an equal one - and its enclosing-name list, and its hash - on every ILTypeInfo.FromType call. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev
|
T-Gro
left a comment
There was a problem hiding this comment.
Nice, surgical memory win — LGTM. ✅
I traced the equivalence to be sure the cached ref is identical, not just similar:
- Both branches are gated by
isILAppTy/ILTyconInfo, which requireIsILTycon, i.e.TypeReprInfo = TILObjectRepr. So the entity is always aTILObjectRepr. - For exactly that shape,
Entity.CompiledRepresentation(TypedTree.fs) computesmkRefForNestedILTypeDef ilScopeRef (ilEnclosingTypeDefs, ilTypeDef)— the same expressionFromTypewas re-running withscoref (enc, tdef)— andCompiledRepresentationForNamedTypereturns thatILAsmNamedtref. Same value, now shared. - The one thing that could have diverged is that
CompiledRepresentationconsultsExceptionInfobeforeTypeReprInfo, butTExnAbbrevRepr/TExnAsmRepronly arise for F#exceptionentities, which are neverTILObjectReprand so never reachFromType. No behavior change.
Since ILTypeRef equality/hash is structural, collapsing the ~98k per-ILTypeInfo duplicates into the shared cached instance is pure upside — the heap numbers back it up nicely, and FromType runs once per MethInfo so this was a real hot allocator.
Verified locally: FSharp.Compiler.Service builds clean for net11.0 and netstandard2.0 (only the pre-existing FS3261 in XmlDocInheritance.fs).
Tiny non-blocking nit: the note landed under ### Added — a memory optimization reads more like ### Improved, but not worth a spin.
ILTypeInfo.FromTypebuilt itsILTypeRefwithmkRefForNestedILTypeDef scoref (enc, tdef)on everycall. For a
TILObjectReprentity,Entity.CompiledRepresentationalready caches the result of thatexact expression (TypedTree.fs), reachable as
tcref.CompiledRepresentationForNamedType.FromTypeis called once perMethInfo—MethInfo.CreateILMethcalls it for every method of a type —so the uncached path produced one duplicate
ILTypeRefper method info, each re-deriving the enclosingname list and re-computing the hash. This takes the cached one instead, in both the tuple and the plain
isILAppTybranch.In a heap snapshot of
ParseAndCheckProjectover a 57-file project with 489 references,ILTypeRefwent from 150,928 live objects (8.06 MB) to 53,262 (2.84 MB) — 98,398 of them were duplicates held
one-per-
ILTypeInfo.