rustc_trans: support scalar pairs directly in the Rust ABI.
This commit is contained in:
parent
7a36141465
commit
18ecc564f2
9 changed files with 373 additions and 369 deletions
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
for arg in &self.args {
|
||||
// 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)
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
for arg in &self.args {
|
||||
if !arg.nested.is_empty() {
|
||||
for arg in &arg.nested {
|
||||
assert!(arg.nested.is_empty());
|
||||
apply(arg);
|
||||
}
|
||||
continue;
|
||||
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.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);
|
||||
}
|
||||
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;
|
||||
};
|
||||
for arg in &self.args {
|
||||
if !arg.nested.is_empty() {
|
||||
for arg in &arg.nested {
|
||||
assert!(arg.nested.is_empty());
|
||||
apply(arg);
|
||||
}
|
||||
continue;
|
||||
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.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);
|
||||
}
|
||||
PassMode::Cast(_) => apply(&ArgAttributes::new()),
|
||||
}
|
||||
apply(arg);
|
||||
}
|
||||
|
||||
if self.cconv != llvm::CCallConv {
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
};
|
||||
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)
|
||||
};
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
if arg.pad.is_some() {
|
||||
llarg_idx += 1;
|
||||
}
|
||||
|
||||
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.
|
||||
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
|
||||
// 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"));
|
||||
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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
return LocalRef::Operand(Some(
|
||||
OperandRef::from_immediate_or_packed_pair(bcx, llarg, arg.layout)
|
||||
));
|
||||
} 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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue