Change the ILC DependencyFramework to use sinks instead of lists - #132955

Open
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink
Open

Change the ILC DependencyFramework to use sinks instead of lists#132955
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink

Conversation

@agocke

Copy link
Copy Markdown
Member

The current DependencyFramework algorithm is based on creating intermediate lists of dependencies that are processed into the whole dependency graph. This intermediate list production is very expensive. By moving to a series of worker-owned sinks that are threaded down through the dependency framework we can avoid intermediate container allocation and enumeration.

Rough dependency analysis numbers, measured on the Azure MCP server (an extremely large AOT application):

┌─────────┬──────────────┬──────────────┬─────────────────────┐
│ Run │ Baseline │ Serial sinks │ Reduction │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 1 │ 7,376.54 MiB │ 6,659.55 MiB │ 716.99 MiB / 9.72% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 2 │ 7,297.80 MiB │ 6,464.73 MiB │ 833.07 MiB / 11.42% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ Average │ 7,337.17 MiB │ 6,562.14 MiB │ 775.03 MiB / 10.56% │
└─────────┴──────────────┴──────────────┴─────────────────────┘

The largest savings were directly from the sink conversion: about 517 MiB from conditional dependency arrays, 163 MiB from static dependency arrays, 125 MiB from conditional iterators, 121 MiB from static enumerators, and 95 MiB from DependencyList objects.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 07268f0e-f7cc-4a0a-87cf-fcb906277823
CopilotAI lite review requested due to automatic review settings August 30, 2026 21:48
@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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away…
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This…
What changed in this PR

This PR refactors the ILCompiler dependency analysis pipeline (ILC/ILTrim/ReadyToRun tooling) from “produce intermediate dependency lists/enumerables” to a sink-based model where nodes push dependencies directly into a provided sink, aiming to reduce allocations and expensive intermediate enumeration.

Changes:

  • Introduces IDependencySink / DependencySink and updates the dependency framework contracts (IDependencyNode) to AddStaticDependencies / AddConditionalDependencies / SearchDynamicDependencies sink-based APIs.
  • Migrates many dependency nodes and helper algorithms from IEnumerable<...>/DependencyList building to directly writing to sinks.
  • Removes ComputedStaticDependencyNode and updates a variety of call sites (ILTrim + ReadyToRun + AOT compiler) to the new pattern.
FileDescription
src/​coreclr/​tools/​ILTrim.Core/​RootingHelpers.csSwitch reflected-member rooting helpers to sink-based collection.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​VirtualMethodUseNode.csConvert static/conditional/dynamic dependency reporting to sink-based overrides.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenWriterNode.csConvert conditional/dynamic dependency APIs to sink-based no-op implementations.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeSpecificationNode.csPush analyzed TypeSpec signature dependencies directly into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeReferenceNode.csReplace yielded dependencies with sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeDefinitionNode.csConvert static + conditional dependencies to sink-based adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​StandaloneSignatureNode.csRoute standalone signature analysis dependencies into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​PropertyDefinitionNode.csConvert CA + owning-type dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ParameterNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleDefinitionNode.csConvert module-level dependencies + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodSpecificationNode.csConvert signature + instantiated-method dependency reporting to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodImplementationNode.csConvert yielded token dependencies to sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MemberReferenceNode.csConvert signature/token analysis dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ManifestResourceNode.csConvert descriptor/implementation/CA dependencies to sink usage and update asserts.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterConstraintNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​FieldDefinitionNode.csConvert signature/constant/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​EventDefinitionNode.csConvert event + accessor + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ConstantNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyDefinitionNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​NodeFactory.csUpdate NullDependencyNode to sink-based dependency APIs.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​MethodBodyNode.csConvert cached dependency list emission to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​InterfaceUseNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​ConstructedTypeNode.csConvert static + conditional dependencies to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​AssemblyRootNode.csConvert rooting deps to sink adds and update empty conditional/dynamic implementations.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​VirtualMethodUseNode.csConvert dependencies and metadata manager calls to sink usage.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ObjectNode.csRefactor relocation + wasm signature deps to be written directly to sink.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​INodeWithRuntimeDeterminedDependencies.csReplace “return deps enumerable” with sink-based AddDependencies signature.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ExternSymbolNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​JitInterface/​CorInfoImpl.RyuJit.csRoute virtual method slot-use tracking deps to the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​Compiler/​DependencyAnalysis/​MethodCodeNode.csConvert conditional + non-relocation dependencies to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​JitInterface/​CorInfoImpl.ReadyToRun.csRoute additional dependencies through the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​TypeMapAssemblyTargetsNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunProxyTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunExternalTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​FileLayoutOptimizer.csUpdate dependency traversal to use reusable per-depth sinks/lists.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRunCodegenNodeFactory.csUpdate helper to accept sink rather than ref-list allocation.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmR2RToInterpreterThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmInterpreterToR2RThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​TypeFixupSignature.csConvert non-relocation deps + helper signatures to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SymbolNodeRange.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​StringDiscoverableAssemblyStubNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SignatureEmbeddedPointerIndirectionNode.csConvert reloc dependency to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​PrecodeMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​NewArrayFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodWithGCInfo.csConvert GC info/fixup deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodGCInfoNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​InstrumentationDataTableNode.csConvert embedded PGO dependency emission to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportThunk.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportSectionNode.csConvert static deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​Import.csConvert static deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​GenericLookupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ExceptionInfoLookupTableNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelegateCtorSignature.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperMethodImport.csConvert base deps + canonical method dep to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperImport.csConvert conditional “helper present” deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DeferredTillPhaseNode.csConvert static deps to sink adds and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DebugDirectoryEntryNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​InheritedVirtualMethodsNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayInterfaceMethodsNode.csConvert conditional deps to sink usage and early-return pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​AllMethodsOnTypeNode.csConvert method enumeration deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ILCompiler.DependencyAnalysisFramework.csprojAdd new sink source files; remove ComputedStaticDependencyNode compile include.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencySink.csIntroduce public sink interfaces for static/conditional dependency emission.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencyNode.csUpdate dependency node interface to sink-based methods.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ComputedStaticDependencyNode.csRemove unused list-based computed-dependency node type.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedTypeMapManager.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedInteropStubManager.csConvert interop dependency hooks to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​TypePreinit.csConvert conditional dependency callback to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​RuntimeConstructableTypeDependencies.csConvert conditional dependency list parameter to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​ReachabilityInstrumentationProvider.csUpdate explicit IDependencyNode implementations to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​InteropStubManager.csUpdate abstract interop dependency hooks to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​EmptyInteropStubManager.csUpdate empty interop stub manager overrides to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DescriptorMarker.csUpdate RootingHelpers call sites to pass sink/list directly.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VTableSliceNode.csConvert static + conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VariantInterfaceMethodUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeThreadStaticIndexNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csConvert deps to sink usage and remove cached dependency list.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TrimmingDescriptorNode.csConvert descriptor-derived deps to sink enumeration adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeInstanceMethodNode.csConvert conditional dep array to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StructMarshallingDataNode.csConvert deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StringAllocatorMethodNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StaticsInfoHashtableNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SerializedFrozenObjectNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SealedVTableNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ScannedMethodNode.csConvert cached deps emission to sink iteration.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeMethodHandleNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeFieldHandleNode.csConvert ldtoken deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionVirtualInvokeMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionInvokeSupportDependencyAlgorithm.csConvert params-array dependency helper to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedTypeNode.csConvert deps to sink adds + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedMethodNode.csConvert reflectability deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedFieldNode.csConvert reflectability + signature deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedDelegateNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapNode.csConvert conditional deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​PropertyMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeFlowDependenciesNode.csRoute dataflow-derived deps into sink and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeCalledNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NotReadOnlyFieldNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NonGCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csConvert module-use deps helper to sink-based signature (and should remove leftover lazy-init).
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleMetadataNode.csConvert static + conditional CA deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MethodParameterMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MetadataEETypeNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidProxyTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidExternalTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceGenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchMapNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InlineableStringsResourceNode.csConvert helper signature to accept sink; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GvmDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodImplNode.csConvert deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericTypesTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericStaticBaseInfoNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericDefinitionEETypeNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FunctionPointerMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FrozenObjectNode.csConvert relocation dependency discovery to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapNode.csConvert conditional/static deps to sink usage + update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EventMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedTrimmingDescriptorNode.csConvert descriptor-derived deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DispatchCellNode.csConvert cell dependency helpers to sink-based emitters.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DictionaryLayoutNode.csConvert static + conditional deps to sink usage and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateTargetVirtualMethodNode.csConvert static/conditional deps to sink-based no-ops / adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateMarshallingDataNode.csConvert static deps to sink AddRange and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DataflowAnalyzedTypeDefinitionNode.csConvert dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CustomAttributeMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ConstructedEETypeNode.csConvert non-relocation deps + interop hook call to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CodeBasedDependencyAlgorithm.csConvert static + conditional dependency helpers to sink interfaces.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CanonicalDefinitionEETypeNode.csConvert non-relocation deps to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedProxyTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedExternalTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalysisCharacteristicNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AddressTakenMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​ReflectionMethodBodyScanner.csRoute Object.GetType dataflow dependencies into an injected sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​GenericArgumentDataFlow.csConvert generic argument dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​AnalysisBasedInteropStubManager.csUpdate interop manager overrides to sink-based signatures.

Comment on lines +61 to 65
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory context)
{
if (_staticDependencies == null)
{
_staticDependencies = new DependencyList();

foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationMethod);

foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries())
InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationType, entry.ImplementationMethod);
}
foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(sink, context, entry.CallingMethod, entry.ImplementationMethod);

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings August 31, 2026 01:12

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:17

  • dependencyList ??= new DependencyList(); no longer propagates a newly-created list back to the caller (the parameter is no longer ref). If dependencyList were ever null, this would silently drop dependencies into a temporary list, and it also keeps a now-unnecessary DependencyList alias. Since all current callers pass a non-null sink, remove the null-coalescing allocation and the unused alias import to avoid misleading behavior and unnecessary allocations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 02:40

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:16

  • dependencyList ??= new DependencyList(); no longer behaves like the old ref DependencyList pattern. If a caller passes null, this only reassigns the local parameter and any dependencies added here are dropped on return (and the DependencyList alias becomes the only use of that using). Since callers are expected to provide a sink, it’s better to require non-null and avoid silently losing dependencies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 03:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”…
Pre-existing issues (1)
SeverityFinding
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Issues resolved since last review (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View resolved comment

Comment on lines +46 to +48
private readonly List<Dependency> _dependencies = new List<Dependency>();
private readonly DependencyNodeCore<DependencyContextType>.DependencyList? _staticDependencies;
private readonly DependencyNodeCore<DependencyContextType>.CombinedDependencyList? _conditionalDependencies;

@MichalStrehovskyMichalStrehovsky left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have unit tests for the framework in #130849. We haven't really done any changes to the framework ever since it was added in dotnet/corert#42 so there wasn't much of a need. I thought I might need to do a change this summer so I ported the tests, but then ended up not making the change anyway so the test PR wasn't a priority.

Comment on lines +55 to 63
protected override void ComputeNonRelocationBasedDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)
{
DependencyList dependencies = base.ComputeNonRelocationBasedDependencies(factory);
DependencySink<NodeFactory> dependencies = sink;
base.ComputeNonRelocationBasedDependencies(sink, factory);

factory.AddVirtualMethodDiscoveryDependencies(ref dependencies, _arrayType);
factory.AddVirtualMethodDiscoveryDependencies(dependencies, _arrayType);

return dependencies;
return;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patterns like this look sloppy (not going to comment on all).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair -- I thought the same thing but I thought it might make the review easier. I'll have copilot clean it up

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not just the return statement, also the DependencySink<NodeFactory> dependencies = sink; variable renaming. In this specific case the diff would actually be smaller without the variable renaming (doesn't apply everywhere).

}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DependencyListEntry is a nested class so it doesn't need to repeat the NodeFactory part and we don't need a using for the framework. It would definitely look better here for DependencySink to be like that too, not sure if there's any drawbacks though.

}

[Fact]
public void DependencyAnalyzerComputesDeferredStaticDependenciesBeforeDynamicDependencies()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand what this is testing.

Comment on lines +44 to +52
public override void AddDependenciesDueToMethodCodePresence(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)
{
}

public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
public override void AddInterestingInteropConstructedTypeDependencies(DependencySink<NodeFactory> dependencies, NodeFactory factory, TypeDesc type)
{
}

public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
public override void AddMarshalAPIsGenericDependencies(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the rules for when this should be IDependencySink interface and when it should use DependencySink class?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In short, IDependencySink should only be used when at least one of the callers uses a DependencyList to construct a temporary list and scan the results. This isn't very common -- I believe there are only two actual roots and I tried to move every place possible over to a DependencySink.

The biggest reason in general to use IDependencySink is when you want to review and filter results after receiving them. If you don't care, you might as well use the main sink, which doesn't guarantee those semantics (and can be kind of heavyweight in that case).

I thought about adding a comment here, but it wasn't quite clear where I should add it. Let me take another shot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So IDependencySink is the abstraction we need and DependencySink is an optimization when we can get away with it. How perf is the optimization really buying?

If everything is behind an interface, we could potentially also look into whether the sink (as used by the analysis engine) really needs to build a list, or whether we could run the marking logic the moment Add is called (those experiments can be done separately).

CopilotAI review requested due to automatic review settings August 31, 2026 20:53

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment

The DependencySink type was holding onto a special CombinedDependencyList just for
ShowMethodNode because ShadowMethodNode wanted to filter the canonical dependencies
in order to construct the specialized dependencies based on the runtime-determined
dependencies. This required wrapping a DependencyList in a DependencySink just to
collect dependencies and then filter.
By refactoring to have the IMethodBodyNode directly support specializing the
runtime-determined dependencies we can avoid saving and filtering the elements and
just filter before we add.
CopilotAI review requested due to automatic review settings September 1, 2026 01:28

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencySink.cs:114

  • DependencySink.Add(node, otherReasonNode, reason) bypasses the _staticDependencies fast-path, so if a sink is constructed around a DependencyList (e.g., new DependencySink(list)), using this overload will silently record the dependency in the sink’s private _dependencies list instead of the provided DependencyList. Adding a debug-time guard here would help catch accidental misuse (especially since DependencySink is also used as an IConditionalDependencySink).

Sign up for freeto 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.

3 participants

@agocke@MichalStrehovsky
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Change the ILC DependencyFramework to use sinks instead of lists - #132955

Open
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink
Open

Change the ILC DependencyFramework to use sinks instead of lists#132955
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink

Conversation

@agocke

Copy link
Copy Markdown
Member

The current DependencyFramework algorithm is based on creating intermediate lists of dependencies that are processed into the whole dependency graph. This intermediate list production is very expensive. By moving to a series of worker-owned sinks that are threaded down through the dependency framework we can avoid intermediate container allocation and enumeration.

Rough dependency analysis numbers, measured on the Azure MCP server (an extremely large AOT application):

┌─────────┬──────────────┬──────────────┬─────────────────────┐
│ Run │ Baseline │ Serial sinks │ Reduction │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 1 │ 7,376.54 MiB │ 6,659.55 MiB │ 716.99 MiB / 9.72% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 2 │ 7,297.80 MiB │ 6,464.73 MiB │ 833.07 MiB / 11.42% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ Average │ 7,337.17 MiB │ 6,562.14 MiB │ 775.03 MiB / 10.56% │
└─────────┴──────────────┴──────────────┴─────────────────────┘

The largest savings were directly from the sink conversion: about 517 MiB from conditional dependency arrays, 163 MiB from static dependency arrays, 125 MiB from conditional iterators, 121 MiB from static enumerators, and 95 MiB from DependencyList objects.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 07268f0e-f7cc-4a0a-87cf-fcb906277823
CopilotAI lite review requested due to automatic review settings August 30, 2026 21:48
@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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away…
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This…
What changed in this PR

This PR refactors the ILCompiler dependency analysis pipeline (ILC/ILTrim/ReadyToRun tooling) from “produce intermediate dependency lists/enumerables” to a sink-based model where nodes push dependencies directly into a provided sink, aiming to reduce allocations and expensive intermediate enumeration.

Changes:

  • Introduces IDependencySink / DependencySink and updates the dependency framework contracts (IDependencyNode) to AddStaticDependencies / AddConditionalDependencies / SearchDynamicDependencies sink-based APIs.
  • Migrates many dependency nodes and helper algorithms from IEnumerable<...>/DependencyList building to directly writing to sinks.
  • Removes ComputedStaticDependencyNode and updates a variety of call sites (ILTrim + ReadyToRun + AOT compiler) to the new pattern.
FileDescription
src/​coreclr/​tools/​ILTrim.Core/​RootingHelpers.csSwitch reflected-member rooting helpers to sink-based collection.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​VirtualMethodUseNode.csConvert static/conditional/dynamic dependency reporting to sink-based overrides.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenWriterNode.csConvert conditional/dynamic dependency APIs to sink-based no-op implementations.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeSpecificationNode.csPush analyzed TypeSpec signature dependencies directly into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeReferenceNode.csReplace yielded dependencies with sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeDefinitionNode.csConvert static + conditional dependencies to sink-based adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​StandaloneSignatureNode.csRoute standalone signature analysis dependencies into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​PropertyDefinitionNode.csConvert CA + owning-type dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ParameterNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleDefinitionNode.csConvert module-level dependencies + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodSpecificationNode.csConvert signature + instantiated-method dependency reporting to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodImplementationNode.csConvert yielded token dependencies to sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MemberReferenceNode.csConvert signature/token analysis dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ManifestResourceNode.csConvert descriptor/implementation/CA dependencies to sink usage and update asserts.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterConstraintNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​FieldDefinitionNode.csConvert signature/constant/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​EventDefinitionNode.csConvert event + accessor + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ConstantNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyDefinitionNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​NodeFactory.csUpdate NullDependencyNode to sink-based dependency APIs.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​MethodBodyNode.csConvert cached dependency list emission to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​InterfaceUseNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​ConstructedTypeNode.csConvert static + conditional dependencies to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​AssemblyRootNode.csConvert rooting deps to sink adds and update empty conditional/dynamic implementations.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​VirtualMethodUseNode.csConvert dependencies and metadata manager calls to sink usage.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ObjectNode.csRefactor relocation + wasm signature deps to be written directly to sink.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​INodeWithRuntimeDeterminedDependencies.csReplace “return deps enumerable” with sink-based AddDependencies signature.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ExternSymbolNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​JitInterface/​CorInfoImpl.RyuJit.csRoute virtual method slot-use tracking deps to the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​Compiler/​DependencyAnalysis/​MethodCodeNode.csConvert conditional + non-relocation dependencies to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​JitInterface/​CorInfoImpl.ReadyToRun.csRoute additional dependencies through the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​TypeMapAssemblyTargetsNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunProxyTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunExternalTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​FileLayoutOptimizer.csUpdate dependency traversal to use reusable per-depth sinks/lists.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRunCodegenNodeFactory.csUpdate helper to accept sink rather than ref-list allocation.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmR2RToInterpreterThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmInterpreterToR2RThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​TypeFixupSignature.csConvert non-relocation deps + helper signatures to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SymbolNodeRange.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​StringDiscoverableAssemblyStubNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SignatureEmbeddedPointerIndirectionNode.csConvert reloc dependency to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​PrecodeMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​NewArrayFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodWithGCInfo.csConvert GC info/fixup deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodGCInfoNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​InstrumentationDataTableNode.csConvert embedded PGO dependency emission to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportThunk.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportSectionNode.csConvert static deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​Import.csConvert static deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​GenericLookupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ExceptionInfoLookupTableNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelegateCtorSignature.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperMethodImport.csConvert base deps + canonical method dep to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperImport.csConvert conditional “helper present” deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DeferredTillPhaseNode.csConvert static deps to sink adds and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DebugDirectoryEntryNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​InheritedVirtualMethodsNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayInterfaceMethodsNode.csConvert conditional deps to sink usage and early-return pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​AllMethodsOnTypeNode.csConvert method enumeration deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ILCompiler.DependencyAnalysisFramework.csprojAdd new sink source files; remove ComputedStaticDependencyNode compile include.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencySink.csIntroduce public sink interfaces for static/conditional dependency emission.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencyNode.csUpdate dependency node interface to sink-based methods.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ComputedStaticDependencyNode.csRemove unused list-based computed-dependency node type.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedTypeMapManager.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedInteropStubManager.csConvert interop dependency hooks to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​TypePreinit.csConvert conditional dependency callback to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​RuntimeConstructableTypeDependencies.csConvert conditional dependency list parameter to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​ReachabilityInstrumentationProvider.csUpdate explicit IDependencyNode implementations to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​InteropStubManager.csUpdate abstract interop dependency hooks to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​EmptyInteropStubManager.csUpdate empty interop stub manager overrides to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DescriptorMarker.csUpdate RootingHelpers call sites to pass sink/list directly.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VTableSliceNode.csConvert static + conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VariantInterfaceMethodUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeThreadStaticIndexNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csConvert deps to sink usage and remove cached dependency list.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TrimmingDescriptorNode.csConvert descriptor-derived deps to sink enumeration adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeInstanceMethodNode.csConvert conditional dep array to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StructMarshallingDataNode.csConvert deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StringAllocatorMethodNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StaticsInfoHashtableNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SerializedFrozenObjectNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SealedVTableNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ScannedMethodNode.csConvert cached deps emission to sink iteration.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeMethodHandleNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeFieldHandleNode.csConvert ldtoken deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionVirtualInvokeMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionInvokeSupportDependencyAlgorithm.csConvert params-array dependency helper to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedTypeNode.csConvert deps to sink adds + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedMethodNode.csConvert reflectability deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedFieldNode.csConvert reflectability + signature deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedDelegateNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapNode.csConvert conditional deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​PropertyMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeFlowDependenciesNode.csRoute dataflow-derived deps into sink and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeCalledNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NotReadOnlyFieldNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NonGCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csConvert module-use deps helper to sink-based signature (and should remove leftover lazy-init).
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleMetadataNode.csConvert static + conditional CA deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MethodParameterMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MetadataEETypeNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidProxyTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidExternalTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceGenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchMapNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InlineableStringsResourceNode.csConvert helper signature to accept sink; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GvmDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodImplNode.csConvert deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericTypesTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericStaticBaseInfoNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericDefinitionEETypeNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FunctionPointerMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FrozenObjectNode.csConvert relocation dependency discovery to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapNode.csConvert conditional/static deps to sink usage + update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EventMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedTrimmingDescriptorNode.csConvert descriptor-derived deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DispatchCellNode.csConvert cell dependency helpers to sink-based emitters.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DictionaryLayoutNode.csConvert static + conditional deps to sink usage and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateTargetVirtualMethodNode.csConvert static/conditional deps to sink-based no-ops / adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateMarshallingDataNode.csConvert static deps to sink AddRange and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DataflowAnalyzedTypeDefinitionNode.csConvert dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CustomAttributeMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ConstructedEETypeNode.csConvert non-relocation deps + interop hook call to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CodeBasedDependencyAlgorithm.csConvert static + conditional dependency helpers to sink interfaces.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CanonicalDefinitionEETypeNode.csConvert non-relocation deps to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedProxyTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedExternalTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalysisCharacteristicNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AddressTakenMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​ReflectionMethodBodyScanner.csRoute Object.GetType dataflow dependencies into an injected sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​GenericArgumentDataFlow.csConvert generic argument dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​AnalysisBasedInteropStubManager.csUpdate interop manager overrides to sink-based signatures.

Comment on lines +61 to 65
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory context)
{
if (_staticDependencies == null)
{
_staticDependencies = new DependencyList();

foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationMethod);

foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries())
InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationType, entry.ImplementationMethod);
}
foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(sink, context, entry.CallingMethod, entry.ImplementationMethod);

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings August 31, 2026 01:12

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:17

  • dependencyList ??= new DependencyList(); no longer propagates a newly-created list back to the caller (the parameter is no longer ref). If dependencyList were ever null, this would silently drop dependencies into a temporary list, and it also keeps a now-unnecessary DependencyList alias. Since all current callers pass a non-null sink, remove the null-coalescing allocation and the unused alias import to avoid misleading behavior and unnecessary allocations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 02:40

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:16

  • dependencyList ??= new DependencyList(); no longer behaves like the old ref DependencyList pattern. If a caller passes null, this only reassigns the local parameter and any dependencies added here are dropped on return (and the DependencyList alias becomes the only use of that using). Since callers are expected to provide a sink, it’s better to require non-null and avoid silently losing dependencies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 03:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”…
Pre-existing issues (1)
SeverityFinding
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Issues resolved since last review (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View resolved comment

Comment on lines +46 to +48
private readonly List<Dependency> _dependencies = new List<Dependency>();
private readonly DependencyNodeCore<DependencyContextType>.DependencyList? _staticDependencies;
private readonly DependencyNodeCore<DependencyContextType>.CombinedDependencyList? _conditionalDependencies;

@MichalStrehovskyMichalStrehovsky left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have unit tests for the framework in #130849. We haven't really done any changes to the framework ever since it was added in dotnet/corert#42 so there wasn't much of a need. I thought I might need to do a change this summer so I ported the tests, but then ended up not making the change anyway so the test PR wasn't a priority.

Comment on lines +55 to 63
protected override void ComputeNonRelocationBasedDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)
{
DependencyList dependencies = base.ComputeNonRelocationBasedDependencies(factory);
DependencySink<NodeFactory> dependencies = sink;
base.ComputeNonRelocationBasedDependencies(sink, factory);

factory.AddVirtualMethodDiscoveryDependencies(ref dependencies, _arrayType);
factory.AddVirtualMethodDiscoveryDependencies(dependencies, _arrayType);

return dependencies;
return;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patterns like this look sloppy (not going to comment on all).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair -- I thought the same thing but I thought it might make the review easier. I'll have copilot clean it up

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not just the return statement, also the DependencySink<NodeFactory> dependencies = sink; variable renaming. In this specific case the diff would actually be smaller without the variable renaming (doesn't apply everywhere).

}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DependencyListEntry is a nested class so it doesn't need to repeat the NodeFactory part and we don't need a using for the framework. It would definitely look better here for DependencySink to be like that too, not sure if there's any drawbacks though.

}

[Fact]
public void DependencyAnalyzerComputesDeferredStaticDependenciesBeforeDynamicDependencies()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand what this is testing.

Comment on lines +44 to +52
public override void AddDependenciesDueToMethodCodePresence(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)
{
}

public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
public override void AddInterestingInteropConstructedTypeDependencies(DependencySink<NodeFactory> dependencies, NodeFactory factory, TypeDesc type)
{
}

public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
public override void AddMarshalAPIsGenericDependencies(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the rules for when this should be IDependencySink interface and when it should use DependencySink class?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In short, IDependencySink should only be used when at least one of the callers uses a DependencyList to construct a temporary list and scan the results. This isn't very common -- I believe there are only two actual roots and I tried to move every place possible over to a DependencySink.

The biggest reason in general to use IDependencySink is when you want to review and filter results after receiving them. If you don't care, you might as well use the main sink, which doesn't guarantee those semantics (and can be kind of heavyweight in that case).

I thought about adding a comment here, but it wasn't quite clear where I should add it. Let me take another shot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So IDependencySink is the abstraction we need and DependencySink is an optimization when we can get away with it. How perf is the optimization really buying?

If everything is behind an interface, we could potentially also look into whether the sink (as used by the analysis engine) really needs to build a list, or whether we could run the marking logic the moment Add is called (those experiments can be done separately).

CopilotAI review requested due to automatic review settings August 31, 2026 20:53

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment

The DependencySink type was holding onto a special CombinedDependencyList just for
ShowMethodNode because ShadowMethodNode wanted to filter the canonical dependencies
in order to construct the specialized dependencies based on the runtime-determined
dependencies. This required wrapping a DependencyList in a DependencySink just to
collect dependencies and then filter.
By refactoring to have the IMethodBodyNode directly support specializing the
runtime-determined dependencies we can avoid saving and filtering the elements and
just filter before we add.
CopilotAI review requested due to automatic review settings September 1, 2026 01:28

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencySink.cs:114

  • DependencySink.Add(node, otherReasonNode, reason) bypasses the _staticDependencies fast-path, so if a sink is constructed around a DependencyList (e.g., new DependencySink(list)), using this overload will silently record the dependency in the sink’s private _dependencies list instead of the provided DependencyList. Adding a debug-time guard here would help catch accidental misuse (especially since DependencySink is also used as an IConditionalDependencySink).

Sign up for freeto 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.

3 participants

@agocke@MichalStrehovsky
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Change the ILC DependencyFramework to use sinks instead of lists - #132955

Open
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink
Open

Change the ILC DependencyFramework to use sinks instead of lists#132955
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink

Conversation

@agocke

Copy link
Copy Markdown
Member

The current DependencyFramework algorithm is based on creating intermediate lists of dependencies that are processed into the whole dependency graph. This intermediate list production is very expensive. By moving to a series of worker-owned sinks that are threaded down through the dependency framework we can avoid intermediate container allocation and enumeration.

Rough dependency analysis numbers, measured on the Azure MCP server (an extremely large AOT application):

┌─────────┬──────────────┬──────────────┬─────────────────────┐
│ Run │ Baseline │ Serial sinks │ Reduction │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 1 │ 7,376.54 MiB │ 6,659.55 MiB │ 716.99 MiB / 9.72% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 2 │ 7,297.80 MiB │ 6,464.73 MiB │ 833.07 MiB / 11.42% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ Average │ 7,337.17 MiB │ 6,562.14 MiB │ 775.03 MiB / 10.56% │
└─────────┴──────────────┴──────────────┴─────────────────────┘

The largest savings were directly from the sink conversion: about 517 MiB from conditional dependency arrays, 163 MiB from static dependency arrays, 125 MiB from conditional iterators, 121 MiB from static enumerators, and 95 MiB from DependencyList objects.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 07268f0e-f7cc-4a0a-87cf-fcb906277823
CopilotAI lite review requested due to automatic review settings August 30, 2026 21:48
@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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away…
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This…
What changed in this PR

This PR refactors the ILCompiler dependency analysis pipeline (ILC/ILTrim/ReadyToRun tooling) from “produce intermediate dependency lists/enumerables” to a sink-based model where nodes push dependencies directly into a provided sink, aiming to reduce allocations and expensive intermediate enumeration.

Changes:

  • Introduces IDependencySink / DependencySink and updates the dependency framework contracts (IDependencyNode) to AddStaticDependencies / AddConditionalDependencies / SearchDynamicDependencies sink-based APIs.
  • Migrates many dependency nodes and helper algorithms from IEnumerable<...>/DependencyList building to directly writing to sinks.
  • Removes ComputedStaticDependencyNode and updates a variety of call sites (ILTrim + ReadyToRun + AOT compiler) to the new pattern.
FileDescription
src/​coreclr/​tools/​ILTrim.Core/​RootingHelpers.csSwitch reflected-member rooting helpers to sink-based collection.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​VirtualMethodUseNode.csConvert static/conditional/dynamic dependency reporting to sink-based overrides.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenWriterNode.csConvert conditional/dynamic dependency APIs to sink-based no-op implementations.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeSpecificationNode.csPush analyzed TypeSpec signature dependencies directly into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeReferenceNode.csReplace yielded dependencies with sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeDefinitionNode.csConvert static + conditional dependencies to sink-based adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​StandaloneSignatureNode.csRoute standalone signature analysis dependencies into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​PropertyDefinitionNode.csConvert CA + owning-type dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ParameterNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleDefinitionNode.csConvert module-level dependencies + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodSpecificationNode.csConvert signature + instantiated-method dependency reporting to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodImplementationNode.csConvert yielded token dependencies to sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MemberReferenceNode.csConvert signature/token analysis dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ManifestResourceNode.csConvert descriptor/implementation/CA dependencies to sink usage and update asserts.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterConstraintNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​FieldDefinitionNode.csConvert signature/constant/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​EventDefinitionNode.csConvert event + accessor + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ConstantNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyDefinitionNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​NodeFactory.csUpdate NullDependencyNode to sink-based dependency APIs.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​MethodBodyNode.csConvert cached dependency list emission to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​InterfaceUseNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​ConstructedTypeNode.csConvert static + conditional dependencies to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​AssemblyRootNode.csConvert rooting deps to sink adds and update empty conditional/dynamic implementations.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​VirtualMethodUseNode.csConvert dependencies and metadata manager calls to sink usage.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ObjectNode.csRefactor relocation + wasm signature deps to be written directly to sink.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​INodeWithRuntimeDeterminedDependencies.csReplace “return deps enumerable” with sink-based AddDependencies signature.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ExternSymbolNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​JitInterface/​CorInfoImpl.RyuJit.csRoute virtual method slot-use tracking deps to the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​Compiler/​DependencyAnalysis/​MethodCodeNode.csConvert conditional + non-relocation dependencies to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​JitInterface/​CorInfoImpl.ReadyToRun.csRoute additional dependencies through the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​TypeMapAssemblyTargetsNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunProxyTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunExternalTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​FileLayoutOptimizer.csUpdate dependency traversal to use reusable per-depth sinks/lists.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRunCodegenNodeFactory.csUpdate helper to accept sink rather than ref-list allocation.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmR2RToInterpreterThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmInterpreterToR2RThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​TypeFixupSignature.csConvert non-relocation deps + helper signatures to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SymbolNodeRange.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​StringDiscoverableAssemblyStubNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SignatureEmbeddedPointerIndirectionNode.csConvert reloc dependency to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​PrecodeMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​NewArrayFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodWithGCInfo.csConvert GC info/fixup deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodGCInfoNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​InstrumentationDataTableNode.csConvert embedded PGO dependency emission to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportThunk.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportSectionNode.csConvert static deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​Import.csConvert static deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​GenericLookupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ExceptionInfoLookupTableNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelegateCtorSignature.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperMethodImport.csConvert base deps + canonical method dep to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperImport.csConvert conditional “helper present” deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DeferredTillPhaseNode.csConvert static deps to sink adds and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DebugDirectoryEntryNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​InheritedVirtualMethodsNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayInterfaceMethodsNode.csConvert conditional deps to sink usage and early-return pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​AllMethodsOnTypeNode.csConvert method enumeration deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ILCompiler.DependencyAnalysisFramework.csprojAdd new sink source files; remove ComputedStaticDependencyNode compile include.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencySink.csIntroduce public sink interfaces for static/conditional dependency emission.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencyNode.csUpdate dependency node interface to sink-based methods.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ComputedStaticDependencyNode.csRemove unused list-based computed-dependency node type.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedTypeMapManager.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedInteropStubManager.csConvert interop dependency hooks to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​TypePreinit.csConvert conditional dependency callback to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​RuntimeConstructableTypeDependencies.csConvert conditional dependency list parameter to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​ReachabilityInstrumentationProvider.csUpdate explicit IDependencyNode implementations to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​InteropStubManager.csUpdate abstract interop dependency hooks to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​EmptyInteropStubManager.csUpdate empty interop stub manager overrides to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DescriptorMarker.csUpdate RootingHelpers call sites to pass sink/list directly.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VTableSliceNode.csConvert static + conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VariantInterfaceMethodUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeThreadStaticIndexNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csConvert deps to sink usage and remove cached dependency list.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TrimmingDescriptorNode.csConvert descriptor-derived deps to sink enumeration adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeInstanceMethodNode.csConvert conditional dep array to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StructMarshallingDataNode.csConvert deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StringAllocatorMethodNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StaticsInfoHashtableNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SerializedFrozenObjectNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SealedVTableNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ScannedMethodNode.csConvert cached deps emission to sink iteration.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeMethodHandleNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeFieldHandleNode.csConvert ldtoken deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionVirtualInvokeMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionInvokeSupportDependencyAlgorithm.csConvert params-array dependency helper to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedTypeNode.csConvert deps to sink adds + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedMethodNode.csConvert reflectability deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedFieldNode.csConvert reflectability + signature deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedDelegateNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapNode.csConvert conditional deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​PropertyMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeFlowDependenciesNode.csRoute dataflow-derived deps into sink and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeCalledNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NotReadOnlyFieldNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NonGCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csConvert module-use deps helper to sink-based signature (and should remove leftover lazy-init).
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleMetadataNode.csConvert static + conditional CA deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MethodParameterMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MetadataEETypeNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidProxyTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidExternalTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceGenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchMapNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InlineableStringsResourceNode.csConvert helper signature to accept sink; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GvmDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodImplNode.csConvert deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericTypesTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericStaticBaseInfoNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericDefinitionEETypeNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FunctionPointerMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FrozenObjectNode.csConvert relocation dependency discovery to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapNode.csConvert conditional/static deps to sink usage + update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EventMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedTrimmingDescriptorNode.csConvert descriptor-derived deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DispatchCellNode.csConvert cell dependency helpers to sink-based emitters.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DictionaryLayoutNode.csConvert static + conditional deps to sink usage and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateTargetVirtualMethodNode.csConvert static/conditional deps to sink-based no-ops / adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateMarshallingDataNode.csConvert static deps to sink AddRange and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DataflowAnalyzedTypeDefinitionNode.csConvert dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CustomAttributeMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ConstructedEETypeNode.csConvert non-relocation deps + interop hook call to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CodeBasedDependencyAlgorithm.csConvert static + conditional dependency helpers to sink interfaces.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CanonicalDefinitionEETypeNode.csConvert non-relocation deps to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedProxyTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedExternalTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalysisCharacteristicNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AddressTakenMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​ReflectionMethodBodyScanner.csRoute Object.GetType dataflow dependencies into an injected sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​GenericArgumentDataFlow.csConvert generic argument dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​AnalysisBasedInteropStubManager.csUpdate interop manager overrides to sink-based signatures.

Comment on lines +61 to 65
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory context)
{
if (_staticDependencies == null)
{
_staticDependencies = new DependencyList();

foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationMethod);

foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries())
InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationType, entry.ImplementationMethod);
}
foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(sink, context, entry.CallingMethod, entry.ImplementationMethod);

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings August 31, 2026 01:12

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:17

  • dependencyList ??= new DependencyList(); no longer propagates a newly-created list back to the caller (the parameter is no longer ref). If dependencyList were ever null, this would silently drop dependencies into a temporary list, and it also keeps a now-unnecessary DependencyList alias. Since all current callers pass a non-null sink, remove the null-coalescing allocation and the unused alias import to avoid misleading behavior and unnecessary allocations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 02:40

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:16

  • dependencyList ??= new DependencyList(); no longer behaves like the old ref DependencyList pattern. If a caller passes null, this only reassigns the local parameter and any dependencies added here are dropped on return (and the DependencyList alias becomes the only use of that using). Since callers are expected to provide a sink, it’s better to require non-null and avoid silently losing dependencies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 03:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”…
Pre-existing issues (1)
SeverityFinding
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Issues resolved since last review (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View resolved comment

Comment on lines +46 to +48
private readonly List<Dependency> _dependencies = new List<Dependency>();
private readonly DependencyNodeCore<DependencyContextType>.DependencyList? _staticDependencies;
private readonly DependencyNodeCore<DependencyContextType>.CombinedDependencyList? _conditionalDependencies;

@MichalStrehovskyMichalStrehovsky left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have unit tests for the framework in #130849. We haven't really done any changes to the framework ever since it was added in dotnet/corert#42 so there wasn't much of a need. I thought I might need to do a change this summer so I ported the tests, but then ended up not making the change anyway so the test PR wasn't a priority.

Comment on lines +55 to 63
protected override void ComputeNonRelocationBasedDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)
{
DependencyList dependencies = base.ComputeNonRelocationBasedDependencies(factory);
DependencySink<NodeFactory> dependencies = sink;
base.ComputeNonRelocationBasedDependencies(sink, factory);

factory.AddVirtualMethodDiscoveryDependencies(ref dependencies, _arrayType);
factory.AddVirtualMethodDiscoveryDependencies(dependencies, _arrayType);

return dependencies;
return;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patterns like this look sloppy (not going to comment on all).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair -- I thought the same thing but I thought it might make the review easier. I'll have copilot clean it up

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not just the return statement, also the DependencySink<NodeFactory> dependencies = sink; variable renaming. In this specific case the diff would actually be smaller without the variable renaming (doesn't apply everywhere).

}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DependencyListEntry is a nested class so it doesn't need to repeat the NodeFactory part and we don't need a using for the framework. It would definitely look better here for DependencySink to be like that too, not sure if there's any drawbacks though.

}

[Fact]
public void DependencyAnalyzerComputesDeferredStaticDependenciesBeforeDynamicDependencies()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand what this is testing.

Comment on lines +44 to +52
public override void AddDependenciesDueToMethodCodePresence(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)
{
}

public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
public override void AddInterestingInteropConstructedTypeDependencies(DependencySink<NodeFactory> dependencies, NodeFactory factory, TypeDesc type)
{
}

public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
public override void AddMarshalAPIsGenericDependencies(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the rules for when this should be IDependencySink interface and when it should use DependencySink class?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In short, IDependencySink should only be used when at least one of the callers uses a DependencyList to construct a temporary list and scan the results. This isn't very common -- I believe there are only two actual roots and I tried to move every place possible over to a DependencySink.

The biggest reason in general to use IDependencySink is when you want to review and filter results after receiving them. If you don't care, you might as well use the main sink, which doesn't guarantee those semantics (and can be kind of heavyweight in that case).

I thought about adding a comment here, but it wasn't quite clear where I should add it. Let me take another shot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So IDependencySink is the abstraction we need and DependencySink is an optimization when we can get away with it. How perf is the optimization really buying?

If everything is behind an interface, we could potentially also look into whether the sink (as used by the analysis engine) really needs to build a list, or whether we could run the marking logic the moment Add is called (those experiments can be done separately).

CopilotAI review requested due to automatic review settings August 31, 2026 20:53

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment

The DependencySink type was holding onto a special CombinedDependencyList just for
ShowMethodNode because ShadowMethodNode wanted to filter the canonical dependencies
in order to construct the specialized dependencies based on the runtime-determined
dependencies. This required wrapping a DependencyList in a DependencySink just to
collect dependencies and then filter.
By refactoring to have the IMethodBodyNode directly support specializing the
runtime-determined dependencies we can avoid saving and filtering the elements and
just filter before we add.
CopilotAI review requested due to automatic review settings September 1, 2026 01:28

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencySink.cs:114

  • DependencySink.Add(node, otherReasonNode, reason) bypasses the _staticDependencies fast-path, so if a sink is constructed around a DependencyList (e.g., new DependencySink(list)), using this overload will silently record the dependency in the sink’s private _dependencies list instead of the provided DependencyList. Adding a debug-time guard here would help catch accidental misuse (especially since DependencySink is also used as an IConditionalDependencySink).

Sign up for freeto 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.

3 participants

@agocke@MichalStrehovsky
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Change the ILC DependencyFramework to use sinks instead of lists - #132955

Open
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink
Open

Change the ILC DependencyFramework to use sinks instead of lists#132955
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink

Conversation

@agocke

Copy link
Copy Markdown
Member

The current DependencyFramework algorithm is based on creating intermediate lists of dependencies that are processed into the whole dependency graph. This intermediate list production is very expensive. By moving to a series of worker-owned sinks that are threaded down through the dependency framework we can avoid intermediate container allocation and enumeration.

Rough dependency analysis numbers, measured on the Azure MCP server (an extremely large AOT application):

┌─────────┬──────────────┬──────────────┬─────────────────────┐
│ Run │ Baseline │ Serial sinks │ Reduction │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 1 │ 7,376.54 MiB │ 6,659.55 MiB │ 716.99 MiB / 9.72% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 2 │ 7,297.80 MiB │ 6,464.73 MiB │ 833.07 MiB / 11.42% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ Average │ 7,337.17 MiB │ 6,562.14 MiB │ 775.03 MiB / 10.56% │
└─────────┴──────────────┴──────────────┴─────────────────────┘

The largest savings were directly from the sink conversion: about 517 MiB from conditional dependency arrays, 163 MiB from static dependency arrays, 125 MiB from conditional iterators, 121 MiB from static enumerators, and 95 MiB from DependencyList objects.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 07268f0e-f7cc-4a0a-87cf-fcb906277823
CopilotAI lite review requested due to automatic review settings August 30, 2026 21:48
@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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away…
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This…
What changed in this PR

This PR refactors the ILCompiler dependency analysis pipeline (ILC/ILTrim/ReadyToRun tooling) from “produce intermediate dependency lists/enumerables” to a sink-based model where nodes push dependencies directly into a provided sink, aiming to reduce allocations and expensive intermediate enumeration.

Changes:

  • Introduces IDependencySink / DependencySink and updates the dependency framework contracts (IDependencyNode) to AddStaticDependencies / AddConditionalDependencies / SearchDynamicDependencies sink-based APIs.
  • Migrates many dependency nodes and helper algorithms from IEnumerable<...>/DependencyList building to directly writing to sinks.
  • Removes ComputedStaticDependencyNode and updates a variety of call sites (ILTrim + ReadyToRun + AOT compiler) to the new pattern.
FileDescription
src/​coreclr/​tools/​ILTrim.Core/​RootingHelpers.csSwitch reflected-member rooting helpers to sink-based collection.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​VirtualMethodUseNode.csConvert static/conditional/dynamic dependency reporting to sink-based overrides.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenWriterNode.csConvert conditional/dynamic dependency APIs to sink-based no-op implementations.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeSpecificationNode.csPush analyzed TypeSpec signature dependencies directly into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeReferenceNode.csReplace yielded dependencies with sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeDefinitionNode.csConvert static + conditional dependencies to sink-based adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​StandaloneSignatureNode.csRoute standalone signature analysis dependencies into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​PropertyDefinitionNode.csConvert CA + owning-type dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ParameterNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleDefinitionNode.csConvert module-level dependencies + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodSpecificationNode.csConvert signature + instantiated-method dependency reporting to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodImplementationNode.csConvert yielded token dependencies to sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MemberReferenceNode.csConvert signature/token analysis dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ManifestResourceNode.csConvert descriptor/implementation/CA dependencies to sink usage and update asserts.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterConstraintNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​FieldDefinitionNode.csConvert signature/constant/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​EventDefinitionNode.csConvert event + accessor + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ConstantNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyDefinitionNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​NodeFactory.csUpdate NullDependencyNode to sink-based dependency APIs.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​MethodBodyNode.csConvert cached dependency list emission to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​InterfaceUseNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​ConstructedTypeNode.csConvert static + conditional dependencies to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​AssemblyRootNode.csConvert rooting deps to sink adds and update empty conditional/dynamic implementations.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​VirtualMethodUseNode.csConvert dependencies and metadata manager calls to sink usage.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ObjectNode.csRefactor relocation + wasm signature deps to be written directly to sink.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​INodeWithRuntimeDeterminedDependencies.csReplace “return deps enumerable” with sink-based AddDependencies signature.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ExternSymbolNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​JitInterface/​CorInfoImpl.RyuJit.csRoute virtual method slot-use tracking deps to the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​Compiler/​DependencyAnalysis/​MethodCodeNode.csConvert conditional + non-relocation dependencies to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​JitInterface/​CorInfoImpl.ReadyToRun.csRoute additional dependencies through the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​TypeMapAssemblyTargetsNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunProxyTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunExternalTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​FileLayoutOptimizer.csUpdate dependency traversal to use reusable per-depth sinks/lists.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRunCodegenNodeFactory.csUpdate helper to accept sink rather than ref-list allocation.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmR2RToInterpreterThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmInterpreterToR2RThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​TypeFixupSignature.csConvert non-relocation deps + helper signatures to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SymbolNodeRange.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​StringDiscoverableAssemblyStubNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SignatureEmbeddedPointerIndirectionNode.csConvert reloc dependency to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​PrecodeMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​NewArrayFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodWithGCInfo.csConvert GC info/fixup deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodGCInfoNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​InstrumentationDataTableNode.csConvert embedded PGO dependency emission to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportThunk.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportSectionNode.csConvert static deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​Import.csConvert static deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​GenericLookupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ExceptionInfoLookupTableNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelegateCtorSignature.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperMethodImport.csConvert base deps + canonical method dep to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperImport.csConvert conditional “helper present” deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DeferredTillPhaseNode.csConvert static deps to sink adds and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DebugDirectoryEntryNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​InheritedVirtualMethodsNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayInterfaceMethodsNode.csConvert conditional deps to sink usage and early-return pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​AllMethodsOnTypeNode.csConvert method enumeration deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ILCompiler.DependencyAnalysisFramework.csprojAdd new sink source files; remove ComputedStaticDependencyNode compile include.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencySink.csIntroduce public sink interfaces for static/conditional dependency emission.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencyNode.csUpdate dependency node interface to sink-based methods.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ComputedStaticDependencyNode.csRemove unused list-based computed-dependency node type.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedTypeMapManager.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedInteropStubManager.csConvert interop dependency hooks to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​TypePreinit.csConvert conditional dependency callback to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​RuntimeConstructableTypeDependencies.csConvert conditional dependency list parameter to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​ReachabilityInstrumentationProvider.csUpdate explicit IDependencyNode implementations to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​InteropStubManager.csUpdate abstract interop dependency hooks to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​EmptyInteropStubManager.csUpdate empty interop stub manager overrides to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DescriptorMarker.csUpdate RootingHelpers call sites to pass sink/list directly.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VTableSliceNode.csConvert static + conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VariantInterfaceMethodUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeThreadStaticIndexNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csConvert deps to sink usage and remove cached dependency list.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TrimmingDescriptorNode.csConvert descriptor-derived deps to sink enumeration adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeInstanceMethodNode.csConvert conditional dep array to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StructMarshallingDataNode.csConvert deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StringAllocatorMethodNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StaticsInfoHashtableNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SerializedFrozenObjectNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SealedVTableNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ScannedMethodNode.csConvert cached deps emission to sink iteration.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeMethodHandleNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeFieldHandleNode.csConvert ldtoken deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionVirtualInvokeMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionInvokeSupportDependencyAlgorithm.csConvert params-array dependency helper to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedTypeNode.csConvert deps to sink adds + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedMethodNode.csConvert reflectability deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedFieldNode.csConvert reflectability + signature deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedDelegateNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapNode.csConvert conditional deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​PropertyMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeFlowDependenciesNode.csRoute dataflow-derived deps into sink and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeCalledNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NotReadOnlyFieldNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NonGCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csConvert module-use deps helper to sink-based signature (and should remove leftover lazy-init).
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleMetadataNode.csConvert static + conditional CA deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MethodParameterMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MetadataEETypeNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidProxyTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidExternalTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceGenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchMapNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InlineableStringsResourceNode.csConvert helper signature to accept sink; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GvmDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodImplNode.csConvert deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericTypesTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericStaticBaseInfoNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericDefinitionEETypeNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FunctionPointerMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FrozenObjectNode.csConvert relocation dependency discovery to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapNode.csConvert conditional/static deps to sink usage + update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EventMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedTrimmingDescriptorNode.csConvert descriptor-derived deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DispatchCellNode.csConvert cell dependency helpers to sink-based emitters.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DictionaryLayoutNode.csConvert static + conditional deps to sink usage and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateTargetVirtualMethodNode.csConvert static/conditional deps to sink-based no-ops / adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateMarshallingDataNode.csConvert static deps to sink AddRange and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DataflowAnalyzedTypeDefinitionNode.csConvert dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CustomAttributeMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ConstructedEETypeNode.csConvert non-relocation deps + interop hook call to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CodeBasedDependencyAlgorithm.csConvert static + conditional dependency helpers to sink interfaces.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CanonicalDefinitionEETypeNode.csConvert non-relocation deps to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedProxyTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedExternalTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalysisCharacteristicNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AddressTakenMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​ReflectionMethodBodyScanner.csRoute Object.GetType dataflow dependencies into an injected sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​GenericArgumentDataFlow.csConvert generic argument dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​AnalysisBasedInteropStubManager.csUpdate interop manager overrides to sink-based signatures.

Comment on lines +61 to 65
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory context)
{
if (_staticDependencies == null)
{
_staticDependencies = new DependencyList();

foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationMethod);

foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries())
InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationType, entry.ImplementationMethod);
}
foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(sink, context, entry.CallingMethod, entry.ImplementationMethod);

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings August 31, 2026 01:12

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:17

  • dependencyList ??= new DependencyList(); no longer propagates a newly-created list back to the caller (the parameter is no longer ref). If dependencyList were ever null, this would silently drop dependencies into a temporary list, and it also keeps a now-unnecessary DependencyList alias. Since all current callers pass a non-null sink, remove the null-coalescing allocation and the unused alias import to avoid misleading behavior and unnecessary allocations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 02:40

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:16

  • dependencyList ??= new DependencyList(); no longer behaves like the old ref DependencyList pattern. If a caller passes null, this only reassigns the local parameter and any dependencies added here are dropped on return (and the DependencyList alias becomes the only use of that using). Since callers are expected to provide a sink, it’s better to require non-null and avoid silently losing dependencies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 03:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”…
Pre-existing issues (1)
SeverityFinding
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Issues resolved since last review (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View resolved comment

Comment on lines +46 to +48
private readonly List<Dependency> _dependencies = new List<Dependency>();
private readonly DependencyNodeCore<DependencyContextType>.DependencyList? _staticDependencies;
private readonly DependencyNodeCore<DependencyContextType>.CombinedDependencyList? _conditionalDependencies;

@MichalStrehovskyMichalStrehovsky left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have unit tests for the framework in #130849. We haven't really done any changes to the framework ever since it was added in dotnet/corert#42 so there wasn't much of a need. I thought I might need to do a change this summer so I ported the tests, but then ended up not making the change anyway so the test PR wasn't a priority.

Comment on lines +55 to 63
protected override void ComputeNonRelocationBasedDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)
{
DependencyList dependencies = base.ComputeNonRelocationBasedDependencies(factory);
DependencySink<NodeFactory> dependencies = sink;
base.ComputeNonRelocationBasedDependencies(sink, factory);

factory.AddVirtualMethodDiscoveryDependencies(ref dependencies, _arrayType);
factory.AddVirtualMethodDiscoveryDependencies(dependencies, _arrayType);

return dependencies;
return;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patterns like this look sloppy (not going to comment on all).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair -- I thought the same thing but I thought it might make the review easier. I'll have copilot clean it up

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not just the return statement, also the DependencySink<NodeFactory> dependencies = sink; variable renaming. In this specific case the diff would actually be smaller without the variable renaming (doesn't apply everywhere).

}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DependencyListEntry is a nested class so it doesn't need to repeat the NodeFactory part and we don't need a using for the framework. It would definitely look better here for DependencySink to be like that too, not sure if there's any drawbacks though.

}

[Fact]
public void DependencyAnalyzerComputesDeferredStaticDependenciesBeforeDynamicDependencies()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand what this is testing.

Comment on lines +44 to +52
public override void AddDependenciesDueToMethodCodePresence(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)
{
}

public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
public override void AddInterestingInteropConstructedTypeDependencies(DependencySink<NodeFactory> dependencies, NodeFactory factory, TypeDesc type)
{
}

public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
public override void AddMarshalAPIsGenericDependencies(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the rules for when this should be IDependencySink interface and when it should use DependencySink class?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In short, IDependencySink should only be used when at least one of the callers uses a DependencyList to construct a temporary list and scan the results. This isn't very common -- I believe there are only two actual roots and I tried to move every place possible over to a DependencySink.

The biggest reason in general to use IDependencySink is when you want to review and filter results after receiving them. If you don't care, you might as well use the main sink, which doesn't guarantee those semantics (and can be kind of heavyweight in that case).

I thought about adding a comment here, but it wasn't quite clear where I should add it. Let me take another shot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So IDependencySink is the abstraction we need and DependencySink is an optimization when we can get away with it. How perf is the optimization really buying?

If everything is behind an interface, we could potentially also look into whether the sink (as used by the analysis engine) really needs to build a list, or whether we could run the marking logic the moment Add is called (those experiments can be done separately).

CopilotAI review requested due to automatic review settings August 31, 2026 20:53

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment

The DependencySink type was holding onto a special CombinedDependencyList just for
ShowMethodNode because ShadowMethodNode wanted to filter the canonical dependencies
in order to construct the specialized dependencies based on the runtime-determined
dependencies. This required wrapping a DependencyList in a DependencySink just to
collect dependencies and then filter.
By refactoring to have the IMethodBodyNode directly support specializing the
runtime-determined dependencies we can avoid saving and filtering the elements and
just filter before we add.
CopilotAI review requested due to automatic review settings September 1, 2026 01:28

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencySink.cs:114

  • DependencySink.Add(node, otherReasonNode, reason) bypasses the _staticDependencies fast-path, so if a sink is constructed around a DependencyList (e.g., new DependencySink(list)), using this overload will silently record the dependency in the sink’s private _dependencies list instead of the provided DependencyList. Adding a debug-time guard here would help catch accidental misuse (especially since DependencySink is also used as an IConditionalDependencySink).

Sign up for freeto 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.

3 participants

@agocke@MichalStrehovsky
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Change the ILC DependencyFramework to use sinks instead of lists - #132955

Open
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink
Open

Change the ILC DependencyFramework to use sinks instead of lists#132955
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink

Conversation

@agocke

Copy link
Copy Markdown
Member

The current DependencyFramework algorithm is based on creating intermediate lists of dependencies that are processed into the whole dependency graph. This intermediate list production is very expensive. By moving to a series of worker-owned sinks that are threaded down through the dependency framework we can avoid intermediate container allocation and enumeration.

Rough dependency analysis numbers, measured on the Azure MCP server (an extremely large AOT application):

┌─────────┬──────────────┬──────────────┬─────────────────────┐
│ Run │ Baseline │ Serial sinks │ Reduction │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 1 │ 7,376.54 MiB │ 6,659.55 MiB │ 716.99 MiB / 9.72% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 2 │ 7,297.80 MiB │ 6,464.73 MiB │ 833.07 MiB / 11.42% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ Average │ 7,337.17 MiB │ 6,562.14 MiB │ 775.03 MiB / 10.56% │
└─────────┴──────────────┴──────────────┴─────────────────────┘

The largest savings were directly from the sink conversion: about 517 MiB from conditional dependency arrays, 163 MiB from static dependency arrays, 125 MiB from conditional iterators, 121 MiB from static enumerators, and 95 MiB from DependencyList objects.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 07268f0e-f7cc-4a0a-87cf-fcb906277823
CopilotAI lite review requested due to automatic review settings August 30, 2026 21:48
@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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away…
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This…
What changed in this PR

This PR refactors the ILCompiler dependency analysis pipeline (ILC/ILTrim/ReadyToRun tooling) from “produce intermediate dependency lists/enumerables” to a sink-based model where nodes push dependencies directly into a provided sink, aiming to reduce allocations and expensive intermediate enumeration.

Changes:

  • Introduces IDependencySink / DependencySink and updates the dependency framework contracts (IDependencyNode) to AddStaticDependencies / AddConditionalDependencies / SearchDynamicDependencies sink-based APIs.
  • Migrates many dependency nodes and helper algorithms from IEnumerable<...>/DependencyList building to directly writing to sinks.
  • Removes ComputedStaticDependencyNode and updates a variety of call sites (ILTrim + ReadyToRun + AOT compiler) to the new pattern.
FileDescription
src/​coreclr/​tools/​ILTrim.Core/​RootingHelpers.csSwitch reflected-member rooting helpers to sink-based collection.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​VirtualMethodUseNode.csConvert static/conditional/dynamic dependency reporting to sink-based overrides.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenWriterNode.csConvert conditional/dynamic dependency APIs to sink-based no-op implementations.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeSpecificationNode.csPush analyzed TypeSpec signature dependencies directly into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeReferenceNode.csReplace yielded dependencies with sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeDefinitionNode.csConvert static + conditional dependencies to sink-based adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​StandaloneSignatureNode.csRoute standalone signature analysis dependencies into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​PropertyDefinitionNode.csConvert CA + owning-type dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ParameterNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleDefinitionNode.csConvert module-level dependencies + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodSpecificationNode.csConvert signature + instantiated-method dependency reporting to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodImplementationNode.csConvert yielded token dependencies to sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MemberReferenceNode.csConvert signature/token analysis dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ManifestResourceNode.csConvert descriptor/implementation/CA dependencies to sink usage and update asserts.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterConstraintNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​FieldDefinitionNode.csConvert signature/constant/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​EventDefinitionNode.csConvert event + accessor + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ConstantNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyDefinitionNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​NodeFactory.csUpdate NullDependencyNode to sink-based dependency APIs.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​MethodBodyNode.csConvert cached dependency list emission to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​InterfaceUseNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​ConstructedTypeNode.csConvert static + conditional dependencies to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​AssemblyRootNode.csConvert rooting deps to sink adds and update empty conditional/dynamic implementations.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​VirtualMethodUseNode.csConvert dependencies and metadata manager calls to sink usage.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ObjectNode.csRefactor relocation + wasm signature deps to be written directly to sink.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​INodeWithRuntimeDeterminedDependencies.csReplace “return deps enumerable” with sink-based AddDependencies signature.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ExternSymbolNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​JitInterface/​CorInfoImpl.RyuJit.csRoute virtual method slot-use tracking deps to the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​Compiler/​DependencyAnalysis/​MethodCodeNode.csConvert conditional + non-relocation dependencies to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​JitInterface/​CorInfoImpl.ReadyToRun.csRoute additional dependencies through the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​TypeMapAssemblyTargetsNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunProxyTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunExternalTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​FileLayoutOptimizer.csUpdate dependency traversal to use reusable per-depth sinks/lists.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRunCodegenNodeFactory.csUpdate helper to accept sink rather than ref-list allocation.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmR2RToInterpreterThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmInterpreterToR2RThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​TypeFixupSignature.csConvert non-relocation deps + helper signatures to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SymbolNodeRange.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​StringDiscoverableAssemblyStubNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SignatureEmbeddedPointerIndirectionNode.csConvert reloc dependency to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​PrecodeMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​NewArrayFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodWithGCInfo.csConvert GC info/fixup deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodGCInfoNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​InstrumentationDataTableNode.csConvert embedded PGO dependency emission to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportThunk.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportSectionNode.csConvert static deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​Import.csConvert static deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​GenericLookupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ExceptionInfoLookupTableNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelegateCtorSignature.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperMethodImport.csConvert base deps + canonical method dep to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperImport.csConvert conditional “helper present” deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DeferredTillPhaseNode.csConvert static deps to sink adds and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DebugDirectoryEntryNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​InheritedVirtualMethodsNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayInterfaceMethodsNode.csConvert conditional deps to sink usage and early-return pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​AllMethodsOnTypeNode.csConvert method enumeration deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ILCompiler.DependencyAnalysisFramework.csprojAdd new sink source files; remove ComputedStaticDependencyNode compile include.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencySink.csIntroduce public sink interfaces for static/conditional dependency emission.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencyNode.csUpdate dependency node interface to sink-based methods.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ComputedStaticDependencyNode.csRemove unused list-based computed-dependency node type.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedTypeMapManager.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedInteropStubManager.csConvert interop dependency hooks to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​TypePreinit.csConvert conditional dependency callback to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​RuntimeConstructableTypeDependencies.csConvert conditional dependency list parameter to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​ReachabilityInstrumentationProvider.csUpdate explicit IDependencyNode implementations to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​InteropStubManager.csUpdate abstract interop dependency hooks to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​EmptyInteropStubManager.csUpdate empty interop stub manager overrides to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DescriptorMarker.csUpdate RootingHelpers call sites to pass sink/list directly.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VTableSliceNode.csConvert static + conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VariantInterfaceMethodUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeThreadStaticIndexNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csConvert deps to sink usage and remove cached dependency list.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TrimmingDescriptorNode.csConvert descriptor-derived deps to sink enumeration adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeInstanceMethodNode.csConvert conditional dep array to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StructMarshallingDataNode.csConvert deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StringAllocatorMethodNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StaticsInfoHashtableNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SerializedFrozenObjectNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SealedVTableNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ScannedMethodNode.csConvert cached deps emission to sink iteration.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeMethodHandleNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeFieldHandleNode.csConvert ldtoken deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionVirtualInvokeMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionInvokeSupportDependencyAlgorithm.csConvert params-array dependency helper to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedTypeNode.csConvert deps to sink adds + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedMethodNode.csConvert reflectability deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedFieldNode.csConvert reflectability + signature deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedDelegateNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapNode.csConvert conditional deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​PropertyMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeFlowDependenciesNode.csRoute dataflow-derived deps into sink and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeCalledNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NotReadOnlyFieldNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NonGCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csConvert module-use deps helper to sink-based signature (and should remove leftover lazy-init).
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleMetadataNode.csConvert static + conditional CA deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MethodParameterMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MetadataEETypeNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidProxyTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidExternalTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceGenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchMapNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InlineableStringsResourceNode.csConvert helper signature to accept sink; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GvmDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodImplNode.csConvert deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericTypesTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericStaticBaseInfoNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericDefinitionEETypeNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FunctionPointerMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FrozenObjectNode.csConvert relocation dependency discovery to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapNode.csConvert conditional/static deps to sink usage + update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EventMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedTrimmingDescriptorNode.csConvert descriptor-derived deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DispatchCellNode.csConvert cell dependency helpers to sink-based emitters.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DictionaryLayoutNode.csConvert static + conditional deps to sink usage and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateTargetVirtualMethodNode.csConvert static/conditional deps to sink-based no-ops / adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateMarshallingDataNode.csConvert static deps to sink AddRange and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DataflowAnalyzedTypeDefinitionNode.csConvert dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CustomAttributeMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ConstructedEETypeNode.csConvert non-relocation deps + interop hook call to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CodeBasedDependencyAlgorithm.csConvert static + conditional dependency helpers to sink interfaces.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CanonicalDefinitionEETypeNode.csConvert non-relocation deps to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedProxyTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedExternalTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalysisCharacteristicNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AddressTakenMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​ReflectionMethodBodyScanner.csRoute Object.GetType dataflow dependencies into an injected sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​GenericArgumentDataFlow.csConvert generic argument dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​AnalysisBasedInteropStubManager.csUpdate interop manager overrides to sink-based signatures.

Comment on lines +61 to 65
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory context)
{
if (_staticDependencies == null)
{
_staticDependencies = new DependencyList();

foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationMethod);

foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries())
InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationType, entry.ImplementationMethod);
}
foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(sink, context, entry.CallingMethod, entry.ImplementationMethod);

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings August 31, 2026 01:12

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:17

  • dependencyList ??= new DependencyList(); no longer propagates a newly-created list back to the caller (the parameter is no longer ref). If dependencyList were ever null, this would silently drop dependencies into a temporary list, and it also keeps a now-unnecessary DependencyList alias. Since all current callers pass a non-null sink, remove the null-coalescing allocation and the unused alias import to avoid misleading behavior and unnecessary allocations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 02:40

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:16

  • dependencyList ??= new DependencyList(); no longer behaves like the old ref DependencyList pattern. If a caller passes null, this only reassigns the local parameter and any dependencies added here are dropped on return (and the DependencyList alias becomes the only use of that using). Since callers are expected to provide a sink, it’s better to require non-null and avoid silently losing dependencies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 03:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”…
Pre-existing issues (1)
SeverityFinding
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Issues resolved since last review (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View resolved comment

Comment on lines +46 to +48
private readonly List<Dependency> _dependencies = new List<Dependency>();
private readonly DependencyNodeCore<DependencyContextType>.DependencyList? _staticDependencies;
private readonly DependencyNodeCore<DependencyContextType>.CombinedDependencyList? _conditionalDependencies;

@MichalStrehovskyMichalStrehovsky left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have unit tests for the framework in #130849. We haven't really done any changes to the framework ever since it was added in dotnet/corert#42 so there wasn't much of a need. I thought I might need to do a change this summer so I ported the tests, but then ended up not making the change anyway so the test PR wasn't a priority.

Comment on lines +55 to 63
protected override void ComputeNonRelocationBasedDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)
{
DependencyList dependencies = base.ComputeNonRelocationBasedDependencies(factory);
DependencySink<NodeFactory> dependencies = sink;
base.ComputeNonRelocationBasedDependencies(sink, factory);

factory.AddVirtualMethodDiscoveryDependencies(ref dependencies, _arrayType);
factory.AddVirtualMethodDiscoveryDependencies(dependencies, _arrayType);

return dependencies;
return;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patterns like this look sloppy (not going to comment on all).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair -- I thought the same thing but I thought it might make the review easier. I'll have copilot clean it up

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not just the return statement, also the DependencySink<NodeFactory> dependencies = sink; variable renaming. In this specific case the diff would actually be smaller without the variable renaming (doesn't apply everywhere).

}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DependencyListEntry is a nested class so it doesn't need to repeat the NodeFactory part and we don't need a using for the framework. It would definitely look better here for DependencySink to be like that too, not sure if there's any drawbacks though.

}

[Fact]
public void DependencyAnalyzerComputesDeferredStaticDependenciesBeforeDynamicDependencies()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand what this is testing.

Comment on lines +44 to +52
public override void AddDependenciesDueToMethodCodePresence(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)
{
}

public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
public override void AddInterestingInteropConstructedTypeDependencies(DependencySink<NodeFactory> dependencies, NodeFactory factory, TypeDesc type)
{
}

public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
public override void AddMarshalAPIsGenericDependencies(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the rules for when this should be IDependencySink interface and when it should use DependencySink class?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In short, IDependencySink should only be used when at least one of the callers uses a DependencyList to construct a temporary list and scan the results. This isn't very common -- I believe there are only two actual roots and I tried to move every place possible over to a DependencySink.

The biggest reason in general to use IDependencySink is when you want to review and filter results after receiving them. If you don't care, you might as well use the main sink, which doesn't guarantee those semantics (and can be kind of heavyweight in that case).

I thought about adding a comment here, but it wasn't quite clear where I should add it. Let me take another shot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So IDependencySink is the abstraction we need and DependencySink is an optimization when we can get away with it. How perf is the optimization really buying?

If everything is behind an interface, we could potentially also look into whether the sink (as used by the analysis engine) really needs to build a list, or whether we could run the marking logic the moment Add is called (those experiments can be done separately).

CopilotAI review requested due to automatic review settings August 31, 2026 20:53

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment

The DependencySink type was holding onto a special CombinedDependencyList just for
ShowMethodNode because ShadowMethodNode wanted to filter the canonical dependencies
in order to construct the specialized dependencies based on the runtime-determined
dependencies. This required wrapping a DependencyList in a DependencySink just to
collect dependencies and then filter.
By refactoring to have the IMethodBodyNode directly support specializing the
runtime-determined dependencies we can avoid saving and filtering the elements and
just filter before we add.
CopilotAI review requested due to automatic review settings September 1, 2026 01:28

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencySink.cs:114

  • DependencySink.Add(node, otherReasonNode, reason) bypasses the _staticDependencies fast-path, so if a sink is constructed around a DependencyList (e.g., new DependencySink(list)), using this overload will silently record the dependency in the sink’s private _dependencies list instead of the provided DependencyList. Adding a debug-time guard here would help catch accidental misuse (especially since DependencySink is also used as an IConditionalDependencySink).

Sign up for freeto 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.

3 participants

@agocke@MichalStrehovsky
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Change the ILC DependencyFramework to use sinks instead of lists - #132955

Open
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink
Open

Change the ILC DependencyFramework to use sinks instead of lists#132955
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink

Conversation

@agocke

Copy link
Copy Markdown
Member

The current DependencyFramework algorithm is based on creating intermediate lists of dependencies that are processed into the whole dependency graph. This intermediate list production is very expensive. By moving to a series of worker-owned sinks that are threaded down through the dependency framework we can avoid intermediate container allocation and enumeration.

Rough dependency analysis numbers, measured on the Azure MCP server (an extremely large AOT application):

┌─────────┬──────────────┬──────────────┬─────────────────────┐
│ Run │ Baseline │ Serial sinks │ Reduction │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 1 │ 7,376.54 MiB │ 6,659.55 MiB │ 716.99 MiB / 9.72% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 2 │ 7,297.80 MiB │ 6,464.73 MiB │ 833.07 MiB / 11.42% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ Average │ 7,337.17 MiB │ 6,562.14 MiB │ 775.03 MiB / 10.56% │
└─────────┴──────────────┴──────────────┴─────────────────────┘

The largest savings were directly from the sink conversion: about 517 MiB from conditional dependency arrays, 163 MiB from static dependency arrays, 125 MiB from conditional iterators, 121 MiB from static enumerators, and 95 MiB from DependencyList objects.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 07268f0e-f7cc-4a0a-87cf-fcb906277823
CopilotAI lite review requested due to automatic review settings August 30, 2026 21:48
@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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away…
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This…
What changed in this PR

This PR refactors the ILCompiler dependency analysis pipeline (ILC/ILTrim/ReadyToRun tooling) from “produce intermediate dependency lists/enumerables” to a sink-based model where nodes push dependencies directly into a provided sink, aiming to reduce allocations and expensive intermediate enumeration.

Changes:

  • Introduces IDependencySink / DependencySink and updates the dependency framework contracts (IDependencyNode) to AddStaticDependencies / AddConditionalDependencies / SearchDynamicDependencies sink-based APIs.
  • Migrates many dependency nodes and helper algorithms from IEnumerable<...>/DependencyList building to directly writing to sinks.
  • Removes ComputedStaticDependencyNode and updates a variety of call sites (ILTrim + ReadyToRun + AOT compiler) to the new pattern.
FileDescription
src/​coreclr/​tools/​ILTrim.Core/​RootingHelpers.csSwitch reflected-member rooting helpers to sink-based collection.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​VirtualMethodUseNode.csConvert static/conditional/dynamic dependency reporting to sink-based overrides.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenWriterNode.csConvert conditional/dynamic dependency APIs to sink-based no-op implementations.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeSpecificationNode.csPush analyzed TypeSpec signature dependencies directly into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeReferenceNode.csReplace yielded dependencies with sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeDefinitionNode.csConvert static + conditional dependencies to sink-based adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​StandaloneSignatureNode.csRoute standalone signature analysis dependencies into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​PropertyDefinitionNode.csConvert CA + owning-type dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ParameterNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleDefinitionNode.csConvert module-level dependencies + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodSpecificationNode.csConvert signature + instantiated-method dependency reporting to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodImplementationNode.csConvert yielded token dependencies to sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MemberReferenceNode.csConvert signature/token analysis dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ManifestResourceNode.csConvert descriptor/implementation/CA dependencies to sink usage and update asserts.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterConstraintNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​FieldDefinitionNode.csConvert signature/constant/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​EventDefinitionNode.csConvert event + accessor + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ConstantNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyDefinitionNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​NodeFactory.csUpdate NullDependencyNode to sink-based dependency APIs.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​MethodBodyNode.csConvert cached dependency list emission to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​InterfaceUseNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​ConstructedTypeNode.csConvert static + conditional dependencies to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​AssemblyRootNode.csConvert rooting deps to sink adds and update empty conditional/dynamic implementations.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​VirtualMethodUseNode.csConvert dependencies and metadata manager calls to sink usage.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ObjectNode.csRefactor relocation + wasm signature deps to be written directly to sink.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​INodeWithRuntimeDeterminedDependencies.csReplace “return deps enumerable” with sink-based AddDependencies signature.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ExternSymbolNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​JitInterface/​CorInfoImpl.RyuJit.csRoute virtual method slot-use tracking deps to the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​Compiler/​DependencyAnalysis/​MethodCodeNode.csConvert conditional + non-relocation dependencies to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​JitInterface/​CorInfoImpl.ReadyToRun.csRoute additional dependencies through the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​TypeMapAssemblyTargetsNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunProxyTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunExternalTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​FileLayoutOptimizer.csUpdate dependency traversal to use reusable per-depth sinks/lists.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRunCodegenNodeFactory.csUpdate helper to accept sink rather than ref-list allocation.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmR2RToInterpreterThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmInterpreterToR2RThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​TypeFixupSignature.csConvert non-relocation deps + helper signatures to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SymbolNodeRange.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​StringDiscoverableAssemblyStubNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SignatureEmbeddedPointerIndirectionNode.csConvert reloc dependency to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​PrecodeMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​NewArrayFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodWithGCInfo.csConvert GC info/fixup deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodGCInfoNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​InstrumentationDataTableNode.csConvert embedded PGO dependency emission to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportThunk.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportSectionNode.csConvert static deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​Import.csConvert static deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​GenericLookupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ExceptionInfoLookupTableNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelegateCtorSignature.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperMethodImport.csConvert base deps + canonical method dep to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperImport.csConvert conditional “helper present” deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DeferredTillPhaseNode.csConvert static deps to sink adds and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DebugDirectoryEntryNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​InheritedVirtualMethodsNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayInterfaceMethodsNode.csConvert conditional deps to sink usage and early-return pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​AllMethodsOnTypeNode.csConvert method enumeration deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ILCompiler.DependencyAnalysisFramework.csprojAdd new sink source files; remove ComputedStaticDependencyNode compile include.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencySink.csIntroduce public sink interfaces for static/conditional dependency emission.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencyNode.csUpdate dependency node interface to sink-based methods.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ComputedStaticDependencyNode.csRemove unused list-based computed-dependency node type.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedTypeMapManager.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedInteropStubManager.csConvert interop dependency hooks to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​TypePreinit.csConvert conditional dependency callback to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​RuntimeConstructableTypeDependencies.csConvert conditional dependency list parameter to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​ReachabilityInstrumentationProvider.csUpdate explicit IDependencyNode implementations to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​InteropStubManager.csUpdate abstract interop dependency hooks to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​EmptyInteropStubManager.csUpdate empty interop stub manager overrides to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DescriptorMarker.csUpdate RootingHelpers call sites to pass sink/list directly.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VTableSliceNode.csConvert static + conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VariantInterfaceMethodUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeThreadStaticIndexNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csConvert deps to sink usage and remove cached dependency list.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TrimmingDescriptorNode.csConvert descriptor-derived deps to sink enumeration adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeInstanceMethodNode.csConvert conditional dep array to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StructMarshallingDataNode.csConvert deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StringAllocatorMethodNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StaticsInfoHashtableNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SerializedFrozenObjectNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SealedVTableNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ScannedMethodNode.csConvert cached deps emission to sink iteration.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeMethodHandleNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeFieldHandleNode.csConvert ldtoken deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionVirtualInvokeMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionInvokeSupportDependencyAlgorithm.csConvert params-array dependency helper to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedTypeNode.csConvert deps to sink adds + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedMethodNode.csConvert reflectability deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedFieldNode.csConvert reflectability + signature deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedDelegateNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapNode.csConvert conditional deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​PropertyMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeFlowDependenciesNode.csRoute dataflow-derived deps into sink and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeCalledNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NotReadOnlyFieldNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NonGCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csConvert module-use deps helper to sink-based signature (and should remove leftover lazy-init).
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleMetadataNode.csConvert static + conditional CA deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MethodParameterMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MetadataEETypeNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidProxyTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidExternalTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceGenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchMapNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InlineableStringsResourceNode.csConvert helper signature to accept sink; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GvmDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodImplNode.csConvert deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericTypesTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericStaticBaseInfoNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericDefinitionEETypeNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FunctionPointerMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FrozenObjectNode.csConvert relocation dependency discovery to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapNode.csConvert conditional/static deps to sink usage + update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EventMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedTrimmingDescriptorNode.csConvert descriptor-derived deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DispatchCellNode.csConvert cell dependency helpers to sink-based emitters.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DictionaryLayoutNode.csConvert static + conditional deps to sink usage and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateTargetVirtualMethodNode.csConvert static/conditional deps to sink-based no-ops / adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateMarshallingDataNode.csConvert static deps to sink AddRange and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DataflowAnalyzedTypeDefinitionNode.csConvert dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CustomAttributeMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ConstructedEETypeNode.csConvert non-relocation deps + interop hook call to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CodeBasedDependencyAlgorithm.csConvert static + conditional dependency helpers to sink interfaces.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CanonicalDefinitionEETypeNode.csConvert non-relocation deps to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedProxyTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedExternalTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalysisCharacteristicNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AddressTakenMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​ReflectionMethodBodyScanner.csRoute Object.GetType dataflow dependencies into an injected sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​GenericArgumentDataFlow.csConvert generic argument dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​AnalysisBasedInteropStubManager.csUpdate interop manager overrides to sink-based signatures.

Comment on lines +61 to 65
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory context)
{
if (_staticDependencies == null)
{
_staticDependencies = new DependencyList();

foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationMethod);

foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries())
InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationType, entry.ImplementationMethod);
}
foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(sink, context, entry.CallingMethod, entry.ImplementationMethod);

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings August 31, 2026 01:12

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:17

  • dependencyList ??= new DependencyList(); no longer propagates a newly-created list back to the caller (the parameter is no longer ref). If dependencyList were ever null, this would silently drop dependencies into a temporary list, and it also keeps a now-unnecessary DependencyList alias. Since all current callers pass a non-null sink, remove the null-coalescing allocation and the unused alias import to avoid misleading behavior and unnecessary allocations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 02:40

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:16

  • dependencyList ??= new DependencyList(); no longer behaves like the old ref DependencyList pattern. If a caller passes null, this only reassigns the local parameter and any dependencies added here are dropped on return (and the DependencyList alias becomes the only use of that using). Since callers are expected to provide a sink, it’s better to require non-null and avoid silently losing dependencies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 03:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”…
Pre-existing issues (1)
SeverityFinding
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Issues resolved since last review (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View resolved comment

Comment on lines +46 to +48
private readonly List<Dependency> _dependencies = new List<Dependency>();
private readonly DependencyNodeCore<DependencyContextType>.DependencyList? _staticDependencies;
private readonly DependencyNodeCore<DependencyContextType>.CombinedDependencyList? _conditionalDependencies;

@MichalStrehovskyMichalStrehovsky left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have unit tests for the framework in #130849. We haven't really done any changes to the framework ever since it was added in dotnet/corert#42 so there wasn't much of a need. I thought I might need to do a change this summer so I ported the tests, but then ended up not making the change anyway so the test PR wasn't a priority.

Comment on lines +55 to 63
protected override void ComputeNonRelocationBasedDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)
{
DependencyList dependencies = base.ComputeNonRelocationBasedDependencies(factory);
DependencySink<NodeFactory> dependencies = sink;
base.ComputeNonRelocationBasedDependencies(sink, factory);

factory.AddVirtualMethodDiscoveryDependencies(ref dependencies, _arrayType);
factory.AddVirtualMethodDiscoveryDependencies(dependencies, _arrayType);

return dependencies;
return;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patterns like this look sloppy (not going to comment on all).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair -- I thought the same thing but I thought it might make the review easier. I'll have copilot clean it up

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not just the return statement, also the DependencySink<NodeFactory> dependencies = sink; variable renaming. In this specific case the diff would actually be smaller without the variable renaming (doesn't apply everywhere).

}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DependencyListEntry is a nested class so it doesn't need to repeat the NodeFactory part and we don't need a using for the framework. It would definitely look better here for DependencySink to be like that too, not sure if there's any drawbacks though.

}

[Fact]
public void DependencyAnalyzerComputesDeferredStaticDependenciesBeforeDynamicDependencies()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand what this is testing.

Comment on lines +44 to +52
public override void AddDependenciesDueToMethodCodePresence(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)
{
}

public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
public override void AddInterestingInteropConstructedTypeDependencies(DependencySink<NodeFactory> dependencies, NodeFactory factory, TypeDesc type)
{
}

