summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml3
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs4
-rw-r--r--library/std_detect/src/detect/arch/riscv.rs3
-rw-r--r--library/std_detect/src/detect/os/linux/riscv.rs8
-rw-r--r--library/std_detect/src/detect/os/riscv.rs2
-rw-r--r--library/std_detect/tests/cpu-detection.rs1
-rw-r--r--library/test/src/lib.rs4
-rwxr-xr-xsrc/ci/scripts/install-rust.sh15
-rw-r--r--src/librustdoc/doctest.rs3
-rw-r--r--src/tools/run-make-support/src/external_deps/llvm.rs30
-rw-r--r--src/tools/run-make-support/src/external_deps/rustc.rs6
-rw-r--r--src/tools/run-make-support/src/lib.rs5
-rw-r--r--tests/run-make/cross-lang-lto-clang/rmake.rs59
-rw-r--r--tests/run-make/fat-then-thin-lto/lib.rs13
-rw-r--r--tests/run-make/fat-then-thin-lto/main.rs11
-rw-r--r--tests/run-make/fat-then-thin-lto/rmake.rs25
-rw-r--r--tests/run-make/linker-plugin-lto-fat/ir.ll6
-rw-r--r--tests/run-make/linker-plugin-lto-fat/main.rs22
-rw-r--r--tests/run-make/linker-plugin-lto-fat/rmake.rs33
19 files changed, 203 insertions, 50 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e91ef4abb12..6ce543071d8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -159,9 +159,6 @@ jobs:
       - name: show the current environment
         run: src/ci/scripts/dump-environment.sh
 
-      - name: install rust
-        run: src/ci/scripts/install-rust.sh
-
       - name: install awscli
         run: src/ci/scripts/install-awscli.sh
 
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 6773d3e24e9..aa29afb7f5b 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -214,7 +214,9 @@ impl ModuleConfig {
                 false
             ),
             emit_obj,
-            emit_thin_lto: sess.opts.unstable_opts.emit_thin_lto,
+            // thin lto summaries prevent fat lto, so do not emit them if fat
+            // lto is requested. See PR #136840 for background information.
+            emit_thin_lto: sess.opts.unstable_opts.emit_thin_lto && sess.lto() != Lto::Fat,
             emit_thin_lto_summary: if_regular!(
                 sess.opts.output_types.contains_key(&OutputType::ThinLinkBitcode),
                 false
diff --git a/library/std_detect/src/detect/arch/riscv.rs b/library/std_detect/src/detect/arch/riscv.rs
index b86190d7bbf..1d21b1d4855 100644
--- a/library/std_detect/src/detect/arch/riscv.rs
+++ b/library/std_detect/src/detect/arch/riscv.rs
@@ -73,6 +73,7 @@ features! {
     /// * Zihintpause: `"zihintpause"`
     /// * Zihpm: `"zihpm"`
     /// * Zimop: `"zimop"`
+    /// * Zabha: `"zabha"`
     /// * Zacas: `"zacas"`
     /// * Zawrs: `"zawrs"`
     /// * Zfa: `"zfa"`
@@ -195,6 +196,8 @@ features! {
     /// "Zaamo" Extension for Atomic Memory Operations
     @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zawrs: "zawrs";
     /// "Zawrs" Extension for Wait-on-Reservation-Set Instructions
+    @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zabha: "zabha";
+    /// "Zabha" Extension for Byte and Halfword Atomic Memory Operations
     @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zacas: "zacas";
     /// "Zacas" Extension for Atomic Compare-and-Swap (CAS) Instructions
     @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zam: "zam";
diff --git a/library/std_detect/src/detect/os/linux/riscv.rs b/library/std_detect/src/detect/os/linux/riscv.rs
index dbb3664890e..18f9f68ec67 100644
--- a/library/std_detect/src/detect/os/linux/riscv.rs
+++ b/library/std_detect/src/detect/os/linux/riscv.rs
@@ -10,13 +10,13 @@ use super::super::riscv::imply_features;
 use super::auxvec;
 use crate::detect::{Feature, bit, cache};
 
-// See <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/prctl.h?h=v6.15>
+// See <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/prctl.h?h=v6.16>
 // for runtime status query constants.
 const PR_RISCV_V_GET_CONTROL: libc::c_int = 70;
 const PR_RISCV_V_VSTATE_CTRL_ON: libc::c_int = 2;
 const PR_RISCV_V_VSTATE_CTRL_CUR_MASK: libc::c_int = 3;
 
-// See <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwprobe.h?h=v6.15>
+// See <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwprobe.h?h=v6.16>
 // for riscv_hwprobe struct and hardware probing constants.
 
 #[repr(C)]
@@ -98,6 +98,7 @@ const RISCV_HWPROBE_EXT_ZVFBFWMA: u64 = 1 << 54;
 const RISCV_HWPROBE_EXT_ZICBOM: u64 = 1 << 55;
 const RISCV_HWPROBE_EXT_ZAAMO: u64 = 1 << 56;
 const RISCV_HWPROBE_EXT_ZALRSC: u64 = 1 << 57;
+const RISCV_HWPROBE_EXT_ZABHA: u64 = 1 << 58;
 
 const RISCV_HWPROBE_KEY_CPUPERF_0: i64 = 5;
 const RISCV_HWPROBE_MISALIGNED_FAST: u64 = 3;
@@ -138,7 +139,7 @@ pub(crate) fn detect_features() -> cache::Initializer {
     // Use auxiliary vector to enable single-letter ISA extensions.
     // The values are part of the platform-specific [asm/hwcap.h][hwcap]
     //
-    // [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.15
+    // [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.16
     let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform
     let mut has_i = bit::test(auxv.hwcap, (b'i' - b'a').into());
     #[allow(clippy::eq_op)]
@@ -233,6 +234,7 @@ pub(crate) fn detect_features() -> cache::Initializer {
         enable_feature(Feature::zalrsc, test(RISCV_HWPROBE_EXT_ZALRSC));
         enable_feature(Feature::zaamo, test(RISCV_HWPROBE_EXT_ZAAMO));
         enable_feature(Feature::zawrs, test(RISCV_HWPROBE_EXT_ZAWRS));
+        enable_feature(Feature::zabha, test(RISCV_HWPROBE_EXT_ZABHA));
         enable_feature(Feature::zacas, test(RISCV_HWPROBE_EXT_ZACAS));
         enable_feature(Feature::ztso, test(RISCV_HWPROBE_EXT_ZTSO));
 
diff --git a/library/std_detect/src/detect/os/riscv.rs b/library/std_detect/src/detect/os/riscv.rs
index dc9a4036d86..c6acbd3525b 100644
--- a/library/std_detect/src/detect/os/riscv.rs
+++ b/library/std_detect/src/detect/os/riscv.rs
@@ -90,7 +90,7 @@ pub(crate) fn imply_features(mut value: cache::Initializer) -> cache::Initialize
         group!(zks == zbkb & zbkc & zbkx & zksed & zksh);
         group!(zk == zkn & zkr & zkt);
 
-        imply!(zacas => zaamo);
+        imply!(zabha | zacas => zaamo);
         group!(a == zalrsc & zaamo);
 
         group!(b == zba & zbb & zbs);
diff --git a/library/std_detect/tests/cpu-detection.rs b/library/std_detect/tests/cpu-detection.rs
index 5ad32d83237..0c4fa57f2b4 100644
--- a/library/std_detect/tests/cpu-detection.rs
+++ b/library/std_detect/tests/cpu-detection.rs
@@ -242,6 +242,7 @@ fn riscv_linux() {
     println!("zalrsc: {}", is_riscv_feature_detected!("zalrsc"));
     println!("zaamo: {}", is_riscv_feature_detected!("zaamo"));
     println!("zawrs: {}", is_riscv_feature_detected!("zawrs"));
+    println!("zabha: {}", is_riscv_feature_detected!("zabha"));
     println!("zacas: {}", is_riscv_feature_detected!("zacas"));
     println!("zam: {}", is_riscv_feature_detected!("zam"));
     println!("ztso: {}", is_riscv_feature_detected!("ztso"));
diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs
index 7f56d1e3626..1190bb56b97 100644
--- a/library/test/src/lib.rs
+++ b/library/test/src/lib.rs
@@ -89,8 +89,8 @@ use options::RunStrategy;
 use test_result::*;
 use time::TestExecTime;
 
-// Process exit code to be used to indicate test failures.
-const ERROR_EXIT_CODE: i32 = 101;
+/// Process exit code to be used to indicate test failures.
+pub const ERROR_EXIT_CODE: i32 = 101;
 
 const SECONDARY_TEST_INVOKER_VAR: &str = "__RUST_TEST_INVOKE";
 const SECONDARY_TEST_BENCH_BENCHMARKS_VAR: &str = "__RUST_TEST_BENCH_BENCHMARKS";
diff --git a/src/ci/scripts/install-rust.sh b/src/ci/scripts/install-rust.sh
deleted file mode 100755
index e4aee98c9fb..00000000000
--- a/src/ci/scripts/install-rust.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-
-# The Arm64 Windows Runner does not have Rust already installed
-# https://github.com/actions/partner-runner-images/issues/77
-
-set -euo pipefail
-IFS=$'\n\t'
-
-source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
-
-if [[ "${CI_JOB_NAME}" = *aarch64* ]] && isWindows; then
-    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
-    sh -s -- -y -q --default-host aarch64-pc-windows-msvc
-    ciCommandAddPath "${USERPROFILE}/.cargo/bin"
-fi
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 0bef091468f..35ace656638 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -409,7 +409,8 @@ pub(crate) fn run_tests(
         // We ensure temp dir destructor is called.
         std::mem::drop(temp_dir);
         times.display_times();
-        // libtest::ERROR_EXIT_CODE is not public but it's the same value.
+        // FIXME(GuillaumeGomez): Uncomment the next line once #144297 has been merged.
+        // std::process::exit(test::ERROR_EXIT_CODE);
         std::process::exit(101);
     }
 }
diff --git a/src/tools/run-make-support/src/external_deps/llvm.rs b/src/tools/run-make-support/src/external_deps/llvm.rs
index 9a6e35da3fe..939160d9f41 100644
--- a/src/tools/run-make-support/src/external_deps/llvm.rs
+++ b/src/tools/run-make-support/src/external_deps/llvm.rs
@@ -60,6 +60,12 @@ pub fn llvm_pdbutil() -> LlvmPdbutil {
     LlvmPdbutil::new()
 }
 
+/// Construct a new `llvm-as` invocation. This assumes that `llvm-as` is available
+/// at `$LLVM_BIN_DIR/llvm-as`.
+pub fn llvm_as() -> LlvmAs {
+    LlvmAs::new()
+}
+
 /// Construct a new `llvm-dis` invocation. This assumes that `llvm-dis` is available
 /// at `$LLVM_BIN_DIR/llvm-dis`.
 pub fn llvm_dis() -> LlvmDis {
@@ -135,6 +141,13 @@ pub struct LlvmPdbutil {
     cmd: Command,
 }
 
+/// A `llvm-as` invocation builder.
+#[derive(Debug)]
+#[must_use]
+pub struct LlvmAs {
+    cmd: Command,
+}
+
 /// A `llvm-dis` invocation builder.
 #[derive(Debug)]
 #[must_use]
@@ -158,6 +171,7 @@ crate::macros::impl_common_helpers!(LlvmNm);
 crate::macros::impl_common_helpers!(LlvmBcanalyzer);
 crate::macros::impl_common_helpers!(LlvmDwarfdump);
 crate::macros::impl_common_helpers!(LlvmPdbutil);
+crate::macros::impl_common_helpers!(LlvmAs);
 crate::macros::impl_common_helpers!(LlvmDis);
 crate::macros::impl_common_helpers!(LlvmObjcopy);
 
@@ -441,6 +455,22 @@ impl LlvmObjcopy {
     }
 }
 
+impl LlvmAs {
+    /// Construct a new `llvm-as` invocation. This assumes that `llvm-as` is available
+    /// at `$LLVM_BIN_DIR/llvm-as`.
+    pub fn new() -> Self {
+        let llvm_as = llvm_bin_dir().join("llvm-as");
+        let cmd = Command::new(llvm_as);
+        Self { cmd }
+    }
+
+    /// Provide an input file.
+    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg(path.as_ref());
+        self
+    }
+}
+
 impl LlvmDis {
     /// Construct a new `llvm-dis` invocation. This assumes that `llvm-dis` is available
     /// at `$LLVM_BIN_DIR/llvm-dis`.
diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs
index 08ba1388dc1..60d3366ee98 100644
--- a/src/tools/run-make-support/src/external_deps/rustc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustc.rs
@@ -173,6 +173,12 @@ impl Rustc {
         self
     }
 
+    /// This flag enables LTO in the specified form.
+    pub fn lto(&mut self, option: &str) -> &mut Self {
+        self.cmd.arg(format!("-Clto={option}"));
+        self
+    }
+
     /// This flag defers LTO optimizations to the linker.
     pub fn linker_plugin_lto(&mut self, option: &str) -> &mut Self {
         self.cmd.arg(format!("-Clinker-plugin-lto={option}"));
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 29cd6c4ad15..b7d89b130c6 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -63,8 +63,9 @@ pub use crate::external_deps::clang::{Clang, clang};
 pub use crate::external_deps::htmldocck::htmldocck;
 pub use crate::external_deps::llvm::{
     self, LlvmAr, LlvmBcanalyzer, LlvmDis, LlvmDwarfdump, LlvmFilecheck, LlvmNm, LlvmObjcopy,
-    LlvmObjdump, LlvmProfdata, LlvmReadobj, llvm_ar, llvm_bcanalyzer, llvm_dis, llvm_dwarfdump,
-    llvm_filecheck, llvm_nm, llvm_objcopy, llvm_objdump, llvm_profdata, llvm_readobj,
+    LlvmObjdump, LlvmProfdata, LlvmReadobj, llvm_ar, llvm_as, llvm_bcanalyzer, llvm_dis,
+    llvm_dwarfdump, llvm_filecheck, llvm_nm, llvm_objcopy, llvm_objdump, llvm_profdata,
+    llvm_readobj,
 };
 pub use crate::external_deps::python::python_command;
 pub use crate::external_deps::rustc::{self, Rustc, bare_rustc, rustc, rustc_path};
diff --git a/tests/run-make/cross-lang-lto-clang/rmake.rs b/tests/run-make/cross-lang-lto-clang/rmake.rs
index 3fed6ea2066..f209318abbc 100644
--- a/tests/run-make/cross-lang-lto-clang/rmake.rs
+++ b/tests/run-make/cross-lang-lto-clang/rmake.rs
@@ -28,7 +28,17 @@ static C_NEVER_INLINED_PATTERN: &'static str = "bl.*<c_never_inlined>";
 static C_NEVER_INLINED_PATTERN: &'static str = "call.*c_never_inlined";
 
 fn main() {
+    test_lto(false);
+    test_lto(true);
+}
+
+fn test_lto(fat_lto: bool) {
+    let lto = if fat_lto { "fat" } else { "thin" };
+    let clang_lto = if fat_lto { "full" } else { "thin" };
+    println!("Running {lto} lto");
+
     rustc()
+        .lto(lto)
         .linker_plugin_lto("on")
         .output(static_lib_name("rustlib-xlto"))
         .opt_level("2")
@@ -36,30 +46,36 @@ fn main() {
         .input("rustlib.rs")
         .run();
     clang()
-        .lto("thin")
+        .lto(clang_lto)
         .use_ld("lld")
         .arg("-lrustlib-xlto")
         .out_exe("cmain")
         .input("cmain.c")
         .arg("-O3")
         .run();
+
+    let dump = llvm_objdump().disassemble().input("cmain").run();
     // Make sure we don't find a call instruction to the function we expect to
     // always be inlined.
-    llvm_objdump()
-        .disassemble()
-        .input("cmain")
-        .run()
-        .assert_stdout_not_contains_regex(RUST_ALWAYS_INLINED_PATTERN);
+    dump.assert_stdout_not_contains_regex(RUST_ALWAYS_INLINED_PATTERN);
     // As a sanity check, make sure we do find a call instruction to a
     // non-inlined function
-    llvm_objdump()
-        .disassemble()
-        .input("cmain")
-        .run()
-        .assert_stdout_contains_regex(RUST_NEVER_INLINED_PATTERN);
-    clang().input("clib.c").lto("thin").arg("-c").out_exe("clib.o").arg("-O2").run();
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    dump.assert_stdout_contains_regex(RUST_NEVER_INLINED_PATTERN);
+    #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
+    {
+        if fat_lto {
+            // fat lto inlines this anyway
+            dump.assert_stdout_not_contains_regex(RUST_NEVER_INLINED_PATTERN);
+        } else {
+            dump.assert_stdout_contains_regex(RUST_NEVER_INLINED_PATTERN);
+        }
+    }
+
+    clang().input("clib.c").lto(clang_lto).arg("-c").out_exe("clib.o").arg("-O2").run();
     llvm_ar().obj_to_ar().output_input(static_lib_name("xyz"), "clib.o").run();
     rustc()
+        .lto(lto)
         .linker_plugin_lto("on")
         .opt_level("2")
         .linker(&env_var("CLANG"))
@@ -67,14 +83,13 @@ fn main() {
         .input("main.rs")
         .output("rsmain")
         .run();
-    llvm_objdump()
-        .disassemble()
-        .input("rsmain")
-        .run()
-        .assert_stdout_not_contains_regex(C_ALWAYS_INLINED_PATTERN);
-    llvm_objdump()
-        .disassemble()
-        .input("rsmain")
-        .run()
-        .assert_stdout_contains_regex(C_NEVER_INLINED_PATTERN);
+
+    let dump = llvm_objdump().disassemble().input("rsmain").run();
+    dump.assert_stdout_not_contains_regex(C_ALWAYS_INLINED_PATTERN);
+    if fat_lto {
+        // fat lto inlines this anyway
+        dump.assert_stdout_not_contains_regex(C_NEVER_INLINED_PATTERN);
+    } else {
+        dump.assert_stdout_contains_regex(C_NEVER_INLINED_PATTERN);
+    }
 }
diff --git a/tests/run-make/fat-then-thin-lto/lib.rs b/tests/run-make/fat-then-thin-lto/lib.rs
new file mode 100644
index 00000000000..c675dcb6e8a
--- /dev/null
+++ b/tests/run-make/fat-then-thin-lto/lib.rs
@@ -0,0 +1,13 @@
+#![allow(internal_features)]
+#![feature(no_core, lang_items)]
+#![no_core]
+#![crate_type = "rlib"]
+
+#[lang = "pointee_sized"]
+trait PointeeSized {}
+#[lang = "meta_sized"]
+trait MetaSized: PointeeSized {}
+#[lang = "sized"]
+trait Sized: MetaSized {}
+
+pub fn foo() {}
diff --git a/tests/run-make/fat-then-thin-lto/main.rs b/tests/run-make/fat-then-thin-lto/main.rs
new file mode 100644
index 00000000000..a3f2e18158b
--- /dev/null
+++ b/tests/run-make/fat-then-thin-lto/main.rs
@@ -0,0 +1,11 @@
+#![allow(internal_features)]
+#![feature(no_core, lang_items)]
+#![no_core]
+#![crate_type = "cdylib"]
+
+extern crate lib;
+
+#[unsafe(no_mangle)]
+pub fn bar() {
+    lib::foo();
+}
diff --git a/tests/run-make/fat-then-thin-lto/rmake.rs b/tests/run-make/fat-then-thin-lto/rmake.rs
new file mode 100644
index 00000000000..ef4f26689d4
--- /dev/null
+++ b/tests/run-make/fat-then-thin-lto/rmake.rs
@@ -0,0 +1,25 @@
+// Compile a library with lto=fat, then compile a binary with lto=thin
+// and check that lto is applied with the library.
+// The goal is to mimic the standard library being build with lto=fat
+// and allowing users to build with lto=thin.
+
+//@ only-x86_64-unknown-linux-gnu
+
+use run_make_support::{dynamic_lib_name, llvm_objdump, rustc};
+
+fn main() {
+    rustc().input("lib.rs").opt_level("3").lto("fat").run();
+    rustc().input("main.rs").panic("abort").opt_level("3").lto("thin").run();
+
+    llvm_objdump()
+        .input(dynamic_lib_name("main"))
+        .arg("--disassemble-symbols=bar")
+        .run()
+        // The called function should be inlined.
+        // Check that we have a ret (to detect tail
+        // calls with a jmp) and no call.
+        .assert_stdout_contains("bar")
+        .assert_stdout_contains("ret")
+        .assert_stdout_not_contains("foo")
+        .assert_stdout_not_contains("call");
+}
diff --git a/tests/run-make/linker-plugin-lto-fat/ir.ll b/tests/run-make/linker-plugin-lto-fat/ir.ll
new file mode 100644
index 00000000000..fa3dbdd4e08
--- /dev/null
+++ b/tests/run-make/linker-plugin-lto-fat/ir.ll
@@ -0,0 +1,6 @@
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @ir_callee() {
+  ret void
+}
diff --git a/tests/run-make/linker-plugin-lto-fat/main.rs b/tests/run-make/linker-plugin-lto-fat/main.rs
new file mode 100644
index 00000000000..ad2a90bc801
--- /dev/null
+++ b/tests/run-make/linker-plugin-lto-fat/main.rs
@@ -0,0 +1,22 @@
+#![allow(internal_features)]
+#![feature(no_core, lang_items)]
+#![no_core]
+#![crate_type = "cdylib"]
+
+#[lang = "pointee_sized"]
+trait PointeeSized {}
+#[lang = "meta_sized"]
+trait MetaSized: PointeeSized {}
+#[lang = "sized"]
+trait Sized: MetaSized {}
+
+extern "C" {
+    fn ir_callee();
+}
+
+#[no_mangle]
+extern "C" fn rs_foo() {
+    unsafe {
+        ir_callee();
+    }
+}
diff --git a/tests/run-make/linker-plugin-lto-fat/rmake.rs b/tests/run-make/linker-plugin-lto-fat/rmake.rs
new file mode 100644
index 00000000000..ff5b647a594
--- /dev/null
+++ b/tests/run-make/linker-plugin-lto-fat/rmake.rs
@@ -0,0 +1,33 @@
+// Check that -C lto=fat with -C linker-plugin-lto actually works and can inline functions.
+// A library is created from LLVM IR, defining a single function. Then a dylib is compiled,
+// linking to the library and calling the function from the library.
+// The function from the library should end up inlined and disappear from the output.
+
+//@ only-x86_64-unknown-linux-gnu
+//@ needs-rust-lld
+
+use run_make_support::{dynamic_lib_name, llvm_as, llvm_objdump, rustc};
+
+fn main() {
+    llvm_as().input("ir.ll").run();
+    rustc()
+        .input("main.rs")
+        .opt_level("3")
+        .lto("fat")
+        .linker_plugin_lto("on")
+        .link_arg("ir.bc")
+        .arg("-Zunstable-options")
+        .arg("-Clinker-features=+lld")
+        .run();
+
+    llvm_objdump()
+        .input(dynamic_lib_name("main"))
+        .arg("--disassemble-symbols=rs_foo")
+        .run()
+        // The called function should be inlined.
+        // Check that we have a ret (to detect tail
+        // calls with a jmp) and no call.
+        .assert_stdout_contains("foo")
+        .assert_stdout_contains("ret")
+        .assert_stdout_not_contains("call");
+}