diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs
index 9b0ae2e9ef8..7030ee56979 100644
--- a/src/librustc_llvm/lib.rs
+++ b/src/librustc_llvm/lib.rs
@@ -157,7 +157,7 @@ bitflags! {
 #[derive(Copy, Clone)]
 pub enum OtherAttribute {
     // The following are not really exposed in
-    // the LLVM c api so instead to add these
+    // the LLVM C api so instead to add these
     // we call a wrapper function in RustWrapper
     // that uses the C++ api.
     SanitizeAddressAttribute = 1 << 32,
@@ -912,6 +912,7 @@ extern {
                                        AddressSpace: c_uint)
                                        -> ValueRef;
     pub fn LLVMGetNamedGlobal(M: ModuleRef, Name: *const c_char) -> ValueRef;
+    pub fn LLVMGetOrInsertGlobal(M: ModuleRef, Name: *const c_char, T: TypeRef) -> ValueRef;
     pub fn LLVMGetFirstGlobal(M: ModuleRef) -> ValueRef;
     pub fn LLVMGetLastGlobal(M: ModuleRef) -> ValueRef;
     pub fn LLVMGetNextGlobal(GlobalVar: ValueRef) -> ValueRef;
@@ -924,6 +925,7 @@ extern {
     pub fn LLVMSetThreadLocal(GlobalVar: ValueRef, IsThreadLocal: Bool);
     pub fn LLVMIsGlobalConstant(GlobalVar: ValueRef) -> Bool;
     pub fn LLVMSetGlobalConstant(GlobalVar: ValueRef, IsConstant: Bool);
+    pub fn LLVMGetNamedValue(M: ModuleRef, Name: *const c_char) -> ValueRef;
 
     /* Operations on aliases */
     pub fn LLVMAddAlias(M: ModuleRef,
@@ -957,6 +959,7 @@ extern {
     pub fn LLVMAddFunctionAttrString(Fn: ValueRef, index: c_uint, Name: *const c_char);
     pub fn LLVMRemoveFunctionAttrString(Fn: ValueRef, index: c_uint, Name: *const c_char);
     pub fn LLVMGetFunctionAttr(Fn: ValueRef) -> c_ulonglong;
+    pub fn LLVMRemoveFunctionAttr(Fn: ValueRef, val: c_ulonglong);
 
     /* Operations on parameters */
     pub fn LLVMCountParams(Fn: ValueRef) -> c_uint;
diff --git a/src/librustc_trans/trans/attributes.rs b/src/librustc_trans/trans/attributes.rs
new file mode 100644
index 00000000000..2615490a9fb
--- /dev/null
+++ b/src/librustc_trans/trans/attributes.rs
@@ -0,0 +1,281 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// 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;
+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]
+pub fn split_stack(val: ValueRef, set: bool) {
+    unsafe {
+        let attr = "split-stack\0".as_ptr() as *const _;
+        if set {
+            llvm::LLVMAddFunctionAttrString(val, llvm::FunctionIndex as c_uint, attr);
+        } else {
+            llvm::LLVMRemoveFunctionAttrString(val, llvm::FunctionIndex as c_uint, attr);
+        }
+    }
+}
+
+/// Mark LLVM function to use provided inline heuristic.
+#[inline]
+pub fn inline(val: ValueRef, inline: InlineAttr) {
+    use self::InlineAttr::*;
+    match inline {
+        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;
+            unsafe {
+                llvm::LLVMRemoveFunctionAttr(val, attr.bits() as c_ulonglong)
+            }
+        },
+    };
+}
+
+/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
+#[inline]
+pub fn emit_uwtable(val: ValueRef, emit: bool) {
+    if emit {
+        llvm::SetFunctionAttribute(val, llvm::UWTableAttribute);
+    } else {
+        unsafe {
+            llvm::LLVMRemoveFunctionAttr(val, llvm::UWTableAttribute.bits() as c_ulonglong);
+        }
+    }
+}
+
+/// Tell LLVM whether the function can or cannot unwind.
+#[inline]
+#[allow(dead_code)] // possibly useful function
+pub fn unwind(val: ValueRef, can_unwind: bool) {
+    if can_unwind {
+        unsafe {
+            llvm::LLVMRemoveFunctionAttr(val, llvm::NoUnwindAttribute.bits() as c_ulonglong);
+        }
+    } else {
+        llvm::SetFunctionAttribute(val, llvm::NoUnwindAttribute);
+    }
+}
+
+/// Tell LLVM whether it should optimise function for size.
+#[inline]
+#[allow(dead_code)] // possibly useful function
+pub fn set_optimize_for_size(val: ValueRef, optimize: bool) {
+    if optimize {
+        llvm::SetFunctionAttribute(val, llvm::OptimizeForSizeAttribute);
+    } else {
+        unsafe {
+            llvm::LLVMRemoveFunctionAttr(val, llvm::OptimizeForSizeAttribute.bits() as c_ulonglong);
+        }
+    }
+}
+
+/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute])
+/// attributes.
+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));
+
+    for attr in attrs {
+        if attr.check_name("no_stack_check") {
+            split_stack(llfn, false);
+        } else if attr.check_name("cold") {
+            unsafe {
+                llvm::LLVMAddFunctionAttribute(llfn,
+                                               llvm::FunctionIndex as c_uint,
+                                               llvm::ColdAttribute as u64)
+            }
+        } else if attr.check_name("allocator") {
+            llvm::NoAliasAttribute.apply_llfn(llvm::ReturnIndex as c_uint, llfn);
+        }
+    }
+}
+
+/// 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) => {
+                    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_rptr(_, ty::mt { ty: inner, .. })
+                | ty::ty_uniq(inner) if common::type_is_sized(ccx.tcx(), 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
+}
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 05c366a645e..7c51a193b98 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -1,4 +1,4 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -7,21 +7,20 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
-
-// trans.rs: Translate the completed AST to the LLVM IR.
-//
-// Some functions here, such as trans_block and trans_expr, return a value --
-// the result of the translation to LLVM -- while others, such as trans_fn,
-// trans_impl, and trans_item, are called only for the side effect of adding a
-// particular definition to the LLVM IR output we're producing.
-//
-// Hopefully useful general knowledge about trans:
-//
-//   * There's no way to find out the Ty type of a ValueRef.  Doing so
-//     would be "trying to get the eggs out of an omelette" (credit:
-//     pcwalton).  You can, instead, find out its TypeRef by calling val_ty,
-//     but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int,
-//     int) and rec(x=int, y=int, z=int) will have the same TypeRef.
+//! Translate the completed AST to the LLVM IR.
+//!
+//! Some functions here, such as trans_block and trans_expr, return a value --
+//! the result of the translation to LLVM -- while others, such as trans_fn,
+//! trans_impl, and trans_item, are called only for the side effect of adding a
+//! particular definition to the LLVM IR output we're producing.
+//!
+//! Hopefully useful general knowledge about trans:
+//!
+//!   * There's no way to find out the Ty type of a ValueRef.  Doing so
+//!     would be "trying to get the eggs out of an omelette" (credit:
+//!     pcwalton).  You can, instead, find out its TypeRef by calling val_ty,
+//!     but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int,
+//!     int) and rec(x=int, y=int, z=int) will have the same TypeRef.
 
 #![allow(non_camel_case_types)]
 
@@ -33,19 +32,20 @@ use super::ModuleTranslation;
 use back::link::mangle_exported_name;
 use back::{link, abi};
 use lint;
-use llvm::{AttrHelper, BasicBlockRef, Linkage, ValueRef, Vector, get_param};
+use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param};
 use llvm;
 use metadata::{csearch, encoder, loader};
 use middle::astencode;
 use middle::cfg;
 use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
 use middle::weak_lang_items;
-use middle::subst::{Subst, Substs};
+use middle::subst::Substs;
 use middle::ty::{self, Ty, ClosureTyper, type_is_simd, simd_size};
 use session::config::{self, NoDebugInfo};
 use session::Session;
 use trans::_match;
 use trans::adt;
+use trans::attributes;
 use trans::build::*;
 use trans::builder::{Builder, noname};
 use trans::callee;
@@ -54,7 +54,7 @@ use trans::cleanup;
 use trans::closure;
 use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_integral};
 use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef};
-use trans::common::{CrateContext, ExternMap, FunctionContext};
+use trans::common::{CrateContext, FunctionContext};
 use trans::common::{Result, NodeIdAndSpan};
 use trans::common::{node_id_type, return_type_is_void};
 use trans::common::{type_is_immediate, type_is_zero_size, val_ty};
@@ -64,10 +64,10 @@ use trans::context::SharedCrateContext;
 use trans::controlflow;
 use trans::datum;
 use trans::debuginfo::{self, DebugLoc, ToDebugLoc};
+use trans::declare;
 use trans::expr;
 use trans::foreign;
 use trans::glue;
-use trans::inline;
 use trans::intrinsic;
 use trans::machine;
 use trans::machine::{llsize_of, llsize_of_real};
@@ -84,7 +84,7 @@ use util::sha2::Sha256;
 use util::nodemap::NodeMap;
 
 use arena::TypedArena;
-use libc::{c_uint, uint64_t};
+use libc::c_uint;
 use std::ffi::{CStr, CString};
 use std::cell::{Cell, RefCell};
 use std::collections::HashSet;
@@ -180,61 +180,6 @@ impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> {
     }
 }
 
-// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions
-pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv,
-               ty: Type, output: ty::FnOutput) -> ValueRef {
-
-    let buf = CString::new(name).unwrap();
-    let llfn: ValueRef = unsafe {
-        llvm::LLVMGetOrInsertFunction(ccx.llmod(), buf.as_ptr(), ty.to_ref())
-    };
-
-    // diverging functions may unwind, but can never return normally
-    if output == ty::FnDiverging {
-        llvm::SetFunctionAttribute(llfn, llvm::NoReturnAttribute);
-    }
-
-    if ccx.tcx().sess.opts.cg.no_redzone
-        .unwrap_or(ccx.tcx().sess.target.target.options.disable_redzone) {
-        llvm::SetFunctionAttribute(llfn, llvm::NoRedZoneAttribute)
-    }
-
-    llvm::SetFunctionCallConv(llfn, cc);
-    // Function addresses in Rust are never significant, allowing functions to be merged.
-    llvm::SetUnnamedAddr(llfn, true);
-
-    if ccx.is_split_stack_supported() && !ccx.sess().opts.cg.no_stack_check {
-        set_split_stack(llfn);
-    }
-
-    llfn
-}
-
-// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions
-pub fn decl_cdecl_fn(ccx: &CrateContext,
-                     name: &str,
-                     ty: Type,
-                     output: Ty) -> ValueRef {
-    decl_fn(ccx, name, llvm::CCallConv, ty, ty::FnConverging(output))
-}
-
-// only use this for foreign function ABIs and glue, use `get_extern_rust_fn` for Rust functions
-pub fn get_extern_fn(ccx: &CrateContext,
-                     externs: &mut ExternMap,
-                     name: &str,
-                     cc: llvm::CallConv,
-                     ty: Type,
-                     output: Ty)
-                     -> ValueRef {
-    match externs.get(name) {
-        Some(n) => return *n,
-        None => {}
-    }
-    let f = decl_fn(ccx, name, cc, ty, ty::FnConverging(output));
-    externs.insert(name.to_string(), f);
-    f
-}
-
 fn get_extern_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_ty: Ty<'tcx>,
                                 name: &str, did: ast::DefId) -> ValueRef {
     match ccx.externs().borrow().get(name) {
@@ -242,10 +187,10 @@ fn get_extern_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_ty: Ty<'tcx>,
         None => ()
     }
 
-    let f = decl_rust_fn(ccx, fn_ty, name);
+    let f = declare::declare_rust_fn(ccx, name, fn_ty);
 
     let attrs = csearch::get_item_attrs(&ccx.sess().cstore, did);
-    set_llvm_fn_attrs(ccx, &attrs[..], f);
+    attributes::from_fn_attrs(ccx, &attrs[..], f);
 
     ccx.externs().borrow_mut().insert(name.to_string(), f);
     f
@@ -272,63 +217,6 @@ pub fn kind_for_closure(ccx: &CrateContext, closure_id: ast::DefId) -> ty::Closu
     *ccx.tcx().closure_kinds.borrow().get(&closure_id).unwrap()
 }
 
-pub fn decl_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                              fn_ty: Ty<'tcx>, name: &str) -> ValueRef {
-    debug!("decl_rust_fn(fn_ty={}, name={:?})",
-           fn_ty.repr(ccx.tcx()),
-           name);
-
-    let fn_ty = monomorphize::normalize_associated_type(ccx.tcx(), &fn_ty);
-
-    debug!("decl_rust_fn: fn_ty={} (after normalized associated types)",
-           fn_ty.repr(ccx.tcx()));
-
-    let function_type; // placeholder so that the memory ownership works out ok
-
-    let (sig, abi, env) = 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);
-            let llenvironment_type = type_of_explicit_arg(ccx, self_type);
-            debug!("decl_rust_fn: function_type={} self_type={}",
-                   function_type.repr(ccx.tcx()),
-                   self_type.repr(ccx.tcx()));
-            (&function_type.sig, RustCall, Some(llenvironment_type))
-        }
-        _ => ccx.sess().bug("expected closure or fn")
-    };
-
-    let sig = ty::erase_late_bound_regions(ccx.tcx(), sig);
-    let sig = ty::Binder(sig);
-
-    debug!("decl_rust_fn: sig={} (after erasing regions)",
-           sig.repr(ccx.tcx()));
-
-    let llfty = type_of_rust_fn(ccx, env, &sig, abi);
-
-    debug!("decl_rust_fn: llfty={}",
-           ccx.tn().type_to_string(llfty));
-
-    let llfn = decl_fn(ccx, name, llvm::CCallConv, llfty, sig.0.output /* (1) */);
-    let attrs = get_fn_llvm_attributes(ccx, fn_ty);
-    attrs.apply_llfn(llfn);
-
-    // (1) it's ok to directly access sig.0.output because we erased all late-bound-regions above
-
-    llfn
-}
-
-pub fn decl_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                       fn_ty: Ty<'tcx>, name: &str) -> ValueRef {
-    let llfn = decl_rust_fn(ccx, fn_ty, name);
-    llvm::SetLinkage(llfn, llvm::InternalLinkage);
-    llfn
-}
-
 pub fn get_extern_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, did: ast::DefId,
                                   t: Ty<'tcx>) -> ValueRef {
     let name = csearch::get_symbol(&ccx.sess().cstore, did);
@@ -337,23 +225,22 @@ pub fn get_extern_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, did: ast::DefId,
         Some(n) => return *n,
         None => ()
     }
-    unsafe {
-        let buf = CString::new(name.clone()).unwrap();
-        let c = llvm::LLVMAddGlobal(ccx.llmod(), ty.to_ref(), buf.as_ptr());
-        // Thread-local statics in some other crate need to *always* be linked
-        // against in a thread-local fashion, so we need to be sure to apply the
-        // thread-local attribute locally if it was present remotely. If we
-        // don't do this then linker errors can be generated where the linker
-        // complains that one object files has a thread local version of the
-        // symbol and another one doesn't.
-        for attr in &*ty::get_attrs(ccx.tcx(), did) {
-            if attr.check_name("thread_local") {
-                llvm::set_thread_local(c, true);
-            }
+    // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow?
+    // FIXME(nagisa): investigate whether it can be changed into define_global
+    let c = declare::declare_global(ccx, &name[..], ty);
+    // Thread-local statics in some other crate need to *always* be linked
+    // against in a thread-local fashion, so we need to be sure to apply the
+    // thread-local attribute locally if it was present remotely. If we
+    // don't do this then linker errors can be generated where the linker
+    // complains that one object files has a thread local version of the
+    // symbol and another one doesn't.
+    for attr in &*ty::get_attrs(ccx.tcx(), did) {
+        if attr.check_name("thread_local") {
+            llvm::set_thread_local(c, true);
         }
-        ccx.externs().borrow_mut().insert(name.to_string(), c);
-        return c;
     }
+    ccx.externs().borrow_mut().insert(name.to_string(), c);
+    return c;
 }
 
 fn require_alloc_fn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
@@ -390,125 +277,6 @@ pub fn malloc_raw_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     Result::new(r.bcx, PointerCast(r.bcx, r.val, llty_ptr))
 }
 
-#[allow(dead_code)] // useful
-pub fn set_optimize_for_size(f: ValueRef) {
-    llvm::SetFunctionAttribute(f, llvm::OptimizeForSizeAttribute)
-}
-
-pub fn set_no_inline(f: ValueRef) {
-    llvm::SetFunctionAttribute(f, llvm::NoInlineAttribute)
-}
-
-#[allow(dead_code)] // useful
-pub fn set_no_unwind(f: ValueRef) {
-    llvm::SetFunctionAttribute(f, llvm::NoUnwindAttribute)
-}
-
-// Tell LLVM to emit the information necessary to unwind the stack for the
-// function f.
-pub fn set_uwtable(f: ValueRef) {
-    llvm::SetFunctionAttribute(f, llvm::UWTableAttribute)
-}
-
-pub fn set_inline_hint(f: ValueRef) {
-    llvm::SetFunctionAttribute(f, llvm::InlineHintAttribute)
-}
-
-pub fn set_llvm_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) {
-    use syntax::attr::{find_inline_attr, InlineAttr};
-    // Set the inline hint if there is one
-    match find_inline_attr(Some(ccx.sess().diagnostic()), attrs) {
-        InlineAttr::Hint   => set_inline_hint(llfn),
-        InlineAttr::Always => set_always_inline(llfn),
-        InlineAttr::Never  => set_no_inline(llfn),
-        InlineAttr::None   => { /* fallthrough */ }
-    }
-
-    for attr in attrs {
-        let mut used = true;
-        match &attr.name()[..] {
-            "no_stack_check" => unset_split_stack(llfn),
-            "cold" => unsafe {
-                llvm::LLVMAddFunctionAttribute(llfn,
-                                               llvm::FunctionIndex as c_uint,
-                                               llvm::ColdAttribute as uint64_t)
-            },
-            "allocator" => {
-                llvm::NoAliasAttribute.apply_llfn(llvm::ReturnIndex as c_uint, llfn);
-            }
-            _ => used = false,
-        }
-        if used {
-            attr::mark_used(attr);
-        }
-    }
-}
-
-pub fn set_always_inline(f: ValueRef) {
-    llvm::SetFunctionAttribute(f, llvm::AlwaysInlineAttribute)
-}
-
-pub fn set_split_stack(f: ValueRef) {
-    unsafe {
-        llvm::LLVMAddFunctionAttrString(f, llvm::FunctionIndex as c_uint,
-                                        "split-stack\0".as_ptr() as *const _);
-    }
-}
-
-pub fn unset_split_stack(f: ValueRef) {
-    unsafe {
-        llvm::LLVMRemoveFunctionAttrString(f, llvm::FunctionIndex as c_uint,
-                                           "split-stack\0".as_ptr() as *const _);
-    }
-}
-
-// Double-check that we never ask LLVM to declare the same symbol twice. It
-// silently mangles such symbols, breaking our linkage model.
-pub fn note_unique_llvm_symbol(ccx: &CrateContext, sym: String) {
-    if ccx.all_llvm_symbols().borrow().contains(&sym) {
-        ccx.sess().bug(&format!("duplicate LLVM symbol: {}", sym));
-    }
-    ccx.all_llvm_symbols().borrow_mut().insert(sym);
-}
-
-
-pub fn get_res_dtor<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                              did: ast::DefId,
-                              t: Ty<'tcx>,
-                              parent_id: ast::DefId,
-                              substs: &Substs<'tcx>)
-                              -> ValueRef {
-    let _icx = push_ctxt("trans_res_dtor");
-    let did = inline::maybe_instantiate_inline(ccx, did);
-
-    if !substs.types.is_empty() {
-        assert_eq!(did.krate, ast::LOCAL_CRATE);
-
-        // Since we're in trans we don't care for any region parameters
-        let substs = ccx.tcx().mk_substs(Substs::erased(substs.types.clone()));
-
-        let (val, _, _) = monomorphize::monomorphic_fn(ccx, did, substs, None);
-
-        val
-    } else if did.krate == ast::LOCAL_CRATE {
-        get_item_val(ccx, did.node)
-    } else {
-        let tcx = ccx.tcx();
-        let name = csearch::get_symbol(&ccx.sess().cstore, did);
-        let class_ty = ty::lookup_item_type(tcx, parent_id).ty.subst(tcx, substs);
-        let llty = type_of_dtor(ccx, class_ty);
-        let dtor_ty = ty::mk_ctor_fn(ccx.tcx(),
-                                     did,
-                                     &[glue::get_drop_glue_type(ccx, t)],
-                                     ty::mk_nil(ccx.tcx()));
-        get_extern_fn(ccx,
-                      &mut *ccx.externs().borrow_mut(),
-                      &name[..],
-                      llvm::CCallConv,
-                      llty,
-                      dtor_ty)
-    }
-}
 
 pub fn bin_op_to_icmp_predicate(ccx: &CrateContext, op: ast::BinOp_, signed: bool)
                                 -> llvm::IntPredicate {
@@ -898,7 +666,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);
-                    set_llvm_fn_attrs(ccx, &attrs, llfn);
+                    attributes::from_fn_attrs(ccx, &attrs, llfn);
                     llfn
                 }
             }
