Skip to content

Prototype named references in precomputed TypeMaps - #132891

Draft
jtschuster wants to merge 1 commit into
dotnet:mainfrom
jtschuster:jtschuster-named-typemap-entries
Draft

Prototype named references in precomputed TypeMaps#132891
jtschuster wants to merge 1 commit into
dotnet:mainfrom
jtschuster:jtschuster-named-typemap-entries

Conversation

@jtschuster

Copy link
Copy Markdown
Member

Discussion prototype

Alternative fix for #132811, published for design comparison rather than as a merge-ready product change. This prototype avoids adding synthetic manifest AssemblyRef entries for custom-attribute-only types by extending the precomputed TypeMap payload itself.

Design

  • Bumps the ReadyToRun format from v27 to v28 while retaining v26/v27 decoding in the runtime.
  • Encodes each external value and proxy key/value as a tagged reference:
    • kind 0: the existing (importSection, fixupIndex) representation.
    • kind 1: the original custom-attribute System.Type SerString stored as a NativeFormat string constant.
  • Keeps representable entries on the existing fixup fast path. The serialized-name path is used only when Crossgen2 cannot form an existing valid metadata reference.
  • Preserves the original SerString alongside the resolved TypeDesc; the descriptor remains the source for validation, proxy hashing, trimming, and dependency analysis.
  • Resolves named entries lazily during TypeMap API lookup through the custom-attribute type-name resolver, using the assembly that contains the TypeMap as requesting context. Proxy lookup keeps the structural TypeDesc hash and compares lazily resolved candidates by exact TypeHandle.
  • Caches successful R2R lookup results per returned managed TypeMap dictionary and module. Misses and exceptions are not cached, and no process-global native cache is introduced.
  • Removes the fallback that synthesized a #<simpleAssemblyName>:<sourceModuleIndex> ModuleRef without a corresponding source AssemblyRef.

Validation

Fail-before on the blob-only TypeMapLib5 case:

  • Ordinary execution: passed.
  • RunCrossGen2=1: failed with FileNotFoundException from FindPrecachedExternalTypeMapEntry.
  • RunCrossGen2=1 CompositeBuildMode=1: failed with the same exception.

With this prototype:

  • ./build.sh clr+libs+host: passed with 0 warnings and 0 errors.
  • Targeted Interop/TypeMap/TypeMapApp.csproj build: passed.
  • Ordinary execution: passed all 12 executed test methods.
  • Non-composite Crossgen2: passed all 13 executed test methods.
  • Composite Crossgen2: passed all 13 executed test methods.
  • R2RDump reports v28 and zero manifest metadata AssemblyRefs in TypeMapBlobOnlyLib.

The test also adds two blob-only proxy source types with the same full name from different assemblies, exercising a shared proxy hash bucket and exact lazy candidate comparison.

Tradeoffs

  • The serialized type name duplicates information already present in the custom-attribute blob, increasing TypeMap section size for unrepresentable entries.
  • Resolving a named entry crosses into managed System.Reflection.TypeNameResolver during the TypeMap API lookup. That path can GC, throw, load assemblies, and invoke resolution callbacks, but it runs only in the existing lazy lookup QCall with no loader or TypeMap dictionary lock held; eager assembly loading remains unchanged.
  • The v28 format bump makes this a coordinated compiler/runtime format change. The older untagged v26/v27 TypeMap reader remains supported.
  • This is an alternative to the manifest-AssemblyRef prototype and intentionally keeps that comparison separate.

Note

This draft pull request description was generated by GitHub Copilot.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@jkoritzinsky

Copy link
Copy Markdown
Member

Instead of adding an additional caching layer in TryGetOrLoadTypeFromPreCachedDictionary and doing the type load in native code, we should instead have the "type name string" path return the string up from here and push the results into _lazyData.

The best way to do this may be to actually encode the "could not resolve to fixup" entries differently. I think a better encoding would be as a Sequence of (string,string) tuples for the entries that we could not pre-resolve into the hash. Then we pass up those results back through the same logic as the attribute-lookup path. We avoid the cost of attribute lookup and decoding, but we also don't have an additional type load path and cache on top of the existing ones.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants