rustc_trans: support scalar pairs directly in the Rust ABI.

This commit is contained in:
Eduard-Mihai Burtescu 2017-10-10 20:54:50 +03:00
parent 7a36141465
commit 18ecc564f2
9 changed files with 373 additions and 369 deletions

View file

@ -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,
}
}
}

View file

@ -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 {

View file

@ -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") {

View file

@ -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();
}
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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);

View file

@ -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,