@@ -920,7 +688,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 => {
@@ -1692,9 +1460,9 @@ pub fn build_return_block<'blk, 'tcx>(fcx: &FunctionContext<'blk, 'tcx>,
     }
 }
 
-// trans_closure: Builds an LLVM function out of a source function.
-// If the function closes over its environment a closure will be
-// returned.
+/// Builds an LLVM function out of a source function.
+///
+/// If the function closes over its environment a closure will be returned.
 pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                    decl: &ast::FnDecl,
                                    body: &ast::Block,
@@ -1708,7 +1476,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1);
 
     let _icx = push_ctxt("trans_closure");
-    set_uwtable(llfndecl);
+    attributes::emit_uwtable(llfndecl, true);
 
     debug!("trans_closure(..., param_substs={})",
            param_substs.repr(ccx.tcx()));
@@ -1827,8 +1595,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     finish_fn(&fcx, bcx, output_type, ret_debug_loc);
 }
 
-// trans_fn: creates an LLVM function corresponding to a source language
-// function.
+/// Creates an LLVM function corresponding to a source language function.
 pub fn trans_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                           decl: &ast::FnDecl,
                           body: &ast::Block,
@@ -1842,15 +1609,7 @@ pub fn trans_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     let fn_ty = ty::node_id_to_type(ccx.tcx(), id);
     let output_type = ty::erase_late_bound_regions(ccx.tcx(), &ty::ty_fn_ret(fn_ty));
     let abi = ty::ty_fn_abi(fn_ty);
-    trans_closure(ccx,
-                  decl,
-                  body,
-                  llfndecl,
-                  param_substs,
-                  id,
-                  attrs,
-                  output_type,
-                  abi,
+    trans_closure(ccx, decl, body, llfndecl, param_substs, id, attrs, output_type, abi,
                   closure::ClosureEnv::NotClosure);
 }
 
@@ -2195,27 +1954,24 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
                 let llfn = get_item_val(ccx, item.id);
                 let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
                 if abi != Rust {
-                    foreign::trans_rust_fn_with_foreign_abi(ccx,
-                                                            &**decl,
-                                                            &**body,
-                                                            &item.attrs,
-                                                            llfn,
-                                                            empty_substs,
-                                                            item.id,
-                                                            None);
+                    foreign::trans_rust_fn_with_foreign_abi(ccx, &**decl, &**body, &item.attrs,
+                                                            llfn, empty_substs, item.id, None);
                 } else {
-                    trans_fn(ccx,
-                             &**decl,
-                             &**body,
-                             llfn,
-                             empty_substs,
-                             item.id,
-                             &item.attrs);
+                    trans_fn(ccx, &**decl, &**body, llfn, empty_substs, item.id, &item.attrs);
                 }
-                update_linkage(ccx,
-                               llfn,
-                               Some(item.id),
+                update_linkage(ccx, llfn, Some(item.id),
                                if is_origin { OriginalTranslation } else { InlinedCopy });
+
+                if is_entry_fn(ccx.sess(), item.id) {
+                    create_entry_wrapper(ccx, item.span, llfn);
+                    // check for the #[rustc_error] annotation, which forces an
+                    // error in trans. This is used to write compile-fail tests
+                    // that actually test that compilation succeeds without
+                    // reporting an error.
+                    if ty::has_attr(ccx.tcx(), local_def(item.id), "rustc_error") {
+                        ccx.tcx().sess.span_fatal(item.span, "compilation successful");
+                    }
+                }
             }
         }
 
@@ -2251,8 +2007,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
           let mut v = TransItemVisitor{ ccx: ccx };
           v.visit_expr(&**expr);
 
-          consts::trans_static(ccx, m, item.id);
-          let g = get_item_val(ccx, item.id);
+          let g = consts::trans_static(ccx, m, item.id);
           update_linkage(ccx, g, Some(item.id), OriginalTranslation);
 
           // Do static_assert checking. It can't really be done much earlier
@@ -2304,7 +2059,25 @@ pub fn trans_mod(ccx: &CrateContext, m: &ast::Mod) {
     }
 }
 
-fn finish_register_fn(ccx: &CrateContext, sp: Span, sym: String, node_id: ast::NodeId,
+
+// 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: String,
+                          node_id: ast::NodeId,
+                      cc: llvm::CallConv,
+                          llfty: Type) -> ValueRef {
+    debug!("register_fn_llvmty id={} sym={}", node_id, sym);
+
+    let llfn = declare::define_fn(ccx, &sym[..], cc, llfty,
+                                   ty::FnConverging(ty::mk_nil(ccx.tcx()))).unwrap_or_else(||{
+        ccx.sess().span_fatal(sp, &format!("symbol `{}` is already defined", sym));
+    });
+    finish_register_fn(ccx, sym, node_id, llfn);
+    llfn
+}
+
+fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId,
                       llfn: ValueRef) {
     ccx.item_symbols().borrow_mut().insert(node_id, sym);
 
@@ -2313,25 +2086,12 @@ fn finish_register_fn(ccx: &CrateContext, sp: Span, sym: String, node_id: ast::N
     // eh_personality functions need to be externally linkable.
     let def = ast_util::local_def(node_id);
     if ccx.tcx().lang_items.stack_exhausted() == Some(def) {
-        unset_split_stack(llfn);
+        attributes::split_stack(llfn, false);
         llvm::SetLinkage(llfn, llvm::ExternalLinkage);
     }
     if ccx.tcx().lang_items.eh_personality() == Some(def) {
         llvm::SetLinkage(llfn, llvm::ExternalLinkage);
     }
-
-
-    if is_entry_fn(ccx.sess(), node_id) {
-        // check for the #[rustc_error] annotation, which forces an
-        // error in trans. This is used to write compile-fail tests
-        // that actually test that compilation succeeds without
-        // reporting an error.
-        if ty::has_attr(ccx.tcx(), local_def(node_id), "rustc_error") {
-            ccx.tcx().sess.span_fatal(sp, "compilation successful");
-        }
-
-        create_entry_wrapper(ccx, sp, llfn);
-    }
 }
 
 fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
@@ -2350,196 +2110,10 @@ fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         ccx.sess().span_bug(sp, "expected bare rust function")
     }
 
-    let llfn = decl_rust_fn(ccx, node_type, &sym[..]);
-    finish_register_fn(ccx, sp, sym, node_id, llfn);
-    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: String,
-                          node_id: ast::NodeId,
-                          cc: llvm::CallConv,
-                          llfty: Type) -> ValueRef {
-    debug!("register_fn_llvmty id={} sym={}", node_id, sym);
-
-    let llfn = decl_fn(ccx,
-                       &sym[..],
-                       cc,
-                       llfty,
-                       ty::FnConverging(ty::mk_nil(ccx.tcx())));
-    finish_register_fn(ccx, sp, sym, node_id, llfn);
+    let llfn = declare::define_rust_fn(ccx, &sym[..], node_type).unwrap_or_else(||{
+        ccx.sess().span_fatal(sp, &format!("symbol `{}` is already defined", sym));
+    });
+    finish_register_fn(ccx, sym, node_id, llfn);
     llfn
 }
 
@@ -2550,27 +2124,35 @@ pub fn is_entry_fn(sess: &Session, node_id: ast::NodeId) -> bool {
     }
 }
 
-// Create a _rust_main(args: ~[str]) function which will be called from the
-// runtime rust_start function
+/// Create the `main` function which will initialise the rust runtime and call users’ main
+/// function.
 pub fn create_entry_wrapper(ccx: &CrateContext,
-                           _sp: Span,
+                           sp: Span,
                            main_llfn: ValueRef) {
     let et = ccx.sess().entry_type.get().unwrap();
     match et {
         config::EntryMain => {
-            create_entry_fn(ccx, main_llfn, true);
+            create_entry_fn(ccx, sp, main_llfn, true);
         }
-        config::EntryStart => create_entry_fn(ccx, main_llfn, false),
+        config::EntryStart => create_entry_fn(ccx, sp, main_llfn, false),
         config::EntryNone => {}    // Do nothing.
     }
 
     fn create_entry_fn(ccx: &CrateContext,
+                       sp: Span,
                        rust_main: ValueRef,
                        use_start_lang_item: bool) {
         let llfty = Type::func(&[ccx.int_type(), Type::i8p(ccx).ptr_to()],
                                &ccx.int_type());
 
-        let llfn = decl_cdecl_fn(ccx, "main", llfty, ty::mk_nil(ccx.tcx()));
+        let llfn = declare::define_cfn(ccx, "main", llfty,
+                                       ty::mk_nil(ccx.tcx())).unwrap_or_else(||{
+            ccx.sess().span_err(sp, "entry symbol `main` defined multiple times");
+            // FIXME: We should be smart and show a better diagnostic here.
+            ccx.sess().help("did you use #[no_mangle] on `fn main`? Use #[start] instead");
+            ccx.sess().abort_if_errors();
+            panic!();
+        });
 
         // FIXME: #16581: Marking a symbol in the executable with `dllexport`
         // linkage forces MinGW's linker to output a `.reloc` section for ASLR
@@ -2645,10 +2227,9 @@ fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, id: ast::NodeId,
         None => {}
     }
 
-    match attr::first_attr_value_str_by_name(attrs, "export_name") {
+    match attr::find_export_name_attr(ccx.sess().diagnostic(), attrs) {
         // Use provided name
         Some(name) => name.to_string(),
-
         _ => ccx.tcx().map.with_path(id, |path| {
             if attr::contains_name(attrs, "no_mangle") {
                 // Don't mangle
@@ -2707,14 +2288,15 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
                         } else {
                             llvm::LLVMTypeOf(v)
                         };
-                        if contains_null(&sym[..]) {
-                            ccx.sess().fatal(
-                                &format!("Illegal null byte in export_name \
-                                         value: `{}`", sym));
-                        }
-                        let buf = CString::new(sym.clone()).unwrap();
-                        let g = llvm::LLVMAddGlobal(ccx.llmod(), llty,
-                                                    buf.as_ptr());
+
+                        // FIXME(nagisa): probably should be declare_global, because no definition
+                        // is happening here, but we depend on it being defined here from
+                        // const::trans_static. This all logic should be replaced.
+                        let g = declare::define_global(ccx, &sym[..],
+                                                       Type::from_ref(llty)).unwrap_or_else(||{
+                            ccx.sess().span_fatal(i.span, &format!("symbol `{}` is already defined",
+                                                                   sym))
+                        });
 
                         if attr::contains_name(&i.attrs,
                                                "thread_local") {
@@ -2730,12 +2312,9 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
                     let llfn = if abi == Rust {
                         register_fn(ccx, i.span, sym, i.id, ty)
                     } else {
-                        foreign::register_rust_fn_with_foreign_abi(ccx,
-                                                                   i.span,
-                                                                   sym,
-                                                                   i.id)
+                        foreign::register_rust_fn_with_foreign_abi(ccx, i.span, sym, i.id)
                     };
-                    set_llvm_fn_attrs(ccx, &i.attrs, llfn);
+                    attributes::from_fn_attrs(ccx, &i.attrs, llfn);
                     llfn
                 }
 
@@ -2796,7 +2375,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);
-                    set_llvm_fn_attrs(ccx, &ni.attrs, llfn);
+                    attributes::from_fn_attrs(ccx, &ni.attrs, llfn);
                     llfn
                 }
                 ast::ForeignItemStatic(..) => {
@@ -2828,7 +2407,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
                 }
                 _ => ccx.sess().bug("NodeVariant, shouldn't happen")
             };
-            set_inline_hint(llfn);
+            attributes::inline(llfn, attributes::InlineAttr::Hint);
             llfn
         }
 
@@ -2850,7 +2429,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);
-            set_inline_hint(llfn);
+            attributes::inline(llfn, attributes::InlineAttr::Hint);
             llfn
         }
 