public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
public override void AddMarshalAPIsGenericDependencies(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the rules for when this should be IDependencySink interface and when it should use DependencySink class?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In short, IDependencySink should only be used when at least one of the callers uses a DependencyList to construct a temporary list and scan the results. This isn't very common -- I believe there are only two actual roots and I tried to move every place possible over to a DependencySink.

The biggest reason in general to use IDependencySink is when you want to review and filter results after receiving them. If you don't care, you might as well use the main sink, which doesn't guarantee those semantics (and can be kind of heavyweight in that case).

I thought about adding a comment here, but it wasn't quite clear where I should add it. Let me take another shot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So IDependencySink is the abstraction we need and DependencySink is an optimization when we can get away with it. How perf is the optimization really buying?

If everything is behind an interface, we could potentially also look into whether the sink (as used by the analysis engine) really needs to build a list, or whether we could run the marking logic the moment Add is called (those experiments can be done separately).

CopilotAI review requested due to automatic review settings August 31, 2026 20:53

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment

The DependencySink type was holding onto a special CombinedDependencyList just for
ShowMethodNode because ShadowMethodNode wanted to filter the canonical dependencies
in order to construct the specialized dependencies based on the runtime-determined
dependencies. This required wrapping a DependencyList in a DependencySink just to
collect dependencies and then filter.
By refactoring to have the IMethodBodyNode directly support specializing the
runtime-determined dependencies we can avoid saving and filtering the elements and
just filter before we add.
CopilotAI review requested due to automatic review settings September 1, 2026 01:28

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencySink.cs:114

  • DependencySink.Add(node, otherReasonNode, reason) bypasses the _staticDependencies fast-path, so if a sink is constructed around a DependencyList (e.g., new DependencySink(list)), using this overload will silently record the dependency in the sink’s private _dependencies list instead of the provided DependencyList. Adding a debug-time guard here would help catch accidental misuse (especially since DependencySink is also used as an IConditionalDependencySink).

Sign up for freeto 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.

3 participants

@agocke@MichalStrehovsky
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Change the ILC DependencyFramework to use sinks instead of lists - #132955

Open
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink
Open

Change the ILC DependencyFramework to use sinks instead of lists#132955
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink

Conversation

@agocke

Copy link
Copy Markdown
Member

The current DependencyFramework algorithm is based on creating intermediate lists of dependencies that are processed into the whole dependency graph. This intermediate list production is very expensive. By moving to a series of worker-owned sinks that are threaded down through the dependency framework we can avoid intermediate container allocation and enumeration.

Rough dependency analysis numbers, measured on the Azure MCP server (an extremely large AOT application):

┌─────────┬──────────────┬──────────────┬─────────────────────┐
│ Run │ Baseline │ Serial sinks │ Reduction │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 1 │ 7,376.54 MiB │ 6,659.55 MiB │ 716.99 MiB / 9.72% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 2 │ 7,297.80 MiB │ 6,464.73 MiB │ 833.07 MiB / 11.42% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ Average │ 7,337.17 MiB │ 6,562.14 MiB │ 775.03 MiB / 10.56% │
└─────────┴──────────────┴──────────────┴─────────────────────┘

The largest savings were directly from the sink conversion: about 517 MiB from conditional dependency arrays, 163 MiB from static dependency arrays, 125 MiB from conditional iterators, 121 MiB from static enumerators, and 95 MiB from DependencyList objects.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 07268f0e-f7cc-4a0a-87cf-fcb906277823
CopilotAI lite review requested due to automatic review settings August 30, 2026 21:48
@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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away…
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This…
What changed in this PR

This PR refactors the ILCompiler dependency analysis pipeline (ILC/ILTrim/ReadyToRun tooling) from “produce intermediate dependency lists/enumerables” to a sink-based model where nodes push dependencies directly into a provided sink, aiming to reduce allocations and expensive intermediate enumeration.

Changes:

  • Introduces IDependencySink / DependencySink and updates the dependency framework contracts (IDependencyNode) to AddStaticDependencies / AddConditionalDependencies / SearchDynamicDependencies sink-based APIs.
  • Migrates many dependency nodes and helper algorithms from IEnumerable<...>/DependencyList building to directly writing to sinks.
  • Removes ComputedStaticDependencyNode and updates a variety of call sites (ILTrim + ReadyToRun + AOT compiler) to the new pattern.
FileDescription
src/​coreclr/​tools/​ILTrim.Core/​RootingHelpers.csSwitch reflected-member rooting helpers to sink-based collection.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​VirtualMethodUseNode.csConvert static/conditional/dynamic dependency reporting to sink-based overrides.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenWriterNode.csConvert conditional/dynamic dependency APIs to sink-based no-op implementations.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeSpecificationNode.csPush analyzed TypeSpec signature dependencies directly into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeReferenceNode.csReplace yielded dependencies with sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeDefinitionNode.csConvert static + conditional dependencies to sink-based adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​StandaloneSignatureNode.csRoute standalone signature analysis dependencies into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​PropertyDefinitionNode.csConvert CA + owning-type dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ParameterNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleDefinitionNode.csConvert module-level dependencies + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodSpecificationNode.csConvert signature + instantiated-method dependency reporting to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodImplementationNode.csConvert yielded token dependencies to sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MemberReferenceNode.csConvert signature/token analysis dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ManifestResourceNode.csConvert descriptor/implementation/CA dependencies to sink usage and update asserts.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterConstraintNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​FieldDefinitionNode.csConvert signature/constant/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​EventDefinitionNode.csConvert event + accessor + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ConstantNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyDefinitionNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​NodeFactory.csUpdate NullDependencyNode to sink-based dependency APIs.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​MethodBodyNode.csConvert cached dependency list emission to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​InterfaceUseNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​ConstructedTypeNode.csConvert static + conditional dependencies to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​AssemblyRootNode.csConvert rooting deps to sink adds and update empty conditional/dynamic implementations.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​VirtualMethodUseNode.csConvert dependencies and metadata manager calls to sink usage.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ObjectNode.csRefactor relocation + wasm signature deps to be written directly to sink.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​INodeWithRuntimeDeterminedDependencies.csReplace “return deps enumerable” with sink-based AddDependencies signature.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ExternSymbolNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​JitInterface/​CorInfoImpl.RyuJit.csRoute virtual method slot-use tracking deps to the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​Compiler/​DependencyAnalysis/​MethodCodeNode.csConvert conditional + non-relocation dependencies to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​JitInterface/​CorInfoImpl.ReadyToRun.csRoute additional dependencies through the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​TypeMapAssemblyTargetsNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunProxyTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunExternalTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​FileLayoutOptimizer.csUpdate dependency traversal to use reusable per-depth sinks/lists.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRunCodegenNodeFactory.csUpdate helper to accept sink rather than ref-list allocation.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmR2RToInterpreterThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmInterpreterToR2RThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​TypeFixupSignature.csConvert non-relocation deps + helper signatures to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SymbolNodeRange.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​StringDiscoverableAssemblyStubNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SignatureEmbeddedPointerIndirectionNode.csConvert reloc dependency to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​PrecodeMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​NewArrayFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodWithGCInfo.csConvert GC info/fixup deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodGCInfoNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​InstrumentationDataTableNode.csConvert embedded PGO dependency emission to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportThunk.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportSectionNode.csConvert static deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​Import.csConvert static deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​GenericLookupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ExceptionInfoLookupTableNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelegateCtorSignature.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperMethodImport.csConvert base deps + canonical method dep to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperImport.csConvert conditional “helper present” deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DeferredTillPhaseNode.csConvert static deps to sink adds and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DebugDirectoryEntryNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​InheritedVirtualMethodsNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayInterfaceMethodsNode.csConvert conditional deps to sink usage and early-return pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​AllMethodsOnTypeNode.csConvert method enumeration deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ILCompiler.DependencyAnalysisFramework.csprojAdd new sink source files; remove ComputedStaticDependencyNode compile include.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencySink.csIntroduce public sink interfaces for static/conditional dependency emission.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencyNode.csUpdate dependency node interface to sink-based methods.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ComputedStaticDependencyNode.csRemove unused list-based computed-dependency node type.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedTypeMapManager.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedInteropStubManager.csConvert interop dependency hooks to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​TypePreinit.csConvert conditional dependency callback to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​RuntimeConstructableTypeDependencies.csConvert conditional dependency list parameter to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​ReachabilityInstrumentationProvider.csUpdate explicit IDependencyNode implementations to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​InteropStubManager.csUpdate abstract interop dependency hooks to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​EmptyInteropStubManager.csUpdate empty interop stub manager overrides to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DescriptorMarker.csUpdate RootingHelpers call sites to pass sink/list directly.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VTableSliceNode.csConvert static + conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VariantInterfaceMethodUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeThreadStaticIndexNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csConvert deps to sink usage and remove cached dependency list.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TrimmingDescriptorNode.csConvert descriptor-derived deps to sink enumeration adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeInstanceMethodNode.csConvert conditional dep array to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StructMarshallingDataNode.csConvert deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StringAllocatorMethodNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StaticsInfoHashtableNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SerializedFrozenObjectNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SealedVTableNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ScannedMethodNode.csConvert cached deps emission to sink iteration.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeMethodHandleNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeFieldHandleNode.csConvert ldtoken deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionVirtualInvokeMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionInvokeSupportDependencyAlgorithm.csConvert params-array dependency helper to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedTypeNode.csConvert deps to sink adds + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedMethodNode.csConvert reflectability deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedFieldNode.csConvert reflectability + signature deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedDelegateNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapNode.csConvert conditional deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​PropertyMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeFlowDependenciesNode.csRoute dataflow-derived deps into sink and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeCalledNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NotReadOnlyFieldNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NonGCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csConvert module-use deps helper to sink-based signature (and should remove leftover lazy-init).
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleMetadataNode.csConvert static + conditional CA deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MethodParameterMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MetadataEETypeNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidProxyTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidExternalTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceGenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchMapNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InlineableStringsResourceNode.csConvert helper signature to accept sink; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GvmDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodImplNode.csConvert deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericTypesTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericStaticBaseInfoNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericDefinitionEETypeNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FunctionPointerMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FrozenObjectNode.csConvert relocation dependency discovery to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapNode.csConvert conditional/static deps to sink usage + update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EventMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedTrimmingDescriptorNode.csConvert descriptor-derived deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DispatchCellNode.csConvert cell dependency helpers to sink-based emitters.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DictionaryLayoutNode.csConvert static + conditional deps to sink usage and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateTargetVirtualMethodNode.csConvert static/conditional deps to sink-based no-ops / adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateMarshallingDataNode.csConvert static deps to sink AddRange and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DataflowAnalyzedTypeDefinitionNode.csConvert dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CustomAttributeMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ConstructedEETypeNode.csConvert non-relocation deps + interop hook call to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CodeBasedDependencyAlgorithm.csConvert static + conditional dependency helpers to sink interfaces.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CanonicalDefinitionEETypeNode.csConvert non-relocation deps to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedProxyTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedExternalTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalysisCharacteristicNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AddressTakenMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​ReflectionMethodBodyScanner.csRoute Object.GetType dataflow dependencies into an injected sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​GenericArgumentDataFlow.csConvert generic argument dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​AnalysisBasedInteropStubManager.csUpdate interop manager overrides to sink-based signatures.

Comment on lines +61 to 65
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory context)
{
if (_staticDependencies == null)
{
_staticDependencies = new DependencyList();

foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationMethod);

foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries())
InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationType, entry.ImplementationMethod);
}
foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(sink, context, entry.CallingMethod, entry.ImplementationMethod);

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings August 31, 2026 01:12

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:17

  • dependencyList ??= new DependencyList(); no longer propagates a newly-created list back to the caller (the parameter is no longer ref). If dependencyList were ever null, this would silently drop dependencies into a temporary list, and it also keeps a now-unnecessary DependencyList alias. Since all current callers pass a non-null sink, remove the null-coalescing allocation and the unused alias import to avoid misleading behavior and unnecessary allocations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 02:40

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:16

  • dependencyList ??= new DependencyList(); no longer behaves like the old ref DependencyList pattern. If a caller passes null, this only reassigns the local parameter and any dependencies added here are dropped on return (and the DependencyList alias becomes the only use of that using). Since callers are expected to provide a sink, it’s better to require non-null and avoid silently losing dependencies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 03:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”…
Pre-existing issues (1)
SeverityFinding
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Issues resolved since last review (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View resolved comment

Comment on lines +46 to +48
private readonly List<Dependency> _dependencies = new List<Dependency>();
private readonly DependencyNodeCore<DependencyContextType>.DependencyList? _staticDependencies;
private readonly DependencyNodeCore<DependencyContextType>.CombinedDependencyList? _conditionalDependencies;

@MichalStrehovskyMichalStrehovsky left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have unit tests for the framework in #130849. We haven't really done any changes to the framework ever since it was added in dotnet/corert#42 so there wasn't much of a need. I thought I might need to do a change this summer so I ported the tests, but then ended up not making the change anyway so the test PR wasn't a priority.

Comment on lines +55 to 63
protected override void ComputeNonRelocationBasedDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)
{
DependencyList dependencies = base.ComputeNonRelocationBasedDependencies(factory);
DependencySink<NodeFactory> dependencies = sink;
base.ComputeNonRelocationBasedDependencies(sink, factory);

factory.AddVirtualMethodDiscoveryDependencies(ref dependencies, _arrayType);
factory.AddVirtualMethodDiscoveryDependencies(dependencies, _arrayType);

return dependencies;
return;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patterns like this look sloppy (not going to comment on all).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair -- I thought the same thing but I thought it might make the review easier. I'll have copilot clean it up

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not just the return statement, also the DependencySink<NodeFactory> dependencies = sink; variable renaming. In this specific case the diff would actually be smaller without the variable renaming (doesn't apply everywhere).

}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DependencyListEntry is a nested class so it doesn't need to repeat the NodeFactory part and we don't need a using for the framework. It would definitely look better here for DependencySink to be like that too, not sure if there's any drawbacks though.

}

[Fact]
public void DependencyAnalyzerComputesDeferredStaticDependenciesBeforeDynamicDependencies()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand what this is testing.

Comment on lines +44 to +52
public override void AddDependenciesDueToMethodCodePresence(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)
{
}

public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
public override void AddInterestingInteropConstructedTypeDependencies(DependencySink<NodeFactory> dependencies, NodeFactory factory, TypeDesc type)
{
}

