@@ -6,7 +6,8 @@ use rustc_middle::mir::{
66 BinOp , Body , Constant , ConstantKind , LocalDecls , Operand , Place , ProjectionElem , Rvalue ,
77 SourceInfo , Statement , StatementKind , Terminator , TerminatorKind , UnOp ,
88} ;
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 } ;
1011
1112pub struct InstCombine ;
1213
@@ -16,7 +17,11 @@ impl<'tcx> MirPass<'tcx> for InstCombine {
1617 }
1718
1819 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+ } ;
2025 for block in body. basic_blocks . as_mut ( ) {
2126 for statement in block. statements . iter_mut ( ) {
2227 match statement. kind {
@@ -33,13 +38,18 @@ impl<'tcx> MirPass<'tcx> for InstCombine {
3338 & mut block. terminator . as_mut ( ) . unwrap ( ) ,
3439 & mut block. statements ,
3540 ) ;
41+ ctx. combine_intrinsic_assert (
42+ & mut block. terminator . as_mut ( ) . unwrap ( ) ,
43+ & mut block. statements ,
44+ ) ;
3645 }
3746 }
3847}
3948
4049struct InstCombineContext < ' tcx , ' a > {
4150 tcx : TyCtxt < ' tcx > ,
4251 local_decls : & ' a LocalDecls < ' tcx > ,
52+ param_env : ParamEnv < ' tcx > ,
4353}
4454
4555impl < ' tcx > InstCombineContext < ' tcx , ' _ > {
@@ -200,4 +210,76 @@ impl<'tcx> InstCombineContext<'tcx, '_> {
200210 } ) ;
201211 terminator. kind = TerminatorKind :: Goto { target : destination_block } ;
202212 }
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
203285}
0 commit comments