@@ -2885,7 +2464,7 @@ fn register_method(ccx: &CrateContext, id: ast::NodeId,
         } else {
             foreign::register_rust_fn_with_foreign_abi(ccx, span, sym, id)
         };
-        set_llvm_fn_attrs(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/callee.rs b/src/librustc_trans/trans/callee.rs
index 9eb46d3ff54..604f185f396 100644
--- a/src/librustc_trans/trans/callee.rs
+++ b/src/librustc_trans/trans/callee.rs
@@ -41,6 +41,7 @@ use trans::common::{self, Block, Result, NodeIdAndSpan, ExprId, CrateContext,
 use trans::consts;
 use trans::datum::*;
 use trans::debuginfo::{DebugLoc, ToDebugLoc};
+use trans::declare;
 use trans::expr;
 use trans::glue;
 use trans::inline;
@@ -326,13 +327,9 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
     debug!("tuple_fn_ty: {}", tuple_fn_ty.repr(tcx));
 
     //
-    let function_name =
-        link::mangle_internal_name_by_type_and_seq(ccx, bare_fn_ty,
-                                                   "fn_pointer_shim");
-    let llfn =
-        decl_internal_rust_fn(ccx,
-                              tuple_fn_ty,
-                              &function_name[..]);
+    let function_name = link::mangle_internal_name_by_type_and_seq(ccx, bare_fn_ty,
+                                                                   "fn_pointer_shim");
+    let llfn = declare::declare_internal_rust_fn(ccx, &function_name[..], tuple_fn_ty);
 
     //
     let empty_substs = tcx.mk_substs(Substs::trans_empty());
diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs
index 19891e93072..61af5bfaef8 100644
--- a/src/librustc_trans/trans/cleanup.rs
+++ b/src/librustc_trans/trans/cleanup.rs
@@ -126,6 +126,7 @@ use trans::callee;
 use trans::common;
 use trans::common::{Block, FunctionContext, ExprId, NodeIdAndSpan};
 use trans::debuginfo::{DebugLoc, ToDebugLoc};
+use trans::declare;
 use trans::glue;
 use middle::region;
 use trans::type_::Type;
@@ -844,10 +845,8 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
                     Some(llpersonality) => llpersonality,
                     None => {
                         let fty = Type::variadic_func(&[], &Type::i32(self.ccx));
-                        let f = base::decl_cdecl_fn(self.ccx,
-                                                    "rust_eh_personality",
-                                                    fty,
-                                                    self.ccx.tcx().types.i32);
+                        let f = declare::declare_cfn(self.ccx, "rust_eh_personality", fty,
+                                                     self.ccx.tcx().types.i32);
                         *personality = Some(f);
                         f
                     }
diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs
index c1aade3663e..eb4acec2551 100644
--- a/src/librustc_trans/trans/closure.rs
+++ b/src/librustc_trans/trans/closure.rs
@@ -13,6 +13,7 @@ use back::link::{self, mangle_internal_name_by_path_and_seq};
 use llvm::{ValueRef, get_param};
 use middle::mem_categorization::Typer;
 use trans::adt;
+use trans::attributes;
 use trans::base::*;
 use trans::build::*;
 use trans::callee::{self, ArgVals, Callee, TraitItem, MethodData};
@@ -20,6 +21,7 @@ use trans::cleanup::{CleanupMethods, CustomScope, ScopeId};
 use trans::common::*;
 use trans::datum::{self, Datum, rvalue_scratch_datum, Rvalue, ByValue};
 use trans::debuginfo::{self, DebugLoc};
+use trans::declare;
 use trans::expr;
 use trans::monomorphize::{self, MonoId};
 use trans::type_of::*;
@@ -161,10 +163,14 @@ pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tc
         mangle_internal_name_by_path_and_seq(path, "closure")
     });
 
-    let llfn = decl_internal_rust_fn(ccx, function_type, &symbol[..]);
+    // Currently there’s only a single user of get_or_create_declaration_if_closure and it
+    // unconditionally defines the function, therefore we use define_* here.
+    let llfn = declare::define_internal_rust_fn(ccx, &symbol[..], function_type).unwrap_or_else(||{
+        ccx.sess().bug(&format!("symbol `{}` already defined", symbol));
+    });
 
     // set an inline hint for all closures
-    set_inline_hint(llfn);
+    attributes::inline(llfn, attributes::InlineAttr::Hint);
 
     debug!("get_or_create_declaration_if_closure(): inserting new \
             closure {:?} (type {})",
@@ -380,7 +386,10 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
 
     // Create the by-value helper.
     let function_name = link::mangle_internal_name_by_type_and_seq(ccx, llonce_fn_ty, "once_shim");
-    let lloncefn = decl_internal_rust_fn(ccx, llonce_fn_ty, &function_name);
+    let lloncefn = declare::define_internal_rust_fn(ccx, &function_name[..], llonce_fn_ty)
+        .unwrap_or_else(||{
+            ccx.sess().bug(&format!("symbol `{}` already defined", function_name));
+        });
 
     let sig = ty::erase_late_bound_regions(tcx, &llonce_bare_fn_ty.sig);
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index 2c8da2dc31c..168a294159d 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -31,6 +31,7 @@ use trans::cleanup;
 use trans::consts;
 use trans::datum;
 use trans::debuginfo::{self, DebugLoc};
+use trans::declare;
 use trans::machine;
 use trans::monomorphize;
 use trans::type_::Type;
@@ -871,9 +872,10 @@ pub fn C_cstr(cx: &CrateContext, s: InternedString, null_terminated: bool) -> Va
                                                 !null_terminated as Bool);
 
         let gsym = token::gensym("str");
-        let buf = CString::new(format!("str{}", gsym.usize()));
-        let buf = buf.unwrap();
-        let g = llvm::LLVMAddGlobal(cx.llmod(), val_ty(sc).to_ref(), buf.as_ptr());
+        let sym = format!("str{}", gsym.usize());
+        let g = declare::define_global(cx, &sym[..], val_ty(sc)).unwrap_or_else(||{
+            cx.sess().bug(&format!("symbol `{}` is already defined", sym));
+        });
         llvm::LLVMSetInitializer(g, sc);
         llvm::LLVMSetGlobalConstant(g, True);
         llvm::SetLinkage(g, llvm::InternalLinkage);
diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs
index c32cb28ec78..aff5f597bfd 100644
--- a/src/librustc_trans/trans/consts.rs
+++ b/src/librustc_trans/trans/consts.rs
@@ -25,6 +25,7 @@ use middle::const_eval::{const_int_checked_shr, const_uint_checked_shr};
 use trans::{adt, closure, debuginfo, expr, inline, machine};
 use trans::base::{self, push_ctxt};
 use trans::common::*;
+use trans::declare;
 use trans::monomorphize;
 use trans::type_::Type;
 use trans::type_of;
@@ -35,6 +36,7 @@ use util::ppaux::{Repr, ty_to_string};
 use std::iter::repeat;
 use libc::c_uint;
 use syntax::{ast, ast_util};
+use syntax::parse::token;
 use syntax::ptr::P;
 
 pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: &ast::Lit)
@@ -83,7 +85,7 @@ pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: &ast::Lit)
         ast::LitBool(b) => C_bool(cx, b),
         ast::LitStr(ref s, _) => C_str_slice(cx, (*s).clone()),
         ast::LitBinary(ref data) => {
-            addr_of(cx, C_bytes(cx, &data[..]), "binary", e.id)
+            addr_of(cx, C_bytes(cx, &data[..]), "binary")
         }
     }
 }
@@ -96,13 +98,16 @@ pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef {
 
 fn addr_of_mut(ccx: &CrateContext,
                cv: ValueRef,
-               kind: &str,
-               id: ast::NodeId)
+               kind: &str)
                -> ValueRef {
     unsafe {
-        let name = format!("{}{}\0", kind, id);
-        let gv = llvm::LLVMAddGlobal(ccx.llmod(), val_ty(cv).to_ref(),
-                                     name.as_ptr() as *const _);
+        // FIXME: this totally needs a better name generation scheme, perhaps a simple global
+        // counter? Also most other uses of gensym in trans.
+        let gsym = token::gensym("_");
+        let name = format!("{}{}", kind, gsym.usize());
+        let gv = declare::define_global(ccx, &name[..], val_ty(cv)).unwrap_or_else(||{
+            ccx.sess().bug(&format!("symbol `{}` is already defined", name));
+        });
         llvm::LLVMSetInitializer(gv, cv);
         SetLinkage(gv, InternalLinkage);
         SetUnnamedAddr(gv, true);
@@ -112,14 +117,13 @@ fn addr_of_mut(ccx: &CrateContext,
 
 pub fn addr_of(ccx: &CrateContext,
                cv: ValueRef,
-               kind: &str,
-               id: ast::NodeId)
+               kind: &str)
                -> ValueRef {
     match ccx.const_globals().borrow().get(&cv) {
         Some(&gv) => return gv,
         None => {}
     }
-    let gv = addr_of_mut(ccx, cv, kind, id);
+    let gv = addr_of_mut(ccx, cv, kind);
     unsafe {
         llvm::LLVMSetGlobalConstant(gv, True);
     }
@@ -233,7 +237,7 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         }
     };
 
-    let lvalue = addr_of(ccx, val, "const", expr.id);
+    let lvalue = addr_of(ccx, val, "const");
     ccx.const_values().borrow_mut().insert(key, lvalue);
     lvalue
 }
@@ -284,7 +288,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     if adj.autoderefs == 0 {
                         // Don't copy data to do a deref+ref
                         // (i.e., skip the last auto-deref).
-                        llconst = addr_of(cx, llconst, "autoref", e.id);
+                        llconst = addr_of(cx, llconst, "autoref");
                     } else {
                         // Seeing as we are deref'ing here and take a reference
                         // again to make the pointer part of the far pointer below,
@@ -312,7 +316,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 None => {}
                 Some(box ty::AutoUnsafe(_, None)) |
                 Some(box ty::AutoPtr(_, _, None)) => {
-                    llconst = addr_of(cx, llconst, "autoref", e.id);
+                    llconst = addr_of(cx, llconst, "autoref");
                 }
                 Some(box ty::AutoUnsize(ref k)) => {
                     let info =
@@ -711,12 +715,12 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                   // If this isn't the address of a static, then keep going through
                   // normal constant evaluation.
                   let (v, _) = const_expr(cx, &**sub, param_substs);
-                  addr_of(cx, v, "ref", e.id)
+                  addr_of(cx, v, "ref")
               }
           }
           ast::ExprAddrOf(ast::MutMutable, ref sub) => {
               let (v, _) = const_expr(cx, &**sub, param_substs);
-              addr_of_mut(cx, v, "ref_mut_slice", e.id)
+              addr_of_mut(cx, v, "ref_mut_slice")
           }
           ast::ExprTup(ref es) => {
               let repr = adt::represent_type(cx, ety);
@@ -862,7 +866,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     }
 }
 
-pub fn trans_static(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) {
+pub fn trans_static(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) -> ValueRef {
     unsafe {
         let _icx = push_ctxt("trans_static");
         let g = base::get_item_val(ccx, id);
@@ -888,6 +892,7 @@ pub fn trans_static(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) {
             }
         }
         debuginfo::create_global_var_metadata(ccx, id, g);
+        g
     }
 }
 
diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs
index 8919a386a45..e54962dc085 100644
--- a/src/librustc_trans/trans/context.rs
+++ b/src/librustc_trans/trans/context.rs
@@ -20,6 +20,7 @@ use trans::base;
 use trans::builder::Builder;
 use trans::common::{ExternMap,BuilderRef_res};
 use trans::debuginfo;
+use trans::declare;
 use trans::monomorphize::MonoId;
 use trans::type_::{Type, TypeNames};
 use middle::subst::Substs;
@@ -133,7 +134,6 @@ pub struct LocalCrateContext<'tcx> {
     llsizingtypes: RefCell<FnvHashMap<Ty<'tcx>, Type>>,
     adt_reprs: RefCell<FnvHashMap<Ty<'tcx>, Rc<adt::Repr<'tcx>>>>,
     type_hashcodes: RefCell<FnvHashMap<Ty<'tcx>, String>>,
-    all_llvm_symbols: RefCell<FnvHashSet<String>>,
     int_type: Type,
     opaque_vec_type: Type,
     builder: BuilderRef_res,
@@ -413,7 +413,6 @@ impl<'tcx> LocalCrateContext<'tcx> {
                 llsizingtypes: RefCell::new(FnvHashMap()),
                 adt_reprs: RefCell::new(FnvHashMap()),
                 type_hashcodes: RefCell::new(FnvHashMap()),
-                all_llvm_symbols: RefCell::new(FnvHashSet()),
                 int_type: Type::from_ref(ptr::null_mut()),
                 opaque_vec_type: Type::from_ref(ptr::null_mut()),
                 builder: BuilderRef_res(llvm::LLVMCreateBuilderInContext(llcx)),
@@ -653,10 +652,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         &self.local.type_hashcodes
     }
 
-    pub fn all_llvm_symbols<'a>(&'a self) -> &'a RefCell<FnvHashSet<String>> {
-        &self.local.all_llvm_symbols
-    }
-
     pub fn stats<'a>(&'a self) -> &'a Stats {
         &self.shared.stats
     }
@@ -743,17 +738,16 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option<ValueRef
     macro_rules! ifn {
         ($name:expr, fn() -> $ret:expr) => (
             if *key == $name {
-                let f = base::decl_cdecl_fn(
-                    ccx, $name, Type::func(&[], &$ret),
-                    ty::mk_nil(ccx.tcx()));
+                let f = declare::declare_cfn(ccx, $name, Type::func(&[], &$ret),
+                                             ty::mk_nil(ccx.tcx()));
                 ccx.intrinsics().borrow_mut().insert($name, f.clone());
                 return Some(f);
             }
         );
         ($name:expr, fn($($arg:expr),*) -> $ret:expr) => (
             if *key == $name {
-                let f = base::decl_cdecl_fn(ccx, $name,
-                                  Type::func(&[$($arg),*], &$ret), ty::mk_nil(ccx.tcx()));
+                let f = declare::declare_cfn(ccx, $name, Type::func(&[$($arg),*], &$ret),
+                                             ty::mk_nil(ccx.tcx()));
                 ccx.intrinsics().borrow_mut().insert($name, f.clone());
                 return Some(f);
             }
@@ -888,9 +882,9 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option<ValueRef
                 // The `if key == $name` is already in ifn!
                 ifn!($name, fn($($arg),*) -> $ret);
             } else if *key == $name {
-                let f = base::decl_cdecl_fn(ccx, stringify!($cname),
-                                      Type::func(&[$($arg),*], &$ret),
-                                      ty::mk_nil(ccx.tcx()));
+                let f = declare::declare_cfn(ccx, stringify!($cname),
+                                             Type::func(&[$($arg),*], &$ret),
+                                             ty::mk_nil(ccx.tcx()));
                 ccx.intrinsics().borrow_mut().insert($name, f.clone());
                 return Some(f);
             }
diff --git a/src/librustc_trans/trans/controlflow.rs b/src/librustc_trans/trans/controlflow.rs
index d1ecc1d6ddf..f544efe7401 100644
--- a/src/librustc_trans/trans/controlflow.rs
+++ b/src/librustc_trans/trans/controlflow.rs
@@ -416,8 +416,7 @@ pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let filename = C_str_slice(ccx, filename);
     let line = C_u32(ccx, loc.line as u32);
     let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false);
-    let expr_file_line = consts::addr_of(ccx, expr_file_line_const,
-                                         "panic_loc", call_info.id);
+    let expr_file_line = consts::addr_of(ccx, expr_file_line_const, "panic_loc");
     let args = vec!(expr_file_line);
     let did = langcall(bcx, Some(call_info.span), "", PanicFnLangItem);
     let bcx = callee::trans_lang_call(bcx,
@@ -449,8 +448,7 @@ pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let filename = C_str_slice(ccx,  filename);
     let line = C_u32(ccx, loc.line as u32);
     let file_line_const = C_struct(ccx, &[filename, line], false);
-    let file_line = consts::addr_of(ccx, file_line_const,
-                                    "panic_bounds_check_loc", call_info.id);
+    let file_line = consts::addr_of(ccx, file_line_const, "panic_bounds_check_loc");
     let args = vec!(file_line, index, len);
     let did = langcall(bcx, Some(call_info.span), "", PanicBoundsCheckFnLangItem);
     let bcx = callee::trans_lang_call(bcx,
diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs
index 9efb91e0c1d..a59616d09b1 100644
--- a/src/librustc_trans/trans/debuginfo.rs
+++ b/src/librustc_trans/trans/debuginfo.rs
@@ -196,8 +196,9 @@ use llvm::debuginfo::*;
 use metadata::csearch;
 use middle::subst::{self, Substs};
 use trans::{self, adt, machine, type_of};
-use trans::common::{self, NodeIdAndSpan, CrateContext, FunctionContext, Block,
-                    C_bytes, NormalizingClosureTyper};
+use trans::common::{self, NodeIdAndSpan, CrateContext, FunctionContext, Block, C_bytes,
+                    NormalizingClosureTyper};
+use trans::declare;
 use trans::_match::{BindingInfo, TrByCopy, TrByMove, TrByRef};
 use trans::monomorphize;
 use trans::type_::Type;
@@ -4067,7 +4068,7 @@ pub fn insert_reference_to_gdb_debug_scripts_section_global(ccx: &CrateContext)
 /// section.
 fn get_or_insert_gdb_debug_scripts_section_global(ccx: &CrateContext)
                                                   -> llvm::ValueRef {
-    let section_var_name = b"__rustc_debug_gdb_scripts_section__\0";
+    let section_var_name = "__rustc_debug_gdb_scripts_section__";
 
     let section_var = unsafe {
         llvm::LLVMGetNamedGlobal(ccx.llmod(),
@@ -4081,10 +4082,11 @@ fn get_or_insert_gdb_debug_scripts_section_global(ccx: &CrateContext)
         unsafe {
             let llvm_type = Type::array(&Type::i8(ccx),
                                         section_contents.len() as u64);
-            let section_var = llvm::LLVMAddGlobal(ccx.llmod(),
-                                                  llvm_type.to_ref(),
-                                                  section_var_name.as_ptr()
-                                                    as *const _);
+
+            let section_var = declare::define_global(ccx, section_var_name,
+                                                     llvm_type).unwrap_or_else(||{
+                ccx.sess().bug(&format!("symbol `{}` is already defined", section_var_name))
+            });
             llvm::LLVMSetSection(section_var, section_name.as_ptr() as *const _);
             llvm::LLVMSetInitializer(section_var, C_bytes(ccx, section_contents));
             llvm::LLVMSetGlobalConstant(section_var, llvm::True);
diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs
new file mode 100644
index 00000000000..9e7449f670f
--- /dev/null
+++ b/src/librustc_trans/trans/declare.rs
@@ -0,0 +1,261 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//! Declare various LLVM values.
+//!
+//! Prefer using functions and methods from this module rather than calling LLVM functions
+//! directly. These functions do some additional work to ensure we do the right thing given
+//! the preconceptions of trans.
+//!
+//! Some useful guidelines:
+//!
+//! * Use declare_* family of methods if you are declaring, but are not interested in defining the
+//! ValueRef they return.
+//! * Use define_* family of methods when you might be defining the ValueRef.
+//! * When in doubt, define.
+use llvm::{self, ValueRef};
+use middle::ty::{self, ClosureTyper};
+use syntax::abi;
+use trans::attributes;
+use trans::base;
+use trans::common;
+use trans::context::CrateContext;
+use trans::monomorphize;
+use trans::type_::Type;
+use trans::type_of;
+use util::ppaux::Repr;
+
+use std::ffi::CString;
+use libc::c_uint;
+
+
+/// Declare a global value.
+///
+/// If there’s a value with the same name already declared, the function will return its ValueRef
+/// instead.
+pub fn declare_global(ccx: &CrateContext, name: &str, ty: Type) -> llvm::ValueRef {
+    debug!("declare_global(name={:?})", name);
+    let namebuf = CString::new(name).unwrap_or_else(|_|{
+        ccx.sess().bug(&format!("name {:?} contains an interior null byte", name))
+    });
+    unsafe {
+        llvm::LLVMGetOrInsertGlobal(ccx.llmod(), namebuf.as_ptr(), ty.to_ref())
+    }
+}
+
+
+/// Declare a function.
+///
+/// For rust functions use `declare_rust_fn` instead.
+///
+/// If there’s a value with the same name already declared, the function will update the
+/// declaration and return existing ValueRef instead.
+pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty: Type,
+                  output: ty::FnOutput) -> ValueRef {
+    debug!("declare_fn(name={:?})", name);
+    let namebuf = CString::new(name).unwrap_or_else(|_|{
+        ccx.sess().bug(&format!("name {:?} contains an interior null byte", name))
+    });
+    let llfn = unsafe {
+        llvm::LLVMGetOrInsertFunction(ccx.llmod(), namebuf.as_ptr(), ty.to_ref())
+    };
+
+    llvm::SetFunctionCallConv(llfn, callconv);
+    // Function addresses in Rust are never significant, allowing functions to be merged.
+    llvm::SetUnnamedAddr(llfn, true);
+
+    if output == ty::FnDiverging {
+        llvm::SetFunctionAttribute(llfn, llvm::NoReturnAttribute);
+    }
+
+    if ccx.tcx().sess.opts.cg.no_redzone
+        .unwrap_or(ccx.tcx().sess.target.target.options.disable_redzone) {
+        llvm::SetFunctionAttribute(llfn, llvm::NoRedZoneAttribute)
+    }
+
+    if ccx.is_split_stack_supported() && !ccx.sess().opts.cg.no_stack_check {
+        attributes::split_stack(llfn, true);
+    }
+    llfn
+}
+
+
+/// Declare a C ABI function.
+///
+/// Only use this for foreign function ABIs and glue. For Rust functions use `declare_rust_fn`
+/// instead.
+///
+/// If there’s a value with the same name already declared, the function will update the
+/// declaration and return existing ValueRef instead.
+pub fn declare_cfn(ccx: &CrateContext, name: &str, fn_type: Type, output: ty::Ty) -> ValueRef {
+    declare_fn(ccx, name, llvm::CCallConv, fn_type, ty::FnConverging(output))
+}
+
+
+/// Declare a Rust function.
+///
+/// If there’s a value with the same name already declared, the function will update the
+/// declaration and return existing ValueRef instead.
+pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
+                                 fn_type: ty::Ty<'tcx>) -> ValueRef {
+    debug!("declare_rust_fn(name={:?}, fn_type={})", name, fn_type.repr(ccx.tcx()));
+    let fn_type = monomorphize::normalize_associated_type(ccx.tcx(), &fn_type);
+    debug!("declare_rust_fn (after normalised associated types) fn_type={}",
+           fn_type.repr(ccx.tcx()));
+
+    let function_type; // placeholder so that the memory ownership works out ok
+    let (sig, abi, env) = 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);
+            let llenvironment_type = type_of::type_of_explicit_arg(ccx, self_type);
+            debug!("declare_rust_fn function_type={} self_type={}",
+                   function_type.repr(ccx.tcx()), self_type.repr(ccx.tcx()));
+            (&function_type.sig, abi::RustCall, Some(llenvironment_type))
+        }
+        _ => ccx.sess().bug("expected closure or fn")
+    };
+
+    let sig = ty::Binder(ty::erase_late_bound_regions(ccx.tcx(), sig));
+    debug!("declare_rust_fn (after region erasure) sig={}", sig.repr(ccx.tcx()));
+    let llfty = type_of::type_of_rust_fn(ccx, env, &sig, abi);
+    debug!("declare_rust_fn llfty={}", ccx.tn().type_to_string(llfty));
+
+    // it is ok to directly access sig.0.output because we erased all late-bound-regions above
+    let llfn = declare_fn(ccx, name, llvm::CCallConv, llfty, sig.0.output);
+    attributes::from_fn_type(ccx, fn_type).apply_llfn(llfn);
+    llfn
+}
+
+
+/// Declare a Rust function with internal linkage.
+///
+/// If there’s a value with the same name already declared, the function will update the
+/// declaration and return existing ValueRef instead.
+pub fn declare_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
+                                          fn_type: ty::Ty<'tcx>) -> ValueRef {
+    let llfn = declare_rust_fn(ccx, name, fn_type);
+    llvm::SetLinkage(llfn, llvm::InternalLinkage);
+    llfn
+}
+
+
+/// Declare a global with an intention to define it.
+///
+/// Use this function when you intend to define a global. This function will return None if the
+/// name already has a definition associated with it. In that case an error should be reported to
+/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or
+/// #[export_name] attributes).
+pub fn define_global(ccx: &CrateContext, name: &str, ty: Type) -> Option<ValueRef> {
+    if get_defined_value(ccx, name).is_some() {
+        None
+    } else {
+        Some(declare_global(ccx, name, ty))
+    }
+}
+
+
+/// Declare a function with an intention to define it.
+///
+/// For rust functions use `define_rust_fn` instead.
+///
+/// Use this function when you intend to define a function. This function will return None if the
+/// name already has a definition associated with it. In that case an error should be reported to
+/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or
+/// #[export_name] attributes).
+pub fn define_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, fn_type: Type,
+                 output: ty::FnOutput) -> Option<ValueRef> {
+    if get_defined_value(ccx, name).is_some() {
+        None
+    } else {
+        Some(declare_fn(ccx, name, callconv, fn_type, output))
+    }
+}
+
+
+/// Declare a C ABI function with an intention to define it.
+///
+/// Use this function when you intend to define a function. This function will return None if the
+/// name already has a definition associated with it. In that case an error should be reported to
+/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or
+/// #[export_name] attributes).
+///
+/// Only use this for foreign function ABIs and glue. For Rust functions use `declare_rust_fn`
+/// instead.
+pub fn define_cfn(ccx: &CrateContext, name: &str, fn_type: Type,
+                  output: ty::Ty) -> Option<ValueRef> {
+    if get_defined_value(ccx, name).is_some() {
+        None
+    } else {
+        Some(declare_cfn(ccx, name, fn_type, output))
+    }
+}
+
+
+/// Declare a Rust function with an intention to define it.
+///
+/// Use this function when you intend to define a function. This function will return None if the
+/// name already has a definition associated with it. In that case an error should be reported to
+/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or
+/// #[export_name] attributes).
+pub fn define_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
+                                fn_type: ty::Ty<'tcx>) -> Option<ValueRef> {
+    if get_defined_value(ccx, name).is_some() {
+        None
+    } else {
+        Some(declare_rust_fn(ccx, name, fn_type))
+    }
+}
+
+
+/// Declare a Rust function with an intention to define it.
+///
+/// Use this function when you intend to define a function. This function will return None if the
+/// name already has a definition associated with it. In that case an error should be reported to
+/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or
+/// #[export_name] attributes).
+pub fn define_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
+                                         fn_type: ty::Ty<'tcx>) -> Option<ValueRef> {
+    if get_defined_value(ccx, name).is_some() {
+        None
+    } else {
+        Some(declare_internal_rust_fn(ccx, name, fn_type))
+    }
+}
+
+
+/// Get defined or externally defined (AvailableExternally linkage) value by name.
+fn get_defined_value(ccx: &CrateContext, name: &str) -> Option<ValueRef> {
+    debug!("get_defined_value(name={:?})", name);
+    let namebuf = CString::new(name).unwrap_or_else(|_|{
+        ccx.sess().bug(&format!("name {:?} contains an interior null byte", name))
+    });
+    let val = unsafe { llvm::LLVMGetNamedValue(ccx.llmod(), namebuf.as_ptr()) };
+    if val.is_null() {
+        debug!("get_defined_value: {:?} value is null", name);
+        None
+    } else {
+        let (declaration, aext_link) = unsafe {
+            let linkage = llvm::LLVMGetLinkage(val);
+            (llvm::LLVMIsDeclaration(val) != 0,
+             linkage == llvm::AvailableExternallyLinkage as c_uint)
+        };
+        debug!("get_defined_value: found {:?} value (declaration: {}, aext_link: {})", name,
+               declaration, aext_link);
+        if !declaration || aext_link {
+            Some(val)
+        } else {
+            None
+        }
+    }
+}
diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs
index e87a5865df0..8f3a51a5007 100644
--- a/src/librustc_trans/trans/foreign.rs
+++ b/src/librustc_trans/trans/foreign.rs
@@ -13,12 +13,14 @@ use back::link;
 use llvm::{ValueRef, CallConv, get_param};
 use llvm;
 use middle::weak_lang_items;
+use trans::attributes;
 use trans::base::{llvm_linkage_by_name, push_ctxt};
 use trans::base;
 use trans::build::*;
 use trans::cabi;
 use trans::common::*;
 use trans::debuginfo::DebugLoc;
+use trans::declare;
 use trans::machine;
 use trans::monomorphize;
 use trans::type_::Type;
@@ -27,7 +29,6 @@ use trans::type_of;
 use middle::ty::{self, Ty};
 use middle::subst::Substs;
 
-use std::ffi::CString;
 use std::cmp;
 use libc::c_uint;
 use syntax::abi::{Cdecl, Aapcs, C, Win64, Abi};
