From 50234bba2cb946adf56a7faab669a7527cd53645 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 19 Aug 2026 17:51:14 -0700 Subject: [PATCH] Use acquire reads for cast cache entries CastCache.TryGet currently orders its payload reads before the final version check with Volatile.ReadBarrier. On ARM64 this emits dmb ishld in the hot cast-cache lookup path. Read the source and target/result fields with acquire semantics instead. This preserves the versioned-entry ordering while allowing ARM64 to use acquire loads rather than a global load barrier. x64 requires no additional instructions for these reads. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6bd43e03-841f-47ee-80f4-4da985b32a67 --- .../System/Runtime/CompilerServices/CastCache.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastCache.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastCache.cs index 41effcee98dbef..880fd3d3850661 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastCache.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastCache.cs @@ -157,29 +157,21 @@ internal static CastResult TryGet(int[] table, nuint source, nuint target) // we must read in this order: version -> [entry parts] -> version // if version is odd or changes, the entry is inconsistent and thus ignored uint version = Volatile.Read(ref pEntry._version); - nuint entrySource = pEntry._source; + nuint entrySource = Volatile.Read(ref pEntry._source); // mask the lower version bit to make it even. // This way we can check if version is odd or changing in just one compare. version &= unchecked((uint)~1); if (entrySource == source) { - // we do ordinary reads of the entry parts and - // Volatile.ReadBarrier() before reading the version - nuint entryTargetAndResult = pEntry._targetAndResult; + // Acquire reads of both entry parts ensure that the second version + // read happens after the entry has been read. + nuint entryTargetAndResult = Volatile.Read(ref pEntry._targetAndResult); // target never has its lower bit set. // a matching entryTargetAndResult would the have same bits, except for the lowest one, which is the result. entryTargetAndResult ^= target; if (entryTargetAndResult <= 1) { - // make sure the second read of 'version' happens after reading 'source' and 'targetAndResults' - // - // We can either: - // - use acquires for both _source and _targetAndResults or - // - issue a load barrier before reading _version - // benchmarks on available hardware (Jan 2020) show that use of a read barrier is cheaper. - Volatile.ReadBarrier(); - if (version != pEntry._version) { // oh, so close, the entry is in inconsistent state.