about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRichard Cobbe <ricobbe@microsoft.com>2021-06-08 13:56:06 -0700
committerRichard Cobbe <ricobbe@microsoft.com>2021-07-09 12:04:54 -0700
commita867dd4c7e65beb1453fcff9e252e7ab19f0e1d0 (patch)
treea1f439cc903d02a00ebc1e4120a34b59928df2d7
parent8b87e85394aa583b01e53aef06343dd0749a3324 (diff)
downloadrust-a867dd4c7e65beb1453fcff9e252e7ab19f0e1d0.tar.gz
rust-a867dd4c7e65beb1453fcff9e252e7ab19f0e1d0.zip
Add support for raw-dylib with stdcall, fastcall functions on i686-pc-windows-msvc.
-rw-r--r--compiler/rustc_codegen_llvm/src/back/archive.rs27
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs69
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs72
-rw-r--r--compiler/rustc_middle/src/middle/cstore.rs21
-rw-r--r--src/test/run-make/raw-dylib-alt-calling-convention/Makefile18
-rw-r--r--src/test/run-make/raw-dylib-alt-calling-convention/driver.rs5
-rw-r--r--src/test/run-make/raw-dylib-alt-calling-convention/extern.c123
-rw-r--r--src/test/run-make/raw-dylib-alt-calling-convention/lib.rs71
-rw-r--r--src/test/run-make/raw-dylib-alt-calling-convention/output.txt18
-rw-r--r--src/test/run-make/raw-dylib-c/Makefile (renamed from src/test/run-make/raw-dylib/Makefile)3
-rw-r--r--src/test/run-make/raw-dylib-c/driver.rs (renamed from src/test/run-make/raw-dylib/driver.rs)0
-rw-r--r--src/test/run-make/raw-dylib-c/extern_1.c (renamed from src/test/run-make/raw-dylib/extern_1.c)0
-rw-r--r--src/test/run-make/raw-dylib-c/extern_2.c (renamed from src/test/run-make/raw-dylib/extern_2.c)0
-rw-r--r--src/test/run-make/raw-dylib-c/lib.rs (renamed from src/test/run-make/raw-dylib/lib.rs)0
-rw-r--r--src/test/run-make/raw-dylib-c/output.txt (renamed from src/test/run-make/raw-dylib/output.txt)0
-rw-r--r--src/test/ui/rfc-2627-raw-dylib/multiple-definitions.rs19
-rw-r--r--src/test/ui/rfc-2627-raw-dylib/multiple-definitions.stderr17
-rw-r--r--src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs13
-rw-r--r--src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr8
19 files changed, 436 insertions, 48 deletions
diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs
index 64416bced31..6ac7093b7de 100644
--- a/compiler/rustc_codegen_llvm/src/back/archive.rs
+++ b/compiler/rustc_codegen_llvm/src/back/archive.rs
@@ -12,7 +12,7 @@ use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
 use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
 use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME};
 use rustc_data_structures::temp_dir::MaybeTempDir;
-use rustc_middle::middle::cstore::DllImport;
+use rustc_middle::middle::cstore::{DllCallingConvention, DllImport};
 use rustc_session::Session;
 use rustc_span::symbol::Symbol;
 
@@ -208,10 +208,12 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
         // have any \0 characters
         let import_name_vector: Vec<CString> = dll_imports
             .iter()
