Split Call into Call and DivergingCall

DivergingCall is different enough from the regular converging Call to warrant the split. This also
inlines CallData struct and creates a new CallTargets enum in order to have a way to differentiate
between calls that do not have an associated cleanup block.

Note, that this patch still does not produce DivergingCall terminator anywhere. Look for that in
the next patches.
This commit is contained in:
Simonas Kazlauskas 2015-12-14 23:27:58 +02:00
parent 7312e0a163
commit 893a66d7a1
5 changed files with 113 additions and 57 deletions

View File

@ -256,13 +256,51 @@ pub enum Terminator<'tcx> {
/// `END_BLOCK`.
Return,
/// block ends with a call; it should have two successors. The
/// first successor indicates normal return. The second indicates
/// unwinding.
/// Block ends with a call of a converging function
Call {
data: CallData<'tcx>,
targets: (BasicBlock, BasicBlock),
/// The function thats being called
func: Operand<'tcx>,
/// Arguments the function is called with
args: Vec<Operand<'tcx>>,
/// Location to write the return value into
destination: Lvalue<'tcx>,
targets: CallTargets,
},
/// Block ends with a call of a diverging function.
DivergingCall {
/// The function thats being called
func: Operand<'tcx>,
/// Arguments the function is called with
args: Vec<Operand<'tcx>>,
/// Some, if theres any cleanup to be done when function unwinds
cleanup: Option<BasicBlock>,
}
}
#[derive(RustcEncodable, RustcDecodable)]
pub enum CallTargets {
/// The only target that should be entered when function returns normally.
Return(BasicBlock),
/// In addition to the normal-return block, function has associated cleanup that should be done
/// when function unwinds.
WithCleanup((BasicBlock, BasicBlock))
}
impl CallTargets {
pub fn as_slice(&self) -> &[BasicBlock] {
match *self {
CallTargets::Return(ref b) => slice::ref_slice(b),
CallTargets::WithCleanup(ref bs) => bs.as_slice()
}
}
pub fn as_mut_slice(&mut self) -> &mut [BasicBlock] {
match *self {
CallTargets::Return(ref mut b) => slice::mut_ref_slice(b),
CallTargets::WithCleanup(ref mut bs) => bs.as_mut_slice()
}
}
}
impl<'tcx> Terminator<'tcx> {
@ -271,12 +309,17 @@ impl<'tcx> Terminator<'tcx> {
match *self {
Goto { target: ref b } => slice::ref_slice(b),
Panic { target: ref b } => slice::ref_slice(b),
If { cond: _, targets: ref b } => b.as_slice(),
If { targets: ref b, .. } => b.as_slice(),
Switch { targets: ref b, .. } => b,
SwitchInt { targets: ref b, .. } => b,
Diverge => &[],
Return => &[],
Call { data: _, targets: ref b } => b.as_slice(),
Call { targets: ref b, .. } => b.as_slice(),
DivergingCall { cleanup: ref b, .. } => if let Some(b) = b.as_ref() {
slice::ref_slice(b)
} else {
&mut []
},
}
}
@ -285,28 +328,21 @@ impl<'tcx> Terminator<'tcx> {
match *self {
Goto { target: ref mut b } => slice::mut_ref_slice(b),
Panic { target: ref mut b } => slice::mut_ref_slice(b),
If { cond: _, targets: ref mut b } => b.as_mut_slice(),
If { targets: ref mut b, .. } => b.as_mut_slice(),
Switch { targets: ref mut b, .. } => b,
SwitchInt { targets: ref mut b, .. } => b,
Diverge => &mut [],
Return => &mut [],
Call { data: _, targets: ref mut b } => b.as_mut_slice(),
Call { targets: ref mut b, .. } => b.as_mut_slice(),
DivergingCall { cleanup: ref mut b, .. } => if let Some(b) = b.as_mut() {
slice::mut_ref_slice(b)
} else {
&mut []
},
}
}
}
#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct CallData<'tcx> {
/// where the return value is written to
pub destination: Lvalue<'tcx>,
/// the fn being called
pub func: Operand<'tcx>,
/// the arguments
pub args: Vec<Operand<'tcx>>,
}
impl<'tcx> BasicBlockData<'tcx> {
pub fn new(terminator: Terminator<'tcx>) -> BasicBlockData<'tcx> {
BasicBlockData {
@ -357,15 +393,13 @@ impl<'tcx> Terminator<'tcx> {
SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
Diverge => write!(fmt, "diverge"),
Return => write!(fmt, "return"),
Call { data: ref c, .. } => {
try!(write!(fmt, "{:?} = {:?}(", c.destination, c.func));
for (index, arg) in c.args.iter().enumerate() {
if index > 0 {
try!(write!(fmt, ", "));
}
try!(write!(fmt, "{:?}", arg));
}
write!(fmt, ")")
Call { .. } => {
// the author didnt bother rebasing this
unimplemented!()
},
DivergingCall { .. } => {
// the author didnt bother rebasing this
unimplemented!()
}
}
}
@ -378,6 +412,7 @@ impl<'tcx> Terminator<'tcx> {
Goto { .. } | Panic { .. } => vec!["".into_cow()],
If { .. } => vec!["true".into_cow(), "false".into_cow()],
Call { .. } => vec!["return".into_cow(), "unwind".into_cow()],
DivergingCall { .. } => vec!["unwind".into_cow()],
Switch { ref adt_def, .. } => {
adt_def.variants
.iter()

View File

@ -137,16 +137,26 @@ pub trait Visitor<'tcx> {
Terminator::Return => {
}
Terminator::Call { ref data, ref targets } => {
self.visit_lvalue(&data.destination, LvalueContext::Store);
self.visit_operand(&data.func);
for arg in &data.args {
Terminator::Call { ref func, ref args, ref destination, ref targets } => {
self.visit_lvalue(destination, LvalueContext::Store);
self.visit_operand(func);
for arg in args {
self.visit_operand(arg);
}
for &target in targets.as_slice() {
self.visit_branch(block, target);
}
}
Terminator::DivergingCall { ref func, ref args, ref cleanup } => {
self.visit_operand(func);
for arg in args {
self.visit_operand(arg);
}
for &target in cleanup.as_ref() {
self.visit_branch(block, target);
}
}
}
}
@ -424,16 +434,29 @@ pub trait MutVisitor<'tcx> {
Terminator::Return => {
}
Terminator::Call { ref mut data, ref mut targets } => {
self.visit_lvalue(&mut data.destination, LvalueContext::Store);
self.visit_operand(&mut data.func);
for arg in &mut data.args {
Terminator::Call { ref mut func,
ref mut args,
ref mut destination,
ref mut targets } => {
self.visit_lvalue(destination, LvalueContext::Store);
self.visit_operand(func);
for arg in args {
self.visit_operand(arg);
}
for &target in targets.as_slice() {
self.visit_branch(block, target);
}
}
Terminator::DivergingCall { ref mut func, ref mut args, ref mut cleanup } => {
self.visit_operand(func);
for arg in args {
self.visit_operand(arg);
}
for &target in cleanup.as_ref() {
self.visit_branch(block, target);
}
}
}
}

View File

@ -218,14 +218,13 @@ impl<'a,'tcx> Builder<'a,'tcx> {
.collect();
let success = this.cfg.start_new_block();
let panic = this.diverge_cleanup();
let targets = CallTargets::WithCleanup((success, panic));
this.cfg.terminate(block,
Terminator::Call {
data: CallData {
destination: destination.clone(),
func: fun,
args: args,
},
targets: (success, panic),
func: fun,
args: args,
destination: destination.clone(),
targets: targets
});
success.unit()
}

View File

@ -90,28 +90,23 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> {
Terminator::Switch { ref mut discr, .. } => {
self.erase_regions_lvalue(discr);
}
Terminator::SwitchInt {
ref mut discr,
ref mut switch_ty,
..
} => {
Terminator::SwitchInt { ref mut discr, ref mut switch_ty, .. } => {
self.erase_regions_lvalue(discr);
*switch_ty = self.tcx.erase_regions(switch_ty);
},
Terminator::Call {
data: CallData {
ref mut destination,
ref mut func,
ref mut args
},
..
} => {
Terminator::Call { ref mut destination, ref mut func, ref mut args, .. } => {
self.erase_regions_lvalue(destination);
self.erase_regions_operand(func);
for arg in &mut *args {
self.erase_regions_operand(arg);
}
}
Terminator::DivergingCall { ref mut func, ref mut args, .. } => {
self.erase_regions_operand(func);
for arg in &mut *args {
self.erase_regions_operand(arg);
}
}
}
}

View File

@ -164,6 +164,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
build::Br(bcx, self.llblock(targets.0), DebugLoc::None)
},
mir::Terminator::DivergingCall { .. } => {
unimplemented!()
}
}
}