Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[RuntimeAsync] Implement IL_ThrowExact for x86/funclets #3100

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 26, 2025

Conversation

filipnavara
Copy link
Member

No description provided.

@jkotas jkotas added the area-async Runtime generate async state machines label Apr 24, 2025
@jkotas jkotas requested a review from VSadov April 24, 2025 08:57
@VSadov
Copy link
Member

VSadov commented Apr 24, 2025

If I try to define FEATURE_EH_FUNCLETS, build and run tests. Even simplest tests not involving EH fail with:

E:\A2\runtimelab\artifacts\tests\coreclr\windows.x86.Checked>async\object\object.cmd
BEGIN EXECUTION
 "E:\A2\runtimelab\artifacts\tests\coreclr\windows.x86.Checked\Tests\Core_Root\corerun.exe" -p "System.Reflection.Metadata.MetadataUpdater.IsSupported=false" -p "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization=true"  object.dll
Failed to load System.Private.CoreLib.dll (error code 0x80131522)
Path: E:\A2\runtimelab\artifacts\tests\coreclr\windows.x86.Checked\Tests\Core_Root\System.Private.CoreLib.dll
Error message: Could not load type 'System.Runtime.ExceptionServices.InternalCalls' from assembly 'System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'.
BEGIN: coreclr_initialize failed - Error: 0x80131522
Exe path: E:\A2\runtimelab\artifacts\tests\coreclr\windows.x86.Checked\Tests\Core_Root\corerun.exe

The way I "define" it is by adding unconditional

add_definitions(-DFEATURE_EH_FUNCLETS) in clrdefinitions.cmake

Am I doing it right? Is it supposed to work?

@filipnavara
Copy link
Member Author

Am I doing it right? Is it supposed to work?

There are at least three places that need to be patched up - filipnavara/runtime@5728708

@VSadov
Copy link
Member

VSadov commented Apr 24, 2025

Now simple async tests that do not do EH are passing, but EH test fails.

E:\A2\runtimelab\artifacts\tests\coreclr\windows.x86.Checked>async\simple-eh\simple-eh.cmd
BEGIN EXECUTION
 "E:\A2\runtimelab\artifacts\tests\coreclr\windows.x86.Checked\Tests\Core_Root\corerun.exe" -p "System.Reflection.Metadata.MetadataUpdater.IsSupported=false" -p "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization=true"  simple-eh.dll
System.AggregateException: One or more errors occurred. (Exception of type 'Async2SimpleEH+IntegerException' was thrown.)
 ---> Async2SimpleEH+IntegerException: Exception of type 'Async2SimpleEH+IntegerException' was thrown.
   at Async2SimpleEH.Throw(Int32 value) in E:\A2\runtimelab\src\tests\async\simple-eh.cs:line 43
   at System.Runtime.CompilerServices.AsyncHelpers.DispatchContinuations(Continuation continuation)
--- End of stack trace from previous location ---
   at Async2SimpleEH.Handler() in E:\A2\runtimelab\src\tests\async\simple-eh.cs:line 31
   at System.Runtime.CompilerServices.AsyncHelpers.DispatchContinuations(Continuation continuation)
   at System.Runtime.CompilerServices.AsyncHelpers.FinalizeTaskReturningThunk[T](Continuation continuation)
   at Async2SimpleEH.AsyncEntry() in E:\A2\runtimelab\src\tests\async\simple-eh.cs:line 23
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at Async2SimpleEH.Test() in E:\A2\runtimelab\src\tests\async\simple-eh.cs:line 18
   at __GeneratedMainWrapper.Main() in E:\A2\runtimelab\artifacts\tests\coreclr\obj\windows.x86.Checked\Managed\async\simple-eh\XUnitWrapperGenerator\XUnitWrapperGenerator.XUnitWrapperGenerator\SimpleRunner.g.cs:line 7
Expected: 100
Actual: 101
END EXECUTION - FAILED
FAILED

This is something with exception dispatching.
This test passes without FEATURE_EH_FUNCLETS

The change may not be as mechanical as copying the pattern from IL_Throw. Something else may be missing.
CC: @jakobbotsch

@filipnavara
Copy link
Member Author

Okay, I will have a look tomorrow. (The funclets patch on top of main passed the tests this morning.)

@filipnavara
Copy link
Member Author

filipnavara commented Apr 25, 2025

Where can I get the Roslyn async2 NuGet packages?

Nvm, I found the instructions.

@filipnavara
Copy link
Member Author

filipnavara commented Apr 25, 2025