-            .map(if self.config.sess.target.arch == "x86" {
-                |import: &DllImport| CString::new(format!("_{}", import.name.to_string())).unwrap()
-            } else {
-                |import: &DllImport| CString::new(import.name.to_string()).unwrap()
+            .map(|import: &DllImport| {
+                if self.config.sess.target.arch == "x86" {
+                    LlvmArchiveBuilder::i686_decorated_name(import)
+                } else {
+                    CString::new(import.name.to_string()).unwrap()
+                }
             })
             .collect();
 
@@ -391,6 +393,21 @@ impl<'a> LlvmArchiveBuilder<'a> {
             ret
         }
     }
+
+    fn i686_decorated_name(import: &DllImport) -> CString {
+        let name = import.name;
+        // We verified during construction that `name` does not contain any NULL characters, so the
+        // conversion to CString is guaranteed to succeed.
+        CString::new(match import.calling_convention {
+            DllCallingConvention::C => format!("_{}", name),
+            DllCallingConvention::Stdcall(arg_list_size) => format!("_{}@{}", name, arg_list_size),
+            DllCallingConvention::Fastcall(arg_list_size) => format!("@{}@{}", name, arg_list_size),
+            DllCallingConvention::Vectorcall(arg_list_size) => {
+                format!("{}@@{}", name, arg_list_size)
+            }
+        })
+        .unwrap()
+    }
 }
 
 fn string_to_io_error(s: String) -> io::Error {
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 59447e9de13..f9efa448c93 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -3,7 +3,7 @@ use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_errors::Handler;
 use rustc_fs_util::fix_windows_verbatim_for_gcc;
 use rustc_hir::def_id::CrateNum;
-use rustc_middle::middle::cstore::DllImport;
+use rustc_middle::middle::cstore::{DllCallingConvention, DllImport};
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
 use rustc_session::config::{OutputFilenames, OutputType, PrintRequest};
@@ -34,8 +34,8 @@ use object::write::Object;
 use object::{Architecture, BinaryFormat, Endianness, FileFlags, SectionFlags, SectionKind};
 use tempfile::Builder as TempFileBuilder;
 
-use std::cmp::Ordering;
 use std::ffi::OsString;
+use std::iter::FromIterator;
 use std::path::{Path, PathBuf};
 use std::process::{ExitStatus, Output, Stdio};
 use std::{ascii, char, env, fmt, fs, io, mem, str};
@@ -259,7 +259,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
     }
 
     for (raw_dylib_name, raw_dylib_imports) in
-        collate_raw_dylibs(&codegen_results.crate_info.used_libraries)
+        collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)
     {
         ab.inject_dll_import_lib(&raw_dylib_name, &raw_dylib_imports, tmpdir);
     }
@@ -451,8 +451,11 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
 /// then the CodegenResults value contains one NativeLib instance for each block.  However, the
 /// linker appears to expect only a single import library for each library used, so we need to
 /// collate the symbols together by library name before generating the import libraries.
