Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
192 changes: 115 additions & 77 deletions src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1475,7 +1475,7 @@ void InterpCompiler::GetNativeRangeForClause(uint32_t startILOffset, uint32_t en
InterpBasicBlock* pEndBB = pStartBB;
for (InterpBasicBlock* pBB = pStartBB->pNextBB; (pBB != NULL) && ((uint32_t)pBB->ilOffset < endILOffset); pBB = pBB->pNextBB)
{
if ((pBB->clauseType == pStartBB->clauseType) && (pBB->overlappingEHClauseCount == pStartBB->overlappingEHClauseCount))
if (pBB->overlappingEHClauseCount == pStartBB->overlappingEHClauseCount)
{
pEndBB = pBB;
}
Expand Down Expand Up @@ -2027,69 +2027,104 @@ void InterpCompiler::CreateNextLocalVar(int iArgToSet, CORINFO_CLASS_HANDLE argC
// Create finally call island basic blocks for all try regions with finally clauses that the leave exits.
// That means when the leaveOffset is inside the try region and the target is outside of it.
// These finally call island blocks are used for non-exceptional finally execution.
// The linked list of finally call island blocks is stored in the pFinallyCallIslandBB field of the finally basic block.
// The pFinallyCallIslandBB in the actual finally call island block points to the outer try region's finally call island block.
void InterpCompiler::CreateFinallyCallIslandBasicBlocks(CORINFO_METHOD_INFO* methodInfo, int32_t leaveOffset, InterpBasicBlock* pLeaveTargetBB)
// The linked list of finally and catch call island blocks is stored in the pLeaveChainIslandBB field of the finally / catch basic block.
// The pLeaveChainIslandBB in the actual island block points to the outer try region's finally call island block / outer catch region's catch call island block.
void InterpCompiler::CreateLeaveChainIslandBasicBlocks(CORINFO_METHOD_INFO* methodInfo, int32_t leaveOffset, InterpBasicBlock* pLeaveTargetBB)
{
bool firstFinallyCallIsland = true;
InterpBasicBlock* pInnerFinallyCallIslandBB = NULL;
bool firstLeaveChainIsland = true;
InterpBasicBlock* pInnerLeaveChainIslandBB = NULL;
for (unsigned int i = 0; i < getEHcount(methodInfo); i++)
{
bool isFinallyCallIsland = false;
CORINFO_EH_CLAUSE clause;
getEHinfo(methodInfo, i, &clause);
if (clause.Flags != CORINFO_EH_CLAUSE_FINALLY)

// Check IL correctness for filter clauses
if (clause.Flags == CORINFO_EH_CLAUSE_FILTER && (uint32_t)leaveOffset >= clause.FilterOffset && (uint32_t)leaveOffset < clause.HandlerOffset)
{
continue;
if ((uint32_t)pLeaveTargetBB->ilOffset < clause.FilterOffset || (uint32_t)pLeaveTargetBB->ilOffset >= clause.HandlerOffset)
{
BADCODE("Leave out of filter clause is not allowed");
}
}

// Only try regions in which the leave instruction is located are considered.
if ((uint32_t)leaveOffset < clause.TryOffset || (uint32_t)leaveOffset >= (clause.TryOffset + clause.TryLength))
if (clause.Flags == CORINFO_EH_CLAUSE_FINALLY)
{
continue;
// Only try regions in which the leave instruction is located are considered.
if ((uint32_t)leaveOffset < clause.TryOffset || (uint32_t)leaveOffset >= (clause.TryOffset + clause.TryLength))
{
continue;
}

// If the leave target is inside the try region, we don't need to create a finally call island block.
if ((uint32_t)pLeaveTargetBB->ilOffset >= clause.TryOffset && (uint32_t)pLeaveTargetBB->ilOffset < (clause.TryOffset + clause.TryLength))
{
continue;
}

isFinallyCallIsland = true;
}
else if ((clause.Flags == CORINFO_EH_CLAUSE_NONE) || (clause.Flags & CORINFO_EH_CLAUSE_FILTER) != 0)
{
// This is a catch
// If the leave instruction is outside of this catch handler, there is nothing to do for this catch
if ((uint32_t)leaveOffset < clause.HandlerOffset || (uint32_t)leaveOffset >= (clause.HandlerOffset + clause.HandlerLength))
{
continue;
}

// If the leave target is inside the try region, we don't need to create a finally call island block.
if ((uint32_t)pLeaveTargetBB->ilOffset >= clause.TryOffset && (uint32_t)pLeaveTargetBB->ilOffset < (clause.TryOffset + clause.TryLength))
// If the leave target is inside the catch region, there is nothing more to do for this catch
if ((uint32_t)pLeaveTargetBB->ilOffset >= clause.HandlerOffset && (uint32_t)pLeaveTargetBB->ilOffset < (clause.HandlerOffset + clause.HandlerLength))
{
continue;
}
}
else
{
assert(clause.Flags == CORINFO_EH_CLAUSE_FAULT);
continue;
}

// We have found a leave that goes out of a try region with a finally or out of a catch handler

InterpBasicBlock* pHandlerBB = GetBB(clause.HandlerOffset);
InterpBasicBlock* pFinallyCallIslandBB = NULL;
InterpBasicBlock* pLeaveChainIslandBB = NULL;

InterpBasicBlock** ppLastBBNext = &pHandlerBB->pFinallyCallIslandBB;
InterpBasicBlock** ppLastBBNext = &pHandlerBB->pLeaveChainIslandBB;
while (*ppLastBBNext != NULL)
{
if ((*ppLastBBNext)->pLeaveTargetBB == pLeaveTargetBB)
{
// We already have finally call island block for the leave target
pFinallyCallIslandBB = (*ppLastBBNext);
pLeaveChainIslandBB = (*ppLastBBNext);
break;
}
ppLastBBNext = &((*ppLastBBNext)->pNextBB);
}

if (pFinallyCallIslandBB == NULL)
if (pLeaveChainIslandBB == NULL)
{
pFinallyCallIslandBB = AllocBB(clause.HandlerOffset + clause.HandlerLength);
pFinallyCallIslandBB->pLeaveTargetBB = pLeaveTargetBB;
*ppLastBBNext = pFinallyCallIslandBB;
pLeaveChainIslandBB = AllocBB(clause.HandlerOffset + clause.HandlerLength);
pLeaveChainIslandBB->pLeaveTargetBB = pLeaveTargetBB;
*ppLastBBNext = pLeaveChainIslandBB;
}

if (pInnerFinallyCallIslandBB != NULL)
pLeaveChainIslandBB->isFinallyCallIsland = isFinallyCallIsland;

if (pInnerLeaveChainIslandBB != NULL)
{
pInnerFinallyCallIslandBB->pFinallyCallIslandBB = pFinallyCallIslandBB;
pInnerLeaveChainIslandBB->pLeaveChainIslandBB = pLeaveChainIslandBB;
}
pInnerFinallyCallIslandBB = pFinallyCallIslandBB;
pInnerLeaveChainIslandBB = pLeaveChainIslandBB;

if (firstFinallyCallIsland)
if (firstLeaveChainIsland)
{
// The leaves table entry points to the first finally call island block
firstFinallyCallIsland = false;
firstLeaveChainIsland = false;

LeavesTableEntry leavesEntry;
leavesEntry.ilOffset = leaveOffset;
leavesEntry.pFinallyCallIslandBB = pFinallyCallIslandBB;
leavesEntry.pLeaveChainIslandBB = pLeaveChainIslandBB;
m_leavesTable.Add(leavesEntry);
}
}
Expand Down Expand Up @@ -2122,7 +2157,7 @@ void InterpCompiler::CreateBasicBlocks(CORINFO_METHOD_INFO* methodInfo)
if (m_isSynchronized && m_currentILOffset < m_ILCodeSizeFromILHeader)
{
// This is a ret instruction coming from the initial IL of a synchronized method.
CreateFinallyCallIslandBasicBlocks(methodInfo, insOffset, GetBB(m_synchronizedPostFinallyOffset));
CreateLeaveChainIslandBasicBlocks(methodInfo, insOffset, GetBB(m_synchronizedPostFinallyOffset));
}
}
break;
Expand Down Expand Up @@ -2150,7 +2185,7 @@ void InterpCompiler::CreateBasicBlocks(CORINFO_METHOD_INFO* methodInfo)
pTargetBB = GetBB(target);
if (opcode == CEE_LEAVE_S)
{
CreateFinallyCallIslandBasicBlocks(methodInfo, insOffset, pTargetBB);
CreateLeaveChainIslandBasicBlocks(methodInfo, insOffset, pTargetBB);
}
ip += 2;
GetBB((int32_t)(ip - codeStart));
Expand All @@ -2162,7 +2197,7 @@ void InterpCompiler::CreateBasicBlocks(CORINFO_METHOD_INFO* methodInfo)
pTargetBB = GetBB(target);
if (opcode == CEE_LEAVE)
{
CreateFinallyCallIslandBasicBlocks(methodInfo, insOffset, pTargetBB);
CreateLeaveChainIslandBasicBlocks(methodInfo, insOffset, pTargetBB);
}
ip += 5;
GetBB((int32_t)(ip - codeStart));
Expand Down Expand Up @@ -2300,27 +2335,22 @@ void InterpCompiler::InitializeClauseBuildingBlocks(CORINFO_METHOD_INFO* methodI
}
}

// Now that we have classified all the basic blocks, we can set the clause type for the finally call island blocks.
// We set it to the same type as the basic block after the finally handler.
// Now that we have classified all the basic blocks, we can set the clause type for the finally call / catch leave island blocks.
// We set it to the same type as the basic block after the finally / catch handler.
for (unsigned int i = 0; i < getEHcount(methodInfo); i++)
{
CORINFO_EH_CLAUSE clause;
getEHinfo(methodInfo, i, &clause);

if (clause.Flags != CORINFO_EH_CLAUSE_FINALLY)
{
continue;
}

InterpBasicBlock* pFinallyBB = GetBB(clause.HandlerOffset);

InterpBasicBlock* pFinallyCallIslandBB = pFinallyBB->pFinallyCallIslandBB;
while (pFinallyCallIslandBB != NULL)
InterpBasicBlock* pLeaveChainIslandBB = pFinallyBB->pLeaveChainIslandBB;
while (pLeaveChainIslandBB != NULL)
{
InterpBasicBlock* pAfterFinallyBB = m_ppOffsetToBB[clause.HandlerOffset + clause.HandlerLength];
assert(pAfterFinallyBB != NULL);
pFinallyCallIslandBB->clauseType = pAfterFinallyBB->clauseType;
pFinallyCallIslandBB = pFinallyCallIslandBB->pNextBB;
pLeaveChainIslandBB->clauseType = pAfterFinallyBB->clauseType;
pLeaveChainIslandBB = pLeaveChainIslandBB->pNextBB;
}
}
}
Expand Down Expand Up @@ -2508,7 +2538,7 @@ void InterpCompiler::EmitLeave(int32_t ilOffset, int32_t target)
// from the building block, because the finally call islands share the same IL
// offset with another block of original code in front of which it is injected.
// The EmitBranch would to that block instead of the finally call island.
pTargetBB = m_leavesTable.Get(i).pFinallyCallIslandBB;
pTargetBB = m_leavesTable.Get(i).pLeaveChainIslandBB;
break;
}
}
Expand All @@ -2520,17 +2550,7 @@ void InterpCompiler::EmitLeave(int32_t ilOffset, int32_t target)

// The leave doesn't jump out of any try region with finally, so we can just emit a branch
// to the target.
if (m_pCBB->clauseType == BBClauseCatch)
{
// leave out of catch is different from a leave out of finally. It
// exits the catch handler and returns the address of the finally
// call island as the continuation address to the EH code.
EmitBranchToBB(INTOP_LEAVE_CATCH, pTargetBB);
}
else
{
EmitBranchToBB(INTOP_BR, pTargetBB);
}
EmitBranchToBB(INTOP_BR, pTargetBB);
}

void InterpCompiler::EmitBinaryArithmeticOp(int32_t opBase)
Expand Down Expand Up @@ -4998,6 +5018,21 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
assert (pNewBB->emitState == BBStateNotEmitted);
}

InterpBasicBlock *pPrevBB = m_pCBB;
InterpBasicBlock *pPrevBB2 = m_pCBB;

pPrevBB = GenerateCodeForLeaveChainIslands(pNewBB, pPrevBB);

if (pPrevBB != pPrevBB2 && !pPrevBB->pNextBB)
{
INTERP_DUMP("Chaining generated BB%d -> BB%d\n" , pPrevBB->index, pNewBB->index);
pPrevBB->pNextBB = pNewBB;
assert(!linkBBlocks);
linkBBlocks = false;
}

m_pCBB = pPrevBB;

// We are starting a new basic block. Change cbb and link them together
if (linkBBlocks)
{
Expand Down Expand Up @@ -5045,14 +5080,10 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
}
}

InterpBasicBlock *pPrevBB = m_pCBB;

pPrevBB = GenerateCodeForFinallyCallIslands(pNewBB, pPrevBB);

if (!pPrevBB->pNextBB)
if (!m_pCBB->pNextBB)
{
INTERP_DUMP("Chaining BB%d -> BB%d\n" , pPrevBB->index, pNewBB->index);
pPrevBB->pNextBB = pNewBB;
INTERP_DUMP("Chaining BB%d -> BB%d\n" , m_pCBB->index, pNewBB->index);
m_pCBB->pNextBB = pNewBB;
}

m_pCBB = pNewBB;
Expand Down Expand Up @@ -7723,39 +7754,46 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
UnlinkUnreachableBBlocks();
}

InterpBasicBlock *InterpCompiler::GenerateCodeForFinallyCallIslands(InterpBasicBlock *pNewBB, InterpBasicBlock *pPrevBB)
InterpBasicBlock *InterpCompiler::GenerateCodeForLeaveChainIslands(InterpBasicBlock *pNewBB, InterpBasicBlock *pPrevBB)
{
InterpBasicBlock *pFinallyCallIslandBB = pNewBB->pFinallyCallIslandBB;
InterpBasicBlock *pLeaveChainIslandBB = pNewBB->pLeaveChainIslandBB;

while (pFinallyCallIslandBB != NULL)
while (pLeaveChainIslandBB != NULL)
{
INTERP_DUMP("Injecting finally call island BB%d\n", pFinallyCallIslandBB->index);
if (pFinallyCallIslandBB->emitState != BBStateEmitted)
INTERP_DUMP("Injecting %s island BB%d\n", pLeaveChainIslandBB->isFinallyCallIsland ? "finally call" : "catch leave", pLeaveChainIslandBB->index);
if (pLeaveChainIslandBB->emitState != BBStateEmitted)
{
// Set the finally call island BB as current so that the instructions are emitted into it
m_pCBB = pFinallyCallIslandBB;
m_pCBB = pLeaveChainIslandBB;
m_pStackPointer = m_pStackBase;
InitBBStackState(m_pCBB);
EmitBranchToBB(INTOP_CALL_FINALLY, pNewBB); // The pNewBB is the finally BB
m_pLastNewIns->ilOffset = -1;
// Try to get the next finally call island block (for an outer try's finally)
if (pFinallyCallIslandBB->pFinallyCallIslandBB)
if (pLeaveChainIslandBB->isFinallyCallIsland)
{
EmitBranchToBB(INTOP_CALL_FINALLY, pNewBB); // The pNewBB is the finally BB
m_pLastNewIns->ilOffset = -1;
}

InterpOpcode branchOpcode = pLeaveChainIslandBB->isFinallyCallIsland ? INTOP_BR : INTOP_LEAVE_CATCH;

// Try to get the next finally call (for an outer try's finally) / catch leave island block (for an outer catch)
if (pLeaveChainIslandBB->pLeaveChainIslandBB)
{
// Branch to the next finally call island (at an outer try block)
EmitBranchToBB(INTOP_BR, pFinallyCallIslandBB->pFinallyCallIslandBB);
EmitBranchToBB(branchOpcode, pLeaveChainIslandBB->pLeaveChainIslandBB);
}
else
{
// This is the last finally call island, so we need to emit a branch to the leave target
EmitBranchToBB(INTOP_BR, pFinallyCallIslandBB->pLeaveTargetBB);
// This is the last finally call / catch leave island, so we need to emit a branch to the leave target
EmitBranchToBB(branchOpcode, pLeaveChainIslandBB->pLeaveTargetBB);
}
m_pLastNewIns->ilOffset = -1;
m_pCBB->emitState = BBStateEmitted;
INTERP_DUMP("Chaining BB%d -> BB%d\n", pPrevBB->index, pFinallyCallIslandBB->index);
INTERP_DUMP("Chaining BB%d -> BB%d\n", pPrevBB->index, pLeaveChainIslandBB->index);
}
assert(pPrevBB->pNextBB == NULL || pPrevBB->pNextBB == pFinallyCallIslandBB);
pPrevBB->pNextBB = pFinallyCallIslandBB;
pPrevBB = pFinallyCallIslandBB;
pFinallyCallIslandBB = pFinallyCallIslandBB->pNextBB;
assert(pPrevBB->pNextBB == NULL || pPrevBB->pNextBB == pLeaveChainIslandBB);
pPrevBB->pNextBB = pLeaveChainIslandBB;
pPrevBB = pLeaveChainIslandBB;
pLeaveChainIslandBB = pLeaveChainIslandBB->pNextBB;
}

return pPrevBB;
Expand Down
Loading
Loading