Repository navigation
[ARM64] R2R methods require RCPC2 without using RCPC2 instructions #133671
Description
Description
Crossgen2 adds RCPC2 dependencies to methods that contain no RCPC2 instructions, causing unnecessary R2R rejection on CPUs without RCPC2.
Context: dotnet/android#12722
Reproduction
Create Probe/Probe.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net11.0</TargetFramework>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
</Project>Add Probe/Probe.cs:
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
public sealed class Fields
{
[FieldOffset(0)]
public volatile int Small;
[FieldOffset(320)]
public volatile int Large;
}
public sealed class ReferenceField
{
public volatile object Value;
}
public static class Probe
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static int ReadSmall(Fields value) => value.Small;
[MethodImpl(MethodImplOptions.NoInlining)]
public static int ReadLarge(Fields value) => value.Large;
[MethodImpl(MethodImplOptions.NoInlining)]
public static void WriteSmall(Fields value, int data) => value.Small = data;
[MethodImpl(MethodImplOptions.NoInlining)]
public static void WriteLarge(Fields value, int data) => value.Large = data;
[MethodImpl(MethodImplOptions.NoInlining)]
public static void WriteReference(ReferenceField value, object data) => value.Value = data;
}Publish:
dotnet publish Probe/Probe.csproj -c Release -r linux-arm64Disassembly and dependency graphs for stock and RCPC1-only builds
Run from the directory containing Probe/. Clean between variants to avoid reusing R2R output:
set -euo pipefail
for policy in stock rcpc-only; do
output="$PWD/evidence/$policy"
mkdir -p "$output"
extra="--mapcsv;--parallelism:1;--dgmllog:$output/dependencies.dgml;--fulllog"
extra+=";--codegenopt:JitDisasm=*;--codegenopt:JitDisasmTesting=1"
extra+=";--codegenopt:JitDisasmWithCodeBytes=1;--codegenopt:JitStdOutFile=$output/jit-disasm.txt"
if [ "$policy" = rcpc-only ]; then
extra+=";--instruction-set:rcpc,-optimistic"
fi
escaped=${extra//;/%3B}
escaped=${escaped//,/%2C}
dotnet clean Probe/Probe.csproj -c Release -r linux-arm64
dotnet publish Probe/Probe.csproj -c Release -r linux-arm64 \
"-p:PublishReadyToRunCrossgen2ExtraArgs=$escaped" \
-o "$output/publish"
cp Probe/obj/Release/net11.0/linux-arm64/R2R/Probe.map.csv "$output/"
doneActual behavior
The stock build declares RCPC2 for all five methods, but only two use it:
| Method | Generated code | Uses RCPC2? |
|---|---|---|
ReadSmall |
ldapur w0, [x0, #8] |
Yes |
WriteSmall |
stlur w1, [x0, #8] |
Yes |
ReadLarge |
add x0, x0, #328; ldapr w0, [x0] |
No |
WriteLarge |
add x0, x0, #328; stlr w1, [x0] |
No |
WriteReference |
Call to CORINFO_HELP_ASSIGN_REF |
No |
ReadLarge:
stp x29, x30, [sp, #-0x10]!
mov x29, sp
add x0, x0, #328
ldapr w0, [x0]
ldp x29, x30, [sp], #0x10
retDespite using only RCPC1, it has this dependency:
MethodWithGCInfo(Probe_Probe__ReadLarge)
-> PrecodeHelperImport->SignaturePointer_ReadyToRunInstructionSets_Rcpc+Rcpc2,
reason: classMustBeLoadedBeforeCodeIsRun
The emitted ISA signature also requires RCPC2. With --instruction-set:rcpc,-optimistic, ReadLarge, WriteLarge and WriteReference have byte-identical machine code without that dependency.
Expected behavior
ReadLarge, WriteLarge and WriteReference should not require RCPC2.
Likely cause
Lowering::TryCreateAddrMode calls compOpportunisticallyDependsOn(InstructionSet_Rcpc2) before checking whether the addressing mode is usable.
That call records the dependency, which survives when a later offset/index/containment check fails.
Here, offset 328 is outside the signed 9-bit range. The reference-store case also records RCPC2 even though codegen takes the GC write-barrier path.
Impact and environment
In a MAUI sample-content composite, 117 of 564 RCPC2-guarded bodies contain no RCPC2 instruction. The other 447 genuinely use RCPC2. Startup impact of a fix has not been measured.
Reproduced on a macOS ARM64 host, publishing a net11.0 library for linux-arm64, with:
- .NET SDK
11.0.100-rc.1.26431.118 - crossgen2/runtime
11.0.0-rc.1.26431.118
The same check is present in runtime main at c307f70 (source inspection only).
Drafted with the Copilot App.
Activity
dotnet-policy-service commentedon Sep 11, 2026
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.
[-][ARM64] Spurious RCPC2 dependencies in ReadyToRun code[/-][+][ARM64] R2R methods require RCPC2 without using RCPC2 instructions[/+]EgorBo commentedon Sep 11, 2026
JulieLeeMSFT commentedon Sep 11, 2026
dotnet-policy-service commentedon Sep 11, 2026
Tagging subscribers to this area: @dotnet/crossgen-contrib
See info in area-owners.md if you want to be subscribed.
dotnet-policy-service commentedon Sep 11, 2026
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.
jkotas commentedon Sep 11, 2026
This is JIT bug in this code:
runtime/src/coreclr/jit/codegenarmarch.cpp
Lines 1656 to 1693 in b44cd90
dotnet-policy-service commentedon Sep 26, 2026
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.
Fix JIT over reporting RCPC2 (#135134)