@@ -135,9 +136,7 @@ pub fn register_static(ccx: &CrateContext,
             };
             unsafe {
                 // Declare a symbol `foo` with the desired linkage.
-                let buf = CString::new(ident.as_bytes()).unwrap();
-                let g1 = llvm::LLVMAddGlobal(ccx.llmod(), llty2.to_ref(),
-                                             buf.as_ptr());
+                let g1 = declare::declare_global(ccx, &ident[..], llty2);
                 llvm::SetLinkage(g1, linkage);
 
                 // Declare an internal global `extern_with_linkage_foo` which
@@ -148,22 +147,37 @@ pub fn register_static(ccx: &CrateContext,
                 // zero.
                 let mut real_name = "_rust_extern_with_linkage_".to_string();
                 real_name.push_str(&ident);
-                let real_name = CString::new(real_name).unwrap();
-                let g2 = llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(),
-                                             real_name.as_ptr());
+                let g2 = declare::define_global(ccx, &real_name[..], llty).unwrap_or_else(||{
+                    ccx.sess().span_fatal(foreign_item.span,
+                                          &format!("symbol `{}` is already defined", ident))
+                });
                 llvm::SetLinkage(g2, llvm::InternalLinkage);
                 llvm::LLVMSetInitializer(g2, g1);
                 g2
             }
         }
-        None => unsafe {
-            // Generate an external declaration.
-            let buf = CString::new(ident.as_bytes()).unwrap();
-            llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(), buf.as_ptr())
-        }
+        None => // Generate an external declaration.
+            declare::declare_global(ccx, &ident[..], llty),
     }
 }
 
+// only use this for foreign function ABIs and glue, use `get_extern_rust_fn` for Rust functions
+pub fn get_extern_fn(ccx: &CrateContext,
+                     externs: &mut ExternMap,
+                     name: &str,
+                     cc: llvm::CallConv,
+                     ty: Type,
+                     output: Ty)
+                     -> ValueRef {
+    match externs.get(name) {
+        Some(n) => return *n,
+        None => {}
+    }
+    let f = declare::declare_fn(ccx, name, cc, ty, ty::FnConverging(output));
+    externs.insert(name.to_string(), f);
+    f
+}
+
 /// Registers a foreign function found in a library. Just adds a LLVM global.
 pub fn register_foreign_item_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                           abi: Abi, fty: Ty<'tcx>,
@@ -189,14 +203,8 @@ pub fn register_foreign_item_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     // 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(ccx,
-                                   &mut *ccx.externs().borrow_mut(),
-                                   name,
-                                   cc,
-                                   llfn_ty,
-                                   fty);
+    let llfn = get_extern_fn(ccx, &mut *ccx.externs().borrow_mut(), name, cc, llfn_ty, fty);
     add_argument_attributes(&tys, llfn);
-
     llfn
 }
 
@@ -471,7 +479,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
@@ -522,7 +530,8 @@ pub fn decl_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         }
         _ => panic!("expected bare fn in decl_rust_fn_with_foreign_abi")
     };
-    let llfn = base::decl_fn(ccx, name, cconv, llfn_ty, ty::FnConverging(ty::mk_nil(ccx.tcx())));
+    let llfn = declare::declare_fn(ccx, name, cconv, llfn_ty,
+                                   ty::FnConverging(ty::mk_nil(ccx.tcx())));
     add_argument_attributes(&tys, llfn);
     debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, llfn={})",
            ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn));
@@ -611,8 +620,10 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                ccx.tcx().map.path_to_string(id),
                id, t.repr(tcx));
 
-        let llfn = base::decl_internal_rust_fn(ccx, t, &ps[..]);
-        base::set_llvm_fn_attrs(ccx, attrs, llfn);
+        let llfn = declare::define_internal_rust_fn(ccx, &ps[..], t).unwrap_or_else(||{
+            ccx.sess().bug(&format!("symbol `{}` already defined", ps));
+        });
+        attributes::from_fn_attrs(ccx, attrs, llfn);
         base::trans_fn(ccx, decl, body, llfn, param_substs, id, &[]);
         llfn
     }
@@ -642,6 +653,11 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         //         return r;
         //     }
 
+        if llvm::LLVMCountBasicBlocks(llwrapfn) != 0 {
+            ccx.sess().bug("wrapping a function inside non-empty wrapper, most likely cause is \
+                           multiple functions being wrapped");
+        }
+
         let ptr = "the block\0".as_ptr();
         let the_block = llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llwrapfn,
                                                             ptr as *const _);
@@ -800,7 +816,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/glue.rs b/src/librustc_trans/trans/glue.rs
index 32b4d14177c..f974796e69c 100644
--- a/src/librustc_trans/trans/glue.rs
+++ b/src/librustc_trans/trans/glue.rs
@@ -15,11 +15,13 @@
 
 use back::abi;
 use back::link::*;
-use llvm::{ValueRef, get_param};
 use llvm;
+use llvm::{ValueRef, get_param};
+use metadata::csearch;
 use middle::lang_items::ExchangeFreeFnLangItem;
 use middle::subst;
 use middle::subst::{Subst, Substs};
+use middle::ty::{self, Ty};
 use trans::adt;
 use trans::adt::GetDtorType; // for tcx.dtor_type()
 use trans::base::*;
@@ -30,13 +32,16 @@ use trans::cleanup::CleanupMethods;
 use trans::common::*;
 use trans::datum;
 use trans::debuginfo::DebugLoc;
+use trans::declare;
 use trans::expr;
+use trans::foreign;
+use trans::inline;
 use trans::machine::*;
+use trans::monomorphize;
+use trans::type_of::{type_of, type_of_dtor, sizing_type_of, align_of};
 use trans::type_::Type;
-use trans::type_of::{type_of, sizing_type_of, align_of};
-use middle::ty::{self, Ty};
-use util::ppaux::{ty_to_short_str, Repr};
 use util::ppaux;
+use util::ppaux::{ty_to_short_str, Repr};
 
 use arena::TypedArena;
 use libc::c_uint;
@@ -178,14 +183,15 @@ pub fn get_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Val
     // To avoid infinite recursion, don't `make_drop_glue` until after we've
     // added the entry to the `drop_glues` cache.
     if let Some(old_sym) = ccx.available_drop_glues().borrow().get(&t) {
-        let llfn = decl_cdecl_fn(ccx, &old_sym, llfnty, ty::mk_nil(ccx.tcx()));
+        let llfn = declare::declare_cfn(ccx, &old_sym, llfnty, ty::mk_nil(ccx.tcx()));
         ccx.drop_glues().borrow_mut().insert(t, llfn);
         return llfn;
     };
 
     let fn_nm = mangle_internal_name_by_type_and_seq(ccx, t, "drop");
-    let llfn = decl_cdecl_fn(ccx, &fn_nm, llfnty, ty::mk_nil(ccx.tcx()));
-    note_unique_llvm_symbol(ccx, fn_nm.clone());
+    let llfn = declare::define_cfn(ccx, &fn_nm, llfnty, ty::mk_nil(ccx.tcx())).unwrap_or_else(||{
+       ccx.sess().bug(&format!("symbol `{}` already defined", fn_nm));
+    });
     ccx.available_drop_glues().borrow_mut().insert(t, fn_nm);
 
     let _s = StatRecorder::new(ccx, format!("drop {}", ty_to_short_str(ccx.tcx(), t)));
@@ -259,6 +265,40 @@ fn trans_struct_drop_flag<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
 
 }
 
+pub fn get_res_dtor<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                              did: ast::DefId,
+                              t: Ty<'tcx>,
+                              parent_id: ast::DefId,
+                              substs: &Substs<'tcx>)
+                              -> ValueRef {
+    let _icx = push_ctxt("trans_res_dtor");
+    let did = inline::maybe_instantiate_inline(ccx, did);
+
+    if !substs.types.is_empty() {
+        assert_eq!(did.krate, ast::LOCAL_CRATE);
+
+        // Since we're in trans we don't care for any region parameters
+        let substs = ccx.tcx().mk_substs(Substs::erased(substs.types.clone()));
+
+        let (val, _, _) = monomorphize::monomorphic_fn(ccx, did, substs, None);
+
+        val
+    } else if did.krate == ast::LOCAL_CRATE {
+        get_item_val(ccx, did.node)
+    } else {
+        let tcx = ccx.tcx();
+        let name = csearch::get_symbol(&ccx.sess().cstore, did);
+        let class_ty = ty::lookup_item_type(tcx, parent_id).ty.subst(tcx, substs);
+        let llty = type_of_dtor(ccx, class_ty);
+        let dtor_ty = ty::mk_ctor_fn(ccx.tcx(),
+                                     did,
+                                     &[get_drop_glue_type(ccx, t)],
+                                     ty::mk_nil(ccx.tcx()));
+        foreign::get_extern_fn(ccx, &mut *ccx.externs().borrow_mut(), &name[..], llvm::CCallConv,
+                               llty, dtor_ty)
+    }
+}
+
 fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                  t: Ty<'tcx>,
                                  v0: ValueRef,
diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs
index 190e44c9674..e2f965a95ff 100644
--- a/src/librustc_trans/trans/meth.rs
+++ b/src/librustc_trans/trans/meth.rs
@@ -28,6 +28,7 @@ use trans::common::*;
 use trans::consts;
 use trans::datum::*;
 use trans::debuginfo::DebugLoc;
+use trans::declare;
 use trans::expr::SaveIn;
 use trans::expr;
 use trans::glue;
@@ -590,10 +591,10 @@ pub fn trans_object_shim<'a, 'tcx>(
     //
     let shim_fn_ty = ty::mk_bare_fn(tcx, None, fty);
     let method_bare_fn_ty = ty::mk_bare_fn(tcx, None, method_ty);
-    let function_name =
-        link::mangle_internal_name_by_type_and_seq(ccx, shim_fn_ty, "object_shim");
-    let llfn =
-        decl_internal_rust_fn(ccx, shim_fn_ty, &function_name);
+    let function_name = link::mangle_internal_name_by_type_and_seq(ccx, shim_fn_ty, "object_shim");
+    let llfn = declare::define_internal_rust_fn(ccx, &function_name, shim_fn_ty).unwrap_or_else(||{
+        ccx.sess().bug(&format!("symbol `{}` already defined", function_name));
+    });
 
     let sig = ty::erase_late_bound_regions(ccx.tcx(), &fty.sig);
 
@@ -756,8 +757,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         C_uint(ccx, align)
     ].into_iter().chain(methods).collect();
 
-    let vtable = consts::addr_of(ccx, C_struct(ccx, &components, false),
-                                 "vtable", trait_ref.def_id().node);
+    let vtable = consts::addr_of(ccx, C_struct(ccx, &components, false), "vtable");
 
     ccx.vtables().borrow_mut().insert(trait_ref, vtable);
     vtable
diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs
index c7857d6a775..7d568ff90ea 100644
--- a/src/librustc_trans/trans/mod.rs
+++ b/src/librustc_trans/trans/mod.rs
@@ -19,43 +19,45 @@ pub use self::common::gensym_name;
 #[macro_use]
 mod macros;
 
