From 2dc4b3c0c6e04dbd348d566fba76c4e81ab6088d Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 16 Jul 2026 20:00:35 +0200 Subject: [PATCH 1/5] Add RuntimeHelpers.IsRuntimeAsync intrinsic The interpreter and JIT fold this intrinsic to a constant based on whether the calling function is runtime async. --- src/coreclr/interpreter/compiler.cpp | 14 ++++++++++++++ src/coreclr/interpreter/intrinsics.cpp | 2 ++ src/coreclr/jit/fgbasic.cpp | 6 ++++++ src/coreclr/jit/importercalls.cpp | 12 ++++++++++++ src/coreclr/jit/namedintrinsiclist.h | 1 + .../Runtime/CompilerServices/RuntimeHelpers.cs | 5 +++++ 6 files changed, 40 insertions(+) diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index b8dd968dfa0f82..dcc0cbf7e0a6b7 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -4032,6 +4032,19 @@ bool InterpCompiler::EmitNamedIntrinsicCall(NamedIntrinsic ni, bool nonVirtualCa return true; } + case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync: + { + int32_t result = m_methodInfo->args.isAsyncCall() ? 1 : 0; + + AddIns(INTOP_LDC_I4); + m_pLastNewIns->data[0] = result; + + PushInterpType(InterpTypeI4, nullptr); + m_pLastNewIns->SetDVar(m_pStackPointer[-1].var); + + return true; + } + case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences: { CORINFO_CLASS_HANDLE clsHnd = sig.sigInst.methInst[0]; @@ -5140,6 +5153,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re ni == NI_System_StubHelpers_NextCallReturnAddress || ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallGenericContext || ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallAsyncContinuation || + ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync || ni == NI_System_Runtime_CompilerServices_AsyncHelpers_AsyncCallContinuation || ni == NI_System_Runtime_CompilerServices_AsyncHelpers_AsyncSuspend || ni == NI_System_Runtime_CompilerServices_AsyncHelpers_TailAwait); diff --git a/src/coreclr/interpreter/intrinsics.cpp b/src/coreclr/interpreter/intrinsics.cpp index 38e31e24a1cf08..3b8af0065d67ed 100644 --- a/src/coreclr/interpreter/intrinsics.cpp +++ b/src/coreclr/interpreter/intrinsics.cpp @@ -108,6 +108,8 @@ NamedIntrinsic GetNamedIntrinsic(COMP_HANDLE compHnd, CORINFO_METHOD_HANDLE comp { if (!strcmp(methodName, "IsReferenceOrContainsReferences")) return NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences; + else if (!strcmp(methodName, "IsRuntimeAsync")) + return NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync; else if (!strcmp(methodName, "GetMethodTable")) return NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable; else if (!strcmp(methodName, "SetNextCallGenericContext")) diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 71d25be333fcfd..875bd4d41c1c2f 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -1170,6 +1170,12 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed foldableIntrinsic = true; break; + case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync: + // RuntimeHelpers.IsRuntimeAsync is always folded into a const + pushedStack.PushConstant(); + foldableIntrinsic = true; + break; + // These are foldable if the first argument is a constant case NI_PRIMITIVE_LeadingZeroCount: case NI_PRIMITIVE_Log2: diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index b1322a05ef7c76..2cbd43c84afcde 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -3518,6 +3518,7 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, { // This one is just `return true/false` case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant: + case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync: case NI_System_Runtime_CompilerServices_RuntimeHelpers_WriteBarrier: @@ -3792,6 +3793,13 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, break; } + case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync: + { + retNode = compIsAsync() ? gtNewTrue() : gtNewFalse(); + JITDUMP("\nExpanding RuntimeHelpers.IsRuntimeAsync to %s early\n", compIsAsync() ? "true" : "false"); + break; + } + case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences: { assert(sig->sigInst.methInstCount == 1); @@ -11429,6 +11437,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant; } + else if (strcmp(methodName, "IsRuntimeAsync") == 0) + { + result = NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync; + } else if (strcmp(methodName, "WriteBarrier") == 0) { result = NI_System_Runtime_CompilerServices_RuntimeHelpers_WriteBarrier; diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index edfb638bd1e5fb..a78298820e8dbe 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -124,6 +124,7 @@ enum NamedIntrinsic : unsigned short NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan, NI_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray, NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant, + NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync, NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences, NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable, NI_System_Runtime_CompilerServices_RuntimeHelpers_WriteBarrier, diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index f9f228e6aea688..038452a1c8faf3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -179,6 +179,11 @@ public static ReadOnlySpan CreateSpan(RuntimeFieldHandle fldHandle) internal static bool IsKnownConstant(T t) where T : struct => false; #pragma warning restore IDE0060 + // Returns true if the method being compiled is a runtime-async method. + // This is folded to a compile-time constant by the JIT and the interpreter. + [Intrinsic] + internal static bool IsRuntimeAsync() => false; + /// true if the given type is a reference type or a value type that contains references or by-refs; otherwise, false. [Intrinsic] public static bool IsReferenceOrContainsReferences() where T : allows ref struct => IsReferenceOrContainsReferences(); From f27814f6224ac83ee89b32262428cfa20ed52099 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 16 Jul 2026 20:27:49 +0200 Subject: [PATCH 2/5] Test with StreamReader --- .../src/System/IO/StreamReader.cs | 86 +++++++++++++++++-- 1 file changed, 79 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 319d31ddaeed55..7a2c31f269f63e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -1,10 +1,11 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -72,13 +73,23 @@ public class StreamReader : TextReader // We don't guarantee thread safety on StreamReader, but we should at // least prevent users from trying to read anything while an Async // read from the same thread is in progress. + // + // When a read is issued from a runtime-async method we call directly into a + // runtime-async read method that tracks progress via the _asyncIOInProgress flag + // (set/cleared by ThrowOnReadsScope). Otherwise we track the returned Task in + // _asyncReadTask. RuntimeHelpers.IsRuntimeAsync() is folded to a constant by the + // JIT/interpreter, so only one of the two approaches is emitted per compilation. + // CheckAsyncTaskInProgress checks both so either kind of in-progress read is caught. private Task _asyncReadTask = Task.CompletedTask; + private bool _asyncIOInProgress; private void CheckAsyncTaskInProgress() { - // We are not locking the access to _asyncReadTask because this is not meant to guarantee thread safety. + // We are not locking this access because this is not meant to guarantee thread safety. // We are simply trying to deter calling any Read APIs while an async Read from the same thread is in progress. - if (!_asyncReadTask.IsCompleted) + // Depending on whether the caller was compiled as runtime-async, an in-progress read is tracked either + // by _asyncReadTask (task approach) or by _asyncIOInProgress (bool approach), so we check both here. + if (!_asyncReadTask.IsCompleted || _asyncIOInProgress) { ThrowAsyncIOInProgress(); } @@ -88,6 +99,24 @@ private void CheckAsyncTaskInProgress() private static void ThrowAsyncIOInProgress() => throw new InvalidOperationException(SR.InvalidOperation_AsyncIOInProgress); + private ThrowOnReadsScope GuardAgainstOtherReads() + { + return new ThrowOnReadsScope(this); + } + + private readonly struct ThrowOnReadsScope : IDisposable + { + private readonly StreamReader _reader; + + public ThrowOnReadsScope(StreamReader reader) + { + reader._asyncIOInProgress = true; + _reader = reader; + } + + public void Dispose() => _reader._asyncIOInProgress = false; + } + // StreamReader by default will ignore illegal UTF8 characters. We don't want to // throw here because we want to be able to read ill-formed data without choking. // The high level goal is to be tolerant of encoding errors when we read and very strict @@ -898,14 +927,20 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) ThrowIfDisposed(); CheckAsyncTaskInProgress(); + if (RuntimeHelpers.IsRuntimeAsync()) + { + return new ValueTask(ReadLineAsyncInternal(cancellationToken)); + } + Task task = ReadLineAsyncInternal(cancellationToken); _asyncReadTask = task; - return new ValueTask(task); } private async Task ReadLineAsyncInternal(CancellationToken cancellationToken) { + using ThrowOnReadsScope _ = GuardAgainstOtherReads(); + if (_charPos == _charLen && (await ReadBufferAsync(cancellationToken).ConfigureAwait(false)) == 0) { return null; @@ -1026,14 +1061,20 @@ public override Task ReadToEndAsync(CancellationToken cancellationToken) ThrowIfDisposed(); CheckAsyncTaskInProgress(); + if (RuntimeHelpers.IsRuntimeAsync()) + { + return ReadToEndAsyncInternal(cancellationToken); + } + Task task = ReadToEndAsyncInternal(cancellationToken); _asyncReadTask = task; - return task; } private async Task ReadToEndAsyncInternal(CancellationToken cancellationToken) { + using ThrowOnReadsScope _ = GuardAgainstOtherReads(); + // Call ReadBuffer, then pull data out of charBuffer. StringBuilder sb = new StringBuilder(_charLen - _charPos); do @@ -1070,10 +1111,20 @@ public override Task ReadAsync(char[] buffer, int index, int count) ThrowIfDisposed(); CheckAsyncTaskInProgress(); + if (RuntimeHelpers.IsRuntimeAsync()) + { + return ReadAsyncInternalWithGuard(new Memory(buffer, index, count), CancellationToken.None); + } + Task task = ReadAsyncInternal(new Memory(buffer, index, count), CancellationToken.None).AsTask(); _asyncReadTask = task; - return task; + + async Task ReadAsyncInternalWithGuard(Memory buffer, CancellationToken cancellationToken) + { + using ThrowOnReadsScope _ = GuardAgainstOtherReads(); + return await ReadAsyncInternal(buffer, cancellationToken).ConfigureAwait(false); + } } public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) @@ -1281,10 +1332,20 @@ public override Task ReadBlockAsync(char[] buffer, int index, int count) ThrowIfDisposed(); CheckAsyncTaskInProgress(); + if (RuntimeHelpers.IsRuntimeAsync()) + { + return ReadBlockAsyncWithGuard(buffer, index, count); + } + Task task = base.ReadBlockAsync(buffer, index, count); _asyncReadTask = task; - return task; + + async Task ReadBlockAsyncWithGuard(char[] buffer, int index, int count) + { + using ThrowOnReadsScope _ = GuardAgainstOtherReads(); + return await base.ReadBlockAsync(buffer, index, count).ConfigureAwait(false); + } } public override ValueTask ReadBlockAsync(Memory buffer, CancellationToken cancellationToken = default) @@ -1304,6 +1365,11 @@ public override ValueTask ReadBlockAsync(Memory buffer, CancellationT return ValueTask.FromCanceled(cancellationToken); } + if (RuntimeHelpers.IsRuntimeAsync()) + { + return ReadBlockAsyncInternalWithGuard(buffer, cancellationToken); + } + ValueTask vt = ReadBlockAsyncInternal(buffer, cancellationToken); if (vt.IsCompletedSuccessfully) { @@ -1313,6 +1379,12 @@ public override ValueTask ReadBlockAsync(Memory buffer, CancellationT Task t = vt.AsTask(); _asyncReadTask = t; return new ValueTask(t); + + async ValueTask ReadBlockAsyncInternalWithGuard(Memory buffer, CancellationToken token) + { + using ThrowOnReadsScope _ = GuardAgainstOtherReads(); + return await ReadBlockAsyncInternal(buffer, token).ConfigureAwait(false); + } } private async ValueTask ReadBufferAsync(CancellationToken cancellationToken) From 20dce7e120c4aa9c1bdece2e90b7b6726583d586 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 16 Jul 2026 23:34:14 +0200 Subject: [PATCH 3/5] Revert "Test with StreamReader" This reverts commit f27814f6224ac83ee89b32262428cfa20ed52099. --- .../src/System/IO/StreamReader.cs | 86 ++----------------- 1 file changed, 7 insertions(+), 79 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 7a2c31f269f63e..319d31ddaeed55 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -1,11 +1,10 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -73,23 +72,13 @@ public class StreamReader : TextReader // We don't guarantee thread safety on StreamReader, but we should at // least prevent users from trying to read anything while an Async // read from the same thread is in progress. - // - // When a read is issued from a runtime-async method we call directly into a - // runtime-async read method that tracks progress via the _asyncIOInProgress flag - // (set/cleared by ThrowOnReadsScope). Otherwise we track the returned Task in - // _asyncReadTask. RuntimeHelpers.IsRuntimeAsync() is folded to a constant by the - // JIT/interpreter, so only one of the two approaches is emitted per compilation. - // CheckAsyncTaskInProgress checks both so either kind of in-progress read is caught. private Task _asyncReadTask = Task.CompletedTask; - private bool _asyncIOInProgress; private void CheckAsyncTaskInProgress() { - // We are not locking this access because this is not meant to guarantee thread safety. + // We are not locking the access to _asyncReadTask because this is not meant to guarantee thread safety. // We are simply trying to deter calling any Read APIs while an async Read from the same thread is in progress. - // Depending on whether the caller was compiled as runtime-async, an in-progress read is tracked either - // by _asyncReadTask (task approach) or by _asyncIOInProgress (bool approach), so we check both here. - if (!_asyncReadTask.IsCompleted || _asyncIOInProgress) + if (!_asyncReadTask.IsCompleted) { ThrowAsyncIOInProgress(); } @@ -99,24 +88,6 @@ private void CheckAsyncTaskInProgress() private static void ThrowAsyncIOInProgress() => throw new InvalidOperationException(SR.InvalidOperation_AsyncIOInProgress); - private ThrowOnReadsScope GuardAgainstOtherReads() - { - return new ThrowOnReadsScope(this); - } - - private readonly struct ThrowOnReadsScope : IDisposable - { - private readonly StreamReader _reader; - - public ThrowOnReadsScope(StreamReader reader) - { - reader._asyncIOInProgress = true; - _reader = reader; - } - - public void Dispose() => _reader._asyncIOInProgress = false; - } - // StreamReader by default will ignore illegal UTF8 characters. We don't want to // throw here because we want to be able to read ill-formed data without choking. // The high level goal is to be tolerant of encoding errors when we read and very strict @@ -927,20 +898,14 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) ThrowIfDisposed(); CheckAsyncTaskInProgress(); - if (RuntimeHelpers.IsRuntimeAsync()) - { - return new ValueTask(ReadLineAsyncInternal(cancellationToken)); - } - Task task = ReadLineAsyncInternal(cancellationToken); _asyncReadTask = task; + return new ValueTask(task); } private async Task ReadLineAsyncInternal(CancellationToken cancellationToken) { - using ThrowOnReadsScope _ = GuardAgainstOtherReads(); - if (_charPos == _charLen && (await ReadBufferAsync(cancellationToken).ConfigureAwait(false)) == 0) { return null; @@ -1061,20 +1026,14 @@ public override Task ReadToEndAsync(CancellationToken cancellationToken) ThrowIfDisposed(); CheckAsyncTaskInProgress(); - if (RuntimeHelpers.IsRuntimeAsync()) - { - return ReadToEndAsyncInternal(cancellationToken); - } - Task task = ReadToEndAsyncInternal(cancellationToken); _asyncReadTask = task; + return task; } private async Task ReadToEndAsyncInternal(CancellationToken cancellationToken) { - using ThrowOnReadsScope _ = GuardAgainstOtherReads(); - // Call ReadBuffer, then pull data out of charBuffer. StringBuilder sb = new StringBuilder(_charLen - _charPos); do @@ -1111,20 +1070,10 @@ public override Task ReadAsync(char[] buffer, int index, int count) ThrowIfDisposed(); CheckAsyncTaskInProgress(); - if (RuntimeHelpers.IsRuntimeAsync()) - { - return ReadAsyncInternalWithGuard(new Memory(buffer, index, count), CancellationToken.None); - } - Task task = ReadAsyncInternal(new Memory(buffer, index, count), CancellationToken.None).AsTask(); _asyncReadTask = task; - return task; - async Task ReadAsyncInternalWithGuard(Memory buffer, CancellationToken cancellationToken) - { - using ThrowOnReadsScope _ = GuardAgainstOtherReads(); - return await ReadAsyncInternal(buffer, cancellationToken).ConfigureAwait(false); - } + return task; } public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) @@ -1332,20 +1281,10 @@ public override Task ReadBlockAsync(char[] buffer, int index, int count) ThrowIfDisposed(); CheckAsyncTaskInProgress(); - if (RuntimeHelpers.IsRuntimeAsync()) - { - return ReadBlockAsyncWithGuard(buffer, index, count); - } - Task task = base.ReadBlockAsync(buffer, index, count); _asyncReadTask = task; - return task; - async Task ReadBlockAsyncWithGuard(char[] buffer, int index, int count) - { - using ThrowOnReadsScope _ = GuardAgainstOtherReads(); - return await base.ReadBlockAsync(buffer, index, count).ConfigureAwait(false); - } + return task; } public override ValueTask ReadBlockAsync(Memory buffer, CancellationToken cancellationToken = default) @@ -1365,11 +1304,6 @@ public override ValueTask ReadBlockAsync(Memory buffer, CancellationT return ValueTask.FromCanceled(cancellationToken); } - if (RuntimeHelpers.IsRuntimeAsync()) - { - return ReadBlockAsyncInternalWithGuard(buffer, cancellationToken); - } - ValueTask vt = ReadBlockAsyncInternal(buffer, cancellationToken); if (vt.IsCompletedSuccessfully) { @@ -1379,12 +1313,6 @@ public override ValueTask ReadBlockAsync(Memory buffer, CancellationT Task t = vt.AsTask(); _asyncReadTask = t; return new ValueTask(t); - - async ValueTask ReadBlockAsyncInternalWithGuard(Memory buffer, CancellationToken token) - { - using ThrowOnReadsScope _ = GuardAgainstOtherReads(); - return await ReadBlockAsyncInternal(buffer, token).ConfigureAwait(false); - } } private async ValueTask ReadBufferAsync(CancellationToken cancellationToken) From 27d1577bb7a925ab6213acc77d4c9c5212253c5d Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 17 Jul 2026 00:03:08 +0200 Subject: [PATCH 4/5] Expand IsRuntimeAsync in debug codegen too --- src/coreclr/jit/importercalls.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 2cbd43c84afcde..98490b0b3a0744 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -3508,6 +3508,12 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, return gtNewNothingNode(); } + if (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync) + { + JITDUMP("\nExpanding RuntimeHelpers.IsRuntimeAsync to %s early\n", compIsAsync() ? "true" : "false"); + return compIsAsync() ? gtNewTrue() : gtNewFalse(); + } + bool betterToExpand = false; // Allow some lightweight intrinsics in Tier0 which can improve throughput @@ -3793,13 +3799,6 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, break; } - case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync: - { - retNode = compIsAsync() ? gtNewTrue() : gtNewFalse(); - JITDUMP("\nExpanding RuntimeHelpers.IsRuntimeAsync to %s early\n", compIsAsync() ? "true" : "false"); - break; - } - case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences: { assert(sig->sigInst.methInstCount == 1); From aae8f4d367f47515277da647c0f6c4f747291bd3 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 17 Jul 2026 00:16:00 +0200 Subject: [PATCH 5/5] Dead code --- src/coreclr/jit/importercalls.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 98490b0b3a0744..721d65b68f3aa3 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -3524,7 +3524,6 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, { // This one is just `return true/false` case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant: - case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync: case NI_System_Runtime_CompilerServices_RuntimeHelpers_WriteBarrier: