Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 5e4d41c

Browse files
Merge pull request #6101 from BruceForstall/RyuJITx86EH
RyuJIT/x86: add support for EH
2 parents ec8157e + 239856a commit 5e4d41c

File tree

4 files changed

+155
-10
lines changed

4 files changed

+155
-10
lines changed

src/jit/codegenxarch.cpp

Lines changed: 114 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,7 @@ void CodeGen::genCodeForBBlist()
918918
case BBJ_CALLFINALLY:
919919

920920
#if FEATURE_EH_FUNCLETS
921+
921922
// Generate a call to the finally, like this:
922923
// mov rcx,qword ptr [rbp + 20H] // Load rcx with PSPSym
923924
// call finally-funclet
@@ -975,6 +976,59 @@ void CodeGen::genCodeForBBlist()
975976
getEmitter()->emitEnableGC();
976977
}
977978

979+
#else // !FEATURE_EH_FUNCLETS
980+
981+
// If we are about to invoke a finally locally from a try block, we have to set the ShadowSP slot
982+
// corresponding to the finally's nesting level. When invoked in response to an exception, the
983+
// EE does this.
984+
//
985+
// We have a BBJ_CALLFINALLY followed by a BBJ_ALWAYS.
986+
//
987+
// We will emit :
988+
// mov [ebp - (n + 1)], 0
989+
// mov [ebp - n ], 0xFC
990+
// push &step
991+
// jmp finallyBlock
992+
// ...
993+
// step:
994+
// mov [ebp - n ], 0
995+
// jmp leaveTarget
996+
// ...
997+
// leaveTarget:
998+
999+
noway_assert(isFramePointerUsed());
1000+
1001+
// Get the nesting level which contains the finally
1002+
compiler->fgGetNestingLevel(block, &finallyNesting);
1003+
1004+
// The last slot is reserved for ICodeManager::FixContext(ppEndRegion)
1005+
unsigned filterEndOffsetSlotOffs;
1006+
filterEndOffsetSlotOffs = (unsigned)(compiler->lvaLclSize(compiler->lvaShadowSPslotsVar) - TARGET_POINTER_SIZE);
1007+
1008+
unsigned curNestingSlotOffs;
1009+
curNestingSlotOffs = (unsigned)(filterEndOffsetSlotOffs - ((finallyNesting + 1) * TARGET_POINTER_SIZE));
1010+
1011+
// Zero out the slot for the next nesting level
1012+
instGen_Store_Imm_Into_Lcl(TYP_I_IMPL, EA_PTRSIZE, 0, compiler->lvaShadowSPslotsVar, curNestingSlotOffs - TARGET_POINTER_SIZE);
1013+
instGen_Store_Imm_Into_Lcl(TYP_I_IMPL, EA_PTRSIZE, LCL_FINALLY_MARK, compiler->lvaShadowSPslotsVar, curNestingSlotOffs);
1014+
1015+
// Now push the address where the finally funclet should return to directly.
1016+
if ( !(block->bbFlags & BBF_RETLESS_CALL) )
1017+
{
1018+
assert(block->isBBCallAlwaysPair());
1019+
getEmitter()->emitIns_J(INS_push_hide, block->bbNext->bbJumpDest);
1020+
}
1021+
else
1022+
{
1023+
// EE expects a DWORD, so we give him 0
1024+
inst_IV(INS_push_hide, 0);
1025+
}
1026+
1027+
// Jump to the finally BB
1028+
inst_JMP(EJ_jmp, block->bbJumpDest);
1029+
1030+
#endif // !FEATURE_EH_FUNCLETS
1031+
9781032
// The BBJ_ALWAYS is used because the BBJ_CALLFINALLY can't point to the
9791033
// jump target using bbJumpDest - that is already used to point
9801034
// to the finally block. So just skip past the BBJ_ALWAYS unless the
@@ -986,13 +1040,13 @@ void CodeGen::genCodeForBBlist()
9861040
lblk = block;
9871041
block = block->bbNext;
9881042
}
989-
#else // !FEATURE_EH_FUNCLETS
990-
NYI_X86("EH for RyuJIT x86");
991-
#endif // !FEATURE_EH_FUNCLETS
1043+
9921044
break;
9931045

1046+
#if FEATURE_EH_FUNCLETS
1047+
9941048
case BBJ_EHCATCHRET:
995-
// Set EAX to the address the VM should return to after the catch.
1049+
// Set RAX to the address the VM should return to after the catch.
9961050
// Generate a RIP-relative
9971051
// lea reg, [rip + disp32] ; the RIP is implicit
9981052
// which will be position-indepenent.
@@ -1001,13 +1055,47 @@ void CodeGen::genCodeForBBlist()
10011055

10021056
case BBJ_EHFINALLYRET:
10031057
case BBJ_EHFILTERRET:
1004-
#if FEATURE_EH_FUNCLETS
10051058
genReserveFuncletEpilog(block);
1059+
break;
1060+
10061061
#else // !FEATURE_EH_FUNCLETS
1007-
NYI_X86("EH for RyuJIT x86");
1008-
#endif // !FEATURE_EH_FUNCLETS
1062+
1063+
case BBJ_EHCATCHRET:
1064+
noway_assert(!"Unexpected BBJ_EHCATCHRET"); // not used on x86
1065+
1066+
case BBJ_EHFINALLYRET:
1067+
case BBJ_EHFILTERRET:
1068+
{
1069+
// The last statement of the block must be a GT_RETFILT, which has already been generated.
1070+
GenTree* tmpNode = nullptr;
1071+
assert((block->bbTreeList != nullptr) &&
1072+
((tmpNode = block->bbTreeList->gtPrev->AsStmt()->gtStmtExpr) != nullptr) &&
1073+
(tmpNode->gtOper == GT_RETFILT));
1074+
1075+
if (block->bbJumpKind == BBJ_EHFINALLYRET)
1076+
{
1077+
assert(tmpNode->gtOp.gtOp1 == nullptr); // op1 == nullptr means endfinally
1078+
1079+
// Return using a pop-jmp sequence. As the "try" block calls
1080+
// the finally with a jmp, this leaves the x86 call-ret stack
1081+
// balanced in the normal flow of path.
1082+
1083+
noway_assert(isFramePointerRequired());
1084+
inst_RV(INS_pop_hide, REG_EAX, TYP_I_IMPL);
1085+
inst_RV(INS_i_jmp, REG_EAX, TYP_I_IMPL);
1086+
}
1087+
else
1088+
{
1089+
assert(block->bbJumpKind == BBJ_EHFILTERRET);
1090+
1091+
// The return value has already been computed.
1092+
instGen_Return(0);
1093+
}
1094+
}
10091095
break;
10101096

1097+
#endif // !FEATURE_EH_FUNCLETS
1098+
10111099
case BBJ_NONE:
10121100
case BBJ_COND:
10131101
case BBJ_SWITCH:
@@ -2644,8 +2732,25 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
26442732

26452733
#if !FEATURE_EH_FUNCLETS
26462734
case GT_END_LFIN:
2647-
NYI_X86("GT_END_LFIN codegen");
2648-
#endif
2735+
2736+
// Have to clear the ShadowSP of the nesting level which encloses the finally. Generates:
2737+
// mov dword ptr [ebp-0xC], 0 // for some slot of the ShadowSP local var
2738+
2739+
unsigned finallyNesting;
2740+
finallyNesting = treeNode->gtVal.gtVal1;
2741+
noway_assert(treeNode->gtVal.gtVal1 < compiler->compHndBBtabCount);
2742+
noway_assert(finallyNesting < compiler->compHndBBtabCount);
2743+
2744+
// The last slot is reserved for ICodeManager::FixContext(ppEndRegion)
2745+
unsigned filterEndOffsetSlotOffs;
2746+
PREFIX_ASSUME(compiler->lvaLclSize(compiler->lvaShadowSPslotsVar) > TARGET_POINTER_SIZE); //below doesn't underflow.
2747+
filterEndOffsetSlotOffs = (unsigned)(compiler->lvaLclSize(compiler->lvaShadowSPslotsVar) - TARGET_POINTER_SIZE);
2748+
2749+
unsigned curNestingSlotOffs;
2750+
curNestingSlotOffs = filterEndOffsetSlotOffs - ((finallyNesting + 1) * TARGET_POINTER_SIZE);
2751+
instGen_Store_Imm_Into_Lcl(TYP_I_IMPL, EA_PTRSIZE, 0, compiler->lvaShadowSPslotsVar, curNestingSlotOffs);
2752+
break;
2753+
#endif // !FEATURE_EH_FUNCLETS
26492754

