Skip to content
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

Add EH filters for generic catch(T) blocks #72721

Merged
merged 30 commits into from
Jul 29, 2022
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8d9b3ef
NativeAOT: Implement a fake EH filter for generic exceptions
EgorBo Jul 23, 2022
7d4bcbd
Add smoke test
EgorBo Jul 23, 2022
39752c7
Update GenericExceptions.cs
EgorBo Jul 23, 2022
3a045da
CI test
EgorBo Jul 23, 2022
fd57c28
Merge branch 'nativeaot-eh-generic-exception' of github.com:EgorBo/ru…
EgorBo Jul 23, 2022
f24208a
Address feedback & fix AVE
EgorBo Jul 23, 2022
f7c55a7
Clean up
EgorBo Jul 23, 2022
861c6c3
Fix runtime lookup for NativeAOT & Crossgen
EgorBo Jul 23, 2022
8a72927
Another attempt to fix runtime lookup (now should be working)
EgorBo Jul 23, 2022
58d3b77
Remove tests from issues.targets
EgorBo Jul 24, 2022
54763a7
Move fgCreateFiltersForGenericExceptions to fgAddInternal
EgorBo Jul 24, 2022
a32b92a
simplify runtime lookup
EgorBo Jul 24, 2022
3668e25
Address feedback
EgorBo Jul 24, 2022
49635eb
Implement HELPER_SimpleIsInstanceOf
EgorBo Jul 25, 2022
f8a50a5
Merge branch 'main' of github.com:dotnet/runtime into nativeaot-eh-ge…
EgorBo Jul 25, 2022
2a47ff9
Clean up
EgorBo Jul 25, 2022
bee1fb3
Add Jan's IL test
EgorBo Jul 25, 2022
10ca969
Address feedback (NativeAOT's part is not finished yet)
EgorBo Jul 25, 2022
79e87b8
remove dead code
EgorBo Jul 25, 2022
5420035
Update src/tests/JIT/Generics/Exceptions/GenericCatchInterfaceProgram.cs
EgorBo Jul 25, 2022
9cf10d2
Fix test
EgorBo Jul 25, 2022
fd3130e
NativeAOT fix
EgorBo Jul 25, 2022
22f2520
remove "variance" block
EgorBo Jul 25, 2022
9110e5f
Address feedback
EgorBo Jul 25, 2022
d3c01cb
Remove IsPolyType
EgorBo Jul 25, 2022
aaa075e
rename csproj to ilproj
EgorBo Jul 25, 2022
98ce006
Update src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanne…
EgorBo Jul 26, 2022
5c7c157
Disable test on mono
EgorBo Jul 26, 2022
40ca568
Merge branch 'main' of github.com:dotnet/runtime into nativeaot-eh-ge…
EgorBo Jul 26, 2022
d13147d
Update src/coreclr/jit/jiteh.cpp
EgorBo Jul 28, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/design/coreclr/botr/readytorun-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,7 @@ enum ReadyToRunHelper
READYTORUN_HELPER_GenericGcTlsBase = 0x66,
READYTORUN_HELPER_GenericNonGcTlsBase = 0x67,
READYTORUN_HELPER_VirtualFuncPtr = 0x68,
READYTORUN_HELPER_IsInstanceOfException = 0x69,

// Long mul/div/shift ops
READYTORUN_HELPER_LMul = 0xC0,
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,8 @@ enum CorInfoHelpFunc
CORINFO_HELP_CHKCASTCLASS_SPECIAL, // Optimized helper for classes. Assumes that the trivial cases
// has been taken care of by the inlined check

CORINFO_HELP_ISINSTANCEOF_EXCEPTION,

CORINFO_HELP_BOX, // Fast box helper. Only possible exception is OutOfMemory
CORINFO_HELP_BOX_NULLABLE, // special form of boxing for Nullable<T>
CORINFO_HELP_UNBOX,
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
#define GUID_DEFINED
#endif // !GUID_DEFINED

constexpr GUID JITEEVersionIdentifier = { /* 0d853657-7a01-421f-b1b0-d22a8e691441 */
0x0d853657,
0x7a01,
0x421f,
{0xb1, 0xb0, 0xd2, 0x2a, 0x8e, 0x69, 0x14, 0x41}
constexpr GUID JITEEVersionIdentifier = { /* 4efa8fe2-8489-4b61-aac9-b4df74af15b7 */
0x4efa8fe2,
0x8489,
0x4b61,
{0xaa, 0xc9, 0xb4, 0xdf, 0x74, 0xaf, 0x15, 0xb7}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
DYNAMICJITHELPER(CORINFO_HELP_CHKCASTANY, NULL, CORINFO_HELP_SIG_REG_ONLY)
DYNAMICJITHELPER(CORINFO_HELP_CHKCASTCLASS_SPECIAL, NULL, CORINFO_HELP_SIG_REG_ONLY)

JITHELPER(CORINFO_HELP_ISINSTANCEOF_EXCEPTION, JIT_IsInstanceOfException, CORINFO_HELP_SIG_REG_ONLY)

DYNAMICJITHELPER(CORINFO_HELP_BOX, JIT_Box, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_BOX_NULLABLE, JIT_Box, CORINFO_HELP_SIG_REG_ONLY)
DYNAMICJITHELPER(CORINFO_HELP_UNBOX, NULL, CORINFO_HELP_SIG_REG_ONLY)
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/inc/readytorun.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

// Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
#define READYTORUN_MAJOR_VERSION 0x0007
#define READYTORUN_MINOR_VERSION 0x0000
#define READYTORUN_MINOR_VERSION 0x0001

#define MINIMUM_READYTORUN_MAJOR_VERSION 0x006

Expand Down Expand Up @@ -329,6 +329,7 @@ enum ReadyToRunHelper
READYTORUN_HELPER_GenericGcTlsBase = 0x66,
READYTORUN_HELPER_GenericNonGcTlsBase = 0x67,
READYTORUN_HELPER_VirtualFuncPtr = 0x68,
READYTORUN_HELPER_IsInstanceOfException = 0x69,

// Long mul/div/shift ops
READYTORUN_HELPER_LMul = 0xC0,
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/inc/readytorunhelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ HELPER(READYTORUN_HELPER_GenericGcTlsBase, CORINFO_HELP_GETGENERICS_GCT
HELPER(READYTORUN_HELPER_GenericNonGcTlsBase, CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE,)

HELPER(READYTORUN_HELPER_VirtualFuncPtr, CORINFO_HELP_VIRTUAL_FUNC_PTR, )
HELPER(READYTORUN_HELPER_IsInstanceOfException, CORINFO_HELP_ISINSTANCEOF_EXCEPTION, )

HELPER(READYTORUN_HELPER_LMul, CORINFO_HELP_LMUL, )
HELPER(READYTORUN_HELPER_LMulOfv, CORINFO_HELP_LMUL_OVF, )
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2216,6 +2216,8 @@ class Compiler
bool fgNormalizeEHCase2();
bool fgNormalizeEHCase3();

void fgCreateFiltersForGenericExceptions();

void fgCheckForLoopsInHandlers();

#ifdef DEBUG
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2590,6 +2590,10 @@ void Compiler::fgAddInternal()
{
noway_assert(!compIsForInlining());

// For runtime determined Exception types we're going to emit a fake EH filter with isinst for this
// type with a runtime lookup
fgCreateFiltersForGenericExceptions();

// The backend requires a scratch BB into which it can safely insert a P/Invoke method prolog if one is
// required. Similarly, we need a scratch BB for poisoning. Create it here.
if (compMethodRequiresPInvokeFrame() || compShouldPoisonFrame())
Expand Down
88 changes: 88 additions & 0 deletions src/coreclr/jit/jiteh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2506,6 +2506,94 @@ bool Compiler::fgNormalizeEHCase2()
return modified;
}

//------------------------------------------------------------------------
// fgCreateFiltersForGenericExceptions:
// For Exception types which require runtime lookup it creates a "fake" single-block
// EH filter that performs "catchArg isinst T!!" and in case of success forwards to the
// original EH handler.
//

void Compiler::fgCreateFiltersForGenericExceptions()
{
for (unsigned ehNum = 0; ehNum < compHndBBtabCount; ehNum++)
{
EHblkDsc* eh = ehGetDsc(ehNum);
if (eh->ebdHandlerType == EH_HANDLER_CATCH)
{
// Resolve Exception type and check if it needs a runtime lookup
CORINFO_RESOLVED_TOKEN resolvedToken;
resolvedToken.tokenContext = impTokenLookupContextHandle;
resolvedToken.tokenScope = info.compScopeHnd;
resolvedToken.token = eh->ebdTyp;
resolvedToken.tokenType = CORINFO_TOKENKIND_Casting;
info.compCompHnd->resolveToken(&resolvedToken);

CORINFO_GENERICHANDLE_RESULT embedInfo;
info.compCompHnd->embedGenericHandle(&resolvedToken, true, &embedInfo);
if (!embedInfo.lookup.lookupKind.needsRuntimeLookup)
{
// Exception type does not need runtime lookup
continue;
}

// Create a new bb for the fake filter
BasicBlock* filterBb = bbNewBasicBlock(BBJ_EHFILTERRET);
BasicBlock* handlerBb = eh->ebdHndBeg;

// Now we need to spill CATCH_ARG (it should be the first thing evaluated)
GenTree* arg = new (this, GT_CATCH_ARG) GenTree(GT_CATCH_ARG, TYP_REF);
arg->gtFlags |= GTF_ORDER_SIDEEFF;
unsigned tempNum = lvaGrabTemp(false DEBUGARG("SpillCatchArg"));
lvaTable[tempNum].lvType = TYP_REF;
GenTree* argAsg = gtNewTempAssign(tempNum, arg);
arg = gtNewLclvNode(tempNum, TYP_REF);
filterBb->bbStkTempsIn = tempNum;
Copy link
Member

Choose a reason for hiding this comment

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

Nit -- you don't need to set this.

Suggested change
filterBb->bbStkTempsIn = tempNum;

Copy link
Member Author

Choose a reason for hiding this comment

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

hm.. I think I had to actually, let me see if CI is happy then

fgInsertStmtAtBeg(filterBb, gtNewStmt(argAsg, handlerBb->firstStmt()->GetDebugInfo()));

// Create "catchArg is TException" tree
GenTree* runtimeLookup;
if (embedInfo.lookup.runtimeLookup.indirections == CORINFO_USEHELPER)
{
GenTree* ctxTree = getRuntimeContextTree(embedInfo.lookup.lookupKind.runtimeLookupKind);
runtimeLookup = impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_GENERIC_HANDLE,
TYP_I_IMPL, &embedInfo.lookup.lookupKind, ctxTree);
}
else
{
runtimeLookup = getTokenHandleTree(&resolvedToken, true);
Copy link
Member

Choose a reason for hiding this comment

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

Just curious if this is measurable for something like Assert.Throws<T> given that it is using the slow runtime lookup here?

Copy link
Member Author

Choose a reason for hiding this comment

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

It turns out Xunit's Assert.Throws doesn't use generic "catch" blocks.

Also, this PR actually improves perf 😮:

static void Main(string[] args) =>
    BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);

[Benchmark]
public void Test()
{
    Throws<DivideByZeroException>(() =>
    {
        int b = 0;
        int x = 100 / b;
    });
}

static void Throws<T>(Action action) where T : Exception
{
    try { action(); }
    catch (T) { return; }
    throw new Exception();
}

by 5%

}
GenTree* isInstOfT = gtNewHelperCallNode(CORINFO_HELP_ISINSTANCEOF_EXCEPTION, TYP_INT, runtimeLookup, arg);
GenTree* retFilt = gtNewOperNode(GT_RETFILT, TYP_INT, isInstOfT);

// Insert it right before the handler (and make it a pred of the handler)
fgInsertBBbefore(handlerBb, filterBb);
fgAddRefPred(handlerBb, filterBb);
fgNewStmtAtEnd(filterBb, retFilt, handlerBb->firstStmt()->GetDebugInfo());

filterBb->bbCatchTyp = BBCT_FILTER;
filterBb->bbCodeOffs = handlerBb->bbCodeOffs;
filterBb->bbHndIndex = handlerBb->bbHndIndex;
filterBb->bbTryIndex = handlerBb->bbTryIndex;
filterBb->bbJumpDest = handlerBb;
filterBb->bbSetRunRarely();
filterBb->bbFlags |= BBF_INTERNAL | BBF_DONT_REMOVE;

handlerBb->bbCatchTyp = BBCT_FILTER_HANDLER;
eh->ebdHandlerType = EH_HANDLER_FILTER;
eh->ebdFilter = filterBb;

#ifdef DEBUG
if (verbose)
{
JITDUMP("EH%d: Adding EH filter block " FMT_BB " in front of generic handler " FMT_BB ":\n", ehNum,
filterBb->bbNum, handlerBb->bbNum);
fgDumpBlock(filterBb);
}
#endif // DEBUG
}
}
}

bool Compiler::fgNormalizeEHCase3()
{
bool modified = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -847,7 +847,7 @@ private static bool ShouldTypedClauseCatchThisException(object exception, Method
AssertNotRuntimeObject(pClauseType);
#endif

return TypeCast.IsInstanceOfClass(pClauseType, exception) != null;
return TypeCast.IsInstanceOfException(pClauseType, exception);
}

private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,43 @@ public static unsafe object IsInstanceOf(MethodTable* pTargetType, object obj)
return IsInstanceOfClass(pTargetType, obj);
}

[RuntimeExport("RhTypeCast_IsInstanceOfException")]
public static unsafe bool IsInstanceOfException(MethodTable* pTargetType, object? obj)
{
// Based on IsInstanceOfClass_Helper

if (obj == null)
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
return false;

MethodTable* pObjType = obj.GetMethodTable();

if (pTargetType->IsCloned)
pTargetType = pTargetType->CanonicalEEType;

if (pObjType->IsCloned)
pObjType = pObjType->CanonicalEEType;

if (pObjType == pTargetType)
return true;

// arrays can be cast to System.Object and System.Array
if (pObjType->IsArray)
return WellKnownEETypes.IsSystemObject(pTargetType) || WellKnownEETypes.IsSystemArray(pTargetType);

while (true)
{
pObjType = pObjType->NonClonedNonArrayBaseType;
if (pObjType == null)
return false;

if (pObjType->IsCloned)
pObjType = pObjType->CanonicalEEType;

if (pObjType == pTargetType)
return true;
}
}

[RuntimeExport("RhTypeCast_CheckCast")]
public static unsafe object CheckCast(MethodTable* pTargetType, object obj)
{
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct ReadyToRunHeaderConstants
static const uint32_t Signature = 0x00525452; // 'RTR'

static const uint32_t CurrentMajorVersion = 7;
static const uint32_t CurrentMinorVersion = 0;
static const uint32_t CurrentMinorVersion = 1;
};

struct ReadyToRunHeader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ internal static unsafe object IsInstanceOf(EETypePtr pTargetType, object obj)
[RuntimeImport(RuntimeLibrary, "RhTypeCast_IsInstanceOfInterface")]
internal static extern unsafe object IsInstanceOfInterface(MethodTable* pTargetType, object obj);

[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhTypeCast_IsInstanceOfException")]
internal static extern unsafe bool IsInstanceOfException(MethodTable* pTargetType, object obj);

internal static unsafe object IsInstanceOfInterface(EETypePtr pTargetType, object obj)
=> IsInstanceOfInterface(pTargetType.ToPointer(), obj);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal struct ReadyToRunHeaderConstants
public const uint Signature = 0x00525452; // 'RTR'

public const ushort CurrentMajorVersion = 7;
public const ushort CurrentMinorVersion = 0;
public const ushort CurrentMinorVersion = 1;
}

#pragma warning disable 0169
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ public enum ReadyToRunHelper
GenericGcTlsBase = 0x66,
GenericNonGcTlsBase = 0x67,
VirtualFuncPtr = 0x68,
IsInstanceOfException = 0x69,

// Long mul/div/shift ops
LMul = 0xC0,
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ which is the right helper to use to allocate an object of a given type. */
CORINFO_HELP_CHKCASTCLASS_SPECIAL, // Optimized helper for classes. Assumes that the trivial cases
// has been taken care of by the inlined check

CORINFO_HELP_ISINSTANCEOF_EXCEPTION,

CORINFO_HELP_BOX, // Fast box helper. Only possible exception is OutOfMemory
CORINFO_HELP_BOX_NULLABLE, // special form of boxing for Nullable<T>
CORINFO_HELP_UNBOX,
Expand Down
18 changes: 0 additions & 18 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -737,24 +737,6 @@ private bool Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, CORIN
Get_CORINFO_SIG_INFO(method, sig: &methodInfo->args, methodIL);
Get_CORINFO_SIG_INFO(methodIL.GetLocals(), &methodInfo->locals);

#if READYTORUN
if ((methodInfo->options & CorInfoOptions.CORINFO_GENERICS_CTXT_MASK) != 0)
{
foreach (var region in exceptionRegions)
{
if (region.Kind == ILExceptionRegionKind.Catch)
{
TypeDesc catchType = (TypeDesc)methodIL.GetObject(region.ClassToken);
if (catchType.IsCanonicalSubtype(CanonicalFormKind.Any))
{
methodInfo->options |= CorInfoOptions.CORINFO_GENERICS_CTXT_KEEP_ALIVE;
break;
}
}
}
}
#endif

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id,
case ReadyToRunHelper.CheckInstanceAny:
mangledName = "RhTypeCast_IsInstanceOf";
break;
case ReadyToRunHelper.IsInstanceOfException:
mangledName = "RhTypeCast_IsInstanceOfException";
break;
case ReadyToRunHelper.CheckCastInterface:
mangledName = "RhTypeCast_CheckCastInterface";
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,15 @@ private void StartImportingBasicBlock(BasicBlock basicBlock)
if (region.Kind == ILExceptionRegionKind.Filter)
MarkBasicBlock(_basicBlocks[region.FilterOffset]);

// Once https://github.com/dotnet/corert/issues/3460 is done, this should be deleted.
// Throwing InvalidProgram is not great, but we want to do *something* if this happens
// because doing nothing means problems at runtime. This is not worth piping a
// a new exception with a fancy message for.
if (region.Kind == ILExceptionRegionKind.Catch)
{
TypeDesc catchType = (TypeDesc)_methodIL.GetObject(region.ClassToken);
if (catchType.IsRuntimeDeterminedSubtype)
ThrowHelper.ThrowInvalidProgramException();
{
// For runtime determined Exception types we're going to emit a fake EH filter with isinst for this
// type inside with a runtime lookup
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
_dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandleForCasting, catchType), "EH filter");
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,9 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
id = ReadyToRunHelper.GetRuntimeTypeHandle;
break;

case CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOF_EXCEPTION:
id = ReadyToRunHelper.IsInstanceOfException;
break;
case CorInfoHelpFunc.CORINFO_HELP_BOX:
id = ReadyToRunHelper.Box;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,10 @@ private void ParseHelper(StringBuilder builder)
builder.Append("CHECK_INSTANCE_ANY");
break;

case ReadyToRunHelper.IsInstanceOfException:
builder.Append("SIMPLE_ISINSTANCE_OF");
break;

case ReadyToRunHelper.GenericGcStaticBase:
builder.Append("GENERIC_GC_STATIC_BASE");
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,9 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
id = ReadyToRunHelper.AreTypesEquivalent;
break;

case CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOF_EXCEPTION:
id = ReadyToRunHelper.IsInstanceOfException;
break;
case CorInfoHelpFunc.CORINFO_HELP_BOX:
id = ReadyToRunHelper.Box;
break;
Expand Down Expand Up @@ -906,13 +909,6 @@ private ObjectNode.ObjectData EncodeEHInfo()
var methodIL = (MethodIL)HandleToObject((IntPtr)_methodScope);
var type = (TypeDesc)methodIL.GetObject((int)clause.ClassTokenOrOffset);

// Once https://github.com/dotnet/corert/issues/3460 is done, this should be an assert.
// Throwing InvalidProgram is not great, but we want to do *something* if this happens
// because doing nothing means problems at runtime. This is not worth piping a
// a new exception with a fancy message for.
if (type.IsCanonicalSubtype(CanonicalFormKind.Any))
ThrowHelper.ThrowInvalidProgramException();

var typeSymbol = _compilation.NecessaryTypeSymbolIfPossible(type);

RelocType rel = (_compilation.NodeFactory.Target.IsWindows) ?
Expand Down
Loading