-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Remove non INPLACE_RUNTIME NativeAOT paths, cleanup exceptions #100379
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
Changes from all commits
8da67f1
da61990
10ab978
9c9ce90
1e36fc8
a765dd8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -316,14 +316,8 @@ internal static Exception GetClasslibException(ExceptionIDs id, IntPtr address) | |
Exception? e = id switch | ||
{ | ||
ExceptionIDs.AccessViolation => new AccessViolationException(), | ||
ExceptionIDs.Arithmetic => new ArithmeticException(), | ||
ExceptionIDs.AmbiguousImplementation => new AmbiguousImplementationException(), | ||
ExceptionIDs.ArrayTypeMismatch => new ArrayTypeMismatchException(), | ||
ExceptionIDs.DataMisaligned => new DataMisalignedException(), | ||
ExceptionIDs.DivideByZero => new DivideByZeroException(), | ||
ExceptionIDs.EntrypointNotFound => new EntryPointNotFoundException(), | ||
ExceptionIDs.IndexOutOfRange => new IndexOutOfRangeException(), | ||
ExceptionIDs.InvalidCast => new InvalidCastException(), | ||
ExceptionIDs.NullReference => new NullReferenceException(), | ||
ExceptionIDs.OutOfMemory => new OutOfMemoryException(), | ||
ExceptionIDs.Overflow => new OverflowException(), | ||
|
@@ -338,63 +332,8 @@ internal static Exception GetClasslibException(ExceptionIDs id, IntPtr address) | |
|
||
return e; | ||
} | ||
#if NATIVEAOT | ||
// Given an ExceptionID and an MethodTable address, get an exception object of a type that the module containing | ||
// the given address will understand. This finds the classlib-defined GetRuntimeException function and asks | ||
// it for the exception object. | ||
internal static Exception GetClasslibExceptionFromEEType(ExceptionIDs id, MethodTable* pEEType) | ||
{ | ||
// Find the classlib function that will give us the exception object we want to throw. This | ||
// is a RuntimeExport function from the classlib module, and is therefore managed-callable. | ||
IntPtr pGetRuntimeExceptionFunction = IntPtr.Zero; | ||
if (pEEType != null) | ||
{ | ||
pGetRuntimeExceptionFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType(pEEType, ClassLibFunctionId.GetRuntimeException); | ||
} | ||
|
||
// Return the exception object we get from the classlib. | ||
Exception? e = null; | ||
try | ||
{ | ||
e = ((delegate*<ExceptionIDs, Exception>)pGetRuntimeExceptionFunction)(id); | ||
} | ||
catch when (true) | ||
{ | ||
// disallow all exceptions leaking out of callbacks | ||
} | ||
|
||
// If the helper fails to yield an object, then we fail-fast. | ||
if (e == null) | ||
{ | ||
FailFastViaClasslib(RhFailFastReason.InternalError, null, (IntPtr)pEEType); | ||
} | ||
|
||
return e; | ||
} | ||
|
||
// RhExceptionHandling_ functions are used to throw exceptions out of our asm helpers. We tail-call from | ||
// the asm helpers to these functions, which performs the throw. The tail-call is important: it ensures that | ||
// the stack is crawlable from within these functions. | ||
[StackTraceHidden] | ||
[RuntimeExport("RhExceptionHandling_ThrowClasslibOverflowException")] | ||
public static void ThrowClasslibOverflowException(IntPtr address) | ||
{ | ||
// Throw the overflow exception defined by the classlib, using the return address of the asm helper | ||
// to find the correct classlib. | ||
|
||
throw GetClasslibException(ExceptionIDs.Overflow, address); | ||
} | ||
|
||
[StackTraceHidden] | ||
[RuntimeExport("RhExceptionHandling_ThrowClasslibDivideByZeroException")] | ||
public static void ThrowClasslibDivideByZeroException(IntPtr address) | ||
{ | ||
// Throw the divide by zero exception defined by the classlib, using the return address of the asm helper | ||
// to find the correct classlib. | ||
|
||
throw GetClasslibException(ExceptionIDs.DivideByZero, address); | ||
} | ||
|
||
#if NATIVEAOT | ||
[StackTraceHidden] | ||
[RuntimeExport("RhExceptionHandling_FailedAllocation")] | ||
public static void FailedAllocation(MethodTable* pEEType, bool fIsOverflow) | ||
|
@@ -403,39 +342,8 @@ public static void FailedAllocation(MethodTable* pEEType, bool fIsOverflow) | |
|
||
// Throw the out of memory exception defined by the classlib, using the input MethodTable* | ||
// to find the correct classlib. | ||
|
||
throw pEEType->GetClasslibException(exID); | ||
} | ||
|
||
#if !INPLACE_RUNTIME | ||
private static readonly OutOfMemoryException s_theOOMException = new OutOfMemoryException(); | ||
|
||
// MRT exports GetRuntimeException for the few cases where we have a helper that throws an exception | ||
// and may be called by either MRT or other classlibs and that helper needs to throw an exception. | ||
// There are only a few cases where this happens now (the fast allocation helpers), so we limit the | ||
// exception types that MRT will return. | ||
[RuntimeExport("GetRuntimeException")] | ||
public static Exception GetRuntimeException(ExceptionIDs id) | ||
{ | ||
switch (id) | ||
{ | ||
case ExceptionIDs.OutOfMemory: | ||
// Throw a preallocated exception to avoid infinite recursion. | ||
return s_theOOMException; | ||
|
||
case ExceptionIDs.Overflow: | ||
return new OverflowException(); | ||
|
||
case ExceptionIDs.InvalidCast: | ||
return new InvalidCastException(); | ||
|
||
default: | ||
Debug.Assert(false, "unexpected ExceptionID"); | ||
FallbackFailFast(RhFailFastReason.InternalError, null); | ||
return null; | ||
} | ||
} | ||
#endif | ||
#endif // NATIVEAOT | ||
|
||
private enum HwExceptionCode : uint | ||
|
@@ -561,16 +469,16 @@ public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) | |
#endif | ||
IntPtr faultingCodeAddress = exInfo._pExContext->IP; | ||
bool instructionFault = true; | ||
ExceptionIDs exceptionId = default(ExceptionIDs); | ||
ExceptionIDs exceptionId = 0; | ||
Exception? exceptionToThrow = null; | ||
|
||
switch (exceptionCode) | ||
switch ((HwExceptionCode)exceptionCode) | ||
{ | ||
case (uint)HwExceptionCode.STATUS_REDHAWK_NULL_REFERENCE: | ||
case HwExceptionCode.STATUS_REDHAWK_NULL_REFERENCE: | ||
exceptionId = ExceptionIDs.NullReference; | ||
break; | ||
|
||
case (uint)HwExceptionCode.STATUS_REDHAWK_UNMANAGED_HELPER_NULL_REFERENCE: | ||
case HwExceptionCode.STATUS_REDHAWK_UNMANAGED_HELPER_NULL_REFERENCE: | ||
// The write barrier where the actual fault happened has been unwound already. | ||
// The IP of this fault needs to be treated as return address, not as IP of | ||
// faulting instruction. | ||
|
@@ -579,26 +487,26 @@ public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) | |
break; | ||
|
||
#if NATIVEAOT | ||
case (uint)HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT: | ||
case HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT: | ||
exceptionToThrow = InternalCalls.RhpGetThreadAbortException(); | ||
break; | ||
#endif | ||
|
||
case (uint)HwExceptionCode.STATUS_DATATYPE_MISALIGNMENT: | ||
case HwExceptionCode.STATUS_DATATYPE_MISALIGNMENT: | ||
exceptionId = ExceptionIDs.DataMisaligned; | ||
break; | ||
|
||
// N.B. -- AVs that have a read/write address lower than 64k are already transformed to | ||
// HwExceptionCode.REDHAWK_NULL_REFERENCE prior to calling this routine. | ||
case (uint)HwExceptionCode.STATUS_ACCESS_VIOLATION: | ||
case HwExceptionCode.STATUS_ACCESS_VIOLATION: | ||
exceptionId = ExceptionIDs.AccessViolation; | ||
break; | ||
|
||
case (uint)HwExceptionCode.STATUS_INTEGER_DIVIDE_BY_ZERO: | ||
case HwExceptionCode.STATUS_INTEGER_DIVIDE_BY_ZERO: | ||
exceptionId = ExceptionIDs.DivideByZero; | ||
break; | ||
|
||
case (uint)HwExceptionCode.STATUS_INTEGER_OVERFLOW: | ||
case HwExceptionCode.STATUS_INTEGER_OVERFLOW: | ||
exceptionId = ExceptionIDs.Overflow; | ||
break; | ||
|
||
|
@@ -610,7 +518,7 @@ public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) | |
break; | ||
} | ||
|
||
if (exceptionId != default(ExceptionIDs)) | ||
if (exceptionId != 0) | ||
{ | ||
exceptionToThrow = GetClasslibException(exceptionId, faultingCodeAddress); | ||
} | ||
|
@@ -1081,38 +989,9 @@ private static bool FindFirstPassHandler(object exception, uint idxStart, | |
return false; | ||
} | ||
|
||
#if DEBUG && !INPLACE_RUNTIME && NATIVEAOT | ||
private static MethodTable* s_pLowLevelObjectType; | ||
private static void AssertNotRuntimeObject(MethodTable* pClauseType) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This still looks valuable if we expect exception handling would still use some C# code and might end up being shared between all managed runtime instances within a process (for loading-multiple-nativeAOT-dlls-in-the-same-process-and sharing-low-level-runtime case). I don't know how such layering would look like, so I defer to Jan. The existing INPLACE_LAYERING was known to actually work because it was used like that in .NET Native. I don't know about this new layering. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that the shared component should be GC and GC support like thread suspension, not much else. Ideally, we would have an option to run in a configuration where a CoreCLR instance and native AOT instances would be able to share one process wide GC instance. Exception handling, casting, interface dispatch should be all private per-runtime. I think that the .NET Native design where these services tried to be in the shared component was problematic. With this plan, the shared component may still need some managed code that can be stackwalked for good perf, but it should not need to do any allocations or handle exceptions on its own. We would use compiler/linker to enforce that. It means that it should be fine to delete this code. |
||
{ | ||
// | ||
// The C# try { } catch { } clause expands into a typed catch of System.Object. | ||
// Since runtime has its own definition of System.Object, try { } catch { } might not do what | ||
// was intended (catch all exceptions). | ||
// | ||
// This assertion is making sure we don't use try { } catch { } within the runtime. | ||
// The runtime codebase should either use try { } catch (Exception) { } for exception types | ||
// from the runtime or a try { } catch when (true) { } to catch all exceptions. | ||
// | ||
|
||
if (s_pLowLevelObjectType == null) | ||
{ | ||
// Allocating might fail, but since this is just a debug assert, it's probably fine. | ||
s_pLowLevelObjectType = new System.Object().MethodTable; | ||
} | ||
|
||
Debug.Assert(!pClauseType->IsEquivalentTo(s_pLowLevelObjectType)); | ||
} | ||
#endif // DEBUG && !INPLACE_RUNTIME && NATIVEAOT | ||
|
||
|
||
private static bool ShouldTypedClauseCatchThisException(object exception, MethodTable* pClauseType, bool tryUnwrapException) | ||
{ | ||
#if NATIVEAOT | ||
#if DEBUG && !INPLACE_RUNTIME | ||
AssertNotRuntimeObject(pClauseType); | ||
#endif | ||
|
||
return TypeCast.IsInstanceOfException(pClauseType, exception); | ||
#else | ||
if (tryUnwrapException && exception is RuntimeWrappedException ex) | ||
|
Uh oh!
There was an error while loading. Please reload this page.