Remove the CallKind

We used to have CallKind only because there was a requirement to have all successors in a
contiguous memory block. Now that the requirement is gone, remove the CallKind and instead just
have the necessary information inline.

Awesome!
This commit is contained in:
Simonas Kazlauskas 2016-01-29 20:42:02 +02:00
parent 02365fe753
commit 65dd5e6a84
6 changed files with 53 additions and 145 deletions

View file

@ -268,71 +268,13 @@ pub enum Terminator<'tcx> {
func: Operand<'tcx>,
/// Arguments the function is called with
args: Vec<Operand<'tcx>>,
/// The kind of call with associated information
kind: CallKind<'tcx>,
/// Destination for the return value. If some, the call is converging.
destination: Option<(Lvalue<'tcx>, BasicBlock)>,
/// Cleanups to be done if the call unwinds.
cleanup: Option<BasicBlock>
},
}
#[derive(Clone, RustcEncodable, RustcDecodable)]
pub enum CallKind<'tcx> {
/// Diverging function without associated cleanup
Diverging,
/// Diverging function with associated cleanup
DivergingCleanup(BasicBlock),
/// Converging function without associated cleanup
Converging {
/// Destination where the call result is written
destination: Lvalue<'tcx>,
/// Block to branch into on successful return
target: BasicBlock,
},
ConvergingCleanup {
/// Destination where the call result is written
destination: Lvalue<'tcx>,
/// First target is branched to on successful return.
/// Second block contains the cleanups to do on unwind.
targets: (BasicBlock, BasicBlock)
}
}
impl<'tcx> CallKind<'tcx> {
pub fn successors(&self) -> &[BasicBlock] {
match *self {
CallKind::Diverging => &[],
CallKind::DivergingCleanup(ref b) |
CallKind::Converging { target: ref b, .. } => slice::ref_slice(b),
CallKind::ConvergingCleanup { ref targets, .. } => targets.as_slice(),
}
}
pub fn successors_mut(&mut self) -> &mut [BasicBlock] {
match *self {
CallKind::Diverging => &mut [],
CallKind::DivergingCleanup(ref mut b) |
CallKind::Converging { target: ref mut b, .. } => slice::mut_ref_slice(b),
CallKind::ConvergingCleanup { ref mut targets, .. } => targets.as_mut_slice(),
}
}
pub fn destination(&self) -> Option<&Lvalue<'tcx>> {
match *self {
CallKind::Converging { ref destination, .. } |
CallKind::ConvergingCleanup { ref destination, .. } => Some(destination),
CallKind::Diverging |
CallKind::DivergingCleanup(_) => None
}
}
pub fn destination_mut(&mut self) -> Option<&mut Lvalue<'tcx>> {
match *self {
CallKind::Converging { ref mut destination, .. } |
CallKind::ConvergingCleanup { ref mut destination, .. } => Some(destination),
CallKind::Diverging |
CallKind::DivergingCleanup(_) => None
}
}
}
impl<'tcx> Terminator<'tcx> {
pub fn successors(&self) -> Cow<[BasicBlock]> {
use self::Terminator::*;
@ -343,7 +285,11 @@ impl<'tcx> Terminator<'tcx> {
SwitchInt { targets: ref b, .. } => b[..].into_cow(),
Resume => (&[]).into_cow(),
Return => (&[]).into_cow(),
Call { ref kind, .. } => kind.successors()[..].into_cow(),
Call { destination: Some((_, t)), cleanup: Some(c), .. } => vec![t, c].into_cow(),
Call { destination: Some((_, ref t)), cleanup: None, .. } =>
slice::ref_slice(t).into_cow(),
Call { destination: None, cleanup: Some(ref c), .. } => slice::ref_slice(c).into_cow(),
Call { destination: None, cleanup: None, .. } => (&[]).into_cow(),
}
}
@ -358,7 +304,10 @@ impl<'tcx> Terminator<'tcx> {
SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(),
Resume => Vec::new(),
Return => Vec::new(),
Call { ref mut kind, .. } => kind.successors_mut().iter_mut().collect(),
Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut c), .. } => vec![t, c],
Call { destination: Some((_, ref mut t)), cleanup: None, .. } => vec![t],
Call { destination: None, cleanup: Some(ref mut c), .. } => vec![c],
Call { destination: None, cleanup: None, .. } => vec![],
}
}
}
@ -425,8 +374,8 @@ impl<'tcx> Terminator<'tcx> {
SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
Return => write!(fmt, "return"),
Resume => write!(fmt, "resume"),
Call { ref kind, ref func, ref args } => {
if let Some(destination) = kind.destination() {
Call { ref func, ref args, ref destination, .. } => {
if let Some((ref destination, _)) = *destination {
try!(write!(fmt, "{:?} = ", destination));
}
try!(write!(fmt, "{:?}(", func));
@ -464,16 +413,11 @@ impl<'tcx> Terminator<'tcx> {
.chain(iter::once(String::from("otherwise").into()))
.collect()
}
Call { ref kind, .. } => match *kind {
CallKind::Diverging =>
vec![],
CallKind::DivergingCleanup(..) =>
vec!["unwind".into_cow()],
CallKind::Converging { .. } =>
vec!["return".into_cow()],
CallKind::ConvergingCleanup { .. } =>
vec!["return".into_cow(), "unwind".into_cow()],
},
Call { destination: Some(_), cleanup: Some(_), .. } =>
vec!["return".into_cow(), "unwind".into_cow()],
Call { destination: Some(_), cleanup: None, .. } => vec!["return".into_cow()],
Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into_cow()],
Call { destination: None, cleanup: None, .. } => vec![],
}
}
}

View file

@ -179,28 +179,17 @@ macro_rules! make_mir_visitor {
Terminator::Call { ref $($mutability)* func,
ref $($mutability)* args,
ref $($mutability)* kind } => {
ref $($mutability)* destination,
ref $($mutability)* cleanup } => {
self.visit_operand(func);
for arg in args {
self.visit_operand(arg);
}
match *kind {
CallKind::Converging {
ref $($mutability)* destination,
..
} |
CallKind::ConvergingCleanup {
ref $($mutability)* destination,
..
} => {
self.visit_lvalue(destination, LvalueContext::Store);
}
CallKind::Diverging |
CallKind::DivergingCleanup(_) => {}
}
for &target in kind.successors() {
if let Some((ref $($mutability)* destination, target)) = *destination {
self.visit_lvalue(destination, LvalueContext::Store);
self.visit_branch(block, target);
}
cleanup.map(|t| self.visit_branch(block, t));
}
}
}

View file

@ -253,17 +253,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
this.cfg.terminate(block, Terminator::Call {
func: fun,
args: args,
kind: match (cleanup, diverges) {
(None, true) => CallKind::Diverging,
(Some(c), true) => CallKind::DivergingCleanup(c),
(None, false) => CallKind::Converging {
destination: destination.clone(),
target: success
},
(Some(c), false) => CallKind::ConvergingCleanup {
destination: destination.clone(),
targets: (success, c)
}
cleanup: cleanup,
destination: if diverges {
None
} else {
Some ((destination.clone(), success))
}
});
success.unit()

View file

@ -273,7 +273,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
));
let func = Constant {
span: item.1,
ty: tcx.lookup_item_type(item.0).ty.subst(substs),
ty: tcx.lookup_item_type(item.0).ty,
literal: Literal::Item {
def_id: item.0,
kind: ItemKind::Function,
@ -285,10 +285,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
self.cfg.terminate(new_block, Terminator::Call {
func: Operand::Constant(func),
args: vec![Operand::Consume(lvalue.clone())],
kind: CallKind::Converging {
target: old_block,
destination: unit_tmp.clone()
}
destination: Some((unit_tmp.clone(), old_block)),
cleanup: None // were already doing divergence cleanups
});
terminator = Terminator::Goto { target: new_block };
}
@ -383,10 +381,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
self.cfg.terminate(block, Terminator::Call {
func: Operand::Constant(func),
args: vec![Operand::Consume(tuple_ref), index, len],
kind: match cleanup {
None => CallKind::Diverging,
Some(c) => CallKind::DivergingCleanup(c)
}
destination: None,
cleanup: cleanup,
});
}
@ -423,10 +419,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
self.cfg.terminate(block, Terminator::Call {
func: Operand::Constant(func),
args: vec![Operand::Consume(tuple_ref)],
kind: match cleanup {
None => CallKind::Diverging,
Some(c) => CallKind::DivergingCleanup(c)
}
cleanup: cleanup,
destination: None,
});
}

View file

@ -93,8 +93,8 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> {
self.erase_regions_lvalue(discr);
*switch_ty = self.tcx.erase_regions(switch_ty);
},
Terminator::Call { ref mut func, ref mut args, ref mut kind } => {
if let Some(destination) = kind.destination_mut() {
Terminator::Call { ref mut func, ref mut args, ref mut destination, .. } => {
if let Some((ref mut destination, _)) = *destination {
self.erase_regions_lvalue(destination);
}
self.erase_regions_operand(func);

View file

@ -94,7 +94,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None);
}
mir::Terminator::Call { ref func, ref args, ref kind } => {
mir::Terminator::Call { ref func, ref args, ref destination, ref cleanup } => {
// Create the callee. This will always be a fn ptr and hence a kind of scalar.
let callee = self.trans_operand(bcx, func);
let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty);
@ -115,7 +115,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
};
// Prepare the return value destination
let (ret_dest_ty, must_copy_dest) = if let Some(d) = kind.destination() {
let (ret_dest_ty, must_copy_dest) = if let Some((ref d, _)) = *destination {
let dest = self.trans_lvalue(bcx, d);
let ret_ty = dest.ty.to_ty(bcx.tcx());
if !is_foreign && type_of::return_uses_outptr(bcx.ccx(), ret_ty) {
@ -144,9 +144,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
// Many different ways to call a function handled here
match (is_foreign, base::avoid_invoke(bcx), kind) {
match (is_foreign, base::avoid_invoke(bcx), cleanup, destination) {
// The two cases below are the only ones to use LLVMs `invoke`.
(false, false, &mir::CallKind::DivergingCleanup(cleanup)) => {
(false, false, &Some(cleanup), &None) => {
let cleanup = self.bcx(cleanup);
let landingpad = self.make_landing_pad(cleanup);
let unreachable_blk = self.unreachable_block();
@ -158,14 +158,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
Some(attrs),
debugloc);
},
(false, false, &mir::CallKind::ConvergingCleanup { ref targets, .. }) => {
let cleanup = self.bcx(targets.1);
(false, false, &Some(cleanup), &Some((_, success))) => {
let cleanup = self.bcx(cleanup);
let landingpad = self.make_landing_pad(cleanup);
let (target, postinvoke) = if must_copy_dest {
(bcx.fcx.new_block("", None),
Some(self.bcx(targets.0)))
(bcx.fcx.new_block("", None), Some(self.bcx(success)))
} else {
(self.bcx(targets.0), None)
(self.bcx(success), None)
};
let invokeret = build::Invoke(bcx,
callee.immediate(),
@ -205,19 +204,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
build::Br(target, postinvoketarget.llbb, debugloc);
}
},
(false, _, &mir::CallKind::DivergingCleanup(_)) |
(false, _, &mir::CallKind::Diverging) => {
(false, _, _, &None) => {
build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc);
build::Unreachable(bcx);
}
(false, _, k@&mir::CallKind::ConvergingCleanup { .. }) |
(false, _, k@&mir::CallKind::Converging { .. }) => {
// FIXME: Bug #20046
let target = match *k {
mir::CallKind::ConvergingCleanup { targets, .. } => targets.0,
mir::CallKind::Converging { target, .. } => target,
_ => unreachable!()
};
(false, _, _, &Some((_, target))) => {
let llret = build::Call(bcx,
callee.immediate(),
&llargs[..],
@ -231,7 +222,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
build::Br(bcx, self.llblock(target), debugloc);
}
// Foreign functions
(true, _, k) => {
(true, _, _, destination) => {
let (dest, _) = ret_dest_ty
.expect("return destination is not set");
bcx = foreign::trans_native_call(bcx,
@ -241,13 +232,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
&llargs[..],
arg_tys,
debugloc);
match *k {
mir::CallKind::ConvergingCleanup { targets, .. } =>
build::Br(bcx, self.llblock(targets.0), debugloc),
mir::CallKind::Converging { target, .. } =>
build::Br(bcx, self.llblock(target), debugloc),
_ => ()
};
if let Some((_, target)) = *destination {
build::Br(bcx, self.llblock(target), debugloc);
}
},
}
}