@@ -6,7 +6,8 @@ use rustc_middle::mir::{
6
6
BinOp , Body , Constant , ConstantKind , LocalDecls , Operand , Place , ProjectionElem , Rvalue ,
7
7
SourceInfo , Statement , StatementKind , Terminator , TerminatorKind , UnOp ,
8
8
} ;
9
- use rustc_middle:: ty:: { self , TyCtxt } ;
9
+ use rustc_middle:: ty:: { self , layout:: TyAndLayout , ParamEnv , ParamEnvAnd , SubstsRef , Ty , TyCtxt } ;
10
+ use rustc_span:: symbol:: { sym, Symbol } ;
10
11
11
12
pub struct InstCombine ;
12
13
@@ -16,7 +17,11 @@ impl<'tcx> MirPass<'tcx> for InstCombine {
16
17
}
17
18
18
19
fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
19
- let ctx = InstCombineContext { tcx, local_decls : & body. local_decls } ;
20
+ let ctx = InstCombineContext {
21
+ tcx,
22
+ local_decls : & body. local_decls ,
23
+ param_env : tcx. param_env_reveal_all_normalized ( body. source . def_id ( ) ) ,
24
+ } ;
20
25
for block in body. basic_blocks . as_mut ( ) {
21
26
for statement in block. statements . iter_mut ( ) {
22
27
match statement. kind {
@@ -33,13 +38,18 @@ impl<'tcx> MirPass<'tcx> for InstCombine {
33
38
& mut block. terminator . as_mut ( ) . unwrap ( ) ,
34
39
& mut block. statements ,
35
40
) ;
41
+ ctx. combine_intrinsic_assert (
42
+ & mut block. terminator . as_mut ( ) . unwrap ( ) ,
43
+ & mut block. statements ,
44
+ ) ;
36
45
}
37
46
}
38
47
}
39
48
40
49
struct InstCombineContext < ' tcx , ' a > {
41
50
tcx : TyCtxt < ' tcx > ,
42
51
local_decls : & ' a LocalDecls < ' tcx > ,
52
+ param_env : ParamEnv < ' tcx > ,
43
53
}
44
54
45
55
impl < ' tcx > InstCombineContext < ' tcx , ' _ > {
@@ -200,4 +210,76 @@ impl<'tcx> InstCombineContext<'tcx, '_> {
200
210
} ) ;
201
211
terminator. kind = TerminatorKind :: Goto { target : destination_block } ;
202
212
}
213
+
214
+ fn combine_intrinsic_assert (
215
+ & self ,
216
+ terminator : & mut Terminator < ' tcx > ,
217
+ _statements : & mut Vec < Statement < ' tcx > > ,
218
+ ) {
219
+ let TerminatorKind :: Call { func, target, .. } = & mut terminator. kind else { return ; } ;
220
+ let Some ( target_block) = target else { return ; } ;
221
+ let func_ty = func. ty ( self . local_decls , self . tcx ) ;
222
+ let Some ( ( intrinsic_name, substs) ) = resolve_rust_intrinsic ( self . tcx , func_ty) else {
223
+ return ;
224
+ } ;
225
+ // The intrinsics we are interested in have one generic parameter
226
+ if substs. is_empty ( ) {
227
+ return ;
228
+ }
229
+ let ty = substs. type_at ( 0 ) ;
230
+
231
+ // Check this is a foldable intrinsic before we query the layout of our generic parameter
232
+ let Some ( assert_panics) = intrinsic_assert_panics ( intrinsic_name) else { return ; } ;
233
+ let Ok ( layout) = self . tcx . layout_of ( self . param_env . and ( ty) ) else { return ; } ;
234
+ if assert_panics ( self . tcx , self . param_env . and ( layout) ) {
235
+ // If we know the assert panics, indicate to later opts that the call diverges
236
+ * target = None ;
237
+ } else {
238
+ // If we know the assert does not panic, turn the call into a Goto
239
+ terminator. kind = TerminatorKind :: Goto { target : * target_block } ;
240
+ }
241
+ }
242
+ }
243
+
244
+ fn intrinsic_assert_panics < ' tcx > (
245
+ intrinsic_name : Symbol ,
246
+ ) -> Option < fn ( TyCtxt < ' tcx > , ParamEnvAnd < ' tcx , TyAndLayout < ' tcx > > ) -> bool > {
247
+ fn inhabited_predicate < ' tcx > (
248
+ _tcx : TyCtxt < ' tcx > ,
249
+ param_env_and_layout : ParamEnvAnd < ' tcx , TyAndLayout < ' tcx > > ,
250
+ ) -> bool {
251
+ let ( _param_env, layout) = param_env_and_layout. into_parts ( ) ;
252
+ layout. abi . is_uninhabited ( )
253
+ }
254
+ fn zero_valid_predicate < ' tcx > (
255
+ tcx : TyCtxt < ' tcx > ,
256
+ param_env_and_layout : ParamEnvAnd < ' tcx , TyAndLayout < ' tcx > > ,
257
+ ) -> bool {
258
+ !tcx. permits_zero_init ( param_env_and_layout)
259
+ }
260
+ fn mem_uninitialized_valid_predicate < ' tcx > (
261
+ tcx : TyCtxt < ' tcx > ,
262
+ param_env_and_layout : ParamEnvAnd < ' tcx , TyAndLayout < ' tcx > > ,
263
+ ) -> bool {
264
+ !tcx. permits_uninit_init ( param_env_and_layout)
265
+ }
266
+
267
+ match intrinsic_name {
268
+ sym:: assert_inhabited => Some ( inhabited_predicate) ,
269
+ sym:: assert_zero_valid => Some ( zero_valid_predicate) ,
270
+ sym:: assert_mem_uninitialized_valid => Some ( mem_uninitialized_valid_predicate) ,
271
+ _ => None ,
272
+ }
273
+ }
274
+
275
+ fn resolve_rust_intrinsic < ' tcx > (
276
+ tcx : TyCtxt < ' tcx > ,
277
+ func_ty : Ty < ' tcx > ,
278
+ ) -> Option < ( Symbol , SubstsRef < ' tcx > ) > {
279
+ if let ty:: FnDef ( def_id, substs) = * func_ty. kind ( ) {
280
+ if tcx. is_intrinsic ( def_id) {
281
+ return Some ( ( tcx. item_name ( def_id) , substs) ) ;
282
+ }
283
+ }
284
+ None
203
285
}
0 commit comments