1
Fork 0

Pass fat pointers in two immediate arguments

This has a number of advantages compared to creating a copy in memory
and passing a pointer. The obvious one is that we don't have to put the
data into memory but can keep it in registers. Since we're currently
passing a pointer anyway (instead of using e.g. a known offset on the
stack, which is what the `byval` attribute would achieve), we only use a
single additional register for each fat pointer, but save at least two
pointers worth of stack in exchange (sometimes more because more than
one copy gets eliminated). On archs that pass arguments on the stack, we
save a pointer worth of stack even without considering the omitted
copies.

Additionally, LLVM can optimize the code a lot better, to a large degree
due to the fact that lots of copies are gone or can be optimized away.
Additionally, we can now emit attributes like nonnull on the data and/or
vtable pointers contained in the fat pointer, potentially allowing for
even more optimizations.

This results in LLVM passes being about 3-7% faster (depending on the
crate), and the resulting code is also a few percent smaller, for
example:

   text    data  filename
5671479 3941461  before/librustc-d8ace771.so
5447663 3905745  after/librustc-d8ace771.so

1944425 2394024  before/libstd-d8ace771.so
1896769 2387610  after/libstd-d8ace771.so

I had to remove a call in the backtrace-debuginfo test, because LLVM can
now merge the tails of some blocks when optimizations are turned on,
which can't correctly preserve line info.

Fixes #22924

Cc #22891 (at least for fat pointers the code is good now)
This commit is contained in:
Björn Steinbrink 2015-06-18 23:57:40 +02:00
parent 02d74a4852
commit f777562eab
15 changed files with 229 additions and 119 deletions

View file

@ -828,19 +828,11 @@ fn compare_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
None, None,
&format!("comparison of `{}`", rhs_t), &format!("comparison of `{}`", rhs_t),
StrEqFnLangItem); StrEqFnLangItem);
let t = ty::mk_str_slice(cx.tcx(), cx.tcx().mk_region(ty::ReStatic), ast::MutImmutable); let lhs_data = Load(cx, expr::get_dataptr(cx, lhs));
// The comparison function gets the slices by value, so we have to make copies here. Even let lhs_len = Load(cx, expr::get_len(cx, lhs));
// if the function doesn't write through the pointer, things like lifetime intrinsics let rhs_data = Load(cx, expr::get_dataptr(cx, rhs));
// require that we do this properly let rhs_len = Load(cx, expr::get_len(cx, rhs));
let lhs_arg = alloc_ty(cx, t, "lhs"); callee::trans_lang_call(cx, did, &[lhs_data, lhs_len, rhs_data, rhs_len], None, debug_loc)
let rhs_arg = alloc_ty(cx, t, "rhs");
memcpy_ty(cx, lhs_arg, lhs, t);
memcpy_ty(cx, rhs_arg, rhs, t);
let res = callee::trans_lang_call(cx, did, &[lhs_arg, rhs_arg], None, debug_loc);
call_lifetime_end(res.bcx, lhs_arg);
call_lifetime_end(res.bcx, rhs_arg);
res
} }
let _icx = push_ctxt("compare_values"); let _icx = push_ctxt("compare_values");

View file

@ -188,7 +188,7 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx
}; };
// Index 0 is the return value of the llvm func, so we start at 1 // Index 0 is the return value of the llvm func, so we start at 1
let mut first_arg_offset = 1; let mut idx = 1;
if let ty::FnConverging(ret_ty) = ret_ty { if let ty::FnConverging(ret_ty) = ret_ty {
// A function pointer is called without the declaration // A function pointer is called without the declaration
// available, so we have to apply any attributes with ABI // available, so we have to apply any attributes with ABI
@ -206,7 +206,7 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx
.arg(1, llvm::DereferenceableAttribute(llret_sz)); .arg(1, llvm::DereferenceableAttribute(llret_sz));
// Add one more since there's an outptr // Add one more since there's an outptr
first_arg_offset += 1; idx += 1;
} else { } else {
// The `noalias` attribute on the return value is useful to a // The `noalias` attribute on the return value is useful to a
// function ptr caller. // function ptr caller.
@ -236,10 +236,9 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx
} }
} }
for (idx, &t) in input_tys.iter().enumerate().map(|(i, v)| (i + first_arg_offset, v)) { for &t in input_tys.iter() {
match t.sty { match t.sty {
// this needs to be first to prevent fat pointers from falling through _ if type_of::arg_is_indirect(ccx, t) => {
_ if !common::type_is_immediate(ccx, t) => {
let llarg_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, t)); let llarg_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, t));
// For non-immediate arguments the callee gets its own copy of // For non-immediate arguments the callee gets its own copy of
@ -256,10 +255,17 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx
// `Box` pointer parameters never alias because ownership is transferred // `Box` pointer parameters never alias because ownership is transferred
ty::TyBox(inner) => { ty::TyBox(inner) => {
let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, inner)); attrs.arg(idx, llvm::Attribute::NoAlias);
attrs.arg(idx, llvm::Attribute::NoAlias) if common::type_is_sized(ccx.tcx(), inner) {
.arg(idx, llvm::DereferenceableAttribute(llsz)); let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, inner));
attrs.arg(idx, llvm::DereferenceableAttribute(llsz));
} else {
attrs.arg(idx, llvm::NonNullAttribute);
if ty::type_is_trait(inner) {
attrs.arg(idx + 1, llvm::NonNullAttribute);
}
}
} }
ty::TyRef(b, mt) => { ty::TyRef(b, mt) => {
@ -278,10 +284,17 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx
attrs.arg(idx, llvm::Attribute::ReadOnly); attrs.arg(idx, llvm::Attribute::ReadOnly);
} }
// & pointer parameters are also never null and we know exactly // & pointer parameters are also never null and for sized types we also know
// how many bytes we can dereference // exactly how many bytes we can dereference
if common::type_is_sized(ccx.tcx(), mt.ty) {
let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, mt.ty)); let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
attrs.arg(idx, llvm::DereferenceableAttribute(llsz)); attrs.arg(idx, llvm::DereferenceableAttribute(llsz));
} else {
attrs.arg(idx, llvm::NonNullAttribute);
if ty::type_is_trait(mt.ty) {
attrs.arg(idx + 1, llvm::NonNullAttribute);
}
}
// When a reference in an argument has no named lifetime, it's // When a reference in an argument has no named lifetime, it's
// impossible for that reference to escape this function // impossible for that reference to escape this function
@ -293,6 +306,12 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx
_ => () _ => ()
} }
if common::type_is_fat_ptr(ccx.tcx(), t) {
idx += 2;
} else {
idx += 1;
}
} }
attrs attrs

View file

@ -1297,16 +1297,28 @@ pub type RvalueDatum<'tcx> = datum::Datum<'tcx, datum::Rvalue>;
// create_datums_for_fn_args: creates rvalue datums for each of the // create_datums_for_fn_args: creates rvalue datums for each of the
// incoming function arguments. These will later be stored into // incoming function arguments. These will later be stored into
// appropriate lvalue datums. // appropriate lvalue datums.
pub fn create_datums_for_fn_args<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, pub fn create_datums_for_fn_args<'a, 'tcx>(bcx: Block<'a, 'tcx>,
arg_tys: &[Ty<'tcx>]) arg_tys: &[Ty<'tcx>])
-> Vec<RvalueDatum<'tcx>> { -> Vec<RvalueDatum<'tcx>> {
let _icx = push_ctxt("create_datums_for_fn_args"); let _icx = push_ctxt("create_datums_for_fn_args");
let fcx = bcx.fcx;
// Return an array wrapping the ValueRefs that we get from `get_param` for // Return an array wrapping the ValueRefs that we get from `get_param` for
// each argument into datums. // each argument into datums.
arg_tys.iter().enumerate().map(|(i, &arg_ty)| { let mut i = fcx.arg_offset() as c_uint;
let llarg = get_param(fcx.llfn, fcx.arg_pos(i) as c_uint); arg_tys.iter().map(|&arg_ty| {
if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
let llty = type_of::type_of(bcx.ccx(), arg_ty);
let data = get_param(fcx.llfn, i);
let extra = get_param(fcx.llfn, i + 1);
let fat_ptr = expr::make_fat_ptr(bcx, llty, data, extra);
i += 2;
datum::Datum::new(fat_ptr, arg_ty, datum::Rvalue { mode: datum::ByValue })
} else {
let llarg = get_param(fcx.llfn, i);
i += 1;
datum::Datum::new(llarg, arg_ty, arg_kind(fcx, arg_ty)) datum::Datum::new(llarg, arg_ty, arg_kind(fcx, arg_ty))
}
}).collect() }).collect()
} }
@ -1321,12 +1333,23 @@ fn create_datums_for_fn_args_under_call_abi<'blk, 'tcx>(
arg_tys: &[Ty<'tcx>]) arg_tys: &[Ty<'tcx>])
-> Vec<RvalueDatum<'tcx>> { -> Vec<RvalueDatum<'tcx>> {
let mut result = Vec::new(); let mut result = Vec::new();
let mut idx = bcx.fcx.arg_offset() as c_uint;
for (i, &arg_ty) in arg_tys.iter().enumerate() { for (i, &arg_ty) in arg_tys.iter().enumerate() {
if i < arg_tys.len() - 1 { if i < arg_tys.len() - 1 {
// Regular argument. // Regular argument.
let llarg = get_param(bcx.fcx.llfn, bcx.fcx.arg_pos(i) as c_uint); result.push(if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
result.push(datum::Datum::new(llarg, arg_ty, arg_kind(bcx.fcx, let llty = type_of::type_of(bcx.ccx(), arg_ty);
arg_ty))); let data = get_param(bcx.fcx.llfn, idx);
let extra = get_param(bcx.fcx.llfn, idx + 1);
idx += 2;
let fat_ptr = expr::make_fat_ptr(bcx, llty, data, extra);
datum::Datum::new(fat_ptr, arg_ty, datum::Rvalue { mode: datum::ByValue })
} else {
let val = get_param(bcx.fcx.llfn, idx);
idx += 1;
datum::Datum::new(val, arg_ty, arg_kind(bcx.fcx, arg_ty))
});
continue continue
} }
@ -1346,15 +1369,21 @@ fn create_datums_for_fn_args_under_call_abi<'blk, 'tcx>(
llval| { llval| {
for (j, &tupled_arg_ty) in for (j, &tupled_arg_ty) in
tupled_arg_tys.iter().enumerate() { tupled_arg_tys.iter().enumerate() {
let llarg =
get_param(bcx.fcx.llfn,
bcx.fcx.arg_pos(i + j) as c_uint);
let lldest = GEPi(bcx, llval, &[0, j]); let lldest = GEPi(bcx, llval, &[0, j]);
if common::type_is_fat_ptr(bcx.tcx(), tupled_arg_ty) {
let data = get_param(bcx.fcx.llfn, idx);
let extra = get_param(bcx.fcx.llfn, idx + 1);
Store(bcx, data, expr::get_dataptr(bcx, lldest));
Store(bcx, extra, expr::get_len(bcx, lldest));
idx += 2;
} else {
let datum = datum::Datum::new( let datum = datum::Datum::new(
llarg, get_param(bcx.fcx.llfn, idx),
tupled_arg_ty, tupled_arg_ty,
arg_kind(bcx.fcx, tupled_arg_ty)); arg_kind(bcx.fcx, tupled_arg_ty));
idx += 1;
bcx = datum.store_to(bcx, lldest); bcx = datum.store_to(bcx, lldest);
};
} }
bcx bcx
})); }));
@ -1566,7 +1595,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
} }
_ => { _ => {
let arg_tys = untuple_arguments_if_necessary(ccx, &monomorphized_arg_types, abi); let arg_tys = untuple_arguments_if_necessary(ccx, &monomorphized_arg_types, abi);
create_datums_for_fn_args(&fcx, &arg_tys) create_datums_for_fn_args(bcx, &arg_tys)
} }
}; };
@ -1773,7 +1802,7 @@ fn trans_enum_variant_or_tuple_like_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx
ty::erase_late_bound_regions( ty::erase_late_bound_regions(
ccx.tcx(), &ty::ty_fn_args(ctor_ty)); ccx.tcx(), &ty::ty_fn_args(ctor_ty));
let arg_datums = create_datums_for_fn_args(&fcx, &arg_tys[..]); let arg_datums = create_datums_for_fn_args(bcx, &arg_tys[..]);
if !type_is_zero_size(fcx.ccx, result_ty.unwrap()) { if !type_is_zero_size(fcx.ccx, result_ty.unwrap()) {
let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot"); let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot");

View file

@ -343,11 +343,12 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
let llargs = get_params(fcx.llfn); let llargs = get_params(fcx.llfn);
let self_idx = fcx.arg_offset();
// the first argument (`self`) will be ptr to the the fn pointer // the first argument (`self`) will be ptr to the the fn pointer
let llfnpointer = if is_by_ref { let llfnpointer = if is_by_ref {
Load(bcx, llargs[fcx.arg_pos(0)]) Load(bcx, llargs[self_idx])
} else { } else {
llargs[fcx.arg_pos(0)] llargs[self_idx]
}; };
assert!(!fcx.needs_ret_allocas); assert!(!fcx.needs_ret_allocas);
@ -360,7 +361,7 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
DebugLoc::None, DebugLoc::None,
bare_fn_ty, bare_fn_ty,
|bcx, _| Callee { bcx: bcx, data: Fn(llfnpointer) }, |bcx, _| Callee { bcx: bcx, data: Fn(llfnpointer) },
ArgVals(&llargs[fcx.arg_pos(1)..]), ArgVals(&llargs[(self_idx + 1)..]),
dest).bcx; dest).bcx;
finish_fn(&fcx, bcx, sig.output, DebugLoc::None); finish_fn(&fcx, bcx, sig.output, DebugLoc::None);
@ -1129,6 +1130,10 @@ pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_id)); bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_id));
val = arg_datum.val; val = arg_datum.val;
} }
DontAutorefArg if common::type_is_fat_ptr(bcx.tcx(), arg_datum_ty) &&
!bcx.fcx.type_needs_drop(arg_datum_ty) => {
val = arg_datum.val
}
DontAutorefArg => { DontAutorefArg => {
// Make this an rvalue, since we are going to be // Make this an rvalue, since we are going to be
// passing ownership. // passing ownership.
@ -1147,7 +1152,7 @@ pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
} }
} }
if formal_arg_ty != arg_datum_ty { if type_of::arg_is_indirect(ccx, formal_arg_ty) && formal_arg_ty != arg_datum_ty {
// this could happen due to e.g. subtyping // this could happen due to e.g. subtyping
let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty); let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty);
debug!("casting actual type ({}) to match formal ({})", debug!("casting actual type ({}) to match formal ({})",
@ -1159,7 +1164,12 @@ pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
debug!("--- trans_arg_datum passing {}", bcx.val_to_string(val)); debug!("--- trans_arg_datum passing {}", bcx.val_to_string(val));
if common::type_is_fat_ptr(bcx.tcx(), formal_arg_ty) {
llargs.push(Load(bcx, expr::get_dataptr(bcx, val)));
llargs.push(Load(bcx, expr::get_len(bcx, val)));
} else {
llargs.push(val); llargs.push(val);
}
bcx bcx
} }