-fn collate_raw_dylibs(used_libraries: &[NativeLib]) -> Vec<(String, Vec<DllImport>)> {
-    let mut dylib_table: FxHashMap<String, FxHashSet<Symbol>> = FxHashMap::default();
+fn collate_raw_dylibs(
+    sess: &Session,
+    used_libraries: &[NativeLib],
+) -> Vec<(String, Vec<DllImport>)> {
+    let mut dylib_table: FxHashMap<String, FxHashSet<DllImport>> = FxHashMap::default();
 
     for lib in used_libraries {
         if lib.kind == NativeLibKind::RawDylib {
@@ -464,35 +467,51 @@ fn collate_raw_dylibs(used_libraries: &[NativeLib]) -> Vec<(String, Vec<DllImpor
             } else {
                 format!("{}.dll", name)
             };
-            dylib_table
-                .entry(name)
-                .or_default()
-                .extend(lib.dll_imports.iter().map(|import| import.name));
+            dylib_table.entry(name).or_default().extend(lib.dll_imports.iter().cloned());
         }
     }
 
-    // FIXME: when we add support for ordinals, fix this to propagate ordinals.  Also figure out
-    // what we should do if we have two DllImport values with the same name but different
-    // ordinals.
-    let mut result = dylib_table
+    // Rustc already signals an error if we have two imports with the same name but different
+    // calling conventions (or function signatures), so we don't have pay attention to those
+    // when ordering.
+    // FIXME: when we add support for ordinals, figure out if we need to do anything if we
+    // have two DllImport values with the same name but different ordinals.
+    let mut result: Vec<(String, Vec<DllImport>)> = dylib_table
         .into_iter()
-        .map(|(lib_name, imported_names)| {
-            let mut names = imported_names
-                .iter()
-                .map(|name| DllImport { name: *name, ordinal: None })
-                .collect::<Vec<_>>();
-            names.sort_unstable_by(|a: &DllImport, b: &DllImport| {
-                match a.name.as_str().cmp(&b.name.as_str()) {
-                    Ordering::Equal => a.ordinal.cmp(&b.ordinal),
-                    x => x,
-                }
-            });
-            (lib_name, names)
+        .map(|(lib_name, import_table)| {
+            let mut imports = Vec::from_iter(import_table.into_iter());
+            imports.sort_unstable_by_key(|x: &DllImport| x.name.as_str());
+            (lib_name, imports)
         })
         .collect::<Vec<_>>();
     result.sort_unstable_by(|a: &(String, Vec<DllImport>), b: &(String, Vec<DllImport>)| {
         a.0.cmp(&b.0)
     });
+    let result = result;
+
+    // Check for multiple imports with the same name but different calling conventions or
+    // (when relevant) argument list sizes.  Rustc only signals an error for this if the
+    // declarations are at the same scope level; if one shadows the other, we only get a lint
+    // warning.
+    for (library, imports) in &result {
+        let mut import_table: FxHashMap<Symbol, DllCallingConvention> = FxHashMap::default();
+        for import in imports {
+            if let Some(old_convention) =
+                import_table.insert(import.name, import.calling_convention)
+            {
+                if import.calling_convention != old_convention {
+                    sess.span_fatal(
+                        import.span,
+                        &format!(
+                            "multiple definitions of external function `{}` from library `{}` have different calling conventions",
+                            import.name,
+                            library,
+                    ));
+                }
+            }
+        }
+    }
+
     result
 }
 
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index cd4c394ae14..16b4d26b37b 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -3,8 +3,8 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
-use rustc_middle::middle::cstore::{DllImport, NativeLib};
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::middle::cstore::{DllCallingConvention, DllImport, NativeLib};
+use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
 use rustc_session::parse::feature_err;
 use rustc_session::utils::NativeLibKind;
 use rustc_session::Session;
@@ -199,22 +199,10 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
             }
 
             if lib.kind == NativeLibKind::RawDylib {
-                match abi {
-                    Abi::C { .. } => (),
-                    Abi::Cdecl => (),
-                    _ => {
-                        if sess.target.arch == "x86" {
-                            sess.span_fatal(
-                                it.span,
-                                r#"`#[link(kind = "raw-dylib")]` only supports C and Cdecl ABIs"#,
-                            );
-                        }
-                    }
-                };
                 lib.dll_imports.extend(
                     foreign_mod_items
                         .iter()
-                        .map(|child_item| DllImport { name: child_item.ident.name, ordinal: None }),
+                        .map(|child_item| self.build_dll_import(abi, child_item)),
                 );
             }
 
@@ -396,4 +384,58 @@ impl Collector<'tcx> {
             }
         }
     }
+
+    fn i686_arg_list_size(&self, item: &hir::ForeignItemRef<'_>) -> usize {
+        let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions(
+            self.tcx
+                .type_of(item.id.def_id)
+                .fn_sig(self.tcx)
+                .inputs()
+                .map_bound(|slice| self.tcx.mk_type_list(slice.iter())),
+        );
+
+        argument_types
+            .iter()
+            .map(|ty| {
+                let layout = self
+                    .tcx
+                    .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty })
+                    .expect("layout")
+                    .layout;
+                // In both stdcall and fastcall, we always round up the argument size to the
+                // nearest multiple of 4 bytes.
+                (layout.size.bytes_usize() + 3) & !3
+            })
+            .sum()
+    }
+
+    fn build_dll_import(&self, abi: Abi, item: &hir::ForeignItemRef<'_>) -> DllImport {
+        let calling_convention = if self.tcx.sess.target.arch == "x86" {
+            match abi {
+                Abi::C { .. } | Abi::Cdecl => DllCallingConvention::C,
+                Abi::Stdcall { .. } | Abi::System { .. } => {
+                    DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
+                }
+                Abi::Fastcall => DllCallingConvention::Fastcall(self.i686_arg_list_size(item)),
+                // Vectorcall is intentionally not supported at this time.
+                _ => {
+                    self.tcx.sess.span_fatal(
+                        item.span,
+                        r#"ABI not supported by `#[link(kind = "raw-dylib")]` on i686"#,
+                    );
+                }
+            }
+        } else {
+            match abi {
+                Abi::C { .. } | Abi::Win64 | Abi::System { .. } => DllCallingConvention::C,
+                _ => {
+                    self.tcx.sess.span_fatal(
+                        item.span,
+                        r#"ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture"#,
+                    );
+                }
+            }
+        };
+        DllImport { name: item.ident.name, ordinal: None, calling_convention, span: item.span }
+    }
 }
