diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index f0238e8f5c550..e8c2388a383ae 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -174,6 +174,7 @@ impl<'self> Drop for StatRecorder<'self> { } } +// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions pub fn decl_fn(llmod: ModuleRef, name: &str, cc: lib::llvm::CallConv, ty: Type) -> ValueRef { let llfn: ValueRef = do name.with_c_str |buf| { unsafe { @@ -185,18 +186,12 @@ pub fn decl_fn(llmod: ModuleRef, name: &str, cc: lib::llvm::CallConv, ty: Type) return llfn; } +// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions pub fn decl_cdecl_fn(llmod: ModuleRef, name: &str, ty: Type) -> ValueRef { return decl_fn(llmod, name, lib::llvm::CCallConv, ty); } -// Only use this if you are going to actually define the function. It's -// not valid to simply declare a function as internal. -pub fn decl_internal_cdecl_fn(llmod: ModuleRef, name: &str, ty: Type) -> ValueRef { - let llfn = decl_cdecl_fn(llmod, name, ty); - lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage); - return llfn; -} - +// 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, name: &str, cc: lib::llvm::CallConv, ty: Type) -> ValueRef { match externs.find_equiv(&name) { @@ -205,7 +200,73 @@ pub fn get_extern_fn(externs: &mut ExternMap, llmod: ModuleRef, name: &str, } let f = decl_fn(llmod, name, cc, ty); externs.insert(name.to_owned(), f); - return f; + f +} + +pub fn get_extern_rust_fn(ccx: &mut CrateContext, inputs: &[ty::t], output: ty::t, + name: &str) -> ValueRef { + match ccx.externs.find_equiv(&name) { + Some(n) => return *n, + None => () + } + let f = decl_rust_fn(ccx, inputs, output, name); + ccx.externs.insert(name.to_owned(), f); + f +} + +pub fn decl_rust_fn(ccx: &mut CrateContext, inputs: &[ty::t], output: ty::t, + name: &str) -> ValueRef { + let llfty = type_of_rust_fn(ccx, inputs, output); + let llfn = decl_cdecl_fn(ccx.llmod, name, llfty); + + match ty::get(output).sty { + // `~` pointer return values never alias because ownership is transferred + ty::ty_uniq(*) | + ty::ty_evec(_, ty::vstore_uniq) => { + unsafe { + llvm::LLVMAddReturnAttribute(llfn, lib::llvm::NoAliasAttribute as c_uint); + } + } + _ => () + } + + let uses_outptr = type_of::return_uses_outptr(ccx.tcx, output); + let offset = if uses_outptr { 2 } else { 1 }; + + 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(*) | + ty::ty_evec(_, ty::vstore_uniq) | + ty::ty_closure(ty::ClosureTy {sigil: ast::OwnedSigil, _}) => { + unsafe { + llvm::LLVMAddAttribute(llarg, lib::llvm::NoAliasAttribute 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 { + 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); + } + } + + llfn +} + +pub fn decl_internal_rust_fn(ccx: &mut CrateContext, inputs: &[ty::t], output: ty::t, + name: &str) -> ValueRef { + let llfn = decl_rust_fn(ccx, inputs, output, name); + lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage); + llfn } pub fn get_extern_const(externs: &mut ExternMap, llmod: ModuleRef, @@ -809,33 +870,30 @@ pub fn null_env_ptr(ccx: &CrateContext) -> ValueRef { C_null(Type::opaque_box(ccx).ptr_to()) } -pub fn trans_external_path(ccx: &mut CrateContext, did: ast::DefId, t: ty::t) - -> ValueRef { +pub fn trans_external_path(ccx: &mut CrateContext, did: ast::DefId, t: ty::t) -> ValueRef { let name = csearch::get_symbol(ccx.sess.cstore, did); match ty::get(t).sty { ty::ty_bare_fn(ref fn_ty) => { - // Currently llvm_calling_convention triggers unimpl/bug on - // Rust/RustIntrinsic, so those two are handled specially here. - let cconv = match fn_ty.abis.for_arch(ccx.sess.targ_cfg.arch) { - Some(Rust) | Some(RustIntrinsic) => lib::llvm::CCallConv, + match fn_ty.abis.for_arch(ccx.sess.targ_cfg.arch) { + Some(Rust) | Some(RustIntrinsic) => { + get_extern_rust_fn(ccx, fn_ty.sig.inputs, fn_ty.sig.output, name) + } Some(*) | None => { let c = foreign::llvm_calling_convention(ccx, fn_ty.abis); - c.unwrap_or(lib::llvm::CCallConv) + let cconv = c.unwrap_or(lib::llvm::CCallConv); + let llty = type_of_fn_from_ty(ccx, t); + get_extern_fn(&mut ccx.externs, ccx.llmod, name, cconv, llty) } - }; - let llty = type_of_fn_from_ty(ccx, t); - return get_extern_fn(&mut ccx.externs, ccx.llmod, name, cconv, llty); + } } - ty::ty_closure(_) => { - let llty = type_of_fn_from_ty(ccx, t); - return get_extern_fn(&mut ccx.externs, ccx.llmod, name, - lib::llvm::CCallConv, llty); + ty::ty_closure(ref f) => { + get_extern_rust_fn(ccx, f.sig.inputs, f.sig.output, name) } _ => { let llty = type_of(ccx, t); - return get_extern_const(&mut ccx.externs, ccx.llmod, name, llty); + get_extern_const(&mut ccx.externs, ccx.llmod, name, llty) } - }; + } } pub fn invoke(bcx: @mut Block, llfn: ValueRef, llargs: ~[ValueRef], @@ -868,7 +926,8 @@ pub fn invoke(bcx: @mut Block, llfn: ValueRef, llargs: ~[ValueRef], llfn, llargs, normal_bcx.llbb, - get_landing_pad(bcx)); + get_landing_pad(bcx), + attributes); return (llresult, normal_bcx); } else { unsafe { @@ -1707,8 +1766,7 @@ pub fn new_fn_ctxt(ccx: @mut CrateContext, // field of the fn_ctxt with pub fn create_llargs_for_fn_args(cx: @mut FunctionContext, self_arg: self_arg, - args: &[ast::arg], - arg_tys: &[ty::t]) + args: &[ast::arg]) -> ~[ValueRef] { let _icx = push_ctxt("create_llargs_for_fn_args"); @@ -1726,23 +1784,7 @@ pub fn create_llargs_for_fn_args(cx: @mut FunctionContext, // Return an array containing the ValueRefs that we get from // llvm::LLVMGetParam for each argument. do vec::from_fn(args.len()) |i| { - let arg_n = cx.arg_pos(i); - let arg_ty = arg_tys[i]; - let llarg = unsafe {llvm::LLVMGetParam(cx.llfn, arg_n as c_uint) }; - - match ty::get(arg_ty).sty { - // `~` pointer parameters never alias because ownership is transferred - ty::ty_uniq(*) | - ty::ty_evec(_, ty::vstore_uniq) | - ty::ty_closure(ty::ClosureTy {sigil: ast::OwnedSigil, _}) => { - unsafe { - llvm::LLVMAddAttribute(llarg, lib::llvm::NoAliasAttribute as c_uint); - } - } - _ => () - } - - llarg + unsafe { llvm::LLVMGetParam(cx.llfn, cx.arg_pos(i) as c_uint) } } } @@ -1896,8 +1938,7 @@ pub fn trans_closure(ccx: @mut CrateContext, // Set up arguments to the function. let arg_tys = ty::ty_fn_args(node_id_type(bcx, id)); - let raw_llargs = create_llargs_for_fn_args(fcx, self_arg, - decl.inputs, arg_tys); + let raw_llargs = create_llargs_for_fn_args(fcx, self_arg, decl.inputs); // Set the fixed stack segment flag if necessary. if attr::contains_name(attributes, "fixed_stack_segment") { @@ -1961,18 +2002,6 @@ pub fn trans_fn(ccx: @mut CrateContext, param_substs.repr(ccx.tcx)); let _icx = push_ctxt("trans_fn"); let output_type = ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, id)); - - match ty::get(output_type).sty { - // `~` pointer return values never alias because ownership is transferred - ty::ty_uniq(*) | - ty::ty_evec(_, ty::vstore_uniq) => { - unsafe { - llvm::LLVMAddReturnAttribute(llfndecl, lib::llvm::NoAliasAttribute as c_uint); - } - } - _ => () - } - trans_closure(ccx, path.clone(), decl, @@ -2120,7 +2149,7 @@ pub fn trans_enum_variant_or_tuple_like_struct( let arg_tys = ty::ty_fn_args(ctor_ty); - let raw_llargs = create_llargs_for_fn_args(fcx, no_self, fn_args, arg_tys); + let raw_llargs = create_llargs_for_fn_args(fcx, no_self, fn_args); let bcx = fcx.entry_bcx.unwrap(); @@ -2298,10 +2327,28 @@ pub fn register_fn(ccx: @mut CrateContext, node_id: ast::NodeId, node_type: ty::t) -> ValueRef { - let llfty = type_of_fn_from_ty(ccx, node_type); - register_fn_llvmty(ccx, sp, sym, node_id, lib::llvm::CCallConv, llfty) + let f = match ty::get(node_type).sty { + ty::ty_bare_fn(ref f) => { + assert!(f.abis.is_rust() || f.abis.is_intrinsic()); + f + } + _ => fail!("expected bare rust fn or an intrinsic") + }; + + let llfn = decl_rust_fn(ccx, f.sig.inputs, f.sig.output, sym); + ccx.item_symbols.insert(node_id, sym); + + // FIXME #4404 android JNI hacks + let is_entry = is_entry_fn(&ccx.sess, node_id) && (!*ccx.sess.building_library || + (*ccx.sess.building_library && + ccx.sess.targ_cfg.os == session::OsAndroid)); + if is_entry { + create_entry_wrapper(ccx, sp, llfn); + } + llfn } +// only use this for foreign function ABIs and glue, use `register_fn` for Rust functions pub fn register_fn_llvmty(ccx: @mut CrateContext, sp: Span, sym: ~str, diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs index aabb389dde114..4b03a2cac4b3a 100644 --- a/src/librustc/middle/trans/build.rs +++ b/src/librustc/middle/trans/build.rs @@ -109,7 +109,8 @@ pub fn Invoke(cx: @mut Block, Fn: ValueRef, Args: &[ValueRef], Then: BasicBlockRef, - Catch: BasicBlockRef) + Catch: BasicBlockRef, + attributes: &[(uint, lib::llvm::Attribute)]) -> ValueRef { if cx.unreachable { return C_null(Type::i8()); @@ -119,15 +120,7 @@ pub fn Invoke(cx: @mut Block, debug!("Invoke(%s with arguments (%s))", cx.val_to_str(Fn), Args.map(|a| cx.val_to_str(*a)).connect(", ")); - B(cx).invoke(Fn, Args, Then, Catch) -} - -pub fn FastInvoke(cx: @mut Block, Fn: ValueRef, Args: &[ValueRef], - Then: BasicBlockRef, Catch: BasicBlockRef) { - if cx.unreachable { return; } - check_not_terminated(cx); - terminate(cx, "FastInvoke"); - B(cx).fast_invoke(Fn, Args, Then, Catch); + B(cx).invoke(Fn, Args, Then, Catch, attributes) } pub fn Unreachable(cx: @mut Block) { diff --git a/src/librustc/middle/trans/builder.rs b/src/librustc/middle/trans/builder.rs index 85e45942b7981..d7a4dbb3510fe 100644 --- a/src/librustc/middle/trans/builder.rs +++ b/src/librustc/middle/trans/builder.rs @@ -154,30 +154,25 @@ impl Builder { llfn: ValueRef, args: &[ValueRef], then: BasicBlockRef, - catch: BasicBlockRef) + catch: BasicBlockRef, + attributes: &[(uint, lib::llvm::Attribute)]) -> ValueRef { self.count_insn("invoke"); unsafe { - llvm::LLVMBuildInvoke(self.llbuilder, - llfn, - vec::raw::to_ptr(args), - args.len() as c_uint, - then, - catch, - noname()) + let v = llvm::LLVMBuildInvoke(self.llbuilder, + llfn, + vec::raw::to_ptr(args), + args.len() as c_uint, + then, + catch, + noname()); + for &(idx, attr) in attributes.iter() { + llvm::LLVMAddInstrAttribute(v, idx as c_uint, attr as c_uint); + } + v } } - pub fn fast_invoke(&self, - llfn: ValueRef, - args: &[ValueRef], - then: BasicBlockRef, - catch: BasicBlockRef) { - self.count_insn("fastinvoke"); - let v = self.invoke(llfn, args, then, catch); - lib::llvm::SetInstructionCallConv(v, lib::llvm::FastCallConv); - } - pub fn unreachable(&self) { self.count_insn("unreachable"); unsafe { diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 45da026afd06a..54c905a4c1651 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -20,7 +20,7 @@ use std::vec; use back::abi; use driver::session; -use lib::llvm::ValueRef; +use lib::llvm::{ValueRef, NoAliasAttribute, StructRetAttribute}; use lib::llvm::llvm; use metadata::csearch; use middle::trans::base; @@ -706,8 +706,26 @@ pub fn trans_call_inner(in_cx: @mut Block, _ => {} } + // 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 = ~[]; + if type_of::return_uses_outptr(in_cx.tcx(), ret_ty) { + attrs.push((1, StructRetAttribute)); + } + + // 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::ty_evec(_, ty::vstore_uniq) => { + attrs.push((0, NoAliasAttribute)); + } + _ => () + } + // Invoke the actual rust fn and update bcx/llresult. - let (llret, b) = base::invoke(bcx, llfn, llargs, []); + let (llret, b) = base::invoke(bcx, llfn, llargs, attrs); bcx = b; llresult = llret; diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index 690d7343489e2..605032dc20c8f 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -381,8 +381,10 @@ pub fn trans_expr_fn(bcx: @mut Block, let ccx = bcx.ccx(); let fty = node_id_type(bcx, outer_id); - - let llfnty = type_of_fn_from_ty(ccx, fty); + let f = match ty::get(fty).sty { + ty::ty_closure(ref f) => f, + _ => fail!("expected closure") + }; let sub_path = vec::append_one(bcx.fcx.path.clone(), path_name(special_idents::anon)); @@ -390,7 +392,7 @@ pub fn trans_expr_fn(bcx: @mut Block, let s = mangle_internal_name_by_path_and_seq(ccx, sub_path.clone(), "expr_fn"); - let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty); + let llfn = decl_internal_rust_fn(ccx, f.sig.inputs, f.sig.output, s); // set an inline hint for all closures set_inline_hint(llfn); diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 1ebe402bbb987..b00d77d72ddb3 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -21,7 +21,6 @@ use middle::trans::cabi; use middle::trans::build::*; use middle::trans::builder::noname; use middle::trans::common::*; -use middle::trans::llrepr::LlvmRepr; use middle::trans::type_of::*; use middle::trans::type_of; use middle::ty; @@ -265,6 +264,9 @@ pub fn trans_native_call(bcx: @mut Block, } }; + // 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 attrs; if fn_type.sret { attrs = &[(1, StructRetAttribute)]; @@ -406,13 +408,12 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext, special_idents::clownshoe_abi ))); - // Compute the LLVM type that the function would have if it - // were just a normal Rust function. This will be the type of - // the wrappee fn. - let llty = match ty::get(t).sty { + // 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 { ty::ty_bare_fn(ref f) => { assert!(!f.abis.is_rust() && !f.abis.is_intrinsic()); - type_of_rust_fn(ccx, f.sig.inputs, f.sig.output) + f } _ => { ccx.sess.bug(fmt!("build_rust_fn: extern fn %s has ty %s, \ @@ -422,13 +423,12 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext, } }; - debug!("build_rust_fn: path=%s id=%? t=%s llty=%s", + debug!("build_rust_fn: path=%s id=%? t=%s", path.repr(tcx), id, - t.repr(tcx), - llty.llrepr(ccx)); + t.repr(tcx)); - let llfndecl = base::decl_internal_cdecl_fn(ccx.llmod, ps, llty); + let llfndecl = base::decl_internal_rust_fn(ccx, f.sig.inputs, f.sig.output, ps); base::trans_fn(ccx, (*path).clone(), decl, @@ -500,7 +500,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext, // Rust expects to use an outpointer. If the foreign fn // also uses an outpointer, we can reuse it, but the types // may vary, so cast first to the Rust type. If the - // foriegn fn does NOT use an outpointer, we will have to + // foreign fn does NOT use an outpointer, we will have to // alloca some scratch space on the stack. match foreign_outptr { Some(llforeign_outptr) => { diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index 2bdff6c8567fe..ef055a52468c8 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -14,14 +14,13 @@ use driver::session; use lib::llvm::ValueRef; use middle::trans::base::{set_llvm_fn_attrs, set_inline_hint}; use middle::trans::base::{trans_enum_variant,push_ctxt}; -use middle::trans::base::{trans_fn, decl_internal_cdecl_fn}; +use middle::trans::base::{trans_fn, decl_internal_rust_fn}; use middle::trans::base::{get_item_val, no_self}; use middle::trans::base; use middle::trans::common::*; use middle::trans::datum; use middle::trans::machine; use middle::trans::meth; -use middle::trans::type_of::type_of_fn_from_ty; use middle::trans::type_of; use middle::trans::type_use; use middle::trans::intrinsic; @@ -177,7 +176,14 @@ pub fn monomorphic_fn(ccx: @mut CrateContext, ty::subst_tps(ccx.tcx, substs, None, llitem_ty) } }; - let llfty = type_of_fn_from_ty(ccx, mono_ty); + + let f = match ty::get(mono_ty).sty { + ty::ty_bare_fn(ref f) => { + assert!(f.abis.is_rust() || f.abis.is_intrinsic()); + f + } + _ => fail!("expected bare rust fn or an intrinsic") + }; ccx.stats.n_monos += 1; @@ -200,7 +206,7 @@ pub fn monomorphic_fn(ccx: @mut CrateContext, debug!("monomorphize_fn mangled to %s", s); let mk_lldecl = || { - let lldecl = decl_internal_cdecl_fn(ccx.llmod, s, llfty); + let lldecl = decl_internal_rust_fn(ccx, f.sig.inputs, f.sig.output, s); ccx.monomorphized.insert(hash_id, lldecl); lldecl }; diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index 300fb64863ca8..23b87c63d6a2c 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -293,8 +293,7 @@ impl Reflector { sub_path, "get_disr"); - let llfty = type_of_rust_fn(ccx, [opaqueptrty], ty::mk_int()); - let llfdecl = decl_internal_cdecl_fn(ccx.llmod, sym, llfty); + let llfdecl = decl_internal_rust_fn(ccx, [opaqueptrty], ty::mk_int(), sym); let fcx = new_fn_ctxt(ccx, ~[], llfdecl,