26502755
case GT_PINVOKE_PROLOG:
26512756
noway_assert(((gcInfo.gcRegGCrefSetCur|gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);

src/jit/importer.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7741,6 +7741,14 @@ void Compiler::impImportLeave(BasicBlock * block)
77417741
endCatches, endCatch);
77427742
else
77437743
endCatches = endCatch;
7744+
7745+
#ifdef DEBUG
7746+
if (verbose)
7747+
{
7748+
printf("impImportLeave - BB%02u jumping out of catch handler EH#%u, adding call to CORINFO_HELP_ENDCATCH\n",
7749+
block->bbNum, XTnum);
7750+
}
7751+
#endif
77447752
}
77457753
else if (HBtab->HasFinallyHandler() &&
77467754
jitIsBetween(blkAddr, tryBeg, tryEnd) &&

src/jit/lowerxarch.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,9 @@ void Lowering::TreeNodeInfoInit(GenTree* stmt)
692692

693693
#if !FEATURE_EH_FUNCLETS
694694
case GT_END_LFIN:
695-
NYI_X86("Implement GT_END_LFIN for x86");
695+
info->srcCount = 0;
696+
info->dstCount = 0;
697+
break;
696698
#endif
697699

698700
case GT_CLS_VAR:

tests/issues.targets

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,18 @@
257257
<ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\stringintern\test1-xassem\test1-xassem.cmd">
258258
<Issue>3554</Issue>
259259
</ExcludeList>
260+
<ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\stringintern\test4-xassem\test4-xassem.cmd">
261+
<Issue>3554</Issue>
262+
</ExcludeList>
263+
<ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\stringintern\_Simpletest4\_Simpletest4.cmd">
264+
<Issue>3554</Issue>
265+
</ExcludeList>
266+
<ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\stringintern\_XAssemblytest4-xassem\_XAssemblytest4-xassem.cmd">
267+
<Issue>3554</Issue>
268+
</ExcludeList>
269+
<ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\stringintern\_XModuletest4-xmod\_XModuletest4-xmod.cmd">
270+
<Issue>3554</Issue>
271+
</ExcludeList>
260272
<ExcludeList Include="$(XunitTestBinBase)\JIT\Performance\CodeQuality\Serialization\Serialize\Serialize.cmd">
261273
<Issue>3597</Issue>
262274
</ExcludeList>
@@ -278,6 +290,24 @@
278290
<ExcludeList Include="$(XunitTestBinBase)\JIT\opt\Tailcall\TailcallVerifyWithPrefix\TailcallVerifyWithPrefix.cmd" >
279291
<Issue>x86 JIT doesn't support tail call opt</Issue>
280292
</ExcludeList>
293+
<ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\fp\exgen\10w5d_cs_do\10w5d_cs_do.cmd">
294+
<Issue>6097</Issue>
295+
</ExcludeList>
296+
<ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\fp\exgen\10w250d_cs_do\10w250d_cs_do.cmd">
297+
<Issue>6097</Issue>
298+
</ExcludeList>
299+
<ExcludeList Include="$(XunitTestBinBase)\JIT\Directed\perffix\primitivevt\mixed1_cs_ro\mixed1_cs_ro.cmd">
300+
<Issue>6097</Issue>
301+
</ExcludeList>
302+
<ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\fp\exgen\10w250d_cs_ro\10w250d_cs_ro.cmd">
303+
<Issue>6097</Issue>
304+
</ExcludeList>
305+
<ExcludeList Include="$(XunitTestBinBase)\JIT\Methodical\fp\exgen\10w5d_cs_ro\10w5d_cs_ro.cmd">
306+
<Issue>6097</Issue>
307+
</ExcludeList>
308+
<ExcludeList Include="$(XunitTestBinBase)\JIT\Directed\perffix\primitivevt\mixed1_cs_do\mixed1_cs_do.cmd">
309+
<Issue>6097</Issue>
310+
</ExcludeList>
281311
</ItemGroup>
282312

283313
<!-- Tests that need to be triaged for vararg usage as that is not supported -->

0 commit comments

Comments
 (0)