diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index c4703ddf4516c7..2ee4217799b003 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1258,6 +1258,23 @@ private CorInfoInline canInline(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHO MethodDesc callerMethod = HandleToObject(callerHnd); MethodDesc calleeMethod = HandleToObject(calleeHnd); + EcmaModule rootModule = (MethodBeingCompiled.OwningType as MetadataType)?.Module as EcmaModule; + EcmaModule calleeModule = (calleeMethod.OwningType as MetadataType)?.Module as EcmaModule; + + // If this inline crosses module boundaries, ensure the modules agree on exception wrapping behavior. + if ((rootModule != calleeModule) && (rootModule != null) && (calleeModule != null)) + { + if (rootModule.IsWrapNonExceptionThrows != calleeModule.IsWrapNonExceptionThrows) + { + var calleeIL = _compilation.GetMethodIL(calleeMethod); + if (calleeIL.GetExceptionRegions().Length != 0) + { + // Fail inlining if root method and callee have different exception wrapping behavior + return CorInfoInline.INLINE_FAIL; + } + } + } + if (_compilation.CanInline(callerMethod, calleeMethod)) { // No restrictions on inlining @@ -1266,6 +1283,10 @@ private CorInfoInline canInline(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHO else { // Call may not be inlined + // + // Note despite returning INLINE_NEVER here, in compilations where jitting is possible + // the jit may still be able to inline this method. So we rely on reportInliningDecision + // to not propagate this into metadata to short-circuit future inlining attempts. return CorInfoInline.INLINE_NEVER; } } diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs index 6758f4a549866c..cd87728b53e852 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs @@ -16,6 +16,8 @@ public partial class EcmaModule : ModuleDesc { private readonly PEReader _peReader; protected readonly MetadataReader _metadataReader; + private volatile bool _isWrapNonExceptionThrowsComputed; + private volatile bool _isWrapNonExceptionThrows; internal interface IEntityHandleObject { @@ -690,5 +692,52 @@ public override string ToString() ModuleDefinition moduleDefinition = _metadataReader.GetModuleDefinition(); return _metadataReader.GetString(moduleDefinition.Name); } + + public bool IsWrapNonExceptionThrows + { + get + { + if (!_isWrapNonExceptionThrowsComputed) + { + ComputeIsWrapNonExceptionThrows(); + _isWrapNonExceptionThrowsComputed = true; + } + return _isWrapNonExceptionThrows; + } + } + + private void ComputeIsWrapNonExceptionThrows() + { + var reader = MetadataReader; + var c = reader.StringComparer; + bool foundAttribute = false; + foreach (var attr in reader.GetAssemblyDefinition().GetCustomAttributes()) + { + if (reader.GetAttributeNamespaceAndName(attr, out var ns, out var n)) + { + if (c.Equals(ns, "System.Runtime.CompilerServices") && c.Equals(n, "RuntimeCompatibilityAttribute")) + { + var dec = reader.GetCustomAttribute(attr).DecodeValue(new CustomAttributeTypeProvider(this)); + + foreach (var arg in dec.NamedArguments) + { + if (arg.Name == "WrapNonExceptionThrows") + { + if (!(arg.Value is bool)) + ThrowHelper.ThrowBadImageFormatException(); + _isWrapNonExceptionThrows = (bool)arg.Value; + foundAttribute = true; + break; + } + } + } + } + + if (foundAttribute) + { + break; + } + } + } } } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 77ffeec4f70ccc..484f27f8a4b9e4 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -7668,6 +7668,37 @@ static void getMethodInfoHelper( &methInfo->locals); } // getMethodInfoHelper + +void CEEInfo::getTransientMethodInfo(MethodDesc* pMD, CORINFO_METHOD_INFO* methInfo) +{ + MethodInfoHelperContext cxt{ pMD }; + + // We will either find or create transient method details. + _ASSERTE(!cxt.HasTransientMethodDetails()); + + // IL methods with no RVA indicate there is no implementation defined in metadata. + // Check if we previously generated details/implementation for this method. + TransientMethodDetails* detailsMaybe = NULL; + if (FindTransientMethodDetails(pMD, &detailsMaybe)) + cxt.UpdateWith(*detailsMaybe); + + getMethodInfoHelper(cxt, methInfo); + + // If we have transient method details we need to handle + // the lifetime of the details. + if (cxt.HasTransientMethodDetails()) + { + // If we didn't find transient details, but we have them + // after getting method info, store them for cleanup. + if (detailsMaybe == NULL) + AddTransientMethodDetails(cxt.CreateTransientMethodDetails()); + + // Reset the context because ownership has either been + // transferred or was found in this instance. + cxt.UpdateWith({}); + } +} + //--------------------------------------------------------------------------------------- // bool @@ -7704,31 +7735,7 @@ CEEInfo::getMethodInfo( } else if (ftn->IsIL() && ftn->GetRVA() == 0) { - // We will either find or create transient method details. - _ASSERTE(!cxt.HasTransientMethodDetails()); - - // IL methods with no RVA indicate there is no implementation defined in metadata. - // Check if we previously generated details/implementation for this method. - TransientMethodDetails* detailsMaybe = NULL; - if (FindTransientMethodDetails(ftn, &detailsMaybe)) - cxt.UpdateWith(*detailsMaybe); - - getMethodInfoHelper(cxt, methInfo); - - // If we have transient method details we need to handle - // the lifetime of the details. - if (cxt.HasTransientMethodDetails()) - { - // If we didn't find transient details, but we have them - // after getting method info, store them for cleanup. - if (detailsMaybe == NULL) - AddTransientMethodDetails(cxt.CreateTransientMethodDetails()); - - // Reset the context because ownership has either been - // transferred or was found in this instance. - cxt.UpdateWith({}); - } - + getTransientMethodInfo(ftn, methInfo); result = true; } @@ -7890,6 +7897,40 @@ CorInfoInline CEEInfo::canInline (CORINFO_METHOD_HANDLE hCaller, } } + // If the root level caller and callee modules do not have the same runtime + // wrapped exception behavior, and the callee has EH, we cannot inline. + _ASSERTE(!pCallee->IsDynamicMethod()); + { + Module* pCalleeModule = pCallee->GetModule(); + Module* pRootModule = pOrigCaller->GetModule(); + + if (pRootModule->IsRuntimeWrapExceptions() != pCalleeModule->IsRuntimeWrapExceptions()) + { + if (pCallee->HasILHeader()) + { + COR_ILMETHOD_DECODER header(pCallee->GetILHeader(), pCallee->GetMDImport(), NULL); + if (header.EHCount() > 0) + { + result = INLINE_FAIL; + szFailReason = "Inlinee and root method have different wrapped exception behavior"; + goto exit; + } + } + else if (pCallee->IsIL() && pCallee->GetRVA() == 0) + { + CORINFO_METHOD_INFO methodInfo; + getTransientMethodInfo(pCallee, &methodInfo); + + if (methodInfo.EHcount > 0) + { + result = INLINE_FAIL; + szFailReason = "Inlinee and root method have different wrapped exception behavior"; + goto exit; + } + } + } + } + #ifdef PROFILING_SUPPORTED if (pOrigCallerModule->IsInliningDisabledByProfiler()) { diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 705a1df5395afa..0974e0cd0cdeeb 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -472,6 +472,9 @@ class CEEInfo : public ICorJitInfo TransientMethodDetails RemoveTransientMethodDetails(MethodDesc* pMD); bool FindTransientMethodDetails(MethodDesc* pMD, TransientMethodDetails** details); + // Get method info for a transient method + void getTransientMethodInfo(MethodDesc* pMD, CORINFO_METHOD_INFO* methInfo); + protected: SArray* m_pJitHandles; // GC handles used by JIT MethodDesc* m_pMethodBeingCompiled; // Top-level method being compiled diff --git a/src/tests/JIT/Directed/throwbox/filter.il b/src/tests/JIT/Directed/throwbox/filter.il index d98bd4e2e680eb..d70da72f192aee 100644 --- a/src/tests/JIT/Directed/throwbox/filter.il +++ b/src/tests/JIT/Directed/throwbox/filter.il @@ -14,7 +14,7 @@ .class public auto ansi beforefieldinit Test extends [mscorlib]System.Object { - .method public hidebysig static int32 Main() cil managed + .method public hidebysig static int32 Main() cil managed aggressiveinlining { .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = ( 01 00 00 00