Skip to content

Preserve module initialization across ReadyToRun inlining - #134359

Merged
AaronRobinsonMSFT merged 9 commits into
mainfrom
copilot/fix-unsafaccessor-observability
Oct 7, 2026
Merged

AaronRobinsonMSFT merged 9 commits into
mainfrom
copilot/fix-unsafaccessor-observability

Conversation

Copilot AI commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Composite ReadyToRun can inline away the call that activates another assembly, leaving its module initializer unexecuted. In the reported UnsafeAccessor case, the private-field write never occurs.

  • Activation: Retain a global-module TypeHandle fixup after successful cross-module inlining, including when the referenced module could gain an initializer in a later version.
  • Version resilience: Allow the global module type’s stable TypeDef token outside the version bubble while preserving checks for ordinary tokens.
  • Regression coverage: Cover composite and non-composite inlining, with and without initializers. Give the test reader generated assembly references so it can decode the activation fixups.

@azure-pipelines

azure-pipelines Bot commented Sep 21, 2026 •

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: @dotnet/crossgen-contrib
See info in area-owners.md if you want to be subscribed.

…s modules

Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix UnsafeAccessor changes in ReadyToRun Module Initializer Preserve module initialization across ReadyToRun inlining Sep 21, 2026
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

@jtschuster @davidwrighton or @jkotas Does this look correct?

Composite ReadyToRun can inline away the call that activates another assembly, leaving its module initializer unexecuted.

This statement seems like an oversight and I'm struggling to see how this has never been hit before.

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

@copilot Please create a test case that runs an UnsafeAccessor in the ModuleInitializer. I'd like to confirm this actually does what we want.

Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>

Copilot AI commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please create a test case that runs an UnsafeAccessor in the ModuleInitializer. I'd like to confirm this actually does what we want.

Added in bf320c7f: a three-assembly executable test whose module initializer uses UnsafeAccessor to set another assembly’s private static field, then asserts the write was observed. AlwaysUseCrossGen2 forces composite compilation through the normal test wrapper.

Verified on Linux x64 Checked: removing the activation fix makes the test fail with the field still false (exit 101); restoring it passes (exit 100). Ordinary JIT execution also passes.

@jkotas

jkotas commented Sep 21, 2026

Copy link
Copy Markdown
Member

Composite ReadyToRun can inline away the call that activates another assembly, leaving its module initializer unexecuted.
This statement seems like an oversight and I'm struggling to see how this has never been hit before.

The module initialization for R2R cross-module references should be taken care of by EnsureActive calls like

// We do not emit activation fixups for version resilient references. Activate the target explicitly.
. I guess we are missing these EnsureActive calls for cross-module field accesses.

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

@copilot Given the statement in #134359 (comment), how does that change your implementation?

Copilot AI commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Given the statement in #134359 (comment), how does that change your implementation?

I would retain the compiler-side activation dependency rather than replace it with a field-access fix. The distinction is which assembly needs activation:

  • The cited EnsureActive() handles a surviving method-entry import. Inlining the empty InitializerLibrary.Touch() removes that call, so its method-entry activation path is no longer reached.
  • The test subsequently reads a field in FieldLibrary, but the initializer that must run belongs to InitializerLibrary. Activating the field’s owner cannot activate the initializer’s assembly.
  • Ordinary non-RVA static-field helpers already call EnsureInstanceActive() followed by CheckRunClassInitThrowing() in DynamicHelperFixup, including the out-of-bubble field-address path.

The proposed TypeHandle fixup preserves the eliminated dependency and uses the existing EnsureInstanceActive() path; it does not introduce a different initialization mechanism.

That is consistent with the earlier execution check: removing only the inline-activation hunk made the three-assembly test fail; restoring it passed. This source review does not rule out a separate defect in other field-access paths, but a field-owner activation fix would not replace this dependency. No code changes or new test runs in this reassessment.

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

@copilot Please make the appropriate changes based on #134359 (comment) and your previous analysis at #134359 (comment).

Copilot AI commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please make the appropriate changes based on #134359 (comment) and your previous analysis at #134359...

My previous analysis recommended retaining the compiler fix already in this PR; it did not identify a confirmed missing activation check in the ordinary static-field path. I need to clarify the intended change: should I retain that fix and add independent field-access regression coverage to investigate Jan’s hypothesis, or are you asking me to replace the compiler fix with a VM-side approach? The latter would need to account for the initializer assembly being different from the field’s owning assembly.

@jkotas

jkotas commented Sep 24, 2026

Copy link
Copy Markdown
Member

I have missed that this is with large version bubble. I can see that we need the explicit activation fixups for cross-module inlined methods with large version bubble.

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

Copilot-Session: a4258af2-2f1f-4af6-b46e-0f6a1fae207f
@AaronRobinsonMSFT
AaronRobinsonMSFT marked this pull request as ready for review October 7, 2026 18:51
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a4258af2-2f1f-4af6-b46e-0f6a1fae207f
@AaronRobinsonMSFT
AaronRobinsonMSFT enabled auto-merge (squash) October 7, 2026 21:46
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/azp run runtime

Note

This CI retrigger command was posted by GitHub Copilot at the user's request.

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/ba-g Unrelated failures.

@AaronRobinsonMSFT
AaronRobinsonMSFT merged commit 8367641 into main Oct 7, 2026
117 of 119 checks passed
@AaronRobinsonMSFT
AaronRobinsonMSFT deleted the copilot/fix-unsafaccessor-observability branch October 7, 2026 22:36
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/backport to release/11.0

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/backport to release 10.0

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Started backporting to release/11.0 (link to workflow run)

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Started backporting to release (link to workflow run)

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

@AaronRobinsonMSFT backporting to release/11.0 failed, the patch most likely resulted in conflicts. Please backport manually!

git am output
$ git cherry-pick 83676416384116800b333e678cbb7a5b1f6a5fd5

Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
CONFLICT (content): Merge conflict in src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
Auto-merging src/coreclr/vm/jitinterface.cpp
error: could not apply 83676416384... Preserve module initialization across ReadyToRun inlining (#134359)
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue".
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".
hint: Disable this message with "git config set advice.mergeConflict false"


$ git am --3way --empty=keep --ignore-whitespace --keep-non-patch changes.patch

Creating an empty commit: Initial plan
Applying: Preserve module activation dependencies when ReadyToRun inlines across modules
Using index info to reconstruct a base tree...
M	src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
M	src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
M	src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs
M	src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
CONFLICT (content): Merge conflict in src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0002 Preserve module activation dependencies when ReadyToRun inlines across modules
Error: The process '/usr/bin/git' failed with exit code 128

Link to workflow output

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

@AaronRobinsonMSFT an error occurred while backporting to release. See the workflow output for details.

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/backport to release/10.0

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Started backporting to release/10.0 (link to workflow run)

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

@AaronRobinsonMSFT backporting to release/10.0 failed, the patch most likely resulted in conflicts. Please backport manually!

git am output
$ git cherry-pick 83676416384116800b333e678cbb7a5b1f6a5fd5

CONFLICT (modify/delete): src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs deleted in HEAD and modified in 83676416384 (Preserve module initialization across ReadyToRun inlining (#134359)).  Version 83676416384 (Preserve module initialization across ReadyToRun inlining (#134359)) of src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs left in tree.
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs
CONFLICT (content): Merge conflict in src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs
CONFLICT (content): Merge conflict in src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
Auto-merging src/coreclr/vm/jitinterface.cpp
error: could not apply 83676416384... Preserve module initialization across ReadyToRun inlining (#134359)
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue".
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".
hint: Disable this message with "git config set advice.mergeConflict false"


$ git am --3way --empty=keep --ignore-whitespace --keep-non-patch changes.patch

Creating an empty commit: Initial plan
Applying: Preserve module activation dependencies when ReadyToRun inlines across modules
Using index info to reconstruct a base tree...
A	src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
A	src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs
M	src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs
M	src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs
M	src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs
M	src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
Falling back to patching base and 3-way merge...
CONFLICT (modify/delete): src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs deleted in HEAD and modified in Preserve module activation dependencies when ReadyToRun inlines across modules.  Version Preserve module activation dependencies when ReadyToRun inlines across modules of src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs left in tree.
CONFLICT (modify/delete): src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs deleted in HEAD and modified in Preserve module activation dependencies when ReadyToRun inlines across modules.  Version Preserve module activation dependencies when ReadyToRun inlines across modules of src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs left in tree.
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs
CONFLICT (content): Merge conflict in src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs
CONFLICT (content): Merge conflict in src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs
Auto-merging src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0002 Preserve module activation dependencies when ReadyToRun inlines across modules
Error: The process '/usr/bin/git' failed with exit code 128

Link to workflow output

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

Backport pull requests:

Note

This comment was prepared with GitHub Copilot assistance.

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

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

UnsafeAccessor changes not observed when used in a ReadyToRun Module Initializer in .NET 11

4 participants