Skip to content

Commit 1be1c2e

Browse files
committed
Fix insertion of statements to be executed along return edge in inlining
Inlining creates additional statements to be executed along the return edge: an assignment to the destination, storage end for temporaries. Previously those statements where inserted directly into a call target, but this is incorrect when the target has other predecessors. Avoid the issue by creating a new dedicated block for those statements. When the block happens to be redundant it will be removed by CFG simplification that follows inlining. Fixes rust-lang#117355
1 parent 525a64f commit 1be1c2e

13 files changed

+237
-605
lines changed

compiler/rustc_mir_transform/src/inline.rs

+44-14
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ struct CallSite<'tcx> {
3232
callee: Instance<'tcx>,
3333
fn_sig: ty::PolyFnSig<'tcx>,
3434
block: BasicBlock,
35-
target: Option<BasicBlock>,
3635
source_info: SourceInfo,
3736
}
3837

@@ -367,7 +366,7 @@ impl<'tcx> Inliner<'tcx> {
367366
) -> Option<CallSite<'tcx>> {
368367
// Only consider direct calls to functions
369368
let terminator = bb_data.terminator();
370-
if let TerminatorKind::Call { ref func, target, fn_span, .. } = terminator.kind {
369+
if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind {
371370
let func_ty = func.ty(caller_body, self.tcx);
372371
if let ty::FnDef(def_id, args) = *func_ty.kind() {
373372
// To resolve an instance its args have to be fully normalized.
@@ -386,7 +385,7 @@ impl<'tcx> Inliner<'tcx> {
386385
let fn_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args);
387386
let source_info = SourceInfo { span: fn_span, ..terminator.source_info };
388387

389-
return Some(CallSite { callee, fn_sig, block: bb, target, source_info });
388+
return Some(CallSite { callee, fn_sig, block: bb, source_info });
390389
}
391390
}
392391

@@ -541,10 +540,23 @@ impl<'tcx> Inliner<'tcx> {
541540
mut callee_body: Body<'tcx>,
542541
) {
543542
let terminator = caller_body[callsite.block].terminator.take().unwrap();
544-
let TerminatorKind::Call { args, destination, unwind, .. } = terminator.kind else {
543+
let TerminatorKind::Call { args, destination, unwind, target, .. } = terminator.kind else {
545544
bug!("unexpected terminator kind {:?}", terminator.kind);
546545
};
547546

547+
let return_block = if let Some(block) = target {
548+
// Prepare a new block for code that should execute when call returns. We don't use
549+
// target block directly since it might have other predecessors.
550+
let mut data = BasicBlockData::new(Some(Terminator {
551+
source_info: terminator.source_info,
552+
kind: TerminatorKind::Goto { target: block },
553+
}));
554+
data.is_cleanup = caller_body[block].is_cleanup;
555+
Some(caller_body.basic_blocks_mut().push(data))
556+
} else {
557+
None
558+
};
559+
548560
// If the call is something like `a[*i] = f(i)`, where
549561
// `i : &mut usize`, then just duplicating the `a[*i]`
550562
// Place could result in two different locations if `f`
@@ -569,7 +581,8 @@ impl<'tcx> Inliner<'tcx> {
569581
destination,
570582
);
571583
let dest_ty = dest.ty(caller_body, self.tcx);
572-
let temp = Place::from(self.new_call_temp(caller_body, &callsite, dest_ty));
584+
let temp =
585+
Place::from(self.new_call_temp(caller_body, &callsite, dest_ty, return_block));
573586
caller_body[callsite.block].statements.push(Statement {
574587
source_info: callsite.source_info,
575588
kind: StatementKind::Assign(Box::new((temp, dest))),
@@ -590,12 +603,14 @@ impl<'tcx> Inliner<'tcx> {
590603
caller_body,
591604
&callsite,
592605
destination.ty(caller_body, self.tcx).ty,
606+
return_block,
593607
),
594608
)
595609
};
596610

597611
// Copy the arguments if needed.
598-
let args: Vec<_> = self.make_call_args(args, &callsite, caller_body, &callee_body);
612+
let args: Vec<_> =
613+
self.make_call_args(args, &callsite, caller_body, &callee_body, return_block);
599614

600615
let mut integrator = Integrator {
601616
args: &args,
@@ -607,6 +622,7 @@ impl<'tcx> Inliner<'tcx> {
607622
callsite,
608623
cleanup_block: unwind,
609624
in_cleanup_block: false,
625+
return_block,
610626
tcx: self.tcx,
611627
always_live_locals: BitSet::new_filled(callee_body.local_decls.len()),
612628
};
@@ -626,7 +642,7 @@ impl<'tcx> Inliner<'tcx> {
626642
});
627643
}
628644
}
629-
if let Some(block) = callsite.target {
645+
if let Some(block) = return_block {
630646
// To avoid repeated O(n) insert, push any new statements to the end and rotate
631647
// the slice once.
632648
let mut n = 0;
@@ -684,6 +700,7 @@ impl<'tcx> Inliner<'tcx> {
684700
callsite: &CallSite<'tcx>,
685701
caller_body: &mut Body<'tcx>,
686702
callee_body: &Body<'tcx>,
703+
return_block: Option<BasicBlock>,
687704
) -> Vec<Local> {
688705
let tcx = self.tcx;
689706

@@ -712,8 +729,18 @@ impl<'tcx> Inliner<'tcx> {
712729
// and the vector is `[closure_ref, tmp0, tmp1, tmp2]`.
713730
if callsite.fn_sig.abi() == Abi::RustCall && callee_body.spread_arg.is_none() {
714731
let mut args = args.into_iter();
715-
let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body);
716-
let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body);
732+
let self_ = self.create_temp_if_necessary(
733+
args.next().unwrap(),
734+
callsite,
735+
caller_body,
736+
return_block,
737+
);
738+
let tuple = self.create_temp_if_necessary(
739+
args.next().unwrap(),
740+
callsite,
741+
caller_body,
742+
return_block,
743+
);
717744
assert!(args.next().is_none());
718745

719746
let tuple = Place::from(tuple);
@@ -730,13 +757,13 @@ impl<'tcx> Inliner<'tcx> {
730757
let tuple_field = Operand::Move(tcx.mk_place_field(tuple, FieldIdx::new(i), ty));
731758

732759
// Spill to a local to make e.g., `tmp0`.
733-
self.create_temp_if_necessary(tuple_field, callsite, caller_body)
760+
self.create_temp_if_necessary(tuple_field, callsite, caller_body, return_block)
734761
});
735762

736763
closure_ref_arg.chain(tuple_tmp_args).collect()
737764
} else {
738765
args.into_iter()
739-
.map(|a| self.create_temp_if_necessary(a, callsite, caller_body))
766+
.map(|a| self.create_temp_if_necessary(a, callsite, caller_body, return_block))
740767
.collect()
741768
}
742769
}
@@ -748,6 +775,7 @@ impl<'tcx> Inliner<'tcx> {
748775
arg: Operand<'tcx>,
749776
callsite: &CallSite<'tcx>,
750777
caller_body: &mut Body<'tcx>,
778+
return_block: Option<BasicBlock>,
751779
) -> Local {
752780
// Reuse the operand if it is a moved temporary.
753781
if let Operand::Move(place) = &arg
@@ -760,7 +788,7 @@ impl<'tcx> Inliner<'tcx> {
760788
// Otherwise, create a temporary for the argument.
761789
trace!("creating temp for argument {:?}", arg);
762790
let arg_ty = arg.ty(caller_body, self.tcx);
763-
let local = self.new_call_temp(caller_body, callsite, arg_ty);
791+
let local = self.new_call_temp(caller_body, callsite, arg_ty, return_block);
764792
caller_body[callsite.block].statements.push(Statement {
765793
source_info: callsite.source_info,
766794
kind: StatementKind::Assign(Box::new((Place::from(local), Rvalue::Use(arg)))),
@@ -774,6 +802,7 @@ impl<'tcx> Inliner<'tcx> {
774802
caller_body: &mut Body<'tcx>,
775803
callsite: &CallSite<'tcx>,
776804
ty: Ty<'tcx>,
805+
return_block: Option<BasicBlock>,
777806
) -> Local {
778807
let local = caller_body.local_decls.push(LocalDecl::new(ty, callsite.source_info.span));
779808

@@ -782,7 +811,7 @@ impl<'tcx> Inliner<'tcx> {
782811
kind: StatementKind::StorageLive(local),
783812
});
784813

785-
if let Some(block) = callsite.target {
814+
if let Some(block) = return_block {
786815
caller_body[block].statements.insert(
787816
0,
788817
Statement {
@@ -813,6 +842,7 @@ struct Integrator<'a, 'tcx> {
813842
callsite: &'a CallSite<'tcx>,
814843
cleanup_block: UnwindAction,
815844
in_cleanup_block: bool,
845+
return_block: Option<BasicBlock>,
816846
tcx: TyCtxt<'tcx>,
817847
always_live_locals: BitSet<Local>,
818848
}
@@ -956,7 +986,7 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> {
956986
*unwind = self.map_unwind(*unwind);
957987
}
958988
TerminatorKind::Return => {
959-
terminator.kind = if let Some(tgt) = self.callsite.target {
989+
terminator.kind = if let Some(tgt) = self.return_block {
960990
TerminatorKind::Goto { target: tgt }
961991
} else {
962992
TerminatorKind::Unreachable

tests/mir-opt/inline/exponential_runtime.main.Inline.panic-abort.diff

+22-22
Original file line numberDiff line numberDiff line change
@@ -69,66 +69,66 @@
6969
+ }
7070
+
7171
+ bb2: {
72+
+ _4 = <() as F>::call() -> [return: bb1, unwind unreachable];
73+
+ }
74+
+
75+
+ bb3: {
7276
+ StorageDead(_7);
7377
+ StorageDead(_6);
7478
+ StorageDead(_5);
75-
+ _3 = <() as F>::call() -> [return: bb3, unwind unreachable];
79+
+ _3 = <() as F>::call() -> [return: bb2, unwind unreachable];
7680
+ }
7781
+
78-
+ bb3: {
79-
+ _4 = <() as F>::call() -> [return: bb1, unwind unreachable];
82+
+ bb4: {
83+
+ _7 = <() as E>::call() -> [return: bb3, unwind unreachable];
8084
+ }
8185
+
82-
+ bb4: {
86+
+ bb5: {
8387
+ StorageDead(_10);
8488
+ StorageDead(_9);
8589
+ StorageDead(_8);
86-
+ _6 = <() as E>::call() -> [return: bb5, unwind unreachable];
90+
+ _6 = <() as E>::call() -> [return: bb4, unwind unreachable];
8791
+ }
8892
+
89-
+ bb5: {
90-
+ _7 = <() as E>::call() -> [return: bb2, unwind unreachable];
93+
+ bb6: {
94+
+ _10 = <() as D>::call() -> [return: bb5, unwind unreachable];
9195
+ }
9296
+
93-
+ bb6: {
97+
+ bb7: {
9498
+ StorageDead(_13);
9599
+ StorageDead(_12);
96100
+ StorageDead(_11);
97-
+ _9 = <() as D>::call() -> [return: bb7, unwind unreachable];
101+
+ _9 = <() as D>::call() -> [return: bb6, unwind unreachable];
98102
+ }
99103
+
100-
+ bb7: {
101-
+ _10 = <() as D>::call() -> [return: bb4, unwind unreachable];
104+
+ bb8: {
105+
+ _13 = <() as C>::call() -> [return: bb7, unwind unreachable];
102106
+ }
103107
+
104-
+ bb8: {
108+
+ bb9: {
105109
+ StorageDead(_16);
106110
+ StorageDead(_15);
107111
+ StorageDead(_14);
108-
+ _12 = <() as C>::call() -> [return: bb9, unwind unreachable];
112+
+ _12 = <() as C>::call() -> [return: bb8, unwind unreachable];
109113
+ }
110114
+
111-
+ bb9: {
112-
+ _13 = <() as C>::call() -> [return: bb6, unwind unreachable];
115+
+ bb10: {
116+
+ _16 = <() as B>::call() -> [return: bb9, unwind unreachable];
113117
+ }
114118
+
115-
+ bb10: {
119+
+ bb11: {
116120
+ StorageDead(_19);
117121
+ StorageDead(_18);
118122
+ StorageDead(_17);
119-
+ _15 = <() as B>::call() -> [return: bb11, unwind unreachable];
120-
+ }
121-
+
122-
+ bb11: {
123-
+ _16 = <() as B>::call() -> [return: bb8, unwind unreachable];
123+
+ _15 = <() as B>::call() -> [return: bb10, unwind unreachable];
124124
+ }
125125
+
126126
+ bb12: {
127127
+ _18 = <() as A>::call() -> [return: bb13, unwind unreachable];
128128
+ }
129129
+
130130
+ bb13: {
131-
+ _19 = <() as A>::call() -> [return: bb10, unwind unreachable];
131+
+ _19 = <() as A>::call() -> [return: bb11, unwind unreachable];
132132
}
133133
}
134134

tests/mir-opt/inline/exponential_runtime.main.Inline.panic-unwind.diff

+22-22
Original file line numberDiff line numberDiff line change
@@ -69,66 +69,66 @@
6969
+ }
7070
+
7171
+ bb2: {
72+
+ _4 = <() as F>::call() -> [return: bb1, unwind continue];
73+
+ }
74+
+
75+
+ bb3: {
7276
+ StorageDead(_7);
7377
+ StorageDead(_6);
7478
+ StorageDead(_5);
75-
+ _3 = <() as F>::call() -> [return: bb3, unwind continue];
79+
+ _3 = <() as F>::call() -> [return: bb2, unwind continue];
7680
+ }
7781
+
78-
+ bb3: {
79-
+ _4 = <() as F>::call() -> [return: bb1, unwind continue];
82+
+ bb4: {
83+
+ _7 = <() as E>::call() -> [return: bb3, unwind continue];
8084
+ }
8185
+
82-
+ bb4: {
86+
+ bb5: {
8387
+ StorageDead(_10);
8488
+ StorageDead(_9);
8589
+ StorageDead(_8);
86-
+ _6 = <() as E>::call() -> [return: bb5, unwind continue];
90+
+ _6 = <() as E>::call() -> [return: bb4, unwind continue];
8791
+ }
8892
+
89-
+ bb5: {
90-
+ _7 = <() as E>::call() -> [return: bb2, unwind continue];
93+
+ bb6: {
94+
+ _10 = <() as D>::call() -> [return: bb5, unwind continue];
9195
+ }
9296
+
93-
+ bb6: {
97+
+ bb7: {
9498
+ StorageDead(_13);
9599
+ StorageDead(_12);
96100
+ StorageDead(_11);
97-
+ _9 = <() as D>::call() -> [return: bb7, unwind continue];
101+
+ _9 = <() as D>::call() -> [return: bb6, unwind continue];
98102
+ }
99103
+
100-
+ bb7: {
101-
+ _10 = <() as D>::call() -> [return: bb4, unwind continue];
104+
+ bb8: {
105+
+ _13 = <() as C>::call() -> [return: bb7, unwind continue];
102106
+ }
103107
+
104-
+ bb8: {
108+
+ bb9: {
105109
+ StorageDead(_16);
106110
+ StorageDead(_15);
107111
+ StorageDead(_14);
108-
+ _12 = <() as C>::call() -> [return: bb9, unwind continue];
112+
+ _12 = <() as C>::call() -> [return: bb8, unwind continue];
109113
+ }
110114
+
111-
+ bb9: {
112-
+ _13 = <() as C>::call() -> [return: bb6, unwind continue];
115+
+ bb10: {
116+
+ _16 = <() as B>::call() -> [return: bb9, unwind continue];
113117
+ }
114118
+
115-
+ bb10: {
119+
+ bb11: {
116120
+ StorageDead(_19);
117121
+ StorageDead(_18);
118122
+ StorageDead(_17);
119-
+ _15 = <() as B>::call() -> [return: bb11, unwind continue];
120-
+ }
121-
+
122-
+ bb11: {
123-
+ _16 = <() as B>::call() -> [return: bb8, unwind continue];
123+
+ _15 = <() as B>::call() -> [return: bb10, unwind continue];
124124
+ }
125125
+
126126
+ bb12: {
127127
+ _18 = <() as A>::call() -> [return: bb13, unwind continue];
128128
+ }
129129
+
130130
+ bb13: {
131-
+ _19 = <() as A>::call() -> [return: bb10, unwind continue];
131+
+ _19 = <() as A>::call() -> [return: bb11, unwind continue];
132132
}
133133
}
134134

0 commit comments

Comments
 (0)