diff --git a/compiler/rustc_middle/src/middle/cstore.rs b/compiler/rustc_middle/src/middle/cstore.rs
index 3c4c4a84d24..d69904f7b11 100644
--- a/compiler/rustc_middle/src/middle/cstore.rs
+++ b/compiler/rustc_middle/src/middle/cstore.rs
@@ -77,10 +77,29 @@ pub struct NativeLib {
     pub dll_imports: Vec<DllImport>,
 }
 
-#[derive(Clone, Debug, Encodable, Decodable, HashStable)]
+#[derive(Clone, Debug, PartialEq, Eq, Encodable, Decodable, Hash, HashStable)]
 pub struct DllImport {
     pub name: Symbol,
     pub ordinal: Option<u16>,
+    /// Calling convention for the function.
+    ///
+    /// On x86_64, this is always `DllCallingConvention::C`; on i686, it can be any
+    /// of the values, and we use `DllCallingConvention::C` to represent `"cdecl"`.
+    pub calling_convention: DllCallingConvention,
+    /// Span of import's "extern" declaration; used for diagnostics.
+    pub span: Span,
+}
+
+/// Calling convention for a function defined in an external library.
+///
+/// The usize value, where present, indicates the size of the function's argument list
+/// in bytes.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Encodable, Decodable, Hash, HashStable)]
+pub enum DllCallingConvention {
+    C,
+    Stdcall(usize),
+    Fastcall(usize),
+    Vectorcall(usize),
 }
 
 #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/Makefile b/src/test/run-make/raw-dylib-alt-calling-convention/Makefile
new file mode 100644
index 00000000000..0f874333fa0
--- /dev/null
+++ b/src/test/run-make/raw-dylib-alt-calling-convention/Makefile
@@ -0,0 +1,18 @@
+# Test the behavior of #[link(.., kind = "raw-dylib")] with alternative calling conventions.
+
+# only-i686-pc-windows-msvc
+
+-include ../../run-make-fulldeps/tools.mk
+
+all:
+	$(call COMPILE_OBJ,"$(TMPDIR)"/extern.obj,extern.c)
+	$(CC) "$(TMPDIR)"/extern.obj -link -dll -out:"$(TMPDIR)"/extern.dll
+	$(RUSTC) --crate-type lib --crate-name raw_dylib_alt_calling_convention_test lib.rs
+	$(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)"
+	"$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt
+
+ifdef RUSTC_BLESS_TEST
+	cp "$(TMPDIR)"/output.txt output.txt
+else
+	$(DIFF) output.txt "$(TMPDIR)"/output.txt
+endif
diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/driver.rs b/src/test/run-make/raw-dylib-alt-calling-convention/driver.rs
new file mode 100644
index 00000000000..3710507f5e4
--- /dev/null
+++ b/src/test/run-make/raw-dylib-alt-calling-convention/driver.rs
@@ -0,0 +1,5 @@
+extern crate raw_dylib_alt_calling_convention_test;
+
+fn main() {
+    raw_dylib_alt_calling_convention_test::library_function();
+}
diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/extern.c b/src/test/run-make/raw-dylib-alt-calling-convention/extern.c
new file mode 100644
index 00000000000..8f64abf2fb5
--- /dev/null
+++ b/src/test/run-make/raw-dylib-alt-calling-convention/extern.c
@@ -0,0 +1,123 @@
+#include <stdio.h>
+#include <stdint.h>
+
+struct S {
+    uint8_t x;
+    int32_t y;
+};
+
+struct S2 {
+    int32_t x;
+    uint8_t y;
+};
+
+struct S3 {
+    uint8_t x[5];
+};
+
+__declspec(dllexport) void __stdcall stdcall_fn_1(int i) {
+    printf("stdcall_fn_1(%d)\n", i);
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __stdcall stdcall_fn_2(uint8_t i, float f) {
+    printf("stdcall_fn_2(%d, %.1f)\n", i, f);
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __stdcall stdcall_fn_3(double d) {
+    printf("stdcall_fn_3(%.1f)\n", d);
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __stdcall stdcall_fn_4(uint8_t i, uint8_t j, float f) {
+    printf("stdcall_fn_4(%d, %d, %.1f)\n", i, j, f);
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __stdcall stdcall_fn_5(struct S s, int i) {
+    printf("stdcall_fn_5(S { x: %d, y: %d }, %d)\n", s.x, s.y, i);
+    fflush(stdout);
+}
+
+// Test that stdcall support works correctly with the nullable pointer optimization.
+__declspec(dllexport) void __stdcall stdcall_fn_6(struct S* s) {
+    if (s) {
+        printf("stdcall_fn_6(S { x: %d, y: %d })\n", s->x, s->y);
+    } else {
+        printf("stdcall_fn_6(null)\n");
+    }
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __stdcall stdcall_fn_7(struct S2 s, int i) {
+    printf("stdcall_fn_7(S2 { x: %d, y: %d }, %d)\n", s.x, s.y, i);
+    fflush(stdout);
+}
+
+// Verify that we compute the correct amount of space in the argument list for a 5-byte struct.
+__declspec(dllexport) void __stdcall stdcall_fn_8(struct S3 s, struct S3 t) {
+    printf("stdcall_fn_8(S3 { x: [%d, %d, %d, %d, %d] }, S3 { x: [%d, %d, %d, %d, %d] })\n",
+           s.x[0], s.x[1], s.x[2], s.x[3], s.x[4],
+           t.x[0], t.x[1], t.x[2], t.x[3], t.x[4]
+        );
+    fflush(stdout);
+}
+
+// test whether f64/double values are aligned on 4-byte or 8-byte boundaries.
+__declspec(dllexport) void __stdcall stdcall_fn_9(uint8_t x, double y) {
+    printf("stdcall_fn_9(%d, %.1f)\n", x, y);
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __fastcall fastcall_fn_1(int i) {
+    printf("fastcall_fn_1(%d)\n", i);
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __fastcall fastcall_fn_2(uint8_t i, float f) {
+    printf("fastcall_fn_2(%d, %.1f)\n", i, f);
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __fastcall fastcall_fn_3(double d) {
+    printf("fastcall_fn_3(%.1f)\n", d);
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __fastcall fastcall_fn_4(uint8_t i, uint8_t j, float f) {
+    printf("fastcall_fn_4(%d, %d, %.1f)\n", i, j, f);
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __fastcall fastcall_fn_5(struct S s, int i) {
+    printf("fastcall_fn_5(S { x: %d, y: %d }, %d)\n", s.x, s.y, i);
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __fastcall fastcall_fn_6(struct S* s) {
+    if (s) {
+        printf("fastcall_fn_6(S { x: %d, y: %d })\n", s->x, s->y);
+    } else {
+        printf("fastcall_fn_6(null)\n");
+    }
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __fastcall fastcall_fn_7(struct S2 s, int i) {
+    printf("fastcall_fn_7(S2 { x: %d, y: %d }, %d)\n", s.x, s.y, i);
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __fastcall fastcall_fn_8(struct S3 s, struct S3 t) {
+    printf("fastcall_fn_8(S3 { x: [%d, %d, %d, %d, %d] }, S3 { x: [%d, %d, %d, %d, %d] })\n",
+           s.x[0], s.x[1], s.x[2], s.x[3], s.x[4],
+           t.x[0], t.x[1], t.x[2], t.x[3], t.x[4]
+        );
+    fflush(stdout);
+}
+
+__declspec(dllexport) void __fastcall fastcall_fn_9(uint8_t x, double y) {
+    printf("fastcall_fn_9(%d, %.1f)\n", x, y);
+    fflush(stdout);
+}
diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs b/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs
new file mode 100644
index 00000000000..ba0f1418aba
--- /dev/null
+++ b/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs
@@ -0,0 +1,71 @@
+#![feature(raw_dylib)]
+
+#[repr(C)]
+#[derive(Clone)]
+struct S {
+    x: u8,
+    y: i32,
+}
+
+#[repr(C)]
+#[derive(Clone)]
+struct S2 {
+    x: i32,
+    y: u8,
+}
+
+#[repr(C)]
+#[derive(Clone)]
+struct S3 {
+    x: [u8; 5],
+}
+
+#[link(name = "extern", kind = "raw-dylib")]
+extern "stdcall" {
+    fn stdcall_fn_1(i: i32);
+    fn stdcall_fn_2(c: u8, f: f32);
+    fn stdcall_fn_3(d: f64);
+    fn stdcall_fn_4(i: u8, j: u8, f: f32);
+    fn stdcall_fn_5(a: S, b: i32);
+    fn stdcall_fn_6(a: Option<&S>);
+    fn stdcall_fn_7(a: S2, b: i32);
+    fn stdcall_fn_8(a: S3, b: S3);
+    fn stdcall_fn_9(x: u8, y: f64);
+}
+
+#[link(name = "extern", kind = "raw-dylib")]
+extern "fastcall" {
+    fn fastcall_fn_1(i: i32);
+    fn fastcall_fn_2(c: u8, f: f32);
+    fn fastcall_fn_3(d: f64);
+    fn fastcall_fn_4(i: u8, j: u8, f: f32);
+    fn fastcall_fn_5(a: S, b: i32);
+    fn fastcall_fn_6(a: Option<&S>);
+    fn fastcall_fn_7(a: S2, b: i32);
+    fn fastcall_fn_8(a: S3, b: S3);
+    fn fastcall_fn_9(x: u8, y: f64);
+}
+
+pub fn library_function() {
+    unsafe {
+        stdcall_fn_1(14);
+        stdcall_fn_2(16, 3.5);
+        stdcall_fn_3(3.5);
+        stdcall_fn_4(1, 2, 3.0);
+        stdcall_fn_5(S { x: 1, y: 2 }, 16);
+        stdcall_fn_6(Some(&S { x: 10, y: 12 }));
+        stdcall_fn_7(S2 { x: 15, y: 16 }, 3);
+        stdcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] });
+        stdcall_fn_9(1, 3.0);
+
+        fastcall_fn_1(14);
+        fastcall_fn_2(16, 3.5);
+        fastcall_fn_3(3.5);
+        fastcall_fn_4(1, 2, 3.0);
+        fastcall_fn_5(S { x: 1, y: 2 }, 16);
+        fastcall_fn_6(Some(&S { x: 10, y: 12 }));
+        fastcall_fn_7(S2 { x: 15, y: 16 }, 3);
+        fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] });
+        fastcall_fn_9(1, 3.0);
+    }
+}
diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/output.txt b/src/test/run-make/raw-dylib-alt-calling-convention/output.txt
new file mode 100644
index 00000000000..be598a22027
--- /dev/null
+++ b/src/test/run-make/raw-dylib-alt-calling-convention/output.txt
@@ -0,0 +1,18 @@
+stdcall_fn_1(14)
+stdcall_fn_2(16, 3.5)
+stdcall_fn_3(3.5)
+stdcall_fn_4(1, 2, 3.0)
+stdcall_fn_5(S { x: 1, y: 2 }, 16)
+stdcall_fn_6(S { x: 10, y: 12 })
+stdcall_fn_7(S2 { x: 15, y: 16 }, 3)
+stdcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] })
+stdcall_fn_9(1, 3.0)
+fastcall_fn_1(14)
+fastcall_fn_2(16, 3.5)
+fastcall_fn_3(3.5)
+fastcall_fn_4(1, 2, 3.0)
+fastcall_fn_5(S { x: 1, y: 2 }, 16)
+fastcall_fn_6(S { x: 10, y: 12 })
+fastcall_fn_7(S2 { x: 15, y: 16 }, 3)
+fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] })
+fastcall_fn_9(1, 3.0)
diff --git a/src/test/run-make/raw-dylib/Makefile b/src/test/run-make/raw-dylib-c/Makefile
index 7ce46fd9331..26ab4d34764 100644
--- a/src/test/run-make/raw-dylib/Makefile
+++ b/src/test/run-make/raw-dylib-c/Makefile
@@ -1,7 +1,6 @@
 # Test the behavior of #[link(.., kind = "raw-dylib")] on windows-msvc
 
-# only-windows
-# only-msvc
+# only-windows-msvc
 
 -include ../../run-make-fulldeps/tools.mk
 
diff --git a/src/test/run-make/raw-dylib/driver.rs b/src/test/run-make/raw-dylib-c/driver.rs
index 4059ede11fc..4059ede11fc 100644
--- a/src/test/run-make/raw-dylib/driver.rs
+++ b/src/test/run-make/raw-dylib-c/driver.rs
diff --git a/src/test/run-make/raw-dylib/extern_1.c b/src/test/run-make/raw-dylib-c/extern_1.c
index 72737c086eb..72737c086eb 100644
--- a/src/test/run-make/raw-dylib/extern_1.c
+++ b/src/test/run-make/raw-dylib-c/extern_1.c
diff --git a/src/test/run-make/raw-dylib/extern_2.c b/src/test/run-make/raw-dylib-c/extern_2.c
index ae87fc3f821..ae87fc3f821 100644
--- a/src/test/run-make/raw-dylib/extern_2.c
+++ b/src/test/run-make/raw-dylib-c/extern_2.c
diff --git a/src/test/run-make/raw-dylib/lib.rs b/src/test/run-make/raw-dylib-c/lib.rs
index d8e6301f38e..d8e6301f38e 100644
--- a/src/test/run-make/raw-dylib/lib.rs
+++ b/src/test/run-make/raw-dylib-c/lib.rs
diff --git a/src/test/run-make/raw-dylib/output.txt b/src/test/run-make/raw-dylib-c/output.txt
index 7800cba1872..7800cba1872 100644
--- a/src/test/run-make/raw-dylib/output.txt
+++ b/src/test/run-make/raw-dylib-c/output.txt
diff --git a/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.rs b/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.rs
new file mode 100644
index 00000000000..3f7597498ba
--- /dev/null
+++ b/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.rs
@@ -0,0 +1,19 @@
+// only-i686-pc-windows-msvc
+// compile-flags: --crate-type lib --emit link
+#![allow(clashing_extern_declarations)]
+#![feature(raw_dylib)]
+//~^ WARN the feature `raw_dylib` is incomplete
+#[link(name = "foo", kind = "raw-dylib")]
+extern "C" {
+    fn f(x: i32);
+    //~^ ERROR multiple definitions of external function `f` from library `foo.dll` have different calling conventions
+}
+
+pub fn lib_main() {
+    #[link(name = "foo", kind = "raw-dylib")]
+    extern "stdcall" {
+        fn f(x: i32);
+    }
+
+    unsafe { f(42); }
+}
diff --git a/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.stderr b/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.stderr
new file mode 100644
index 00000000000..91f6f0cf722
--- /dev/null
+++ b/src/test/ui/rfc-2627-raw-dylib/multiple-definitions.stderr
@@ -0,0 +1,17 @@
+warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/multiple-definitions.rs:4:12
+   |
+LL | #![feature(raw_dylib)]
+   |            ^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
+
+error: multiple definitions of external function `f` from library `foo.dll` have different calling conventions
+  --> $DIR/multiple-definitions.rs:8:5
+   |
+LL |     fn f(x: i32);
+   |     ^^^^^^^^^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
diff --git a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs
new file mode 100644
index 00000000000..e5a5ac2eb2b
--- /dev/null
+++ b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs
@@ -0,0 +1,13 @@
+// only-x86_64-pc-windows-msvc
+// compile-flags: --crate-type lib --emit link
+#![allow(incomplete_features)]
+#![feature(raw_dylib)]
+#[link(name = "foo", kind = "raw-dylib")]
+extern "stdcall" {
+    fn f(x: i32);
+    //~^ ERROR ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture
+}
+
+pub fn lib_main() {
+    unsafe { f(42); }
+}
diff --git a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr
new file mode 100644
index 00000000000..fc9008128ae
--- /dev/null
+++ b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr
@@ -0,0 +1,8 @@
+error: ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture
+  --> $DIR/unsupported-abi.rs:7:5
+   |
+LL |     fn f(x: i32);
+   |     ^^^^^^^^^^^^^
+
+error: aborting due to previous error
+