-mod inline;
-mod monomorphize;
-mod controlflow;
-mod glue;
-mod datum;
-mod callee;
-mod expr;
-mod common;
-mod context;
-mod consts;
-mod type_of;
+mod adt;
+mod asm;
+mod attributes;
+mod base;
+mod basic_block;
 mod build;
 mod builder;
-mod base;
-mod _match;
-mod closure;
-mod tvec;
-mod meth;
 mod cabi;
+mod cabi_aarch64;
+mod cabi_arm;
+mod cabi_mips;
+mod cabi_powerpc;
 mod cabi_x86;
 mod cabi_x86_64;
 mod cabi_x86_win64;
-mod cabi_arm;
-mod cabi_aarch64;
-mod cabi_mips;
-mod cabi_powerpc;
-mod foreign;
-mod intrinsic;
-mod debuginfo;
-mod machine;
-mod adt;
-mod asm;
-mod type_;
-mod value;
-mod basic_block;
-mod llrepr;
+mod callee;
 mod cleanup;
+mod closure;
+mod common;
+mod consts;
+mod context;
+mod controlflow;
+mod datum;
+mod debuginfo;
+mod declare;
+mod expr;
+mod foreign;
+mod glue;
+mod inline;
+mod intrinsic;
+mod llrepr;
+mod machine;
+mod _match;
+mod meth;
+mod monomorphize;
+mod tvec;
+mod type_;
+mod type_of;
+mod value;
 
 #[derive(Copy, Clone)]
 pub struct ModuleTranslation {
diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs
index dcb21c5cd93..1c8d020494f 100644
--- a/src/librustc_trans/trans/monomorphize.rs
+++ b/src/librustc_trans/trans/monomorphize.rs
@@ -17,11 +17,12 @@ use middle::subst;
 use middle::subst::{Subst, Substs};
 use middle::traits;
 use middle::ty_fold::{TypeFolder, TypeFoldable};
-use trans::base::{set_llvm_fn_attrs, set_inline_hint};
+use trans::attributes;
 use trans::base::{trans_enum_variant, push_ctxt, get_item_val};
-use trans::base::{trans_fn, decl_internal_rust_fn};
+use trans::base::trans_fn;
 use trans::base;
 use trans::common::*;
+use trans::declare;
 use trans::foreign;
 use middle::ty::{self, HasProjectionTypes, Ty};
 use util::ppaux::Repr;
@@ -143,7 +144,10 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         let lldecl = if abi != abi::Rust {
             foreign::decl_rust_fn_with_foreign_abi(ccx, mono_ty, &s[..])
         } else {
-            decl_internal_rust_fn(ccx, mono_ty, &s[..])
+            // FIXME(nagisa): perhaps needs a more fine grained selection? See setup_lldecl below.
+            declare::define_internal_rust_fn(ccx, &s[..], mono_ty).unwrap_or_else(||{
+                ccx.sess().bug(&format!("symbol `{}` already defined", s));
+            })
         };
 
         ccx.monomorphized().borrow_mut().insert(hash_id.take().unwrap(), lldecl);
@@ -151,7 +155,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);
-        set_llvm_fn_attrs(ccx, attrs, lldecl);
+        attributes::from_fn_attrs(ccx, attrs, lldecl);
 
         let is_first = !ccx.available_monomorphizations().borrow().contains(&s);
         if is_first {
@@ -200,7 +204,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);
-            set_inline_hint(d);
+            attributes::inline(d, attributes::InlineAttr::Hint);
             match v.node.kind {
                 ast::TupleVariantKind(ref args) => {
                     trans_enum_variant(ccx,
@@ -259,7 +263,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         }
         ast_map::NodeStructCtor(struct_def) => {
             let d = mk_lldecl(abi::Rust);
-            set_inline_hint(d);
+            attributes::inline(d, attributes::InlineAttr::Hint);
             base::trans_tuple_struct(ccx,
                                      &struct_def.fields,
                                      struct_def.ctor_id.expect("ast-mapped tuple struct \
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 29b0716c817..755dd3bb458 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -282,6 +282,23 @@ pub fn find_crate_name(attrs: &[Attribute]) -> Option<InternedString> {
     first_attr_value_str_by_name(attrs, "crate_name")
 }
 
+/// Find the value of #[export_name=*] attribute and check its validity.
+pub fn find_export_name_attr(diag: &SpanHandler, attrs: &[Attribute]) -> Option<InternedString> {
+    attrs.iter().fold(None, |ia,attr| {
+        if attr.check_name("export_name") {
+            if let s@Some(_) = attr.value_str() {
+                s
+            } else {
+                diag.span_err(attr.span, "export_name attribute has invalid format");
+                diag.handler.help("use #[export_name=\"*\"]");
+                None
+            }
+        } else {
+            ia
+        }
+    })
+}
+
 #[derive(Copy, Clone, PartialEq)]
 pub enum InlineAttr {
     None,
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 9a87c03f1c4..31f75ae03b0 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -77,6 +77,11 @@ extern "C" void LLVMRustPrintPassTimings() {
   TimerGroup::printAll(OS);
 }
 
+extern "C" LLVMValueRef LLVMGetNamedValue(LLVMModuleRef M,
+                                          const char* Name) {
+    return wrap(unwrap(M)->getNamedValue(Name));
+}
+
 extern "C" LLVMValueRef LLVMGetOrInsertFunction(LLVMModuleRef M,
                                                 const char* Name,
                                                 LLVMTypeRef FunctionTy) {
@@ -84,6 +89,12 @@ extern "C" LLVMValueRef LLVMGetOrInsertFunction(LLVMModuleRef M,
                                              unwrap<FunctionType>(FunctionTy)));
 }
 
+extern "C" LLVMValueRef LLVMGetOrInsertGlobal(LLVMModuleRef M,
+                                              const char* Name,
+                                              LLVMTypeRef Ty) {
+  return wrap(unwrap(M)->getOrInsertGlobal(Name, unwrap(Ty)));
+}
+
 extern "C" LLVMTypeRef LLVMMetadataTypeInContext(LLVMContextRef C) {
   return wrap(Type::getMetadataTy(*unwrap(C)));
 }
diff --git a/src/test/compile-fail/dupe-symbols-1.rs b/src/test/compile-fail/dupe-symbols-1.rs
new file mode 100644
index 00000000000..9fa4eafcad0
--- /dev/null
+++ b/src/test/compile-fail/dupe-symbols-1.rs
@@ -0,0 +1,21 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+#![crate_type="rlib"]
+#![allow(warnings)]
+
+#[export_name="fail"]
+pub fn a() {
+}
+
+#[export_name="fail"]
+pub fn b() {
+//~^ symbol `fail` is already defined
+}
diff --git a/src/test/compile-fail/dupe-symbols-2.rs b/src/test/compile-fail/dupe-symbols-2.rs
new file mode 100644
index 00000000000..976a65589b8
--- /dev/null
+++ b/src/test/compile-fail/dupe-symbols-2.rs
@@ -0,0 +1,25 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+#![crate_type="rlib"]
+#![allow(warnings)]
+
+mod a {
+    #[no_mangle]
+    pub extern fn fail() {
+    }
+}
+
+mod b {
+    #[no_mangle]
+    pub extern fn fail() {
+    //~^ symbol `fail` is already defined
+    }
+}
diff --git a/src/test/compile-fail/dupe-symbols-3.rs b/src/test/compile-fail/dupe-symbols-3.rs
new file mode 100644
index 00000000000..98a61c33c58
--- /dev/null
+++ b/src/test/compile-fail/dupe-symbols-3.rs
@@ -0,0 +1,21 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+#![crate_type="rlib"]
+#![allow(warnings)]
+
+#[export_name="fail"]
+pub fn a() {
+}
+
+#[no_mangle]
+pub fn fail() {
+//~^ symbol `fail` is already defined
+}
diff --git a/src/test/compile-fail/dupe-symbols-4.rs b/src/test/compile-fail/dupe-symbols-4.rs
new file mode 100644
index 00000000000..9e730699d25
--- /dev/null
+++ b/src/test/compile-fail/dupe-symbols-4.rs
@@ -0,0 +1,31 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// error-pattern: symbol `fail` is already defined
+#![crate_type="rlib"]
+#![allow(warnings)]
+
+
+pub trait A {
+    fn fail(self);
+}
+
+struct B;
+struct C;
+
+impl A for B {
+    #[no_mangle]
+    fn fail(self) {}
+}
+
+impl A for C {
+    #[no_mangle]
+    fn fail(self) {}
+}
diff --git a/src/test/compile-fail/dupe-symbols-5.rs b/src/test/compile-fail/dupe-symbols-5.rs
new file mode 100644
index 00000000000..eb4b50d03ca
--- /dev/null
+++ b/src/test/compile-fail/dupe-symbols-5.rs
@@ -0,0 +1,20 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+#![crate_type="rlib"]
+#![allow(warnings)]
+
+#[export_name="fail"]
+static HELLO: u8 = 0;
+
+#[export_name="fail"]
+pub fn b() {
+//~^ symbol `fail` is already defined
+}
diff --git a/src/test/compile-fail/dupe-symbols-6.rs b/src/test/compile-fail/dupe-symbols-6.rs
new file mode 100644
index 00000000000..6f412d9a0de
--- /dev/null
+++ b/src/test/compile-fail/dupe-symbols-6.rs
@@ -0,0 +1,18 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+#![crate_type="rlib"]
+#![allow(warnings)]
+
+#[export_name="fail"]
+static HELLO: u8 = 0;
+
+#[export_name="fail"]
+static HELLO_TWICE: u16 = 0;
+//~^ symbol `fail` is already defined
diff --git a/src/test/compile-fail/dupe-symbols-7.rs b/src/test/compile-fail/dupe-symbols-7.rs
new file mode 100644
index 00000000000..c2880ba6f51
--- /dev/null
+++ b/src/test/compile-fail/dupe-symbols-7.rs
@@ -0,0 +1,15 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// error-pattern: entry symbol `main` defined multiple times
+#![allow(warnings)]
+
+#[no_mangle]
+fn main(){}
diff --git a/src/test/run-pass/issue-15562.rs b/src/test/run-pass/issue-15562.rs
index f1ef57e44b1..da1e15d826d 100644
--- a/src/test/run-pass/issue-15562.rs
+++ b/src/test/run-pass/issue-15562.rs
@@ -15,9 +15,6 @@
 extern crate issue_15562 as i;
 
 pub fn main() {
-    extern {
-        fn transmute();
-    }
     unsafe {
         transmute();
         i::transmute();