Skip to content

Commit 2bc8029

Browse files
committed
Support inlining diverging function calls
Additionally introduce storage markers for all temporaries created by the inliner. The temporary introduced for destination rebrorrow, didn't use them previously.
1 parent cf9cf7c commit 2bc8029

7 files changed

+256
-76
lines changed

compiler/rustc_mir/src/transform/inline.rs

+76-76
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ pub struct Inline;
3131
#[derive(Copy, Clone, Debug)]
3232
struct CallSite<'tcx> {
3333
callee: Instance<'tcx>,
34-
bb: BasicBlock,
34+
block: BasicBlock,
35+
target: Option<BasicBlock>,
3536
source_info: SourceInfo,
3637
}
3738

@@ -175,8 +176,7 @@ impl Inliner<'tcx> {
175176

176177
// Only consider direct calls to functions
177178
let terminator = bb_data.terminator();
178-
// FIXME: Handle inlining of diverging calls
179-
if let TerminatorKind::Call { func: ref op, destination: Some(_), .. } = terminator.kind {
179+
if let TerminatorKind::Call { func: ref op, ref destination, .. } = terminator.kind {
180180
if let ty::FnDef(callee_def_id, substs) = *op.ty(caller_body, self.tcx).kind() {
181181
// To resolve an instance its substs have to be fully normalized, so
182182
// we do this here.
@@ -190,7 +190,12 @@ impl Inliner<'tcx> {
190190
return None;
191191
}
192192

193-
return Some(CallSite { callee, bb, source_info: terminator.source_info });
193+
return Some(CallSite {
194+
callee,
195+
block: bb,
196+
target: destination.map(|(_, target)| target),
197+
source_info: terminator.source_info,
198+
});
194199
}
195200
}
196201

