@@ -20,7 +20,7 @@ use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
2020use rustc_span:: source_map:: Span ;
2121use rustc_span:: { sym, Symbol } ;
2222use rustc_target:: abi:: call:: { ArgAbi , FnAbi , PassMode } ;
23- use rustc_target:: abi:: { self , LayoutOf } ;
23+ use rustc_target:: abi:: { self , HasDataLayout , LayoutOf } ;
2424use rustc_target:: spec:: abi:: Abi ;
2525
2626/// Used by `FunctionCx::codegen_terminator` for emitting common patterns
@@ -32,13 +32,34 @@ struct TerminatorCodegenHelper<'tcx> {
3232}
3333
3434impl < ' a , ' tcx > TerminatorCodegenHelper < ' tcx > {
35- /// Returns the associated funclet from `FunctionCx::funclets ` for the
36- /// `funclet_bb` member if it is not `None `.
35+ /// Returns the appropriate `Funclet ` for the current funclet, if on MSVC,
36+ /// either already previously cached, or newly created, by `landing_pad_for `.
3737 fn funclet < ' b , Bx : BuilderMethods < ' a , ' tcx > > (
3838 & self ,
39- fx : & ' b FunctionCx < ' a , ' tcx , Bx > ,
39+ fx : & ' b mut FunctionCx < ' a , ' tcx , Bx > ,
4040 ) -> Option < & ' b Bx :: Funclet > {
41- self . funclet_bb . and_then ( |funcl| fx. funclets [ funcl] . as_ref ( ) )
41+ let funclet_bb = self . funclet_bb ?;
42+ if base:: wants_msvc_seh ( fx. cx . tcx ( ) . sess ) {
43+ // If `landing_pad_for` hasn't been called yet to create the `Funclet`,
44+ // it has to be now. This may not seem necessary, as RPO should lead
45+ // to all the unwind edges being visited (and so to `landing_pad_for`
46+ // getting called for them), before building any of the blocks inside
47+ // the funclet itself - however, if MIR contains edges that end up not
48+ // being needed in the LLVM IR after monomorphization, the funclet may
49+ // be unreachable, and we don't have yet a way to skip building it in
50+ // such an eventuality (which may be a better solution than this).
51+ if fx. funclets [ funclet_bb] . is_none ( ) {
52+ fx. landing_pad_for ( funclet_bb) ;
53+ }
54+
55+ Some (
56+ fx. funclets [ funclet_bb]
57+ . as_ref ( )
58+ . expect ( "landing_pad_for didn't also create funclets entry" ) ,
59+ )
60+ } else {
61+ None
62+ }
4263 }
4364
4465 fn lltarget < Bx : BuilderMethods < ' a , ' tcx > > (
@@ -54,10 +75,10 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
5475 ( Some ( f) , Some ( t_f) ) if f == t_f || !base:: wants_msvc_seh ( fx. cx . tcx ( ) . sess ) => {
5576 ( lltarget, false )
5677 }
57- // jump *into* cleanup - need a landing pad if GNU
58- ( None , Some ( _) ) => ( fx. landing_pad_to ( target) , false ) ,
78+ // jump *into* cleanup - need a landing pad if GNU, cleanup pad if MSVC
79+ ( None , Some ( _) ) => ( fx. landing_pad_for ( target) , false ) ,
5980 ( Some ( _) , None ) => span_bug ! ( span, "{:?} - jump out of cleanup?" , self . terminator) ,
60- ( Some ( _) , Some ( _) ) => ( fx. landing_pad_to ( target) , true ) ,
81+ ( Some ( _) , Some ( _) ) => ( fx. landing_pad_for ( target) , true ) ,
6182 }
6283 }
6384
@@ -1170,38 +1191,88 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
11701191 }
11711192 }
11721193
1173- /// Returns the landing-pad wrapper around the given basic block.
1174- ///
1175- /// No-op in MSVC SEH scheme.
1176- fn landing_pad_to ( & mut self , target_bb : mir:: BasicBlock ) -> Bx :: BasicBlock {
1177- if let Some ( block) = self . landing_pads [ target_bb] {
1178- return block;
1194+ /// Returns the landing/cleanup pad wrapper around the given basic block.
1195+ // FIXME(eddyb) rename this to `eh_pad_for`.
1196+ fn landing_pad_for ( & mut self , bb : mir:: BasicBlock ) -> Bx :: BasicBlock {
1197+ if let Some ( landing_pad) = self . landing_pads [ bb] {
1198+ return landing_pad;
11791199 }
11801200
1181- let block = self . blocks [ target_bb] ;
1182- let landing_pad = self . landing_pad_uncached ( block) ;
1183- self . landing_pads [ target_bb] = Some ( landing_pad) ;
1201+ let landing_pad = self . landing_pad_for_uncached ( bb) ;
1202+ self . landing_pads [ bb] = Some ( landing_pad) ;
11841203 landing_pad
11851204 }
11861205
1187- fn landing_pad_uncached ( & mut self , target_bb : Bx :: BasicBlock ) -> Bx :: BasicBlock {
1206+ // FIXME(eddyb) rename this to `eh_pad_for_uncached`.
1207+ fn landing_pad_for_uncached ( & mut self , bb : mir:: BasicBlock ) -> Bx :: BasicBlock {
1208+ let llbb = self . blocks [ bb] ;
11881209 if base:: wants_msvc_seh ( self . cx . sess ( ) ) {
1189- span_bug ! ( self . mir. span, "landing pad was not inserted?" )
1190- }
1191-
1192- let mut bx = self . new_block ( "cleanup" ) ;
1210+ let funclet;
1211+ let ret_llbb;
1212+ match self . mir [ bb] . terminator . as_ref ( ) . map ( |t| & t. kind ) {
1213+ // This is a basic block that we're aborting the program for,
1214+ // notably in an `extern` function. These basic blocks are inserted
1215+ // so that we assert that `extern` functions do indeed not panic,
1216+ // and if they do we abort the process.
1217+ //
1218+ // On MSVC these are tricky though (where we're doing funclets). If
1219+ // we were to do a cleanuppad (like below) the normal functions like
1220+ // `longjmp` would trigger the abort logic, terminating the
1221+ // program. Instead we insert the equivalent of `catch(...)` for C++
1222+ // which magically doesn't trigger when `longjmp` files over this
1223+ // frame.
1224+ //
1225+ // Lots more discussion can be found on #48251 but this codegen is
1226+ // modeled after clang's for:
1227+ //
1228+ // try {
1229+ // foo();
1230+ // } catch (...) {
1231+ // bar();
1232+ // }
1233+ Some ( & mir:: TerminatorKind :: Abort ) => {
1234+ let mut cs_bx = self . new_block ( & format ! ( "cs_funclet{:?}" , bb) ) ;
1235+ let mut cp_bx = self . new_block ( & format ! ( "cp_funclet{:?}" , bb) ) ;
1236+ ret_llbb = cs_bx. llbb ( ) ;
1237+
1238+ let cs = cs_bx. catch_switch ( None , None , 1 ) ;
1239+ cs_bx. add_handler ( cs, cp_bx. llbb ( ) ) ;
1240+
1241+ // The "null" here is actually a RTTI type descriptor for the
1242+ // C++ personality function, but `catch (...)` has no type so
1243+ // it's null. The 64 here is actually a bitfield which
1244+ // represents that this is a catch-all block.
1245+ let null = cp_bx. const_null (
1246+ cp_bx. type_i8p_ext ( cp_bx. cx ( ) . data_layout ( ) . instruction_address_space ) ,
1247+ ) ;
1248+ let sixty_four = cp_bx. const_i32 ( 64 ) ;
1249+ funclet = cp_bx. catch_pad ( cs, & [ null, sixty_four, null] ) ;
1250+ cp_bx. br ( llbb) ;
1251+ }
1252+ _ => {
1253+ let mut cleanup_bx = self . new_block ( & format ! ( "funclet_{:?}" , bb) ) ;
1254+ ret_llbb = cleanup_bx. llbb ( ) ;
1255+ funclet = cleanup_bx. cleanup_pad ( None , & [ ] ) ;
1256+ cleanup_bx. br ( llbb) ;
1257+ }
1258+ }
1259+ self . funclets [ bb] = Some ( funclet) ;
1260+ ret_llbb
1261+ } else {
1262+ let mut bx = self . new_block ( "cleanup" ) ;
11931263
1194- let llpersonality = self . cx . eh_personality ( ) ;
1195- let llretty = self . landing_pad_type ( ) ;
1196- let lp = bx. landing_pad ( llretty, llpersonality, 1 ) ;
1197- bx. set_cleanup ( lp) ;
1264+ let llpersonality = self . cx . eh_personality ( ) ;
1265+ let llretty = self . landing_pad_type ( ) ;
1266+ let lp = bx. landing_pad ( llretty, llpersonality, 1 ) ;
1267+ bx. set_cleanup ( lp) ;
11981268
1199- let slot = self . get_personality_slot ( & mut bx) ;
1200- slot. storage_live ( & mut bx) ;
1201- Pair ( bx. extract_value ( lp, 0 ) , bx. extract_value ( lp, 1 ) ) . store ( & mut bx, slot) ;
1269+ let slot = self . get_personality_slot ( & mut bx) ;
1270+ slot. storage_live ( & mut bx) ;
1271+ Pair ( bx. extract_value ( lp, 0 ) , bx. extract_value ( lp, 1 ) ) . store ( & mut bx, slot) ;
12021272
1203- bx. br ( target_bb) ;
1204- bx. llbb ( )
1273+ bx. br ( llbb) ;
1274+ bx. llbb ( )
1275+ }
12051276 }
12061277
12071278 fn landing_pad_type ( & self ) -> Bx :: Type {
0 commit comments