auto merge of #14306 : luqmana/rust/up-llvm, r=alexcrichton

We can now mark arguments and returns as `nonnull` in LLVM. It's still somewhat limited by the fact that LLVM loses this information after inlining but it might help in certain cases.
This commit is contained in:
bors 2014-05-23 19:51:20 -07:00
commit 9c870b3d1c
14 changed files with 279 additions and 281 deletions

View File

@ -15,7 +15,7 @@
use std::c_str::ToCStr;
use std::cell::RefCell;
use collections::HashMap;
use libc::{c_uint, c_ushort, c_void, free};
use libc::{c_uint, c_ushort, c_void, free, uint64_t};
use std::str::raw::from_c_str;
use middle::trans::type_::Type;
@ -92,6 +92,33 @@ pub enum Attribute {
NonLazyBindAttribute = 1 << 31,
}
#[repr(u64)]
pub enum OtherAttribute {
// The following are not really exposed in
// the LLVM c api so instead to add these
// we call a wrapper function in RustWrapper
// that uses the C++ api.
SanitizeAddressAttribute = 1 << 32,
MinSizeAttribute = 1 << 33,
NoDuplicateAttribute = 1 << 34,
StackProtectStrongAttribute = 1 << 35,
SanitizeThreadAttribute = 1 << 36,
SanitizeMemoryAttribute = 1 << 37,
NoBuiltinAttribute = 1 << 38,
ReturnedAttribute = 1 << 39,
ColdAttribute = 1 << 40,
BuiltinAttribute = 1 << 41,
OptimizeNoneAttribute = 1 << 42,
InAllocaAttribute = 1 << 43,
NonNullAttribute = 1 << 44,
}
#[repr(C)]
pub enum AttributeSet {
ReturnIndex = 0,
FunctionIndex = !0
}
// enum for the LLVM IntPredicate type
pub enum IntPredicate {
IntEQ = 32,
@ -308,7 +335,7 @@ pub mod llvm {
use super::{CodeGenModel, RelocMode, CodeGenOptLevel};
use super::debuginfo::*;
use libc::{c_char, c_int, c_longlong, c_ushort, c_uint, c_ulonglong,
size_t};
size_t, uint64_t};
// Link to our native llvm bindings (things that we need to use the C++ api
// for) and because llvm is written in C++ we need to link against libstdc++
@ -706,20 +733,11 @@ pub mod llvm {
pub fn LLVMSetFunctionCallConv(Fn: ValueRef, CC: c_uint);
pub fn LLVMGetGC(Fn: ValueRef) -> *c_char;
pub fn LLVMSetGC(Fn: ValueRef, Name: *c_char);
pub fn LLVMAddFunctionAttr(Fn: ValueRef, PA: c_uint);
pub fn LLVMAddFunctionAttrString(Fn: ValueRef, Name: *c_char);
pub fn LLVMRemoveFunctionAttrString(Fn: ValueRef, Name: *c_char);
pub fn LLVMAddFunctionAttribute(Fn: ValueRef, index: c_uint, PA: uint64_t);
pub fn LLVMAddFunctionAttrString(Fn: ValueRef, index: c_uint, Name: *c_char);
pub fn LLVMRemoveFunctionAttrString(Fn: ValueRef, index: c_uint, Name: *c_char);
pub fn LLVMGetFunctionAttr(Fn: ValueRef) -> c_ulonglong;
pub fn LLVMAddReturnAttribute(Fn: ValueRef, PA: c_uint);
pub fn LLVMRemoveReturnAttribute(Fn: ValueRef, PA: c_uint);
pub fn LLVMAddColdAttribute(Fn: ValueRef);
pub fn LLVMRemoveFunctionAttr(Fn: ValueRef,
PA: c_ulonglong,
HighPA: c_ulonglong);
/* Operations on parameters */
pub fn LLVMCountParams(Fn: ValueRef) -> c_uint;
pub fn LLVMGetParams(Fn: ValueRef, Params: *ValueRef);
@ -783,6 +801,9 @@ pub mod llvm {
pub fn LLVMSetInstrParamAlignment(Instr: ValueRef,
index: c_uint,
align: c_uint);
pub fn LLVMAddCallSiteAttribute(Instr: ValueRef,
index: c_uint,
Val: uint64_t);
/* Operations on call instructions (only) */
pub fn LLVMIsTailCall(CallInst: ValueRef) -> Bool;
@ -1835,7 +1856,7 @@ pub fn ConstFCmp(pred: RealPredicate, v1: ValueRef, v2: ValueRef) -> ValueRef {
pub fn SetFunctionAttribute(fn_: ValueRef, attr: Attribute) {
unsafe {
llvm::LLVMAddFunctionAttr(fn_, attr as c_uint)
llvm::LLVMAddFunctionAttribute(fn_, FunctionIndex as c_uint, attr as uint64_t)
}
}
/* Memory-managed object interface to type handles. */

View File

@ -73,7 +73,7 @@ use util::sha2::Sha256;
use util::nodemap::NodeMap;
use arena::TypedArena;
use libc::c_uint;
use libc::{c_uint, uint64_t};
use std::c_str::ToCStr;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
@ -167,6 +167,7 @@ impl<'a> Drop for StatRecorder<'a> {
// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions
fn decl_fn(llmod: ModuleRef, name: &str, cc: lib::llvm::CallConv,
ty: Type, output: ty::t) -> ValueRef {
let llfn: ValueRef = name.with_c_str(|buf| {
unsafe {
llvm::LLVMGetOrInsertFunction(llmod, buf, ty.to_ref())
@ -177,14 +178,9 @@ fn decl_fn(llmod: ModuleRef, name: &str, cc: lib::llvm::CallConv,
// functions returning bottom may unwind, but can never return normally
ty::ty_bot => {
unsafe {
llvm::LLVMAddFunctionAttr(llfn, lib::llvm::NoReturnAttribute as c_uint)
}
}
// `~` pointer return values never alias because ownership is transferred
ty::ty_uniq(..) // | ty::ty_trait(_, _, ty::UniqTraitStore, _, _)
=> {
unsafe {
llvm::LLVMAddReturnAttribute(llfn, lib::llvm::NoAliasAttribute as c_uint);
llvm::LLVMAddFunctionAttribute(llfn,
lib::llvm::FunctionIndex as c_uint,
lib::llvm::NoReturnAttribute as uint64_t)
}
}
_ => {}
@ -207,8 +203,8 @@ pub fn decl_cdecl_fn(llmod: ModuleRef,
}
// only use this for foreign function ABIs and glue, use `get_extern_rust_fn` for Rust functions
pub fn get_extern_fn(externs: &mut ExternMap,
llmod: ModuleRef,
pub fn get_extern_fn(ccx: &CrateContext,
externs: &mut ExternMap,
name: &str,
cc: lib::llvm::CallConv,
ty: Type,
@ -218,19 +214,19 @@ pub fn get_extern_fn(externs: &mut ExternMap,
Some(n) => return *n,
None => {}
}
let f = decl_fn(llmod, name, cc, ty, output);
let f = decl_fn(ccx.llmod, name, cc, ty, output);
externs.insert(name.to_strbuf(), f);
f
}
fn get_extern_rust_fn(ccx: &CrateContext, inputs: &[ty::t], output: ty::t,
name: &str, did: ast::DefId) -> ValueRef {
fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::DefId) -> ValueRef {
match ccx.externs.borrow().find_equiv(&name) {
Some(n) => return *n,
None => ()
}
let f = decl_rust_fn(ccx, false, inputs, output, name);
let f = decl_rust_fn(ccx, fn_ty, name);
csearch::get_item_attrs(&ccx.sess().cstore, did, |meta_items| {
set_llvm_fn_attrs(meta_items.iter().map(|&x| attr::mk_attr_outer(x))
.collect::<Vec<_>>().as_slice(), f)
@ -240,72 +236,27 @@ fn get_extern_rust_fn(ccx: &CrateContext, inputs: &[ty::t], output: ty::t,
f
}
pub fn decl_rust_fn(ccx: &CrateContext, has_env: bool,
inputs: &[ty::t], output: ty::t,
name: &str) -> ValueRef {
use middle::ty::{BrAnon, ReLateBound};
pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef {
let (inputs, output, has_env) = match ty::get(fn_ty).sty {
ty::ty_bare_fn(ref f) => (f.sig.inputs.clone(), f.sig.output, false),
ty::ty_closure(ref f) => (f.sig.inputs.clone(), f.sig.output, true),
_ => fail!("expected closure or fn")
};
let llfty = type_of_rust_fn(ccx, has_env, inputs, output);
let llfn = decl_cdecl_fn(ccx.llmod, name, llfty, output);
let uses_outptr = type_of::return_uses_outptr(ccx, output);
let offset = if uses_outptr { 1 } else { 0 };
let offset = if has_env { offset + 1 } else { offset };
for (i, &arg_ty) in inputs.iter().enumerate() {
let llarg = unsafe { llvm::LLVMGetParam(llfn, (offset + i) as c_uint) };
match ty::get(arg_ty).sty {
// `~` pointer parameters never alias because ownership is transferred
ty::ty_uniq(..) => {
unsafe {
llvm::LLVMAddAttribute(llarg, lib::llvm::NoAliasAttribute as c_uint);
}
}
// `&mut` pointer parameters never alias other parameters, or mutable global data
ty::ty_rptr(_, mt) if mt.mutbl == ast::MutMutable => {
unsafe {
llvm::LLVMAddAttribute(llarg, lib::llvm::NoAliasAttribute as c_uint);
}
}
// When a reference in an argument has no named lifetime, it's impossible for that
// reference to escape this function (returned or stored beyond the call by a closure).
ty::ty_rptr(ReLateBound(_, BrAnon(_)), _) => {
debug!("marking argument of {} as nocapture because of anonymous lifetime", name);
unsafe {
llvm::LLVMAddAttribute(llarg, lib::llvm::NoCaptureAttribute as c_uint);
}
}
_ => {
// For non-immediate arguments the callee gets its own copy of
// the value on the stack, so there are no aliases
if !type_is_immediate(ccx, arg_ty) {
unsafe {
llvm::LLVMAddAttribute(llarg, lib::llvm::NoAliasAttribute as c_uint);
llvm::LLVMAddAttribute(llarg, lib::llvm::NoCaptureAttribute as c_uint);
}
}
}
}
}
// The out pointer will never alias with any other pointers, as the object only exists at a
// language level after the call. It can also be tagged with SRet to indicate that it is
// guaranteed to point to a usable block of memory for the type.
if uses_outptr {
let llfty = type_of_rust_fn(ccx, has_env, inputs.as_slice(), output);
let llfn = decl_fn(ccx.llmod, name, lib::llvm::CCallConv, llfty, output);
let attrs = get_fn_llvm_attributes(ccx, fn_ty);
for &(idx, attr) in attrs.iter() {
unsafe {
let outptr = llvm::LLVMGetParam(llfn, 0);
llvm::LLVMAddAttribute(outptr, lib::llvm::StructRetAttribute as c_uint);
llvm::LLVMAddAttribute(outptr, lib::llvm::NoAliasAttribute as c_uint);
llvm::LLVMAddFunctionAttribute(llfn, idx as c_uint, attr);
}
}
llfn
}
pub fn decl_internal_rust_fn(ccx: &CrateContext, has_env: bool,
inputs: &[ty::t], output: ty::t,
name: &str) -> ValueRef {
let llfn = decl_rust_fn(ccx, has_env, inputs, output, name);
pub fn decl_internal_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef {
let llfn = decl_rust_fn(ccx, fn_ty, name);
lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage);
llfn
}
@ -453,7 +404,11 @@ pub fn set_llvm_fn_attrs(attrs: &[ast::Attribute], llfn: ValueRef) {
}
if contains_name(attrs, "cold") {
unsafe { llvm::LLVMAddColdAttribute(llfn) }
unsafe {
llvm::LLVMAddFunctionAttribute(llfn,
lib::llvm::FunctionIndex as c_uint,
lib::llvm::ColdAttribute as uint64_t)
}
}
}
@ -463,13 +418,13 @@ pub fn set_always_inline(f: ValueRef) {
pub fn set_split_stack(f: ValueRef) {
"split-stack".with_c_str(|buf| {
unsafe { llvm::LLVMAddFunctionAttrString(f, buf); }
unsafe { llvm::LLVMAddFunctionAttrString(f, lib::llvm::FunctionIndex as c_uint, buf); }
})
}
pub fn unset_split_stack(f: ValueRef) {
"split-stack".with_c_str(|buf| {
unsafe { llvm::LLVMRemoveFunctionAttrString(f, buf); }
unsafe { llvm::LLVMRemoveFunctionAttrString(f, lib::llvm::FunctionIndex as c_uint, buf); }
})
}
@ -485,6 +440,7 @@ pub fn note_unique_llvm_symbol(ccx: &CrateContext, sym: StrBuf) {
pub fn get_res_dtor(ccx: &CrateContext,
did: ast::DefId,
t: ty::t,
parent_id: ast::DefId,
substs: &ty::substs)
-> ValueRef {
@ -510,13 +466,14 @@ pub fn get_res_dtor(ccx: &CrateContext,
let class_ty = ty::subst(tcx, substs,
ty::lookup_item_type(tcx, parent_id).ty);
let llty = type_of_dtor(ccx, class_ty);
get_extern_fn(&mut *ccx.externs.borrow_mut(),
ccx.llmod,
let dtor_ty = ty::mk_ctor_fn(ccx.tcx(), ast::DUMMY_NODE_ID,
[glue::get_drop_glue_type(ccx, t)], ty::mk_nil());
get_extern_fn(ccx,
&mut *ccx.externs.borrow_mut(),
name.as_slice(),
lib::llvm::CCallConv,
llty,
ty::mk_nil())
dtor_ty)
}
}
@ -858,11 +815,7 @@ pub fn trans_external_path(ccx: &CrateContext, did: ast::DefId, t: ty::t) -> Val
match fn_ty.abi.for_target(ccx.sess().targ_cfg.os,
ccx.sess().targ_cfg.arch) {
Some(Rust) | Some(RustIntrinsic) => {
get_extern_rust_fn(ccx,
fn_ty.sig.inputs.as_slice(),
fn_ty.sig.output,
name.as_slice(),
did)
get_extern_rust_fn(ccx, t, name.as_slice(), did)
}
Some(..) | None => {
foreign::register_foreign_item_fn(ccx, fn_ty.abi, t,
@ -870,12 +823,8 @@ pub fn trans_external_path(ccx: &CrateContext, did: ast::DefId, t: ty::t) -> Val
}
}
}
ty::ty_closure(ref f) => {
get_extern_rust_fn(ccx,
f.sig.inputs.as_slice(),
f.sig.output,
name.as_slice(),
did)
ty::ty_closure(_) => {
get_extern_rust_fn(ccx, t, name.as_slice(), did)
}
_ => {
let llty = type_of(ccx, t);
@ -891,7 +840,7 @@ pub fn invoke<'a>(
bcx: &'a Block<'a>,
llfn: ValueRef,
llargs: Vec<ValueRef> ,
attributes: &[(uint, lib::llvm::Attribute)],
fn_ty: ty::t,
call_info: Option<NodeInfo>)
-> (ValueRef, &'a Block<'a>) {
let _icx = push_ctxt("invoke_");
@ -899,6 +848,8 @@ pub fn invoke<'a>(
return (C_null(Type::i8(bcx.ccx())), bcx);
}
let attributes = get_fn_llvm_attributes(bcx.ccx(), fn_ty);
match bcx.opt_node_id {
None => {
debug!("invoke at ???");
@ -926,7 +877,7 @@ pub fn invoke<'a>(
llargs.as_slice(),
normal_bcx.llbb,
landing_pad,
attributes);
attributes.as_slice());
return (llresult, normal_bcx);
} else {
debug!("calling {} at {}", llfn, bcx.llbb);
@ -939,7 +890,7 @@ pub fn invoke<'a>(
None => debuginfo::clear_source_location(bcx.fcx)
};
let llresult = Call(bcx, llfn, llargs.as_slice(), attributes);
let llresult = Call(bcx, llfn, llargs.as_slice(), attributes.as_slice());
return (llresult, bcx);
}
}
@ -1708,34 +1659,132 @@ fn register_fn(ccx: &CrateContext,
node_id: ast::NodeId,
node_type: ty::t)
-> ValueRef {
let f = match ty::get(node_type).sty {
match ty::get(node_type).sty {
ty::ty_bare_fn(ref f) => {
assert!(f.abi == Rust || f.abi == RustIntrinsic);
f
}
_ => fail!("expected bare rust fn or an intrinsic")
};
let llfn = decl_rust_fn(ccx,
false,
f.sig.inputs.as_slice(),
f.sig.output,
sym.as_slice());
let llfn = decl_rust_fn(ccx, node_type, sym.as_slice());
finish_register_fn(ccx, sp, sym, node_id, llfn);
llfn
}
pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t) -> Vec<(uint, u64)> {
use middle::ty::{BrAnon, ReLateBound};
let (fn_sig, has_env) = match ty::get(fn_ty).sty {
ty::ty_closure(ref f) => (f.sig.clone(), true),
ty::ty_bare_fn(ref f) => (f.sig.clone(), false),
_ => fail!("expected closure or function.")
};
// Since index 0 is the return value of the llvm func, we start
// at either 1 or 2 depending on whether there's an env slot or not
let mut first_arg_offset = if has_env { 2 } else { 1 };
let mut attrs = Vec::new();
let ret_ty = fn_sig.output;
// A function pointer is called without the declaration
// available, so we have to apply any attributes with ABI
// implications directly to the call instruction. Right now,
// the only attribute we need to worry about is `sret`.
if type_of::return_uses_outptr(ccx, ret_ty) {
attrs.push((1, lib::llvm::StructRetAttribute as u64));
// The outptr can be noalias and nocapture because it's entirely
// invisible to the program. We can also mark it as nonnull
attrs.push((1, lib::llvm::NoAliasAttribute as u64));
attrs.push((1, lib::llvm::NoCaptureAttribute as u64));
attrs.push((1, lib::llvm::NonNullAttribute as u64));
// Add one more since there's an outptr
first_arg_offset += 1;
} else {
// The `noalias` attribute on the return value is useful to a
// function ptr caller.
match ty::get(ret_ty).sty {
// `~` pointer return values never alias because ownership
// is transferred
ty::ty_uniq(_) => {
attrs.push((lib::llvm::ReturnIndex as uint, lib::llvm::NoAliasAttribute as u64));
}
_ => {}
}
// We can also mark the return value as `nonnull` in certain cases
match ty::get(ret_ty).sty {
// These are not really pointers but pairs, (pointer, len)
ty::ty_rptr(_, ty::mt { ty: it, .. }) |
ty::ty_rptr(_, ty::mt { ty: it, .. }) if match ty::get(it).sty {
ty::ty_str | ty::ty_vec(..) => true, _ => false
} => {}
ty::ty_uniq(_) | ty::ty_rptr(_, _) => {
attrs.push((lib::llvm::ReturnIndex as uint, lib::llvm::NonNullAttribute as u64));
}
_ => {}
}
}
for (idx, &t) in fn_sig.inputs.iter().enumerate().map(|(i, v)| (i + first_arg_offset, v)) {
match ty::get(t).sty {
// `~` pointer parameters never alias because ownership is transferred
ty::ty_uniq(_) => {
attrs.push((idx, lib::llvm::NoAliasAttribute as u64));
attrs.push((idx, lib::llvm::NonNullAttribute as u64));
}
// These are not really pointers but pairs, (pointer, len)
ty::ty_rptr(_, ty::mt { ty: it, .. }) |
ty::ty_rptr(_, ty::mt { ty: it, .. }) if match ty::get(it).sty {
ty::ty_str | ty::ty_vec(..) => true, _ => false
} => {}
// `&mut` pointer parameters never alias other parameters, or mutable global data
ty::ty_rptr(b, mt) if mt.mutbl == ast::MutMutable => {
attrs.push((idx, lib::llvm::NoAliasAttribute as u64));
attrs.push((idx, lib::llvm::NonNullAttribute as u64));
match b {
ReLateBound(_, BrAnon(_)) => {
attrs.push((idx, lib::llvm::NoCaptureAttribute as u64));
}
_ => {}
}
}
// When a reference in an argument has no named lifetime, it's impossible for that
// reference to escape this function (returned or stored beyond the call by a closure).
ty::ty_rptr(ReLateBound(_, BrAnon(_)), _) => {
attrs.push((idx, lib::llvm::NoCaptureAttribute as u64));
attrs.push((idx, lib::llvm::NonNullAttribute as u64));
}
// & pointer parameters are never null
ty::ty_rptr(_, _) => {
attrs.push((idx, lib::llvm::NonNullAttribute as u64));
}
_ => {
// 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
if !type_is_immediate(ccx, t) {
attrs.push((idx, lib::llvm::NoAliasAttribute as u64));
attrs.push((idx, lib::llvm::NoCaptureAttribute as u64));
}
}
}
}
attrs
}
// only use this for foreign function ABIs and glue, use `register_fn` for Rust functions
pub fn register_fn_llvmty(ccx: &CrateContext,
sp: Span,
sym: StrBuf,
node_id: ast::NodeId,
cc: lib::llvm::CallConv,
fn_ty: Type,
output: ty::t) -> ValueRef {
llfty: Type) -> ValueRef {
debug!("register_fn_llvmty id={} sym={}", node_id, sym);
let llfn = decl_fn(ccx.llmod, sym.as_slice(), cc, fn_ty, output);
let llfn = decl_fn(ccx.llmod, sym.as_slice(), cc, llfty, ty::mk_nil());
finish_register_fn(ccx, sp, sym, node_id, llfn);
llfn
}

View File

@ -113,7 +113,7 @@ pub fn Invoke(cx: &Block,
args: &[ValueRef],
then: BasicBlockRef,
catch: BasicBlockRef,
attributes: &[(uint, lib::llvm::Attribute)])
attributes: &[(uint, u64)])
-> ValueRef {
if cx.unreachable.get() {
return C_null(Type::i8(cx.ccx()));
@ -679,13 +679,13 @@ pub fn InlineAsmCall(cx: &Block, asm: *c_char, cons: *c_char,
}
pub fn Call(cx: &Block, fn_: ValueRef, args: &[ValueRef],
attributes: &[(uint, lib::llvm::Attribute)]) -> ValueRef {
attributes: &[(uint, u64)]) -> ValueRef {
if cx.unreachable.get() { return _UndefReturn(cx, fn_); }
B(cx).call(fn_, args, attributes)
}
pub fn CallWithConv(cx: &Block, fn_: ValueRef, args: &[ValueRef], conv: CallConv,
attributes: &[(uint, lib::llvm::Attribute)]) -> ValueRef {
attributes: &[(uint, u64)]) -> ValueRef {
if cx.unreachable.get() { return _UndefReturn(cx, fn_); }
B(cx).call_with_conv(fn_, args, conv, attributes)
}

View File

@ -156,7 +156,7 @@ impl<'a> Builder<'a> {
args: &[ValueRef],
then: BasicBlockRef,
catch: BasicBlockRef,
attributes: &[(uint, lib::llvm::Attribute)])
attributes: &[(uint, u64)])
-> ValueRef {
self.count_insn("invoke");
unsafe {
@ -168,7 +168,7 @@ impl<'a> Builder<'a> {
catch,
noname());
for &(idx, attr) in attributes.iter() {
llvm::LLVMAddInstrAttribute(v, idx as c_uint, attr as c_uint);
llvm::LLVMAddCallSiteAttribute(v, idx as c_uint, attr);
}
v
}
@ -799,7 +799,7 @@ impl<'a> Builder<'a> {
}
pub fn call(&self, llfn: ValueRef, args: &[ValueRef],
attributes: &[(uint, lib::llvm::Attribute)]) -> ValueRef {
attributes: &[(uint, u64)]) -> ValueRef {
self.count_insn("call");
debug!("Call {} with args ({})",
@ -813,14 +813,14 @@ impl<'a> Builder<'a> {
let v = llvm::LLVMBuildCall(self.llbuilder, llfn, args.as_ptr(),
args.len() as c_uint, noname());
for &(idx, attr) in attributes.iter() {
llvm::LLVMAddInstrAttribute(v, idx as c_uint, attr as c_uint);
llvm::LLVMAddCallSiteAttribute(v, idx as c_uint, attr);
}
v
}
}
pub fn call_with_conv(&self, llfn: ValueRef, args: &[ValueRef],
conv: CallConv, attributes: &[(uint, lib::llvm::Attribute)]) -> ValueRef {
conv: CallConv, attributes: &[(uint, u64)]) -> ValueRef {
self.count_insn("callwithconv");
let v = self.call(llfn, args, attributes);
lib::llvm::SetInstructionCallConv(v, conv);

View File

@ -18,7 +18,7 @@
use back::abi;
use driver::session;
use lib::llvm::{ValueRef, NoAliasAttribute, StructRetAttribute, NoCaptureAttribute};
use lib::llvm::ValueRef;
use lib::llvm::llvm;
use metadata::csearch;
use middle::trans::base;
@ -614,13 +614,9 @@ pub fn trans_call_inner<'a>(
llargs.push(opt_llretslot.unwrap());
}
// start at 1, because index 0 is the return value of the llvm func
let mut first_arg_offset = 1;
// Push the environment (or a trait object's self).
match (llenv, llself) {
(Some(llenv), None) => {
first_arg_offset += 1;
llargs.push(llenv)
},
(None, Some(llself)) => llargs.push(llself),
@ -634,61 +630,11 @@ pub fn trans_call_inner<'a>(
fcx.pop_custom_cleanup_scope(arg_cleanup_scope);
// A function pointer is called without the declaration
// available, so we have to apply any attributes with ABI
// implications directly to the call instruction. Right now,
// the only attribute we need to worry about is `sret`.
let mut attrs = Vec::new();
if type_of::return_uses_outptr(ccx, ret_ty) {
attrs.push((1, StructRetAttribute));
// The outptr can be noalias and nocapture because it's entirely
// invisible to the program.
attrs.push((1, NoAliasAttribute));
attrs.push((1, NoCaptureAttribute));
first_arg_offset += 1;
}
// The `noalias` attribute on the return value is useful to a
// function ptr caller.
match ty::get(ret_ty).sty {
// `~` pointer return values never alias because ownership
// is transferred
ty::ty_uniq(ty) => match ty::get(ty).sty {
ty::ty_str => {}
_ => attrs.push((0, NoAliasAttribute)),
},
_ => {}
}
debug!("trans_callee_inner: first_arg_offset={}", first_arg_offset);
for (idx, &t) in ty::ty_fn_args(callee_ty).iter().enumerate()
.map(|(i, v)| (i+first_arg_offset, v)) {
use middle::ty::{BrAnon, ReLateBound};
if !type_is_immediate(ccx, t) {
// if it's not immediate, we have a program-invisible pointer,
// which it can't possibly capture
attrs.push((idx, NoCaptureAttribute));
debug!("trans_callee_inner: argument {} nocapture because it's non-immediate", idx);
continue;
}
let t_ = ty::get(t);
match t_.sty {
ty::ty_rptr(ReLateBound(_, BrAnon(_)), _) => {
debug!("trans_callee_inner: argument {} nocapture because \
of anonymous lifetime", idx);
attrs.push((idx, NoCaptureAttribute));
},
_ => { }
}
}
// Invoke the actual rust fn and update bcx/llresult.
let (llret, b) = base::invoke(bcx,
llfn,
llargs,
attrs.as_slice(),
callee_ty,
call_info);
bcx = b;
llresult = llret;

View File

@ -340,21 +340,12 @@ pub fn trans_expr_fn<'a>(
};
let ccx = bcx.ccx();
let fty = node_id_type(bcx, id);
let f = match ty::get(fty).sty {
ty::ty_closure(ref f) => f,
_ => fail!("expected closure")
};
let tcx = bcx.tcx();
let fty = node_id_type(bcx, id);
let s = tcx.map.with_path(id, |path| {
mangle_internal_name_by_path_and_seq(path, "closure")
});
let llfn = decl_internal_rust_fn(ccx,
true,
f.sig.inputs.as_slice(),
f.sig.output,
s.as_slice());
let llfn = decl_internal_rust_fn(ccx, fty, s.as_slice());
// set an inline hint for all closures
set_inline_hint(llfn);
@ -414,17 +405,9 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,
mangle_internal_name_by_path_and_seq(path, "as_closure")
});
let llfn = if is_local {
decl_internal_rust_fn(ccx,
true,
f.sig.inputs.as_slice(),
f.sig.output,
name.as_slice())
decl_internal_rust_fn(ccx, closure_ty, name.as_slice())
} else {
decl_rust_fn(ccx,
true,
f.sig.inputs.as_slice(),
f.sig.output,
name.as_slice())
decl_rust_fn(ccx, closure_ty, name.as_slice())
};
ccx.closure_bare_wrapper_cache.borrow_mut().insert(fn_ptr, llfn);

View File

@ -227,12 +227,12 @@ pub fn register_foreign_item_fn(ccx: &CrateContext, abi: Abi, fty: ty::t,
// Create the LLVM value for the C extern fn
let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys);
let llfn = base::get_extern_fn(&mut *ccx.externs.borrow_mut(),
ccx.llmod,
let llfn = base::get_extern_fn(ccx,
&mut *ccx.externs.borrow_mut(),
name,
cc,
llfn_ty,
tys.fn_sig.output);
fty);
add_argument_attributes(&tys, llfn);
llfn
@ -378,17 +378,21 @@ pub fn trans_native_call<'a>(
// A function pointer is called without the declaration available, so we have to apply
// any attributes with ABI implications directly to the call instruction. Right now, the
// only attribute we need to worry about is `sret`.
let sret_attr = if fn_type.ret_ty.is_indirect() {
Some((1, StructRetAttribute))
} else {
None
let mut attrs = Vec::new();
if fn_type.ret_ty.is_indirect() {
attrs.push((1, lib::llvm::StructRetAttribute as u64));
// The outptr can be noalias and nocapture because it's entirely
// invisible to the program. We can also mark it as nonnull
attrs.push((1, lib::llvm::NoAliasAttribute as u64));
attrs.push((1, lib::llvm::NoCaptureAttribute as u64));
attrs.push((1, lib::llvm::NonNullAttribute as u64));
};
let attrs = sret_attr.as_slice();
let llforeign_retval = CallWithConv(bcx,
llfn,
llargs_foreign.as_slice(),
cc,
attrs);
attrs.as_slice());
// If the function we just called does not use an outpointer,
// store the result into the rust outpointer. Cast the outpointer
@ -500,14 +504,14 @@ pub fn register_rust_fn_with_foreign_abi(ccx: &CrateContext,
let tys = foreign_types_for_id(ccx, node_id);
let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys);
let t = ty::node_id_to_type(ccx.tcx(), node_id);
let (cconv, output) = match ty::get(t).sty {
let cconv = match ty::get(t).sty {
ty::ty_bare_fn(ref fn_ty) => {
let c = llvm_calling_convention(ccx, fn_ty.abi);
(c.unwrap_or(lib::llvm::CCallConv), fn_ty.sig.output)
c.unwrap_or(lib::llvm::CCallConv)
}
_ => fail!("expected bare fn in register_rust_fn_with_foreign_abi")
};
let llfn = base::register_fn_llvmty(ccx, sp, sym, node_id, cconv, llfn_ty, output);
let llfn = base::register_fn_llvmty(ccx, sp, sym, node_id, cconv, llfn_ty);
add_argument_attributes(&tys, llfn);
debug!("register_rust_fn_with_foreign_abi(node_id={:?}, llfn_ty={}, llfn={})",
node_id, ccx.tn.type_to_str(llfn_ty), ccx.tn.val_to_str(llfn));
@ -528,7 +532,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
let llrustfn = build_rust_fn(ccx, decl, body, attrs, id);
// Build up the foreign wrapper (`foo` above).
return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys);
return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys, id);
}
fn build_rust_fn(ccx: &CrateContext,
@ -548,10 +552,9 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
// Compute the type that the function would have if it were just a
// normal Rust function. This will be the type of the wrappee fn.
let f = match ty::get(t).sty {
match ty::get(t).sty {
ty::ty_bare_fn(ref f) => {
assert!(f.abi != Rust && f.abi != RustIntrinsic);
f
}
_ => {
ccx.sess().bug(format!("build_rust_fn: extern fn {} has ty {}, \
@ -565,11 +568,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
ccx.tcx.map.path_to_str(id),
id, t.repr(tcx));
let llfn = base::decl_internal_rust_fn(ccx,
false,
f.sig.inputs.as_slice(),
f.sig.output,
ps.as_slice());
let llfn = base::decl_internal_rust_fn(ccx, t, ps.as_slice());
base::set_llvm_fn_attrs(attrs, llfn);
base::trans_fn(ccx, decl, body, llfn, None, id, []);
llfn
@ -578,14 +577,18 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
unsafe fn build_wrap_fn(ccx: &CrateContext,
llrustfn: ValueRef,
llwrapfn: ValueRef,
tys: &ForeignTypes) {
tys: &ForeignTypes,
id: ast::NodeId) {
let _icx = push_ctxt(
"foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn");
let tcx = ccx.tcx();
debug!("build_wrap_fn(llrustfn={}, llwrapfn={})",
let t = ty::node_id_to_type(tcx, id);
debug!("build_wrap_fn(llrustfn={}, llwrapfn={}, t={})",
ccx.tn.val_to_str(llrustfn),
ccx.tn.val_to_str(llwrapfn));
ccx.tn.val_to_str(llwrapfn),
t.repr(ccx.tcx()));
// Avoid all the Rust generation stuff and just generate raw
// LLVM here.
@ -731,10 +734,15 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
}
// Perform the call itself
debug!("calling llrustfn = {}", ccx.tn.val_to_str(llrustfn));
debug!("calling llrustfn = {}, t = {}", ccx.tn.val_to_str(llrustfn), t.repr(ccx.tcx()));
let llrust_ret_val = llvm::LLVMBuildCall(builder, llrustfn, llrust_args.as_ptr(),
llrust_args.len() as c_uint, noname());
let attributes = base::get_fn_llvm_attributes(ccx, t);
for &(idx, attr) in attributes.iter() {
llvm::LLVMAddCallSiteAttribute(llrust_ret_val, idx as c_uint, attr);
}
// Get the return value where the foreign fn expects it.
let llforeign_ret_ty = match tys.fn_ty.ret_ty.cast {
Some(ty) => ty,

View File

@ -88,7 +88,7 @@ pub fn take_ty<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t)
}
}
fn get_drop_glue_type(ccx: &CrateContext, t: ty::t) -> ty::t {
pub fn get_drop_glue_type(ccx: &CrateContext, t: ty::t) -> ty::t {
let tcx = ccx.tcx();
if !ty::type_needs_drop(tcx, t) {
return ty::mk_i8();
@ -248,7 +248,7 @@ fn trans_struct_drop<'a>(bcx: &'a Block<'a>,
let repr = adt::represent_type(bcx.ccx(), t);
// Find and call the actual destructor
let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did,
let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did, t,
class_did, substs);
// The second argument is the "self" argument for drop
@ -279,7 +279,9 @@ fn trans_struct_drop<'a>(bcx: &'a Block<'a>,
fld.mt.ty);
}
let (_, bcx) = invoke(bcx, dtor_addr, args, [], None);
let dtor_ty = ty::mk_ctor_fn(bcx.tcx(), ast::DUMMY_NODE_ID,
[get_drop_glue_type(bcx.ccx(), t)], ty::mk_nil());
let (_, bcx) = invoke(bcx, dtor_addr, args, dtor_ty, None);
bcx.fcx.pop_and_trans_custom_cleanup_scope(bcx, field_scope)
}
@ -459,10 +461,7 @@ fn declare_generic_glue(ccx: &CrateContext, t: ty::t, llfnty: Type,
t,
format!("glue_{}", name).as_slice());
debug!("{} is for type {}", fn_nm, ppaux::ty_to_str(ccx.tcx(), t));
let llfn = decl_cdecl_fn(ccx.llmod,
fn_nm.as_slice(),
llfnty,
ty::mk_nil());
let llfn = decl_cdecl_fn(ccx.llmod, fn_nm.as_slice(), llfnty, ty::mk_nil());
note_unique_llvm_symbol(ccx, fn_nm);
return llfn;
}

View File

@ -172,14 +172,6 @@ pub fn monomorphic_fn(ccx: &CrateContext,
}
};
let f = match ty::get(mono_ty).sty {
ty::ty_bare_fn(ref f) => {
assert!(f.abi == abi::Rust || f.abi == abi::RustIntrinsic);
f
}
_ => fail!("expected bare rust fn or an intrinsic")
};
ccx.stats.n_monos.set(ccx.stats.n_monos.get() + 1);
let depth;
@ -214,11 +206,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
// This shouldn't need to option dance.
let mut hash_id = Some(hash_id);
let mk_lldecl = || {
let lldecl = decl_internal_rust_fn(ccx,
false,
f.sig.inputs.as_slice(),
f.sig.output,
s.as_slice());
let lldecl = decl_internal_rust_fn(ccx, mono_ty, s.as_slice());
ccx.monomorphized.borrow_mut().insert(hash_id.take_unwrap(), lldecl);
lldecl
};

View File

@ -291,10 +291,10 @@ impl<'a, 'b> Reflector<'a, 'b> {
let sym = mangle_internal_name_by_path_and_seq(
ast_map::Values([].iter()).chain(None), "get_disr");
let fn_ty = ty::mk_ctor_fn(&ccx.tcx, ast::DUMMY_NODE_ID,
[opaqueptrty], ty::mk_u64());
let llfdecl = decl_internal_rust_fn(ccx,
false,
[opaqueptrty],
ty::mk_u64(),
fn_ty,
sym.as_slice());
let arena = TypedArena::new();
let fcx = new_fn_ctxt(ccx, llfdecl, -1, false,

@ -1 +1 @@
Subproject commit 4b4d0533b4f76cc3fbba31bd9e7ac02e0c738b1d
Subproject commit 0a894645cf120539876e9eb4eb0d7b572dfa9d14

View File

@ -13,6 +13,7 @@
#include "rustllvm.h"
#include "llvm/Support/CBindingWrapping.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Target/TargetLibraryInfo.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"

View File

@ -12,6 +12,12 @@
#include "llvm/Object/Archive.h"
#include "llvm/Object/ObjectFile.h"
#if LLVM_VERSION_MINOR >= 5
#include "llvm/IR/CallSite.h"
#else
#include "llvm/Support/CallSite.h"
#endif
//===----------------------------------------------------------------------===
//
// This file defines alternate interfaces to core functions that are more
@ -83,46 +89,43 @@ extern "C" LLVMTypeRef LLVMMetadataTypeInContext(LLVMContextRef C) {
return wrap(Type::getMetadataTy(*unwrap(C)));
}
extern "C" void LLVMAddFunctionAttrString(LLVMValueRef fn, const char *Name) {
unwrap<Function>(fn)->addFnAttr(Name);
extern "C" void LLVMAddCallSiteAttribute(LLVMValueRef Instr, unsigned index, uint64_t Val) {
CallSite Call = CallSite(unwrap<Instruction>(Instr));
AttrBuilder B;
B.addRawValue(Val);
Call.setAttributes(
Call.getAttributes().addAttributes(Call->getContext(), index,
AttributeSet::get(Call->getContext(),
index, B)));
}
extern "C" void LLVMRemoveFunctionAttrString(LLVMValueRef fn, const char *Name) {
extern "C" void LLVMAddFunctionAttribute(LLVMValueRef Fn, unsigned index, uint64_t Val) {
Function *A = unwrap<Function>(Fn);
AttrBuilder B;
B.addRawValue(Val);
A->addAttributes(index, AttributeSet::get(A->getContext(), index, B));
}
extern "C" void LLVMAddFunctionAttrString(LLVMValueRef Fn, unsigned index, const char *Name) {
Function *F = unwrap<Function>(Fn);
AttrBuilder B;
B.addAttribute(Name);
F->addAttributes(index, AttributeSet::get(F->getContext(), index, B));
}
extern "C" void LLVMRemoveFunctionAttrString(LLVMValueRef fn, unsigned index, const char *Name) {
Function *f = unwrap<Function>(fn);
LLVMContext &C = f->getContext();
AttrBuilder B;
B.addAttribute(Name);
AttributeSet to_remove = AttributeSet::get(C, AttributeSet::FunctionIndex, B);
AttributeSet to_remove = AttributeSet::get(C, index, B);
AttributeSet attrs = f->getAttributes();
f->setAttributes(attrs.removeAttributes(f->getContext(),
AttributeSet::FunctionIndex,
index,
to_remove));
}
extern "C" void LLVMAddReturnAttribute(LLVMValueRef Fn, LLVMAttribute PA) {
Function *A = unwrap<Function>(Fn);
AttrBuilder B(PA);
A->addAttributes(AttributeSet::ReturnIndex,
AttributeSet::get(A->getContext(), AttributeSet::ReturnIndex, B));
}
extern "C" void LLVMRemoveReturnAttribute(LLVMValueRef Fn, LLVMAttribute PA) {
Function *A = unwrap<Function>(Fn);
AttrBuilder B(PA);
A->removeAttributes(AttributeSet::ReturnIndex,
AttributeSet::get(A->getContext(), AttributeSet::ReturnIndex, B));
}
#if LLVM_VERSION_MINOR >= 5
extern "C" void LLVMAddColdAttribute(LLVMValueRef Fn) {
Function *A = unwrap<Function>(Fn);
A->addAttribute(AttributeSet::FunctionIndex, Attribute::Cold);
}
#else
extern "C" void LLVMAddColdAttribute(LLVMValueRef Fn) {}
#endif
extern "C" LLVMValueRef LLVMBuildAtomicLoad(LLVMBuilderRef B,
LLVMValueRef source,
const char* Name,

View File

@ -1,4 +1,4 @@
# If this file is modified, then llvm will be forcibly cleaned and then rebuilt.
# The actual contents of this file do not matter, but to trigger a change on the
# build bots then the contents should be changed so git updates the mtime.
2014-04-14
2014-05-20