public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
public override void AddMarshalAPIsGenericDependencies(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the rules for when this should be IDependencySink interface and when it should use DependencySink class?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In short, IDependencySink should only be used when at least one of the callers uses a DependencyList to construct a temporary list and scan the results. This isn't very common -- I believe there are only two actual roots and I tried to move every place possible over to a DependencySink.

The biggest reason in general to use IDependencySink is when you want to review and filter results after receiving them. If you don't care, you might as well use the main sink, which doesn't guarantee those semantics (and can be kind of heavyweight in that case).

I thought about adding a comment here, but it wasn't quite clear where I should add it. Let me take another shot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So IDependencySink is the abstraction we need and DependencySink is an optimization when we can get away with it. How perf is the optimization really buying?

If everything is behind an interface, we could potentially also look into whether the sink (as used by the analysis engine) really needs to build a list, or whether we could run the marking logic the moment Add is called (those experiments can be done separately).

CopilotAI review requested due to automatic review settings August 31, 2026 20:53

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment

The DependencySink type was holding onto a special CombinedDependencyList just for
ShowMethodNode because ShadowMethodNode wanted to filter the canonical dependencies
in order to construct the specialized dependencies based on the runtime-determined
dependencies. This required wrapping a DependencyList in a DependencySink just to
collect dependencies and then filter.
By refactoring to have the IMethodBodyNode directly support specializing the
runtime-determined dependencies we can avoid saving and filtering the elements and
just filter before we add.
CopilotAI review requested due to automatic review settings September 1, 2026 01:28

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencySink.cs:114

  • DependencySink.Add(node, otherReasonNode, reason) bypasses the _staticDependencies fast-path, so if a sink is constructed around a DependencyList (e.g., new DependencySink(list)), using this overload will silently record the dependency in the sink’s private _dependencies list instead of the provided DependencyList. Adding a debug-time guard here would help catch accidental misuse (especially since DependencySink is also used as an IConditionalDependencySink).

Sign up for freeto 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.

3 participants

@agocke@MichalStrehovsky
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Change the ILC DependencyFramework to use sinks instead of lists - #132955

Open
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink
Open

Change the ILC DependencyFramework to use sinks instead of lists#132955
agocke wants to merge 6 commits into
dotnet:mainfrom
agocke:ilc-dependencysink

Conversation

@agocke

Copy link
Copy Markdown
Member

The current DependencyFramework algorithm is based on creating intermediate lists of dependencies that are processed into the whole dependency graph. This intermediate list production is very expensive. By moving to a series of worker-owned sinks that are threaded down through the dependency framework we can avoid intermediate container allocation and enumeration.

Rough dependency analysis numbers, measured on the Azure MCP server (an extremely large AOT application):

┌─────────┬──────────────┬──────────────┬─────────────────────┐
│ Run │ Baseline │ Serial sinks │ Reduction │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 1 │ 7,376.54 MiB │ 6,659.55 MiB │ 716.99 MiB / 9.72% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ 2 │ 7,297.80 MiB │ 6,464.73 MiB │ 833.07 MiB / 11.42% │
├─────────┼──────────────┼──────────────┼─────────────────────┤
│ Average │ 7,337.17 MiB │ 6,562.14 MiB │ 775.03 MiB / 10.56% │
└─────────┴──────────────┴──────────────┴─────────────────────┘

The largest savings were directly from the sink conversion: about 517 MiB from conditional dependency arrays, 163 MiB from static dependency arrays, 125 MiB from conditional iterators, 121 MiB from static enumerators, and 95 MiB from DependencyList objects.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 07268f0e-f7cc-4a0a-87cf-fcb906277823
CopilotAI lite review requested due to automatic review settings August 30, 2026 21:48
@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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away…
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This…
What changed in this PR

This PR refactors the ILCompiler dependency analysis pipeline (ILC/ILTrim/ReadyToRun tooling) from “produce intermediate dependency lists/enumerables” to a sink-based model where nodes push dependencies directly into a provided sink, aiming to reduce allocations and expensive intermediate enumeration.

Changes:

  • Introduces IDependencySink / DependencySink and updates the dependency framework contracts (IDependencyNode) to AddStaticDependencies / AddConditionalDependencies / SearchDynamicDependencies sink-based APIs.
  • Migrates many dependency nodes and helper algorithms from IEnumerable<...>/DependencyList building to directly writing to sinks.
  • Removes ComputedStaticDependencyNode and updates a variety of call sites (ILTrim + ReadyToRun + AOT compiler) to the new pattern.
FileDescription
src/​coreclr/​tools/​ILTrim.Core/​RootingHelpers.csSwitch reflected-member rooting helpers to sink-based collection.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​VirtualMethodUseNode.csConvert static/conditional/dynamic dependency reporting to sink-based overrides.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenWriterNode.csConvert conditional/dynamic dependency APIs to sink-based no-op implementations.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeSpecificationNode.csPush analyzed TypeSpec signature dependencies directly into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeReferenceNode.csReplace yielded dependencies with sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​TypeDefinitionNode.csConvert static + conditional dependencies to sink-based adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​StandaloneSignatureNode.csRoute standalone signature analysis dependencies into the sink.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​PropertyDefinitionNode.csConvert CA + owning-type dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ParameterNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ModuleDefinitionNode.csConvert module-level dependencies + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodSpecificationNode.csConvert signature + instantiated-method dependency reporting to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MethodImplementationNode.csConvert yielded token dependencies to sink adds.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​MemberReferenceNode.csConvert signature/token analysis dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ManifestResourceNode.csConvert descriptor/implementation/CA dependencies to sink usage and update asserts.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​GenericParameterConstraintNode.csConvert constraint/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​FieldDefinitionNode.csConvert signature/constant/CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​EventDefinitionNode.csConvert event + accessor + CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​ConstantNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyReferenceNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​TokenBased/​AssemblyDefinitionNode.csConvert CA dependencies to sink usage.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​NodeFactory.csUpdate NullDependencyNode to sink-based dependency APIs.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​MethodBodyNode.csConvert cached dependency list emission to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​InterfaceUseNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​ConstructedTypeNode.csConvert static + conditional dependencies to sink iteration.
src/​coreclr/​tools/​ILTrim.Core/​DependencyAnalysis/​AssemblyRootNode.csConvert rooting deps to sink adds and update empty conditional/dynamic implementations.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​VirtualMethodUseNode.csConvert dependencies and metadata manager calls to sink usage.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ObjectNode.csRefactor relocation + wasm signature deps to be written directly to sink.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​INodeWithRuntimeDeterminedDependencies.csReplace “return deps enumerable” with sink-based AddDependencies signature.
src/​coreclr/​tools/​Common/​Compiler/​DependencyAnalysis/​ExternSymbolNode.csConvert “no deps” implementations to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​JitInterface/​CorInfoImpl.RyuJit.csRoute virtual method slot-use tracking deps to the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.RyuJit/​Compiler/​DependencyAnalysis/​MethodCodeNode.csConvert conditional + non-relocation dependencies to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​JitInterface/​CorInfoImpl.ReadyToRun.csRoute additional dependencies through the shared additional-dependency sink.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​TypeMapAssemblyTargetsNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunProxyTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​ReadyToRunExternalTypeMapNode.csConvert deps to sink usage and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​FileLayoutOptimizer.csUpdate dependency traversal to use reusable per-depth sinks/lists.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRunCodegenNodeFactory.csUpdate helper to accept sink rather than ref-list allocation.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmR2RToInterpreterThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​WasmInterpreterToR2RThunkNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​TypeFixupSignature.csConvert non-relocation deps + helper signatures to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SymbolNodeRange.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​StringDiscoverableAssemblyStubNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​SignatureEmbeddedPointerIndirectionNode.csConvert reloc dependency to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​PrecodeMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​NewArrayFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodWithGCInfo.csConvert GC info/fixup deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodGCInfoNode.csConvert “no deps” implementation to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​MethodFixupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​InstrumentationDataTableNode.csConvert embedded PGO dependency emission to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportThunk.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ImportSectionNode.csConvert static deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​Import.csConvert static deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​GenericLookupSignature.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​ExceptionInfoLookupTableNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelegateCtorSignature.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadMethodImport.csConvert base+local deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperMethodImport.csConvert base deps + canonical method dep to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DelayLoadHelperImport.csConvert conditional “helper present” deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DeferredTillPhaseNode.csConvert static deps to sink adds and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​DebugDirectoryEntryNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​InheritedVirtualMethodsNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ArrayInterfaceMethodsNode.csConvert conditional deps to sink usage and early-return pattern.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​AllMethodsOnTypeNode.csConvert method enumeration deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ILCompiler.DependencyAnalysisFramework.csprojAdd new sink source files; remove ComputedStaticDependencyNode compile include.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencySink.csIntroduce public sink interfaces for static/conditional dependency emission.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​IDependencyNode.csUpdate dependency node interface to sink-based methods.
src/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​ComputedStaticDependencyNode.csRemove unused list-based computed-dependency node type.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedTypeMapManager.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​UsageBasedInteropStubManager.csConvert interop dependency hooks to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​TypePreinit.csConvert conditional dependency callback to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​RuntimeConstructableTypeDependencies.csConvert conditional dependency list parameter to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​ReachabilityInstrumentationProvider.csUpdate explicit IDependencyNode implementations to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​InteropStubManager.csUpdate abstract interop dependency hooks to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​EmptyInteropStubManager.csUpdate empty interop stub manager overrides to sink-based APIs.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DescriptorMarker.csUpdate RootingHelpers call sites to pass sink/list directly.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VTableSliceNode.csConvert static + conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​VariantInterfaceMethodUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeThreadStaticIndexNode.csConvert non-relocation deps to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csConvert deps to sink usage and remove cached dependency list.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TrimmingDescriptorNode.csConvert descriptor-derived deps to sink enumeration adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TentativeInstanceMethodNode.csConvert conditional dep array to sink add.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StructMarshallingDataNode.csConvert deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StringAllocatorMethodNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​StaticsInfoHashtableNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SerializedFrozenObjectNode.csConvert conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​SealedVTableNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ScannedMethodNode.csConvert cached deps emission to sink iteration.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeMethodHandleNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​RuntimeFieldHandleNode.csConvert ldtoken deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionVirtualInvokeMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectionInvokeSupportDependencyAlgorithm.csConvert params-array dependency helper to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedTypeNode.csConvert deps to sink adds + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedMethodNode.csConvert reflectability deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedFieldNode.csConvert reflectability + signature deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ReflectedDelegateNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ProxyTypeMapNode.csConvert conditional deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​PropertyMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeFlowDependenciesNode.csRoute dataflow-derived deps into sink and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ObjectGetTypeCalledNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NotReadOnlyFieldNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​NonGCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csConvert module-use deps helper to sink-based signature (and should remove leftover lazy-init).
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleMetadataNode.csConvert static + conditional CA deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MethodParameterMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​MetadataEETypeNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidProxyTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InvalidExternalTypeMapNode.csConvert static deps to sink add; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceUseNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceGenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchMapNode.csConvert non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InterfaceDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​InlineableStringsResourceNode.csConvert helper signature to accept sink; replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GvmDispatchCellInfoSectionNode.csConvert “cell deps” helper from returning list to sink-based emitter.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodTableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericVirtualMethodImplNode.csConvert deps to sink usage + update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericTypesTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericStaticBaseInfoNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsTemplateMap.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericMethodsHashtableEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GenericDefinitionEETypeNode.csConvert non-relocation deps + metadata manager calls to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​GCStaticsNode.csConvert conditional + non-relocation deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FunctionPointerMapNode.csConvert helper signature to accept sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​FrozenObjectNode.csConvert relocation dependency discovery to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapRequestNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExternalTypeMapNode.csConvert conditional/static deps to sink usage + update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsNode.csConvert helper signature + metadata deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ExactMethodInstantiationsEntryNode.csConvert deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EventMetadataNode.csConvert CA-based conditional deps to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedTrimmingDescriptorNode.csConvert descriptor-derived deps to sink adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedPointerIndirectionNode.csUpdate abstract contract to sink-based AddStaticDependencies.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​EmbeddedObjectNode.csConvert empty conditional/dynamic deps to sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DispatchCellNode.csConvert cell dependency helpers to sink-based emitters.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DictionaryLayoutNode.csConvert static + conditional deps to sink usage and update dynamic signature.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateTargetVirtualMethodNode.csConvert static/conditional deps to sink-based no-ops / adds.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DelegateMarshallingDataNode.csConvert static deps to sink AddRange and update empty enumerables to no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​DataflowAnalyzedTypeDefinitionNode.csConvert dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CustomAttributeMetadataNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ConstructedEETypeNode.csConvert non-relocation deps + interop hook call to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CodeBasedDependencyAlgorithm.csConvert static + conditional dependency helpers to sink interfaces.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​CanonicalDefinitionEETypeNode.csConvert non-relocation deps to sink-based no-op.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ArrayOfEmbeddedPointersNode.csConvert deps to sink AddRange pattern.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedProxyTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalyzedExternalTypeMapNode.csConvert static deps to sink adds and replace empty enumerables with no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AnalysisCharacteristicNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​AddressTakenMethodNode.csReplace empty enumerables with sink-based no-ops.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​ReflectionMethodBodyScanner.csRoute Object.GetType dataflow dependencies into an injected sink.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​Dataflow/​GenericArgumentDataFlow.csConvert generic argument dataflow dependency plumbing to sink usage.
src/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​AnalysisBasedInteropStubManager.csUpdate interop manager overrides to sink-based signatures.

Comment on lines +61 to 65
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory context)
{
if (_staticDependencies == null)
{
_staticDependencies = new DependencyList();

foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationMethod);

foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries())
InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationType, entry.ImplementationMethod);
}
foreach (var entry in ScanForGenericVirtualMethodEntries())
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(sink, context, entry.CallingMethod, entry.ImplementationMethod);

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings August 31, 2026 01:12

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:17

  • dependencyList ??= new DependencyList(); no longer propagates a newly-created list back to the caller (the parameter is no longer ref). If dependencyList were ever null, this would silently drop dependencies into a temporary list, and it also keeps a now-unnecessary DependencyList alias. Since all current callers pass a non-null sink, remove the null-coalescing allocation and the unused alias import to avoid misleading behavior and unnecessary allocations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 02:40

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleUseBasedDependencyAlgorithm.cs:16

  • dependencyList ??= new DependencyList(); no longer behaves like the old ref DependencyList pattern. If a caller passes null, this only reassigns the local parameter and any dependencies added here are dropped on return (and the DependencyList alias becomes the only use of that using). Since callers are expected to provide a sink, it’s better to require non-null and avoid silently losing dependencies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b693437d-4563-4edc-9bf7-9901adaade97
CopilotAI review requested due to automatic review settings August 31, 2026 03:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

New issues introduced by this change (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”…
Pre-existing issues (1)
SeverityFinding
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Issues resolved since last review (1)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​ModuleUseBasedDependencyAlgorithm.csdependencyList ??= new DependencyList(); no longer has the intended effect after switching away… View resolved comment

Comment on lines +46 to +48
private readonly List<Dependency> _dependencies = new List<Dependency>();
private readonly DependencyNodeCore<DependencyContextType>.DependencyList? _staticDependencies;
private readonly DependencyNodeCore<DependencyContextType>.CombinedDependencyList? _conditionalDependencies;

@MichalStrehovskyMichalStrehovsky left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have unit tests for the framework in #130849. We haven't really done any changes to the framework ever since it was added in dotnet/corert#42 so there wasn't much of a need. I thought I might need to do a change this summer so I ported the tests, but then ended up not making the change anyway so the test PR wasn't a priority.

Comment on lines +55 to 63
protected override void ComputeNonRelocationBasedDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)
{
DependencyList dependencies = base.ComputeNonRelocationBasedDependencies(factory);
DependencySink<NodeFactory> dependencies = sink;
base.ComputeNonRelocationBasedDependencies(sink, factory);

factory.AddVirtualMethodDiscoveryDependencies(ref dependencies, _arrayType);
factory.AddVirtualMethodDiscoveryDependencies(dependencies, _arrayType);

return dependencies;
return;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patterns like this look sloppy (not going to comment on all).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair -- I thought the same thing but I thought it might make the review easier. I'll have copilot clean it up

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not just the return statement, also the DependencySink<NodeFactory> dependencies = sink; variable renaming. In this specific case the diff would actually be smaller without the variable renaming (doesn't apply everywhere).

}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
public override void AddStaticDependencies(DependencySink<NodeFactory> sink, NodeFactory factory)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DependencyListEntry is a nested class so it doesn't need to repeat the NodeFactory part and we don't need a using for the framework. It would definitely look better here for DependencySink to be like that too, not sure if there's any drawbacks though.

}

[Fact]
public void DependencyAnalyzerComputesDeferredStaticDependenciesBeforeDynamicDependencies()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand what this is testing.

Comment on lines +44 to +52
public override void AddDependenciesDueToMethodCodePresence(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)
{
}

public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
public override void AddInterestingInteropConstructedTypeDependencies(DependencySink<NodeFactory> dependencies, NodeFactory factory, TypeDesc type)
{
}

public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
public override void AddMarshalAPIsGenericDependencies(IDependencySink<NodeFactory> dependencies, NodeFactory factory, MethodDesc method)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the rules for when this should be IDependencySink interface and when it should use DependencySink class?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In short, IDependencySink should only be used when at least one of the callers uses a DependencyList to construct a temporary list and scan the results. This isn't very common -- I believe there are only two actual roots and I tried to move every place possible over to a DependencySink.

The biggest reason in general to use IDependencySink is when you want to review and filter results after receiving them. If you don't care, you might as well use the main sink, which doesn't guarantee those semantics (and can be kind of heavyweight in that case).

I thought about adding a comment here, but it wasn't quite clear where I should add it. Let me take another shot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So IDependencySink is the abstraction we need and DependencySink is an optimization when we can get away with it. How perf is the optimization really buying?

If everything is behind an interface, we could potentially also look into whether the sink (as used by the analysis engine) really needs to build a list, or whether we could run the marking logic the moment Add is called (those experiments can be done separately).

CopilotAI review requested due to automatic review settings August 31, 2026 20:53

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment

The DependencySink type was holding onto a special CombinedDependencyList just for
ShowMethodNode because ShadowMethodNode wanted to filter the canonical dependencies
in order to construct the specialized dependencies based on the runtime-determined
dependencies. This required wrapping a DependencyList in a DependencySink just to
collect dependencies and then filter.
By refactoring to have the IMethodBodyNode directly support specializing the
runtime-determined dependencies we can avoid saving and filtering the elements and
just filter before we add.
CopilotAI review requested due to automatic review settings September 1, 2026 01:28

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity · 1 Low severity

Pre-existing issues (2)
SeverityFinding
Medium severitysrc/​coreclr/​tools/​aot/​ILCompiler.DependencyAnalysisFramework/​DependencySink.cs — DependencySink allocates a List<Dependency> unconditionally, even when constructed in the “wrapper”… View comment
Low severitysrc/​coreclr/​tools/​aot/​ILCompiler.Compiler/​Compiler/​DependencyAnalysis/​TypeGVMEntriesNode.csAddStaticDependencies now re-scans all virtual slots and interfaces each time it is called. This… View comment
Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencySink.cs:114

  • DependencySink.Add(node, otherReasonNode, reason) bypasses the _staticDependencies fast-path, so if a sink is constructed around a DependencyList (e.g., new DependencySink(list)), using this overload will silently record the dependency in the sink’s private _dependencies list instead of the provided DependencyList. Adding a debug-time guard here would help catch accidental misuse (especially since DependencySink is also used as an IConditionalDependencySink).

Sign up for freeto 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.

3 participants

@agocke@MichalStrehovsky