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:
parent
7312e0a163
commit
893a66d7a1
@ -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 that’s 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 that’s being called
|
||||
func: Operand<'tcx>,
|
||||
/// Arguments the function is called with
|
||||
args: Vec<Operand<'tcx>>,
|
||||
/// Some, if there’s 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 didn’t bother rebasing this
|
||||
unimplemented!()
|
||||
},
|
||||
DivergingCall { .. } => {
|
||||
// the author didn’t 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()
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,6 +164,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||
}
|
||||
|
||||
build::Br(bcx, self.llblock(targets.0), DebugLoc::None)
|
||||
},
|
||||
|
||||
mir::Terminator::DivergingCall { .. } => {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user