@@ -398,9 +403,9 @@ impl Inliner<'tcx> {
398403
caller_body: &mut Body<'tcx>,
399404
mut callee_body: Body<'tcx>,
400405
) {
401-
let terminator = caller_body[callsite.bb].terminator.take().unwrap();
406+
let terminator = caller_body[callsite.block].terminator.take().unwrap();
402407
match terminator.kind {
403-
TerminatorKind::Call { args, destination: Some(destination), cleanup, .. } => {
408+
TerminatorKind::Call { args, destination, cleanup, .. } => {
404409
// If the call is something like `a[*i] = f(i)`, where
405410
// `i : &mut usize`, then just duplicating the `a[*i]`
406411
// Place could result in two different locations if `f`
@@ -417,43 +422,39 @@ impl Inliner<'tcx> {
417422
false
418423
}
419424

420-
let dest = if dest_needs_borrow(destination.0) {
421-
trace!("creating temp for return destination");
422-
let dest = Rvalue::Ref(
423-
self.tcx.lifetimes.re_erased,
424-
BorrowKind::Mut { allow_two_phase_borrow: false },
425-
destination.0,
426-
);
427-
428-
let ty = dest.ty(caller_body, self.tcx);
429-
430-
let temp = LocalDecl::new(ty, callsite.source_info.span);
431-
432-
let tmp = caller_body.local_decls.push(temp);
433-
let tmp = Place::from(tmp);
434-
435-
let stmt = Statement {
436-
source_info: callsite.source_info,
437-
kind: StatementKind::Assign(box (tmp, dest)),
438-
};
439-
caller_body[callsite.bb].statements.push(stmt);
440-
self.tcx.mk_place_deref(tmp)
425+
let dest = if let Some((destination_place, _)) = destination {
426+
if dest_needs_borrow(destination_place) {
427+
trace!("creating temp for return destination");
428+
let dest = Rvalue::Ref(
429+
self.tcx.lifetimes.re_erased,
430+
BorrowKind::Mut { allow_two_phase_borrow: false },
431+
destination_place,
432+
);
433+
let dest_ty = dest.ty(caller_body, self.tcx);
434+
let temp = Place::from(self.new_call_temp(caller_body, &callsite, dest_ty));
435+
caller_body[callsite.block].statements.push(Statement {
436+
source_info: callsite.source_info,
437+
kind: StatementKind::Assign(box (temp, dest)),
438+
});
439+
self.tcx.mk_place_deref(temp)
440+
} else {
441+
destination_place
442+
}
441443
} else {
442-
destination.0
444+
trace!("creating temp for return place");
445+
Place::from(self.new_call_temp(caller_body, &callsite, callee_body.return_ty()))
443446
};
444447

445-
let return_block = destination.1;
446-
447448
// Copy the arguments if needed.
448-
let args: Vec<_> = self.make_call_args(args, &callsite, caller_body, return_block);
449+
let args: Vec<_> = self.make_call_args(args, &callsite, caller_body);
449450

450451
let mut integrator = Integrator {
451452
args: &args,
452453
new_locals: Local::new(caller_body.local_decls.len())..,
453454
new_scopes: SourceScope::new(caller_body.source_scopes.len())..,
454455
new_blocks: BasicBlock::new(caller_body.basic_blocks().len())..,
455456
destination: dest,
456-
return_block,
457+
return_block: callsite.target,
457458
cleanup_block: cleanup,
458459
in_cleanup_block: false,
459460
tcx: self.tcx,
@@ -502,7 +503,7 @@ impl Inliner<'tcx> {
502503
caller_body.var_debug_info.extend(callee_body.var_debug_info.drain(..));
503504
caller_body.basic_blocks_mut().extend(callee_body.basic_blocks_mut().drain(..));
504505

505-
caller_body[callsite.bb].terminator = Some(Terminator {
506+
caller_body[callsite.block].terminator = Some(Terminator {
506507
source_info: callsite.source_info,
507508
kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) },
508509
});
@@ -526,7 +527,6 @@ impl Inliner<'tcx> {
526527
args: Vec<Operand<'tcx>>,
527528
callsite: &CallSite<'tcx>,
528529
caller_body: &mut Body<'tcx>,
529-
return_block: BasicBlock,
530530
) -> Vec<Local> {
531531
let tcx = self.tcx;
532532

@@ -557,18 +557,8 @@ impl Inliner<'tcx> {
557557
// `callee_body.spread_arg == None`, instead of special-casing closures.
558558
if tcx.is_closure(callsite.callee.def_id()) {
559559
let mut args = args.into_iter();
560-
let self_ = self.create_temp_if_necessary(
561-
args.next().unwrap(),
562-
callsite,
563-
caller_body,
564-
return_block,
565-
);
566-
let tuple = self.create_temp_if_necessary(
567-
args.next().unwrap(),
568-
callsite,
569-
caller_body,
570-
return_block,
571-
);
560+
let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body);
561+
let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body);
572562
assert!(args.next().is_none());
573563

574564
let tuple = Place::from(tuple);
@@ -588,13 +578,13 @@ impl Inliner<'tcx> {
588578
Operand::Move(tcx.mk_place_field(tuple, Field::new(i), ty.expect_ty()));
589579

590580
// Spill to a local to make e.g., `tmp0`.
591-
self.create_temp_if_necessary(tuple_field, callsite, caller_body, return_block)
581+
self.create_temp_if_necessary(tuple_field, callsite, caller_body)
592582
});
593583

594584
closure_ref_arg.chain(tuple_tmp_args).collect()
595585
} else {
596586
args.into_iter()
597-
.map(|a| self.create_temp_if_necessary(a, callsite, caller_body, return_block))
587+
.map(|a| self.create_temp_if_necessary(a, callsite, caller_body))
598588
.collect()
599589
}
600590
}
@@ -606,46 +596,52 @@ impl Inliner<'tcx> {
606596
arg: Operand<'tcx>,
607597
callsite: &CallSite<'tcx>,
608598
caller_body: &mut Body<'tcx>,
609-
return_block: BasicBlock,
610599
) -> Local {
611-
// FIXME: Analysis of the usage of the arguments to avoid
612-
// unnecessary temporaries.
613-
600+
// Reuse the operand if it is a moved temporary.
614601
if let Operand::Move(place) = &arg {
615602
if let Some(local) = place.as_local() {
616603
if caller_body.local_kind(local) == LocalKind::Temp {
617-
// Reuse the operand if it's a temporary already
618604
return local;
619605
}
620606
}
621607
}
622608

609+
// Otherwise, create a temporary for the argument.
623610
trace!("creating temp for argument {:?}", arg);
624-
// Otherwise, create a temporary for the arg
625-
let arg = Rvalue::Use(arg);
626-
627-
let ty = arg.ty(caller_body, self.tcx);
628-
629-
let arg_tmp = LocalDecl::new(ty, callsite.source_info.span);
630-
let arg_tmp = caller_body.local_decls.push(arg_tmp);
631-
632-
caller_body[callsite.bb].statements.push(Statement {
611+
let arg_ty = arg.ty(caller_body, self.tcx);
612+
let local = self.new_call_temp(caller_body, callsite, arg_ty);
613+
caller_body[callsite.block].statements.push(Statement {
633614
source_info: callsite.source_info,
634-
kind: StatementKind::StorageLive(arg_tmp),
615+
kind: StatementKind::Assign(box (Place::from(local), Rvalue::Use(arg))),
635616
});
636-
caller_body[callsite.bb].statements.push(Statement {
617+
local
618+
}
619+
620+
/// Introduces a new temporary into the caller body that is live for the duration of the call.
621+
fn new_call_temp(
622+
&self,
623+
caller_body: &mut Body<'tcx>,
624+
callsite: &CallSite<'tcx>,
625+
ty: Ty<'tcx>,
626+
) -> Local {
627+
let local = caller_body.local_decls.push(LocalDecl::new(ty, callsite.source_info.span));
628+
629+
caller_body[callsite.block].statements.push(Statement {
637630
source_info: callsite.source_info,
638-
kind: StatementKind::Assign(box (Place::from(arg_tmp), arg)),
631+
kind: StatementKind::StorageLive(local),
639632
});
640-
caller_body[return_block].statements.insert(
641-
0,
642-
Statement {
643-
source_info: callsite.source_info,
644-
kind: StatementKind::StorageDead(arg_tmp),
645-
},
646-
);
647-
648-
arg_tmp
633+
634+
if let Some(block) = callsite.target {
635+
caller_body[block].statements.insert(
636+
0,
637+
Statement {
638+
source_info: callsite.source_info,
639+
kind: StatementKind::StorageDead(local),
640+
},
641+
);
642+
}
643+
644+
local
649645
}
650646
}
651647

@@ -670,7 +666,7 @@ struct Integrator<'a, 'tcx> {
670666
new_scopes: RangeFrom<SourceScope>,
671667
new_blocks: RangeFrom<BasicBlock>,
672668
destination: Place<'tcx>,
673-
return_block: BasicBlock,
669+
return_block: Option<BasicBlock>,
674670
cleanup_block: Option<BasicBlock>,
675671
in_cleanup_block: bool,
676672
tcx: TyCtxt<'tcx>,
@@ -810,7 +806,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
810806
}
811807
}
812808
TerminatorKind::Return => {
813-
terminator.kind = TerminatorKind::Goto { target: self.return_block };
809+
terminator.kind = if let Some(tgt) = self.return_block {
810+
TerminatorKind::Goto { target: tgt }
811+
} else {
812+
TerminatorKind::Unreachable
813+
}
814814
}
815815
TerminatorKind::Resume => {
816816
if let Some(tgt) = self.cleanup_block {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Tests inlining of diverging calls.
2+
//
3+
// ignore-wasm32-bare compiled with panic=abort by default
4+
#![crate_type = "lib"]
5+
6+
// EMIT_MIR inline_diverging.f.Inline.diff
7+
pub fn f() {
8+
sleep();
9+
}
10+
11+
// EMIT_MIR inline_diverging.g.Inline.diff
12+
pub fn g(i: i32) -> u32 {
13+
if i > 0 {
14+
i as u32
15+
} else {
16+
panic();
17+
}
18+
}
19+
20+
// EMIT_MIR inline_diverging.h.Inline.diff
21+
pub fn h() {
22+
call_twice(sleep);
23+
}
24+
25+
#[inline(always)]
26+
pub fn call_twice<R, F: Fn() -> R>(f: F) -> (R, R) {
27+
let a = f();
28+
let b = f();
29+
(a, b)
30+
}
31+
32+
#[inline(always)]
33+
fn panic() -> ! {
34+
panic!();
35+
}
36+
37+
#[inline(always)]
38+
fn sleep() -> ! {
39+
loop {}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
- // MIR for `f` before Inline
2+
+ // MIR for `f` after Inline
3+
4+
fn f() -> () {
5+
let mut _0: (); // return place in scope 0 at $DIR/inline-diverging.rs:7:12: 7:12
6+
let mut _1: !; // in scope 0 at $DIR/inline-diverging.rs:7:12: 9:2
7+
let _2: !; // in scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
8+
+ let mut _3: !; // in scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
9+
+ scope 1 (inlined sleep) { // at $DIR/inline-diverging.rs:8:5: 8:12
10+
+ }
11+
12+
bb0: {
13+
StorageLive(_2); // scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
14+
- sleep(); // scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
15+
- // mir::Constant
16+
- // + span: $DIR/inline-diverging.rs:8:5: 8:10
17+
- // + literal: Const { ty: fn() -> ! {sleep}, val: Value(Scalar(<ZST>)) }
18+
+ StorageLive(_3); // scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
19+
+ goto -> bb1; // scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
20+
+ }
21+
+
22+
+ bb1: {
23+
+ goto -> bb1; // scope 1 at $DIR/inline-diverging.rs:8:5: 8:12
24+
}
25+
}
26+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
- // MIR for `g` before Inline
2+
+ // MIR for `g` after Inline
3+
4+
fn g(_1: i32) -> u32 {
5+
debug i => _1; // in scope 0 at $DIR/inline-diverging.rs:12:10: 12:11
6+
let mut _0: u32; // return place in scope 0 at $DIR/inline-diverging.rs:12:21: 12:24
7+
let mut _2: bool; // in scope 0 at $DIR/inline-diverging.rs:13:8: 13:13
8+
let mut _3: i32; // in scope 0 at $DIR/inline-diverging.rs:13:8: 13:9
9+
let mut _4: i32; // in scope 0 at $DIR/inline-diverging.rs:14:9: 14:10
10+
let mut _5: !; // in scope 0 at $DIR/inline-diverging.rs:15:12: 17:6
11+
let _6: !; // in scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
12+
+ let mut _7: !; // in scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
13+
+ scope 1 (inlined panic) { // at $DIR/inline-diverging.rs:16:9: 16:16
14+
+ }
15+
16+
bb0: {
17+
StorageLive(_2); // scope 0 at $DIR/inline-diverging.rs:13:8: 13:13
18+
StorageLive(_3); // scope 0 at $DIR/inline-diverging.rs:13:8: 13:9
19+
_3 = _1; // scope 0 at $DIR/inline-diverging.rs:13:8: 13:9
20+
_2 = Gt(move _3, const 0_i32); // scope 0 at $DIR/inline-diverging.rs:13:8: 13:13
21+
StorageDead(_3); // scope 0 at $DIR/inline-diverging.rs:13:12: 13:13
22+
switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/inline-diverging.rs:13:5: 17:6
23+
}
24+
25+
bb1: {
26+
StorageLive(_6); // scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
27+
- panic(); // scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
28+
+ StorageLive(_7); // scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
29+
+ begin_panic::<&str>(const "explicit panic"); // scope 1 at $DIR/inline-diverging.rs:16:9: 16:16
30+
// mir::Constant
31+
- // + span: $DIR/inline-diverging.rs:16:9: 16:14
32+
- // + literal: Const { ty: fn() -> ! {panic}, val: Value(Scalar(<ZST>)) }
33+
+ // + span: $DIR/inline-diverging.rs:16:9: 16:16
34+
+ // + literal: Const { ty: fn(&str) -> ! {std::rt::begin_panic::<&str>}, val: Value(Scalar(<ZST>)) }
35+
+ // ty::Const
36+
+ // + ty: &str
37+
+ // + val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 })
38+
+ // mir::Constant
39+
+ // + span: $DIR/inline-diverging.rs:16:9: 16:16
40+
+ // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) }
41+
}
42+
43+
bb2: {
44+
StorageLive(_4); // scope 0 at $DIR/inline-diverging.rs:14:9: 14:10
45+
_4 = _1; // scope 0 at $DIR/inline-diverging.rs:14:9: 14:10
46+
_0 = move _4 as u32 (Misc); // scope 0 at $DIR/inline-diverging.rs:14:9: 14:17
47+
StorageDead(_4); // scope 0 at $DIR/inline-diverging.rs:14:16: 14:17
48+
StorageDead(_2); // scope 0 at $DIR/inline-diverging.rs:18:1: 18:2
49+
return; // scope 0 at $DIR/inline-diverging.rs:18:2: 18:2
50+
}
51+
}
52+

0 commit comments

Comments
 (0)