diff --git a/src/librustc_trans/trans/attributes.rs b/src/librustc_trans/trans/attributes.rs index 76034420e4b9..a8e9ef98d71d 100644 --- a/src/librustc_trans/trans/attributes.rs +++ b/src/librustc_trans/trans/attributes.rs @@ -9,13 +9,17 @@ // except according to those terms. //! Set and unset common attributes on LLVM values. -use llvm::{self, ValueRef, AttrHelper}; -use syntax::ast; -use syntax::attr::InlineAttr; -pub use syntax::attr::InlineAttr::*; -use trans::context::CrateContext; - use libc::{c_uint, c_ulonglong}; +use llvm::{self, ValueRef, AttrHelper}; +use middle::ty::{self, ClosureTyper}; +use syntax::abi; +use syntax::ast; +pub use syntax::attr::InlineAttr; +use trans::base; +use trans::common; +use trans::context::CrateContext; +use trans::machine; +use trans::type_of; /// Mark LLVM function to use split stack. #[inline] @@ -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; @@ -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)); @@ -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` 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 +} diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 5dfa84c15d04..a1fa4e77a131 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -227,7 +227,7 @@ fn get_extern_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_ty: Ty<'tcx>, 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 @@ -770,7 +770,7 @@ pub fn trans_external_path<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, _ => { 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 } } @@ -792,7 +792,7 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, 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 => { @@ -2226,176 +2226,6 @@ fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, 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` 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, @@ -2605,7 +2435,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { sym, i.id) }; - attributes::convert_fn_attrs_to_llvm(ccx, &i.attrs, llfn); + attributes::from_fn_attrs(ccx, &i.attrs, llfn); llfn } @@ -2666,7 +2496,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { 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(..) => { @@ -2698,7 +2528,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { } _ => ccx.sess().bug("NodeVariant, shouldn't happen") }; - attributes::inline(llfn, attributes::InlineHint); + attributes::inline(llfn, attributes::InlineAttr::Hint); llfn } @@ -2720,7 +2550,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { &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 } @@ -2755,7 +2585,7 @@ fn register_method(ccx: &CrateContext, id: ast::NodeId, } 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"); diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index e83ac8a5e0c9..c343d705e866 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -165,7 +165,7 @@ pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tc 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 {})", diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index e0299ba63f52..20da3c1c9f89 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -209,7 +209,6 @@ pub fn register_foreign_item_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let llfn = get_extern_fn(ccx, &mut *ccx.externs().borrow_mut(), name, cc, llfn_ty, fty); add_argument_attributes(&tys, llfn); - llfn } @@ -484,7 +483,7 @@ pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) { } 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 @@ -625,7 +624,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, 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 } @@ -818,7 +817,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // 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. diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index fc08b1a10794..862eb31303e4 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -151,7 +151,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, }; 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 { @@ -200,7 +200,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, 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, @@ -259,7 +259,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } 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 \