I initially compiled everything in Debug configuration and the simple-eh test passed, but many other tests are failing. For example:

        System.AggregateException: One or more errors occurred. (Object reference not set to an instance of an object.)
         ---> System.NullReferenceException: Object reference not set to an instance of an object.
           at System.Runtime.CompilerServices.AsyncHelpers.AllocContinuation(Continuation prevContinuation, UIntPtr numGCRefs, UIntPtr dataSize)
           at AwaitNotAsync.AsyncEntryPoint() in D:\runtime\src\tests\async\awaitingnotasync.cs:line 39
           at System.Runtime.CompilerServices.AsyncHelpers.DispatchContinuations(Continuation continuation)
           at System.Runtime.CompilerServices.AsyncHelpers.FinalizeTaskReturningThunk(Continuation continuation)
           --- End of inner exception stack trace ---
           at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
           at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
           at System.Threading.Tasks.Task.Wait()
           at AwaitNotAsync.TestEntryPoint() in D:\runtime\src\tests\async\awaitingnotasync.cs:line 14
           at __GeneratedMainWrapper.Main() in D:\runtime\artifacts\tests\coreclr\obj\windows.x86.Debug\Managed\async\awaitingnotasync\XUnitWrapperGenerator\XUnitWrapperGenerator.XUnitW
  rapperGenerator\SimpleRunner.g.cs:line 7

(prevContinuation is null)

I don't think there's a problem with the implementation of IL_ThrowExact but there is definitely some problem, and it looks like incorrect treatment of the ECX register somewhere.

@filipnavara
Copy link
Member Author

filipnavara commented Apr 25, 2025

FWIW I see the same failures even with the baseline (no funclets) in Debug builds.

UPD: Checked runtime w/ Debug libs also fails:

        Process terminated.
        Assertion failed.
        continuation != null

           at System.Runtime.CompilerServices.AsyncHelpers.DispatchContinuations(Continuation continuation)
           at System.Runtime.CompilerServices.AsyncHelpers.FinalizeTaskReturningThunk(Continuation continuation)
           at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.ExecutionContextCallback(Object s)
           at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
           at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
           at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext()
           at System.Threading.Tasks.AwaitTaskContinuation.System.Threading.IThreadPoolWorkItem.Execute()
           at System.Threading.ThreadPoolWorkQueue.Dispatch()
           at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
           at System.Threading.Thread.StartCallback()

UPD2: I did a full rebuild, checked runtime + release libs, still fails with the assert above (without any of my changes).

@VSadov
Copy link
Member

VSadov commented Apr 25, 2025

buildroslynnugets.bat is a prerequisite. It will build the nugets locally

There are some tests that are not passing in the baseline on x86. Seems like something got broken recently. Even when we run tests, sometimes we miss things since there is no CI.

async\simple-eh should be passing (passes when I run it)

@VSadov
Copy link
Member

VSadov commented Apr 25, 2025

I think we should get all baseline tests to pass first.
Sorry for the mess.

We are in a process of moving the code to main repo, so need to merge code back and forth to maintain some coherence of changes. It is a bit of a fragile state. It is temporary.

I still suspect the IL_ThrowExact might need more changes than IL_Throw, since it is kind of rethrow of an exception that logically has not been thrown yet.
But maybe we are seeing something that is broken already, just not affecting the baseline simple-eh somehow

@filipnavara
Copy link
Member Author

I think we should get all baseline tests to pass first.

Makes sense to me.

I still suspect the IL_ThrowExact might need more changes than IL_Throw, since it is kind of rethrow of an exception that has not been thrown yet.

The changes are in the C part of the code. This PR modifies only how the initial context for the exception propagation gets captured. Unless there's a change to SoftwareExceptionFrame that I missed the added assembly prologs/epilogs around IL_Throw[Exact] should be identical.

But maybe we are seeing something that is broken already, just not affecting the baseline simple-eh somehow

The test passed for me both without funclets and with this change + funclet cherry-pick commit. I only tested the Debug build though. I got sidetracked by the other issues and didn't retest it in Release/Checked to see if it fails there.

@VSadov
Copy link
Member

VSadov commented Apr 25, 2025

The problem with baseline seems have been resolved. #3101
We removed [BypassReadyToRun] from some helper methods prematurely. It looks like we do need to make R2R typesystem understand MethodImpl.Async before that works in all scenarios.

@filipnavara
Copy link
Member Author

Thanks, I will resume testing tomorrow.

@VSadov
Copy link
Member

VSadov commented Apr 25, 2025

now, with the rebased PR, I see all async tests pass on x86, except for simple-eh. Same stack as before:

E:\A2\runtimelab\artifacts\tests\coreclr\windows.x86.Checked>async\simple-eh\simple-eh.cmd
BEGIN EXECUTION
 "E:\A2\runtimelab\artifacts\tests\coreclr\windows.x86.Checked\Tests\Core_Root\corerun.exe" -p "System.Reflection.Metadata.MetadataUpdater.IsSupported=false" -p "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization=true"  simple-eh.dll
System.AggregateException: One or more errors occurred. (Exception of type 'Async2SimpleEH+IntegerException' was thrown.)
 ---> Async2SimpleEH+IntegerException: Exception of type 'Async2SimpleEH+IntegerException' was thrown.
   at Async2SimpleEH.Throw(Int32 value) in E:\A2\runtimelab\src\tests\async\simple-eh.cs:line 43
   at System.Runtime.CompilerServices.AsyncHelpers.DispatchContinuations(Continuation continuation)
--- End of stack trace from previous location ---
   at Async2SimpleEH.Handler() in E:\A2\runtimelab\src\tests\async\simple-eh.cs:line 31
   at System.Runtime.CompilerServices.AsyncHelpers.DispatchContinuations(Continuation continuation)
   at System.Runtime.CompilerServices.AsyncHelpers.FinalizeTaskReturningThunk[T](Continuation continuation)
   at Async2SimpleEH.AsyncEntry() in E:\A2\runtimelab\src\tests\async\simple-eh.cs:line 23
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at Async2SimpleEH.Test() in E:\A2\runtimelab\src\tests\async\simple-eh.cs:line 18
   at __GeneratedMainWrapper.Main() in E:\A2\runtimelab\artifacts\tests\coreclr\obj\windows.x86.Checked\Managed\async\simple-eh\XUnitWrapperGenerator\XUnitWrapperGenerator.XUnitWrapperGenerator\SimpleRunner.g.cs:line 7
Expected: 100
Actual: 101
END EXECUTION - FAILED
FAILED
    public static async2 Task<int> Handler()
    {
        try
        {
            return await Throw(42);
        }
        catch (IntegerException ex)
        {
            return ex.Value;
        }
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static async2 Task<int> Throw(int value)
    {
        await Task.Yield();
        throw new IntegerException(value);
    }

Something is not working.
We are able to throw, but not to catch.

Note that Throw will suspend, then resume and then throw. At the time of the throw the Handler is not on the stack anymore. Since Throw does not catch, the exception will be caught by the DispatchContinuations.
The dispatcher will walk up the continuation list, ignoring those that have no EH, then resumes the first one that does, after storing the exception into the continuation's data. The resumption will branch into where it left (yes, it will branch into try, it is allowed in this case), then it will see that we have an exceptional result and throw the stashed exception, which at that point should be caught, but it looks like it is not.

I'll try to figure what part does not match up.

@filipnavara
Copy link
Member Author

filipnavara commented Apr 25, 2025

X86 doesn't have personality routines attached to managed code, so there have to be explicit places where we need to "inject" ProcessCLRException into the SEH handler chain. I suspect there may be some place where this is missing in the new flow although I didn't see it when I checked the code yesterday.

Let me know if you find something but no need to waste too much time on it. I hope I will be able to reproduce it and debug it.

@filipnavara
Copy link
Member Author

filipnavara commented Apr 25, 2025

It seems that it fails to find the handler in Handler because the try end offset is 0x44 and the code offset is 0x44 as well. This gets to RhEHClause.ContainsCodeOffset which treats the end offset as exclusive and doesn't match the try region.

The part of the JIT dump that throws the exception:

G_M3719_IG04:        ; offs=0x00003C, size=0x0008, bbWeight=0, PerfScore 0.00, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, BB09 [0008], byref

IN000b: 00003C mov      ecx, gword ptr [V09 ebp-0x28]
IN000c: 00003F call     CORINFO_HELP_THROWEXACT

G_M3719_IG05:        ; offs=0x000044, size=0x0005, bbWeight=1, PerfScore 1.25, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, BB03 [0002], byref

IN000d: 000044 mov      eax, dword ptr [V01 ebp-0x08]
IN000e: 000047 xor      ecx, ecx

and reported EH table:

*************** EH table for Async2SimpleEH:Handler():int
1 EH table entries, 0 duplicate clauses, 0 cloned finallys, 1 total EH entries reported to VM
setEHcount(cEH=1)
EH#0: try [0021..0044) handled by [0123..0136) (class: 2000004)

@filipnavara
Copy link
Member Author

filipnavara commented Apr 25, 2025

Possible fix is to change SoftwareExceptionFrame::UpdateContextFromTransitionBlock to subtract something from the return address to point at the IL_Throw[Exact] call site instead of right behind it. I'll need to cross-check it with x64 and run full test suite though.

There's a comment in RhEHClause.ContainsCodeOffset that states the return address should be adjusted, so I just need to find the right place to do it:

We expect the stackwalker to adjust return addresses to point at 'return address - 1' so that we
can use an interval here that is closed at the start and open at the end.

@filipnavara
Copy link
Member Author

Fix for the bug submitted upstream: dotnet/runtime#115065
This PR should work as is once the upstream PR is merged and flows through.

@VSadov
Copy link
Member

VSadov commented Apr 26, 2025

Thanks @filipnavara ! I will try cherrypicking the upstream change.

…ateContextFromTransitionBlock (#115065)

This could lead to -1 offset inadvertently not applied in `SfiInit` for control PC. As a consequence a `throw` at the very last instruction of `try` block may not be matched correctly.
@VSadov
Copy link
Member

VSadov commented Apr 26, 2025

I have rebased the changes onto upstream/feature/async2-experiment and added a commit from dotnet/runtime#115065 .

With all that the async tests are passing on x86 regardless if FEATURE_EH_FUNCLETS is enabled or not. I think we can merge this.

Copy link
Member

@VSadov VSadov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!!

@VSadov VSadov merged commit ad96467 into dotnet:feature/async2-experiment Apr 26, 2025
1 of 7 checks passed
@filipnavara filipnavara deleted the throwexact-x86 branch April 26, 2025 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-async Runtime generate async state machines
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants