@@ -20,7 +20,7 @@ use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
20
20
use rustc_span:: source_map:: Span ;
21
21
use rustc_span:: { sym, Symbol } ;
22
22
use rustc_target:: abi:: call:: { ArgAbi , FnAbi , PassMode } ;
23
- use rustc_target:: abi:: { self , LayoutOf } ;
23
+ use rustc_target:: abi:: { self , HasDataLayout , LayoutOf } ;
24
24
use rustc_target:: spec:: abi:: Abi ;
25
25
26
26
/// Used by `FunctionCx::codegen_terminator` for emitting common patterns
@@ -32,13 +32,34 @@ struct TerminatorCodegenHelper<'tcx> {
32
32
}
33
33
34
34
impl < ' 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 `.
37
37
fn funclet < ' b , Bx : BuilderMethods < ' a , ' tcx > > (
38
38
& self ,
39
- fx : & ' b FunctionCx < ' a , ' tcx , Bx > ,
39
+ fx : & ' b mut FunctionCx < ' a , ' tcx , Bx > ,
40
40
) -> 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
+ }
42
63
}
43
64
44
65
fn lltarget < Bx : BuilderMethods < ' a , ' tcx > > (
@@ -54,10 +75,10 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
54
75
( Some ( f) , Some ( t_f) ) if f == t_f || !base:: wants_msvc_seh ( fx. cx . tcx ( ) . sess ) => {
55
76
( lltarget, false )
56
77
}
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 ) ,
59
80
( 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 ) ,
61
82
}
62
83
}
63
84
@@ -1170,38 +1191,88 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
1170
1191
}
1171
1192
}
1172
1193
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;
1179
1199
}
1180
1200
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) ;
1184
1203
landing_pad
1185
1204
}
1186
1205
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] ;
1188
1209
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" ) ;
1193
1263
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) ;
1198
1268
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) ;
1202
1272
1203
- bx. br ( target_bb) ;
1204
- bx. llbb ( )
1273
+ bx. br ( llbb) ;
1274
+ bx. llbb ( )
1275
+ }
1205
1276
}
1206
1277
1207
1278
fn landing_pad_type ( & self ) -> Bx :: Type {
0 commit comments