Skip to content

[CoreCLR] Exclude RCPC2 from ARM64 ReadyToRun defaults - #12722

Open
simonrozsival wants to merge 8 commits into
mainfrom
simonrozsival-maui-startup-profiling
Open

simonrozsival wants to merge 8 commits into
mainfrom
simonrozsival-maui-startup-profiling

Conversation

@simonrozsival

@simonrozsival simonrozsival commented Sep 8, 2026

Copy link
Copy Markdown
Member

Summary: current policy

Default Android CoreCLR ARM64 ReadyToRun to the explicit ISA list tested on Samsung A16 and S23:

--instruction-set:-optimistic,aes,crc,dotprod,lse,rcpc,rdma,sha1,sha2

This disables automatic optimistic additions and explicitly enables the listed features, retaining the queried ARM64 features except RCPC2 without asserting that RCPC2 must be absent. Other Android CPU architectures retain --instruction-set:-optimistic. Mono, NativeAOT and runtime JIT hardware-specific/tiered optimization are unchanged.

This is a higher baseline, not a selective optimistic set. The eight explicit features become eager image-level requirements. A device missing a required effective runtime capability can reject the composite's R2R code and fall back to JIT. The A16 and S23 advertise all eight; these two-device results are not a universal Android compatibility guarantee.

Current evidence: the exact explicit-list APKs launch and retain R2R on both phones, with 65 pre-Ready JIT compilations in each explicit-list census. This is functional/R2R evidence, not a demonstrated 10% speedup for the new default. The larger historical benchmark below measured stock versus -optimistic and an RCPC1-only sensitivity policy, not this new explicit list. The explicit-list A16 first-use Ready observations were slower than the controls; equal performance/non-regression is not established.

Implementation and overrides

  • Select the ARM64 list only for RuntimeIdentifier=android-arm64, before _PrepareForReadyToRunCompilation, after project/package imports have supplied their arguments. Android's per-RID inner builds supply this property.
  • Preserve existing extra arguments, including partial compilation and map options; other architectures keep the prior conservative policy.
  • Any explicit --instruction-set option in either PublishReadyToRunCrossgen2ExtraArgs or PublishReadyToRunCrossgen2CompositeExtraArgs suppresses the default, independently of composite mode. Both properties are preserved, and command-line global properties retain MSBuild precedence.
  • A composite-only override opts the entire build out, including PublishReadyToRunCompositeExclusions. The SDK concatenates normal and composite arguments for the composite image; crossgen2 rejects duplicate ISA options. Put one override in the normal arguments to cover both the composite and excluded assemblies.
  • Use the private _AndroidUseOptimisticReadyToRunInstructionSet=true escape hatch to skip the default ISA override. The target runs when this property is not true: blank/false keeps the override enabled. Opting out leaves supplied arguments unchanged and does not disable R2R. No new public property is introduced.
  • Update the build-property documentation and retain the architecture-aware assertion in the existing BasicApplicationPublishReadyToRun integration test. The separate ReadyToRunInstructionSet parameterized test has been removed.

No custom profile retraining, runtime feature masking or application rewrite is included in this PR.

What the ISA experiments established

Stock RCPC2 code was present, but rejected on A16

The measured A16 is SM-A165F / MT6789; the S23 is SM-S911B / SM8550. Both run Android 16/API 36. The A16 advertises RCPC but not RCPC2; the S23 advertises both.

The actual ConcurrentDictionary<__Canon,__Canon>.TryGetValueInternal native body contained LDAPR and LDAPUR, with an RCPC2 entry dependency. On A16, the runtime rejects the unsatisfied fixup and falls back to Tier 0 JIT. The profile already contained 21 canonical reference/reference owner records, including constructors, GetOrAdd, lookup, insertion, growth and lock helpers: missing profile coverage was not the cause of these rejections.

An optimized crossgen2 replay recovered from the binlog's actual TaskCommandLineEventArgs, with verbose/maps/dependency output, reproduced that diagnostic composite byte-for-byte. It attempted 20,230 methods, emitted 20,162 native method nodes and explicitly skipped 68 (51 always-throw and 17 no-IL declarations); none overlapped the original Tier 0 target set's owner/member stems. An initial strings-based extraction that dropped -O was excluded.

ARM64 volatile lowering / native-entry fixup failure.

Literal -rcpc2 is not the chosen solution

The negative switch creates a compatibility condition, not an ARM hardware limitation. The parser calls RemoveInstructionSetSupport("rcpc2"), which removes supported membership and adds explicitly unsupported membership:

_supportedInstructionSets.Remove(instructionSet);
_unsupportedInstructionSets.Add(instructionSet);

For this JIT-enabled target, crossgen2 writes ExplicitlyUnsupportedFlags into the baseline ISA signature and roots it in EagerImports. Per-method dependency generation trusts that global baseline. Parser / builder / eager baseline.

The actual negative image contains:

EagerImports->SignaturePointer_ReadyToRunInstructionSets_ArmBase+AdvSimd+Atomics,Rcpc2
Signature RVA: 0x0063AAE0
File offset:   0x0060AAE0
Bytes:         30 04 21 23 2b 54

30 is the ISA-support fixup; 04 is its entry count. Entries encode (feature ID << 1) | expectedSupport. RCPC2 is ID 42: 0x54 means expected unsupported, whereas 0x55 would mean expected supported. The other entries require ArmBase, AdvSimd and Atomics. Signature writer.

CoreCLR compares its effective CPU-flag boolean with the expected boolean, not just a minimum architecture version. On an RCPC2-enabled S23, true != false; failed eager fixups disable R2R through the composite. IL/JIT fallback keeps the app running. "Absent" means unsupported in CoreCLR's effective flags, not necessarily absent from the silicon; no runtime feature masking was introduced. Runtime comparison / composite disablement.

S23, before native Ready, separate one-launch census -optimistic control Literal -rcpc2
Tier 0 compilations 34 13,928
Plain Optimized compilations 31 264
Tier 1 / OSR 0 / 0 0 / 1
Total JIT compilation events 65 14,193
Distinct JITted runtime methods 65 14,192
Nonzero R2R entrypoint results 8,704 0
Null R2R entrypoint results 62 14,578
Explicit per-method rejection flags 0 0

Zero explicit rejection flags do not mean R2R succeeded. The disabled-image path returns before per-method lookup/fixup checks. The negative trace has zero lookup-start events; its 14,578 null results before Ready cover 14,188 distinct runtime method identities. Over the complete recording it has 14,586 null results covering 14,196 methods, with zero nonzero results. The control has 8,779 lookup-start events and 8,717 nonzero / 66 null results over its complete recording. Null results are not individually proven ISA rejection events; ordinary missing bodies can return null too.

The negative image's startup JIT work included 4,243 CoreLib, 3,170 MAUI Controls, 1,612 MAUI, 1,009 Mono.Android and 917 Syncfusion Toolkit events. Its one OSR compilation was Element.OnResourcesChangedKeys, not relabeled Tier 0.

Four separate untraced fresh-install C/N/C/N launches all rendered the sample:

Order Policy TTID ms Ready ms
1 -optimistic 713 1,259
2 Literal -rcpc2 1,546 2,489
3 -optimistic 554 1,072
4 Literal -rcpc2 1,448 2,310

These are compatibility observations, not a statistical slowdown estimate: two per policy, USB charging, battery temperatures 38.5-39.9 C, no filtering or confidence interval. The first control's secondary bounded crash-log query was incomplete; its successful launch/Ready/screenshot were retained.

Why negative dependencies exist, and why this does not dismiss selective optimism

Feature availability can affect observable behavior, not only instruction choice. As an x86 illustration, a method returning Avx2.IsSupported can be folded under an absent-feature assumption and disagree with a JIT version on an AVX2-capable process. This is not an RCPC2 API or an observed Android sample method.

The source exempts ordinary reviewed CoreLib fallbacks from many negative per-method dependencies, but keeps get_IsSupported and get_IsHardwareAccelerated as exceptions. This supports a generic dependency policy, not proof that a blanket RCPC2-absence assertion is necessary. We have not shown that the sample's weaker bodies intrinsically require the CPU to lack RCPC2. Dependency recording / per-method handling.

The tested crossgen2 has no separate optimistic-only exclusion switch. Its help advertises -feature as disabling optimistic light-up but omits the additional eager absence condition. -optimistic is special-cased as allowOptimistic = false, without marking all omitted features explicitly unsupported. Positive additions then become baseline requirements.

Consequently, the selected explicit list is not equivalent to Vlad's narrower suggestion to remove only RCPC2 from automatic optimistic additions. That would require a compiler policy change or new option. It remains unimplemented/unmeasured here; broader code-quality and IsSupported concerns are not disproven.

The selected explicit list: host proof and two-device confirmation

The exact compiler replay uses -optimistic,aes,crc,dotprod,lse,rcpc,rdma,sha1,sha2, with all 188 linked inputs, eight profiles and other arguments fixed. The new eager signature is:

ReadyToRunInstructionSets_ArmBase+AdvSimd+Aes+Crc32+Dp+Rdm+Sha1+Sha256+Atomics+Rcpc,
RVA:   0x0063A6A0
Bytes: 30 0a 21 23 13 25 2f 31 27 29 2b 35

The only other ISA signature requires ArmBase/AdvSimd/VectorT128. RCPC2 appears in neither a positive nor a negative requirement. The image is 7,443,968 bytes, SHA-256 99973c94cb9fb2f70c0d0365515caaa0b584a12c5bb8e543a7dad104ad99a58a.

The exact same plain and native-census APKs were used on A16 and S23. Each census variant preserves the corresponding plain variant's managed/R2R/profile/runtime-feature bytes; only native diagnostic configuration and signing members differ. The native app compression descriptor was unchanged versus the corresponding control because the composite size was unchanged. All 1,460 ZIP entries and 223 managed payloads plus 223 empty lookup descriptors were audited, with v2/v3 signatures and 16 KB alignment.

Before native Ready, one census per policy/device Control JIT (Tier 0 + Optimized) Explicit-list JIT (Tier 0 + Optimized) Control nonzero R2R Explicit-list nonzero R2R
S23 70 (39 + 31) 65 (34 + 31) 8,708 8,703
A16 65 (34 + 31) 65 (34 + 31) 8,711 8,708

All four captures have zero explicit per-method rejection flags and no startup Tier 1/OSR/MinOpt/unknown tiers. Null results are S23 control/explicit 67/62 and A16 62/62. Complete-recording nonzero results are S23 8,721/8,721 and A16 8,727/8,724. These are result-event counts, not invocations. The small count differences do not establish a repeatable benefit.

Each phone had an untraced seed launch before these censuses. Both this comparison and the literal-negative comparison used native provider Microsoft-Windows-DotNETRuntime:0x6000080018:5 for 15 seconds, without periodic sampling or allocation recording; managed EventSource/Metrics remained disabled. Actual EntryPoint payloads were decoded using the registered CLR parser. All captures had zero raw/ETLX loss, matching event totals, complete rundown, no truncation or payload errors, and 1 GHz monotonic-clock alignment. No JIT/R2R count changes within Ready's 1 ms quantization interval.

Separate untraced fresh-install observations:

Device Order Policy TTID ms Ready ms
S23 1 Control 608 1,098
S23 2 Explicit list 520 1,054
S23 3 Control 524 1,073
A16 1 Control 1,203 2,354
A16 2 Explicit list 1,214 2,623
A16 3 Control 1,097 2,462
A16 4 Explicit list 1,157 2,567

All listed launches rendered the sample. The S23's planned fourth launch was not run: secondary bounded log export hit invalid UTF-8 after the third successful control launch. The original blocked record and all three successful run records were retained; no successful launch was discarded or rerun. The private log decoder was then corrected and checked with a malformed-byte host fixture; all four later A16 trial/log records completed. The native census was separate and completed on both phones.

No speedup or equivalence claim is made from these first-use observations. A16's explicit-list Ready times were slower in both observed pairs. USB charging was active; S23 prelaunch temperatures were 32.1-33.7 C, A16 28.6-28.7 C. These few observations are not pooled with the 480 historical seeded timings below; traced durations are not substituted for untraced timings.

Candidate APK SHA-256s: plain 077e88e16bae6e6774b670e35c10a074e018d151c9691fd9c6d967e14dd3fd0e; native census 2f9450326eb88ba9d251b9c3f1c7f636dd40f7a373d69b5da778993ae99c10a9. The executable section is not byte-identical to the earlier RCPC1-only sensitivity control, so its timing data are not reused as measurements of this new image.

Older-phone coverage and the baseline tradeoff

The measured A16 advertises aes crc32 asimddp atomics lrcpc asimdrdm sha1 sha2, but not ilrcpc; the S23 advertises all nine queried features. rdma is Arm RDM SIMD arithmetic, not networking RDMA; sha2 maps to SHA-256 support.

Public AArch64 captures show why this cannot be generalized to all Android phones:

Captured phone / SoC Missing from the queried nine
Redmi Note 8 / Snapdragon 665 LSE, RCPC, RCPC2, dotprod, RDM
Redmi Note 9 / Helio G85 RCPC2 only
OnePlus 9 Pro / Snapdragon 888 RCPC2 only

"Missing" means not advertised by that captured OS/kernel, not proven physical absence or a guarantee for every SKU. The 665 capture is official Android 9/MIUI 11.0.9; the G85 is Android 11/MIUI 12.5.4; the 888 fixture does not specify OS/kernel. Kernel backports and the common safe capability set across heterogeneous cores matter. No .NET startup measurements were made on these public devices. Linux capability reporting.

LSE is already part of the pinned Android ARM64 R2R baseline (armv8-a plus lse), even with -optimistic. The historical stock and conservative images both require Atomics eagerly. A non-LSE target can therefore fail the old baseline too; the new explicit list additionally makes RCPC/dotprod/RDM/crypto/CRC requirements eager. This is an intentional stronger-baseline tradeoff, not a portable replacement for optional optimism. Baseline source.

The frozen historical stock sample's guards reference only LSE/RCPC/RCPC2 among the queried nine. No AES/CRC/dotprod/RDM/SHA ISA guards occur in that exact image. There is no demonstrated independent startup benefit from enabling those six for this sample; other workloads can differ.

Related experiments: background, not additional product changes

Further A16 compiler studies used the historical conservative baseline, not the new explicit list. Across 288 measured launches, filtered paired Ready savings were --method-layout:hotcold -7.54 ms (descriptive 95% interval -24.24 to +9.14), --Os +8.39 ms (-5.71 to +22.68), and --Ot +12.90 ms (-6.06 to +32.39). Every interval includes zero; none established extra startup improvement or +/-1% equivalence. --hot-cold-splitting crashed before Ready, including both candidates in a separate C/H/C/H reproduction while both controls succeeded. No flag combinations or additional defaults were adopted.

Profile inspection also separated method coverage from richer PGO: custom/generated profiles were coverage-only, while six shipped MAUI profiles contained edge/type instrumentation; none of the eight inputs had nonzero exclusive/call layout weights. A separate host lifecycle control showed that stopping EventPipe before managed exit loses shutdown PGO export: default/custom rundown alone yielded zero schemas, while exit with capture active yielded 908 chunks and 603 schema-bearing methods. EventPipe sample-type selection in the converter is a separate layout-weight issue. No profile-pipeline/runtime implementation change is part of this PR.

Historical evidence boundary

Everything in the measurement setup, statistical timing tables and eight-launch census below compares the earlier stock, RCPC1-only and conservative policies. The historical approximately 10% A16 gain is stock versus -optimistic, not a benchmark result for the current explicit ARM64 list. The original observations, filtering and S23 non-regression limitations remain intact.

Measurement setup

Workload: unchanged dotnet new maui --sample-content; SDK 11.0.100-rc.1.26431.118, CoreCLR 11.0.0-rc.1.26431.118, Android 37.0.0-rc.1.2257, MAUI 11.0.0-rc.1.26451.6; Release ARM64, partial trimming and composite/partial R2R. Measured Android source: b65b55d5357abb23960a999c7c5ffaceb75c4515; MAUI source: 484132f9e51f4d1eae72dba038575d6102639730.

Within each typemap study, all managed compiler inputs (188 trimmable / 91 LLVM-IR), eight profile inputs, package graph, runtime configuration, DEX, resources and manifest match exactly across ISA policies. Only native assembly-store/application-descriptor payloads differ. Trimmable uses its completed 15,322-record custom profile; LLVM-IR uses its independently trained 14,912-record profile. The same profile is used on both sides of each ISA comparison. The custom profiles add method coverage rather than block/edge counts; the six shipped MAUI MIBCs already supply instrumentation data, including edge counts and type histograms. They are not a direct trimmable-versus-LLVM benchmark.

Diagnostics, managed EventSource and MAUI layout meters are disabled during timing. The same native Java endpoint reports useful content after initial data loading and two committed frames. TTID is Android am start -W TotalTime. The dedicated study app is seeded during conditioning and force-stopped between measured launches; existing app data is untouched. Every installation is non-incremental, and launches start from HOME after one second of settling.

There are 48 measured launches per policy per phone. Trimmable uses all six permutations of three policies; LLVM-IR uses six AB/BA-alternating paired blocks. Each treatment/block has eight measured launches plus one excluded post-install conditioning launch: 480 successful timing measurements and 60 conditioning launches overall.

Policy Crossgen2 override Role
Stock None Current optimistic defaults
RCPC1 only --instruction-set:rcpc,-optimistic Trimmable sensitivity control, retains RCPC1 but not RCPC2
Conservative --instruction-set:-optimistic Historical conservative control (not the current ARM64 default)

The control policies disable other optimistic additions as well; in the actual frozen images the decoded requirement sets differ only in RCPC/RCPC2, while baseline/VectorT128 requirements remain. RCPC1-only has a positive RCPC1 requirement, not an RCPC2-absence assertion.

Statistics: positive savings mean baseline minus candidate. The primary estimator is the mean of paired-block mean differences; 10,000 fixed-seed hierarchical bootstrap replicates resample paired blocks and then launches within each arm. Intervals are descriptive, unadjusted for multiple comparisons and based on one device/session per study. With filtering, each block retains equal weight even when arm sample counts differ, so paired estimates can differ from pooled mean differences.

At the user's request, a post-hoc 1.5 x IQR Tukey rule was applied identically within each device/policy to TTID and useful-content time. A whole launch is excluded if either metric lies outside its fence; fixed fences are used during bootstrap. Unfiltered data and exact exclusions remain below. Outliers are not automatically measurement errors. The interpretation margins declared before these runs were 5% for a substantial saving and +/-1% for negligible startup difference; non-significance is not equivalence.

Untraced timing results

All successful launches, before outlier filtering

Device Typemap Policy N Mean TTID ms Mean Ready ms Median Ready ms Ready p90 ms
A16 trimmable Stock 48 1,433.8 1,677.9 1,675.0 1,708.2
A16 trimmable RCPC1 only 48 1,279.3 1,518.2 1,512.5 1,553.3
A16 trimmable Conservative 48 1,274.4 1,512.9 1,508.5 1,550.9
S23 trimmable Stock 48 545.6 636.8 633.5 683.7
S23 trimmable RCPC1 only 48 539.5 629.8 631.0 668.8
S23 trimmable Conservative 48 547.4 638.9 639.5 675.3
A16 llvm-ir Stock 48 1,425.4 1,653.6 1,668.0 1,711.5
A16 llvm-ir Conservative 48 1,273.8 1,493.7 1,507.0 1,550.9
S23 llvm-ir Stock 48 553.8 645.5 646.0 686.1
S23 llvm-ir Conservative 48 545.7 626.4 608.0 686.0

With the requested outlier filter

Device Typemap Policy Retained N Mean TTID ms Mean Ready ms Median Ready ms Ready p90 ms
A16 trimmable Stock 45 1,431.8 1,674.0 1,675.0 1,703.0
A16 trimmable RCPC1 only 47 1,277.6 1,516.6 1,512.0 1,552.4
A16 trimmable Conservative 42 1,267.8 1,506.4 1,505.5 1,528.0
S23 trimmable Stock 45 540.3 631.6 633.0 667.0
S23 trimmable RCPC1 only 47 537.5 627.4 630.0 664.6
S23 trimmable Conservative 38 549.9 645.3 642.0 670.1
A16 llvm-ir Stock 48 1,425.4 1,653.6 1,668.0 1,711.5
A16 llvm-ir Conservative 48 1,273.8 1,493.7 1,507.0 1,550.9
S23 llvm-ir Stock 47 552.0 643.6 646.0 681.6
S23 llvm-ir Conservative 47 542.9 623.0 608.0 680.6

Historical conservative policy versus stock: paired effects

All intervals below are descriptive 95% saving intervals. Negative saving means slower.

Useful-content Ready

Device Typemap Analysis Saving ms Saving % 95% interval ms 95% interval %
A16 trimmable Raw 165.0 9.83% 151.6 to 178.1 9.1 to 10.6
A16 trimmable Filtered 167.4 10.00% 156.9 to 177.3 9.4 to 10.6
S23 trimmable Raw -2.1 -0.34% -25.8 to 21.3 -4.2 to 3.3
S23 trimmable Filtered -13.9 -2.21% -35.9 to 7.9 -5.9 to 1.2
A16 llvm-ir Raw 159.9 9.67% 144.8 to 177.7 8.8 to 10.8
A16 llvm-ir Filtered 159.9 9.67% 144.8 to 177.7 8.8 to 10.8
S23 llvm-ir Raw 19.1 2.96% -18.7 to 50.7 -3.0 to 7.7
S23 llvm-ir Filtered 20.4 3.17% -11.2 to 47.9 -1.8 to 7.3

Initial display (TTID)

Device Typemap Analysis Saving ms Saving % 95% interval ms 95% interval %
A16 trimmable Raw 159.4 11.12% 145.0 to 173.5 10.2 to 12.0
A16 trimmable Filtered 164.0 11.45% 153.3 to 174.2 10.7 to 12.1
S23 trimmable Raw -1.8 -0.32% -15.9 to 10.6 -3.0 to 1.9
S23 trimmable Filtered -11.3 -2.10% -21.1 to -1.0 -3.9 to -0.2
A16 llvm-ir Raw 151.6 10.64% 137.9 to 167.0 9.7 to 11.7
A16 llvm-ir Filtered 151.6 10.64% 137.9 to 167.0 9.7 to 11.7
S23 llvm-ir Raw 8.0 1.45% -18.2 to 28.8 -3.3 to 5.1
S23 llvm-ir Filtered 8.8 1.59% -11.4 to 26.2 -2.1 to 4.7

Interpretation: A16 improves robustly with either typemap; every stock/conservative paired block favors conservative codegen. S23 useful-content estimates remain inconclusive. The filtered S23 trimmable TTID contrast indicates about 11.3 ms / 2.10% slower initial display, with a slowdown interval of approximately 1.0-21.1 ms; this should not be hidden behind the inconclusive Ready result. The filtered Ready intervals still permit approximately 5.9% slowdown with trimmable or 1.8% with LLVM-IR. No universal high-end non-regression or steady-state-throughput claim is made.

RCPC1-only sensitivity comparisons (trimmable)
Device Analysis Metric Comparison Saving ms Saving % 95% interval ms
A16 Raw ttid_ms Stock -> RCPC1 only 154.5 10.78% 144.5 to 164.5
A16 Raw ready_ms Stock -> RCPC1 only 159.7 9.52% 148.9 to 171.0
A16 Raw ttid_ms RCPC1 only -> Conservative 4.9 0.38% -7.0 to 16.2
A16 Raw ready_ms RCPC1 only -> Conservative 5.3 0.35% -7.1 to 17.7
A16 Filtered ttid_ms Stock -> RCPC1 only 154.5 10.79% 145.0 to 164.7
A16 Filtered ready_ms Stock -> RCPC1 only 157.7 9.42% 148.0 to 168.7
A16 Filtered ttid_ms RCPC1 only -> Conservative 9.5 0.74% -0.1 to 18.7
A16 Filtered ready_ms RCPC1 only -> Conservative 9.6 0.63% -1.2 to 20.6
S23 Raw ttid_ms Stock -> RCPC1 only 6.1 1.11% -9.2 to 20.4
S23 Raw ready_ms Stock -> RCPC1 only 6.9 1.09% -16.8 to 28.3
S23 Raw ttid_ms RCPC1 only -> Conservative -7.8 -1.45% -22.9 to 7.0
S23 Raw ready_ms RCPC1 only -> Conservative -9.1 -1.44% -33.2 to 17.3
S23 Filtered ttid_ms Stock -> RCPC1 only 2.1 0.40% -9.2 to 10.9
S23 Filtered ready_ms Stock -> RCPC1 only 3.1 0.50% -17.7 to 19.7
S23 Filtered ttid_ms RCPC1 only -> Conservative -13.5 -2.50% -24.9 to -1.9
S23 Filtered ready_ms RCPC1 only -> Conservative -17.1 -2.72% -40.9 to 4.4

Outliers, interruptions and environment

Device Typemap Excluded stock / RCPC1 / conservative Battery temperature C
A16 trimmable 3/1/6 30.9-32.2
S23 trimmable 3/1/10 33.7-39.2
A16 llvm-ir 0/-/0 30.1-31.0
S23 llvm-ir 1/-/1 30.3-34.9

Trimmable S23 filtering removes eight unusually fast conservative runs as well as slow outliers, shifting its Ready estimate from 0.34% slower raw to 2.21% slower filtered. LLVM-IR S23 filtering removes one slow run per policy. The two views are retained rather than choosing whichever supports the hypothesis.

S23 installer timeouts interrupted the final block of each study before the affected launch measurements. Completed observations were preserved. Active-user/non-streaming installs were used on recovery; the last APK had committed despite the client timeout and was hash-verified before reuse. Conditioning was retained, and first-five-block sensitivity was evaluated. The LLVM incremental build initially had a stale after-link composite; it was detected and excluded before device measurement, then the postprocessing stamps were invalidated and the packaged image checked against fresh crossgen2 output.

Benchmark-matched single-launch JIT census

One separate seeded traced launch per typemap/policy/phone (eight total), cut at Ready. Each census APK has the exact timed build's managed inputs, R2R composite, profiles and runtime feature switches. Only the native diagnostic-port configuration changes. AndroidEnableProfiler=true enables transport while EnableDiagnostics=false, EventSourceSupport=false and MetricsSupport=false keep the managed diagnostic features disabled. No allocation sampler or sampling profiler is used.

Provider: Microsoft-Windows-DotNETRuntime:0x4000080018:5. Native Java Ready/Idle timestamps are aligned with the runtime's CLOCK_MONOTONIC counter; its inferred frequency is 1 GHz and no count changes within the marker's 1 ms quantization interval. All eight traces report zero lost events. Traced elapsed times are not added to the untraced performance cohorts.

Device Typemap Policy Tier 0 Optimized (not Tier 1) Tier 1 OSR Total R2R rejections
A16 llvm-ir Stock 389 53 0 0 442 329
A16 llvm-ir Conservative 58 53 0 0 111 0
A16 trimmable Stock 377 33 0 0 410 342
A16 trimmable Conservative 34 31 0 0 65 0
S23 llvm-ir Stock 57 52 0 0 109 0
S23 llvm-ir Conservative 57 52 0 0 109 0
S23 trimmable Stock 33 31 0 0 64 0
S23 trimmable Conservative 34 31 0 0 65 0

Startup compilation-event counts equal distinct runtime-method counts in these launches. Tier classification comes from matched method-load flags, not thread, duration or code-version ID. QuickJitted/instrumented quick JIT are Tier 0; plain Optimized is not Tier 1; instrumented Tier 1 and OSR remain separate. No MinOpt or unknown-tier startup events were observed.

Expected compilation after Ready, through the approximately three-second Idle marker
Device Typemap Policy Tier 0 Optimized Tier 1 OSR Total
A16 llvm-ir Stock 19 0 276 0 295
A16 llvm-ir Conservative 8 0 283 0 291
A16 trimmable Stock 14 1 187 0 202
A16 trimmable Conservative 7 1 186 0 194
S23 llvm-ir Stock 6 0 136 0 142
S23 llvm-ir Conservative 6 0 141 0 147
S23 trimmable Stock 3 1 104 0 108
S23 trimmable Conservative 3 1 110 0 114

These are compilation events, not distinct methods across tiers, and are excluded from startup counts. Tier 1 is expected runtime optimization, not a startup profile-coverage failure.

Earlier compiler/profile isolation experiments: why the illustrative two-JIT result differs

The earlier trimmable diagnostic/managed-marker build had 379/380/380 Tier 0 starts, 34 other Optimized starts per run, and no Tier 1/OSR before Ready. A fresh control had 380 Tier 0, including 343 rejected R2R entries. Device-specific RCPC2 exclusion produced 37/37/37; conservative optimistic-ISA policy produced 50/37/37, retaining the initial finalizer/lock-path extra work.

Of the stable 37, 36 were absent from all eight original profile inputs and one was explicitly bypassed by R2R. Targeted compiler probes could emit the AndroidRuntime constructor, a generic runtime helper and a JNI creation-option setter. A new MethodDetails/R2R-entrypoint-enabled capture (0x6000080019:5) and profile merge added 51 records, 15,271 -> 15,322. Conservative codegen plus that completed profile yielded 2/2/2 Tier 0 in that diagnostic variant, about 0.826 ms combined compilation spans, plus 27 other Optimized compilations per run.

The two exceptions were ProbabilisticMap.SetCharBit ([BypassReadyToRun] for representation correctness) and Single.MultiplyAddEstimate (mandatory nondeterministic estimate-intrinsic expansion explicitly blocked in R2R; an explicit single-method probe reports JIT implementation limitation). Do not remove these safety/semantic restrictions to make a counter zero.

That two-method count is not the timed APK's count. The later matched census above preserves the feature-off/native-Java-Ready timing binaries, including their additional JNI readiness, feature-off first-use and UI/scroll paths. It supersedes the earlier diagnostic counts for interpreting the timing tables. The custom profile changes and runtime exceptions are not modified by this PR.

Representation constraint | Estimate-intrinsic R2R restriction

Reproducible per-block timing observations and exclusion audit

All values are milliseconds. Lists contain measured launches 1-8 in each block; conditioning is omitted. excluded lists the 1-based launch indices removed by the rule above. Trimmable block order: S/R/C, R/C/S, C/S/R, C/R/S, R/S/C, S/C/R. LLVM-IR order alternates S/C and C/S. S=stock, R=RCPC1-only, C=conservative. These observations reproduce both raw and filtered summaries and paired-block contrasts.

