about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2013-03-13 22:25:28 -0400
committerBrian Anderson <banderson@mozilla.com>2013-03-29 18:36:20 -0700
commit6965fe4bceea836586bd8e7aa01a92a35b467f78 (patch)
treed95089f050cd67db2a5171a799763faa09f5b0a8
parentf864934f548be9f03d2c0512de8d7e908469e2ae (diff)
downloadrust-6965fe4bceea836586bd8e7aa01a92a35b467f78.tar.gz
rust-6965fe4bceea836586bd8e7aa01a92a35b467f78.zip
Add AbiSet and integrate it into the AST.
I believe this patch incorporates all expected syntax changes from extern
function reform (#3678). You can now write things like:

    extern "<abi>" fn foo(s: S) -> T { ... }
    extern "<abi>" mod { ... }
    extern "<abi>" fn(S) -> T

The ABI for foreign functions is taken from this syntax (rather than from an
annotation).  We support the full ABI specification I described on the mailing
list.  The correct ABI is chosen based on the target architecture.

Calls by pointer to C functions are not yet supported, and the Rust type of
crust fns is still *u8.
-rw-r--r--src/libcore/cast.rs2
-rw-r--r--src/libcore/libc.rs2
-rw-r--r--src/libcore/os.rs8
-rw-r--r--src/libcore/ptr.rs2
-rw-r--r--src/libcore/rt/thread_local_storage.rs2
-rw-r--r--src/libcore/stackwalk.rs2
-rw-r--r--src/libcore/sys.rs2
-rw-r--r--src/libcore/unstable/intrinsics.rs2
-rw-r--r--src/librustc/back/link.rs2
-rw-r--r--src/librustc/driver/driver.rs37
-rw-r--r--src/librustc/driver/session.rs6
-rw-r--r--src/librustc/front/config.rs2
-rw-r--r--src/librustc/front/intrinsic.rs2
-rw-r--r--src/librustc/front/test.rs6
-rw-r--r--src/librustc/metadata/creader.rs8
-rw-r--r--src/librustc/metadata/encoder.rs7
-rw-r--r--src/librustc/metadata/tydecode.rs39
-rw-r--r--src/librustc/metadata/tyencode.rs12
-rw-r--r--src/librustc/middle/lint.rs4
-rw-r--r--src/librustc/middle/resolve.rs4
-rw-r--r--src/librustc/middle/trans/base.rs27
-rw-r--r--src/librustc/middle/trans/callee.rs7
-rw-r--r--src/librustc/middle/trans/common.rs17
-rw-r--r--src/librustc/middle/trans/debuginfo.rs2
-rw-r--r--src/librustc/middle/trans/foreign.rs123
-rw-r--r--src/librustc/middle/trans/monomorphize.rs8
-rw-r--r--src/librustc/middle/trans/reachable.rs2
-rw-r--r--src/librustc/middle/trans/type_use.rs4
-rw-r--r--src/librustc/middle/ty.rs11
-rw-r--r--src/librustc/middle/typeck/astconv.rs7
-rw-r--r--src/librustc/middle/typeck/check/mod.rs23
-rw-r--r--src/librustc/middle/typeck/collect.rs15
-rw-r--r--src/librustc/middle/typeck/infer/coercion.rs23
-rw-r--r--src/librustc/middle/typeck/infer/combine.rs9
-rw-r--r--src/librustc/middle/typeck/infer/glb.rs6
-rw-r--r--src/librustc/middle/typeck/infer/lub.rs3
-rw-r--r--src/librustc/middle/typeck/infer/sub.rs3
-rw-r--r--src/librustc/middle/typeck/mod.rs2
-rw-r--r--src/librustc/util/ppaux.rs12
-rw-r--r--src/librustdoc/tystr_pass.rs2
-rw-r--r--src/librusti/rusti.rc2
-rw-r--r--src/librustpkg/rustpkg.rc2
-rw-r--r--src/libstd/arena.rs2
-rw-r--r--src/libstd/priority_queue.rs2
-rw-r--r--src/libsyntax/abi.rs427
-rw-r--r--src/libsyntax/ast.rs41
-rw-r--r--src/libsyntax/ast_map.rs11
-rw-r--r--src/libsyntax/ast_util.rs2
-rw-r--r--src/libsyntax/attr.rs21
-rw-r--r--src/libsyntax/ext/pipes/ast_builder.rs4
-rw-r--r--src/libsyntax/fold.rs7
-rw-r--r--src/libsyntax/parse/parser.rs113
-rw-r--r--src/libsyntax/print/pprust.rs64
-rw-r--r--src/libsyntax/syntax.rc1
-rw-r--r--src/libsyntax/visit.rs30
-rw-r--r--src/test/auxiliary/cci_intrinsic.rs6
-rw-r--r--src/test/compile-fail/block-coerce-no-2.rs2
-rw-r--r--src/test/compile-fail/main-wrong-type-2.rs2
-rw-r--r--src/test/compile-fail/main-wrong-type.rs2
-rw-r--r--src/test/compile-fail/missing-do.rs2
-rw-r--r--src/test/pretty/fn-types.rs2
-rw-r--r--src/test/run-pass/intrinsic-alignment.rs2
-rw-r--r--src/test/run-pass/intrinsic-atomics.rs6
-rw-r--r--src/test/run-pass/intrinsic-frame-address.rs2
-rw-r--r--src/test/run-pass/intrinsic-move-val.rs2
-rw-r--r--src/test/run-pass/intrinsics-integer.rs4
-rw-r--r--src/test/run-pass/intrinsics-math.rs4
-rw-r--r--src/test/run-pass/issue-2718.rs1
-rw-r--r--src/test/run-pass/morestack-address.rs2
-rw-r--r--src/test/run-pass/rec-align-u32.rs2
-rw-r--r--src/test/run-pass/rec-align-u64.rs2
-rw-r--r--src/test/run-pass/x86stdcall2.rs2
72 files changed, 879 insertions, 352 deletions
diff --git a/src/libcore/cast.rs b/src/libcore/cast.rs
index 4369d4be217..42464c848ef 100644
--- a/src/libcore/cast.rs
+++ b/src/libcore/cast.rs
@@ -13,7 +13,7 @@
 pub mod rusti {
     #[abi = "rust-intrinsic"]
     #[link_name = "rusti"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         fn forget<T>(+x: T);
         fn reinterpret_cast<T, U>(&&e: T) -> U;
     }
diff --git a/src/libcore/libc.rs b/src/libcore/libc.rs
index 084437354fb..79bb375d360 100644
--- a/src/libcore/libc.rs
+++ b/src/libcore/libc.rs
@@ -1617,7 +1617,7 @@ pub mod funcs {
             use libc::types::os::arch::extra::{HANDLE};
 
             #[abi = "stdcall"]
-            pub extern {
+            pub extern "stdcall" {
                 unsafe fn GetEnvironmentVariableW(n: LPCWSTR,
                                                   v: LPWSTR,
                                                   nsize: DWORD)
diff --git a/src/libcore/os.rs b/src/libcore/os.rs
index 7dbc1773994..4f1edf34494 100644
--- a/src/libcore/os.rs
+++ b/src/libcore/os.rs
@@ -942,7 +942,7 @@ pub fn errno() -> uint {
 
     #[link_name = "kernel32"]
     #[abi = "stdcall"]
-    extern {
+    extern "stdcall" {
         unsafe fn GetLastError() -> DWORD;
     }
 
@@ -1004,7 +1004,7 @@ pub fn last_os_error() -> ~str {
 
         #[link_name = "kernel32"]
         #[abi = "stdcall"]
-        extern {
+        extern "stdcall" {
             unsafe fn FormatMessageA(flags: DWORD, lpSrc: LPVOID,
                                      msgId: DWORD, langId: DWORD,
                                      buf: LPSTR, nsize: DWORD,
@@ -1118,7 +1118,7 @@ type LPCWSTR = *u16;
 #[cfg(windows)]
 #[link_name="kernel32"]
 #[abi="stdcall"]
-extern {
+extern "stdcall" {
     fn GetCommandLineW() -> LPCWSTR;
     fn LocalFree(ptr: *c_void);
 }
@@ -1126,7 +1126,7 @@ extern {
 #[cfg(windows)]
 #[link_name="shell32"]
 #[abi="stdcall"]
-extern {
+extern "stdcall" {
     fn CommandLineToArgvW(lpCmdLine: LPCWSTR, pNumArgs: *mut c_int) -> **u16;
 }
 
diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs
index fc95e320013..70bdb6f41d8 100644
--- a/src/libcore/ptr.rs
+++ b/src/libcore/ptr.rs
@@ -43,7 +43,7 @@ pub mod libc_ {
 
 pub mod rusti {
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         fn addr_of<T>(&&val: T) -> *T;
     }
 }
diff --git a/src/libcore/rt/thread_local_storage.rs b/src/libcore/rt/thread_local_storage.rs
index 76111328b3a..c8a9689bbc6 100644
--- a/src/libcore/rt/thread_local_storage.rs
+++ b/src/libcore/rt/thread_local_storage.rs
@@ -73,7 +73,7 @@ pub unsafe fn get(key: Key) -> *mut c_void {
 
 #[cfg(windows)]
 #[abi = "stdcall"]
-extern {
+extern "stdcall" {
        fn TlsAlloc() -> DWORD;
        fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
        fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID;
diff --git a/src/libcore/stackwalk.rs b/src/libcore/stackwalk.rs
index ceb7eb575f5..99bbc93056b 100644
--- a/src/libcore/stackwalk.rs
+++ b/src/libcore/stackwalk.rs
@@ -94,7 +94,7 @@ pub mod rustrt {
 
 pub mod rusti {
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         pub fn frame_address(f: &once fn(x: *u8));
     }
 }
diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs
index 338893d2388..52ca0204126 100644
--- a/src/libcore/sys.rs
+++ b/src/libcore/sys.rs
@@ -39,7 +39,7 @@ pub struct Closure {
 
 pub mod rusti {
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         fn get_tydesc<T>() -> *();
         fn size_of<T>() -> uint;
         fn pref_align_of<T>() -> uint;
diff --git a/src/libcore/unstable/intrinsics.rs b/src/libcore/unstable/intrinsics.rs
index a27ac2ccb6b..ba96c6e5d30 100644
--- a/src/libcore/unstable/intrinsics.rs
+++ b/src/libcore/unstable/intrinsics.rs
@@ -15,7 +15,7 @@ The intrinsics are defined in librustc/middle/trans/foreign.rs.
 */
 
 #[abi = "rust-intrinsic"]
-pub extern {
+pub extern "rust-intrinsic" {
     pub fn atomic_cxchg(dst: &mut int, old: int, src: int) -> int;
     pub fn atomic_cxchg_acq(dst: &mut int, old: int, src: int) -> int;
     pub fn atomic_cxchg_rel(dst: &mut int, old: int, src: int) -> int;
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index 807ea7d492d..fae56aac38f 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -97,7 +97,7 @@ pub mod jit {
     pub mod rusti {
         #[nolink]
         #[abi = "rust-intrinsic"]
-        pub extern {
+        pub extern "rust-intrinsic" {
             pub fn morestack_addr() -> *();
         }
     }
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 93bc64eafa3..5d0ba4bb911 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -33,6 +33,7 @@ use std::getopts::groups::{optopt, optmulti, optflag, optflagopt, getopts};
 use std::getopts::{opt_present};
 use std::getopts;
 use syntax::ast;
+use syntax::abi;
 use syntax::attr;
 use syntax::codemap;
 use syntax::diagnostic;
@@ -85,10 +86,10 @@ pub fn default_configuration(sess: Session, +argv0: ~str, input: input) ->
     // ARM is bi-endian, however using NDK seems to default
     // to little-endian unless a flag is provided.
     let (end,arch,wordsz) = match sess.targ_cfg.arch {
-      session::arch_x86 => (~"little",~"x86",~"32"),
-      session::arch_x86_64 => (~"little",~"x86_64",~"64"),
-      session::arch_arm => (~"little",~"arm",~"32"),
-      session::arch_mips => (~"little",~"arm",~"32")
+        abi::X86 => (~"little",~"x86",~"32"),
+        abi::X86_64 => (~"little",~"x86_64",~"64"),
+        abi::Arm => (~"little",~"arm",~"32"),
+        abi::Mips => (~"little",~"arm",~"32")
     };
 
     return ~[ // Target bindings.
@@ -308,7 +309,7 @@ pub fn compile_rest(sess: Session, cfg: ast::crate_cfg,
     };
 
     // NOTE: Android hack
-    if sess.targ_cfg.arch == session::arch_arm &&
+    if sess.targ_cfg.arch == abi::Arm &&
             (sess.opts.output_type == link::output_type_object ||
              sess.opts.output_type == link::output_type_exe) {
         let output_type = link::output_type_assembly;
@@ -453,20 +454,20 @@ pub fn get_os(triple: &str) -> Option<session::os> {
         } else { None }
 }
 
-pub fn get_arch(triple: &str) -> Option<session::arch> {
+pub fn get_arch(triple: &str) -> Option<abi::Architecture> {
     if str::contains(triple, ~"i386") ||
         str::contains(triple, ~"i486") ||
                str::contains(triple, ~"i586") ||
                str::contains(triple, ~"i686") ||
                str::contains(triple, ~"i786") {
-            Some(session::arch_x86)
+            Some(abi::X86)
         } else if str::contains(triple, ~"x86_64") {
-            Some(session::arch_x86_64)
+            Some(abi::X86_64)
         } else if str::contains(triple, ~"arm") ||
                       str::contains(triple, ~"xscale") {
-            Some(session::arch_arm)
+            Some(abi::Arm)
         } else if str::contains(triple, ~"mips") {
-            Some(session::arch_mips)
+            Some(abi::Mips)
         } else { None }
 }
 
@@ -483,16 +484,16 @@ pub fn build_target_config(sopts: @session::options,
                           ~"unknown architecture: " + sopts.target_triple)
     };
     let (int_type, uint_type, float_type) = match arch {
-      session::arch_x86 => (ast::ty_i32, ast::ty_u32, ast::ty_f64),
-      session::arch_x86_64 => (ast::ty_i64, ast::ty_u64, ast::ty_f64),
-      session::arch_arm => (ast::ty_i32, ast::ty_u32, ast::ty_f64),
-      session::arch_mips => (ast::ty_i32, ast::ty_u32, ast::ty_f64)
+      abi::X86 => (ast::ty_i32, ast::ty_u32, ast::ty_f64),
+      abi::X86_64 => (ast::ty_i64, ast::ty_u64, ast::ty_f64),
+      abi::Arm => (ast::ty_i32, ast::ty_u32, ast::ty_f64),
+      abi::Mips => (ast::ty_i32, ast::ty_u32, ast::ty_f64)
     };
     let target_strs = match arch {
-      session::arch_x86 => x86::get_target_strs(os),
-      session::arch_x86_64 => x86_64::get_target_strs(os),
-      session::arch_arm => arm::get_target_strs(os),
-      session::arch_mips => mips::get_target_strs(os)
+      abi::X86 => x86::get_target_strs(os),
+      abi::X86_64 => x86_64::get_target_strs(os),
+      abi::Arm => arm::get_target_strs(os),
+      abi::Mips => mips::get_target_strs(os)
     };
     let target_cfg = @session::config {
         os: os,
diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs
index d1f95dbeacc..95740e0f837 100644
--- a/src/librustc/driver/session.rs
+++ b/src/librustc/driver/session.rs
@@ -25,19 +25,17 @@ use syntax::codemap::span;
 use syntax::diagnostic;
 use syntax::parse::ParseSess;
 use syntax::{ast, codemap};
+use syntax::abi;
 use syntax;
 
 #[deriving(Eq)]
 pub enum os { os_win32, os_macos, os_linux, os_android, os_freebsd, }
 
-#[deriving(Eq)]
-pub enum arch { arch_x86, arch_x86_64, arch_arm, arch_mips, }
-
 pub enum crate_type { bin_crate, lib_crate, unknown_crate, }
 
 pub struct config {
     os: os,
-    arch: arch,
+    arch: abi::Architecture,
     target_strs: target_strs::t,
     int_type: int_ty,
     uint_type: uint_ty,
diff --git a/src/librustc/front/config.rs b/src/librustc/front/config.rs
index 2cec6ec5ab1..b92acb111e1 100644
--- a/src/librustc/front/config.rs
+++ b/src/librustc/front/config.rs
@@ -92,7 +92,7 @@ fn fold_foreign_mod(
         nm.view_items.filter_mapped(|a| filter_view_item(cx, *a));
     ast::foreign_mod {
         sort: nm.sort,
-        abi: nm.abi,
+        abis: nm.abis,
         view_items: vec::map(filtered_view_items, |x| fld.fold_view_item(*x)),
         items: filtered_items
     }
diff --git a/src/librustc/front/intrinsic.rs b/src/librustc/front/intrinsic.rs
index 6cfcac3e85a..7d5177a6dfb 100644
--- a/src/librustc/front/intrinsic.rs
+++ b/src/librustc/front/intrinsic.rs
@@ -126,7 +126,7 @@ pub mod intrinsic {
         use super::{TyDesc, TyVisitor};
 
         #[abi = "rust-intrinsic"]
-        pub extern {
+        pub extern "rust-intrinsic" {
             pub fn get_tydesc<T>() -> *();
             pub fn visit_tydesc(++td: *TyDesc, &&tv: @TyVisitor);
         }
diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs
index 40100531010..dbedeeaa5cc 100644
--- a/src/librustc/front/test.rs
+++ b/src/librustc/front/test.rs
@@ -146,7 +146,7 @@ fn fold_item(cx: @mut TestCtxt, &&i: @ast::item, fld: @fold::ast_fold)
 
     if is_test_fn(i) || is_bench_fn(i) {
         match i.node {
-          ast::item_fn(_, purity, _, _) if purity == ast::unsafe_fn => {
+          ast::item_fn(_, purity, _, _, _) if purity == ast::unsafe_fn => {
             let sess = cx.sess;
             sess.span_fatal(
                 i.span,
@@ -178,7 +178,7 @@ fn is_test_fn(i: @ast::item) -> bool {
 
     fn has_test_signature(i: @ast::item) -> bool {
         match &i.node {
-          &ast::item_fn(ref decl, _, ref generics, _) => {
+          &ast::item_fn(ref decl, _, _, ref generics, _) => {
             let no_output = match decl.output.node {
                 ast::ty_nil => true,
                 _ => false
@@ -200,7 +200,7 @@ fn is_bench_fn(i: @ast::item) -> bool {
 
     fn has_test_signature(i: @ast::item) -> bool {
         match i.node {
-            ast::item_fn(ref decl, _, ref generics, _) => {
+            ast::item_fn(ref decl, _, _, ref generics, _) => {
                 let input_cnt = vec::len(decl.inputs);
                 let no_output = match decl.output.node {
                     ast::ty_nil => true,
diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs
index b8bf27d7627..5e2ba4156bd 100644
--- a/src/librustc/metadata/creader.rs
+++ b/src/librustc/metadata/creader.rs
@@ -156,12 +156,8 @@ fn visit_view_item(e: @mut Env, i: @ast::view_item) {
 fn visit_item(e: @mut Env, i: @ast::item) {
     match i.node {
       ast::item_foreign_mod(ref fm) => {
-        match attr::foreign_abi(i.attrs) {
-          either::Right(abi) => {
-            if abi != ast::foreign_abi_cdecl &&
-               abi != ast::foreign_abi_stdcall { return; }
-          }
-          either::Left(ref msg) => e.diag.span_fatal(i.span, (*msg))
+        if fm.abis.is_rust() || fm.abis.is_intrinsic() {
+            return;
         }
 
         let cstore = e.cstore;
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index af9ab7d94f1..340ad443b58 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -35,6 +35,7 @@ use core::uint;
 use core::vec;
 use std::serialize::Encodable;
 use std;
+use syntax::abi::AbiSet;
 use syntax::ast::*;
 use syntax::ast;
 use syntax::ast_map;
@@ -653,7 +654,7 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
         (ecx.encode_inlined_item)(ecx, ebml_w, path, ii_item(item));
         ebml_w.end_tag();
       }
-      item_fn(_, purity, ref generics, _) => {
+      item_fn(_, purity, _, ref generics, _) => {
         add_to_index();
         ebml_w.start_tag(tag_items_data_item);
         encode_def_id(ebml_w, local_def(item.id));
@@ -979,7 +980,7 @@ fn encode_info_for_foreign_item(ecx: @EncodeContext,
                                 nitem: @foreign_item,
                                 index: @mut ~[entry<int>],
                                 +path: ast_map::path,
-                                abi: foreign_abi) {
+                                abi: AbiSet) {
     if !reachable(ecx, nitem.id) { return; }
     index.push(entry { val: nitem.id, pos: ebml_w.writer.tell() });
 
@@ -990,7 +991,7 @@ fn encode_info_for_foreign_item(ecx: @EncodeContext,
         encode_family(ebml_w, purity_fn_family(purity));
         encode_type_param_bounds(ebml_w, ecx, &generics.ty_params);
         encode_type(ecx, ebml_w, node_id_to_type(ecx.tcx, nitem.id));
-        if abi == foreign_abi_rust_intrinsic {
+        if abi.is_intrinsic() {
             (ecx.encode_inlined_item)(ecx, ebml_w, path, ii_foreign(nitem));
         } else {
             encode_symbol(ecx, ebml_w, nitem.id);
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index e233bf78d78..111bc307ed3 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -21,6 +21,8 @@ use middle::ty;
 use core::str;
 use core::uint;
 use core::vec;
+use syntax::abi::AbiSet;
+use syntax::abi;
 use syntax::ast;
 use syntax::ast::*;
 use syntax::codemap::dummy_sp;
@@ -76,6 +78,20 @@ fn next_byte(st: @mut PState) -> u8 {
     return b;
 }
 
+fn scan<R>(st: &mut PState, is_last: &fn(char) -> bool,
+           op: &fn(&[u8]) -> R) -> R
+{
+    let start_pos = st.pos;
+    debug!("scan: '%c' (start)", st.data[st.pos] as char);
+    while !is_last(st.data[st.pos] as char) {
+        st.pos += 1;
+        debug!("scan: '%c'", st.data[st.pos] as char);
+    }
+    let end_pos = st.pos;
+    st.pos += 1;
+    return op(st.data.slice(start_pos, end_pos));
+}
+
 pub fn parse_ident(st: @mut PState, last: char) -> ast::ident {
     fn is_last(b: char, c: char) -> bool { return c == b; }
     return parse_ident_(st, |a| is_last(last, a) );
@@ -83,10 +99,7 @@ pub fn parse_ident(st: @mut PState, last: char) -> ast::ident {
 
 fn parse_ident_(st: @mut PState, is_last: @fn(char) -> bool) ->
    ast::ident {
-    let mut rslt = ~"";
-    while !is_last(peek(st)) {
-        rslt += str::from_byte(next_byte(st));
-    }
+    let rslt = scan(st, is_last, str::from_bytes);
     return st.tcx.sess.ident_of(rslt);
 }
 
@@ -417,11 +430,17 @@ fn parse_purity(c: char) -> purity {
     }
 }
 
-fn parse_abi(c: char) -> Abi {
-    match c {
-      'r' => ast::RustAbi,
-      _ => fail!(fmt!("parse_abi: bad ABI '%c'", c))
+fn parse_abi_set(st: @mut PState) -> AbiSet {
+    assert!(next(st) == '[');
+    let mut abis = AbiSet::empty();
+    while peek(st) != ']' {
+        // FIXME(#5422) str API should not force this copy
+        let abi_str = scan(st, |c| c == ',', str::from_bytes);
+        let abi = abi::lookup(abi_str).expect(abi_str);
+        abis.add(abi);
     }
+    assert!(next(st) == ']');
+    return abis;
 }
 
 fn parse_onceness(c: char) -> ast::Onceness {
@@ -462,11 +481,11 @@ fn parse_closure_ty(st: @mut PState, conv: conv_did) -> ty::ClosureTy {
 
 fn parse_bare_fn_ty(st: @mut PState, conv: conv_did) -> ty::BareFnTy {
     let purity = parse_purity(next(st));
-    let abi = parse_abi(next(st));
+    let abi = parse_abi_set(st);
     let sig = parse_sig(st, conv);
     ty::BareFnTy {
         purity: purity,
-        abi: abi,
+        abis: abi,
         sig: sig
     }
 }
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index e54036e66c0..9b777332c27 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -21,6 +21,7 @@ use core::io::WriterUtil;
 use core::io;
 use core::uint;
 use core::vec;
+use syntax::abi::AbiSet;
 use syntax::ast::*;
 use syntax::diagnostic::span_handler;
 use syntax::print::pprust::*;
@@ -367,10 +368,13 @@ fn enc_purity(w: @io::Writer, p: purity) {
     }
 }
 
-fn enc_abi(w: @io::Writer, a: Abi) {
-    match a {
-        RustAbi => w.write_char('r'),
+fn enc_abi_set(w: @io::Writer, abis: AbiSet) {
+    w.write_char('[');
+    for abis.each |abi| {
+        w.write_str(abi.name());
+        w.write_char(',');
     }
+    w.write_char(']')
 }
 
 fn enc_onceness(w: @io::Writer, o: Onceness) {
@@ -382,7 +386,7 @@ fn enc_onceness(w: @io::Writer, o: Onceness) {
 
 fn enc_bare_fn_ty(w: @io::Writer, cx: @ctxt, ft: &ty::BareFnTy) {
     enc_purity(w, ft.purity);
-    enc_abi(w, ft.abi);
+    enc_abi_set(w, ft.abis);
     enc_fn_sig(w, cx, &ft.sig);
 }
 
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index 024d862461d..64c6c339179 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -762,9 +762,7 @@ fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
     }
 
     match it.node {
-      ast::item_foreign_mod(ref nmod)
-      if attr::foreign_abi(it.attrs) !=
-            either::Right(ast::foreign_abi_rust_intrinsic) => {
+      ast::item_foreign_mod(ref nmod) if !nmod.abis.is_intrinsic() => {
         for nmod.items.each |ni| {
             match ni.node {
               ast::foreign_item_fn(ref decl, _, _) => {
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 8503fa2e2cd..5bad8d1d6ca 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -1139,7 +1139,7 @@ pub impl Resolver {
                 name_bindings.define_value
                     (privacy, def_const(local_def(item.id)), sp);
             }
-            item_fn(_, purity, _, _) => {
+            item_fn(_, purity, _, _, _) => {
               let (name_bindings, new_parent) =
                 self.add_child(ident, parent, ForbidDuplicateValues, sp);
 
@@ -3530,7 +3530,7 @@ pub impl Resolver {
                 }
             }
 
-            item_fn(ref fn_decl, _, ref generics, ref block) => {
+            item_fn(ref fn_decl, _, _, ref generics, ref block) => {
                 // If this is the main function, we must record it in the
                 // session.
                 // FIXME #4404 android JNI hacks
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index f1988096a7e..0947dfa93fe 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -82,6 +82,7 @@ use syntax::parse::token::special_idents;
 use syntax::print::pprust::stmt_to_str;
 use syntax::visit;
 use syntax::{ast, ast_util, codemap, ast_map};
+use syntax::abi::{X86, X86_64, Arm, Mips};
 
 pub struct icx_popper {
     ccx: @CrateContext,
@@ -1455,10 +1456,12 @@ pub fn call_memcpy(cx: block, dst: ValueRef, src: ValueRef,
     let _icx = cx.insn_ctxt("call_memcpy");
     let ccx = cx.ccx();
     let key = match ccx.sess.targ_cfg.arch {
-      session::arch_x86
-      | session::arch_arm
-      | session::arch_mips => ~"llvm.memcpy.p0i8.p0i8.i32",
-      session::arch_x86_64 => ~"llvm.memcpy.p0i8.p0i8.i64"
+        X86 | Arm | Mips => {
+            ~"llvm.memcpy.p0i8.p0i8.i32"
+        }
+        X86_64 => {
+            ~"llvm.memcpy.p0i8.p0i8.i64"
+        }
     };
     let memcpy = *ccx.intrinsics.get(&key);
     let src_ptr = PointerCast(cx, src, T_ptr(T_i8()));
@@ -1499,12 +1502,10 @@ pub fn memzero(cx: block, llptr: ValueRef, llty: TypeRef) {
 
     let intrinsic_key;
     match ccx.sess.targ_cfg.arch {
-        session::arch_x86
-        | session::arch_arm
-        | session::arch_mips => {
+        X86 | Arm | Mips => {
             intrinsic_key = ~"llvm.memset.p0i8.i32";
         }
-        session::arch_x86_64 => {
+        X86_64 => {
             intrinsic_key = ~"llvm.memset.p0i8.i64";
         }
     }
@@ -2063,7 +2064,7 @@ pub fn trans_item(ccx: @CrateContext, item: ast::item) {
         _ => fail!(~"trans_item"),
     };
     match item.node {
-      ast::item_fn(ref decl, purity, ref generics, ref body) => {
+      ast::item_fn(ref decl, purity, _abis, ref generics, ref body) => {
         if purity == ast::extern_fn  {
             let llfndecl = get_item_val(ccx, item.id);
             foreign::trans_foreign_fn(ccx,
@@ -2105,11 +2106,7 @@ pub fn trans_item(ccx: @CrateContext, item: ast::item) {
       }
       ast::item_const(_, expr) => consts::trans_const(ccx, expr, item.id),
       ast::item_foreign_mod(ref foreign_mod) => {
-        let abi = match attr::foreign_abi(item.attrs) {
-            Right(abi_) => abi_,
-            Left(msg) => ccx.sess.span_fatal(item.span, msg)
-        };
-        foreign::trans_foreign_mod(ccx, foreign_mod, abi);
+        foreign::trans_foreign_mod(ccx, path, foreign_mod);
       }
       ast::item_struct(struct_def, ref generics) => {
         if !generics.is_type_parameterized() {
@@ -2405,7 +2402,7 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::node_id) -> ValueRef {
                     g
                 }
               }
-              ast::item_fn(_, purity, _, _) => {
+              ast::item_fn(_, purity, _, _, _) => {
                 let llfn = if purity != ast::extern_fn {
                     register_fn(ccx, i.span, my_path, i.id, i.attrs)
                 } else {
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index 0377ed15a2d..ec67c68f93c 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -269,11 +269,8 @@ pub fn trans_fn_ref_with_vtables(
             || fmt!("local item should be in ast map"));
 
         match *map_node {
-            ast_map::node_foreign_item(_,
-                                       ast::foreign_abi_rust_intrinsic,
-                                       _,
-                                       _) => {
-                must_monomorphise = true;
+            ast_map::node_foreign_item(_, abis, _, _) => {
+                must_monomorphise = abis.is_intrinsic()
             }
             _ => {
                 must_monomorphise = false;
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index e9fc36bbd7d..6bb30a5d5b5 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -57,6 +57,7 @@ use syntax::ast_map::{path, path_elt};
 use syntax::codemap::span;
 use syntax::parse::token::ident_interner;
 use syntax::{ast, ast_map};
+use syntax::abi::{X86, X86_64, Arm, Mips};
 
 pub type namegen = @fn(+s: ~str) -> ident;
 pub fn new_namegen(intr: @ident_interner) -> namegen {
@@ -777,10 +778,10 @@ pub fn T_bool() -> TypeRef { return T_i8(); }
 
 pub fn T_int(targ_cfg: @session::config) -> TypeRef {
     return match targ_cfg.arch {
-      session::arch_x86 => T_i32(),
-      session::arch_x86_64 => T_i64(),
-      session::arch_arm => T_i32(),
-      session::arch_mips => T_i32()
+        X86 => T_i32(),
+        X86_64 => T_i64(),
+        Arm => T_i32(),
+        Mips => T_i32()
     };
 }
 
@@ -815,10 +816,10 @@ pub fn T_float_ty(cx: @CrateContext, t: ast::float_ty) -> TypeRef {
 
 pub fn T_float(targ_cfg: @session::config) -> TypeRef {
     return match targ_cfg.arch {
-      session::arch_x86 => T_f64(),
-      session::arch_x86_64 => T_f64(),
-      session::arch_arm => T_f64(),
-      session::arch_mips => T_f64()
+        X86 => T_f64(),
+        X86_64 => T_f64(),
+        Arm => T_f64(),
+        Mips => T_f64()
     };
 }
 
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index 452a021ab51..d290a8ffa5c 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -852,7 +852,7 @@ pub fn create_function(fcx: fn_ctxt) -> @Metadata<SubProgramMetadata> {
     let (ident, ret_ty, id) = match *cx.tcx.items.get(&fcx.id) {
       ast_map::node_item(item, _) => {
         match item.node {
-          ast::item_fn(ref decl, _, _, _) => {
+          ast::item_fn(ref decl, _, _, _, _) => {
             (item.ident, decl.output, item.id)
           }
           _ => fcx.ccx.sess.span_bug(item.span, ~"create_function: item \
diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs
index 038f5b79327..6a040e81186 100644
--- a/src/librustc/middle/trans/foreign.rs
+++ b/src/librustc/middle/trans/foreign.rs
@@ -12,9 +12,6 @@ use core::prelude::*;
 
 use back::{link, abi};
 use driver::session;
-use driver::session::arch_x86_64;
-use driver::session::arch_arm;
-use driver::session::arch_mips;
 use lib::llvm::{SequentiallyConsistent, Acquire, Release, Xchg};
 use lib::llvm::{TypeRef, ValueRef};
 use lib;
@@ -42,13 +39,16 @@ use syntax::{ast, ast_util};
 use syntax::{attr, ast_map};
 use syntax::opt_vec;
 use syntax::parse::token::special_idents;
+use syntax::abi::{Architecture, X86, X86_64, Arm, Mips};
+use syntax::abi::{RustIntrinsic, Rust, Stdcall, Fastcall,
+                  Cdecl, Aapcs, C};
 
-fn abi_info(arch: session::arch) -> @cabi::ABIInfo {
+fn abi_info(arch: Architecture) -> @cabi::ABIInfo {
     return match arch {
-        arch_x86_64 => x86_64_abi_info(),
-        arch_arm => cabi_arm::abi_info(),
-        arch_mips => mips_abi_info(),
-        _ => cabi::llvm_abi_info()
+        X86_64 => x86_64_abi_info(),
+        Arm => cabi_arm::abi_info(),
+        Mips => mips_abi_info(),
+        X86 => cabi::llvm_abi_info()
     }
 }
 
@@ -239,36 +239,65 @@ fn build_wrap_fn_(ccx: @CrateContext,
 // unnecessary). We used to do this, in fact, and will perhaps do so
 // in the future.
 pub fn trans_foreign_mod(ccx: @CrateContext,
-                         foreign_mod: &ast::foreign_mod,
-                         abi: ast::foreign_abi)
+                         path: &ast_map::path,
+                         foreign_mod: &ast::foreign_mod)
 {
     let _icx = ccx.insn_ctxt("foreign::trans_foreign_mod");
 
-    let mut cc = match abi {
-        ast::foreign_abi_rust_intrinsic |
-        ast::foreign_abi_cdecl => lib::llvm::CCallConv,
-        ast::foreign_abi_stdcall => lib::llvm::X86StdcallCallConv
+    let arch = ccx.sess.targ_cfg.arch;
+    let abi = match foreign_mod.abis.for_arch(arch) {
+        None => {
+            ccx.sess.fatal(
+                fmt!("No suitable ABI for target architecture \
+                      in module %s",
+                     ast_map::path_to_str(*path,
+                                          ccx.sess.intr())));
+        }
+
+        Some(abi) => abi,
     };
 
-    for vec::each(foreign_mod.items) |foreign_item| {
+    for vec::each(foreign_mod.items) |&foreign_item| {
         match foreign_item.node {
             ast::foreign_item_fn(*) => {
                 let id = foreign_item.id;
-                if abi != ast::foreign_abi_rust_intrinsic {
-                    let llwrapfn = get_item_val(ccx, id);
-                    let tys = shim_types(ccx, id);
-                    if attr::attrs_contains_name(
-                        foreign_item.attrs, "rust_stack")
-                    {
-                        build_direct_fn(ccx, llwrapfn, *foreign_item,
-                                        &tys, cc);
-                    } else {
-                        let llshimfn = build_shim_fn(ccx, *foreign_item,
-                                                     &tys, cc);
-                        build_wrap_fn(ccx, &tys, llshimfn, llwrapfn);
+                match abi {
+                    RustIntrinsic => {
+                        // Intrinsics are emitted by monomorphic fn
+                    }
+
+                    Rust => {
+                        // FIXME(#3678) Implement linking to foreign fns with Rust ABI
+                        ccx.sess.unimpl(
+                            fmt!("Foreign functions with Rust ABI"));
+                    }
+
+                    Stdcall => {
+                        build_foreign_fn(ccx, id, foreign_item,
+                                         lib::llvm::X86StdcallCallConv);
+                    }
+
+                    Fastcall => {
+                        build_foreign_fn(ccx, id, foreign_item,
+                                         lib::llvm::X86FastcallCallConv);
+                    }
+
+                    Cdecl => {
+                        // FIXME(#3678) should really be more specific
+                        build_foreign_fn(ccx, id, foreign_item,
+                                         lib::llvm::CCallConv);
+                    }
+
+                    Aapcs => {
+                        // FIXME(#3678) should really be more specific
+                        build_foreign_fn(ccx, id, foreign_item,
+                                         lib::llvm::CCallConv);
+                    }
+
+                    C => {
+                        build_foreign_fn(ccx, id, foreign_item,
+                                         lib::llvm::CCallConv);
                     }
-                } else {
-                    // Intrinsics are emitted by monomorphic fn
                 }
             }
             ast::foreign_item_const(*) => {
@@ -279,6 +308,25 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
         }
     }
 
+    fn build_foreign_fn(ccx: @CrateContext,
+                        id: ast::node_id,
+                        foreign_item: @ast::foreign_item,
+                        cc: lib::llvm::CallConv)
+    {
+        let llwrapfn = get_item_val(ccx, id);
+        let tys = shim_types(ccx, id);
+        if attr::attrs_contains_name(
+            foreign_item.attrs, "rust_stack")
+        {
+            build_direct_fn(ccx, llwrapfn, foreign_item,
+                            &tys, cc);
+        } else {
+            let llshimfn = build_shim_fn(ccx, foreign_item,
+                                         &tys, cc);
+            build_wrap_fn(ccx, &tys, llshimfn, llwrapfn);
+        }
+    }
+
     fn build_shim_fn(ccx: @CrateContext,
                      foreign_item: @ast::foreign_item,
                      tys: &ShimTypes,
@@ -1080,20 +1128,3 @@ pub fn register_foreign_fn(ccx: @CrateContext,
                            t, lib::llvm::CCallConv, fnty)
     }
 }
-
-fn abi_of_foreign_fn(ccx: @CrateContext, i: @ast::foreign_item)
-    -> ast::foreign_abi {
-    match attr::first_attr_value_str_by_name(i.attrs, ~"abi") {
-      None => match *ccx.tcx.items.get(&i.id) {
-        ast_map::node_foreign_item(_, abi, _, _) => abi,
-        // ??
-        _ => fail!(~"abi_of_foreign_fn: not foreign")
-      },
-      Some(_) => match attr::foreign_abi(i.attrs) {
-        either::Right(abi) => abi,
-        either::Left(ref msg) => {
-            ccx.sess.span_fatal(i.span, (/*bad*/copy *msg))
-        }
-      }
-    }
-}
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index 71e8f7c2125..89617ac5eb8 100644
--- a/src/librustc/middle/trans/monomorphize.rs
+++ b/src/librustc/middle/trans/monomorphize.rs
@@ -39,6 +39,7 @@ use syntax::ast_map::path_name;
 use syntax::ast_util::local_def;
 use syntax::opt_vec;
 use syntax::parse::token::special_idents;
+use syntax::abi::AbiSet;
 
 pub fn monomorphic_fn(ccx: @CrateContext,
                       fn_id: ast::def_id,
@@ -97,7 +98,7 @@ pub fn monomorphic_fn(ccx: @CrateContext,
       ast_map::node_item(i, pt) => (pt, i.ident, i.span),
       ast_map::node_variant(ref v, enm, pt) => (pt, (*v).node.name, enm.span),
       ast_map::node_method(m, _, pt) => (pt, m.ident, m.span),
-      ast_map::node_foreign_item(i, ast::foreign_abi_rust_intrinsic, _, pt)
+      ast_map::node_foreign_item(i, abis, _, pt) if abis.is_intrinsic()
       => (pt, i.ident, i.span),
       ast_map::node_foreign_item(*) => {
         // Foreign externs don't have to be monomorphized.
@@ -174,8 +175,7 @@ pub fn monomorphic_fn(ccx: @CrateContext,
 
     let lldecl = match *map_node {
       ast_map::node_item(i@@ast::item {
-                // XXX: Bad copy.
-                node: ast::item_fn(ref decl, _, _, ref body),
+                node: ast::item_fn(ref decl, _, _, _, ref body),
                 _
             }, _) => {
         let d = mk_lldecl();
@@ -280,7 +280,7 @@ pub fn normalize_for_monomorphization(tcx: ty::ctxt,
                 tcx,
                 ty::BareFnTy {
                     purity: ast::impure_fn,
-                    abi: ast::RustAbi,
+                    abis: AbiSet::Rust(),
                     sig: FnSig {bound_lifetime_names: opt_vec::Empty,
                                 inputs: ~[],
                                 output: ty::mk_nil(tcx)}}))
diff --git a/src/librustc/middle/trans/reachable.rs b/src/librustc/middle/trans/reachable.rs
index f1c625e13aa..8824e0b7829 100644
--- a/src/librustc/middle/trans/reachable.rs
+++ b/src/librustc/middle/trans/reachable.rs
@@ -108,7 +108,7 @@ fn traverse_public_item(cx: ctx, item: @item) {
               }
           }
       }
-      item_fn(_, _, ref generics, ref blk) => {
+      item_fn(_, _, _, ref generics, ref blk) => {
         if generics.ty_params.len() > 0u ||
            attr::find_inline_attr(item.attrs) != attr::ia_none {
             traverse_inline_body(cx, blk);
diff --git a/src/librustc/middle/trans/type_use.rs b/src/librustc/middle/trans/type_use.rs
index a4495192b18..572e9348f63 100644
--- a/src/librustc/middle/trans/type_use.rs
+++ b/src/librustc/middle/trans/type_use.rs
@@ -102,7 +102,7 @@ pub fn type_uses_for(ccx: @CrateContext, fn_id: def_id, n_tps: uint)
                                      fn_id_loc))
     };
     match map_node {
-      ast_map::node_item(@ast::item { node: item_fn(_, _, _, ref body),
+      ast_map::node_item(@ast::item { node: item_fn(_, _, _, _, ref body),
                                       _ }, _) |
       ast_map::node_method(@ast::method {body: ref body, _}, _, _) => {
         handle_body(cx, body);
@@ -121,7 +121,7 @@ pub fn type_uses_for(ccx: @CrateContext, fn_id: def_id, n_tps: uint)
                                  abi,
                                  _,
                                  _) => {
-        if abi == foreign_abi_rust_intrinsic {
+        if abi.is_intrinsic() {
             let flags = match *cx.ccx.sess.str_of(i.ident) {
                 ~"size_of"  | ~"pref_align_of"    | ~"min_align_of" |
                 ~"init"     | ~"reinterpret_cast" |
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 425f14b7c07..fddcce523f4 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -47,6 +47,7 @@ use syntax::print::pprust;
 use syntax::{ast, ast_map};
 use syntax::opt_vec::OptVec;
 use syntax::opt_vec;
+use syntax::abi::AbiSet;
 use syntax;
 
 // Data types
@@ -349,7 +350,7 @@ pub fn type_id(t: t) -> uint { get(t).id }
 #[deriving(Eq)]
 pub struct BareFnTy {
     purity: ast::purity,
-    abi: Abi,
+    abis: AbiSet,
     sig: FnSig
 }
 
@@ -378,7 +379,7 @@ pub struct FnSig {
 
 impl to_bytes::IterBytes for BareFnTy {
     fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
-        to_bytes::iter_bytes_3(&self.purity, &self.abi, &self.sig, lsb0, f)
+        to_bytes::iter_bytes_3(&self.purity, &self.abis, &self.sig, lsb0, f)
     }
 }
 
@@ -546,7 +547,7 @@ pub enum type_err {
     terr_mismatch,
     terr_purity_mismatch(expected_found<purity>),
     terr_onceness_mismatch(expected_found<Onceness>),
-    terr_abi_mismatch(expected_found<ast::Abi>),
+    terr_abi_mismatch(expected_found<AbiSet>),
     terr_mutability,
     terr_sigil_mismatch(expected_found<ast::Sigil>),
     terr_box_mutability,
@@ -1056,7 +1057,7 @@ pub fn mk_ctor_fn(cx: ctxt, input_tys: &[ty::t], output: ty::t) -> t {
     mk_bare_fn(cx,
                BareFnTy {
                    purity: ast::pure_fn,
-                   abi: ast::RustAbi,
+                   abis: AbiSet::Rust(),
                    sig: FnSig {bound_lifetime_names: opt_vec::Empty,
                                inputs: input_args,
                                output: output}})
@@ -1240,7 +1241,7 @@ fn fold_sty(sty: &sty, fldop: &fn(t) -> t) -> sty {
         }
         ty_bare_fn(ref f) => {
             let sig = fold_sig(&f.sig, fldop);
-            ty_bare_fn(BareFnTy {sig: sig, abi: f.abi, purity: f.purity})
+            ty_bare_fn(BareFnTy {sig: sig, abis: f.abis, purity: f.purity})
         }
         ty_closure(ref f) => {
             let sig = fold_sig(&f.sig, fldop);
diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs
index 697649a4187..4b4cb857252 100644
--- a/src/librustc/middle/typeck/astconv.rs
+++ b/src/librustc/middle/typeck/astconv.rs
@@ -64,6 +64,7 @@ use middle::typeck::rscope::RegionParamNames;
 
 use core::result;
 use core::vec;
+use syntax::abi::AbiSet;
 use syntax::{ast, ast_util};
 use syntax::codemap::span;
 use syntax::opt_vec::OptVec;
@@ -348,7 +349,7 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:region_scope + Copy + Durable>(
       }
       ast::ty_bare_fn(ref bf) => {
           ty::mk_bare_fn(tcx, ty_of_bare_fn(self, rscope, bf.purity,
-                                            bf.abi, &bf.lifetimes, &bf.decl))
+                                            bf.abis, &bf.lifetimes, &bf.decl))
       }
       ast::ty_closure(ref f) => {
           let fn_decl = ty_of_closure(self,
@@ -543,7 +544,7 @@ pub fn ty_of_bare_fn<AC:AstConv,RS:region_scope + Copy + Durable>(
     self: &AC,
     rscope: &RS,
     purity: ast::purity,
-    abi: ast::Abi,
+    abi: AbiSet,
     lifetimes: &OptVec<ast::Lifetime>,
     decl: &ast::fn_decl) -> ty::BareFnTy
 {
@@ -562,7 +563,7 @@ pub fn ty_of_bare_fn<AC:AstConv,RS:region_scope + Copy + Durable>(
 
     ty::BareFnTy {
         purity: purity,
-        abi: abi,
+        abis: abi,
         sig: ty::FnSig {bound_lifetime_names: bound_lifetime_names,
                         inputs: input_tys,
                         output: output_ty}
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index f1396432957..41877017685 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -118,7 +118,8 @@ use core::result;
 use core::str;
 use core::vec;
 use std::list::Nil;
-use syntax::ast::{provided, required};
+use syntax::abi::AbiSet;
+use syntax::ast::{provided, required, ty_i};
 use syntax::ast;
 use syntax::ast_map;
 use syntax::ast_util::local_def;
@@ -567,7 +568,7 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) {
                             enum_definition.variants,
                             it.id);
       }
-      ast::item_fn(ref decl, _, _, ref body) => {
+      ast::item_fn(ref decl, _, _, _, ref body) => {
         check_bare_fn(ccx, decl, body, it.id, None);
       }
       ast::item_impl(ref generics, _, ty, ref ms) => {
@@ -603,8 +604,7 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) {
         check_bounds_are_used(ccx, t.span, &generics.ty_params, tpt_ty);
       }
       ast::item_foreign_mod(ref m) => {
-        if syntax::attr::foreign_abi(it.attrs) ==
-            either::Right(ast::foreign_abi_rust_intrinsic) {
+        if m.abis.is_intrinsic() {
             for m.items.each |item| {
                 check_intrinsic_type(ccx, *item);
             }
@@ -1145,6 +1145,19 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
 
         let formal_tys;
 
+        // FIXME(#3678) For now, do not permit calls to C abi functions.
+        match structure_of(fcx, sp, in_fty) {
+            ty::ty_bare_fn(ty::BareFnTy {abis, _}) => {
+                if !abis.is_rust() {
+                    tcx.sess.span_err(
+                        sp,
+                        fmt!("Calls to C ABI functions are not (yet) \
+                              supported; be patient, dear user"));
+                }
+            }
+            _ => {}
+        }
+
         // This is subtle: we expect `fty` to be a function type, which
         // normally introduce a level of binding.  In this case, we want to
         // process the types bound by the function but not by any nested
@@ -3672,7 +3685,7 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) {
     };
     let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {
         purity: ast::unsafe_fn,
-        abi: ast::RustAbi,
+        abis: AbiSet::Rust(),
         sig: FnSig {bound_lifetime_names: opt_vec::Empty,
                     inputs: inputs,
                     output: output}
diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs
index f2d10d53427..0ef65d5932c 100644
--- a/src/librustc/middle/typeck/collect.rs
+++ b/src/librustc/middle/typeck/collect.rs
@@ -33,7 +33,7 @@ are represented as `ty_param()` instances.
 use core::prelude::*;
 
 use metadata::csearch;
-use middle::ty::InstantiatedTraitRef;
+use middle::ty::{InstantiatedTraitRef, arg};
 use middle::ty::{substs, ty_param_bounds_and_ty, ty_param_substs_and_ty};
 use middle::ty;
 use middle::typeck::astconv::{AstConv, ty_of_arg};
@@ -48,6 +48,7 @@ use util::common::{indenter, pluralize};
 use util::ppaux;
 
 use core::vec;
+use syntax::abi::AbiSet;
 use syntax::ast::{RegionTyParamBound, TraitTyParamBound};
 use syntax::ast;
 use syntax::ast_map;
@@ -707,7 +708,7 @@ pub fn convert_struct(ccx: &CrateCtxt,
                 ccx,
                 &type_rscope(region_parameterization),
                 ast::impure_fn,
-                ast::RustAbi,
+                AbiSet::Rust(),
                 &opt_vec::Empty,
                 &ast_util::dtor_dec()));
         write_ty_to_tcx(tcx, dtor.node.id, t_dtor);
@@ -776,7 +777,7 @@ pub fn ty_of_method(ccx: &CrateCtxt,
         fty: astconv::ty_of_bare_fn(ccx,
                                     &rscope,
                                     m.purity,
-                                    ast::RustAbi,
+                                    AbiSet::Rust(),
                                     &method_generics.lifetimes,
                                     &m.decl),
         self_ty: m.self_ty.node,
@@ -798,7 +799,7 @@ pub fn ty_of_ty_method(self: &CrateCtxt,
         fty: astconv::ty_of_bare_fn(self,
                                     &rscope,
                                     m.purity,
-                                    ast::RustAbi,
+                                    AbiSet::Rust(),
                                     &m.generics.lifetimes,
                                     &m.decl),
         // assume public, because this is only invoked on trait methods
@@ -859,12 +860,12 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: @ast::item)
         tcx.tcache.insert(local_def(it.id), tpt);
         return tpt;
       }
-      ast::item_fn(ref decl, purity, ref generics, _) => {
+      ast::item_fn(ref decl, purity, abi, ref generics, _) => {
         let bounds = ty_param_bounds(ccx, generics);
         let tofd = astconv::ty_of_bare_fn(ccx,
                                           &empty_rscope,
                                           purity,
-                                          ast::RustAbi,
+                                          AbiSet::Rust(),
                                           &generics.lifetimes,
                                           decl);
         let tpt = ty_param_bounds_and_ty {
@@ -1040,7 +1041,7 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
     let t_fn = ty::mk_bare_fn(
         ccx.tcx,
         ty::BareFnTy {
-            abi: ast::RustAbi,
+            abis: AbiSet::Rust(),
             purity: ast::unsafe_fn,
             sig: ty::FnSig {bound_lifetime_names: opt_vec::Empty,
                             inputs: input_tys,
diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs
index e2b135a6a64..95ff552eb51 100644
--- a/src/librustc/middle/typeck/infer/coercion.rs
+++ b/src/librustc/middle/typeck/infer/coercion.rs
@@ -66,9 +66,9 @@ we may want to adjust precisely when coercions occur.
 
 use core::prelude::*;
 
-use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowFn};
-use middle::ty::{AutoDerefRef, AutoRef};
-use middle::ty::{vstore_slice, vstore_box, vstore_uniq};
+use middle::ty::{TyVar, AutoPtr, AutoBorrowVec, AutoBorrowFn};
+use middle::ty::{AutoAdjustment, AutoDerefRef, AutoRef};
+use middle::ty::{vstore_slice, vstore_box, vstore_uniq, vstore_fixed};
 use middle::ty::{mt};
 use middle::ty;
 use middle::typeck::infer::{CoerceResult, resolve_type};
@@ -76,9 +76,10 @@ use middle::typeck::infer::combine::CombineFields;
 use middle::typeck::infer::sub::Sub;
 use middle::typeck::infer::to_str::InferStr;
 use middle::typeck::infer::resolve::try_resolve_tvar_shallow;
-use util::common::indenter;
+use util::common::{indent, indenter};
 
-use syntax::ast::m_imm;
+use syntax::abi::AbiSet;
+use syntax::ast::{m_const, m_imm, m_mutbl};
 use syntax::ast;
 
 // Note: Coerce is not actually a combiner, in that it does not
@@ -334,9 +335,19 @@ pub impl Coerce {
                                        b: ty::t,
                                        sty_b: &ty::sty) -> CoerceResult
     {
+        /*!
+         *
+         * Attempts to coerce from a bare Rust function (`extern
+         * "rust" fn`) into a closure.
+         */
+
         debug!("coerce_from_bare_fn(a=%s, b=%s)",
                a.inf_str(self.infcx), b.inf_str(self.infcx));
 
+        if !fn_ty_a.abis.is_rust() {
+            return self.subtype(a, b);
+        }
+
         let fn_ty_b = match *sty_b {
             ty::ty_closure(ref f) => {copy *f}
             _ => {
@@ -344,8 +355,6 @@ pub impl Coerce {
             }
         };
 
-        // for now, bare fn and closures have the same
-        // representation
         let adj = @ty::AutoAddEnv(fn_ty_b.region, fn_ty_b.sigil);
         let a_closure = ty::mk_closure(
             self.infcx.tcx,
diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs
index f90bc8314a4..add70b21e39 100644
--- a/src/librustc/middle/typeck/infer/combine.rs
+++ b/src/librustc/middle/typeck/infer/combine.rs
@@ -72,6 +72,7 @@ use syntax::ast::{Onceness, purity};
 use syntax::ast;
 use syntax::opt_vec;
 use syntax::codemap::span;
+use syntax::abi::AbiSet;
 
 pub trait Combine {
     fn infcx(&self) -> @mut InferCtxt;
@@ -101,7 +102,7 @@ pub trait Combine {
     fn args(&self, a: ty::arg, b: ty::arg) -> cres<ty::arg>;
     fn sigils(&self, p1: ast::Sigil, p2: ast::Sigil) -> cres<ast::Sigil>;
     fn purities(&self, a: purity, b: purity) -> cres<purity>;
-    fn abis(&self, a: ast::Abi, b: ast::Abi) -> cres<ast::Abi>;
+    fn abis(&self, a: AbiSet, b: AbiSet) -> cres<AbiSet>;
     fn oncenesses(&self, a: Onceness, b: Onceness) -> cres<Onceness>;
     fn contraregions(&self, a: ty::Region, b: ty::Region)
                   -> cres<ty::Region>;
@@ -396,7 +397,7 @@ pub fn super_closure_tys<C:Combine>(
 }
 
 pub fn super_abis<C:Combine>(
-    self: &C, a: ast::Abi, b: ast::Abi) -> cres<ast::Abi>
+    self: &C, a: AbiSet, b: AbiSet) -> cres<AbiSet>
 {
     if a == b {
         Ok(a)
@@ -409,10 +410,10 @@ pub fn super_bare_fn_tys<C:Combine>(
     self: &C, a_f: &ty::BareFnTy, b_f: &ty::BareFnTy) -> cres<ty::BareFnTy>
 {
     let purity = if_ok!(self.purities(a_f.purity, b_f.purity));
-    let abi = if_ok!(self.abis(a_f.abi, b_f.abi));
+    let abi = if_ok!(self.abis(a_f.abis, b_f.abis));
     let sig = if_ok!(self.fn_sigs(&a_f.sig, &b_f.sig));
     Ok(ty::BareFnTy {purity: purity,
-                     abi: abi,
+                     abis: abi,
                      sig: sig})
 }
 
diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs
index 2ed73061743..d521d005aba 100644
--- a/src/librustc/middle/typeck/infer/glb.rs
+++ b/src/librustc/middle/typeck/infer/glb.rs
@@ -21,7 +21,9 @@ use middle::typeck::infer::{cres, InferCtxt};
 use middle::typeck::isr_alist;
 use syntax::ast;
 use syntax::ast::{Many, Once, extern_fn, impure_fn, m_const, m_imm, m_mutbl};
-use syntax::ast::{pure_fn, unsafe_fn, Onceness, purity};
+use syntax::ast::{noreturn, pure_fn, ret_style, return_val, unsafe_fn};
+use syntax::ast::{Onceness, purity};
+use syntax::abi::AbiSet;
 use syntax::codemap::span;
 use util::common::{indent, indenter};
 use util::ppaux::mt_to_str;
@@ -283,7 +285,7 @@ impl Combine for Glb {
         super_sigils(self, p1, p2)
     }
 
-    fn abis(&self, p1: ast::Abi, p2: ast::Abi) -> cres<ast::Abi> {
+    fn abis(&self, p1: AbiSet, p2: AbiSet) -> cres<AbiSet> {
         super_abis(self, p1, p2)
     }
 
diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs
index 8821926be35..5a705c31c12 100644
--- a/src/librustc/middle/typeck/infer/lub.rs
+++ b/src/librustc/middle/typeck/infer/lub.rs
@@ -23,6 +23,7 @@ use util::common::indent;
 use util::ppaux::mt_to_str;
 
 use std::list;
+use syntax::abi::AbiSet;
 use syntax::ast;
 use syntax::ast::{Many, Once, extern_fn, m_const, impure_fn};
 use syntax::ast::{pure_fn, unsafe_fn};
@@ -210,7 +211,7 @@ impl Combine for Lub {
         super_sigils(self, p1, p2)
     }
 
-    fn abis(&self, p1: ast::Abi, p2: ast::Abi) -> cres<ast::Abi> {
+    fn abis(&self, p1: AbiSet, p2: AbiSet) -> cres<AbiSet> {
         super_abis(self, p1, p2)
     }
 
diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs
index 1fcd89ea73f..161905c12e5 100644
--- a/src/librustc/middle/typeck/infer/sub.rs
+++ b/src/librustc/middle/typeck/infer/sub.rs
@@ -24,6 +24,7 @@ use util::ppaux::bound_region_to_str;
 
 use std::list::Nil;
 use std::list;
+use syntax::abi::AbiSet;
 use syntax::ast;
 use syntax::ast::{Onceness, m_const, purity};
 use syntax::codemap::span;
@@ -216,7 +217,7 @@ impl Combine for Sub {
         super_sigils(self, p1, p2)
     }
 
-    fn abis(&self, p1: ast::Abi, p2: ast::Abi) -> cres<ast::Abi> {
+    fn abis(&self, p1: AbiSet, p2: AbiSet) -> cres<AbiSet> {
         super_abis(self, p1, p2)
     }
 
diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs
index 2df9884911e..2b5a47c18c6 100644
--- a/src/librustc/middle/typeck/mod.rs
+++ b/src/librustc/middle/typeck/mod.rs
@@ -293,7 +293,7 @@ fn check_main_fn_ty(ccx: @mut CrateCtxt,
             match tcx.items.find(&main_id) {
                 Some(&ast_map::node_item(it,_)) => {
                     match it.node {
-                        ast::item_fn(_, _, ref ps, _)
+                        ast::item_fn(_, _, _, ref ps, _)
                         if ps.is_parameterized() => {
                             tcx.sess.span_err(
                                 main_span,
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 48ed754a4a6..afbf3f485d0 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -30,6 +30,7 @@ use syntax::print::pprust;
 use syntax::print::pprust::mode_to_str;
 use syntax::{ast, ast_util};
 use syntax::ast_map;
+use syntax::abi::AbiSet;
 
 use core::str;
 use core::vec;
@@ -302,15 +303,14 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
     }
     fn bare_fn_to_str(cx: ctxt,
                       purity: ast::purity,
-                      abi: ast::Abi,
+                      abis: AbiSet,
                       ident: Option<ast::ident>,
                       sig: &ty::FnSig) -> ~str
     {
         let mut s = ~"extern ";
 
-        match abi {
-            ast::RustAbi => {}
-        }
+        s.push_str(abis.to_str());
+        s.push_char(' ');
 
         match purity {
             ast::impure_fn => {}
@@ -386,7 +386,7 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
     fn method_to_str(cx: ctxt, m: method) -> ~str {
         bare_fn_to_str(cx,
                        m.fty.purity,
-                       m.fty.abi,
+                       m.fty.abis,
                        Some(m.ident),
                        &m.fty.sig) + ~";"
     }
@@ -429,7 +429,7 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
           closure_to_str(cx, f)
       }
       ty_bare_fn(ref f) => {
-          bare_fn_to_str(cx, f.purity, f.abi, None, &f.sig)
+          bare_fn_to_str(cx, f.purity, f.abis, None, &f.sig)
       }
       ty_infer(infer_ty) => infer_ty.to_str(),
       ty_err => ~"[type error]",
diff --git a/src/librustdoc/tystr_pass.rs b/src/librustdoc/tystr_pass.rs
index 293799d85b2..6ccfb1bb8c4 100644
--- a/src/librustdoc/tystr_pass.rs
+++ b/src/librustdoc/tystr_pass.rs
@@ -69,7 +69,7 @@ fn get_fn_sig(srv: astsrv::Srv, fn_id: doc::AstId) -> Option<~str> {
         match *ctxt.ast_map.get(&fn_id) {
           ast_map::node_item(@ast::item {
             ident: ident,
-            node: ast::item_fn(ref decl, purity, ref tys, _), _
+            node: ast::item_fn(ref decl, purity, _, ref tys, _), _
           }, _) |
           ast_map::node_foreign_item(@ast::foreign_item {
             ident: ident,
diff --git a/src/librusti/rusti.rc b/src/librusti/rusti.rc
index 98c4c0409d4..6b91377b674 100644
--- a/src/librusti/rusti.rc
+++ b/src/librusti/rusti.rc
@@ -159,7 +159,7 @@ fn run(repl: Repl, input: ~str) -> Repl {
 
     for crate.node.module.items.each |item| {
         match item.node {
-            ast::item_fn(_, _, _, blk) => {
+            ast::item_fn(_, _, _, _, blk) => {
                 if item.ident == sess.ident_of(~"main") {
                     opt = blk.node.expr;
                 }
diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc
index caab57dd23a..ff549ad2c22 100644
--- a/src/librustpkg/rustpkg.rc
+++ b/src/librustpkg/rustpkg.rc
@@ -185,7 +185,7 @@ impl PackageScript {
         // the build API.
         for crate.node.module.items.each |i| {
             match i.node {
-                ast::item_fn(_, _, _, _) => {
+                ast::item_fn(*) => {
                     custom = true;
 
                     break;
diff --git a/src/libstd/arena.rs b/src/libstd/arena.rs
index 683e4a62975..81c28f94d9f 100644
--- a/src/libstd/arena.rs
+++ b/src/libstd/arena.rs
@@ -48,7 +48,7 @@ use core::vec;
 
 pub mod rusti {
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         fn move_val_init<T>(dst: &mut T, +src: T);
         fn needs_drop<T>() -> bool;
     }
diff --git a/src/libstd/priority_queue.rs b/src/libstd/priority_queue.rs
index 1fb79fcab28..dd56e413595 100644
--- a/src/libstd/priority_queue.rs
+++ b/src/libstd/priority_queue.rs
@@ -18,7 +18,7 @@ use core::ptr::addr_of;
 use core::vec;
 
 #[abi = "rust-intrinsic"]
-extern "C" mod rusti {
+extern "rust-intrinsic" mod rusti {
     fn move_val_init<T>(dst: &mut T, +src: T);
     fn init<T>() -> T;
 }
diff --git a/src/libsyntax/abi.rs b/src/libsyntax/abi.rs
new file mode 100644
index 00000000000..076c09e446d
--- /dev/null
+++ b/src/libsyntax/abi.rs
@@ -0,0 +1,427 @@
+// Copyright 2012-2013 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.
+
+use core::prelude::*;
+use core::to_bytes;
+use core::to_str::ToStr;
+
+#[deriving(Eq)]
+pub enum Abi {
+    // NB: This ordering MUST match the AbiDatas array below.
+    // (This is ensured by the test indices_are_correct().)
+
+    // Single platform ABIs come first (`for_arch()` relies on this)
+    Cdecl,
+    Stdcall,
+    Fastcall,
+    Aapcs,
+
+    // Multiplatform ABIs second
+    Rust,
+    C,
+    RustIntrinsic,
+}
+
+#[deriving(Eq)]
+pub enum Architecture {
+    // NB. You cannot change the ordering of these
+    // constants without adjusting IntelBits below.
+    // (This is ensured by the test indices_are_correct().)
+    X86,
+    X86_64,
+    Arm,
+    Mips
+}
+
+// FIXME(#5423) After a snapshot, we can change these constants:
+// const IntelBits: u32 = (1 << (X86 as uint)) | (1 << X86_64 as uint));
+// const ArmBits: u32 = (1 << (Arm as uint));
+static IntelBits: u32 = 1 | 2;
+static ArmBits: u32 = 4;
+
+struct AbiData {
+    abi: Abi,
+
+    // Name of this ABI as we like it called.
+    name: &'static str,
+
+    // Is it specific to a platform? If so, which one?  Also, what is
+    // the name that LLVM gives it (in case we disagree)
+    abi_arch: AbiArchitecture
+}
+
+enum AbiArchitecture {
+    RustArch,   // Not a real ABI (e.g., intrinsic)
+    AllArch,    // An ABI that specifies cross-platform defaults (e.g., "C")
+    Archs(u32)  // Multiple architectures (bitset)
+}
+
+#[auto_encode]
+#[auto_decode]
+#[deriving(Eq)]
+pub struct AbiSet {
+    priv bits: u32   // each bit represents one of the abis below
+}
+
+static AbiDatas: &'static [AbiData] = &[
+    // Platform-specific ABIs
+    AbiData {abi: Cdecl, name: "cdecl", abi_arch: Archs(IntelBits)},
+    AbiData {abi: Stdcall, name: "stdcall", abi_arch: Archs(IntelBits)},
+    AbiData {abi: Fastcall, name:"fastcall", abi_arch: Archs(IntelBits)},
+    AbiData {abi: Aapcs, name: "aapcs", abi_arch: Archs(ArmBits)},
+
+    // Cross-platform ABIs
+    //
+    // NB: Do not adjust this ordering without
+    // adjusting the indices below.
+    AbiData {abi: Rust, name: "Rust", abi_arch: RustArch},
+    AbiData {abi: C, name: "C", abi_arch: AllArch},
+    AbiData {abi: RustIntrinsic, name: "rust-intrinsic", abi_arch: RustArch},
+];
+
+fn each_abi(op: &fn(abi: Abi) -> bool) {
+    /*!
+     *
+     * Iterates through each of the defined ABIs.
+     */
+
+    for AbiDatas.each |abi_data| {
+        if !op(abi_data.abi) {
+            return;
+        }
+    }
+}
+
+pub fn lookup(name: &str) -> Option<Abi> {
+    /*!
+     *
+     * Returns the ABI with the given name (if any).
+     */
+
+    for each_abi |abi| {
+        if name == abi.data().name {
+            return Some(abi);
+        }
+    }
+    return None;
+}
+
+pub fn all_names() -> ~[&'static str] {
+    AbiDatas.map(|d| d.name)
+}
+
+pub impl Abi {
+    #[inline]
+    fn index(&self) -> uint {
+        *self as uint
+    }
+
+    #[inline]
+    fn data(&self) -> &'static AbiData {
+        &AbiDatas[self.index()]
+    }
+
+    fn name(&self) -> &'static str {
+        self.data().name
+    }
+}
+
+impl Architecture {
+    fn bit(&self) -> u32 {
+        1 << (*self as u32)
+    }
+}
+
+pub impl AbiSet {
+    fn from(abi: Abi) -> AbiSet {
+        AbiSet { bits: (1 << abi.index()) }
+    }
+
+    #[inline]
+    fn Rust() -> AbiSet {
+        AbiSet::from(Rust)
+    }
+
+    #[inline]
+    fn C() -> AbiSet {
+        AbiSet::from(C)
+    }
+
+    #[inline]
+    fn Intrinsic() -> AbiSet {
+        AbiSet::from(RustIntrinsic)
+    }
+
+    fn default() -> AbiSet {
+        AbiSet::C()
+    }
+
+    fn empty() -> AbiSet {
+        AbiSet { bits: 0 }
+    }
+
+    #[inline]
+    fn is_rust(&self) -> bool {
+        self.bits == 1 << Rust.index()
+    }
+
+    #[inline]
+    fn is_c(&self) -> bool {
+        self.bits == 1 << C.index()
+    }
+
+    #[inline]
+    fn is_intrinsic(&self) -> bool {
+        self.bits == 1 << RustIntrinsic.index()
+    }
+
+    fn contains(&self, abi: Abi) -> bool {
+        (self.bits & (1 << abi.index())) != 0
+    }
+
+    fn subset_of(&self, other_abi_set: AbiSet) -> bool {
+        (self.bits & other_abi_set.bits) == self.bits
+    }
+
+    fn add(&mut self, abi: Abi) {
+        self.bits |= (1 << abi.index());
+    }
+
+    fn each(&self, op: &fn(abi: Abi) -> bool) {
+        for each_abi |abi| {
+            if self.contains(abi) {
+                if !op(abi) {
+                    return;
+                }
+            }
+        }
+    }
+
+    fn is_empty(&self) -> bool {
+        self.bits == 0
+    }
+
+    fn for_arch(&self, arch: Architecture) -> Option<Abi> {
+        // NB---Single platform ABIs come first
+        for self.each |abi| {
+            let data = abi.data();
+            match data.abi_arch {
+                Archs(a) if (a & arch.bit()) != 0 => { return Some(abi); }
+                Archs(_) => { }
+                RustArch | AllArch => { return Some(abi); }
+            }
+        }
+
+        None
+    }
+
+    fn check_valid(&self) -> Option<(Abi, Abi)> {
+        let mut abis = ~[];
+        for self.each |abi| { abis.push(abi); }
+
+        for abis.eachi |i, abi| {
+            let data = abi.data();
+            for abis.slice(0, i).each |other_abi| {
+                let other_data = other_abi.data();
+                debug!("abis=(%?,%?) datas=(%?,%?)",
+                       abi, data.abi_arch,
+                       other_abi, other_data.abi_arch);
+                match (&data.abi_arch, &other_data.abi_arch) {
+                    (&AllArch, &AllArch) => {
+                        // Two cross-architecture ABIs
+                        return Some((*abi, *other_abi));
+                    }
+                    (_, &RustArch) |
+                    (&RustArch, _) => {
+                        // Cannot combine Rust or Rust-Intrinsic with
+                        // anything else.
+                        return Some((*abi, *other_abi));
+                    }
+                    (&Archs(is), &Archs(js)) if (is & js) != 0 => {
+                        // Two ABIs for same architecture
+                        return Some((*abi, *other_abi));
+                    }
+                    _ => {}
+                }
+            }
+        }
+
+        return None;
+    }
+}
+
+impl to_bytes::IterBytes for Abi {
+    fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
+        self.index().iter_bytes(lsb0, f)
+    }
+}
+
+impl to_bytes::IterBytes for AbiSet {
+    fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
+        self.bits.iter_bytes(lsb0, f)
+    }
+}
+
+impl ToStr for Abi {
+    fn to_str(&self) -> ~str {
+        self.data().name.to_str()
+    }
+}
+
+impl ToStr for AbiSet {
+    fn to_str(&self) -> ~str {
+        unsafe { // so we can push to strs.
+            let mut strs = ~[];
+            for self.each |abi| {
+                strs.push(abi.data().name);
+            }
+            fmt!("\"%s\"", str::connect_slices(strs, " "))
+        }
+    }
+}
+
+#[test]
+fn lookup_Rust() {
+    let abi = lookup("Rust");
+    assert!(abi.is_some() && abi.get().data().name == "Rust");
+}
+
+#[test]
+fn lookup_cdecl() {
+    let abi = lookup("cdecl");
+    assert!(abi.is_some() && abi.get().data().name == "cdecl");
+}
+
+#[test]
+fn lookup_baz() {
+    let abi = lookup("baz");
+    assert!(abi.is_none());
+}
+
+#[cfg(test)]
+fn cannot_combine(n: Abi, m: Abi) {
+    let mut set = AbiSet::empty();
+    set.add(n);
+    set.add(m);
+    match set.check_valid() {
+        Some((a, b)) => {
+            assert!((n == a && m == b) ||
+                         (m == a && n == b));
+        }
+        None => {
+            fail!(~"Invalid match not detected");
+        }
+    }
+}
+
+#[cfg(test)]
+fn can_combine(n: Abi, m: Abi) {
+    let mut set = AbiSet::empty();
+    set.add(n);
+    set.add(m);
+    match set.check_valid() {
+        Some((_, _)) => {
+            fail!(~"Valid match declared invalid");
+        }
+        None => {}
+    }
+}
+
+#[test]
+fn cannot_combine_cdecl_and_stdcall() {
+    cannot_combine(Cdecl, Stdcall);
+}
+
+#[test]
+fn cannot_combine_c_and_rust() {
+    cannot_combine(C, Rust);
+}
+
+#[test]
+fn cannot_combine_rust_and_cdecl() {
+    cannot_combine(Rust, Cdecl);
+}
+
+#[test]
+fn cannot_combine_rust_intrinsic_and_cdecl() {
+    cannot_combine(RustIntrinsic, Cdecl);
+}
+
+#[test]
+fn can_combine_c_and_stdcall() {
+    can_combine(C, Stdcall);
+}
+
+#[test]
+fn can_combine_aapcs_and_stdcall() {
+    can_combine(Aapcs, Stdcall);
+}
+
+#[test]
+fn abi_to_str_stdcall_aaps() {
+    let mut set = AbiSet::empty();
+    set.add(Aapcs);
+    set.add(Stdcall);
+    assert!(set.to_str() == ~"\"stdcall aapcs\"");
+}
+
+#[test]
+fn abi_to_str_c_aaps() {
+    let mut set = AbiSet::empty();
+    set.add(Aapcs);
+    set.add(C);
+    debug!("set = %s", set.to_str());
+    assert!(set.to_str() == ~"\"aapcs C\"");
+}
+
+#[test]
+fn abi_to_str_rust() {
+    let mut set = AbiSet::empty();
+    set.add(Rust);
+    debug!("set = %s", set.to_str());
+    assert!(set.to_str() == ~"\"Rust\"");
+}
+
+#[test]
+fn indices_are_correct() {
+    for AbiDatas.eachi |i, abi_data| {
+        assert!(i == abi_data.abi.index());
+    }
+
+    let bits = 1 << (X86 as u32);
+    let bits = bits | 1 << (X86_64 as u32);
+    assert!(IntelBits == bits);
+
+    let bits = 1 << (Arm as u32);
+    assert!(ArmBits == bits);
+}
+
+#[cfg(test)]
+fn check_arch(abis: &[Abi], arch: Architecture, expect: Option<Abi>) {
+    let mut set = AbiSet::empty();
+    for abis.each |&abi| {
+        set.add(abi);
+    }
+    let r = set.for_arch(arch);
+    assert!(r == expect);
+}
+
+#[test]
+fn pick_multiplatform() {
+    check_arch([C, Cdecl], X86, Some(Cdecl));
+    check_arch([C, Cdecl], X86_64, Some(Cdecl));
+    check_arch([C, Cdecl], Arm, Some(C));
+}
+
+#[test]
+fn pick_uniplatform() {
+    check_arch([Stdcall], X86, Some(Stdcall));
+    check_arch([Stdcall], Arm, None);
+}
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index d7fc2f2f264..db04c46ea59 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -10,7 +10,9 @@
 
 // The Rust abstract syntax tree.
 
-use codemap::{span, spanned};
+use codemap::{span, FileName, spanned};
+use abi::AbiSet;
+use opt_vec::OptVec;
 
 use core::cast;
 use core::option::{None, Option, Some};
@@ -19,7 +21,6 @@ use core::to_bytes;
 use core::to_str::ToStr;
 use std::serialize::{Encodable, Decodable, Encoder, Decoder};
 
-use opt_vec::OptVec;
 
 /* can't import macros yet, so this is copied from token.rs. See its comment
  * there. */
@@ -328,27 +329,6 @@ impl to_bytes::IterBytes for mutability {
 #[auto_encode]
 #[auto_decode]
 #[deriving(Eq)]
-pub enum Abi {
-    RustAbi
-}
-
-impl to_bytes::IterBytes for Abi {
-    fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
-        (*self as uint).iter_bytes(lsb0, f)
-    }
-}
-
-impl ToStr for Abi {
-    fn to_str(&self) -> ~str {
-        match *self {
-            RustAbi => ~"\"rust\""
-        }
-    }
-}
-
-#[auto_encode]
-#[auto_decode]
-#[deriving(Eq)]
 pub enum Sigil {
     BorrowedSigil,
     OwnedSigil,
@@ -893,7 +873,7 @@ pub struct TyClosure {
 #[deriving(Eq)]
 pub struct TyBareFn {
     purity: purity,
-    abi: Abi,
+    abis: AbiSet,
     lifetimes: OptVec<Lifetime>,
     decl: fn_decl
 }
@@ -1057,15 +1037,6 @@ pub struct _mod {
     items: ~[@item],
 }
 
-#[auto_encode]
-#[auto_decode]
-#[deriving(Eq)]
-pub enum foreign_abi {
-    foreign_abi_rust_intrinsic,
-    foreign_abi_cdecl,
-    foreign_abi_stdcall,
-}
-
 // Foreign mods can be named or anonymous
 #[auto_encode]
 #[auto_decode]
@@ -1077,7 +1048,7 @@ pub enum foreign_mod_sort { named, anonymous }
 #[deriving(Eq)]
 pub struct foreign_mod {
     sort: foreign_mod_sort,
-    abi: ident,
+    abis: AbiSet,
     view_items: ~[@view_item],
     items: ~[@foreign_item],
 }
@@ -1267,7 +1238,7 @@ pub struct item {
 #[deriving(Eq)]
 pub enum item_ {
     item_const(@Ty, @expr),
-    item_fn(fn_decl, purity, Generics, blk),
+    item_fn(fn_decl, purity, AbiSet, Generics, blk),
     item_mod(_mod),
     item_foreign_mod(foreign_mod),
     item_ty(@Ty, Generics),
diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs
index 11178054b4c..ff5cbbe9f23 100644
--- a/src/libsyntax/ast_map.rs
+++ b/src/libsyntax/ast_map.rs
@@ -10,6 +10,7 @@
 
 use core::prelude::*;
 
+use abi::AbiSet;
 use ast::*;
 use ast;
 use ast_util::{inlined_item_utils, stmt_id};
@@ -87,7 +88,7 @@ pub fn path_elt_to_str(pe: path_elt, itr: @ident_interner) -> ~str {
 
 pub enum ast_node {
     node_item(@item, @path),
-    node_foreign_item(@foreign_item, foreign_abi, visibility, @path),
+    node_foreign_item(@foreign_item, AbiSet, visibility, @path),
     node_trait_method(@trait_method, def_id /* trait did */,
                       @path /* path to the trait */),
     node_method(@method, def_id /* impl did */, @path /* path to the impl */),
@@ -171,7 +172,7 @@ pub fn map_decoded_item(diag: @span_handler,
       ii_item(*) | ii_dtor(*) => { /* fallthrough */ }
       ii_foreign(i) => {
         cx.map.insert(i.id, node_foreign_item(i,
-                                              foreign_abi_rust_intrinsic,
+                                              AbiSet::Intrinsic(),
                                               i.vis,    // Wrong but OK
                                               @path));
       }
@@ -274,10 +275,6 @@ pub fn map_item(i: @item, &&cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
             }
         }
         item_foreign_mod(ref nm) => {
-            let abi = match attr::foreign_abi(i.attrs) {
-                Left(ref msg) => cx.diag.span_fatal(i.span, (*msg)),
-                Right(abi) => abi
-            };
             for nm.items.each |nitem| {
                 // Compute the visibility for this native item.
                 let visibility = match nitem.vis {
@@ -289,7 +286,7 @@ pub fn map_item(i: @item, &&cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
                 cx.map.insert(nitem.id,
                     node_foreign_item(
                         *nitem,
-                        abi,
+                        nm.abis,
                         visibility,
                         // FIXME (#2543)
                         if nm.sort == ast::named {
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 131b6616df6..208ed1e35fe 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -465,7 +465,7 @@ pub fn id_visitor(vfn: @fn(node_id)) -> visit::vt<()> {
                     vfn(self_id);
                     vfn(parent_id.node);
                 }
-                visit::fk_item_fn(_, generics, _) => {
+                visit::fk_item_fn(_, generics, _, _) => {
                     visit_generics(generics);
                 }
                 visit::fk_method(_, generics, m) => {
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 90e8c0aa616..72355d04456 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -303,27 +303,6 @@ pub fn find_linkage_metas(attrs: &[ast::attribute]) -> ~[@ast::meta_item] {
     }
 }
 
-pub fn foreign_abi(attrs: &[ast::attribute])
-                -> Either<~str, ast::foreign_abi> {
-    return match attr::first_attr_value_str_by_name(attrs, ~"abi") {
-        None => {
-            Right(ast::foreign_abi_cdecl)
-        }
-        Some(@~"rust-intrinsic") => {
-            Right(ast::foreign_abi_rust_intrinsic)
-        }
-        Some(@~"cdecl") => {
-            Right(ast::foreign_abi_cdecl)
-        }
-        Some(@~"stdcall") => {
-            Right(ast::foreign_abi_stdcall)
-        }
-        Some(t) => {
-            Left(~"unsupported abi: " + *t)
-        }
-    };
-}
-
 #[deriving(Eq)]
 pub enum inline_attr {
     ia_none,
diff --git a/src/libsyntax/ext/pipes/ast_builder.rs b/src/libsyntax/ext/pipes/ast_builder.rs
index 075474a2a0d..72e6c22dbc8 100644
--- a/src/libsyntax/ext/pipes/ast_builder.rs
+++ b/src/libsyntax/ext/pipes/ast_builder.rs
@@ -15,7 +15,8 @@
 
 use core::prelude::*;
 
-use ast::ident;
+use abi::AbiSet;
+use ast::{ident, node_id};
 use ast;
 use ast_util;
 use codemap::{span, respan, dummy_sp, spanned};
@@ -272,6 +273,7 @@ impl ext_ctxt_ast_builder for @ext_ctxt {
                   dummy_sp(),
                   ast::item_fn(self.fn_decl(inputs, output),
                                ast::impure_fn,
+                               AbiSet::Rust(),
                                generics,
                                body))
     }
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index f43e541052e..0a473b1cebe 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -236,10 +236,11 @@ fn noop_fold_struct_field(sf: @struct_field, fld: @ast_fold)
 pub fn noop_fold_item_underscore(i: &item_, fld: @ast_fold) -> item_ {
     match *i {
         item_const(t, e) => item_const(fld.fold_ty(t), fld.fold_expr(e)),
-        item_fn(ref decl, purity, ref generics, ref body) => {
+        item_fn(ref decl, purity, abi, ref generics, ref body) => {
             item_fn(
                 fold_fn_decl(decl, fld),
                 purity,
+                abi,
                 fold_generics(generics, fld),
                 fld.fold_block(body)
             )
@@ -612,7 +613,7 @@ pub fn noop_fold_ty(t: &ty_, fld: @ast_fold) -> ty_ {
             ty_bare_fn(@TyBareFn {
                 lifetimes: f.lifetimes,
                 purity: f.purity,
-                abi: f.abi,
+                abis: f.abis,
                 decl: fold_fn_decl(&f.decl, fld)
             })
         }
@@ -639,7 +640,7 @@ pub fn noop_fold_mod(m: &_mod, fld: @ast_fold) -> _mod {
 fn noop_fold_foreign_mod(nm: &foreign_mod, fld: @ast_fold) -> foreign_mod {
     ast::foreign_mod {
         sort: nm.sort,
-        abi: nm.abi,
+        abis: nm.abis,
         view_items: vec::map(nm.view_items, |x| fld.fold_view_item(*x)),
         items: vec::map(nm.items, |x| fld.fold_foreign_item(*x)),
     }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index ac0e42fc65d..353e3014cf7 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -10,7 +10,9 @@
 
 use core::prelude::*;
 
-use ast::{Sigil, BorrowedSigil, ManagedSigil, OwnedSigil, RustAbi};
+use abi;
+use abi::AbiSet;
+use ast::{Sigil, BorrowedSigil, ManagedSigil, OwnedSigil};
 use ast::{CallSugar, NoSugar, DoSugar, ForSugar};
 use ast::{TyBareFn, TyClosure};
 use ast::{RegionTyParamBound, TraitTyParamBound};
@@ -361,11 +363,13 @@ pub impl Parser {
 
         */
 
+        let opt_abis = self.parse_opt_abis();
+        let abis = opt_abis.get_or_default(AbiSet::Rust());
         let purity = self.parse_purity();
         self.expect_keyword(&~"fn");
         let (decl, lifetimes) = self.parse_ty_fn_decl();
         return ty_bare_fn(@TyBareFn {
-            abi: RustAbi,
+            abis: abis,
             purity: purity,
             lifetimes: lifetimes,
             decl: decl
@@ -3041,11 +3045,13 @@ pub impl Parser {
                      span: mk_sp(lo, hi) }
     }
 
-    fn parse_item_fn(&self, purity: purity) -> item_info {
+    fn parse_item_fn(&self, purity: purity, abis: AbiSet) -> item_info {
         let (ident, generics) = self.parse_fn_header();
         let decl = self.parse_fn_decl(|p| p.parse_arg());
         let (inner_attrs, body) = self.parse_inner_attrs_and_block(true);
-        (ident, item_fn(decl, purity, generics, body), Some(inner_attrs))
+        (ident,
+         item_fn(decl, purity, abis, generics, body),
+         Some(inner_attrs))
     }
 
     fn parse_method(&self) -> @method {
@@ -3607,7 +3613,7 @@ pub impl Parser {
     }
 
     fn parse_foreign_mod_items(&self, sort: ast::foreign_mod_sort,
-                               +abi: ast::ident,
+                               +abis: AbiSet,
                                +first_item_attrs: ~[attribute])
                             -> foreign_mod {
         // Shouldn't be any view items since we've already parsed an item attr
@@ -3630,30 +3636,20 @@ pub impl Parser {
         }
         ast::foreign_mod {
             sort: sort,
-            abi: abi,
+            abis: abis,
             view_items: view_items,
             items: items
         }
     }
 
-    fn parse_item_foreign_mod(&self, lo: BytePos,
+    fn parse_item_foreign_mod(&self,
+                              lo: BytePos,
+                              opt_abis: Option<AbiSet>,
                               visibility: visibility,
                               attrs: ~[attribute],
                               items_allowed: bool)
-                           -> item_or_view_item {
-
-        // Parse the ABI.
-        let abi_opt;
-        match *self.token {
-            token::LIT_STR(copy found_abi) => {
-                self.bump();
-                abi_opt = Some(found_abi);
-            }
-            _ => {
-                abi_opt = None;
-            }
-        }
-
+                           -> item_or_view_item
+    {
         let mut must_be_named_mod = false;
         if self.is_keyword(&~"mod") {
             must_be_named_mod = true;
@@ -3688,14 +3684,10 @@ pub impl Parser {
 
         // extern mod { ... }
         if items_allowed && self.eat(&token::LBRACE) {
-            let abi;
-            match abi_opt {
-                Some(found_abi) => abi = found_abi,
-                None => abi = special_idents::c_abi,
-            }
+            let abis = opt_abis.get_or_default(AbiSet::C());
 
             let (inner, next) = self.parse_inner_attrs_and_next();
-            let m = self.parse_foreign_mod_items(sort, abi, next);
+            let m = self.parse_foreign_mod_items(sort, abis, next);
             self.expect(&token::RBRACE);
 
             return iovi_item(self.mk_item(lo, self.last_span.hi, ident,
@@ -3704,12 +3696,8 @@ pub impl Parser {
                                                        Some(inner))));
         }
 
-        match abi_opt {
-            None => {}  // OK.
-            Some(_) => {
-                self.span_err(*self.span, ~"an ABI may not be specified \
-                                                here");
-            }
+        if opt_abis.is_some() {
+            self.span_err(*self.span, ~"an ABI may not be specified here");
         }
 
         // extern mod foo;
@@ -3913,6 +3901,49 @@ pub impl Parser {
         }
     }
 
+    fn parse_opt_abis(&self) -> Option<AbiSet> {
+        match *self.token {
+            token::LIT_STR(s) => {
+                self.bump();
+                let the_string = self.id_to_str(s);
+                let mut words = ~[];
+                for str::each_word(*the_string) |s| { words.push(s) }
+                let mut abis = AbiSet::empty();
+                for words.each |word| {
+                    match abi::lookup(*word) {
+                        Some(abi) => {
+                            if abis.contains(abi) {
+                                self.span_err(
+                                    *self.span,
+                                    fmt!("ABI `%s` appears twice",
+                                         *word));
+                            } else {
+                                abis.add(abi);
+                            }
+                        }
+
+                        None => {
+                            self.span_err(
+                                *self.span,
+                                fmt!("illegal ABI: \
+                                      expected one of [%s], \
+                                      found `%s`",
+                                     str::connect_slices(
+                                         abi::all_names(),
+                                         ", "),
+                                     *word));
+                        }
+                    }
+                }
+                Some(abis)
+            }
+
+            _ => {
+                None
+            }
+        }
+    }
+
     // parse one of the items or view items allowed by the
     // flags; on failure, return iovi_none.
     fn parse_item_or_view_item(
@@ -3961,7 +3992,8 @@ pub impl Parser {
             self.is_keyword(&~"fn") &&
             !self.fn_expr_lookahead(self.look_ahead(1u)) {
             self.bump();
-            let (ident, item_, extra_attrs) = self.parse_item_fn(impure_fn);
+            let (ident, item_, extra_attrs) =
+                self.parse_item_fn(impure_fn, AbiSet::Rust());
             return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_,
                                           visibility,
                                           maybe_append(attrs, extra_attrs)));
@@ -3970,7 +4002,8 @@ pub impl Parser {
             // PURE FUNCTION ITEM
             self.obsolete(*self.last_span, ObsoletePurity);
             self.expect_keyword(&~"fn");
-            let (ident, item_, extra_attrs) = self.parse_item_fn(impure_fn);
+            let (ident, item_, extra_attrs) =
+                self.parse_item_fn(impure_fn, AbiSet::Rust());
             return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_,
                                           visibility,
                                           maybe_append(attrs, extra_attrs)));
@@ -3987,16 +4020,20 @@ pub impl Parser {
             // UNSAFE FUNCTION ITEM (where items are allowed)
             self.bump();
             self.expect_keyword(&~"fn");
-            let (ident, item_, extra_attrs) = self.parse_item_fn(unsafe_fn);
+            let (ident, item_, extra_attrs) =
+                self.parse_item_fn(unsafe_fn, AbiSet::Rust());
             return iovi_item(self.mk_item(lo, self.last_span.hi, ident, item_,
                                           visibility,
                                           maybe_append(attrs, extra_attrs)));
         }
         if self.eat_keyword(&~"extern") {
+            let opt_abis = self.parse_opt_abis();
+
             if items_allowed && self.eat_keyword(&~"fn") {
                 // EXTERN FUNCTION ITEM
+                let abis = opt_abis.get_or_default(AbiSet::C());
                 let (ident, item_, extra_attrs) =
-                    self.parse_item_fn(extern_fn);
+                    self.parse_item_fn(extern_fn, abis);
                 return iovi_item(self.mk_item(lo, self.last_span.hi, ident,
                                               item_, visibility,
                                               maybe_append(attrs,
@@ -4004,7 +4041,7 @@ pub impl Parser {
             }
             if !foreign_items_allowed {
                 // EXTERN MODULE ITEM
-                return self.parse_item_foreign_mod(lo, visibility, attrs,
+                return self.parse_item_foreign_mod(lo, opt_abis, visibility, attrs,
                                                    items_allowed);
             }
         }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 7ca5fba4c81..0ec7bdba3d1 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -10,6 +10,8 @@
 
 use core::prelude::*;
 
+use abi::AbiSet;
+use abi;
 use ast::{RegionTyParamBound, TraitTyParamBound, required, provided};
 use ast;
 use ast_util;
@@ -186,7 +188,8 @@ pub fn fun_to_str(decl: &ast::fn_decl, purity: ast::purity, name: ast::ident,
                   generics: &ast::Generics, intr: @ident_interner) -> ~str {
     do io::with_str_writer |wr| {
         let s = rust_printer(wr, intr);
-        print_fn(s, decl, purity, name, generics, opt_self_ty, ast::inherited);
+        print_fn(s, decl, Some(purity), AbiSet::Rust(),
+                 name, generics, opt_self_ty, ast::inherited);
         end(s); // Close the head box
         end(s); // Close the outer box
         eof(s.s);
@@ -405,7 +408,7 @@ pub fn print_type(s: @ps, &&ty: @ast::Ty) {
       ast::ty_bare_fn(f) => {
           let generics = ast::Generics {lifetimes: copy f.lifetimes,
                                         ty_params: opt_vec::Empty};
-          print_ty_fn(s, Some(f.abi), None, None,
+          print_ty_fn(s, Some(f.abis), None, None,
                       f.purity, ast::Many, &f.decl, None,
                       Some(&generics), None);
       }
@@ -446,7 +449,7 @@ pub fn print_foreign_item(s: @ps, item: @ast::foreign_item) {
     print_outer_attributes(s, item.attrs);
     match item.node {
       ast::foreign_item_fn(ref decl, purity, ref generics) => {
-        print_fn(s, decl, purity, item.ident, generics, None,
+        print_fn(s, decl, Some(purity), AbiSet::Rust(), item.ident, generics, None,
                  ast::inherited);
         end(s); // end head-ibox
         word(s.s, ~";");
@@ -485,11 +488,12 @@ pub fn print_item(s: @ps, &&item: @ast::item) {
         end(s); // end the outer cbox
 
       }
-      ast::item_fn(ref decl, purity, ref typarams, ref body) => {
+      ast::item_fn(ref decl, purity, abi, ref typarams, ref body) => {
         print_fn(
             s,
             decl,
-            purity,
+            Some(purity),
+            abi,
             item.ident,
             typarams,
             None,
@@ -508,8 +512,7 @@ pub fn print_item(s: @ps, &&item: @ast::item) {
       }
       ast::item_foreign_mod(ref nmod) => {
         head(s, visibility_qualified(item.vis, ~"extern"));
-        print_string(s, *s.intr.get(nmod.abi));
-        nbsp(s);
+        word_nbsp(s, nmod.abis.to_str());
         match nmod.sort {
             ast::named => {
                 word_nbsp(s, ~"mod");
@@ -817,7 +820,7 @@ pub fn print_method(s: @ps, meth: @ast::method) {
     hardbreak_if_not_bol(s);
     maybe_print_comment(s, meth.span.lo);
     print_outer_attributes(s, meth.attrs);
-    print_fn(s, &meth.decl, meth.purity,
+    print_fn(s, &meth.decl, Some(meth.purity), AbiSet::Rust(),
              meth.ident, &meth.generics, Some(meth.self_ty.node),
              meth.vis);
     word(s.s, ~" ");
@@ -1650,13 +1653,14 @@ pub fn print_self_ty(s: @ps, self_ty: ast::self_ty_) -> bool {
 
 pub fn print_fn(s: @ps,
                 decl: &ast::fn_decl,
-                purity: ast::purity,
+                purity: Option<ast::purity>,
+                abis: AbiSet,
                 name: ast::ident,
                 generics: &ast::Generics,
                 opt_self_ty: Option<ast::self_ty_>,
                 vis: ast::visibility) {
     head(s, ~"");
-    print_fn_header_info(s, purity, ast::Many, None, vis);
+    print_fn_header_info(s, opt_self_ty, purity, abis, ast::Many, None, vis);
     nbsp(s);
     print_ident(s, name);
     print_generics(s, generics);
@@ -1905,7 +1909,7 @@ pub fn print_arg(s: @ps, input: ast::arg) {
 }
 
 pub fn print_ty_fn(s: @ps,
-                   opt_abi: Option<ast::Abi>,
+                   opt_abis: Option<AbiSet>,
                    opt_sigil: Option<ast::Sigil>,
                    opt_region: Option<@ast::Lifetime>,
                    purity: ast::purity,
@@ -1918,7 +1922,7 @@ pub fn print_ty_fn(s: @ps,
 
     // Duplicates the logic in `print_fn_header_info()`.  This is because that
     // function prints the sigil in the wrong place.  That should be fixed.
-    print_opt_abi(s, opt_abi);
+    print_extern_opt_abis(s, opt_abis);
     print_opt_sigil(s, opt_sigil);
     print_opt_lifetime(s, opt_region);
     print_purity(s, purity);
@@ -2146,9 +2150,22 @@ pub fn next_comment(s: @ps) -> Option<comments::cmnt> {
     }
 }
 
-pub fn print_opt_abi(s: @ps, opt_abi: Option<ast::Abi>) {
-    match opt_abi {
-        Some(ast::RustAbi) => { word_nbsp(s, ~"extern"); }
+pub fn print_opt_purity(s: @ps, opt_purity: Option<ast::purity>) {
+    match opt_purity {
+        Some(ast::impure_fn) => { }
+        Some(purity) => {
+            word_nbsp(s, purity_to_str(purity));
+        }
+        None => {}
+    }
+}
+
+pub fn print_extern_opt_abis(s: @ps, opt_abis: Option<AbiSet>) {
+    match opt_abis {
+        Some(abis) => {
+            word_nbsp(s, ~"extern");
+            word_nbsp(s, abis.to_str());
+        }
         None => {}
     };
 }
@@ -2163,12 +2180,25 @@ pub fn print_opt_sigil(s: @ps, opt_sigil: Option<ast::Sigil>) {
 }
 
 pub fn print_fn_header_info(s: @ps,
-                            purity: ast::purity,
+                            opt_sty: Option<ast::self_ty_>,
+                            opt_purity: Option<ast::purity>,
+                            abis: AbiSet,
                             onceness: ast::Onceness,
                             opt_sigil: Option<ast::Sigil>,
                             vis: ast::visibility) {
     word(s.s, visibility_qualified(vis, ~""));
-    print_purity(s, purity);
+
+    if abis != AbiSet::Rust() {
+        word_nbsp(s, ~"extern");
+        word_nbsp(s, abis.to_str());
+
+        if opt_purity != Some(ast::extern_fn) {
+            print_opt_purity(s, opt_purity);
+        }
+    } else {
+        print_opt_purity(s, opt_purity);
+    }
+
     print_onceness(s, onceness);
     word(s.s, ~"fn");
     print_opt_sigil(s, opt_sigil);
diff --git a/src/libsyntax/syntax.rc b/src/libsyntax/syntax.rc
index feff4b0616a..21c52f1bfcc 100644
--- a/src/libsyntax/syntax.rc
+++ b/src/libsyntax/syntax.rc
@@ -39,6 +39,7 @@ pub mod opt_vec;
 pub mod attr;
 pub mod diagnostic;
 pub mod codemap;
+pub mod abi;
 pub mod ast;
 pub mod ast_util;
 pub mod ast_map;
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index dd724948b32..a994f2b5b22 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -10,6 +10,7 @@
 
 use core::prelude::*;
 
+use abi::AbiSet;
 use ast::*;
 use ast;
 use ast_util;
@@ -29,10 +30,18 @@ use opt_vec::OptVec;
 pub enum vt<E> { mk_vt(visitor<E>), }
 
 pub enum fn_kind<'self> {
-    fk_item_fn(ident, &'self Generics, purity),   // fn foo()
-    fk_method(ident, &'self Generics, &'self method),   // fn foo(&self)
-    fk_anon(ast::Sigil),                    // fn@(x, y) { ... }
-    fk_fn_block,                            // |x, y| ...
+    // fn foo() or extern "Abi" fn foo()
+    fk_item_fn(ident, &'self Generics, purity, AbiSet),
+
+    // fn foo(&self)
+    fk_method(ident, &'self Generics, &'self method),
+
+    // fn@(x, y) { ... }
+    fk_anon(ast::Sigil),
+
+    // |x, y| ...
+    fk_fn_block,
+
     fk_dtor( // class destructor
         &'self Generics,
         &'self [attribute],
@@ -43,8 +52,8 @@ pub enum fn_kind<'self> {
 
 pub fn name_of_fn(fk: &fn_kind) -> ident {
     match *fk {
-      fk_item_fn(name, _, _) | fk_method(name, _, _) => {
-          /* FIXME (#2543) */ copy name
+      fk_item_fn(name, _, _, _) | fk_method(name, _, _) => {
+          name
       }
       fk_anon(*) | fk_fn_block(*) => parse::token::special_idents::anon,
       fk_dtor(*)                  => parse::token::special_idents::dtor
@@ -53,7 +62,7 @@ pub fn name_of_fn(fk: &fn_kind) -> ident {
 
 pub fn generics_of_fn(fk: &fn_kind) -> Generics {
     match *fk {
-        fk_item_fn(_, generics, _) |
+        fk_item_fn(_, generics, _, _) |
         fk_method(_, generics, _) |
         fk_dtor(generics, _, _, _) => {
             copy *generics
@@ -144,12 +153,13 @@ pub fn visit_item<E>(i: @item, e: E, v: vt<E>) {
             (v.visit_ty)(t, e, v);
             (v.visit_expr)(ex, e, v);
         }
-        item_fn(ref decl, purity, ref generics, ref body) => {
+        item_fn(ref decl, purity, abi, ref generics, ref body) => {
             (v.visit_fn)(
                 &fk_item_fn(
-                    /* FIXME (#2543) */ copy i.ident,
+                    i.ident,
                     generics,
-                    purity
+                    purity,
+                    abi
                 ),
                 decl,
                 body,
diff --git a/src/test/auxiliary/cci_intrinsic.rs b/src/test/auxiliary/cci_intrinsic.rs
index d8c97218d6f..e98fc4aac0a 100644
--- a/src/test/auxiliary/cci_intrinsic.rs
+++ b/src/test/auxiliary/cci_intrinsic.rs
@@ -10,7 +10,7 @@
 
 pub mod rusti {
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         fn atomic_cxchg(dst: &mut int, old: int, src: int) -> int;
         fn atomic_cxchg_acq(dst: &mut int, old: int, src: int) -> int;
         fn atomic_cxchg_rel(dst: &mut int, old: int, src: int) -> int;
@@ -18,11 +18,11 @@ pub mod rusti {
         fn atomic_xchg(dst: &mut int, src: int) -> int;
         fn atomic_xchg_acq(dst: &mut int, src: int) -> int;
         fn atomic_xchg_rel(dst: &mut int, src: int) -> int;
-        
+
         fn atomic_xadd(dst: &mut int, src: int) -> int;
         fn atomic_xadd_acq(dst: &mut int, src: int) -> int;
         fn atomic_xadd_rel(dst: &mut int, src: int) -> int;
-        
+
         fn atomic_xsub(dst: &mut int, src: int) -> int;
         fn atomic_xsub_acq(dst: &mut int, src: int) -> int;
         fn atomic_xsub_rel(dst: &mut int, src: int) -> int;
diff --git a/src/test/compile-fail/block-coerce-no-2.rs b/src/test/compile-fail/block-coerce-no-2.rs
index 95ff995258f..85ef09cc2a6 100644
--- a/src/test/compile-fail/block-coerce-no-2.rs
+++ b/src/test/compile-fail/block-coerce-no-2.rs
@@ -19,5 +19,5 @@ fn main() {
     }
 
     f(g);
-    //~^ ERROR mismatched types: expected `extern fn(extern fn(extern fn()))`
+    //~^ ERROR mismatched types: expected `extern "Rust" fn(extern "Rust" fn(extern "Rust" fn()))`
 }
diff --git a/src/test/compile-fail/main-wrong-type-2.rs b/src/test/compile-fail/main-wrong-type-2.rs
index bb6633ecdc1..136cc2b8680 100644
--- a/src/test/compile-fail/main-wrong-type-2.rs
+++ b/src/test/compile-fail/main-wrong-type-2.rs
@@ -9,5 +9,5 @@
 // except according to those terms.
 
 fn main() -> char {
-//~^ ERROR Wrong type in main function: found `extern fn() -> char`
+//~^ ERROR Wrong type in main function: found `extern "Rust" fn() -> char`
 }
diff --git a/src/test/compile-fail/main-wrong-type.rs b/src/test/compile-fail/main-wrong-type.rs
index cdd3b19f0ae..f3ecac3f406 100644
--- a/src/test/compile-fail/main-wrong-type.rs
+++ b/src/test/compile-fail/main-wrong-type.rs
@@ -14,5 +14,5 @@ struct S {
 }
 
 fn main(foo: S) {
-//~^ ERROR Wrong type in main function: found `extern fn(S)`
+//~^ ERROR Wrong type in main function: found `extern "Rust" fn(S)`
 }
diff --git a/src/test/compile-fail/missing-do.rs b/src/test/compile-fail/missing-do.rs
index 974f30feb06..b5789d73771 100644
--- a/src/test/compile-fail/missing-do.rs
+++ b/src/test/compile-fail/missing-do.rs
@@ -14,6 +14,6 @@ fn foo(f: &fn()) { f() }
 
 fn main() {
     ~"" || 42; //~ ERROR binary operation || cannot be applied to type `~str`
-    foo || {}; //~ ERROR binary operation || cannot be applied to type `extern fn(&fn())`
+    foo || {}; //~ ERROR binary operation || cannot be applied to type `extern "Rust" fn(&fn())`
     //~^ NOTE did you forget the `do` keyword for the call?
 }
diff --git a/src/test/pretty/fn-types.rs b/src/test/pretty/fn-types.rs
index 7470151e46c..b1f7828a2f4 100644
--- a/src/test/pretty/fn-types.rs
+++ b/src/test/pretty/fn-types.rs
@@ -10,7 +10,7 @@
 
 // pp-exact
 
-fn from_foreign_fn(x: extern fn()) { }
+fn from_foreign_fn(x: extern "Rust" fn()) { }
 fn from_stack_closure(x: &fn()) { }
 fn from_box_closure(x: @fn()) { }
 fn from_unique_closure(x: ~fn()) { }
diff --git a/src/test/run-pass/intrinsic-alignment.rs b/src/test/run-pass/intrinsic-alignment.rs
index 5d1ed5f8692..d6a59fac57a 100644
--- a/src/test/run-pass/intrinsic-alignment.rs
+++ b/src/test/run-pass/intrinsic-alignment.rs
@@ -12,7 +12,7 @@
 
 mod rusti {
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         pub fn pref_align_of<T>() -> uint;
         pub fn min_align_of<T>() -> uint;
     }
diff --git a/src/test/run-pass/intrinsic-atomics.rs b/src/test/run-pass/intrinsic-atomics.rs
index cd2079667c2..42d4f5e4d20 100644
--- a/src/test/run-pass/intrinsic-atomics.rs
+++ b/src/test/run-pass/intrinsic-atomics.rs
@@ -10,7 +10,7 @@
 
 mod rusti {
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         pub fn atomic_cxchg(dst: &mut int, old: int, src: int) -> int;
         pub fn atomic_cxchg_acq(dst: &mut int, old: int, src: int) -> int;
         pub fn atomic_cxchg_rel(dst: &mut int, old: int, src: int) -> int;
@@ -18,11 +18,11 @@ mod rusti {
         pub fn atomic_xchg(dst: &mut int, src: int) -> int;
         pub fn atomic_xchg_acq(dst: &mut int, src: int) -> int;
         pub fn atomic_xchg_rel(dst: &mut int, src: int) -> int;
-        
+
         pub fn atomic_xadd(dst: &mut int, src: int) -> int;
         pub fn atomic_xadd_acq(dst: &mut int, src: int) -> int;
         pub fn atomic_xadd_rel(dst: &mut int, src: int) -> int;
-        
+
         pub fn atomic_xsub(dst: &mut int, src: int) -> int;
         pub fn atomic_xsub_acq(dst: &mut int, src: int) -> int;
         pub fn atomic_xsub_rel(dst: &mut int, src: int) -> int;
diff --git a/src/test/run-pass/intrinsic-frame-address.rs b/src/test/run-pass/intrinsic-frame-address.rs
index 5562d26677d..4e00b36cbeb 100644
--- a/src/test/run-pass/intrinsic-frame-address.rs
+++ b/src/test/run-pass/intrinsic-frame-address.rs
@@ -13,7 +13,7 @@
 
 mod rusti {
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         pub fn frame_address(f: &once fn(*u8));
     }
 }
diff --git a/src/test/run-pass/intrinsic-move-val.rs b/src/test/run-pass/intrinsic-move-val.rs
index 21a471db480..966061a8085 100644
--- a/src/test/run-pass/intrinsic-move-val.rs
+++ b/src/test/run-pass/intrinsic-move-val.rs
@@ -10,7 +10,7 @@
 
 mod rusti {
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         pub fn move_val_init<T>(dst: &mut T, +src: T);
         pub fn move_val<T>(dst: &mut T, +src: T);
     }
diff --git a/src/test/run-pass/intrinsics-integer.rs b/src/test/run-pass/intrinsics-integer.rs
index 75713d8c710..b96ea8cbb7b 100644
--- a/src/test/run-pass/intrinsics-integer.rs
+++ b/src/test/run-pass/intrinsics-integer.rs
@@ -13,8 +13,8 @@
 extern mod std;
 
 mod rusti {
-    #[abi = "rust-intrinsic"]  
-    pub extern {
+    #[abi = "rust-intrinsic"]
+    pub extern "rust-intrinsic" {
         fn ctpop8(x: i8) -> i8;
         fn ctpop16(x: i16) -> i16;
         fn ctpop32(x: i32) -> i32;
diff --git a/src/test/run-pass/intrinsics-math.rs b/src/test/run-pass/intrinsics-math.rs
index 500f06e0517..60e32a56ee5 100644
--- a/src/test/run-pass/intrinsics-math.rs
+++ b/src/test/run-pass/intrinsics-math.rs
@@ -15,8 +15,8 @@ extern mod std;
 use std::cmp::FuzzyEq;
 
 mod rusti {
-    #[abi = "rust-intrinsic"]  
-    pub extern {
+    #[abi = "rust-intrinsic"]
+    pub extern "rust-intrinsic" {
         fn sqrtf32(x: f32) -> f32;
         fn sqrtf64(x: f64) -> f64;
         fn powif32(a: f32, x: i32) -> f32;
diff --git a/src/test/run-pass/issue-2718.rs b/src/test/run-pass/issue-2718.rs
index 35b92d994d2..816c20ba35f 100644
--- a/src/test/run-pass/issue-2718.rs
+++ b/src/test/run-pass/issue-2718.rs
@@ -45,7 +45,6 @@ pub mod pipes {
         }
     }
 
-    #[abi = "rust-intrinsic"]
     mod rusti {
       pub fn atomic_xchg(_dst: &mut int, _src: int) -> int { fail!(); }
       pub fn atomic_xchg_acq(_dst: &mut int, _src: int) -> int { fail!(); }
diff --git a/src/test/run-pass/morestack-address.rs b/src/test/run-pass/morestack-address.rs
index 8089a2dc034..38628367eb8 100644
--- a/src/test/run-pass/morestack-address.rs
+++ b/src/test/run-pass/morestack-address.rs
@@ -11,7 +11,7 @@
 mod rusti {
     #[nolink]
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         pub fn morestack_addr() -> *();
     }
 }
diff --git a/src/test/run-pass/rec-align-u32.rs b/src/test/run-pass/rec-align-u32.rs
index 5cf978b5b60..703852d20bd 100644
--- a/src/test/run-pass/rec-align-u32.rs
+++ b/src/test/run-pass/rec-align-u32.rs
@@ -12,7 +12,7 @@
 
 mod rusti {
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         pub fn pref_align_of<T>() -> uint;
         pub fn min_align_of<T>() -> uint;
     }
diff --git a/src/test/run-pass/rec-align-u64.rs b/src/test/run-pass/rec-align-u64.rs
index cd4c8640424..96713d120be 100644
--- a/src/test/run-pass/rec-align-u64.rs
+++ b/src/test/run-pass/rec-align-u64.rs
@@ -12,7 +12,7 @@
 
 mod rusti {
     #[abi = "rust-intrinsic"]
-    pub extern {
+    pub extern "rust-intrinsic" {
         pub fn pref_align_of<T>() -> uint;
         pub fn min_align_of<T>() -> uint;
     }
diff --git a/src/test/run-pass/x86stdcall2.rs b/src/test/run-pass/x86stdcall2.rs
index 9145abbf5cb..452bfbf90b1 100644
--- a/src/test/run-pass/x86stdcall2.rs
+++ b/src/test/run-pass/x86stdcall2.rs
@@ -19,7 +19,7 @@ mod kernel32 {
 
     #[cfg(target_os = "win32")]
     #[abi = "stdcall"]
-    pub extern {
+    pub extern "stdcall" {
         pub fn GetProcessHeap() -> HANDLE;
         pub fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T)
                       -> LPVOID;