From efeeddbc342c68a01440314c546aeca570d51800 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 10:16:57 +0200 Subject: [PATCH 01/13] JIT: Do not report duplicate EH clauses to VM --- src/coreclr/jit/codegencommon.cpp | 365 +----------------------------- 1 file changed, 4 insertions(+), 361 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 89142c5cf5d38c..6cda78bbaed410 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -2343,95 +2343,16 @@ void CodeGen::genReportEH() unsigned XTnum; - bool isNativeAOT = compiler->IsTargetAbi(CORINFO_NATIVEAOT_ABI); - - unsigned EHCount = compiler->compHndBBtabCount; - - // Count duplicated clauses. This uses the same logic as below, where we actually generate them for reporting to the - // VM. - unsigned duplicateClauseCount = 0; - unsigned enclosingTryIndex; - - // Duplicate clauses are not used by NativeAOT ABI - if (compiler->UsesFunclets() && !isNativeAOT) - { - for (XTnum = 0; XTnum < compiler->compHndBBtabCount; XTnum++) - { - for (enclosingTryIndex = compiler->ehTrueEnclosingTryIndex(XTnum); // find the true enclosing try index, - // ignoring 'mutual protect' trys - enclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX; - enclosingTryIndex = compiler->ehGetEnclosingTryIndex(enclosingTryIndex)) - { - ++duplicateClauseCount; - } - } - EHCount += duplicateClauseCount; - } - - unsigned clonedFinallyCount = 0; - - // Duplicate clauses are not used by NativeAOT ABI - if (compiler->UsesFunclets() && compiler->UsesCallFinallyThunks() && !isNativeAOT) - { - // We don't keep track of how many cloned finally there are. So, go through and count. - // We do a quick pass first through the EH table to see if there are any try/finally - // clauses. If there aren't, we don't need to look for BBJ_CALLFINALLY. - - bool anyFinallys = false; - for (EHblkDsc* const HBtab : EHClauses(compiler)) - { - if (HBtab->HasFinallyHandler()) - { - anyFinallys = true; - break; - } - } - if (anyFinallys) - { - for (BasicBlock* const block : compiler->Blocks()) - { - if (block->KindIs(BBJ_CALLFINALLY)) - { - ++clonedFinallyCount; - } - } - - EHCount += clonedFinallyCount; - } - } - #ifdef DEBUG if (compiler->opts.dspEHTable) { - if (compiler->UsesFunclets()) - { - if (compiler->UsesCallFinallyThunks()) - { - printf("%d EH table entries, %d duplicate clauses, %d cloned finallys, %d total EH entries reported to " - "VM\n", - compiler->compHndBBtabCount, duplicateClauseCount, clonedFinallyCount, EHCount); - assert(compiler->compHndBBtabCount + duplicateClauseCount + clonedFinallyCount == EHCount); - } - else - { - printf("%d EH table entries, %d duplicate clauses, %d total EH entries reported to VM\n", - compiler->compHndBBtabCount, duplicateClauseCount, EHCount); - assert(compiler->compHndBBtabCount + duplicateClauseCount == EHCount); - } - } -#if defined(FEATURE_EH_WINDOWS_X86) - else - { - printf("%d EH table entries, %d total EH entries reported to VM\n", compiler->compHndBBtabCount, EHCount); - assert(compiler->compHndBBtabCount == EHCount); - } -#endif // FEATURE_EH_WINDOWS_X86 + printf("%d EH table entries\n", compiler->compHndBBtabCount); } #endif // DEBUG // Tell the VM how many EH clauses to expect. - compiler->eeSetEHcount(EHCount); - compiler->Metrics.EHClauseCount = (int)EHCount; + compiler->eeSetEHcount(compiler->compHndBBtabCount); + compiler->Metrics.EHClauseCount = (int)compiler->compHndBBtabCount; struct EHClauseInfo { @@ -2519,288 +2440,10 @@ void CodeGen::genReportEH() } } - assert(XTnum < EHCount); compiler->eeSetEHinfo(XTnum, &clause); } - // Now output duplicated clauses. - // - // If a funclet has been created by moving a handler out of a try region that it was originally nested - // within, then we need to report a "duplicate" clause representing the fact that an exception in that - // handler can be caught by the 'try' it has been moved out of. This is because the original 'try' region - // descriptor can only specify a single, contiguous protected range, but the funclet we've moved out is - // no longer contiguous with the original 'try' region. The new EH descriptor will have the same handler - // region as the enclosing try region's handler region. This is the sense in which it is duplicated: - // there is now a "duplicate" clause with the same handler region as another, but a different 'try' - // region. - // - // For example, consider this (capital letters represent an unknown code sequence, numbers identify a - // try or handler region): - // - // A - // try (1) { - // B - // try (2) { - // C - // } catch (3) { - // D - // } catch (4) { - // E - // } - // F - // } catch (5) { - // G - // } - // H - // - // Here, we have try region (1) BCDEF protected by catch (5) G, and region (2) C protected - // by catch (3) D and catch (4) E. Note that catch (4) E does *NOT* protect the code "D". - // This is an example of 'mutually protect' regions. First, we move handlers (3) and (4) - // to the end of the code. However, (3) and (4) are nested inside, and protected by, try (1). Again - // note that (3) is not nested inside (4), despite ebdEnclosingTryIndex indicating that. - // The code "D" and "E" won't be contiguous with the protected region for try (1) (which - // will, after moving catch (3) AND (4), be BCF). Thus, we need to add a new EH descriptor - // representing try (1) protecting the new funclets catch (3) and (4). - // The code will be generated as follows: - // - // ABCFH // "main" code - // D // funclet - // E // funclet - // G // funclet - // - // The EH regions are: - // - // C -> D - // C -> E - // BCF -> G - // D -> G // "duplicate" clause - // E -> G // "duplicate" clause - // - // Note that we actually need to generate one of these additional "duplicate" clauses for every - // region the funclet is nested in. Take this example: - // - // A - // try (1) { - // B - // try (2,3) { - // C - // try (4) { - // D - // try (5,6) { - // E - // } catch { - // F - // } catch { - // G - // } - // H - // } catch { - // I - // } - // J - // } catch { - // K - // } catch { - // L - // } - // M - // } catch { - // N - // } - // O - // - // When we pull out funclets, we get the following generated code: - // - // ABCDEHJMO // "main" function - // F // funclet - // G // funclet - // I // funclet - // K // funclet - // L // funclet - // N // funclet - // - // And the EH regions we report to the VM are (in order; main clauses - // first in most-to-least nested order, funclets ("duplicated clauses") - // last, in most-to-least nested) are: - // - // E -> F - // E -> G - // DEH -> I - // CDEHJ -> K - // CDEHJ -> L - // BCDEHJM -> N - // F -> I // funclet clause #1 for F - // F -> K // funclet clause #2 for F - // F -> L // funclet clause #3 for F - // F -> N // funclet clause #4 for F - // G -> I // funclet clause #1 for G - // G -> K // funclet clause #2 for G - // G -> L // funclet clause #3 for G - // G -> N // funclet clause #4 for G - // I -> K // funclet clause #1 for I - // I -> L // funclet clause #2 for I - // I -> N // funclet clause #3 for I - // K -> N // funclet clause #1 for K - // L -> N // funclet clause #1 for L - // - // So whereas the IL had 6 EH clauses, we need to report 19 EH clauses to the VM. - // Note that due to the nature of 'mutually protect' clauses, it would be incorrect - // to add a clause "F -> G" because F is NOT protected by G, but we still have - // both "F -> K" and "F -> L" because F IS protected by both of those handlers. - // - // The overall ordering of the clauses is still the same most-to-least nesting - // after front-to-back start offset. Because we place the funclets at the end - // these new clauses should also go at the end by this ordering. - // - - if (duplicateClauseCount > 0) - { - unsigned reportedDuplicateClauseCount = 0; // How many duplicated clauses have we reported? - unsigned XTnum2; - EHblkDsc* HBtab; - for (XTnum2 = 0, HBtab = compiler->compHndBBtab; XTnum2 < compiler->compHndBBtabCount; XTnum2++, HBtab++) - { - unsigned enclosingTryIndex; - - EHblkDsc* fletTab = compiler->ehGetDsc(XTnum2); - - for (enclosingTryIndex = compiler->ehTrueEnclosingTryIndex(XTnum2); // find the true enclosing try index, - // ignoring 'mutual protect' trys - enclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX; - enclosingTryIndex = compiler->ehGetEnclosingTryIndex(enclosingTryIndex)) - { - // The funclet we moved out is nested in a try region, so create a new EH descriptor for the funclet - // that will have the enclosing try protecting the funclet. - - noway_assert(XTnum2 < enclosingTryIndex); // the enclosing region must be less nested, and hence have a - // greater EH table index - - EHblkDsc* encTab = compiler->ehGetDsc(enclosingTryIndex); - - // The try region is the handler of the funclet. Note that for filters, we don't protect the - // filter region, only the filter handler region. This is because exceptions in filters never - // escape; the VM swallows them. - - BasicBlock* bbTryBeg = fletTab->ebdHndBeg; - BasicBlock* bbTryLast = fletTab->ebdHndLast; - - BasicBlock* bbHndBeg = encTab->ebdHndBeg; // The handler region is the same as the enclosing try - BasicBlock* bbHndLast = encTab->ebdHndLast; - - UNATIVE_OFFSET tryBeg, tryEnd, hndBeg, hndEnd, hndTyp; - - tryBeg = compiler->ehCodeOffset(bbTryBeg); - hndBeg = compiler->ehCodeOffset(bbHndBeg); - - tryEnd = (bbTryLast == compiler->fgLastBB) ? compiler->info.compNativeCodeSize - : compiler->ehCodeOffset(bbTryLast->Next()); - hndEnd = (bbHndLast == compiler->fgLastBB) ? compiler->info.compNativeCodeSize - : compiler->ehCodeOffset(bbHndLast->Next()); - - if (encTab->HasFilter()) - { - hndTyp = compiler->ehCodeOffset(encTab->ebdFilter); - } - else - { - hndTyp = encTab->ebdTyp; - } - - CORINFO_EH_CLAUSE_FLAGS flags = ToCORINFO_EH_CLAUSE_FLAGS(encTab->ebdHandlerType); - - // Tell the VM this is an extra clause caused by moving funclets out of line. - flags = (CORINFO_EH_CLAUSE_FLAGS)(flags | CORINFO_EH_CLAUSE_DUPLICATE); - - // Note that the JIT-EE interface reuses the CORINFO_EH_CLAUSE type, even though the names of - // the fields aren't really accurate. For example, we set "TryLength" to the offset of the - // instruction immediately after the 'try' body. So, it really could be more accurately named - // "TryEndOffset". - - CORINFO_EH_CLAUSE clause; - clause.ClassToken = hndTyp; /* filter offset is passed back here for filter-based exception handlers */ - clause.Flags = flags; - clause.TryOffset = tryBeg; - clause.TryLength = tryEnd; - clause.HandlerOffset = hndBeg; - clause.HandlerLength = hndEnd; - - assert(XTnum < EHCount); - - // Tell the VM about this EH clause (a duplicated clause). - compiler->eeSetEHinfo(XTnum, &clause); - - ++XTnum; - ++reportedDuplicateClauseCount; - -#ifndef DEBUG - if (duplicateClauseCount == reportedDuplicateClauseCount) - { - break; // we've reported all of them; no need to continue looking - } -#endif // !DEBUG - - } // for each 'true' enclosing 'try' - } // for each EH table entry - - assert(duplicateClauseCount == reportedDuplicateClauseCount); - } // if (duplicateClauseCount > 0) - - if (clonedFinallyCount > 0) - { - unsigned reportedClonedFinallyCount = 0; - for (BasicBlock* const block : compiler->Blocks()) - { - if (block->KindIs(BBJ_CALLFINALLY)) - { - UNATIVE_OFFSET hndBeg, hndEnd; - - hndBeg = compiler->ehCodeOffset(block); - - // How big is it? The BBJ_CALLFINALLYRET has a null bbEmitCookie! Look for the block after, which must - // be a label or jump target, since the BBJ_CALLFINALLY doesn't fall through. - BasicBlock* bbLabel = block->Next(); - if (block->isBBCallFinallyPair()) - { - bbLabel = bbLabel->Next(); // skip the BBJ_CALLFINALLYRET - } - if (bbLabel == nullptr) - { - hndEnd = compiler->info.compNativeCodeSize; - } - else - { - hndEnd = compiler->ehCodeOffset(bbLabel); - } - - CORINFO_EH_CLAUSE clause; - clause.ClassToken = 0; // unused - clause.Flags = (CORINFO_EH_CLAUSE_FLAGS)(CORINFO_EH_CLAUSE_FINALLY | CORINFO_EH_CLAUSE_DUPLICATE); - clause.TryOffset = hndBeg; - clause.TryLength = hndBeg; - clause.HandlerOffset = hndBeg; - clause.HandlerLength = hndEnd; - - assert(XTnum < EHCount); - - // Tell the VM about this EH clause (a cloned finally clause). - compiler->eeSetEHinfo(XTnum, &clause); - - ++XTnum; - ++reportedClonedFinallyCount; - -#ifndef DEBUG - if (clonedFinallyCount == reportedClonedFinallyCount) - { - break; // we're done; no need to keep looking - } -#endif // !DEBUG - } // block is BBJ_CALLFINALLY - } // for each block - - assert(clonedFinallyCount == reportedClonedFinallyCount); - } // if (clonedFinallyCount > 0) - - assert(XTnum == EHCount); + assert(XTnum == compiler->compHndBBtabCount); } //---------------------------------------------------------------------- From 07c0a5aac1378a363c901efc9f5e7bebf172f454 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 19:56:26 +0200 Subject: [PATCH 02/13] Remove the flags and their usage too --- src/coreclr/debug/daccess/request.cpp | 1 - src/coreclr/inc/corhdr.h | 1 - src/coreclr/inc/corinfo.h | 1 - src/coreclr/inc/dacprivate.h | 2 +- src/coreclr/inc/eexcp.h | 29 ------------------- src/coreclr/jit/jiteh.cpp | 14 ++------- .../tools/Common/JitInterface/CorInfoTypes.cs | 1 - .../EHInfo.cs | 6 ---- src/coreclr/vm/codeman.cpp | 7 ----- src/coreclr/vm/exceptionhandling.cpp | 12 ++------ 10 files changed, 6 insertions(+), 68 deletions(-) diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index b9824ee131e5e7..96e38cc41b525f 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -3458,7 +3458,6 @@ ClrDataAccess::TraverseEHInfo(CLRDATA_ADDRESS ip, DUMPEHINFO pFunc, LPVOID token deh.tryEndOffset = EHClause.TryEndPC; deh.handlerStartOffset = EHClause.HandlerStartPC; deh.handlerEndOffset = EHClause.HandlerEndPC; - deh.isDuplicateClause = IsDuplicateClause(&EHClause); if (!(pFunc)(i, EHCount, &deh, token)) { diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h index 9f79288b2b4fc5..3e54bdae54b0bf 100644 --- a/src/coreclr/inc/corhdr.h +++ b/src/coreclr/inc/corhdr.h @@ -1141,7 +1141,6 @@ typedef enum CorExceptionFlag // definitions for the Flags COR_ILEXCEPTION_CLAUSE_FILTER = 0x0001, // If this bit is on, then this EH entry is for a filter COR_ILEXCEPTION_CLAUSE_FINALLY = 0x0002, // This clause is a finally clause COR_ILEXCEPTION_CLAUSE_FAULT = 0x0004, // Fault clause (finally that is called on exception only) - COR_ILEXCEPTION_CLAUSE_DUPLICATED = 0x0008, // duplicated clause. This clause was duplicated to a funclet which was pulled out of line } CorExceptionFlag; /***********************************/ diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index adfa01c89ef6c1..7e0073fb391a0b 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -819,7 +819,6 @@ enum CORINFO_EH_CLAUSE_FLAGS CORINFO_EH_CLAUSE_FILTER = 0x0001, // If this bit is on, then this EH entry is for a filter CORINFO_EH_CLAUSE_FINALLY = 0x0002, // This clause is a finally clause CORINFO_EH_CLAUSE_FAULT = 0x0004, // This clause is a fault clause - CORINFO_EH_CLAUSE_DUPLICATE = 0x0008, // Duplicated clause. This clause was duplicated to a funclet which was pulled out of line CORINFO_EH_CLAUSE_SAMETRY = 0x0010, // This clause covers same try block as the previous one }; diff --git a/src/coreclr/inc/dacprivate.h b/src/coreclr/inc/dacprivate.h index 62821f71395afc..1ce9b41db53811 100644 --- a/src/coreclr/inc/dacprivate.h +++ b/src/coreclr/inc/dacprivate.h @@ -967,7 +967,7 @@ struct MSLAYOUT DACEHInfo CLRDATA_ADDRESS tryEndOffset = 0; CLRDATA_ADDRESS handlerStartOffset = 0; CLRDATA_ADDRESS handlerEndOffset = 0; - BOOL isDuplicateClause = FALSE; + BOOL isDuplicateClause = FALSE; // unused; removed in R2R version 13 CLRDATA_ADDRESS filterOffset = 0; // valid when clauseType is EHFilter BOOL isCatchAllHandler = FALSE; // valid when clauseType is EHTyped CLRDATA_ADDRESS moduleAddr = 0; // when == 0 mtCatch contains a MethodTable, when != 0 tokCatch contains a type token diff --git a/src/coreclr/inc/eexcp.h b/src/coreclr/inc/eexcp.h index fb7bccbe073469..5c470b78dbf527 100644 --- a/src/coreclr/inc/eexcp.h +++ b/src/coreclr/inc/eexcp.h @@ -112,34 +112,5 @@ inline BOOL IsTypedHandler(EE_ILEXCEPTION_CLAUSE *EHClause) return ! (IsFilterHandler(EHClause) || IsFaultOrFinally(EHClause)); } -inline BOOL IsDuplicateClause(EE_ILEXCEPTION_CLAUSE* pEHClause) -{ - return pEHClause->Flags & COR_ILEXCEPTION_CLAUSE_DUPLICATED; -} - -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) -// Finally is the only EH construct that can be part of the execution as being fall-through. -// -// "Cloned" finally is a construct that represents a finally block that is used as -// fall through for normal try-block execution. Such a "cloned" finally will: -// -// 1) Have its try-clause's Start and End PC the same as its handler's start PC (i.e. will have -// zero length try block), AND -// 2) Is marked duplicate -// -// Because of their fall-through nature, JIT guarantees that only finally constructs can be cloned, -// and not catch or fault (since they cannot be fallen through but are invoked as funclets). -// -// The cloned finally construct is also used to mark "call to finally" thunks that are not within -// the EH region protected by the finally, and also not within the enclosing region. This is done -// to prevent ThreadAbortException from creating an infinite loop of calling the same finally. -inline BOOL IsClonedFinally(EE_ILEXCEPTION_CLAUSE* pEHClause) -{ - return ((pEHClause->TryStartPC == pEHClause->TryEndPC) && - (pEHClause->TryStartPC == pEHClause->HandlerStartPC) && - IsFinally(pEHClause) && IsDuplicateClause(pEHClause)); -} -#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - #endif // __eexcp_h__ diff --git a/src/coreclr/jit/jiteh.cpp b/src/coreclr/jit/jiteh.cpp index f3ae995d2df0c9..7c974ba9c5d296 100644 --- a/src/coreclr/jit/jiteh.cpp +++ b/src/coreclr/jit/jiteh.cpp @@ -3193,7 +3193,7 @@ void Compiler::dispOutgoingEHClause(unsigned num, const CORINFO_EH_CLAUSE& claus // Note: the flags field is kind of weird. It should be compared for equality // to determine the type of clause, even though it looks like a bitfield. In // Particular, CORINFO_EH_CLAUSE_NONE is zero, so you can "&" to check it. - // You do need to mask off the bits, though, because CORINFO_EH_CLAUSE_DUPLICATE + // You do need to mask off the bits, though, because CORINFO_EH_CLAUSE_SAMETRY // is and'ed in. const DWORD CORINFO_EH_CLAUSE_TYPE_MASK = 0x7; switch (clause.Flags & CORINFO_EH_CLAUSE_TYPE_MASK) @@ -3226,17 +3226,7 @@ void Compiler::dispOutgoingEHClause(unsigned num, const CORINFO_EH_CLAUSE& claus break; } - if ((clause.TryOffset == clause.TryLength) && (clause.TryOffset == clause.HandlerOffset) && - ((clause.Flags & (CORINFO_EH_CLAUSE_DUPLICATE | CORINFO_EH_CLAUSE_FINALLY)) == - (CORINFO_EH_CLAUSE_DUPLICATE | CORINFO_EH_CLAUSE_FINALLY))) - { - printf(" cloned finally"); - } - else if (clause.Flags & CORINFO_EH_CLAUSE_DUPLICATE) - { - printf(" duplicated"); - } - else if (clause.Flags & CORINFO_EH_CLAUSE_SAMETRY) + if (clause.Flags & CORINFO_EH_CLAUSE_SAMETRY) { printf(" same try"); } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 124df22bdc7864..34e3e6dcd9125e 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -620,7 +620,6 @@ public enum CORINFO_EH_CLAUSE_FLAGS CORINFO_EH_CLAUSE_FILTER = 0x0001, // If this bit is on, then this EH entry is for a filter CORINFO_EH_CLAUSE_FINALLY = 0x0002, // This clause is a finally clause CORINFO_EH_CLAUSE_FAULT = 0x0004, // This clause is a fault clause - CORINFO_EH_CLAUSE_DUPLICATED = 0x0008, // Duplicated clause. This clause was duplicated to a funclet which was pulled out of line CORINFO_EH_CLAUSE_SAMETRY = 0x0010, // This clause covers same try block as the previous one. (Used by NativeAOT ABI.) }; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/EHInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/EHInfo.cs index 0fc90302cc4cac..2631441514848f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/EHInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/EHInfo.cs @@ -25,7 +25,6 @@ public enum CorExceptionFlag COR_ILEXCEPTION_CLAUSE_FILTER = 0x0001, // If this bit is on, then this EH entry is for a filter COR_ILEXCEPTION_CLAUSE_FINALLY = 0x0002, // This clause is a finally clause COR_ILEXCEPTION_CLAUSE_FAULT = 0x0004, // Fault clause (finally that is called on exception only) - COR_ILEXCEPTION_CLAUSE_DUPLICATED = 0x0008, // duplicated clause. This clause was duplicated to a funclet which was pulled out of line COR_ILEXCEPTION_CLAUSE_SAMETRY = 0x0010, // This clause covers same try block as the previous one COR_ILEXCEPTION_CLAUSE_KIND_MASK = COR_ILEXCEPTION_CLAUSE_FILTER | COR_ILEXCEPTION_CLAUSE_FINALLY | COR_ILEXCEPTION_CLAUSE_FAULT, @@ -151,11 +150,6 @@ public void WriteTo(TextWriter writer, int methodRva, bool dumpRva) throw new NotImplementedException(Flags.ToString()); } - if ((Flags & CorExceptionFlag.COR_ILEXCEPTION_CLAUSE_DUPLICATED) != (CorExceptionFlag)0) - { - writer.Write(" DUPLICATED"); - } - if ((Flags & CorExceptionFlag.COR_ILEXCEPTION_CLAUSE_SAMETRY) != (CorExceptionFlag)0) { writer.Write(" SAMETRY"); diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index c1c1bfe60441c6..7093f102e6d00b 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -1149,13 +1149,6 @@ BOOL IJitManager::IsFilterFunclet(EECodeInfo * pCodeInfo) { GetNextEHClause(&pEnumState, &EHClause); - // Duplicate clauses are always listed at the end, so when we hit a duplicate clause, - // we have already visited all of the normal clauses. - if (IsDuplicateClause(&EHClause)) - { - break; - } - if (IsFilterHandler(&EHClause)) { if (EHClause.FilterOffset == funcletStartOffset) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 48ea83e763023a..1a8b02aaea3570 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -870,7 +870,7 @@ void DumpClauses(IJitManager* pJitMan, const METHODTOKEN& MethToken, UINT_PTR uM EE_ILEXCEPTION_CLAUSE EHClause; pJitMan->GetNextEHClause(&EnumState, &EHClause); - EH_LOG((LL_INFO1000, " | %s clause [%x, %x], handler: [%x, %x] %s", + EH_LOG((LL_INFO1000, " | %s clause [%x, %x], handler: [%x, %x]", (IsFault(&EHClause) ? "fault" : (IsFinally(&EHClause) ? "finally" : (IsFilterHandler(&EHClause) ? "filter" : @@ -878,8 +878,7 @@ void DumpClauses(IJitManager* pJitMan, const METHODTOKEN& MethToken, UINT_PTR uM EHClause.TryStartPC , // + uMethodStartPC, EHClause.TryEndPC , // + uMethodStartPC, EHClause.HandlerStartPC , // + uMethodStartPC, - EHClause.HandlerEndPC , // + uMethodStartPC - (IsDuplicateClause(&EHClause) ? "[duplicate]" : "") + EHClause.HandlerEndPC // + uMethodStartPC )); if (IsFilterHandler(&EHClause)) @@ -3517,12 +3516,7 @@ extern "C" CLR_BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClau pEHClause->_tryStartOffset, pEHClause->_tryEndOffset, pEHClause->_isSameTry ? ", isSameTry" : "", pEHClause->_handlerAddress)); - if (flags & COR_ILEXCEPTION_CLAUSE_DUPLICATED) - { - EH_LOG((LL_INFO100, " duplicated clause\n")); - result = FALSE; - } - else if (flags == COR_ILEXCEPTION_CLAUSE_NONE) + if (flags == COR_ILEXCEPTION_CLAUSE_NONE) { pEHClause->_clauseKind = RH_EH_CLAUSE_TYPED; pEHClause->_pTargetType = pJitMan->ResolveEHClause(&EHClause, &pFrameIter->m_crawl).AsMethodTable(); From fcfe67744e7777a2397db5cef0037feec8449e1e Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 19:58:45 +0200 Subject: [PATCH 03/13] Fix comment --- src/coreclr/jit/jiteh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/jiteh.cpp b/src/coreclr/jit/jiteh.cpp index 7c974ba9c5d296..ca369e95a69129 100644 --- a/src/coreclr/jit/jiteh.cpp +++ b/src/coreclr/jit/jiteh.cpp @@ -3192,7 +3192,7 @@ void Compiler::dispOutgoingEHClause(unsigned num, const CORINFO_EH_CLAUSE& claus // Note: the flags field is kind of weird. It should be compared for equality // to determine the type of clause, even though it looks like a bitfield. In - // Particular, CORINFO_EH_CLAUSE_NONE is zero, so you can "&" to check it. + // particular, CORINFO_EH_CLAUSE_NONE is zero, so you cannot "&" to check it. // You do need to mask off the bits, though, because CORINFO_EH_CLAUSE_SAMETRY // is and'ed in. const DWORD CORINFO_EH_CLAUSE_TYPE_MASK = 0x7; From 88b00cb742e18e7358f24578c0da1808120037a8 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 22 Apr 2025 11:11:00 -0700 Subject: [PATCH 04/13] Update src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs Co-authored-by: Filip Navara --- src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 34e3e6dcd9125e..ea5ff67d9d8057 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -620,7 +620,7 @@ public enum CORINFO_EH_CLAUSE_FLAGS CORINFO_EH_CLAUSE_FILTER = 0x0001, // If this bit is on, then this EH entry is for a filter CORINFO_EH_CLAUSE_FINALLY = 0x0002, // This clause is a finally clause CORINFO_EH_CLAUSE_FAULT = 0x0004, // This clause is a fault clause - CORINFO_EH_CLAUSE_SAMETRY = 0x0010, // This clause covers same try block as the previous one. (Used by NativeAOT ABI.) + CORINFO_EH_CLAUSE_SAMETRY = 0x0010, // This clause covers same try block as the previous one. }; public struct CORINFO_EH_CLAUSE From 118e70fcc82c8d867ce38cd3e98d91040c7f0d1a Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 20:17:25 +0200 Subject: [PATCH 05/13] Address PR feedback --- src/coreclr/inc/corinfo.h | 1 + src/coreclr/inc/dacprivate.h | 2 +- .../tools/aot/ILCompiler.Reflection.ReadyToRun/EHInfo.cs | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 7e0073fb391a0b..489e12cf9444b3 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -819,6 +819,7 @@ enum CORINFO_EH_CLAUSE_FLAGS CORINFO_EH_CLAUSE_FILTER = 0x0001, // If this bit is on, then this EH entry is for a filter CORINFO_EH_CLAUSE_FINALLY = 0x0002, // This clause is a finally clause CORINFO_EH_CLAUSE_FAULT = 0x0004, // This clause is a fault clause + // UNUSED = 0x0008, CORINFO_EH_CLAUSE_SAMETRY = 0x0010, // This clause covers same try block as the previous one }; diff --git a/src/coreclr/inc/dacprivate.h b/src/coreclr/inc/dacprivate.h index 1ce9b41db53811..9e23b4df7f9484 100644 --- a/src/coreclr/inc/dacprivate.h +++ b/src/coreclr/inc/dacprivate.h @@ -967,7 +967,7 @@ struct MSLAYOUT DACEHInfo CLRDATA_ADDRESS tryEndOffset = 0; CLRDATA_ADDRESS handlerStartOffset = 0; CLRDATA_ADDRESS handlerEndOffset = 0; - BOOL isDuplicateClause = FALSE; // unused; removed in R2R version 13 + BOOL isDuplicateClause = FALSE; // unused CLRDATA_ADDRESS filterOffset = 0; // valid when clauseType is EHFilter BOOL isCatchAllHandler = FALSE; // valid when clauseType is EHTyped CLRDATA_ADDRESS moduleAddr = 0; // when == 0 mtCatch contains a MethodTable, when != 0 tokCatch contains a type token diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/EHInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/EHInfo.cs index 2631441514848f..0fc90302cc4cac 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/EHInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/EHInfo.cs @@ -25,6 +25,7 @@ public enum CorExceptionFlag COR_ILEXCEPTION_CLAUSE_FILTER = 0x0001, // If this bit is on, then this EH entry is for a filter COR_ILEXCEPTION_CLAUSE_FINALLY = 0x0002, // This clause is a finally clause COR_ILEXCEPTION_CLAUSE_FAULT = 0x0004, // Fault clause (finally that is called on exception only) + COR_ILEXCEPTION_CLAUSE_DUPLICATED = 0x0008, // duplicated clause. This clause was duplicated to a funclet which was pulled out of line COR_ILEXCEPTION_CLAUSE_SAMETRY = 0x0010, // This clause covers same try block as the previous one COR_ILEXCEPTION_CLAUSE_KIND_MASK = COR_ILEXCEPTION_CLAUSE_FILTER | COR_ILEXCEPTION_CLAUSE_FINALLY | COR_ILEXCEPTION_CLAUSE_FAULT, @@ -150,6 +151,11 @@ public void WriteTo(TextWriter writer, int methodRva, bool dumpRva) throw new NotImplementedException(Flags.ToString()); } + if ((Flags & CorExceptionFlag.COR_ILEXCEPTION_CLAUSE_DUPLICATED) != (CorExceptionFlag)0) + { + writer.Write(" DUPLICATED"); + } + if ((Flags & CorExceptionFlag.COR_ILEXCEPTION_CLAUSE_SAMETRY) != (CorExceptionFlag)0) { writer.Write(" SAMETRY"); From dbcfb37826d885b79133fce483254c455c24c139 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 20:21:26 +0200 Subject: [PATCH 06/13] Remove doc for Duplicated Clauses --- docs/design/coreclr/botr/clr-abi.md | 79 ----------------------------- 1 file changed, 79 deletions(-) diff --git a/docs/design/coreclr/botr/clr-abi.md b/docs/design/coreclr/botr/clr-abi.md index 0875c152259e14..15edf189536ef0 100644 --- a/docs/design/coreclr/botr/clr-abi.md +++ b/docs/design/coreclr/botr/clr-abi.md @@ -499,85 +499,6 @@ When the inner "throw new UserException4" is executed, the exception handling fi Filters are invoked in the 1st pass of EH processing and as such execution might resume back at the faulting address, or in the filter-handler, or someplace else. Because the VM must allow GC's to occur during and after a filter invocation, but before the EH subsystem knows where it will resume, we need to keep everything alive at both the faulting address **and** within the filter. This is accomplished by 3 means: (1) the VM's stackwalker and GCInfoDecoder report as live both the filter frame and its corresponding parent frame, (2) the JIT encodes all stack slots that are live within the filter as being pinned, and (3) the JIT reports as live (and possible zero-initializes) anything live-out of the filter. Because of (1) it is likely that a stack variable that is live within the filter and the try body will be double reported. During the mark phase of the GC double reporting is not a problem. The problem only arises if the object is relocated: if the same location is reported twice, the GC will try to relocate the address stored at that location twice. Thus we prevent the object from being relocated by pinning it, which leads us to why we must do (2). (3) is done so that after the filter returns, we can still safely incur a GC before executing the filter-handler or any outer handler within the same frame. For the same reason, control must exit a filter region via its final block (in other words, a filter region must terminate with the instruction that leaves the filter region, and the program may not exit the filter region via other paths). -## Duplicated Clauses - -Duplicated clauses are a special set of entries in the EH tables to assist the VM. Specifically, if handler 'A' is also protected by an outer EH clause 'B', then the JIT must emit a duplicated clause, a duplicate of 'B', that marks the whole handler 'A' (which is now lexically disjoint for the range of code for the corresponding try body 'A') as being protected by the handler for 'B'. - -Duplicated clauses are not needed for x86 and for NativeAOT ABI. - -During exception dispatch the VM uses these duplicated clauses to know when to skip any frames between the handler and its parent function. After skipping to the parent function, due to a duplicated clause, the VM searches for a regular/non-duplicate clause in the parent function. The order of duplicated clauses is important. They should appear after all of the main function clauses. They should still follow the normal sorting rules (inner-to-outer, top-to-bottom), but because the try-start/try-end will all be the same for a given handler, they should maintain the ordering, regarding inner-to-outer, as the corresponding original clause. - -Example: - -``` -A: try { -B: ... -C: try { -D: ... -E: try { -F: ... -G: } -H: catch { -I: ... -J: } -K: ... -L: } -M: finally { -N: ... -O: } -P: ... -Q: } -R: catch { -S: ... -T: } -``` - -In MSIL this would generate 3 EH clauses: - -``` -.try E-G catch H-J -.try C-L finally M-O -.try A-Q catch R-T -``` - -The native code would be laid out as follows (the order of the handlers is irrelevant except they are after the main method body) with their corresponding (fake) native offsets: - -``` -A: -> 1 -B: -> 2 -C: -> 3 -D: -> 4 -E: -> 5 -F: -> 6 -G: -> 7 -K: -> 8 -L: -> 9 -P: -> 10 -Q: -> 11 -H: -> 12 -I: -> 13 -J: -> 14 -M: -> 15 -N: -> 16 -O: -> 17 -R: -> 18 -S: -> 19 -T: -> 20 -``` - -The native EH clauses would be listed as follows: - -``` -1. .try 5-7 catch 12-14 (top-most & inner-most first) -2. .try 3-9 finally 15-17 (top-most & next inner-most) -3. .try 1-11 catch 18-20 (top-most & outer-most) -4. .try 12-14 finally 15-17 duplicated (inner-most because clause 2 is inside clause 3, top-most because handler H-J is first) -5. .try 12-14 catch 18-20 duplicated -6. .try 15-17 catch 18-20 -``` - -If the handlers were in a different order, then clause 6 might appear before clauses 4 and 5, but never in between. - ## Clauses covering the same try region Several consecutive clauses may cover the same `try` block. A clause covering the same region as the previous one is marked by the `COR_ILEXCEPTION_CLAUSE_SAMETRY` flag. When exception ex1 is thrown while running handler for another exception ex2 and the exception ex2 escapes the ex1's handler frame, this enables the runtime to skip clauses that cover the same `try` block as the clause that handled the ex1. From aedc4dc078fa2c383ccf7bd39c13fcf2d09a6e34 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 20:26:55 +0200 Subject: [PATCH 07/13] Update JIT-EE GUID --- src/coreclr/inc/jiteeversionguid.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 866c77a6e9990e..364918107d1317 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 72704ed3-9bc5-4e07-bfe9-a4e091812ba8 */ - 0x72704ed3, - 0x9bc5, - 0x4e07, - {0xbf, 0xe9, 0xa4, 0xe0, 0x91, 0x81, 0x2b, 0xa8} +constexpr GUID JITEEVersionIdentifier = { /* caad5bcc-1f14-4f63-81e6-61e7e88535d0 */ + 0xcaad5bcc, + 0x1f14, + 0x4f63, + {0x81, 0xe6, 0x61, 0xe7, 0xe8, 0x85, 0x35, 0xd0} }; #endif // JIT_EE_VERSIONING_GUID_H From 364b2024ce8bb6211da410cd6d568de754991514 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 21:32:52 +0200 Subject: [PATCH 08/13] Update doc for cloned finallys --- docs/design/coreclr/botr/clr-abi.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/design/coreclr/botr/clr-abi.md b/docs/design/coreclr/botr/clr-abi.md index 15edf189536ef0..f225b7897caaea 100644 --- a/docs/design/coreclr/botr/clr-abi.md +++ b/docs/design/coreclr/botr/clr-abi.md @@ -200,11 +200,7 @@ For Windows/x86 on NativeAOT and Linux/x86, funclets are used just like on other ## Cloned finallys -JIT64 attempts to speed the normal control flow by 'inlining' a called finally along the 'normal' control flow (i.e., leaving a try body in a non-exceptional manner via C# fall-through). Because the VM semantics for non-rude Thread.Abort dictate that handlers will not be aborted, the JIT must mark these 'inlined' finally bodies. These show up as special entries at the end of the EH tables and are marked with `COR_ILEXCEPTION_CLAUSE_FINALLY | COR_ILEXCEPTION_CLAUSE_DUPLICATED`, and the try_start, try_end, and handler_start are all the same: the start of the cloned finally. - -RyuJit also implements finally cloning, for all supported architectures. However, the implementation does not yet handle the thread abort case; cloned finally bodies are not guaranteed to remain intact and are not reported to the runtime. Because of this, finally cloning is disabled for VMs that support thread abort (desktop clr). - -JIT32 does not implement finally cloning. +RyuJIT attempts to speed the normal control flow by 'inlining' a called finally along the 'normal' control flow (i.e., leaving a try body in a non-exceptional manner via C# fall-through). This optimization is supported on all architectures. ## Invoking Finallys/Non-local exits From 70c11274136f6ac0959d0619d4f112346515d224 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 22:55:27 +0200 Subject: [PATCH 09/13] Delete COR_ILEXCEPTION_CLAUSE_OFFSETLEN and COR_ILEXCEPTION_CLAUSE_DEPRECATED --- src/coreclr/inc/corhdr.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h index 3e54bdae54b0bf..4489282e264277 100644 --- a/src/coreclr/inc/corhdr.h +++ b/src/coreclr/inc/corhdr.h @@ -1136,8 +1136,6 @@ typedef struct IMAGE_COR_ILMETHOD_SECT_FAT typedef enum CorExceptionFlag // definitions for the Flags field below (for both big and small) { COR_ILEXCEPTION_CLAUSE_NONE, // This is a typed handler - COR_ILEXCEPTION_CLAUSE_OFFSETLEN = 0x0000, // Deprecated - COR_ILEXCEPTION_CLAUSE_DEPRECATED = 0x0000, // Deprecated COR_ILEXCEPTION_CLAUSE_FILTER = 0x0001, // If this bit is on, then this EH entry is for a filter COR_ILEXCEPTION_CLAUSE_FINALLY = 0x0002, // This clause is a finally clause COR_ILEXCEPTION_CLAUSE_FAULT = 0x0004, // Fault clause (finally that is called on exception only) From c10c07a4acf8c2f804f5e4ba03e91f483600e673 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 23:03:03 +0200 Subject: [PATCH 10/13] Add comment --- src/coreclr/inc/corinfo.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 489e12cf9444b3..16018bb403117e 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -813,6 +813,7 @@ enum CORINFO_ACCESS_FLAGS }; // These are the flags set on an CORINFO_EH_CLAUSE +// Keep values in sync with COR_ILEXCEPTION_CLAUSE flags enum CORINFO_EH_CLAUSE_FLAGS { CORINFO_EH_CLAUSE_NONE = 0, From 069c189d1b819f8bd23bc2967502020eff7953f7 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 23:03:51 +0200 Subject: [PATCH 11/13] Update src/coreclr/inc/corhdr.h Co-authored-by: Jan Kotas --- src/coreclr/inc/corhdr.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h index 4489282e264277..1e47d7557d319e 100644 --- a/src/coreclr/inc/corhdr.h +++ b/src/coreclr/inc/corhdr.h @@ -1139,6 +1139,8 @@ typedef enum CorExceptionFlag // definitions for the Flags COR_ILEXCEPTION_CLAUSE_FILTER = 0x0001, // If this bit is on, then this EH entry is for a filter COR_ILEXCEPTION_CLAUSE_FINALLY = 0x0002, // This clause is a finally clause COR_ILEXCEPTION_CLAUSE_FAULT = 0x0004, // Fault clause (finally that is called on exception only) + COR_ILEXCEPTION_CLAUSE_DUPLICATED = 0x0008, // Deprecated: Duplicated clause. This clause was duplicated to a funclet which was pulled out of line + COR_ILEXCEPTION_CLAUSE_SAMETRY = 0x0010, // This clause covers same try block as the previous one } CorExceptionFlag; /***********************************/ From d5f43ab7ada230fd35664100fc98ea11cac676d3 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 23:05:28 +0200 Subject: [PATCH 12/13] Replace magic value with COR_ILEXCEPTION_CLAUSE_SAMETRY --- src/coreclr/vm/exceptionhandling.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 1a8b02aaea3570..34307e039a8947 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -3507,7 +3507,7 @@ extern "C" CLR_BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClau pEHClause->_handlerAddress = (BYTE*)pJitMan->GetCodeAddressForRelOffset(MethToken, EHClause.HandlerStartPC); result = TRUE; - pEHClause->_isSameTry = (EHClause.Flags & 0x10) != 0; // CORINFO_EH_CLAUSE_SAMETRY + pEHClause->_isSameTry = (EHClause.Flags & COR_ILEXCEPTION_CLAUSE_SAMETRY) != 0; // Clear special flags - like COR_ILEXCEPTION_CLAUSE_CACHED_CLASS ULONG flags = (CorExceptionFlag)(EHClause.Flags & 0x0f); From 424aa1bbb53d95eaceb2adbb69473799a8a83aca Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 22 Apr 2025 23:07:18 +0200 Subject: [PATCH 13/13] Fix build --- src/coreclr/ilasm/assembler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/ilasm/assembler.cpp b/src/coreclr/ilasm/assembler.cpp index 633daec4510877..e347fc2f69482f 100644 --- a/src/coreclr/ilasm/assembler.cpp +++ b/src/coreclr/ilasm/assembler.cpp @@ -1129,7 +1129,7 @@ void Assembler::AddException(DWORD pcStart, DWORD pcEnd, DWORD pcHandler, DWORD clause->SetHandlerLength(pcHandlerTo - pcHandler); clause->SetClassToken(crException); - int flags = COR_ILEXCEPTION_CLAUSE_OFFSETLEN; + int flags = 0; if (isFilter) { flags |= COR_ILEXCEPTION_CLAUSE_FILTER; }