typemap,device,policy,block,ttid_ms_1_to_8,ready_ms_1_to_8,excluded_indices
trimmable,a16,stock,0,"1436 1408 1409 1471 1441 1468 1430 1471","1680 1646 1642 1729 1681 1711 1675 1707","4"
trimmable,a16,rcpc1,0,"1272 1357 1243 1296 1310 1250 1282 1244","1513 1597 1478 1529 1545 1485 1520 1475","2"
trimmable,a16,baseline-isa,0,"1270 1270 1263 1275 1277 1272 1265 1260","1515 1512 1509 1506 1511 1513 1497 1489","-"
trimmable,a16,stock,1,"1431 1414 1443 1453 1428 1465 1426 1438","1666 1658 1689 1692 1672 1706 1659 1697","-"
trimmable,a16,rcpc1,1,"1311 1266 1299 1256 1256 1254 1311 1265","1553 1504 1538 1496 1491 1484 1562 1500","-"
trimmable,a16,baseline-isa,1,"1257 1310 1261 1246 1255 1265 1268 1245","1494 1546 1512 1494 1484 1511 1500 1479","-"
trimmable,a16,stock,2,"1438 1431 1485 1440 1446 1430 1442 1445","1681 1665 1737 1675 1681 1677 1682 1675","3"
trimmable,a16,rcpc1,2,"1285 1268 1266 1261 1277 1278 1314 1289","1527 1502 1511 1494 1541 1519 1556 1532","-"
trimmable,a16,baseline-isa,2,"1267 1253 1274 1325 1264 1254 1257 1253","1505 1488 1528 1576 1500 1497 1490 1486","4"
trimmable,a16,stock,3,"1455 1404 1420 1406 1427 1447 1411 1423","1703 1636 1677 1655 1662 1687 1651 1671","-"
trimmable,a16,rcpc1,3,"1322 1267 1305 1254 1260 1277 1299 1266","1554 1501 1552 1495 1495 1512 1537 1500","-"
trimmable,a16,baseline-isa,3,"1275 1318 1272 1259 1264 1264 1257 1314","1511 1559 1548 1492 1502 1494 1491 1544","2 8"
trimmable,a16,stock,4,"1434 1435 1396 1421 1429 1455 1413 1442","1675 1742 1644 1674 1664 1703 1659 1675","2"
trimmable,a16,rcpc1,4,"1268 1311 1263 1281 1258 1280 1264 1279","1517 1546 1510 1521 1489 1509 1504 1523","-"
trimmable,a16,baseline-isa,4,"1332 1272 1321 1287 1281 1267 1283 1285","1565 1508 1558 1528 1526 1502 1510 1533","1 3"
trimmable,a16,stock,5,"1442 1431 1422 1405 1440 1431 1424 1422","1678 1665 1667 1640 1712 1671 1672 1673","-"
trimmable,a16,rcpc1,5,"1298 1304 1279 1265 1287 1269 1277 1263","1557 1540 1528 1492 1533 1507 1511 1490","-"
trimmable,a16,baseline-isa,5,"1316 1239 1306 1263 1277 1251 1281 1282","1550 1479 1553 1498 1507 1482 1513 1524","1"
trimmable,s23,stock,0,"559 556 545 543 567 569 559 525","696 664 650 648 675 664 669 625","-"
trimmable,s23,rcpc1,0,"544 544 570 552 541 633 546 568","648 655 680 660 641 744 645 660","6"
trimmable,s23,baseline-isa,0,"567 536 512 503 556 588 514 614","651 605 578 574 637 665 595 714","3 4 7 8"
trimmable,s23,stock,1,"525 530 523 512 515 612 538 512","597 607 614 585 586 681 620 586","6"
trimmable,s23,rcpc1,1,"507 503 510 521 496 500 536 552","577 572 583 593 565 572 609 620","-"
trimmable,s23,baseline-isa,1,"502 516 502 560 563 534 525 540","571 584 567 664 662 631 614 645","1 2 3"
trimmable,s23,stock,2,"518 644 558 539 534 539 537 534","587 738 654 632 632 626 632 643","2"
trimmable,s23,rcpc1,2,"505 552 509 519 514 549 554 521","578 622 580 598 586 616 630 596","-"
trimmable,s23,baseline-isa,2,"563 545 551 541 597 531 534 540","668 645 655 631 682 639 636 638","-"
trimmable,s23,stock,3,"569 511 504 515 506 545 515 546","643 577 575 583 568 633 594 613","-"
trimmable,s23,rcpc1,3,"514 511 597 547 521 589 542 527","585 583 697 652 612 690 650 623","-"
trimmable,s23,baseline-isa,3,"531 528 530 542 543 574 539 548","642 637 623 635 647 675 640 640","-"
trimmable,s23,stock,4,"533 540 584 546 533 620 540 549","623 632 690 640 633 723 633 652","6"
trimmable,s23,rcpc1,4,"533 564 542 538 530 566 535 538","639 661 663 630 621 673 638 643","-"
trimmable,s23,baseline-isa,4,"504 502 557 564 543 706 580 562","574 574 624 659 644 814 676 651","1 2 6"
trimmable,s23,stock,5,"553 555 543 536 562 589 544 558","657 641 656 634 650 704 636 663","-"
trimmable,s23,rcpc1,5,"532 563 534 534 535 572 544 543","618 661 639 629 657 667 632 639","-"
trimmable,s23,baseline-isa,5,"542 541 547 540 541 586 545 544","639 636 645 642 633 686 646 634","-"
llvm-ir,a16,stock,0,"1450 1434 1449 1449 1448 1441 1449 1433","1693 1669 1692 1689 1689 1690 1689 1667","-"
llvm-ir,a16,baseline-isa,0,"1291 1353 1295 1271 1281 1298 1282 1278","1533 1584 1537 1515 1508 1526 1516 1521","-"
llvm-ir,a16,stock,1,"1428 1470 1425 1442 1440 1431 1436 1479","1670 1705 1678 1678 1692 1671 1678 1724","-"
llvm-ir,a16,baseline-isa,1,"1301 1343 1291 1296 1300 1296 1286 1293","1545 1584 1536 1534 1538 1532 1522 1542","-"
llvm-ir,a16,stock,2,"1461 1462 1460 1509 1462 1475 1477 1472","1700 1701 1715 1753 1705 1710 1725 1751","-"
llvm-ir,a16,baseline-isa,2,"1362 1298 1337 1287 1314 1289 1302 1296","1597 1535 1580 1524 1547 1524 1560 1530","-"
llvm-ir,a16,stock,3,"1391 1374 1390 1403 1384 1371 1394 1385","1596 1586 1613 1614 1592 1584 1610 1596","-"
llvm-ir,a16,baseline-isa,3,"1278 1243 1242 1246 1252 1251 1251 1245","1483 1444 1444 1448 1450 1450 1457 1447","-"
llvm-ir,a16,stock,4,"1403 1418 1384 1421 1379 1464 1395 1454","1617 1635 1598 1631 1587 1678 1605 1667","-"
llvm-ir,a16,baseline-isa,4,"1230 1217 1239 1230 1233 1234 1237 1243","1424 1418 1437 1426 1431 1434 1439 1435","-"
llvm-ir,a16,stock,5,"1387 1383 1402 1412 1382 1392 1380 1390","1593 1595 1615 1623 1588 1627 1582 1605","-"
llvm-ir,a16,baseline-isa,5,"1270 1229 1299 1235 1262 1232 1278 1225","1476 1437 1506 1433 1470 1435 1476 1426","-"
llvm-ir,s23,stock,0,"561 586 562 555 554 597 571 568","668 695 665 641 640 716 678 658","-"
llvm-ir,s23,baseline-isa,0,"517 524 523 535 517 587 527 532","587 597 593 608 598 667 600 607","-"
llvm-ir,s23,stock,1,"523 525 533 519 568 512 515 531","604 600 612 589 637 594 583 608","-"
llvm-ir,s23,baseline-isa,1,"517 527 517 612 590 681 602 546","592 597 585 693 683 784 717 635","6"
llvm-ir,s23,stock,2,"545 543 557 553 548 538 553 562","646 644 661 656 648 627 646 656","-"
llvm-ir,s23,baseline-isa,2,"523 504 546 531 513 563 523 558","594 577 620 605 584 636 598 653","-"
llvm-ir,s23,stock,3,"544 548 555 563 550 531 638 571","648 660 652 678 642 633 735 673","7"
llvm-ir,s23,baseline-isa,3,"520 521 519 556 562 575 504 516","592 597 589 641 638 645 580 596","-"
llvm-ir,s23,stock,4,"521 531 529 568 535 574 515 594","592 598 605 646 611 667 590 692","-"
llvm-ir,s23,baseline-isa,4,"510 544 532 523 532 536 535 527","579 619 611 596 602 608 627 597","-"
llvm-ir,s23,stock,5,"573 586 541 554 559 569 559 594","680 691 641 639 646 659 649 684","-"
llvm-ir,s23,baseline-isa,5,"586 575 558 554 598 547 594 556","679 666 649 641 700 650 699 654","-"

Custom-profile SHA-256s:

  • Trimmable: 5a4c8b28d00e3bc35df21172aa8452f4e4d93e03d6224ef087c8bc26d7c3e584
  • LLVM-IR: 1ee28cfdf8d327b9db792669d5b38018d412e35b7896cf6c5ed70147d3363f97

Signed timing APK sizes: 24,733,775 B for each trimmable ISA policy; 24,098,895 B for each LLVM-IR ISA policy. Native composites were frozen and shared across phones. Raw captures, complete method/event records, compiler logs/maps and hash manifests are retained in the investigation artifacts; the block data above are embedded here so timing summaries do not depend on private filesystem links.

Validation

  • Used the existing isolated host harness to check 17 saved argument scenarios against the edited target after renaming and inverting the opt-out. Blank/false applies the ISA override; true skips it. This covers argument preservation, explicit overrides, composite exclusions and supported-RID selection, but is not a full application build or a new repository test. The dedicated ReadyToRunInstructionSet test was removed; the earlier invalid x86 case had exposed that property-only checks cannot establish runtime support.
  • Evaluated the target after actual pinned Android SDK imports: AndroidApplication=true, PublishReadyToRun=true, ARM64 selects the complete explicit list and x64 retains -optimistic, preserving --mapcsv. These are SDK property/hook checks, not new full application builds.
  • Replayed the exact ARM64 compiler arguments with the explicit list and fixed 188 DLLs/eight profiles; decoded the emitted positive requirements and verified no positive or negative RCPC2 guard. The resulting code-matched APKs were run on A16 and S23 as described above.
  • Earlier full integration builds covered x64 composite default/opt-out, ARM64 composite/non-composite and ARM32 under the previous conservative policy. They are historical coverage, not additional measurements of this explicit-list change.
  • The full repository NUnit host suite was not run locally because this worktree lacks a bootstrapped local SDK. The existing BasicApplicationPublishReadyToRun test still checks the full ARM64 argument in x64/ARM64 composite/non-composite coverage for CI.

