Refactor FunctionCx::codgen_terminator
- Move closures defined in codegen_terminator into a separate helper structure and implementation. - Create helper functions for each of the complex match arms on the terminators kind in codegen_terminator.
This commit is contained in:
parent
1a6e9e2408
commit
a618ad6335
1 changed files with 790 additions and 690 deletions
|
@ -13,6 +13,8 @@ use crate::meth;
|
||||||
|
|
||||||
use crate::traits::*;
|
use crate::traits::*;
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use syntax::symbol::Symbol;
|
use syntax::symbol::Symbol;
|
||||||
use syntax_pos::Pos;
|
use syntax_pos::Pos;
|
||||||
|
|
||||||
|
@ -21,128 +23,117 @@ use super::place::PlaceRef;
|
||||||
use super::operand::{OperandValue, OperandRef};
|
use super::operand::{OperandValue, OperandRef};
|
||||||
use super::operand::OperandValue::{Pair, Ref, Immediate};
|
use super::operand::OperandValue::{Pair, Ref, Immediate};
|
||||||
|
|
||||||
impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
/// Used by `FunctionCx::codegen_terminator` for emitting common patterns
|
||||||
pub fn codegen_block(
|
/// e.g., creating a basic block, calling a function, etc.
|
||||||
&mut self,
|
struct TerminatorCodegenHelper<'a, 'tcx> {
|
||||||
bb: mir::BasicBlock,
|
bb: &'a mir::BasicBlock,
|
||||||
) {
|
terminator: &'a mir::Terminator<'tcx>,
|
||||||
let mut bx = self.build_block(bb);
|
funclet_bb: Option<mir::BasicBlock>,
|
||||||
let data = &self.mir[bb];
|
|
||||||
|
|
||||||
debug!("codegen_block({:?}={:?})", bb, data);
|
|
||||||
|
|
||||||
for statement in &data.statements {
|
|
||||||
bx = self.codegen_statement(bx, statement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.codegen_terminator(bx, bb, data.terminator());
|
impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
|
||||||
}
|
/// Returns the associated funclet from `FunctionCx::funclets` for the
|
||||||
|
/// `funclet_bb` member if it is not `None`.
|
||||||
fn codegen_terminator(
|
fn funclet<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
|
||||||
&mut self,
|
&self,
|
||||||
mut bx: Bx,
|
fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
|
||||||
bb: mir::BasicBlock,
|
) -> Option<&'c Bx::Funclet> {
|
||||||
terminator: &mir::Terminator<'tcx>
|
match self.funclet_bb {
|
||||||
) {
|
Some(funcl) => fx.funclets[funcl].as_ref(),
|
||||||
debug!("codegen_terminator: {:?}", terminator);
|
|
||||||
|
|
||||||
// Create the cleanup bundle, if needed.
|
|
||||||
let tcx = self.cx.tcx();
|
|
||||||
let span = terminator.source_info.span;
|
|
||||||
let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb);
|
|
||||||
|
|
||||||
// HACK(eddyb) force the right lifetimes, NLL can't figure them out.
|
|
||||||
fn funclet_closure_factory<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
|
|
||||||
funclet_bb: Option<mir::BasicBlock>
|
|
||||||
) -> impl for<'b> Fn(
|
|
||||||
&'b FunctionCx<'a, 'tcx, Bx>,
|
|
||||||
) -> Option<&'b Bx::Funclet> {
|
|
||||||
move |this| {
|
|
||||||
match funclet_bb {
|
|
||||||
Some(funclet_bb) => this.funclets[funclet_bb].as_ref(),
|
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
let funclet = funclet_closure_factory(funclet_bb);
|
|
||||||
|
|
||||||
let lltarget = |this: &mut Self, target: mir::BasicBlock| {
|
fn lltarget<'b, 'c, Bx: BuilderMethods<'b, 'tcx>>(
|
||||||
let lltarget = this.blocks[target];
|
&self,
|
||||||
let target_funclet = this.cleanup_kinds[target].funclet_bb(target);
|
fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
|
||||||
match (funclet_bb, target_funclet) {
|
target: mir::BasicBlock,
|
||||||
|
) -> (Bx::BasicBlock, bool) {
|
||||||
|
let span = self.terminator.source_info.span;
|
||||||
|
let lltarget = fx.blocks[target];
|
||||||
|
let target_funclet = fx.cleanup_kinds[target].funclet_bb(target);
|
||||||
|
match (self.funclet_bb, target_funclet) {
|
||||||
(None, None) => (lltarget, false),
|
(None, None) => (lltarget, false),
|
||||||
(Some(f), Some(t_f))
|
(Some(f), Some(t_f)) if f == t_f || !base::wants_msvc_seh(fx.cx.tcx().sess) =>
|
||||||
if f == t_f || !base::wants_msvc_seh(tcx.sess)
|
(lltarget, false),
|
||||||
=> (lltarget, false),
|
|
||||||
(None, Some(_)) => {
|
|
||||||
// jump *into* cleanup - need a landing pad if GNU
|
// jump *into* cleanup - need a landing pad if GNU
|
||||||
(this.landing_pad_to(target), false)
|
(None, Some(_)) => (fx.landing_pad_to(target), false),
|
||||||
}
|
(Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", self.terminator),
|
||||||
(Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", terminator),
|
(Some(_), Some(_)) => (fx.landing_pad_to(target), true),
|
||||||
(Some(_), Some(_)) => {
|
|
||||||
(this.landing_pad_to(target), true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let llblock = |this: &mut Self, target: mir::BasicBlock| {
|
/// Create a basic block.
|
||||||
let (lltarget, is_cleanupret) = lltarget(this, target);
|
fn llblock<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
|
||||||
|
&self,
|
||||||
|
fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
|
||||||
|
target: mir::BasicBlock,
|
||||||
|
) -> Bx::BasicBlock {
|
||||||
|
let (lltarget, is_cleanupret) = self.lltarget(fx, target);
|
||||||
if is_cleanupret {
|
if is_cleanupret {
|
||||||
// MSVC cross-funclet jump - need a trampoline
|
// MSVC cross-funclet jump - need a trampoline
|
||||||
|
|
||||||
debug!("llblock: creating cleanup trampoline for {:?}", target);
|
debug!("llblock: creating cleanup trampoline for {:?}", target);
|
||||||
let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target);
|
let name = &format!("{:?}_cleanup_trampoline_{:?}", self.bb, target);
|
||||||
let mut trampoline = this.new_block(name);
|
let mut trampoline = fx.new_block(name);
|
||||||
trampoline.cleanup_ret(funclet(this).unwrap(), Some(lltarget));
|
trampoline.cleanup_ret(self.funclet(fx).unwrap(),
|
||||||
|
Some(lltarget));
|
||||||
trampoline.llbb()
|
trampoline.llbb()
|
||||||
} else {
|
} else {
|
||||||
lltarget
|
lltarget
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
let funclet_br =
|
fn funclet_br<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
|
||||||
|this: &mut Self, bx: &mut Bx, target: mir::BasicBlock| {
|
&self,
|
||||||
let (lltarget, is_cleanupret) = lltarget(this, target);
|
fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
|
||||||
|
bx: &mut Bx,
|
||||||
|
target: mir::BasicBlock,
|
||||||
|
) {
|
||||||
|
let (lltarget, is_cleanupret) = self.lltarget(fx, target);
|
||||||
if is_cleanupret {
|
if is_cleanupret {
|
||||||
// micro-optimization: generate a `ret` rather than a jump
|
// micro-optimization: generate a `ret` rather than a jump
|
||||||
// to a trampoline.
|
// to a trampoline.
|
||||||
bx.cleanup_ret(funclet(this).unwrap(), Some(lltarget));
|
bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget));
|
||||||
} else {
|
} else {
|
||||||
bx.br(lltarget);
|
bx.br(lltarget);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
let do_call = |
|
/// Call `fn_ptr` of `fn_ty` with the arguments `llargs`, the optional
|
||||||
this: &mut Self,
|
/// return destination `destination` and the cleanup function `cleanup`.
|
||||||
|
fn do_call<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
|
||||||
|
&self,
|
||||||
|
fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
|
||||||
bx: &mut Bx,
|
bx: &mut Bx,
|
||||||
fn_ty: FnType<'tcx, Ty<'tcx>>,
|
fn_ty: FnType<'tcx, Ty<'tcx>>,
|
||||||
fn_ptr: Bx::Value,
|
fn_ptr: Bx::Value,
|
||||||
llargs: &[Bx::Value],
|
llargs: &[Bx::Value],
|
||||||
destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
|
destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
|
||||||
cleanup: Option<mir::BasicBlock>
|
cleanup: Option<mir::BasicBlock>,
|
||||||
| {
|
) {
|
||||||
if let Some(cleanup) = cleanup {
|
if let Some(cleanup) = cleanup {
|
||||||
let ret_bx = if let Some((_, target)) = destination {
|
let ret_bx = if let Some((_, target)) = destination {
|
||||||
this.blocks[target]
|
fx.blocks[target]
|
||||||
} else {
|
} else {
|
||||||
this.unreachable_block()
|
fx.unreachable_block()
|
||||||
};
|
};
|
||||||
let invokeret = bx.invoke(fn_ptr,
|
let invokeret = bx.invoke(fn_ptr,
|
||||||
&llargs,
|
&llargs,
|
||||||
ret_bx,
|
ret_bx,
|
||||||
llblock(this, cleanup),
|
self.llblock(fx, cleanup),
|
||||||
funclet(this));
|
self.funclet(fx));
|
||||||
bx.apply_attrs_callsite(&fn_ty, invokeret);
|
bx.apply_attrs_callsite(&fn_ty, invokeret);
|
||||||
|
|
||||||
if let Some((ret_dest, target)) = destination {
|
if let Some((ret_dest, target)) = destination {
|
||||||
let mut ret_bx = this.build_block(target);
|
let mut ret_bx = fx.build_block(target);
|
||||||
this.set_debug_loc(&mut ret_bx, terminator.source_info);
|
fx.set_debug_loc(&mut ret_bx, self.terminator.source_info);
|
||||||
this.store_return(&mut ret_bx, ret_dest, &fn_ty.ret, invokeret);
|
fx.store_return(&mut ret_bx, ret_dest, &fn_ty.ret, invokeret);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let llret = bx.call(fn_ptr, &llargs, funclet(this));
|
let llret = bx.call(fn_ptr, &llargs, self.funclet(fx));
|
||||||
bx.apply_attrs_callsite(&fn_ty, llret);
|
bx.apply_attrs_callsite(&fn_ty, llret);
|
||||||
if this.mir[bb].is_cleanup {
|
if fx.mir[*self.bb].is_cleanup {
|
||||||
// Cleanup is always the cold path. Don't inline
|
// Cleanup is always the cold path. Don't inline
|
||||||
// drop glue. Also, when there is a deeply-nested
|
// drop glue. Also, when there is a deeply-nested
|
||||||
// struct, there are "symmetry" issues that cause
|
// struct, there are "symmetry" issues that cause
|
||||||
|
@ -151,18 +142,24 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((ret_dest, target)) = destination {
|
if let Some((ret_dest, target)) = destination {
|
||||||
this.store_return(bx, ret_dest, &fn_ty.ret, llret);
|
fx.store_return(bx, ret_dest, &fn_ty.ret, llret);
|
||||||
funclet_br(this, bx, target);
|
self.funclet_br(fx, bx, target);
|
||||||
} else {
|
} else {
|
||||||
bx.unreachable();
|
bx.unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.set_debug_loc(&mut bx, terminator.source_info);
|
/// Codegen implementations for some terminator variants.
|
||||||
match terminator.kind {
|
impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
mir::TerminatorKind::Resume => {
|
/// Generates code for a `Resume` terminator.
|
||||||
if let Some(funclet) = funclet(self) {
|
fn codegen_resume_terminator<'b>(
|
||||||
|
&mut self,
|
||||||
|
helper: TerminatorCodegenHelper<'b, 'tcx>,
|
||||||
|
mut bx: Bx,
|
||||||
|
) {
|
||||||
|
if let Some(funclet) = helper.funclet(self) {
|
||||||
bx.cleanup_ret(funclet, None);
|
bx.cleanup_ret(funclet, None);
|
||||||
} else {
|
} else {
|
||||||
let slot = self.get_personality_slot(&mut bx);
|
let slot = self.get_personality_slot(&mut bx);
|
||||||
|
@ -178,27 +175,27 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
lp = bx.insert_value(lp, lp1, 1);
|
lp = bx.insert_value(lp, lp1, 1);
|
||||||
bx.resume(lp);
|
bx.resume(lp);
|
||||||
} else {
|
} else {
|
||||||
bx.call(bx.eh_unwind_resume(), &[lp0], funclet(self));
|
bx.call(bx.eh_unwind_resume(), &[lp0],
|
||||||
|
helper.funclet(self));
|
||||||
bx.unreachable();
|
bx.unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mir::TerminatorKind::Abort => {
|
fn codegen_switchint_terminator<'b>(
|
||||||
bx.abort();
|
&mut self,
|
||||||
bx.unreachable();
|
helper: TerminatorCodegenHelper<'b, 'tcx>,
|
||||||
}
|
mut bx: Bx,
|
||||||
|
discr: &mir::Operand<'tcx>,
|
||||||
mir::TerminatorKind::Goto { target } => {
|
switch_ty: Ty<'tcx>,
|
||||||
funclet_br(self, &mut bx, target);
|
values: &Cow<'tcx, [u128]>,
|
||||||
}
|
targets: &Vec<mir::BasicBlock>,
|
||||||
|
) {
|
||||||
mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
|
let discr = self.codegen_operand(&mut bx, &discr);
|
||||||
let discr = self.codegen_operand(&mut bx, discr);
|
|
||||||
if targets.len() == 2 {
|
if targets.len() == 2 {
|
||||||
// If there are two targets, emit br instead of switch
|
// If there are two targets, emit br instead of switch
|
||||||
let lltrue = llblock(self, targets[0]);
|
let lltrue = helper.llblock(self, targets[0]);
|
||||||
let llfalse = llblock(self, targets[1]);
|
let llfalse = helper.llblock(self, targets[1]);
|
||||||
if switch_ty == bx.tcx().types.bool {
|
if switch_ty == bx.tcx().types.bool {
|
||||||
// Don't generate trivial icmps when switching on bool
|
// Don't generate trivial icmps when switching on bool
|
||||||
if let [0] = values[..] {
|
if let [0] = values[..] {
|
||||||
|
@ -218,20 +215,23 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
} else {
|
} else {
|
||||||
let (otherwise, targets) = targets.split_last().unwrap();
|
let (otherwise, targets) = targets.split_last().unwrap();
|
||||||
let switch = bx.switch(discr.immediate(),
|
let switch = bx.switch(discr.immediate(),
|
||||||
llblock(self, *otherwise),
|
helper.llblock(self, *otherwise),
|
||||||
values.len());
|
values.len());
|
||||||
let switch_llty = bx.immediate_backend_type(
|
let switch_llty = bx.immediate_backend_type(
|
||||||
bx.layout_of(switch_ty)
|
bx.layout_of(switch_ty)
|
||||||
);
|
);
|
||||||
for (&value, target) in values.iter().zip(targets) {
|
for (&value, target) in values.iter().zip(targets) {
|
||||||
let llval = bx.const_uint_big(switch_llty, value);
|
let llval = bx.const_uint_big(switch_llty, value);
|
||||||
let llbb = llblock(self, *target);
|
let llbb = helper.llblock(self, *target);
|
||||||
bx.add_case(switch, llval, llbb)
|
bx.add_case(switch, llval, llbb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mir::TerminatorKind::Return => {
|
fn codegen_return_terminator<'b>(
|
||||||
|
&mut self,
|
||||||
|
mut bx: Bx,
|
||||||
|
) {
|
||||||
if self.fn_ty.variadic {
|
if self.fn_ty.variadic {
|
||||||
if let Some(va_list) = self.va_list_ref {
|
if let Some(va_list) = self.va_list_ref {
|
||||||
bx.va_end(va_list.llval);
|
bx.va_end(va_list.llval);
|
||||||
|
@ -244,7 +244,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
PassMode::Ignore(IgnoreMode::CVarArgs) => {
|
PassMode::Ignore(IgnoreMode::CVarArgs) => {
|
||||||
bug!("C variadic arguments should never be the return type");
|
bug!("C-variadic arguments should never be the return type");
|
||||||
}
|
}
|
||||||
|
|
||||||
PassMode::Direct(_) | PassMode::Pair(..) => {
|
PassMode::Direct(_) | PassMode::Pair(..) => {
|
||||||
|
@ -291,18 +291,22 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
bx.ret(llval);
|
bx.ret(llval);
|
||||||
}
|
}
|
||||||
|
|
||||||
mir::TerminatorKind::Unreachable => {
|
|
||||||
bx.unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
mir::TerminatorKind::Drop { ref location, target, unwind } => {
|
fn codegen_drop_terminator<'b>(
|
||||||
|
&mut self,
|
||||||
|
helper: TerminatorCodegenHelper<'b, 'tcx>,
|
||||||
|
mut bx: Bx,
|
||||||
|
location: &mir::Place<'tcx>,
|
||||||
|
target: mir::BasicBlock,
|
||||||
|
unwind: Option<mir::BasicBlock>,
|
||||||
|
) {
|
||||||
let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx());
|
let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx());
|
||||||
let ty = self.monomorphize(&ty);
|
let ty = self.monomorphize(&ty);
|
||||||
let drop_fn = monomorphize::resolve_drop_in_place(bx.tcx(), ty);
|
let drop_fn = monomorphize::resolve_drop_in_place(bx.tcx(), ty);
|
||||||
|
|
||||||
if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def {
|
if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def {
|
||||||
// we don't actually need to drop anything.
|
// we don't actually need to drop anything.
|
||||||
funclet_br(self, &mut bx, target);
|
helper.funclet_br(self, &mut bx, target);
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,8 +321,8 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
};
|
};
|
||||||
let (drop_fn, fn_ty) = match ty.sty {
|
let (drop_fn, fn_ty) = match ty.sty {
|
||||||
ty::Dynamic(..) => {
|
ty::Dynamic(..) => {
|
||||||
let sig = drop_fn.fn_sig(tcx);
|
let sig = drop_fn.fn_sig(self.cx.tcx());
|
||||||
let sig = tcx.normalize_erasing_late_bound_regions(
|
let sig = self.cx.tcx().normalize_erasing_late_bound_regions(
|
||||||
ty::ParamEnv::reveal_all(),
|
ty::ParamEnv::reveal_all(),
|
||||||
&sig,
|
&sig,
|
||||||
);
|
);
|
||||||
|
@ -332,12 +336,23 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
bx.fn_type_of_instance(&drop_fn))
|
bx.fn_type_of_instance(&drop_fn))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
do_call(self, &mut bx, fn_ty, drop_fn, args,
|
helper.do_call(self, &mut bx, fn_ty, drop_fn, args,
|
||||||
Some((ReturnDest::Nothing, target)),
|
Some((ReturnDest::Nothing, target)),
|
||||||
unwind);
|
unwind);
|
||||||
}
|
}
|
||||||
|
|
||||||
mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => {
|
fn codegen_assert_terminator<'b>(
|
||||||
|
&mut self,
|
||||||
|
helper: TerminatorCodegenHelper<'b, 'tcx>,
|
||||||
|
mut bx: Bx,
|
||||||
|
terminator: &mir::Terminator<'tcx>,
|
||||||
|
cond: &mir::Operand<'tcx>,
|
||||||
|
expected: bool,
|
||||||
|
msg: &mir::AssertMessage<'tcx>,
|
||||||
|
target: mir::BasicBlock,
|
||||||
|
cleanup: Option<mir::BasicBlock>,
|
||||||
|
) {
|
||||||
|
let span = terminator.source_info.span;
|
||||||
let cond = self.codegen_operand(&mut bx, cond).immediate();
|
let cond = self.codegen_operand(&mut bx, cond).immediate();
|
||||||
let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1);
|
let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1);
|
||||||
|
|
||||||
|
@ -356,7 +371,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
|
|
||||||
// Don't codegen the panic block if success if known.
|
// Don't codegen the panic block if success if known.
|
||||||
if const_cond == Some(expected) {
|
if const_cond == Some(expected) {
|
||||||
funclet_br(self, &mut bx, target);
|
helper.funclet_br(self, &mut bx, target);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,7 +379,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
let cond = bx.expect(cond, expected);
|
let cond = bx.expect(cond, expected);
|
||||||
|
|
||||||
// Create the failure block and the conditional branch to it.
|
// Create the failure block and the conditional branch to it.
|
||||||
let lltarget = llblock(self, target);
|
let lltarget = helper.llblock(self, target);
|
||||||
let panic_block = self.new_block("panic");
|
let panic_block = self.new_block("panic");
|
||||||
if expected {
|
if expected {
|
||||||
bx.cond_br(cond, lltarget, panic_block.llbb());
|
bx.cond_br(cond, lltarget, panic_block.llbb());
|
||||||
|
@ -382,9 +397,9 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
let filename = bx.const_str_slice(filename);
|
let filename = bx.const_str_slice(filename);
|
||||||
let line = bx.const_u32(loc.line as u32);
|
let line = bx.const_u32(loc.line as u32);
|
||||||
let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
|
let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
|
||||||
let align = tcx.data_layout.aggregate_align.abi
|
let align = self.cx.tcx().data_layout.aggregate_align.abi
|
||||||
.max(tcx.data_layout.i32_align.abi)
|
.max(self.cx.tcx().data_layout.i32_align.abi)
|
||||||
.max(tcx.data_layout.pointer_align.abi);
|
.max(self.cx.tcx().data_layout.pointer_align.abi);
|
||||||
|
|
||||||
// Put together the arguments to the panic entry point.
|
// Put together the arguments to the panic entry point.
|
||||||
let (lang_item, args) = match *msg {
|
let (lang_item, args) = match *msg {
|
||||||
|
@ -426,20 +441,20 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
let llfn = bx.get_fn(instance);
|
let llfn = bx.get_fn(instance);
|
||||||
|
|
||||||
// Codegen the actual panic invoke/call.
|
// Codegen the actual panic invoke/call.
|
||||||
do_call(self, &mut bx, fn_ty, llfn, &args, None, cleanup);
|
helper.do_call(self, &mut bx, fn_ty, llfn, &args, None, cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
mir::TerminatorKind::DropAndReplace { .. } => {
|
fn codegen_call_terminator<'b>(
|
||||||
bug!("undesugared DropAndReplace in codegen: {:?}", terminator);
|
&mut self,
|
||||||
}
|
helper: TerminatorCodegenHelper<'b, 'tcx>,
|
||||||
|
mut bx: Bx,
|
||||||
mir::TerminatorKind::Call {
|
terminator: &mir::Terminator<'tcx>,
|
||||||
ref func,
|
func: &mir::Operand<'tcx>,
|
||||||
ref args,
|
args: &Vec<mir::Operand<'tcx>>,
|
||||||
ref destination,
|
destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>,
|
||||||
cleanup,
|
cleanup: Option<mir::BasicBlock>,
|
||||||
from_hir_call: _
|
) {
|
||||||
} => {
|
let span = terminator.source_info.span;
|
||||||
// Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
|
// Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
|
||||||
let callee = self.codegen_operand(&mut bx, func);
|
let callee = self.codegen_operand(&mut bx, func);
|
||||||
|
|
||||||
|
@ -454,7 +469,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
ty::FnPtr(_) => {
|
ty::FnPtr(_) => {
|
||||||
(None, Some(callee.immediate()))
|
(None, Some(callee.immediate()))
|
||||||
}
|
}
|
||||||
_ => bug!("{} is not callable", callee.layout.ty)
|
_ => bug!("{} is not callable", callee.layout.ty),
|
||||||
};
|
};
|
||||||
let def = instance.map(|i| i.def);
|
let def = instance.map(|i| i.def);
|
||||||
let sig = callee.layout.ty.fn_sig(bx.tcx());
|
let sig = callee.layout.ty.fn_sig(bx.tcx());
|
||||||
|
@ -466,8 +481,8 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
|
|
||||||
// Handle intrinsics old codegen wants Expr's for, ourselves.
|
// Handle intrinsics old codegen wants Expr's for, ourselves.
|
||||||
let intrinsic = match def {
|
let intrinsic = match def {
|
||||||
Some(ty::InstanceDef::Intrinsic(def_id))
|
Some(ty::InstanceDef::Intrinsic(def_id)) =>
|
||||||
=> Some(bx.tcx().item_name(def_id).as_str()),
|
Some(bx.tcx().item_name(def_id).as_str()),
|
||||||
_ => None
|
_ => None
|
||||||
};
|
};
|
||||||
let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
|
let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
|
||||||
|
@ -476,7 +491,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
if let Some(destination_ref) = destination.as_ref() {
|
if let Some(destination_ref) = destination.as_ref() {
|
||||||
let &(ref dest, target) = destination_ref;
|
let &(ref dest, target) = destination_ref;
|
||||||
self.codegen_transmute(&mut bx, &args[0], dest);
|
self.codegen_transmute(&mut bx, &args[0], dest);
|
||||||
funclet_br(self, &mut bx, target);
|
helper.funclet_br(self, &mut bx, target);
|
||||||
} else {
|
} else {
|
||||||
// If we are trying to transmute to an uninhabited type,
|
// If we are trying to transmute to an uninhabited type,
|
||||||
// it is likely there is no allotted destination. In fact,
|
// it is likely there is no allotted destination. In fact,
|
||||||
|
@ -504,15 +519,15 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
bx.new_vtable(sig, &extra_args)
|
bx.new_vtable(sig, &extra_args)
|
||||||
}
|
}
|
||||||
Some(ty::InstanceDef::DropGlue(_, None)) => {
|
Some(ty::InstanceDef::DropGlue(_, None)) => {
|
||||||
// empty drop glue - a nop.
|
// Empty drop glue; a no-op.
|
||||||
let &(_, target) = destination.as_ref().unwrap();
|
let &(_, target) = destination.as_ref().unwrap();
|
||||||
funclet_br(self, &mut bx, target);
|
helper.funclet_br(self, &mut bx, target);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => bx.new_fn_type(sig, &extra_args)
|
_ => bx.new_fn_type(sig, &extra_args)
|
||||||
};
|
};
|
||||||
|
|
||||||
// emit a panic or a NOP for `panic_if_uninhabited`
|
// Emit a panic or a no-op for `panic_if_uninhabited`.
|
||||||
if intrinsic == Some("panic_if_uninhabited") {
|
if intrinsic == Some("panic_if_uninhabited") {
|
||||||
let ty = instance.unwrap().substs.type_at(0);
|
let ty = instance.unwrap().substs.type_at(0);
|
||||||
let layout = bx.layout_of(ty);
|
let layout = bx.layout_of(ty);
|
||||||
|
@ -522,9 +537,9 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
let filename = bx.const_str_slice(filename);
|
let filename = bx.const_str_slice(filename);
|
||||||
let line = bx.const_u32(loc.line as u32);
|
let line = bx.const_u32(loc.line as u32);
|
||||||
let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
|
let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
|
||||||
let align = tcx.data_layout.aggregate_align.abi
|
let align = self.cx.tcx().data_layout.aggregate_align.abi
|
||||||
.max(tcx.data_layout.i32_align.abi)
|
.max(self.cx.tcx().data_layout.i32_align.abi)
|
||||||
.max(tcx.data_layout.pointer_align.abi);
|
.max(self.cx.tcx().data_layout.pointer_align.abi);
|
||||||
|
|
||||||
let str = format!(
|
let str = format!(
|
||||||
"Attempted to instantiate uninhabited type {}",
|
"Attempted to instantiate uninhabited type {}",
|
||||||
|
@ -550,7 +565,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
let llfn = bx.get_fn(instance);
|
let llfn = bx.get_fn(instance);
|
||||||
|
|
||||||
// Codegen the actual panic invoke/call.
|
// Codegen the actual panic invoke/call.
|
||||||
do_call(
|
helper.do_call(
|
||||||
self,
|
self,
|
||||||
&mut bx,
|
&mut bx,
|
||||||
fn_ty,
|
fn_ty,
|
||||||
|
@ -561,7 +576,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// a NOP
|
// a NOP
|
||||||
funclet_br(self, &mut bx, destination.as_ref().unwrap().1);
|
helper.funclet_br(self, &mut bx, destination.as_ref().unwrap().1)
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -582,13 +597,12 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
if intrinsic.is_some() && intrinsic != Some("drop_in_place") {
|
if intrinsic.is_some() && intrinsic != Some("drop_in_place") {
|
||||||
let dest = match ret_dest {
|
let dest = match ret_dest {
|
||||||
_ if fn_ty.ret.is_indirect() => llargs[0],
|
_ if fn_ty.ret.is_indirect() => llargs[0],
|
||||||
ReturnDest::Nothing => {
|
ReturnDest::Nothing =>
|
||||||
bx.const_undef(bx.type_ptr_to(bx.memory_ty(&fn_ty.ret)))
|
bx.const_undef(bx.type_ptr_to(bx.memory_ty(&fn_ty.ret))),
|
||||||
}
|
ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) =>
|
||||||
ReturnDest::IndirectOperand(dst, _) |
|
dst.llval,
|
||||||
ReturnDest::Store(dst) => dst.llval,
|
|
||||||
ReturnDest::DirectOperand(_) =>
|
ReturnDest::DirectOperand(_) =>
|
||||||
bug!("Cannot use direct operand with an intrinsic call")
|
bug!("Cannot use direct operand with an intrinsic call"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| {
|
let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| {
|
||||||
|
@ -620,7 +634,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
layout: bx.layout_of(ty),
|
layout: bx.layout_of(ty),
|
||||||
};
|
};
|
||||||
|
|
||||||
},
|
}
|
||||||
mir::Operand::Copy(_) |
|
mir::Operand::Copy(_) |
|
||||||
mir::Operand::Move(_) => {
|
mir::Operand::Move(_) => {
|
||||||
span_bug!(span, "shuffle indices must be constant");
|
span_bug!(span, "shuffle indices must be constant");
|
||||||
|
@ -654,7 +668,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((_, target)) = *destination {
|
if let Some((_, target)) = *destination {
|
||||||
funclet_br(self, &mut bx, target);
|
helper.funclet_br(self, &mut bx, target);
|
||||||
} else {
|
} else {
|
||||||
bx.unreachable();
|
bx.unreachable();
|
||||||
}
|
}
|
||||||
|
@ -682,14 +696,14 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
// populate it with a dummy operand so that the users real arguments
|
// populate it with a dummy operand so that the users real arguments
|
||||||
// are not overwritten.
|
// are not overwritten.
|
||||||
let i = if sig.variadic && last_arg_idx.map(|x| x == i).unwrap_or(false) {
|
let i = if sig.variadic && last_arg_idx.map(|x| x == i).unwrap_or(false) {
|
||||||
let layout = match tcx.lang_items().va_list() {
|
let layout = match self.cx.tcx().lang_items().va_list() {
|
||||||
Some(did) => bx.cx().layout_of(bx.tcx().type_of(did)),
|
Some(did) => bx.cx().layout_of(bx.tcx().type_of(did)),
|
||||||
None => bug!("va_list language item required for C variadics"),
|
None => bug!("`va_list` language item required for C-variadics"),
|
||||||
};
|
};
|
||||||
let op = OperandRef {
|
let op = OperandRef {
|
||||||
val: OperandValue::Immediate(
|
val: OperandValue::Immediate(
|
||||||
bx.cx().const_undef(bx.cx().immediate_backend_type(layout))
|
bx.cx().const_undef(bx.cx().immediate_backend_type(layout)
|
||||||
),
|
)),
|
||||||
layout: layout,
|
layout: layout,
|
||||||
};
|
};
|
||||||
self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]);
|
self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]);
|
||||||
|
@ -738,7 +752,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
llargs.push(data_ptr);
|
llargs.push(data_ptr);
|
||||||
continue 'make_args
|
continue 'make_args
|
||||||
}
|
}
|
||||||
other => bug!("expected a Pair, got {:?}", other)
|
other => bug!("expected a Pair, got {:?}", other),
|
||||||
}
|
}
|
||||||
} else if let Ref(data_ptr, Some(meta), _) = op.val {
|
} else if let Ref(data_ptr, Some(meta), _) = op.val {
|
||||||
// by-value dynamic dispatch
|
// by-value dynamic dispatch
|
||||||
|
@ -776,10 +790,96 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
_ => span_bug!(span, "no llfn for call"),
|
_ => span_bug!(span, "no llfn for call"),
|
||||||
};
|
};
|
||||||
|
|
||||||
do_call(self, &mut bx, fn_ty, fn_ptr, &llargs,
|
helper.do_call(self, &mut bx, fn_ty, fn_ptr, &llargs,
|
||||||
destination.as_ref().map(|&(_, target)| (ret_dest, target)),
|
destination.as_ref().map(|&(_, target)| (ret_dest, target)),
|
||||||
cleanup);
|
cleanup);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
|
pub fn codegen_block(
|
||||||
|
&mut self,
|
||||||
|
bb: mir::BasicBlock,
|
||||||
|
) {
|
||||||
|
let mut bx = self.build_block(bb);
|
||||||
|
let data = &self.mir[bb];
|
||||||
|
|
||||||
|
debug!("codegen_block({:?}={:?})", bb, data);
|
||||||
|
|
||||||
|
for statement in &data.statements {
|
||||||
|
bx = self.codegen_statement(bx, statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.codegen_terminator(bx, bb, data.terminator());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn codegen_terminator(
|
||||||
|
&mut self,
|
||||||
|
mut bx: Bx,
|
||||||
|
bb: mir::BasicBlock,
|
||||||
|
terminator: &mir::Terminator<'tcx>
|
||||||
|
) {
|
||||||
|
debug!("codegen_terminator: {:?}", terminator);
|
||||||
|
|
||||||
|
// Create the cleanup bundle, if needed.
|
||||||
|
let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb);
|
||||||
|
let helper = TerminatorCodegenHelper {
|
||||||
|
bb: &bb, terminator, funclet_bb
|
||||||
|
};
|
||||||
|
|
||||||
|
self.set_debug_loc(&mut bx, terminator.source_info);
|
||||||
|
match terminator.kind {
|
||||||
|
mir::TerminatorKind::Resume => {
|
||||||
|
self.codegen_resume_terminator(helper, bx)
|
||||||
|
}
|
||||||
|
|
||||||
|
mir::TerminatorKind::Abort => {
|
||||||
|
bx.abort();
|
||||||
|
bx.unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
mir::TerminatorKind::Goto { target } => {
|
||||||
|
helper.funclet_br(self, &mut bx, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
mir::TerminatorKind::SwitchInt {
|
||||||
|
ref discr, switch_ty, ref values, ref targets
|
||||||
|
} => {
|
||||||
|
self.codegen_switchint_terminator(helper, bx, discr, switch_ty,
|
||||||
|
values, targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
mir::TerminatorKind::Return => {
|
||||||
|
self.codegen_return_terminator(bx);
|
||||||
|
}
|
||||||
|
|
||||||
|
mir::TerminatorKind::Unreachable => {
|
||||||
|
bx.unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
mir::TerminatorKind::Drop { ref location, target, unwind } => {
|
||||||
|
self.codegen_drop_terminator(helper, bx, location, target, unwind);
|
||||||
|
}
|
||||||
|
|
||||||
|
mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => {
|
||||||
|
self.codegen_assert_terminator(helper, bx, terminator, cond,
|
||||||
|
expected, msg, target, cleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
mir::TerminatorKind::DropAndReplace { .. } => {
|
||||||
|
bug!("undesugared DropAndReplace in codegen: {:?}", terminator);
|
||||||
|
}
|
||||||
|
|
||||||
|
mir::TerminatorKind::Call {
|
||||||
|
ref func,
|
||||||
|
ref args,
|
||||||
|
ref destination,
|
||||||
|
cleanup,
|
||||||
|
from_hir_call: _
|
||||||
|
} => {
|
||||||
|
self.codegen_call_terminator(helper, bx, terminator, func,
|
||||||
|
args, destination, cleanup);
|
||||||
|
}
|
||||||
mir::TerminatorKind::GeneratorDrop |
|
mir::TerminatorKind::GeneratorDrop |
|
||||||
mir::TerminatorKind::Yield { .. } => bug!("generator ops in codegen"),
|
mir::TerminatorKind::Yield { .. } => bug!("generator ops in codegen"),
|
||||||
mir::TerminatorKind::FalseEdges { .. } |
|
mir::TerminatorKind::FalseEdges { .. } |
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue