@@ -918,6 +918,7 @@ void CodeGen::genCodeForBBlist()
918
918
case BBJ_CALLFINALLY:
919
919
920
920
#if FEATURE_EH_FUNCLETS
921
+
921
922
// Generate a call to the finally, like this:
922
923
// mov rcx,qword ptr [rbp + 20H] // Load rcx with PSPSym
923
924
// call finally-funclet
@@ -975,6 +976,59 @@ void CodeGen::genCodeForBBlist()
975
976
getEmitter ()->emitEnableGC ();
976
977
}
977
978
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
+
978
1032
// The BBJ_ALWAYS is used because the BBJ_CALLFINALLY can't point to the
979
1033
// jump target using bbJumpDest - that is already used to point
980
1034
// to the finally block. So just skip past the BBJ_ALWAYS unless the
@@ -986,13 +1040,13 @@ void CodeGen::genCodeForBBlist()
986
1040
lblk = block;
987
1041
block = block->bbNext ;
988
1042
}
989
- #else // !FEATURE_EH_FUNCLETS
990
- NYI_X86 (" EH for RyuJIT x86" );
991
- #endif // !FEATURE_EH_FUNCLETS
1043
+
992
1044
break ;
993
1045
1046
+ #if FEATURE_EH_FUNCLETS
1047
+
994
1048
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.
996
1050
// Generate a RIP-relative
997
1051
// lea reg, [rip + disp32] ; the RIP is implicit
998
1052
// which will be position-indepenent.
@@ -1001,13 +1055,47 @@ void CodeGen::genCodeForBBlist()
1001
1055
1002
1056
case BBJ_EHFINALLYRET:
1003
1057
case BBJ_EHFILTERRET:
1004
- #if FEATURE_EH_FUNCLETS
1005
1058
genReserveFuncletEpilog (block);
1059
+ break ;
1060
+
1006
1061
#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
+ }
1009
1095
break ;
1010
1096
1097
+ #endif // !FEATURE_EH_FUNCLETS
1098
+
1011
1099
case BBJ_NONE:
1012
1100
case BBJ_COND:
1013
1101
case BBJ_SWITCH:
@@ -2644,8 +2732,25 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
2644
2732
2645
2733
#if !FEATURE_EH_FUNCLETS
2646
2734
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
2649
2754
2650
2755
case GT_PINVOKE_PROLOG:
2651
2756
noway_assert (((gcInfo.gcRegGCrefSetCur |gcInfo.gcRegByrefSetCur ) & ~fullIntArgRegMask ()) == 0 );
0 commit comments