All device windows were cleaned up and released; only dedicated study packages and owned diagnostic routes/processes were used. Raw traces, complete method/event records, compiler maps, packaging audits and hash manifests are retained. Public runtime source links identify the inspected base ab19415702aa8139d5369e47c73edb47343c34ad; actual emitted bytes/device results are primary evidence, not an assumption that every file in the packaged VMR is identical to that base.

No issue is linked; this change follows the startup profiling investigation.

Default ARM64 Android ReadyToRun to conservative instruction selection.  Crossgen2's optimistic RCPC2 code is rejected on CPUs without RCPC2, forcing otherwise precompiled startup methods through Tier 0 JIT.

Resolve the default after package imports and before R2R preparation.  Preserve explicit normal/composite instruction-set arguments and leave other architectures, Mono, NativeAOT, and runtime tiering unchanged.

Add policy coverage and document the override behavior.  Fixed-profile untraced sample startup improved about 10% on Samsung A16; S23 results do not establish sub-percent non-regression.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 8, 2026 16:45

Copilot AI 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

🟢 Approval recommended

The behavioral change is narrowly scoped, documented, and accompanied by targeted MSBuild/test coverage to validate override and composite/non-composite scenarios.

Review tier: Lite
Findings: 1 Low severity

New issues introduced by this change (1)
Severity Finding
Low severity src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​BuildTest2.cs — 💡 suggestion Resource managementCreateApkBuilder() returns a disposable ProjectBuilder;…
What changed in this PR

This PR adjusts the .NET for Android CoreCLR ReadyToRun (R2R) defaults for android-arm64 so crossgen2 uses a conservative instruction-set policy (--instruction-set:-optimistic) unless the build already specifies an explicit --instruction-set option. This aims to reduce R2R entry rejection (and resulting Tier 0 JIT fallback) on devices that lack some optional ARM64 instruction features.

Changes:

  • Add an MSBuild target in the CoreCLR targets to append --instruction-set:-optimistic to $(PublishReadyToRunCrossgen2ExtraArgs) for Android apps targeting android-arm64 when no explicit instruction-set policy is provided.
  • Extend test coverage to validate argument-policy behavior across composite/non-composite and explicit override combinations, plus assert the flag appears only for arm64 in the basic publish test.
  • Document the new default and override behavior in the build properties documentation.
File Description
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​BuildTest2.cs Adds assertions and parameterized tests validating the new ARM64 R2R instruction-set default/override behavior.
src/​Xamarin.Android.Build.Tasks/​Microsoft.Android.Sdk/​targets/​Microsoft.Android.Sdk.CoreCLR.targets Introduces _AndroidSetReadyToRunInstructionSet to set the default crossgen2 instruction-set policy for CoreCLR android-arm64 R2R.
Documentation/​docs-mobile/​building-apps/​build-properties.md Documents the ARM64 --instruction-set:-optimistic default and how explicit instruction-set args override it.

Comment thread src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs Outdated
@simonrozsival

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #12722

@github-actions github-actions Bot 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.

⚠️ Needs Changes — 1 correctness issue.

The ARM64 policy is otherwise scoped appropriately and the default/non-default paths are documented and tested, but a composite-only override currently leaves excluded assemblies on crossgen2's optimistic default.

CI build #1587516 is still in progress and already reports failures in Package Tests macOS > Tests > APKs 2 and MAUI Tests MAUI Integration. Azure log inspection was unavailable because the runner could not initialize its Azure CLI configuration, so I could not determine whether those failures are related to this change.

Generated by Android PR Reviewer for #12722 · gpt56 · 289.4 AIC · ⌖ 5.8 AIC · ⊞ 26.5K
Comment /review to run again

Preserve build-wide opt-out for composite-only ISA overrides: the SDK concatenates both argument lists, and crossgen2 rejects duplicate instruction-set options. Document how to apply a policy to composite exclusions via the common arguments and include exclusions in the policy regression cases.

Dispose the ReadyToRun application test builder as requested in review.

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

Copy link
Copy Markdown
Member Author

CI follow-up for build 1587516:

  • MAUI failed during NuGet restore, before R2R compilation. Its floating Logging.Debug dependency selected 11.0.0-rc.2.26458.103, while the Logging version list still stopped at 26457.116. The exact Debug → Logging → DependencyInjection dependency chain now restores successfully from the existing dotnet11 feed in an isolated, fresh-cache local restore. Metadata-cache/publication skew is the likely cause; no package pins or feed changes were needed.
  • HttpContentStreamIsRewoundAfterCancellation failed with Broken pipe; the same symptom predates this PR and is recorded in CI flakiness inventory: Aug 11-Sep 8, 2026 #12704. No networking assertions were weakened or tests disabled.

Pushed 472af43 with the review follow-up. Fresh validation 1587685 is running; this is not yet a green-CI claim.

Remove the ARM64-only guard and simplify explicit ISA override detection. Add the private _AndroidUseConservativeReadyToRunInstructionSet=false escape hatch without disabling R2R or changing existing compiler arguments.

Extend argument-policy coverage to x64 and ARM, composite/non-composite overrides and the private escape hatch. Keep measured startup claims scoped to the ARM64 phones.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival changed the title [CoreCLR] Avoid optimistic ARM64 instructions in ReadyToRun [CoreCLR] Avoid optimistic instructions in ReadyToRun Sep 8, 2026
@vitek-karas

Copy link
Copy Markdown
Member

Can you try to get a signoff from somebody on the JIT team (or close by)? Just want to make sure there's no unintended consequence of the change. Otherwise it looks great :-)

@simonrozsival

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #12722

@github-actions github-actions Bot 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.

⚠️ Needs Changes

Found 1 warning: a composite-only instruction-set argument currently disables the conservative default even when PublishReadyToRunComposite=false, although the .NET SDK does not consume composite arguments in that mode. This leaves the effective non-composite crossgen2 invocation optimistic.

The late target placement, preservation of normal arguments, regression coverage, resource disposal, and performance evidence are otherwise well handled. CI is still running: 25 checks have passed, 18 are in progress, and 1 is queued, with no failures reported so far.

Generated by Android PR Reviewer for #12722 · gpt56 · 228.2 AIC · ⌖ 22.2 AIC · ⊞ 25.7K
Comment /review to run again

@davidnguyen-tech davidnguyen-tech 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.

LGTM, we should get extended startup measurements.

