Skip to content

Commit

Permalink
Move a big attribute fn into trans::attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
nagisa committed Apr 3, 2015
1 parent deb097a commit caea044
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 195 deletions.
193 changes: 184 additions & 9 deletions src/librustc_trans/trans/attributes.rs
Expand Up @@ -9,13 +9,17 @@
// 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]
Expand All @@ -33,11 +37,12 @@ pub fn split_stack(val: ValueRef, set: bool) {
/// 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;
Expand Down Expand Up @@ -88,7 +93,7 @@ pub fn set_optimize_for_size(val: ValueRef, optimize: bool) {

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

Expand All @@ -106,3 +111,173 @@ pub fn convert_fn_attrs_to_llvm(ccx: &CrateContext, attrs: &[ast::Attribute], ll
}
}
}

/// 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
}

0 comments on commit caea044

Please sign in to comment.