View file

@ -411,7 +411,8 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
let self_scope = fcx.push_custom_cleanup_scope(); let self_scope = fcx.push_custom_cleanup_scope();
let self_scope_id = CustomScope(self_scope); let self_scope_id = CustomScope(self_scope);
let rvalue_mode = datum::appropriate_rvalue_mode(ccx, closure_ty); let rvalue_mode = datum::appropriate_rvalue_mode(ccx, closure_ty);
let llself = llargs[fcx.arg_pos(0)]; let self_idx = fcx.arg_offset();
let llself = llargs[self_idx];
let env_datum = Datum::new(llself, closure_ty, Rvalue::new(rvalue_mode)); let env_datum = Datum::new(llself, closure_ty, Rvalue::new(rvalue_mode));
let env_datum = unpack_datum!(bcx, let env_datum = unpack_datum!(bcx,
env_datum.to_lvalue_datum_in_scope(bcx, "self", env_datum.to_lvalue_datum_in_scope(bcx, "self",
@ -431,7 +432,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
DebugLoc::None, DebugLoc::None,
llref_fn_ty, llref_fn_ty,
|bcx, _| Callee { bcx: bcx, data: callee_data }, |bcx, _| Callee { bcx: bcx, data: callee_data },
ArgVals(&llargs[fcx.arg_pos(1)..]), ArgVals(&llargs[(self_idx + 1)..]),
dest).bcx; dest).bcx;
fcx.pop_custom_cleanup_scope(self_scope); fcx.pop_custom_cleanup_scope(self_scope);

View file

@ -421,13 +421,8 @@ pub struct FunctionContext<'a, 'tcx: 'a> {
} }
impl<'a, 'tcx> FunctionContext<'a, 'tcx> { impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
pub fn arg_pos(&self, arg: usize) -> usize { pub fn arg_offset(&self) -> usize {
let arg = self.env_arg_pos() + arg; self.env_arg_pos() + if self.llenv.is_some() { 1 } else { 0 }
if self.llenv.is_some() {
arg + 1
} else {
arg
}
} }
pub fn env_arg_pos(&self) -> usize { pub fn env_arg_pos(&self) -> usize {

View file

@ -293,6 +293,9 @@ pub fn get_dataptr(bcx: Block, fat_ptr: ValueRef) -> ValueRef {
GEPi(bcx, fat_ptr, &[0, abi::FAT_PTR_ADDR]) GEPi(bcx, fat_ptr, &[0, abi::FAT_PTR_ADDR])
} }
pub fn make_fat_ptr(bcx: Block, ty: Type, data: ValueRef, extra: ValueRef) -> ValueRef {
InsertValue(bcx, InsertValue(bcx, C_undef(ty), data, 0), extra, 1)
}
pub fn copy_fat_ptr(bcx: Block, src_ptr: ValueRef, dst_ptr: ValueRef) { pub fn copy_fat_ptr(bcx: Block, src_ptr: ValueRef, dst_ptr: ValueRef) {
Store(bcx, Load(bcx, get_dataptr(bcx, src_ptr)), get_dataptr(bcx, dst_ptr)); Store(bcx, Load(bcx, get_dataptr(bcx, src_ptr)), get_dataptr(bcx, dst_ptr));
Store(bcx, Load(bcx, get_len(bcx, src_ptr)), get_len(bcx, dst_ptr)); Store(bcx, Load(bcx, get_len(bcx, src_ptr)), get_len(bcx, dst_ptr));

View file

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
use back::link; use back::{abi, link};
use llvm::{ValueRef, CallConv, get_param}; use llvm::{ValueRef, CallConv, get_param};
use llvm; use llvm;
use middle::weak_lang_items; use middle::weak_lang_items;
@ -22,6 +22,7 @@ use trans::cabi;
use trans::common::*; use trans::common::*;
use trans::debuginfo::DebugLoc; use trans::debuginfo::DebugLoc;
use trans::declare; use trans::declare;
use trans::expr;
use trans::machine; use trans::machine;
use trans::monomorphize; use trans::monomorphize;
use trans::type_::Type; use trans::type_::Type;
@ -272,10 +273,11 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
} }
} }
for (i, &llarg_rust) in llargs_rust.iter().enumerate() { let mut offset = 0;
let mut llarg_rust = llarg_rust; for (i, arg_ty) in arg_tys.iter().enumerate() {
let mut llarg_rust = llargs_rust[i + offset];
if arg_tys[i].is_ignore() { if arg_ty.is_ignore() {
continue; continue;
} }
@ -286,7 +288,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
i, i,
ccx.tn().val_to_string(llarg_rust), ccx.tn().val_to_string(llarg_rust),
rust_indirect, rust_indirect,
ccx.tn().type_to_string(arg_tys[i].ty)); ccx.tn().type_to_string(arg_ty.ty));
// Ensure that we always have the Rust value indirectly, // Ensure that we always have the Rust value indirectly,
// because it makes bitcasting easier. // because it makes bitcasting easier.
@ -295,7 +297,13 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
base::alloca(bcx, base::alloca(bcx,
type_of::type_of(ccx, passed_arg_tys[i]), type_of::type_of(ccx, passed_arg_tys[i]),
"__arg"); "__arg");
if type_is_fat_ptr(ccx.tcx(), passed_arg_tys[i]) {
Store(bcx, llargs_rust[i + offset], expr::get_dataptr(bcx, scratch));
Store(bcx, llargs_rust[i + offset + 1], expr::get_len(bcx, scratch));
offset += 1;
} else {
base::store_ty(bcx, llarg_rust, scratch, passed_arg_tys[i]); base::store_ty(bcx, llarg_rust, scratch, passed_arg_tys[i]);
}
llarg_rust = scratch; llarg_rust = scratch;
} }
@ -303,7 +311,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ccx.tn().val_to_string(llarg_rust)); ccx.tn().val_to_string(llarg_rust));
// Check whether we need to do any casting // Check whether we need to do any casting
match arg_tys[i].cast { match arg_ty.cast {
Some(ty) => llarg_rust = BitCast(bcx, llarg_rust, ty.ptr_to()), Some(ty) => llarg_rust = BitCast(bcx, llarg_rust, ty.ptr_to()),
None => () None => ()
} }
@ -312,7 +320,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ccx.tn().val_to_string(llarg_rust)); ccx.tn().val_to_string(llarg_rust));
// Finally, load the value if needed for the foreign ABI // Finally, load the value if needed for the foreign ABI
let foreign_indirect = arg_tys[i].is_indirect(); let foreign_indirect = arg_ty.is_indirect();
let llarg_foreign = if foreign_indirect { let llarg_foreign = if foreign_indirect {
llarg_rust llarg_rust
} else { } else {
@ -328,7 +336,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
i, ccx.tn().val_to_string(llarg_foreign)); i, ccx.tn().val_to_string(llarg_foreign));
// fill padding with undef value // fill padding with undef value
match arg_tys[i].pad { match arg_ty.pad {
Some(ty) => llargs_foreign.push(C_undef(ty)), Some(ty) => llargs_foreign.push(C_undef(ty)),
None => () None => ()
} }
@ -783,12 +791,12 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
// If the types in the ABI and the Rust types don't match, // If the types in the ABI and the Rust types don't match,
// bitcast the llforeign_arg pointer so it matches the types // bitcast the llforeign_arg pointer so it matches the types
// Rust expects. // Rust expects.
if llforeign_arg_ty.cast.is_some() { if llforeign_arg_ty.cast.is_some() && !type_is_fat_ptr(ccx.tcx(), rust_ty){
assert!(!foreign_indirect); assert!(!foreign_indirect);
llforeign_arg = builder.bitcast(llforeign_arg, llrust_ty.ptr_to()); llforeign_arg = builder.bitcast(llforeign_arg, llrust_ty.ptr_to());
} }
let llrust_arg = if rust_indirect { let llrust_arg = if rust_indirect || type_is_fat_ptr(ccx.tcx(), rust_ty) {
llforeign_arg llforeign_arg
} else { } else {
if ty::type_is_bool(rust_ty) { if ty::type_is_bool(rust_ty) {
@ -810,8 +818,16 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
debug!("llrust_arg {}{}: {}", "#", debug!("llrust_arg {}{}: {}", "#",
i, ccx.tn().val_to_string(llrust_arg)); i, ccx.tn().val_to_string(llrust_arg));
if type_is_fat_ptr(ccx.tcx(), rust_ty) {
let next_llrust_ty = rust_param_tys.next().expect("Not enough parameter types!");
llrust_args.push(builder.load(builder.bitcast(builder.gepi(
llrust_arg, &[0, abi::FAT_PTR_ADDR]), llrust_ty.ptr_to())));
llrust_args.push(builder.load(builder.bitcast(builder.gepi(
llrust_arg, &[0, abi::FAT_PTR_EXTRA]), next_llrust_ty.ptr_to())));
} else {
llrust_args.push(llrust_arg); llrust_args.push(llrust_arg);
} }
}
// Perform the call itself // Perform the call itself
debug!("calling llrustfn = {}, t = {:?}", debug!("calling llrustfn = {}, t = {:?}",

View file

@ -259,7 +259,7 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
// llfn is expected be declared to take a parameter of the appropriate // llfn is expected be declared to take a parameter of the appropriate
// type, so we don't need to explicitly cast the function parameter. // type, so we don't need to explicitly cast the function parameter.
let llrawptr0 = get_param(llfn, fcx.arg_pos(0) as c_uint); let llrawptr0 = get_param(llfn, fcx.arg_offset() as c_uint);
let bcx = make_drop_glue(bcx, llrawptr0, g); let bcx = make_drop_glue(bcx, llrawptr0, g);
finish_fn(&fcx, bcx, ty::FnConverging(ty::mk_nil(ccx.tcx())), DebugLoc::None); finish_fn(&fcx, bcx, ty::FnConverging(ty::mk_nil(ccx.tcx())), DebugLoc::None);

View file

@ -275,17 +275,13 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
} else { } else {
(&exprs[0], &exprs[1]) (&exprs[0], &exprs[1])
}; };
let arg_tys = ty::erase_late_bound_regions(bcx.tcx(), &ty::ty_fn_args(callee_ty));
// evaluate destination address // evaluate destination address
let lldest_addr = unpack_result!(bcx, {
let dest_datum = unpack_datum!(bcx, expr::trans(bcx, dest_expr)); let dest_datum = unpack_datum!(bcx, expr::trans(bcx, dest_expr));
callee::trans_arg_datum(bcx, let dest_datum = unpack_datum!(
arg_tys[0], bcx, dest_datum.to_rvalue_datum(bcx, "arg"));
dest_datum, let dest_datum = unpack_datum!(
cleanup::CustomScope(cleanup_scope), bcx, dest_datum.to_appropriate_datum(bcx));
callee::DontAutorefArg)
});
// `expr::trans_into(bcx, expr, dest)` is equiv to // `expr::trans_into(bcx, expr, dest)` is equiv to
// //
@ -294,7 +290,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
// which for `dest == expr::SaveIn(addr)`, is equivalent to: // which for `dest == expr::SaveIn(addr)`, is equivalent to:
// //
// `trans(bcx, expr).store_to(bcx, addr)`. // `trans(bcx, expr).store_to(bcx, addr)`.
let lldest = expr::Dest::SaveIn(lldest_addr); let lldest = expr::Dest::SaveIn(dest_datum.val);
bcx = expr::trans_into(bcx, source_expr, lldest); bcx = expr::trans_into(bcx, source_expr, lldest);
let llresult = C_nil(ccx); let llresult = C_nil(ccx);
@ -370,8 +366,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
(_, "size_of_val") => { (_, "size_of_val") => {
let tp_ty = *substs.types.get(FnSpace, 0); let tp_ty = *substs.types.get(FnSpace, 0);
if !type_is_sized(tcx, tp_ty) { if !type_is_sized(tcx, tp_ty) {
let info = Load(bcx, expr::get_len(bcx, llargs[0])); let (llsize, _) = glue::size_and_align_of_dst(bcx, tp_ty, llargs[1]);
let (llsize, _) = glue::size_and_align_of_dst(bcx, tp_ty, info);
llsize llsize
} else { } else {
let lltp_ty = type_of::type_of(ccx, tp_ty); let lltp_ty = type_of::type_of(ccx, tp_ty);
@ -385,8 +380,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
(_, "min_align_of_val") => { (_, "min_align_of_val") => {
let tp_ty = *substs.types.get(FnSpace, 0); let tp_ty = *substs.types.get(FnSpace, 0);
if !type_is_sized(tcx, tp_ty) { if !type_is_sized(tcx, tp_ty) {
let info = Load(bcx, expr::get_len(bcx, llargs[0])); let (_, llalign) = glue::size_and_align_of_dst(bcx, tp_ty, llargs[1]);
let (_, llalign) = glue::size_and_align_of_dst(bcx, tp_ty, info);
llalign llalign
} else { } else {
C_uint(ccx, type_of::align_of(ccx, tp_ty)) C_uint(ccx, type_of::align_of(ccx, tp_ty))
@ -399,7 +393,16 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
} }
(_, "drop_in_place") => { (_, "drop_in_place") => {
let tp_ty = *substs.types.get(FnSpace, 0); let tp_ty = *substs.types.get(FnSpace, 0);
glue::drop_ty(bcx, llargs[0], tp_ty, call_debug_location); let ptr = if type_is_sized(tcx, tp_ty) {
llargs[0]
} else {
let scratch = rvalue_scratch_datum(bcx, tp_ty, "tmp");
Store(bcx, llargs[0], expr::get_dataptr(bcx, scratch.val));
Store(bcx, llargs[1], expr::get_len(bcx, scratch.val));
fcx.schedule_lifetime_end(cleanup::CustomScope(cleanup_scope), scratch.val);
scratch.val
};
glue::drop_ty(bcx, ptr, tp_ty, call_debug_location);
C_nil(ccx) C_nil(ccx)
} }
(_, "type_name") => { (_, "type_name") => {
@ -980,7 +983,7 @@ fn with_overflow_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let ret = C_undef(type_of::type_of(bcx.ccx(), t)); let ret = C_undef(type_of::type_of(bcx.ccx(), t));
let ret = InsertValue(bcx, ret, result, 0); let ret = InsertValue(bcx, ret, result, 0);
let ret = InsertValue(bcx, ret, overflow, 1); let ret = InsertValue(bcx, ret, overflow, 1);
if type_is_immediate(bcx.ccx(), t) { if !arg_is_indirect(bcx.ccx(), t) {
let tmp = alloc_ty(bcx, t, "tmp"); let tmp = alloc_ty(bcx, t, "tmp");
Store(bcx, ret, tmp); Store(bcx, ret, tmp);
load_ty(bcx, tmp, t) load_ty(bcx, tmp, t)

View file

@ -468,7 +468,9 @@ fn trans_trait_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
self_datum.val self_datum.val
}; };
trans_trait_callee_from_llval(bcx, method_ty, vtable_index, llval) let llself = Load(bcx, GEPi(bcx, llval, &[0, abi::FAT_PTR_ADDR]));
let llvtable = Load(bcx, GEPi(bcx, llval, &[0, abi::FAT_PTR_EXTRA]));
trans_trait_callee_from_llval(bcx, method_ty, vtable_index, llself, llvtable)
} }
/// Same as `trans_trait_callee()` above, except that it is given a by-ref pointer to the object /// Same as `trans_trait_callee()` above, except that it is given a by-ref pointer to the object
@ -476,19 +478,18 @@ fn trans_trait_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
pub fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
callee_ty: Ty<'tcx>, callee_ty: Ty<'tcx>,
vtable_index: usize, vtable_index: usize,
llpair: ValueRef) llself: ValueRef,
llvtable: ValueRef)
-> Callee<'blk, 'tcx> { -> Callee<'blk, 'tcx> {
let _icx = push_ctxt("meth::trans_trait_callee"); let _icx = push_ctxt("meth::trans_trait_callee");
let ccx = bcx.ccx(); let ccx = bcx.ccx();
// Load the data pointer from the object. // Load the data pointer from the object.
debug!("trans_trait_callee_from_llval(callee_ty={}, vtable_index={}, llpair={})", debug!("trans_trait_callee_from_llval(callee_ty={}, vtable_index={}, llself={}, llvtable={})",
callee_ty, callee_ty,
vtable_index, vtable_index,
bcx.val_to_string(llpair)); bcx.val_to_string(llself),
let llboxptr = GEPi(bcx, llpair, &[0, abi::FAT_PTR_ADDR]); bcx.val_to_string(llvtable));
let llbox = Load(bcx, llboxptr);
let llself = PointerCast(bcx, llbox, Type::i8p(ccx));
// Replace the self type (&Self or Box<Self>) with an opaque pointer. // Replace the self type (&Self or Box<Self>) with an opaque pointer.
let llcallee_ty = match callee_ty.sty { let llcallee_ty = match callee_ty.sty {
@ -505,19 +506,13 @@ pub fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ccx.sess().bug("meth::trans_trait_callee given non-bare-rust-fn"); ccx.sess().bug("meth::trans_trait_callee given non-bare-rust-fn");
} }
}; };
let llvtable = Load(bcx, let mptr = Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET]));
PointerCast(bcx,
GEPi(bcx, llpair,
&[0, abi::FAT_PTR_EXTRA]),
Type::vtable(ccx).ptr_to().ptr_to()));
let mptr = Load(bcx, GEPi(bcx, llvtable, &[0, vtable_index + VTABLE_OFFSET]));
let mptr = PointerCast(bcx, mptr, llcallee_ty.ptr_to());
return Callee { return Callee {
bcx: bcx, bcx: bcx,
data: TraitItem(MethodData { data: TraitItem(MethodData {
llfn: mptr, llfn: PointerCast(bcx, mptr, llcallee_ty.ptr_to()),
llself: llself, llself: PointerCast(bcx, llself, Type::i8p(ccx)),
}) })
}; };
} }
@ -613,11 +608,12 @@ pub fn trans_object_shim<'a, 'tcx>(
let llargs = get_params(fcx.llfn); let llargs = get_params(fcx.llfn);
// the first argument (`self`) will be a trait object let self_idx = fcx.arg_offset();
let llobject = llargs[fcx.arg_pos(0)]; let llself = llargs[self_idx];
let llvtable = llargs[self_idx + 1];
debug!("trans_object_shim: llobject={}", debug!("trans_object_shim: llself={}, llvtable={}",
bcx.val_to_string(llobject)); bcx.val_to_string(llself), bcx.val_to_string(llvtable));
assert!(!fcx.needs_ret_allocas); assert!(!fcx.needs_ret_allocas);
@ -639,8 +635,8 @@ pub fn trans_object_shim<'a, 'tcx>(
|bcx, _| trans_trait_callee_from_llval(bcx, |bcx, _| trans_trait_callee_from_llval(bcx,
method_bare_fn_ty, method_bare_fn_ty,
method_offset_in_vtable, method_offset_in_vtable,
llobject), llself, llvtable),
ArgVals(&llargs[fcx.arg_pos(1)..]), ArgVals(&llargs[(self_idx + 2)..]),
dest).bcx; dest).bcx;
finish_fn(&fcx, bcx, sig.output, DebugLoc::None); finish_fn(&fcx, bcx, sig.output, DebugLoc::None);

View file

@ -171,10 +171,6 @@ impl Type {
Type::struct_(ccx, &[], false) Type::struct_(ccx, &[], false)
} }
pub fn vtable(ccx: &CrateContext) -> Type {
Type::array(&Type::i8p(ccx).ptr_to(), 1)
}
pub fn glue_fn(ccx: &CrateContext, t: Type) -> Type { pub fn glue_fn(ccx: &CrateContext, t: Type) -> Type {
Type::func(&[t], &Type::void(ccx)) Type::func(&[t], &Type::void(ccx))
} }

View file

@ -36,12 +36,12 @@ fn ensure_array_fits_in_address_space<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
pub fn arg_is_indirect<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn arg_is_indirect<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
arg_ty: Ty<'tcx>) -> bool { arg_ty: Ty<'tcx>) -> bool {
!type_is_immediate(ccx, arg_ty) !type_is_immediate(ccx, arg_ty) && !type_is_fat_ptr(ccx.tcx(), arg_ty)
} }
pub fn return_uses_outptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn return_uses_outptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
ty: Ty<'tcx>) -> bool { ty: Ty<'tcx>) -> bool {
!type_is_immediate(ccx, ty) arg_is_indirect(ccx, ty)
} }
pub fn type_of_explicit_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn type_of_explicit_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
@ -136,8 +136,15 @@ pub fn type_of_rust_fn<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
} }
// ... then explicit args. // ... then explicit args.
let input_tys = inputs.iter().map(|&arg_ty| type_of_explicit_arg(cx, arg_ty)); for input in &inputs {
atys.extend(input_tys); let arg_ty = type_of_explicit_arg(cx, input);
if type_is_fat_ptr(cx.tcx(), input) {
atys.extend(arg_ty.field_types());
} else {
atys.push(arg_ty);
}
}
Type::func(&atys[..], &lloutputtype) Type::func(&atys[..], &lloutputtype)
} }

View file

@ -87,6 +87,53 @@ pub fn struct_return() -> S {
} }
} }
// Hack to get the correct size for the length part in slices
// CHECK: @helper([[USIZE:i[0-9]+]])
#[no_mangle]
fn helper(_: usize) {
}
// CHECK: @slice(i8* noalias nonnull readonly, [[USIZE]])
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
fn slice(_: &[u8]) {
}
// CHECK: @mutable_slice(i8* noalias nonnull, [[USIZE]])
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
fn mutable_slice(_: &mut [u8]) {
}
// CHECK: @unsafe_slice(%UnsafeInner* nonnull, [[USIZE]])
// unsafe interior means this isn't actually readonly and there may be aliases ...
#[no_mangle]
pub fn unsafe_slice(_: &[UnsafeInner]) {
}
// CHECK: @str(i8* noalias nonnull readonly, [[USIZE]])
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
fn str(_: &[u8]) {
}
// CHECK: @trait_borrow(i8* nonnull, void (i8*)** nonnull)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
fn trait_borrow(_: &Drop) {
}
// CHECK: @trait_box(i8* noalias nonnull, void (i8*)** nonnull)
#[no_mangle]
fn trait_box(_: Box<Drop>) {
}
// CHECK: { i16*, [[USIZE]] } @return_slice(i16* noalias nonnull readonly, [[USIZE]])
#[no_mangle]
fn return_slice(x: &[u16]) -> &[u16] {
x
}
// CHECK: noalias i8* @allocator() // CHECK: noalias i8* @allocator()
#[no_mangle] #[no_mangle]
#[allocator] #[allocator]

View file

@ -97,10 +97,6 @@ fn inner_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
let inner_pos = pos!(); aux::callback_inlined(|aux_pos| { let inner_pos = pos!(); aux::callback_inlined(|aux_pos| {
check!(counter; main_pos, outer_pos, inner_pos, aux_pos); check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
}); });
// this tests a distinction between two independent calls to the inlined function.
// (un)fortunately, LLVM somehow merges two consecutive such calls into one node.
inner_further_inlined(counter, main_pos, outer_pos, pos!());
} }
#[inline(never)] #[inline(never)]