rustc_trans: nest abi::ArgType's for fat pointers instead of eagerly flattening.

This commit is contained in:
Eduard-Mihai Burtescu 2017-09-20 05:16:06 +03:00
parent f2e7e17d9e
commit 88f70323e4
4 changed files with 161 additions and 137 deletions

View File

@ -420,7 +420,7 @@ impl CastTarget {
/// should be passed to or returned from a function
///
/// This is borrowed from clang's ABIInfo.h
#[derive(Clone, Copy, Debug)]
#[derive(Debug)]
pub struct ArgType<'tcx> {
kind: ArgKind,
pub layout: FullLayout<'tcx>,
@ -429,7 +429,8 @@ pub struct ArgType<'tcx> {
/// Dummy argument, which is emitted before the real argument.
pub pad: Option<Reg>,
/// Attributes of argument.
pub attrs: ArgAttributes
pub attrs: ArgAttributes,
pub nested: Vec<ArgType<'tcx>>
}
impl<'a, 'tcx> ArgType<'tcx> {
@ -439,11 +440,13 @@ impl<'a, 'tcx> ArgType<'tcx> {
layout,
cast: None,
pad: None,
attrs: ArgAttributes::default()
attrs: ArgAttributes::default(),
nested: vec![]
}
}
pub fn make_indirect(&mut self, ccx: &CrateContext<'a, 'tcx>) {
assert!(self.nested.is_empty());
assert_eq!(self.kind, ArgKind::Direct);
// Wipe old attributes, likely not valid through indirection.
@ -460,6 +463,7 @@ impl<'a, 'tcx> ArgType<'tcx> {
}
pub fn ignore(&mut self) {
assert!(self.nested.is_empty());
assert_eq!(self.kind, ArgKind::Direct);
self.kind = ArgKind::Ignore;
}
@ -482,10 +486,12 @@ impl<'a, 'tcx> ArgType<'tcx> {
}
pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) {
assert!(self.nested.is_empty());
self.cast = Some(target.into());
}
pub fn pad_with(&mut self, reg: Reg) {
assert!(self.nested.is_empty());
self.pad = Some(reg);
}
@ -561,6 +567,12 @@ impl<'a, 'tcx> ArgType<'tcx> {
}
pub fn store_fn_arg(&self, bcx: &Builder<'a, 'tcx>, idx: &mut usize, dst: LvalueRef<'tcx>) {
if !self.nested.is_empty() {
for (i, arg) in self.nested.iter().enumerate() {
arg.store_fn_arg(bcx, idx, dst.project_field(bcx, i));
}
return;
}
if self.pad.is_some() {
*idx += 1;
}
@ -578,7 +590,7 @@ impl<'a, 'tcx> ArgType<'tcx> {
///
/// I will do my best to describe this structure, but these
/// comments are reverse-engineered and may be inaccurate. -NDM
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct FnType<'tcx> {
/// The LLVM types of each argument.
pub args: Vec<ArgType<'tcx>>,
@ -613,7 +625,8 @@ impl<'a, 'tcx> FnType<'tcx> {
extra_args: &[Ty<'tcx>]) -> FnType<'tcx> {
let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args);
// Don't pass the vtable, it's not an argument of the virtual fn.
fn_ty.args[1].ignore();
assert_eq!(fn_ty.args[0].nested.len(), 2);
fn_ty.args[0].nested[1].ignore();
fn_ty.adjust_for_abi(ccx, sig);
fn_ty
}
@ -766,7 +779,7 @@ impl<'a, 'tcx> FnType<'tcx> {
for ty in inputs.iter().chain(extra_args.iter()) {
let mut arg = arg_of(ty, false);
if let ty::layout::Layout::FatPointer { .. } = *arg.layout.layout {
if type_is_fat_ptr(ccx, ty) {
let mut data = ArgType::new(arg.layout.field(ccx, 0));
let mut info = ArgType::new(arg.layout.field(ccx, 1));
@ -780,14 +793,16 @@ impl<'a, 'tcx> FnType<'tcx> {
info.attrs.set(ArgAttribute::NoAlias);
}
}
args.push(data);
args.push(info);
// FIXME(eddyb) other ABIs don't have logic for nested.
if rust_abi {
arg.nested = vec![data, info];
}
} else {
if let Some(inner) = rust_ptr_attrs(ty, &mut arg) {
arg.attrs.set_dereferenceable(ccx.size_of(inner));
}
args.push(arg);
}
args.push(arg);
}
FnType {
@ -854,6 +869,13 @@ impl<'a, 'tcx> FnType<'tcx> {
}
for arg in &mut self.args {
if arg.is_ignore() { continue; }
if !arg.nested.is_empty() {
for arg in &mut arg.nested {
assert!(arg.nested.is_empty());
fixup(arg);
}
continue;
}
fixup(arg);
}
if self.ret.is_indirect() {
@ -915,24 +937,36 @@ impl<'a, 'tcx> FnType<'tcx> {
ccx.immediate_llvm_type_of(self.ret.layout.ty)
};
for arg in &self.args {
if arg.is_ignore() {
continue;
}
// add padding
if let Some(ty) = arg.pad {
llargument_tys.push(ty.llvm_type(ccx));
}
{
let mut push = |arg: &ArgType<'tcx>| {
if arg.is_ignore() {
return;
}
// add padding
if let Some(ty) = arg.pad {
llargument_tys.push(ty.llvm_type(ccx));
}
let llarg_ty = if arg.is_indirect() {
arg.memory_ty(ccx).ptr_to()
} else if let Some(cast) = arg.cast {
cast.llvm_type(ccx)
} else {
ccx.immediate_llvm_type_of(arg.layout.ty)
let llarg_ty = if arg.is_indirect() {
arg.memory_ty(ccx).ptr_to()
} else if let Some(cast) = arg.cast {
cast.llvm_type(ccx)
} else {
ccx.immediate_llvm_type_of(arg.layout.ty)
};
llargument_tys.push(llarg_ty);
};
llargument_tys.push(llarg_ty);
for arg in &self.args {
if !arg.nested.is_empty() {
for arg in &arg.nested {
assert!(arg.nested.is_empty());
push(arg);
}
continue;
}
push(arg);
}
}
if self.variadic {
@ -948,12 +982,22 @@ impl<'a, 'tcx> FnType<'tcx> {
self.ret.attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn);
}
i += 1;
for arg in &self.args {
let mut apply = |arg: &ArgType| {
if !arg.is_ignore() {
if arg.pad.is_some() { i += 1; }
arg.attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn);
i += 1;
}
};
for arg in &self.args {
if !arg.nested.is_empty() {
for arg in &arg.nested {
assert!(arg.nested.is_empty());
apply(arg);
}
continue;
}
apply(arg);
}
}
@ -963,12 +1007,22 @@ impl<'a, 'tcx> FnType<'tcx> {
self.ret.attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite);
}
i += 1;
for arg in &self.args {
let mut apply = |arg: &ArgType| {
if !arg.is_ignore() {
if arg.pad.is_some() { i += 1; }
arg.attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite);
i += 1;
}
};
for arg in &self.args {
if !arg.nested.is_empty() {
for arg in &arg.nested {
assert!(arg.nested.is_empty());
apply(arg);
}
continue;
}
apply(arg);
}
if self.cconv != llvm::CCallConv {

View File

@ -27,7 +27,7 @@ use type_::Type;
use value::Value;
use rustc::traits;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::layout::{self, HasDataLayout, Layout, LayoutOf};
use rustc::ty::layout::{self, HasDataLayout, LayoutOf};
use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::hir;
@ -41,10 +41,15 @@ use syntax_pos::{Span, DUMMY_SP};
pub use context::{CrateContext, SharedCrateContext};
pub fn type_is_fat_ptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
if let Layout::FatPointer { .. } = *ccx.layout_of(ty).layout {
true
} else {
false
match ty.sty {
ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
!ccx.shared().type_is_sized(ty)
}
ty::TyAdt(def, _) if def.is_box() => {
!ccx.shared().type_is_sized(ty.boxed_ty())
}
_ => false
}
}
@ -63,9 +68,8 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -
pub fn type_is_imm_pair<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
-> bool {
let layout = ccx.layout_of(ty);
match *layout.layout {
Layout::FatPointer => true,
Layout::Univariant => {
match *layout.fields {
layout::FieldPlacement::Arbitrary { .. } => {
// There must be only 2 fields.
if layout.fields.count() != 2 {
return false;

View File

@ -215,13 +215,12 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
}
mir::TerminatorKind::Return => {
let ret = self.fn_ty.ret;
if ret.is_ignore() || ret.is_indirect() {
if self.fn_ty.ret.is_ignore() || self.fn_ty.ret.is_indirect() {
bcx.ret_void();
return;
}
let llval = if let Some(cast_ty) = ret.cast {
let llval = if let Some(cast_ty) = self.fn_ty.ret.cast {
let op = match self.locals[mir::RETURN_POINTER] {
LocalRef::Operand(Some(op)) => op,
LocalRef::Operand(None) => bug!("use of return before def"),
@ -234,7 +233,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
};
let llslot = match op.val {
Immediate(_) | Pair(..) => {
let scratch = LvalueRef::alloca(&bcx, ret.layout.ty, "ret");
let scratch = LvalueRef::alloca(&bcx, self.fn_ty.ret.layout.ty, "ret");
op.store(&bcx, scratch);
scratch.llval
}
@ -246,7 +245,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
};
let load = bcx.load(
bcx.pointercast(llslot, cast_ty.llvm_type(bcx.ccx).ptr_to()),
Some(ret.layout.align(bcx.ccx)));
Some(self.fn_ty.ret.layout.align(bcx.ccx)));
load
} else {
let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER));
@ -562,9 +561,18 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
(&args[..], None)
};
let mut idx = 0;
for arg in first_args {
for (idx, arg) in first_args.iter().enumerate() {
let mut op = self.trans_operand(&bcx, arg);
if idx == 0 {
if let Pair(_, meta) = op.val {
if let Some(ty::InstanceDef::Virtual(_, idx)) = def {
let llmeth = meth::VirtualIndex::from_index(idx)
.get_fn(&bcx, meta);
let llty = fn_ty.llvm_type(bcx.ccx).ptr_to();
llfn = Some(bcx.pointercast(llmeth, llty));
}
}
}
// The callee needs to own the argument memory if we pass it
// by-ref, so make a local copy of non-immediate constants.
@ -574,12 +582,11 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
op.val = Ref(tmp.llval, tmp.alignment);
}
self.trans_argument(&bcx, op, &mut llargs, &fn_ty,
&mut idx, &mut llfn, &def);
self.trans_argument(&bcx, op, &mut llargs, &fn_ty.args[idx]);
}
if let Some(tup) = untuple {
self.trans_arguments_untupled(&bcx, tup, &mut llargs, &fn_ty,
&mut idx, &mut llfn, &def)
self.trans_arguments_untupled(&bcx, tup, &mut llargs,
&fn_ty.args[first_args.len()..])
}
let fn_ptr = match (llfn, instance) {
@ -602,36 +609,22 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
bcx: &Builder<'a, 'tcx>,
op: OperandRef<'tcx>,
llargs: &mut Vec<ValueRef>,
fn_ty: &FnType<'tcx>,
next_idx: &mut usize,
llfn: &mut Option<ValueRef>,
def: &Option<ty::InstanceDef<'tcx>>) {
arg: &ArgType<'tcx>) {
if let Pair(a, b) = op.val {
// Treat the values in a fat pointer separately.
if common::type_is_fat_ptr(bcx.ccx, op.ty) {
let (ptr, meta) = (a, b);
if *next_idx == 0 {
if let Some(ty::InstanceDef::Virtual(_, idx)) = *def {
let llmeth = meth::VirtualIndex::from_index(idx).get_fn(bcx, meta);
let llty = fn_ty.llvm_type(bcx.ccx).ptr_to();
*llfn = Some(bcx.pointercast(llmeth, llty));
}
}
if !arg.nested.is_empty() {
assert_eq!(arg.nested.len(), 2);
let imm_op = |x| OperandRef {
val: Immediate(x),
// We won't be checking the type again.
ty: bcx.tcx().types.err
};
self.trans_argument(bcx, imm_op(ptr), llargs, fn_ty, next_idx, llfn, def);
self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, llfn, def);
self.trans_argument(bcx, imm_op(a), llargs, &arg.nested[0]);
self.trans_argument(bcx, imm_op(b), llargs, &arg.nested[1]);
return;
}
}
let arg = &fn_ty.args[*next_idx];
*next_idx += 1;
// Fill padding with undef value, where applicable.
if let Some(ty) = arg.pad {
llargs.push(C_undef(ty.llvm_type(bcx.ccx)));
@ -686,10 +679,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
bcx: &Builder<'a, 'tcx>,
operand: &mir::Operand<'tcx>,
llargs: &mut Vec<ValueRef>,
fn_ty: &FnType<'tcx>,
next_idx: &mut usize,
llfn: &mut Option<ValueRef>,
def: &Option<ty::InstanceDef<'tcx>>) {
args: &[ArgType<'tcx>]) {
let tuple = self.trans_operand(bcx, operand);
let arg_types = match tuple.ty.sty {
@ -702,18 +692,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
match tuple.val {
Ref(llval, align) => {
let tuple_ptr = LvalueRef::new_sized(llval, tuple.ty, align);
for (n, &ty) in arg_types.iter().enumerate() {
for n in 0..arg_types.len() {
let field_ptr = tuple_ptr.project_field(bcx, n);
let op = if common::type_is_fat_ptr(bcx.ccx, ty) {
field_ptr.load(bcx)
} else {
// trans_argument will load this if it needs to
OperandRef {
val: Ref(field_ptr.llval, field_ptr.alignment),
ty
}
};
self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def);
self.trans_argument(bcx, field_ptr.load(bcx), llargs, &args[n]);
}
}
@ -728,7 +709,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
val: Immediate(elem),
ty,
};
self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def);
self.trans_argument(bcx, op, llargs, &args[n]);
}
}
Pair(a, b) => {
@ -740,7 +721,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
val: Immediate(elem),
ty,
};
self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def);
self.trans_argument(bcx, op, llargs, &args[n]);
}
}
}

View File

@ -22,7 +22,7 @@ use builder::Builder;
use common::{self, CrateContext, Funclet};
use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext};
use monomorphize::Instance;
use abi::{self, ArgAttribute, FnType};
use abi::{ArgAttribute, FnType};
use type_of;
use syntax_pos::{DUMMY_SP, NO_EXPANSION, BytePos, Span};
@ -401,22 +401,10 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
};
let lvalue = LvalueRef::alloca(bcx, arg_ty, &name);
for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() {
let dst = lvalue.project_field(bcx, i);
for i in 0..tupled_arg_tys.len() {
let arg = &mircx.fn_ty.args[idx];
idx += 1;
if common::type_is_fat_ptr(bcx.ccx, tupled_arg_ty) {
// We pass fat pointers as two words, but inside the tuple
// they are the two sub-fields of a single aggregate field.
let meta = &mircx.fn_ty.args[idx];
idx += 1;
arg.store_fn_arg(bcx, &mut llarg_idx,
dst.project_field(bcx, abi::FAT_PTR_ADDR));
meta.store_fn_arg(bcx, &mut llarg_idx,
dst.project_field(bcx, abi::FAT_PTR_EXTRA));
} else {
arg.store_fn_arg(bcx, &mut llarg_idx, dst);
}
arg.store_fn_arg(bcx, &mut llarg_idx, lvalue.project_field(bcx, i));
}
// Now that we have one alloca that contains the aggregate value,
@ -453,26 +441,19 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
llarg_idx += 1;
LvalueRef::new_sized(llarg, arg_ty, Alignment::AbiAligned)
} else if !lvalue_locals.contains(local.index()) &&
arg.cast.is_none() && arg_scope.is_none() {
if arg.is_ignore() {
return LocalRef::new_operand(bcx.ccx, arg_ty);
}
!arg.nested.is_empty() {
assert_eq!(arg.nested.len(), 2);
let (a, b) = (&arg.nested[0], &arg.nested[1]);
assert!(!a.is_ignore() && a.cast.is_none() && a.pad.is_none());
assert!(!b.is_ignore() && b.cast.is_none() && b.pad.is_none());
// We don't have to cast or keep the argument in the alloca.
// FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
// of putting everything in allocas just so we can use llvm.dbg.declare.
if arg.pad.is_some() {
llarg_idx += 1;
}
let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
let mut a = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
llarg_idx += 1;
let val = if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
let meta = &mircx.fn_ty.args[idx];
idx += 1;
assert!(meta.cast.is_none() && meta.pad.is_none());
let llmeta = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
llarg_idx += 1;
let mut b = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
llarg_idx += 1;
if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
// FIXME(eddyb) As we can't perfectly represent the data and/or
// vtable pointer in a fat pointers in Rust's typesystem, and
// because we split fat pointers into two ArgType's, they're
@ -486,36 +467,40 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
let data_llty = bcx.ccx.llvm_type_of(pointee);
let meta_llty = type_of::unsized_info_ty(bcx.ccx, pointee);
let llarg = bcx.pointercast(llarg, data_llty.ptr_to());
bcx.set_value_name(llarg, &(name.clone() + ".ptr"));
let llmeta = bcx.pointercast(llmeta, meta_llty);
bcx.set_value_name(llmeta, &(name + ".meta"));
a = bcx.pointercast(a, data_llty.ptr_to());
bcx.set_value_name(a, &(name.clone() + ".ptr"));
b = bcx.pointercast(b, meta_llty);
bcx.set_value_name(b, &(name + ".meta"));
}
OperandValue::Pair(llarg, llmeta)
} else {
bcx.set_value_name(llarg, &name);
OperandValue::Immediate(llarg)
};
return LocalRef::Operand(Some(OperandRef {
val: OperandValue::Pair(a, b),
ty: arg_ty
}));
} else if !lvalue_locals.contains(local.index()) &&
!arg.is_indirect() && arg.cast.is_none() &&
arg_scope.is_none() {
if arg.is_ignore() {
return LocalRef::new_operand(bcx.ccx, arg_ty);
}
// We don't have to cast or keep the argument in the alloca.
// FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
// of putting everything in allocas just so we can use llvm.dbg.declare.
if arg.pad.is_some() {
llarg_idx += 1;
}
let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
bcx.set_value_name(llarg, &name);
llarg_idx += 1;
let operand = OperandRef {
val,
val: OperandValue::Immediate(llarg),
ty: arg_ty
};
return LocalRef::Operand(Some(operand.unpack_if_pair(bcx)));
} else {
let tmp = LvalueRef::alloca(bcx, arg_ty, &name);
if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
// we pass fat pointers as two words, but we want to
// represent them internally as a pointer to two words,
// so make an alloca to store them in.
let meta = &mircx.fn_ty.args[idx];
idx += 1;
arg.store_fn_arg(bcx, &mut llarg_idx, tmp.project_field(bcx, abi::FAT_PTR_ADDR));
meta.store_fn_arg(bcx, &mut llarg_idx, tmp.project_field(bcx, abi::FAT_PTR_EXTRA));
} else {
// otherwise, arg is passed by value, so make a
// temporary and store it there
arg.store_fn_arg(bcx, &mut llarg_idx, tmp);
}
arg.store_fn_arg(bcx, &mut llarg_idx, tmp);
tmp
};
arg_scope.map(|scope| {