// except according to those terms.
//! Set and unset common attributes on LLVM values.
+use libc::{c_uint, c_ulonglong};
use llvm::{self, ValueRef, AttrHelper};
+use middle::ty::{self, ClosureTyper};
+use syntax::abi;
use syntax::ast;
-use syntax::attr::InlineAttr;
-pub use syntax::attr::InlineAttr::*;
+pub use syntax::attr::InlineAttr;
+use trans::base;
+use trans::common;
use trans::context::CrateContext;
-
-use libc::{c_uint, c_ulonglong};
+use trans::machine;
+use trans::type_of;
/// Mark LLVM function to use split stack.
#[inline]
/// Mark LLVM function to use provided inline heuristic.
#[inline]
pub fn inline(val: ValueRef, inline: InlineAttr) {
+ use self::InlineAttr::*;
match inline {
- InlineHint => llvm::SetFunctionAttribute(val, llvm::InlineHintAttribute),
- InlineAlways => llvm::SetFunctionAttribute(val, llvm::AlwaysInlineAttribute),
- InlineNever => llvm::SetFunctionAttribute(val, llvm::NoInlineAttribute),
- InlineNone => {
+ Hint => llvm::SetFunctionAttribute(val, llvm::InlineHintAttribute),
+ Always => llvm::SetFunctionAttribute(val, llvm::AlwaysInlineAttribute),
+ Never => llvm::SetFunctionAttribute(val, llvm::NoInlineAttribute),
+ None => {
let attr = llvm::InlineHintAttribute |
llvm::AlwaysInlineAttribute |
llvm::NoInlineAttribute;
/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute])
/// attributes.
-pub fn convert_fn_attrs_to_llvm(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) {
+pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) {
use syntax::attr::*;
inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), attrs));
}
}
}
+
+/// Composite function which converts function type into LLVM attributes for the function.
+pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx>)
+ -> llvm::AttrBuilder {
+ use middle::ty::{BrAnon, ReLateBound};
+
+ let function_type;
+ let (fn_sig, abi, env_ty) = match fn_type.sty {
+ ty::ty_bare_fn(_, ref f) => (&f.sig, f.abi, None),
+ ty::ty_closure(closure_did, substs) => {
+ let typer = common::NormalizingClosureTyper::new(ccx.tcx());
+ function_type = typer.closure_type(closure_did, substs);
+ let self_type = base::self_type_for_closure(ccx, closure_did, fn_type);
+ (&function_type.sig, abi::RustCall, Some(self_type))
+ }
+ _ => ccx.sess().bug("expected closure or function.")
+ };
+
+ let fn_sig = ty::erase_late_bound_regions(ccx.tcx(), fn_sig);
+
+ let mut attrs = llvm::AttrBuilder::new();
+ let ret_ty = fn_sig.output;
+
+ // These have an odd calling convention, so we need to manually
+ // unpack the input ty's
+ let input_tys = match fn_type.sty {
+ ty::ty_closure(..) => {
+ assert!(abi == abi::RustCall);
+
+ match fn_sig.inputs[0].sty {
+ ty::ty_tup(ref inputs) => {
+ let mut full_inputs = vec![env_ty.expect("Missing closure environment")];
+ full_inputs.push_all(inputs);
+ full_inputs
+ }
+ _ => ccx.sess().bug("expected tuple'd inputs")
+ }
+ },
+ ty::ty_bare_fn(..) if abi == abi::RustCall => {
+ let mut inputs = vec![fn_sig.inputs[0]];
+
+ match fn_sig.inputs[1].sty {
+ ty::ty_tup(ref t_in) => {
+ inputs.push_all(&t_in[..]);
+ inputs
+ }
+ _ => ccx.sess().bug("expected tuple'd inputs")
+ }
+ }
+ _ => fn_sig.inputs.clone()
+ };
+
+ // Index 0 is the return value of the llvm func, so we start at 1
+ let mut first_arg_offset = 1;
+ if let ty::FnConverging(ret_ty) = ret_ty {
+ // 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) {
+ let llret_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, ret_ty));
+
+ // The outptr can be noalias and nocapture because it's entirely
+ // invisible to the program. We also know it's nonnull as well
+ // as how many bytes we can dereference
+ attrs.arg(1, llvm::StructRetAttribute)
+ .arg(1, llvm::NoAliasAttribute)
+ .arg(1, llvm::NoCaptureAttribute)
+ .arg(1, llvm::DereferenceableAttribute(llret_sz));
+
+ // 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 ret_ty.sty {
+ // `~` pointer return values never alias because ownership
+ // is transferred
+ ty::ty_uniq(it) if !common::type_is_sized(ccx.tcx(), it) => {}
+ ty::ty_uniq(_) => {
+ attrs.ret(llvm::NoAliasAttribute);
+ }
+ _ => {}
+ }
+
+ // We can also mark the return value as `dereferenceable` in certain cases
+ match ret_ty.sty {
+ // These are not really pointers but pairs, (pointer, len)
+ ty::ty_uniq(it) |
+ ty::ty_rptr(_, ty::mt { ty: it, .. }) if !common::type_is_sized(ccx.tcx(), it) => {}
+ ty::ty_uniq(inner) | ty::ty_rptr(_, ty::mt { ty: inner, .. }) => {
+ let llret_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, inner));
+ attrs.ret(llvm::DereferenceableAttribute(llret_sz));
+ }
+ _ => {}
+ }
+
+ if let ty::ty_bool = ret_ty.sty {
+ attrs.ret(llvm::ZExtAttribute);
+ }
+ }
+ }
+
+ for (idx, &t) in input_tys.iter().enumerate().map(|(i, v)| (i + first_arg_offset, v)) {
+ match t.sty {
+ // this needs to be first to prevent fat pointers from falling through
+ _ if !common::type_is_immediate(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
+ // the value on the stack, so there are no aliases. It's also
+ // program-invisible so can't possibly capture
+ attrs.arg(idx, llvm::NoAliasAttribute)
+ .arg(idx, llvm::NoCaptureAttribute)
+ .arg(idx, llvm::DereferenceableAttribute(llarg_sz));
+ }
+
+ ty::ty_bool => {
+ attrs.arg(idx, llvm::ZExtAttribute);
+ }
+
+ // `~` pointer parameters never alias because ownership is transferred
+ ty::ty_uniq(inner) => {
+ let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, inner));
+
+ attrs.arg(idx, llvm::NoAliasAttribute)
+ .arg(idx, llvm::DereferenceableAttribute(llsz));
+ }
+
+ // `&mut` pointer parameters never alias other parameters, or mutable global data
+ //
+ // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as both
+ // `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on
+ // memory dependencies rather than pointer equality
+ ty::ty_rptr(b, mt) if mt.mutbl == ast::MutMutable ||
+ !ty::type_contents(ccx.tcx(), mt.ty).interior_unsafe() => {
+
+ let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
+ attrs.arg(idx, llvm::NoAliasAttribute)
+ .arg(idx, llvm::DereferenceableAttribute(llsz));
+
+ if mt.mutbl == ast::MutImmutable {
+ attrs.arg(idx, llvm::ReadOnlyAttribute);
+ }
+
+ if let ReLateBound(_, BrAnon(_)) = *b {
+ attrs.arg(idx, llvm::NoCaptureAttribute);
+ }
+ }
+
+ // 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(_)), mt) => {
+ let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
+ attrs.arg(idx, llvm::NoCaptureAttribute)
+ .arg(idx, llvm::DereferenceableAttribute(llsz));
+ }
+
+ // & pointer parameters are also never null and we know exactly how
+ // many bytes we can dereference
+ ty::ty_rptr(_, mt) => {
+ let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
+ attrs.arg(idx, llvm::DereferenceableAttribute(llsz));
+ }
+ _ => ()
+ }
+ }
+
+ attrs
+}
let f = decl_rust_fn(ccx, fn_ty, name);
let attrs = csearch::get_item_attrs(&ccx.sess().cstore, did);
- attributes::convert_fn_attrs_to_llvm(ccx, &attrs[..], f);
+ attributes::from_fn_attrs(ccx, &attrs[..], f);
ccx.externs().borrow_mut().insert(name.to_string(), f);
f
_ => {
let llfn = foreign::register_foreign_item_fn(ccx, fn_ty.abi, t, &name[..]);
let attrs = csearch::get_item_attrs(&ccx.sess().cstore, did);
- attributes::convert_fn_attrs_to_llvm(ccx, &attrs, llfn);
+ attributes::from_fn_attrs(ccx, &attrs, llfn);
llfn
}
}
return (C_null(Type::i8(bcx.ccx())), bcx);
}
- let attributes = get_fn_llvm_attributes(bcx.ccx(), fn_ty);
+ let attributes = attributes::from_fn_type(bcx.ccx(), fn_ty);
match bcx.opt_node_id {
None => {
llfn
}
-pub fn get_fn_llvm_attributes<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_ty: Ty<'tcx>)
- -> llvm::AttrBuilder
-{
- use middle::ty::{BrAnon, ReLateBound};
-
- let function_type;
- let (fn_sig, abi, env_ty) = match fn_ty.sty {
- ty::ty_bare_fn(_, ref f) => (&f.sig, f.abi, None),
- ty::ty_closure(closure_did, substs) => {
- let typer = common::NormalizingClosureTyper::new(ccx.tcx());
- function_type = typer.closure_type(closure_did, substs);
- let self_type = self_type_for_closure(ccx, closure_did, fn_ty);
- (&function_type.sig, RustCall, Some(self_type))
- }
- _ => ccx.sess().bug("expected closure or function.")
- };
-
- let fn_sig = ty::erase_late_bound_regions(ccx.tcx(), fn_sig);
-
- let mut attrs = llvm::AttrBuilder::new();
- let ret_ty = fn_sig.output;
-
- // These have an odd calling convention, so we need to manually
- // unpack the input ty's
- let input_tys = match fn_ty.sty {
- ty::ty_closure(..) => {
- assert!(abi == RustCall);
-
- match fn_sig.inputs[0].sty {
- ty::ty_tup(ref inputs) => {
- let mut full_inputs = vec![env_ty.expect("Missing closure environment")];
- full_inputs.push_all(inputs);
- full_inputs
- }
- _ => ccx.sess().bug("expected tuple'd inputs")
- }
- },
- ty::ty_bare_fn(..) if abi == RustCall => {
- let mut inputs = vec![fn_sig.inputs[0]];
-
- match fn_sig.inputs[1].sty {
- ty::ty_tup(ref t_in) => {
- inputs.push_all(&t_in[..]);
- inputs
- }
- _ => ccx.sess().bug("expected tuple'd inputs")
- }
- }
- _ => fn_sig.inputs.clone()
- };
-
- // Index 0 is the return value of the llvm func, so we start at 1
- let mut first_arg_offset = 1;
- if let ty::FnConverging(ret_ty) = ret_ty {
- // 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) {
- let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, ret_ty));
-
- // The outptr can be noalias and nocapture because it's entirely
- // invisible to the program. We also know it's nonnull as well
- // as how many bytes we can dereference
- attrs.arg(1, llvm::StructRetAttribute)
- .arg(1, llvm::NoAliasAttribute)
- .arg(1, llvm::NoCaptureAttribute)
- .arg(1, llvm::DereferenceableAttribute(llret_sz));
-
- // 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 ret_ty.sty {
- // `~` pointer return values never alias because ownership
- // is transferred
- ty::ty_uniq(it) if !common::type_is_sized(ccx.tcx(), it) => {}
- ty::ty_uniq(_) => {
- attrs.ret(llvm::NoAliasAttribute);
- }
- _ => {}
- }
-
- // We can also mark the return value as `dereferenceable` in certain cases
- match ret_ty.sty {
- // These are not really pointers but pairs, (pointer, len)
- ty::ty_uniq(it) |
- ty::ty_rptr(_, ty::mt { ty: it, .. }) if !common::type_is_sized(ccx.tcx(), it) => {}
- ty::ty_uniq(inner) | ty::ty_rptr(_, ty::mt { ty: inner, .. }) => {
- let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, inner));
- attrs.ret(llvm::DereferenceableAttribute(llret_sz));
- }
- _ => {}
- }
-
- if let ty::ty_bool = ret_ty.sty {
- attrs.ret(llvm::ZExtAttribute);
- }
- }
- }
-
- for (idx, &t) in input_tys.iter().enumerate().map(|(i, v)| (i + first_arg_offset, v)) {
- match t.sty {
- // this needs to be first to prevent fat pointers from falling through
- _ if !type_is_immediate(ccx, t) => {
- let llarg_sz = llsize_of_real(ccx, type_of::type_of(ccx, t));
-
- // 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
- attrs.arg(idx, llvm::NoAliasAttribute)
- .arg(idx, llvm::NoCaptureAttribute)
- .arg(idx, llvm::DereferenceableAttribute(llarg_sz));
- }
-
- ty::ty_bool => {
- attrs.arg(idx, llvm::ZExtAttribute);
- }
-
- // `~` pointer parameters never alias because ownership is transferred
- ty::ty_uniq(inner) => {
- let llsz = llsize_of_real(ccx, type_of::type_of(ccx, inner));
-
- attrs.arg(idx, llvm::NoAliasAttribute)
- .arg(idx, llvm::DereferenceableAttribute(llsz));
- }
-
- // `&mut` pointer parameters never alias other parameters, or mutable global data
- //
- // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as both
- // `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on
- // memory dependencies rather than pointer equality
- ty::ty_rptr(b, mt) if mt.mutbl == ast::MutMutable ||
- !ty::type_contents(ccx.tcx(), mt.ty).interior_unsafe() => {
-
- let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
- attrs.arg(idx, llvm::NoAliasAttribute)
- .arg(idx, llvm::DereferenceableAttribute(llsz));
-
- if mt.mutbl == ast::MutImmutable {
- attrs.arg(idx, llvm::ReadOnlyAttribute);
- }
-
- if let ReLateBound(_, BrAnon(_)) = *b {
- attrs.arg(idx, llvm::NoCaptureAttribute);
- }
- }
-
- // 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(_)), mt) => {
- let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
- attrs.arg(idx, llvm::NoCaptureAttribute)
- .arg(idx, llvm::DereferenceableAttribute(llsz));
- }
-
- // & pointer parameters are also never null and we know exactly how
- // many bytes we can dereference
- ty::ty_rptr(_, mt) => {
- let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
- attrs.arg(idx, llvm::DereferenceableAttribute(llsz));
- }
- _ => ()
- }
- }
-
- 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,
i.id)
};
- attributes::convert_fn_attrs_to_llvm(ccx, &i.attrs, llfn);
+ attributes::from_fn_attrs(ccx, &i.attrs, llfn);
llfn
}
let ty = ty::node_id_to_type(ccx.tcx(), ni.id);
let name = foreign::link_name(&*ni);
let llfn = foreign::register_foreign_item_fn(ccx, abi, ty, &name);
- attributes::convert_fn_attrs_to_llvm(ccx, &ni.attrs, llfn);
+ attributes::from_fn_attrs(ccx, &ni.attrs, llfn);
llfn
}
ast::ForeignItemStatic(..) => {
}
_ => ccx.sess().bug("NodeVariant, shouldn't happen")
};
- attributes::inline(llfn, attributes::InlineHint);
+ attributes::inline(llfn, attributes::InlineAttr::Hint);
llfn
}
&struct_item.attrs);
let llfn = register_fn(ccx, struct_item.span,
sym, ctor_id, ty);
- attributes::inline(llfn, attributes::InlineHint);
+ attributes::inline(llfn, attributes::InlineAttr::Hint);
llfn
}
} else {
foreign::register_rust_fn_with_foreign_abi(ccx, span, sym, id)
};
- attributes::convert_fn_attrs_to_llvm(ccx, &attrs, llfn);
+ attributes::from_fn_attrs(ccx, &attrs, llfn);
return llfn;
} else {
ccx.sess().span_bug(span, "expected bare rust function");
let llfn = decl_internal_rust_fn(ccx, function_type, &symbol[..]);
// set an inline hint for all closures
- attributes::inline(llfn, attributes::InlineHint);
+ attributes::inline(llfn, attributes::InlineAttr::Hint);
debug!("get_or_create_declaration_if_closure(): inserting new \
closure {:?} (type {})",
let llfn = get_extern_fn(ccx, &mut *ccx.externs().borrow_mut(), name, cc, llfn_ty, fty);
add_argument_attributes(&tys, llfn);
-
llfn
}
}
let llfn = register_foreign_item_fn(ccx, abi, ty, &lname);
- base::set_llvm_fn_attrs(ccx, &foreign_item.attrs, llfn);
+ attributes::from_fn_attrs(ccx, &foreign_item.attrs, llfn);
// Unlike for other items, we shouldn't call
// `base::update_linkage` here. Foreign items have
// special linkage requirements, which are handled
id, t.repr(tcx));
let llfn = base::decl_internal_rust_fn(ccx, t, &ps[..]);
- attributes::convert_fn_attrs_to_llvm(ccx, attrs, llfn);
+ attributes::from_fn_attrs(ccx, attrs, llfn);
base::trans_fn(ccx, decl, body, llfn, param_substs, id, &[]);
llfn
}
// Perform the call itself
debug!("calling llrustfn = {}, t = {}",
ccx.tn().val_to_string(llrustfn), t.repr(ccx.tcx()));
- let attributes = base::get_fn_llvm_attributes(ccx, t);
+ let attributes = attributes::from_fn_type(ccx, t);
let llrust_ret_val = builder.call(llrustfn, &llrust_args, Some(attributes));
// Get the return value where the foreign fn expects it.
};
let setup_lldecl = |lldecl, attrs: &[ast::Attribute]| {
base::update_linkage(ccx, lldecl, None, base::OriginalTranslation);
- attributes::convert_fn_attrs_to_llvm(ccx, attrs, lldecl);
+ attributes::from_fn_attrs(ccx, attrs, lldecl);
let is_first = !ccx.available_monomorphizations().borrow().contains(&s);
if is_first {
let tvs = ty::enum_variants(ccx.tcx(), local_def(parent));
let this_tv = tvs.iter().find(|tv| { tv.id.node == fn_id.node}).unwrap();
let d = mk_lldecl(abi::Rust);
- attributes::inline(d, attributes::InlineHint);
+ attributes::inline(d, attributes::InlineAttr::Hint);
match v.node.kind {
ast::TupleVariantKind(ref args) => {
trans_enum_variant(ccx,
}
ast_map::NodeStructCtor(struct_def) => {
let d = mk_lldecl(abi::Rust);
- attributes::inline(d, attributes::InlineHint);
+ attributes::inline(d, attributes::InlineAttr::Hint);
base::trans_tuple_struct(ccx,
&struct_def.fields,
struct_def.ctor_id.expect("ast-mapped tuple struct \