diff options
| author | Eduard-Mihai Burtescu <edy.burt@gmail.com> | 2017-09-20 05:16:06 +0300 |
|---|---|---|
| committer | Eduard-Mihai Burtescu <edy.burt@gmail.com> | 2017-11-19 02:14:31 +0200 |
| commit | 88f70323e451f63f812c6cd92cc1d654b32971e1 (patch) | |
| tree | a7531ba09b2de91cb954d2ced959dc16b6b0791c | |
| parent | f2e7e17d9e9b8faeb13c388c56ef135978a77c58 (diff) | |
| download | rust-88f70323e451f63f812c6cd92cc1d654b32971e1.tar.gz rust-88f70323e451f63f812c6cd92cc1d654b32971e1.zip | |
rustc_trans: nest abi::ArgType's for fat pointers instead of eagerly flattening.
| -rw-r--r-- | src/librustc_trans/abi.rs | 108 | ||||
| -rw-r--r-- | src/librustc_trans/common.rs | 20 | ||||
| -rw-r--r-- | src/librustc_trans/mir/block.rs | 75 | ||||
| -rw-r--r-- | src/librustc_trans/mir/mod.rs | 95 |
4 files changed, 161 insertions, 137 deletions
diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 6aa49080dd0..689976b6c42 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -420,7 +420,7 @@ impl CastTarget { /// should be passed to or returned from a function /// /// This is borrowed from clang's ABIInfo.h -#[derive(Clone, Copy, Debug)] +#[derive(Debug)] pub struct ArgType<'tcx> { kind: ArgKind, pub layout: FullLayout<'tcx>, @@ -429,7 +429,8 @@ pub struct ArgType<'tcx> { /// Dummy argument, which is emitted before the real argument. pub pad: Option<Reg>, /// Attributes of argument. - pub attrs: ArgAttributes + pub attrs: ArgAttributes, + pub nested: Vec<ArgType<'tcx>> } impl<'a, 'tcx> ArgType<'tcx> { @@ -439,11 +440,13 @@ impl<'a, 'tcx> ArgType<'tcx> { layout, cast: None, pad: None, - attrs: ArgAttributes::default() + attrs: ArgAttributes::default(), + nested: vec![] } } pub fn make_indirect(&mut self, ccx: &CrateContext<'a, 'tcx>) { + assert!(self.nested.is_empty()); assert_eq!(self.kind, ArgKind::Direct); // Wipe old attributes, likely not valid through indirection. @@ -460,6 +463,7 @@ impl<'a, 'tcx> ArgType<'tcx> { } pub fn ignore(&mut self) { + assert!(self.nested.is_empty()); assert_eq!(self.kind, ArgKind::Direct); self.kind = ArgKind::Ignore; } @@ -482,10 +486,12 @@ impl<'a, 'tcx> ArgType<'tcx> { } pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) { + assert!(self.nested.is_empty()); self.cast = Some(target.into()); } pub fn pad_with(&mut self, reg: Reg) { + assert!(self.nested.is_empty()); self.pad = Some(reg); } @@ -561,6 +567,12 @@ impl<'a, 'tcx> ArgType<'tcx> { } pub fn store_fn_arg(&self, bcx: &Builder<'a, 'tcx>, idx: &mut usize, dst: LvalueRef<'tcx>) { + if !self.nested.is_empty() { + for (i, arg) in self.nested.iter().enumerate() { + arg.store_fn_arg(bcx, idx, dst.project_field(bcx, i)); + } + return; + } if self.pad.is_some() { *idx += 1; } @@ -578,7 +590,7 @@ impl<'a, 'tcx> ArgType<'tcx> { /// /// I will do my best to describe this structure, but these /// comments are reverse-engineered and may be inaccurate. -NDM -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct FnType<'tcx> { /// The LLVM types of each argument. pub args: Vec<ArgType<'tcx>>, @@ -613,7 +625,8 @@ impl<'a, 'tcx> FnType<'tcx> { extra_args: &[Ty<'tcx>]) -> FnType<'tcx> { let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args); // Don't pass the vtable, it's not an argument of the virtual fn. - fn_ty.args[1].ignore(); + assert_eq!(fn_ty.args[0].nested.len(), 2); + fn_ty.args[0].nested[1].ignore(); fn_ty.adjust_for_abi(ccx, sig); fn_ty } @@ -766,7 +779,7 @@ impl<'a, 'tcx> FnType<'tcx> { for ty in inputs.iter().chain(extra_args.iter()) { let mut arg = arg_of(ty, false); - if let ty::layout::Layout::FatPointer { .. } = *arg.layout.layout { + if type_is_fat_ptr(ccx, ty) { let mut data = ArgType::new(arg.layout.field(ccx, 0)); let mut info = ArgType::new(arg.layout.field(ccx, 1)); @@ -780,14 +793,16 @@ impl<'a, 'tcx> FnType<'tcx> { info.attrs.set(ArgAttribute::NoAlias); } } - args.push(data); - args.push(info); + // FIXME(eddyb) other ABIs don't have logic for nested. + if rust_abi { + arg.nested = vec![data, info]; + } } else { if let Some(inner) = rust_ptr_attrs(ty, &mut arg) { arg.attrs.set_dereferenceable(ccx.size_of(inner)); } - args.push(arg); } + args.push(arg); } FnType { @@ -854,6 +869,13 @@ impl<'a, 'tcx> FnType<'tcx> { } for arg in &mut self.args { if arg.is_ignore() { continue; } + if !arg.nested.is_empty() { + for arg in &mut arg.nested { + assert!(arg.nested.is_empty()); + fixup(arg); + } + continue; + } fixup(arg); } if self.ret.is_indirect() { @@ -915,24 +937,36 @@ impl<'a, 'tcx> FnType<'tcx> { ccx.immediate_llvm_type_of(self.ret.layout.ty) }; - for arg in &self.args { - if arg.is_ignore() { - continue; - } - // add padding - if let Some(ty) = arg.pad { - llargument_tys.push(ty.llvm_type(ccx)); - } + { + let mut push = |arg: &ArgType<'tcx>| { + if arg.is_ignore() { + return; + } + // add padding + if let Some(ty) = arg.pad { + llargument_tys.push(ty.llvm_type(ccx)); + } - let llarg_ty = if arg.is_indirect() { - arg.memory_ty(ccx).ptr_to() - } else if let Some(cast) = arg.cast { - cast.llvm_type(ccx) - } else { - ccx.immediate_llvm_type_of(arg.layout.ty) - }; + let llarg_ty = if arg.is_indirect() { + arg.memory_ty(ccx).ptr_to() + } else if let Some(cast) = arg.cast { + cast.llvm_type(ccx) + } else { + ccx.immediate_llvm_type_of(arg.layout.ty) + }; - llargument_tys.push(llarg_ty); + llargument_tys.push(llarg_ty); + }; + for arg in &self.args { + if !arg.nested.is_empty() { + for arg in &arg.nested { + assert!(arg.nested.is_empty()); + push(arg); + } + continue; + } + push(arg); + } } if self.variadic { @@ -948,12 +982,22 @@ impl<'a, 'tcx> FnType<'tcx> { self.ret.attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn); } i += 1; - for arg in &self.args { + let mut apply = |arg: &ArgType| { if !arg.is_ignore() { if arg.pad.is_some() { i += 1; } arg.attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn); i += 1; } + }; + for arg in &self.args { + if !arg.nested.is_empty() { + for arg in &arg.nested { + assert!(arg.nested.is_empty()); + apply(arg); + } + continue; + } + apply(arg); } } @@ -963,12 +1007,22 @@ impl<'a, 'tcx> FnType<'tcx> { self.ret.attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite); } i += 1; - for arg in &self.args { + let mut apply = |arg: &ArgType| { if !arg.is_ignore() { if arg.pad.is_some() { i += 1; } arg.attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite); i += 1; } + }; + for arg in &self.args { + if !arg.nested.is_empty() { + for arg in &arg.nested { + assert!(arg.nested.is_empty()); + apply(arg); + } + continue; + } + apply(arg); } if self.cconv != llvm::CCallConv { diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index d7397e359a1..426a44671bc 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -27,7 +27,7 @@ use type_::Type; use value::Value; use rustc::traits; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::layout::{self, HasDataLayout, Layout, LayoutOf}; +use rustc::ty::layout::{self, HasDataLayout, LayoutOf}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::hir; @@ -41,10 +41,15 @@ use syntax_pos::{Span, DUMMY_SP}; pub use context::{CrateContext, SharedCrateContext}; pub fn type_is_fat_ptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { - if let Layout::FatPointer { .. } = *ccx.layout_of(ty).layout { - true - } else { - false + match ty.sty { + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { + !ccx.shared().type_is_sized(ty) + } + ty::TyAdt(def, _) if def.is_box() => { + !ccx.shared().type_is_sized(ty.boxed_ty()) + } + _ => false } } @@ -63,9 +68,8 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) - pub fn type_is_imm_pair<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { let layout = ccx.layout_of(ty); - match *layout.layout { - Layout::FatPointer => true, - Layout::Univariant => { + match *layout.fields { + layout::FieldPlacement::Arbitrary { .. } => { // There must be only 2 fields. if layout.fields.count() != 2 { return false; diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index da3f6559dac..7dbb8253e42 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -215,13 +215,12 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } mir::TerminatorKind::Return => { - let ret = self.fn_ty.ret; - if ret.is_ignore() || ret.is_indirect() { + if self.fn_ty.ret.is_ignore() || self.fn_ty.ret.is_indirect() { bcx.ret_void(); return; } - let llval = if let Some(cast_ty) = ret.cast { + let llval = if let Some(cast_ty) = self.fn_ty.ret.cast { let op = match self.locals[mir::RETURN_POINTER] { LocalRef::Operand(Some(op)) => op, LocalRef::Operand(None) => bug!("use of return before def"), @@ -234,7 +233,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { }; let llslot = match op.val { Immediate(_) | Pair(..) => { - let scratch = LvalueRef::alloca(&bcx, ret.layout.ty, "ret"); + let scratch = LvalueRef::alloca(&bcx, self.fn_ty.ret.layout.ty, "ret"); op.store(&bcx, scratch); scratch.llval } @@ -246,7 +245,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { }; let load = bcx.load( bcx.pointercast(llslot, cast_ty.llvm_type(bcx.ccx).ptr_to()), - Some(ret.layout.align(bcx.ccx))); + Some(self.fn_ty.ret.layout.align(bcx.ccx))); load } else { let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER)); @@ -562,9 +561,18 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { (&args[..], None) }; - let mut idx = 0; - for arg in first_args { + for (idx, arg) in first_args.iter().enumerate() { let mut op = self.trans_operand(&bcx, arg); + if idx == 0 { + if let Pair(_, meta) = op.val { + if let Some(ty::InstanceDef::Virtual(_, idx)) = def { + let llmeth = meth::VirtualIndex::from_index(idx) + .get_fn(&bcx, meta); + let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); + llfn = Some(bcx.pointercast(llmeth, llty)); + } + } + } // The callee needs to own the argument memory if we pass it // by-ref, so make a local copy of non-immediate constants. @@ -574,12 +582,11 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { op.val = Ref(tmp.llval, tmp.alignment); } - self.trans_argument(&bcx, op, &mut llargs, &fn_ty, - &mut idx, &mut llfn, &def); + self.trans_argument(&bcx, op, &mut llargs, &fn_ty.args[idx]); } if let Some(tup) = untuple { - self.trans_arguments_untupled(&bcx, tup, &mut llargs, &fn_ty, - &mut idx, &mut llfn, &def) + self.trans_arguments_untupled(&bcx, tup, &mut llargs, + &fn_ty.args[first_args.len()..]) } let fn_ptr = match (llfn, instance) { @@ -602,36 +609,22 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx: &Builder<'a, 'tcx>, op: OperandRef<'tcx>, llargs: &mut Vec<ValueRef>, - fn_ty: &FnType<'tcx>, - next_idx: &mut usize, - llfn: &mut Option<ValueRef>, - def: &Option<ty::InstanceDef<'tcx>>) { + arg: &ArgType<'tcx>) { if let Pair(a, b) = op.val { // Treat the values in a fat pointer separately. - if common::type_is_fat_ptr(bcx.ccx, op.ty) { - let (ptr, meta) = (a, b); - if *next_idx == 0 { - if let Some(ty::InstanceDef::Virtual(_, idx)) = *def { - let llmeth = meth::VirtualIndex::from_index(idx).get_fn(bcx, meta); - let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); - *llfn = Some(bcx.pointercast(llmeth, llty)); - } - } - + if !arg.nested.is_empty() { + assert_eq!(arg.nested.len(), 2); let imm_op = |x| OperandRef { val: Immediate(x), // We won't be checking the type again. ty: bcx.tcx().types.err }; - self.trans_argument(bcx, imm_op(ptr), llargs, fn_ty, next_idx, llfn, def); - self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, llfn, def); + self.trans_argument(bcx, imm_op(a), llargs, &arg.nested[0]); + self.trans_argument(bcx, imm_op(b), llargs, &arg.nested[1]); return; } } - let arg = &fn_ty.args[*next_idx]; - *next_idx += 1; - // Fill padding with undef value, where applicable. if let Some(ty) = arg.pad { llargs.push(C_undef(ty.llvm_type(bcx.ccx))); @@ -686,10 +679,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx: &Builder<'a, 'tcx>, operand: &mir::Operand<'tcx>, llargs: &mut Vec<ValueRef>, - fn_ty: &FnType<'tcx>, - next_idx: &mut usize, - llfn: &mut Option<ValueRef>, - def: &Option<ty::InstanceDef<'tcx>>) { + args: &[ArgType<'tcx>]) { let tuple = self.trans_operand(bcx, operand); let arg_types = match tuple.ty.sty { @@ -702,18 +692,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { match tuple.val { Ref(llval, align) => { let tuple_ptr = LvalueRef::new_sized(llval, tuple.ty, align); - for (n, &ty) in arg_types.iter().enumerate() { + for n in 0..arg_types.len() { let field_ptr = tuple_ptr.project_field(bcx, n); - let op = if common::type_is_fat_ptr(bcx.ccx, ty) { - field_ptr.load(bcx) - } else { - // trans_argument will load this if it needs to - OperandRef { - val: Ref(field_ptr.llval, field_ptr.alignment), - ty - } - }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); + self.trans_argument(bcx, field_ptr.load(bcx), llargs, &args[n]); } } @@ -728,7 +709,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { val: Immediate(elem), ty, }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); + self.trans_argument(bcx, op, llargs, &args[n]); } } Pair(a, b) => { @@ -740,7 +721,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { val: Immediate(elem), ty, }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); + self.trans_argument(bcx, op, llargs, &args[n]); } } } diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index a1e89013bdb..7440551f322 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -22,7 +22,7 @@ use builder::Builder; use common::{self, CrateContext, Funclet}; use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext}; use monomorphize::Instance; -use abi::{self, ArgAttribute, FnType}; +use abi::{ArgAttribute, FnType}; use type_of; use syntax_pos::{DUMMY_SP, NO_EXPANSION, BytePos, Span}; @@ -401,22 +401,10 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, }; let lvalue = LvalueRef::alloca(bcx, arg_ty, &name); - for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() { - let dst = lvalue.project_field(bcx, i); + for i in 0..tupled_arg_tys.len() { let arg = &mircx.fn_ty.args[idx]; idx += 1; - if common::type_is_fat_ptr(bcx.ccx, tupled_arg_ty) { - // We pass fat pointers as two words, but inside the tuple - // they are the two sub-fields of a single aggregate field. - let meta = &mircx.fn_ty.args[idx]; - idx += 1; - arg.store_fn_arg(bcx, &mut llarg_idx, - dst.project_field(bcx, abi::FAT_PTR_ADDR)); - meta.store_fn_arg(bcx, &mut llarg_idx, - dst.project_field(bcx, abi::FAT_PTR_EXTRA)); - } else { - arg.store_fn_arg(bcx, &mut llarg_idx, dst); - } + arg.store_fn_arg(bcx, &mut llarg_idx, lvalue.project_field(bcx, i)); } // Now that we have one alloca that contains the aggregate value, @@ -453,26 +441,19 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, llarg_idx += 1; LvalueRef::new_sized(llarg, arg_ty, Alignment::AbiAligned) } else if !lvalue_locals.contains(local.index()) && - arg.cast.is_none() && arg_scope.is_none() { - if arg.is_ignore() { - return LocalRef::new_operand(bcx.ccx, arg_ty); - } + !arg.nested.is_empty() { + assert_eq!(arg.nested.len(), 2); + let (a, b) = (&arg.nested[0], &arg.nested[1]); + assert!(!a.is_ignore() && a.cast.is_none() && a.pad.is_none()); + assert!(!b.is_ignore() && b.cast.is_none() && b.pad.is_none()); - // We don't have to cast or keep the argument in the alloca. - // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead - // of putting everything in allocas just so we can use llvm.dbg.declare. - if arg.pad.is_some() { - llarg_idx += 1; - } - let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); + let mut a = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); llarg_idx += 1; - let val = if common::type_is_fat_ptr(bcx.ccx, arg_ty) { - let meta = &mircx.fn_ty.args[idx]; - idx += 1; - assert!(meta.cast.is_none() && meta.pad.is_none()); - let llmeta = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); - llarg_idx += 1; + let mut b = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); + llarg_idx += 1; + + if common::type_is_fat_ptr(bcx.ccx, arg_ty) { // FIXME(eddyb) As we can't perfectly represent the data and/or // vtable pointer in a fat pointers in Rust's typesystem, and // because we split fat pointers into two ArgType's, they're @@ -486,36 +467,40 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, let data_llty = bcx.ccx.llvm_type_of(pointee); let meta_llty = type_of::unsized_info_ty(bcx.ccx, pointee); - let llarg = bcx.pointercast(llarg, data_llty.ptr_to()); - bcx.set_value_name(llarg, &(name.clone() + ".ptr")); - let llmeta = bcx.pointercast(llmeta, meta_llty); - bcx.set_value_name(llmeta, &(name + ".meta")); + a = bcx.pointercast(a, data_llty.ptr_to()); + bcx.set_value_name(a, &(name.clone() + ".ptr")); + b = bcx.pointercast(b, meta_llty); + bcx.set_value_name(b, &(name + ".meta")); + } - OperandValue::Pair(llarg, llmeta) - } else { - bcx.set_value_name(llarg, &name); - OperandValue::Immediate(llarg) - }; + return LocalRef::Operand(Some(OperandRef { + val: OperandValue::Pair(a, b), + ty: arg_ty + })); + } else if !lvalue_locals.contains(local.index()) && + !arg.is_indirect() && arg.cast.is_none() && + arg_scope.is_none() { + if arg.is_ignore() { + return LocalRef::new_operand(bcx.ccx, arg_ty); + } + + // We don't have to cast or keep the argument in the alloca. + // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead + // of putting everything in allocas just so we can use llvm.dbg.declare. + if arg.pad.is_some() { + llarg_idx += 1; + } + let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); + bcx.set_value_name(llarg, &name); + llarg_idx += 1; let operand = OperandRef { - val, + val: OperandValue::Immediate(llarg), ty: arg_ty }; return LocalRef::Operand(Some(operand.unpack_if_pair(bcx))); } else { let tmp = LvalueRef::alloca(bcx, arg_ty, &name); - if common::type_is_fat_ptr(bcx.ccx, arg_ty) { - // we pass fat pointers as two words, but we want to - // represent them internally as a pointer to two words, - // so make an alloca to store them in. - let meta = &mircx.fn_ty.args[idx]; - idx += 1; - arg.store_fn_arg(bcx, &mut llarg_idx, tmp.project_field(bcx, abi::FAT_PTR_ADDR)); - meta.store_fn_arg(bcx, &mut llarg_idx, tmp.project_field(bcx, abi::FAT_PTR_EXTRA)); - } else { - // otherwise, arg is passed by value, so make a - // temporary and store it there - arg.store_fn_arg(bcx, &mut llarg_idx, tmp); - } + arg.store_fn_arg(bcx, &mut llarg_idx, tmp); tmp }; arg_scope.map(|scope| { |
