diff options
| author | Eduard-Mihai Burtescu <edy.burt@gmail.com> | 2017-10-10 20:54:50 +0300 |
|---|---|---|
| committer | Eduard-Mihai Burtescu <edy.burt@gmail.com> | 2017-11-19 02:43:55 +0200 |
| commit | 18ecc564f2cee4da3ef9397ba58e19d3fd9be3de (patch) | |
| tree | dd9970b4cfa69a7c7d52bf428280d0c196eb4f98 /src | |
| parent | 7a36141465d1f97936cfceca87ed428dbfafdd3f (diff) | |
| download | rust-18ecc564f2cee4da3ef9397ba58e19d3fd9be3de.tar.gz rust-18ecc564f2cee4da3ef9397ba58e19d3fd9be3de.zip | |
rustc_trans: support scalar pairs directly in the Rust ABI.
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc_llvm/lib.rs | 9 | ||||
| -rw-r--r-- | src/librustc_trans/abi.rs | 453 | ||||
| -rw-r--r-- | src/librustc_trans/attributes.rs | 2 | ||||
| -rw-r--r-- | src/librustc_trans/cabi_asmjs.rs | 5 | ||||
| -rw-r--r-- | src/librustc_trans/cabi_x86.rs | 17 | ||||
| -rw-r--r-- | src/librustc_trans/cabi_x86_64.rs | 6 | ||||
| -rw-r--r-- | src/librustc_trans/intrinsic.rs | 6 | ||||
| -rw-r--r-- | src/librustc_trans/mir/block.rs | 134 | ||||
| -rw-r--r-- | src/librustc_trans/mir/mod.rs | 98 |
9 files changed, 367 insertions, 363 deletions
diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 5ccce8de706..592bd620564 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -74,22 +74,19 @@ pub fn AddFunctionAttrStringValue(llfn: ValueRef, } } -#[repr(C)] #[derive(Copy, Clone)] pub enum AttributePlace { + ReturnValue, Argument(u32), Function, } impl AttributePlace { - pub fn ReturnValue() -> Self { - AttributePlace::Argument(0) - } - pub fn as_uint(self) -> c_uint { match self { + AttributePlace::ReturnValue => 0, + AttributePlace::Argument(i) => 1 + i, AttributePlace::Function => !0, - AttributePlace::Argument(i) => i, } } } diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index d69103bbb52..7ef89597b11 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -11,7 +11,7 @@ use llvm::{self, ValueRef, AttributePlace}; use base; use builder::Builder; -use common::{instance_ty, ty_fn_sig, type_is_fat_ptr, C_usize}; +use common::{instance_ty, ty_fn_sig, C_usize}; use context::CrateContext; use cabi_x86; use cabi_x86_64; @@ -30,7 +30,8 @@ use cabi_sparc64; use cabi_nvptx; use cabi_nvptx64; use cabi_hexagon; -use mir::lvalue::LvalueRef; +use mir::lvalue::{Alignment, LvalueRef}; +use mir::operand::OperandValue; use type_::Type; use type_of::{LayoutLlvmExt, PointerKind}; @@ -44,15 +45,19 @@ use std::{cmp, iter}; pub use syntax::abi::Abi; pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; -#[derive(Clone, Copy, PartialEq, Debug)] -enum ArgKind { - /// Pass the argument directly using the normal converted - /// LLVM type or by coercing to another specified type - Direct, - /// Pass the argument indirectly via a hidden pointer - Indirect, - /// Ignore the argument (useful for empty struct) +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum PassMode { + /// Ignore the argument (useful for empty struct). Ignore, + /// Pass the argument directly. + Direct(ArgAttributes), + /// Pass a pair's elements directly in two arguments. + Pair(ArgAttributes, ArgAttributes), + /// Pass the argument after casting it, to either + /// a single uniform or a pair of registers. + Cast(CastTarget), + /// Pass the argument indirectly via a hidden pointer. + Indirect(ArgAttributes), } // Hack to disable non_upper_case_globals only for the bitflags! and not for the rest @@ -94,7 +99,7 @@ impl ArgAttribute { /// A compact representation of LLVM attributes (at least those relevant for this module) /// that can be manipulated without interacting with LLVM's Attribute machinery. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct ArgAttributes { regular: ArgAttribute, pointee_size: Size, @@ -248,7 +253,7 @@ impl Reg { /// An argument passed entirely registers with the /// same kind (e.g. HFA / HVA on PPC64 and AArch64). -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Uniform { pub unit: Reg, @@ -399,7 +404,7 @@ impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum CastTarget { Uniform(Uniform), Pair(Reg, Reg) @@ -452,66 +457,53 @@ impl CastTarget { } } -/// Information about how a specific C type -/// should be passed to or returned from a function -/// -/// This is borrowed from clang's ABIInfo.h +/// Information about how to pass an argument to, +/// or return a value from, a function, under some ABI. #[derive(Debug)] pub struct ArgType<'tcx> { - kind: ArgKind, pub layout: TyLayout<'tcx>, - /// Cast target, either a single uniform or a pair of registers. - pub cast: Option<CastTarget>, + /// Dummy argument, which is emitted before the real argument. pub pad: Option<Reg>, - /// Attributes of argument. - pub attrs: ArgAttributes, - pub nested: Vec<ArgType<'tcx>> + + pub mode: PassMode, } impl<'a, 'tcx> ArgType<'tcx> { fn new(layout: TyLayout<'tcx>) -> ArgType<'tcx> { - let mut attrs = ArgAttributes::new(); - - if let layout::Abi::Scalar(ref scalar) = layout.abi { - if scalar.is_bool() { - attrs.set(ArgAttribute::ZExt); - } - } - ArgType { - kind: ArgKind::Direct, layout, - cast: None, pad: None, - attrs, - nested: vec![] + mode: PassMode::Direct(ArgAttributes::new()), } } pub fn make_indirect(&mut self) { - assert!(self.nested.is_empty()); - assert_eq!(self.kind, ArgKind::Direct); + assert_eq!(self.mode, PassMode::Direct(ArgAttributes::new())); - // Wipe old attributes, likely not valid through indirection. - self.attrs = ArgAttributes::new(); + // Start with fresh attributes for the pointer. + let mut attrs = ArgAttributes::new(); // For non-immediate arguments the callee gets its own copy of // the value on the stack, so there are no aliases. It's also // program-invisible so can't possibly capture - self.attrs.set(ArgAttribute::NoAlias) - .set(ArgAttribute::NoCapture) - .set(ArgAttribute::NonNull); - self.attrs.pointee_size = self.layout.size; - self.attrs.pointee_align = Some(self.layout.align); + attrs.set(ArgAttribute::NoAlias) + .set(ArgAttribute::NoCapture) + .set(ArgAttribute::NonNull); + attrs.pointee_size = self.layout.size; + attrs.pointee_align = Some(self.layout.align); - self.kind = ArgKind::Indirect; + self.mode = PassMode::Indirect(attrs); } - pub fn ignore(&mut self) { - assert!(self.nested.is_empty()); - assert_eq!(self.kind, ArgKind::Direct); - self.kind = ArgKind::Ignore; + pub fn make_indirect_byval(&mut self) { + self.make_indirect(); + match self.mode { + PassMode::Indirect(ref mut attrs) => { + attrs.set(ArgAttribute::ByVal); + } + _ => bug!() + } } pub fn extend_integer_width_to(&mut self, bits: u64) { @@ -519,32 +511,36 @@ impl<'a, 'tcx> ArgType<'tcx> { if let layout::Abi::Scalar(ref scalar) = self.layout.abi { if let layout::Int(i, signed) = scalar.value { if i.size().bits() < bits { - self.attrs.set(if signed { - ArgAttribute::SExt - } else { - ArgAttribute::ZExt - }); + if let PassMode::Direct(ref mut attrs) = self.mode { + attrs.set(if signed { + ArgAttribute::SExt + } else { + ArgAttribute::ZExt + }); + } } } } } pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) { - assert!(self.nested.is_empty()); - self.cast = Some(target.into()); + assert_eq!(self.mode, PassMode::Direct(ArgAttributes::new())); + self.mode = PassMode::Cast(target.into()); } pub fn pad_with(&mut self, reg: Reg) { - assert!(self.nested.is_empty()); self.pad = Some(reg); } pub fn is_indirect(&self) -> bool { - self.kind == ArgKind::Indirect + match self.mode { + PassMode::Indirect(_) => true, + _ => false + } } pub fn is_ignore(&self) -> bool { - self.kind == ArgKind::Ignore + self.mode == PassMode::Ignore } /// Get the LLVM type for an lvalue of the original Rust type of @@ -557,20 +553,19 @@ impl<'a, 'tcx> ArgType<'tcx> { /// lvalue for the original Rust type of this argument/return. /// Can be used for both storing formal arguments into Rust variables /// or results of call/invoke instructions into their destinations. - pub fn store(&self, bcx: &Builder<'a, 'tcx>, mut val: ValueRef, dst: LvalueRef<'tcx>) { + pub fn store(&self, bcx: &Builder<'a, 'tcx>, val: ValueRef, dst: LvalueRef<'tcx>) { if self.is_ignore() { return; } let ccx = bcx.ccx; if self.is_indirect() { - let llsz = C_usize(ccx, self.layout.size.bytes()); - base::call_memcpy(bcx, dst.llval, val, llsz, self.layout.align); - } else if let Some(ty) = self.cast { + OperandValue::Ref(val, Alignment::AbiAligned).store(bcx, dst) + } else if let PassMode::Cast(cast) = self.mode { // FIXME(eddyb): Figure out when the simpler Store is safe, clang // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. let can_store_through_cast_ptr = false; if can_store_through_cast_ptr { - let cast_dst = bcx.pointercast(dst.llval, ty.llvm_type(ccx).ptr_to()); + let cast_dst = bcx.pointercast(dst.llval, cast.llvm_type(ccx).ptr_to()); bcx.store(val, cast_dst, Some(self.layout.align)); } else { // The actual return type is a struct, but the ABI @@ -588,8 +583,8 @@ impl<'a, 'tcx> ArgType<'tcx> { // bitcasting to the struct type yields invalid cast errors. // We instead thus allocate some scratch space... - let llscratch = bcx.alloca(ty.llvm_type(ccx), "abi_cast", None); - let scratch_size = ty.size(ccx); + let llscratch = bcx.alloca(cast.llvm_type(ccx), "abi_cast", None); + let scratch_size = cast.size(ccx); bcx.lifetime_start(llscratch, scratch_size); // ...where we first store the value... @@ -600,32 +595,33 @@ impl<'a, 'tcx> ArgType<'tcx> { bcx.pointercast(dst.llval, Type::i8p(ccx)), bcx.pointercast(llscratch, Type::i8p(ccx)), C_usize(ccx, self.layout.size.bytes()), - self.layout.align.min(ty.align(ccx))); + self.layout.align.min(cast.align(ccx))); bcx.lifetime_end(llscratch, scratch_size); } } else { - val = base::from_immediate(bcx, val); - bcx.store(val, dst.llval, None); + OperandValue::Immediate(val).store(bcx, dst); } } 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; } - if self.is_ignore() { - return; + let mut next = || { + let val = llvm::get_param(bcx.llfn(), *idx as c_uint); + *idx += 1; + val + }; + match self.mode { + PassMode::Ignore => {}, + PassMode::Pair(..) => { + OperandValue::Pair(next(), next()).store(bcx, dst); + } + PassMode::Direct(_) | PassMode::Indirect(_) | PassMode::Cast(_) => { + self.store(bcx, next(), dst); + } } - let val = llvm::get_param(bcx.llfn(), *idx as c_uint); - *idx += 1; - self.store(bcx, val, dst); } } @@ -660,7 +656,7 @@ impl<'a, 'tcx> FnType<'tcx> { sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> FnType<'tcx> { let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args); - fn_ty.adjust_for_abi(ccx, sig); + fn_ty.adjust_for_abi(ccx, sig.abi); fn_ty } @@ -669,9 +665,23 @@ 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. - assert_eq!(fn_ty.args[0].nested.len(), 2); - fn_ty.args[0].nested[1].ignore(); - fn_ty.adjust_for_abi(ccx, sig); + { + let self_arg = &mut fn_ty.args[0]; + match self_arg.mode { + PassMode::Pair(data_ptr, _) => { + self_arg.mode = PassMode::Direct(data_ptr); + } + _ => bug!("FnType::new_vtable: non-pair self {:?}", self_arg) + } + + let pointee = self_arg.layout.ty.builtin_deref(true, ty::NoPreference) + .unwrap_or_else(|| { + bug!("FnType::new_vtable: non-pointer self {:?}", self_arg) + }).ty; + let fat_ptr_ty = ccx.tcx().mk_mut_ptr(pointee); + self_arg.layout = ccx.layout_of(fat_ptr_ty).field(ccx, 0); + } + fn_ty.adjust_for_abi(ccx, sig.abi); fn_ty } @@ -737,31 +747,37 @@ impl<'a, 'tcx> FnType<'tcx> { }; // Handle safe Rust thin and fat pointers. - let adjust_for_rust_type = |arg: &mut ArgType<'tcx>, is_return: bool| { - match arg.layout.abi { - layout::Abi::Scalar(layout::Scalar { - value: layout::Pointer, - ref valid_range - }) => { - if valid_range.start > 0 && valid_range.start < valid_range.end { - arg.attrs.set(ArgAttribute::NonNull); - } - } - _ => { - // Nothing to do for non-pointer types. - return; + let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, + scalar: &layout::Scalar, + layout: TyLayout<'tcx>, + offset: Size, + is_return: bool| { + // Booleans are always an i1 that needs to be zero-extended. + if scalar.is_bool() { + attrs.set(ArgAttribute::ZExt); + return; + } + + // Only pointer types handled below. + if scalar.value != layout::Pointer { + return; + } + + if scalar.valid_range.start < scalar.valid_range.end { + if scalar.valid_range.start > 0 { + attrs.set(ArgAttribute::NonNull); } } - if let Some(pointee) = arg.layout.pointee_info_at(ccx, Size::from_bytes(0)) { + if let Some(pointee) = layout.pointee_info_at(ccx, offset) { if let Some(kind) = pointee.safe { - arg.attrs.pointee_size = pointee.size; - arg.attrs.pointee_align = Some(pointee.align); + attrs.pointee_size = pointee.size; + attrs.pointee_align = Some(pointee.align); // HACK(eddyb) LLVM inserts `llvm.assume` calls when inlining functions // with align attributes, and those calls later block optimizations. if !is_return { - arg.attrs.pointee_align = None; + attrs.pointee_align = None; } // `Box` pointer parameters never alias because ownership is transferred @@ -778,11 +794,11 @@ impl<'a, 'tcx> FnType<'tcx> { PointerKind::UniqueBorrowed => !is_return }; if no_alias { - arg.attrs.set(ArgAttribute::NoAlias); + attrs.set(ArgAttribute::NoAlias); } if kind == PointerKind::Frozen && !is_return { - arg.attrs.set(ArgAttribute::ReadOnly); + attrs.set(ArgAttribute::ReadOnly); } } } @@ -794,22 +810,39 @@ impl<'a, 'tcx> FnType<'tcx> { // For some forsaken reason, x86_64-pc-windows-gnu // doesn't ignore zero-sized struct arguments. // The same is true for s390x-unknown-linux-gnu. - if is_return || rust_abi || - (!win_x64_gnu && !linux_s390x) { - arg.ignore(); + if is_return || rust_abi || (!win_x64_gnu && !linux_s390x) { + arg.mode = PassMode::Ignore; } } - // FIXME(eddyb) other ABIs don't have logic for nested. - if !is_return && type_is_fat_ptr(ccx, arg.layout.ty) && rust_abi { - arg.nested = vec![ - ArgType::new(arg.layout.field(ccx, 0)), - ArgType::new(arg.layout.field(ccx, 1)) - ]; - adjust_for_rust_type(&mut arg.nested[0], false); - adjust_for_rust_type(&mut arg.nested[1], false); - } else { - adjust_for_rust_type(&mut arg, is_return); + // FIXME(eddyb) other ABIs don't have logic for scalar pairs. + if !is_return && rust_abi { + if let layout::Abi::ScalarPair(ref a, ref b) = arg.layout.abi { + let mut a_attrs = ArgAttributes::new(); + let mut b_attrs = ArgAttributes::new(); + adjust_for_rust_scalar(&mut a_attrs, + a, + arg.layout, + Size::from_bytes(0), + false); + adjust_for_rust_scalar(&mut b_attrs, + b, + arg.layout, + a.value.size(ccx).abi_align(b.value.align(ccx)), + false); + arg.mode = PassMode::Pair(a_attrs, b_attrs); + return arg; + } + } + + if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { + if let PassMode::Direct(ref mut attrs) = arg.mode { + adjust_for_rust_scalar(attrs, + scalar, + arg.layout, + Size::from_bytes(0), + is_return); + } } arg @@ -827,40 +860,20 @@ impl<'a, 'tcx> FnType<'tcx> { fn adjust_for_abi(&mut self, ccx: &CrateContext<'a, 'tcx>, - sig: ty::FnSig<'tcx>) { - let abi = sig.abi; + abi: Abi) { if abi == Abi::Unadjusted { return } if abi == Abi::Rust || abi == Abi::RustCall || abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { let fixup = |arg: &mut ArgType<'tcx>| { + if arg.is_ignore() { return; } + match arg.layout.abi { layout::Abi::Aggregate { .. } => {} _ => return } let size = arg.layout.size; - - if let Some(unit) = arg.layout.homogeneous_aggregate(ccx) { - // Replace newtypes with their inner-most type. - if unit.size == size { - // Needs a cast as we've unpacked a newtype. - arg.cast_to(unit); - return; - } - - // Pairs of floats. - if unit.kind == RegKind::Float { - if unit.size.checked_mul(2, ccx) == Some(size) { - // FIXME(eddyb) This should be using Uniform instead of a pair, - // but the resulting [2 x float/double] breaks emscripten. - // See https://github.com/kripken/emscripten-fastcomp/issues/178. - arg.cast_to(CastTarget::Pair(unit, unit)); - return; - } - } - } - if size > layout::Pointer.size(ccx) { arg.make_indirect(); } else { @@ -873,25 +886,12 @@ impl<'a, 'tcx> FnType<'tcx> { }); } }; - // Fat pointers are returned by-value. - if !self.ret.is_ignore() { - if !type_is_fat_ptr(ccx, sig.output()) { - fixup(&mut self.ret); - } - } + fixup(&mut self.ret); 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() { - self.ret.attrs.set(ArgAttribute::StructRet); + if let PassMode::Indirect(ref mut attrs) = self.ret.mode { + attrs.set(ArgAttribute::StructRet); } return; } @@ -930,55 +930,44 @@ impl<'a, 'tcx> FnType<'tcx> { a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a)) } - if self.ret.is_indirect() { - self.ret.attrs.set(ArgAttribute::StructRet); + if let PassMode::Indirect(ref mut attrs) = self.ret.mode { + attrs.set(ArgAttribute::StructRet); } } pub fn llvm_type(&self, ccx: &CrateContext<'a, 'tcx>) -> Type { let mut llargument_tys = Vec::new(); - let llreturn_ty = if self.ret.is_ignore() { - Type::void(ccx) - } else if self.ret.is_indirect() { - llargument_tys.push(self.ret.memory_ty(ccx).ptr_to()); - Type::void(ccx) - } else if let Some(cast) = self.ret.cast { - cast.llvm_type(ccx) - } else { - self.ret.layout.immediate_llvm_type(ccx) + let llreturn_ty = match self.ret.mode { + PassMode::Ignore => Type::void(ccx), + PassMode::Direct(_) | PassMode::Pair(..) => { + self.ret.layout.immediate_llvm_type(ccx) + } + PassMode::Cast(cast) => cast.llvm_type(ccx), + PassMode::Indirect(_) => { + llargument_tys.push(self.ret.memory_ty(ccx).ptr_to()); + Type::void(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 { - arg.layout.immediate_llvm_type(ccx) - }; + for arg in &self.args { + // add padding + if let Some(ty) = arg.pad { + llargument_tys.push(ty.llvm_type(ccx)); + } - 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); - } + let llarg_ty = match arg.mode { + PassMode::Ignore => continue, + PassMode::Direct(_) => arg.layout.immediate_llvm_type(ccx), + PassMode::Pair(..) => { + llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(ccx, 0)); + llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(ccx, 1)); continue; } - push(arg); - } + PassMode::Cast(cast) => cast.llvm_type(ccx), + PassMode::Indirect(_) => arg.memory_ty(ccx).ptr_to(), + }; + llargument_tys.push(llarg_ty); } if self.variadic { @@ -989,52 +978,62 @@ impl<'a, 'tcx> FnType<'tcx> { } pub fn apply_attrs_llfn(&self, llfn: ValueRef) { - let mut i = if self.ret.is_indirect() { 1 } else { 0 }; - if !self.ret.is_ignore() { - self.ret.attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn); - } - i += 1; - 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; - } + let mut i = 0; + let mut apply = |attrs: &ArgAttributes| { + attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn); + i += 1; }; + match self.ret.mode { + PassMode::Direct(ref attrs) => { + attrs.apply_llfn(llvm::AttributePlace::ReturnValue, llfn); + } + PassMode::Indirect(ref attrs) => apply(attrs), + _ => {} + } for arg in &self.args { - if !arg.nested.is_empty() { - for arg in &arg.nested { - assert!(arg.nested.is_empty()); - apply(arg); + if arg.pad.is_some() { + apply(&ArgAttributes::new()); + } + match arg.mode { + PassMode::Ignore => {} + PassMode::Direct(ref attrs) | + PassMode::Indirect(ref attrs) => apply(attrs), + PassMode::Pair(ref a, ref b) => { + apply(a); + apply(b); } - continue; + PassMode::Cast(_) => apply(&ArgAttributes::new()), } - apply(arg); } } pub fn apply_attrs_callsite(&self, callsite: ValueRef) { - let mut i = if self.ret.is_indirect() { 1 } else { 0 }; - if !self.ret.is_ignore() { - self.ret.attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite); - } - i += 1; - 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; - } + let mut i = 0; + let mut apply = |attrs: &ArgAttributes| { + attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite); + i += 1; }; + match self.ret.mode { + PassMode::Direct(ref attrs) => { + attrs.apply_callsite(llvm::AttributePlace::ReturnValue, callsite); + } + PassMode::Indirect(ref attrs) => apply(attrs), + _ => {} + } for arg in &self.args { - if !arg.nested.is_empty() { - for arg in &arg.nested { - assert!(arg.nested.is_empty()); - apply(arg); + if arg.pad.is_some() { + apply(&ArgAttributes::new()); + } + match arg.mode { + PassMode::Ignore => {} + PassMode::Direct(ref attrs) | + PassMode::Indirect(ref attrs) => apply(attrs), + PassMode::Pair(ref a, ref b) => { + apply(a); + apply(b); } - continue; + PassMode::Cast(_) => apply(&ArgAttributes::new()), } - apply(arg); } if self.cconv != llvm::CCallConv { diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs index b6ca1460a7d..745aa0da829 100644 --- a/src/librustc_trans/attributes.rs +++ b/src/librustc_trans/attributes.rs @@ -116,7 +116,7 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe naked(llfn, true); } else if attr.check_name("allocator") { Attribute::NoAlias.apply_llfn( - llvm::AttributePlace::ReturnValue(), llfn); + llvm::AttributePlace::ReturnValue, llfn); } else if attr.check_name("unwind") { unwind(llfn, true); } else if attr.check_name("rustc_allocator_nounwind") { diff --git a/src/librustc_trans/cabi_asmjs.rs b/src/librustc_trans/cabi_asmjs.rs index da13b75c414..1664251cf89 100644 --- a/src/librustc_trans/cabi_asmjs.rs +++ b/src/librustc_trans/cabi_asmjs.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use abi::{FnType, ArgType, ArgAttribute, LayoutExt, Uniform}; +use abi::{FnType, ArgType, LayoutExt, Uniform}; use context::CrateContext; // Data layout: e-p:32:32-i64:64-v128:32:128-n32-S128 @@ -35,8 +35,7 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc fn classify_arg_ty(arg: &mut ArgType) { if arg.layout.is_aggregate() { - arg.make_indirect(); - arg.attrs.set(ArgAttribute::ByVal); + arg.make_indirect_byval(); } } diff --git a/src/librustc_trans/cabi_x86.rs b/src/librustc_trans/cabi_x86.rs index dc9f681af52..6fd0140c399 100644 --- a/src/librustc_trans/cabi_x86.rs +++ b/src/librustc_trans/cabi_x86.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use abi::{ArgAttribute, FnType, LayoutExt, Reg, RegKind}; +use abi::{ArgAttribute, FnType, LayoutExt, PassMode, Reg, RegKind}; use common::CrateContext; use rustc::ty::layout::{self, TyLayout}; @@ -82,8 +82,7 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, for arg in &mut fty.args { if arg.is_ignore() { continue; } if arg.layout.is_aggregate() { - arg.make_indirect(); - arg.attrs.set(ArgAttribute::ByVal); + arg.make_indirect_byval(); } else { arg.extend_integer_width_to(32); } @@ -102,7 +101,15 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let mut free_regs = 2; for arg in &mut fty.args { - if arg.is_ignore() || arg.is_indirect() { continue; } + let attrs = match arg.mode { + PassMode::Ignore | + PassMode::Indirect(_) => continue, + PassMode::Direct(ref mut attrs) => attrs, + PassMode::Pair(..) | + PassMode::Cast(_) => { + bug!("x86 shouldn't be passing arguments by {:?}", arg.mode) + } + }; // At this point we know this must be a primitive of sorts. let unit = arg.layout.homogeneous_aggregate(ccx).unwrap(); @@ -124,7 +131,7 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, free_regs -= size_in_regs; if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer { - arg.attrs.set(ArgAttribute::InReg); + attrs.set(ArgAttribute::InReg); } if free_regs == 0 { diff --git a/src/librustc_trans/cabi_x86_64.rs b/src/librustc_trans/cabi_x86_64.rs index eeb69276500..81eb362ca46 100644 --- a/src/librustc_trans/cabi_x86_64.rs +++ b/src/librustc_trans/cabi_x86_64.rs @@ -11,7 +11,7 @@ // The classification code for the x86_64 ABI is taken from the clay language // https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp -use abi::{ArgType, ArgAttribute, CastTarget, FnType, LayoutExt, Reg, RegKind}; +use abi::{ArgType, CastTarget, FnType, LayoutExt, Reg, RegKind}; use context::CrateContext; use rustc::ty::layout::{self, TyLayout, Size}; @@ -214,11 +214,11 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType }; if in_mem { - arg.make_indirect(); if is_arg { - arg.attrs.set(ArgAttribute::ByVal); + arg.make_indirect_byval(); } else { // `sret` parameter thus one less integer register available + arg.make_indirect(); int_regs -= 1; } } else { diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 7d08090cd7e..5abc096407d 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -13,7 +13,7 @@ use intrinsics::{self, Intrinsic}; use llvm; use llvm::{ValueRef}; -use abi::{Abi, FnType}; +use abi::{Abi, FnType, PassMode}; use mir::lvalue::{LvalueRef, Alignment}; use mir::operand::{OperandRef, OperandValue}; use base::*; @@ -237,7 +237,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, "volatile_load" => { let tp_ty = substs.type_at(0); let mut ptr = args[0].immediate(); - if let Some(ty) = fn_ty.ret.cast { + if let PassMode::Cast(ty) = fn_ty.ret.mode { ptr = bcx.pointercast(ptr, ty.llvm_type(ccx).ptr_to()); } let load = bcx.volatile_load(ptr); @@ -671,7 +671,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, }; if !fn_ty.ret.is_ignore() { - if let Some(ty) = fn_ty.ret.cast { + if let PassMode::Cast(ty) = fn_ty.ret.mode { let ptr = bcx.pointercast(llresult, ty.llvm_type(ccx).ptr_to()); bcx.store(llval, ptr, Some(ccx.align_of(ret_ty))); } else { diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 67e0f35b46e..f43eba36a82 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -15,7 +15,7 @@ use rustc::ty::{self, TypeFoldable}; use rustc::ty::layout::{self, LayoutOf}; use rustc::traits; use rustc::mir; -use abi::{Abi, FnType, ArgType}; +use abi::{Abi, FnType, ArgType, PassMode}; use base; use callee; use builder::Builder; @@ -207,44 +207,47 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } mir::TerminatorKind::Return => { - if self.fn_ty.ret.is_ignore() || self.fn_ty.ret.is_indirect() { - bcx.ret_void(); - return; - } + let llval = match self.fn_ty.ret.mode { + PassMode::Ignore | PassMode::Indirect(_) => { + bcx.ret_void(); + return; + } - 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"), - LocalRef::Lvalue(tr_lvalue) => { - OperandRef { - val: Ref(tr_lvalue.llval, tr_lvalue.alignment), - layout: tr_lvalue.layout - } - } - }; - let llslot = match op.val { - Immediate(_) | Pair(..) => { - let scratch = LvalueRef::alloca(&bcx, self.fn_ty.ret.layout, "ret"); - op.val.store(&bcx, scratch); - scratch.llval - } - Ref(llval, align) => { - assert_eq!(align, Alignment::AbiAligned, - "return pointer is unaligned!"); - llval + PassMode::Direct(_) | PassMode::Pair(..) => { + let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER)); + if let Ref(llval, align) = op.val { + bcx.load(llval, align.non_abi()) + } else { + op.immediate_or_packed_pair(&bcx) } - }; - let load = bcx.load( - bcx.pointercast(llslot, cast_ty.llvm_type(bcx.ccx).ptr_to()), - Some(self.fn_ty.ret.layout.align)); - load - } else { - let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER)); - if let Ref(llval, align) = op.val { - bcx.load(llval, align.non_abi()) - } else { - op.immediate_or_packed_pair(&bcx) + } + + PassMode::Cast(cast_ty) => { + let op = match self.locals[mir::RETURN_POINTER] { + LocalRef::Operand(Some(op)) => op, + LocalRef::Operand(None) => bug!("use of return before def"), + LocalRef::Lvalue(tr_lvalue) => { + OperandRef { + val: Ref(tr_lvalue.llval, tr_lvalue.alignment), + layout: tr_lvalue.layout + } + } + }; + let llslot = match op.val { + Immediate(_) | Pair(..) => { + let scratch = LvalueRef::alloca(&bcx, self.fn_ty.ret.layout, "ret"); + op.val.store(&bcx, scratch); + scratch.llval + } + Ref(llval, align) => { + assert_eq!(align, Alignment::AbiAligned, + "return pointer is unaligned!"); + llval + } + }; + bcx.load( + bcx.pointercast(llslot, cast_ty.llvm_type(bcx.ccx).ptr_to()), + Some(self.fn_ty.ret.layout.align)) } }; bcx.ret(llval); @@ -559,12 +562,12 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { for (i, arg) in first_args.iter().enumerate() { let mut op = self.trans_operand(&bcx, arg); - if i == 0 { - if let Pair(_, meta) = op.val { - if let Some(ty::InstanceDef::Virtual(_, idx)) = def { - llfn = Some(meth::VirtualIndex::from_index(idx) - .get_fn(&bcx, meta, &fn_ty)); - } + if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { + if let Pair(data_ptr, meta) = op.val { + llfn = Some(meth::VirtualIndex::from_index(idx) + .get_fn(&bcx, meta, &fn_ty)); + llargs.push(data_ptr); + continue; } } @@ -604,21 +607,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { op: OperandRef<'tcx>, llargs: &mut Vec<ValueRef>, arg: &ArgType<'tcx>) { - if let Pair(a, b) = op.val { - // Treat the values in a fat pointer separately. - 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. - layout: bcx.ccx.layout_of(bcx.tcx().types.never) - }; - self.trans_argument(bcx, imm_op(a), llargs, &arg.nested[0]); - self.trans_argument(bcx, imm_op(b), llargs, &arg.nested[1]); - return; - } - } - // Fill padding with undef value, where applicable. if let Some(ty) = arg.pad { llargs.push(C_undef(ty.llvm_type(bcx.ccx))); @@ -628,15 +616,29 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { return; } + if let PassMode::Pair(..) = arg.mode { + match op.val { + Pair(a, b) => { + llargs.push(a); + llargs.push(b); + return; + } + _ => bug!("trans_argument: {:?} invalid for pair arugment", op) + } + } + // Force by-ref if we have to load through a cast pointer. let (mut llval, align, by_ref) = match op.val { Immediate(_) | Pair(..) => { - if arg.is_indirect() || arg.cast.is_some() { - let scratch = LvalueRef::alloca(bcx, arg.layout, "arg"); - op.val.store(bcx, scratch); - (scratch.llval, Alignment::AbiAligned, true) - } else { - (op.immediate_or_packed_pair(bcx), Alignment::AbiAligned, false) + match arg.mode { + PassMode::Indirect(_) | PassMode::Cast(_) => { + let scratch = LvalueRef::alloca(bcx, arg.layout, "arg"); + op.val.store(bcx, scratch); + (scratch.llval, Alignment::AbiAligned, true) + } + _ => { + (op.immediate_or_packed_pair(bcx), Alignment::AbiAligned, false) + } } } Ref(llval, align @ Alignment::Packed(_)) if arg.is_indirect() => { @@ -653,7 +655,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { if by_ref && !arg.is_indirect() { // Have to load the argument, maybe while casting it. - if let Some(ty) = arg.cast { + if let PassMode::Cast(ty) = arg.mode { llval = bcx.load(bcx.pointercast(llval, ty.llvm_type(bcx.ccx).ptr_to()), (align | Alignment::Packed(arg.layout.align)) .non_abi()); @@ -890,7 +892,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } DirectOperand(index) => { // If there is a cast, we have to store and reload. - let op = if ret_ty.cast.is_some() { + let op = if let PassMode::Cast(_) = ret_ty.mode { let tmp = LvalueRef::alloca(bcx, ret_ty.layout, "tmp_ret"); tmp.storage_live(bcx); ret_ty.store(bcx, llval, tmp); diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 6f9d32b1a37..7f3a430c418 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -22,7 +22,7 @@ use builder::Builder; use common::{CrateContext, Funclet}; use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext}; use monomorphize::Instance; -use abi::{ArgAttribute, FnType}; +use abi::{ArgAttribute, FnType, PassMode}; use syntax_pos::{DUMMY_SP, NO_EXPANSION, BytePos, Span}; use syntax::symbol::keywords; @@ -429,55 +429,52 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, let arg = &mircx.fn_ty.args[idx]; idx += 1; - let lvalue = if arg.is_indirect() { - // Don't copy an indirect argument to an alloca, the caller - // already put it in a temporary alloca and gave it up - // FIXME: lifetimes - 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; - LvalueRef::new_sized(llarg, arg.layout, Alignment::AbiAligned) - } else if !lvalue_locals.contains(local.index()) && - !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()); - - let a = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); - bcx.set_value_name(a, &(name.clone() + ".0")); - llarg_idx += 1; - - let b = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); - bcx.set_value_name(b, &(name + ".1")); + if arg.pad.is_some() { llarg_idx += 1; + } - return LocalRef::Operand(Some(OperandRef { - val: OperandValue::Pair(a, b), - layout: arg.layout - })); - } 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.layout); - } - + if arg_scope.is_none() && !lvalue_locals.contains(local.index()) { // 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 local = |op| LocalRef::Operand(Some(op)); + match arg.mode { + PassMode::Ignore => { + return local(OperandRef::new_zst(bcx.ccx, arg.layout)); + } + PassMode::Direct(_) => { + let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); + bcx.set_value_name(llarg, &name); + llarg_idx += 1; + return local( + OperandRef::from_immediate_or_packed_pair(bcx, llarg, arg.layout)); + } + PassMode::Pair(..) => { + let a = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); + bcx.set_value_name(a, &(name.clone() + ".0")); + llarg_idx += 1; + + let b = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); + bcx.set_value_name(b, &(name + ".1")); + llarg_idx += 1; + + return local(OperandRef { + val: OperandValue::Pair(a, b), + layout: arg.layout + }); + } + _ => {} } + } + + let lvalue = if arg.is_indirect() { + // Don't copy an indirect argument to an alloca, the caller + // already put it in a temporary alloca and gave it up. + // FIXME: lifetimes let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); bcx.set_value_name(llarg, &name); llarg_idx += 1; - return LocalRef::Operand(Some( - OperandRef::from_immediate_or_packed_pair(bcx, llarg, arg.layout) - )); + LvalueRef::new_sized(llarg, arg.layout, Alignment::AbiAligned) } else { let tmp = LvalueRef::alloca(bcx, arg.layout, &name); arg.store_fn_arg(bcx, &mut llarg_idx, tmp); @@ -489,16 +486,19 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, // The Rust ABI passes indirect variables using a pointer and a manual copy, so we // need to insert a deref here, but the C ABI uses a pointer and a copy using the // byval attribute, for which LLVM does the deref itself, so we must not add it. - let variable_access = if arg.is_indirect() && - !arg.attrs.contains(ArgAttribute::ByVal) { - VariableAccess::IndirectVariable { - alloca: lvalue.llval, - address_operations: &deref_op, - } - } else { - VariableAccess::DirectVariable { alloca: lvalue.llval } + let mut variable_access = VariableAccess::DirectVariable { + alloca: lvalue.llval }; + if let PassMode::Indirect(ref attrs) = arg.mode { + if !attrs.contains(ArgAttribute::ByVal) { + variable_access = VariableAccess::IndirectVariable { + alloca: lvalue.llval, + address_operations: &deref_op, + }; + } + } + declare_local( bcx, &mircx.debug_context, |