Comment thread Documentation/docs-mobile/building-apps/build-properties.md Outdated
@simonrozsival simonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label Sep 9, 2026
Keep the tested ARM64 instruction set except RCPC2, without encoding an RCPC2-absence assertion. Preserve other architectures, explicit overrides and the private opt-out, and document the higher baseline requirements.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival changed the title [CoreCLR] Avoid optimistic instructions in ReadyToRun [CoreCLR] Use explicit ARM64 ReadyToRun ISA defaults Sep 9, 2026
@simonrozsival simonrozsival changed the title [CoreCLR] Use explicit ARM64 ReadyToRun ISA defaults [CoreCLR] Exclude RCPC2 from ARM64 ReadyToRun defaults Sep 9, 2026
jonathanpeppers pushed a commit that referenced this pull request Sep 9, 2026
Remove the primary-constructor parameter list from the integration tests' `MockBuildEngine`, fixing CS0111 on `main`.

#12724 (`ecef743e91614754e6e1ab0ed7ed1834ecdedf5c`) added that signature after #12736 had introduced an explicit constructor with the same optional warning-list parameter. The merged combination left both constructors in place. This one-line repair keeps the explicit constructor and warning collection unchanged, including the existing no-XA4326 assertions; it does not change the production generator.

The failure appears in the Linux and macOS builds of [Azure DevOps build 1588988](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1588988):

    MockBuildEngine.cs(15,9): error CS0111: Type 'MockBuildEngine' already defines a member called 'MockBuildEngine' with the same parameter types.

Based directly on current `main` (`d907cd735615d5d17616cbba9e1a4aeef99ab61d`), independently of #12722. Rechecked live main and open PRs before editing and immediately before opening this PR; no equivalent fix was found.

### Validation

- Compiled the actual `MockBuildEngine.cs` using an isolated .NET SDK `11.0.100-rc.1.26431.118`, its net11.0 reference assemblies, and its real `Microsoft.Build.Framework.dll`. The same command reproduced CS0111 before the fix and succeeded without diagnostics after it.
- An artifacts-only host driver compiled together with the fixed source passed default construction, explicit `null`, and supplied-list cases. Logging with default/null was harmless; successive warnings appended to the supplied list with object identity preserved.
- The fixed file is byte-for-byte identical to its last working main version, immediately before #12724.

<details>
<summary>Direct compiler command</summary>

`SDK` points to the private SDK copy and `OUT` to the session artifact directory; run from the repository root:

```bash
refs=()
for ref in "$SDK"/packs/Microsoft.NETCore.App.Ref/*/ref/net11.0/*.dll; do
    refs+=("-r:$ref")
done
"$SDK/dotnet" exec \
    "$SDK/sdk/11.0.100-rc.1.26431.118/Roslyn/bincore/csc.dll" \
    -nologo -target:library -nullable:enable -langversion:latest \
    "-out:$OUT/MockBuildEngine.dll" "${refs[@]}" \
    "-r:$SDK/sdk/11.0.100-rc.1.26431.118/Microsoft.Build.Framework.dll" \
    tests/Microsoft.Android.Sdk.TrimmableTypeMap.IntegrationTests/MockBuildEngine.cs
```

</details>

**Integration-test limit:** Attempted `dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.IntegrationTests/Microsoft.Android.Sdk.TrimmableTypeMap.IntegrationTests.csproj --no-restore -p:BuildProjectReferences=false --filter 'FullyQualifiedName~JniAssemblyRewriterTests|FullyQualifiedName~GeneratedTypeMapRewriterTests' -v minimal`, but no tests executed in the unprepared worktree. A subsequent `dotnet build` of the same project with `--no-restore -p:BuildProjectReferences=false -v minimal` confirmed NETSDK1004 (missing `project.assets.json`). The local Android build outputs are also absent. No full bootstrap or device tests were run, and older installed generator binaries were not substituted for current-main integration validation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival removed the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label Sep 9, 2026
simonrozsival and others added 3 commits September 9, 2026 22:37
Remove the android-x86 and android-arm cases from the CoreCLR argument-policy test. Keep the matrix on android-arm64 and android-x64, and clarify the documented x64 default.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use the checked-in NDK support metadata: CoreCLR remains enabled for ARM, ARM64 and x64; only x86 is disabled. Restore the ARM case and retain the existing architecture-neutral documentation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use _AndroidUseOptimisticReadyToRunInstructionSet=true to skip the default ISA override. Remove the dedicated ReadyToRunInstructionSet test while retaining the existing ReadyToRun integration assertion.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
[`$(MauiEnableFullReadyToRun)`](#mauienablefullreadytorun) to opt in to
full ReadyToRun.

For ARM64, .NET for Android defaults crossgen2 to:

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 these defaults in crossgen2 for all other OSes. If the defaults in crossgen2 do not work well for Android, we should fix it in crossgen2. I do not think we want to have these low-level defaults spread over multiple places.

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 may want to drop rcpc2 from the optimistic set for all OSes. It is relatively minor optimization that makes a lot of R2R methods rejected on older hardware.

@jkotas

jkotas commented Sep 9, 2026

Copy link
Copy Markdown
Member

Can you check the disassembly of the methods that declare dependency on rcpc2 and verify that they actually depend on rcpc2 instruction? I suspect that there may be a bug in the JIT that declares dependency on rcpc2 even when the code does not actually contain rcpc2 instruction.

@simonrozsival

simonrozsival commented Sep 10, 2026

Copy link
Copy Markdown
Member Author

@jkotas Yes, you were right. In the stock MAUI sample, 117 of the 564 RCPC2-guarded method bodies contain no RCPC2 instructions. The other 447 really do use them.

The problem is in Lowering::TryCreateAddrMode: it calls compOpportunisticallyDependsOn(Rcpc2) before checking whether the addressing mode will work. That call records the dependency, and a later rejection of the addressing mode doesn't undo it.

For example, JsonTypeInfo.get_IsConfigured() declares RCPC2 but accesses its field with:

add     x0, x0, #0x153
ldaprb  w0, [x0]

That's RCPC1, not RCPC2. The offset is too large for the RCPC2 form.

I also reproduced this with a small volatile-field example using the same compiler. Stock and rcpc,-optimistic produce identical machine-code bytes for the large-offset access, but only stock adds an RCPC2 guard. A reference write through the GC write-barrier helper shows the same issue.

So there are unnecessary dependencies to fix here, though the genuine RCPC2 users will still need fallback on older devices. I haven't measured the startup impact of a JIT fix yet.

Posted from the Copilot App on Simon's behalf.

@simonrozsival simonrozsival added ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). and removed ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). labels Sep 10, 2026
@jkotas

jkotas commented Sep 10, 2026

Copy link
Copy Markdown
Member

117 of the 564 RCPC2-guarded method bodies contain no RCPC2 instructions

Can you open an issue on this in dotnet/runtime?

ready-to-review

I think this PR can be closed. The fix is going to come from dotnet/runtime.

@jonathanpeppers jonathanpeppers added the do-not-merge PR should not be merged. label Sep 10, 2026
BrzVlad added a commit to dotnet/runtime that referenced this pull request Sep 11, 2026
Testing on android (dotnet/android#12722) showed
that older devices don't have rcpc2 instruction set available which
leads to significant r2r code rejection. This is a simpler, cross
platform fix for the issue, compared to exposing knobs to tweak the
optimistic set (#133514) and have
dotnet/android use them.

---------

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge PR should not be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants