diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index f0b94047ed9..77dd4ccd64e 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -113,6 +113,14 @@ impl<'a> PostExpansionVisitor<'a> {
                     "rust-call ABI is subject to change"
                 );
             }
+            "rust-cold" => {
+                gate_feature_post!(
+                    &self,
+                    rust_cold_cc,
+                    span,
+                    "rust-cold is experimental and subject to change"
+                );
+            }
             "ptx-kernel" => {
                 gate_feature_post!(
                     &self,
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index decb7841990..ffa5d747b11 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -23,6 +23,7 @@ fn clif_sig_from_fn_abi<'tcx>(
 ) -> Signature {
     let call_conv = match fn_abi.conv {
         Conv::Rust | Conv::C => default_call_conv,
+        Conv::RustCold => CallConv::Cold,
         Conv::X86_64SysV => CallConv::SystemV,
         Conv::X86_64Win64 => CallConv::WindowsFastcall,
         Conv::ArmAapcs
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index b9baa87bac7..cc8b3a1a4e4 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -393,6 +393,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
     fn llvm_cconv(&self) -> llvm::CallConv {
         match self.conv {
             Conv::C | Conv::Rust | Conv::CCmseNonSecureCall => llvm::CCallConv,
+            Conv::RustCold => llvm::ColdCallConv,
             Conv::AmdGpuKernel => llvm::AmdGpuKernel,
             Conv::AvrInterrupt => llvm::AvrInterrupt,
             Conv::AvrNonBlockingInterrupt => llvm::AvrNonBlockingInterrupt,
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index b6ab60f9f03..1803f665013 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -496,6 +496,8 @@ declare_features! (
     (incomplete, repr128, "1.16.0", Some(56071), None),
     /// Allows `repr(simd)` and importing the various simd intrinsics.
     (active, repr_simd, "1.4.0", Some(27731), None),
+    /// Allows `extern "rust-cold"`.
+    (active, rust_cold_cc, "1.63.0", Some(97544), None),
     /// Allows the use of SIMD types in functions declared in `extern` blocks.
     (active, simd_ffi, "1.0.0", Some(27731), None),
     /// Allows specialization of implementations (RFC 1210).
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 30552c685a3..3b05e42a53e 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -2971,7 +2971,7 @@ pub fn fn_can_unwind<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: Option<DefId>, abi: Spe
         | RustIntrinsic
         | PlatformIntrinsic
         | Unadjusted => false,
-        Rust | RustCall => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
+        Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
     }
 }
 
@@ -2980,6 +2980,7 @@ pub fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi) -> Conv {
     use rustc_target::spec::abi::Abi::*;
     match tcx.sess.target.adjust_abi(abi) {
         RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust,
+        RustCold => Conv::RustCold,
 
         // It's the ABI's job to select this, not ours.
         System { .. } => bug!("system abi should be selected elsewhere"),
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 5f301962061..bcaf53639cc 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1163,6 +1163,7 @@ symbols! {
         rust_2024,
         rust_2024_preview,
         rust_begin_unwind,
+        rust_cold_cc,
         rust_eh_catch_typeinfo,
         rust_eh_personality,
         rust_eh_register_frames,
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index afce10ff1cb..ca1d1302ec6 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -580,6 +580,11 @@ pub enum Conv {
     C,
     Rust,
 
+    /// For things unlikely to be called, where smaller caller codegen is
+    /// preferred over raw speed.
+    /// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
+    RustCold,
+
     // Target-specific calling conventions.
     ArmAapcs,
     CCmseNonSecureCall,
diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs
index d9e571c72e5..337554dc96e 100644
--- a/compiler/rustc_target/src/spec/abi.rs
+++ b/compiler/rustc_target/src/spec/abi.rs
@@ -35,6 +35,7 @@ pub enum Abi {
     RustCall,
     PlatformIntrinsic,
     Unadjusted,
+    RustCold,
 }
 
 #[derive(Copy, Clone)]
@@ -81,6 +82,7 @@ const AbiDatas: &[AbiData] = &[
     AbiData { abi: Abi::RustCall, name: "rust-call" },
     AbiData { abi: Abi::PlatformIntrinsic, name: "platform-intrinsic" },
     AbiData { abi: Abi::Unadjusted, name: "unadjusted" },
+    AbiData { abi: Abi::RustCold, name: "rust-cold" },
 ];
 
 /// Returns the ABI with the given name (if any).
@@ -139,6 +141,7 @@ impl Abi {
             RustCall => 31,
             PlatformIntrinsic => 32,
             Unadjusted => 33,
+            RustCold => 34,
         };
         debug_assert!(
             AbiDatas
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 0f5db8982e8..4ede0677ab3 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1620,7 +1620,8 @@ impl Target {
             | PlatformIntrinsic
             | Unadjusted
             | Cdecl { .. }
-            | EfiApi => true,
+            | EfiApi
+            | RustCold => true,
             X86Interrupt => ["x86", "x86_64"].contains(&&self.arch[..]),
             Aapcs { .. } => "arm" == self.arch,
             CCmseNonSecureCall => ["arm", "aarch64"].contains(&&self.arch[..]),
diff --git a/src/test/codegen/cold-call-declare-and-call.rs b/src/test/codegen/cold-call-declare-and-call.rs
new file mode 100644
index 00000000000..71d49478bfc
--- /dev/null
+++ b/src/test/codegen/cold-call-declare-and-call.rs
@@ -0,0 +1,18 @@
+// compile-flags: -C no-prepopulate-passes
+
+#![crate_type = "lib"]
+#![feature(rust_cold_cc)]
+
+// wasm marks the definition as `dso_local`, so allow that as optional.
+
+// CHECK: define{{( dso_local)?}} coldcc void @this_should_never_happen(i16
+// CHECK: call coldcc void @this_should_never_happen(i16
+
+#[no_mangle]
+pub extern "rust-cold" fn this_should_never_happen(x: u16) {}
+
+pub fn do_things(x: u16) {
+    if x == 12345 {
+        this_should_never_happen(54321);
+    }
+}
diff --git a/src/test/ui/codemap_tests/unicode.stderr b/src/test/ui/codemap_tests/unicode.stderr
index e5aef04b689..bf7aaa5f0eb 100644
--- a/src/test/ui/codemap_tests/unicode.stderr
+++ b/src/test/ui/codemap_tests/unicode.stderr
@@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `路濫狼á́́`
 LL | extern "路濫狼á́́" fn foo() {}
    |        ^^^^^^^^^ invalid ABI
    |
-   = help: valid ABIs: Rust, C, C-unwind, cdecl, cdecl-unwind, stdcall, stdcall-unwind, fastcall, fastcall-unwind, vectorcall, vectorcall-unwind, thiscall, thiscall-unwind, aapcs, aapcs-unwind, win64, win64-unwind, sysv64, sysv64-unwind, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
+   = help: valid ABIs: Rust, C, C-unwind, cdecl, cdecl-unwind, stdcall, stdcall-unwind, fastcall, fastcall-unwind, vectorcall, vectorcall-unwind, thiscall, thiscall-unwind, aapcs, aapcs-unwind, win64, win64-unwind, sysv64, sysv64-unwind, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted, rust-cold
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/feature-gates/feature-gate-rust_cold_cc.rs b/src/test/ui/feature-gates/feature-gate-rust_cold_cc.rs
new file mode 100644
index 00000000000..9ba8e32ac07
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-rust_cold_cc.rs
@@ -0,0 +1,21 @@
+#![crate_type = "lib"]
+
+extern "rust-cold" fn fu() {} //~ ERROR rust-cold is experimental
+
+trait T {
+    extern "rust-cold" fn mu(); //~ ERROR rust-cold is experimental
+    extern "rust-cold" fn dmu() {} //~ ERROR rust-cold is experimental
+}
+
+struct S;
+impl T for S {
+    extern "rust-cold" fn mu() {} //~ ERROR rust-cold is experimental
+}
+
+impl S {
+    extern "rust-cold" fn imu() {} //~ ERROR rust-cold is experimental
+}
+
+type TAU = extern "rust-cold" fn(); //~ ERROR rust-cold is experimental
+
+extern "rust-cold" {} //~ ERROR rust-cold is experimental
diff --git a/src/test/ui/feature-gates/feature-gate-rust_cold_cc.stderr b/src/test/ui/feature-gates/feature-gate-rust_cold_cc.stderr
new file mode 100644
index 00000000000..ab7e5f0366d
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-rust_cold_cc.stderr
@@ -0,0 +1,66 @@
+error[E0658]: rust-cold is experimental and subject to change
+  --> $DIR/feature-gate-rust_cold_cc.rs:3:8
+   |
+LL | extern "rust-cold" fn fu() {}
+   |        ^^^^^^^^^^^
+   |
+   = note: see issue #97544 <https://github.com/rust-lang/rust/issues/97544> for more information
+   = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable
+
+error[E0658]: rust-cold is experimental and subject to change
+  --> $DIR/feature-gate-rust_cold_cc.rs:6:12
+   |
+LL |     extern "rust-cold" fn mu();
+   |            ^^^^^^^^^^^
+   |
+   = note: see issue #97544 <https://github.com/rust-lang/rust/issues/97544> for more information
+   = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable
+
+error[E0658]: rust-cold is experimental and subject to change
+  --> $DIR/feature-gate-rust_cold_cc.rs:7:12
+   |
+LL |     extern "rust-cold" fn dmu() {}
+   |            ^^^^^^^^^^^
+   |
+   = note: see issue #97544 <https://github.com/rust-lang/rust/issues/97544> for more information
+   = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable
+
+error[E0658]: rust-cold is experimental and subject to change
+  --> $DIR/feature-gate-rust_cold_cc.rs:12:12
+   |
+LL |     extern "rust-cold" fn mu() {}
+   |            ^^^^^^^^^^^
+   |
+   = note: see issue #97544 <https://github.com/rust-lang/rust/issues/97544> for more information
+   = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable
+
+error[E0658]: rust-cold is experimental and subject to change
+  --> $DIR/feature-gate-rust_cold_cc.rs:16:12
+   |
+LL |     extern "rust-cold" fn imu() {}
+   |            ^^^^^^^^^^^
+   |
+   = note: see issue #97544 <https://github.com/rust-lang/rust/issues/97544> for more information
+   = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable
+
+error[E0658]: rust-cold is experimental and subject to change
+  --> $DIR/feature-gate-rust_cold_cc.rs:19:19
+   |
+LL | type TAU = extern "rust-cold" fn();
+   |                   ^^^^^^^^^^^
+   |
+   = note: see issue #97544 <https://github.com/rust-lang/rust/issues/97544> for more information
+   = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable
+
+error[E0658]: rust-cold is experimental and subject to change
+  --> $DIR/feature-gate-rust_cold_cc.rs:21:8
+   |
+LL | extern "rust-cold" {}
+   |        ^^^^^^^^^^^
+   |
+   = note: see issue #97544 <https://github.com/rust-lang/rust/issues/97544> for more information
+   = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/parser/issues/issue-8537.stderr b/src/test/ui/parser/issues/issue-8537.stderr
index 5f8d4315de8..505d830ef3e 100644
--- a/src/test/ui/parser/issues/issue-8537.stderr
+++ b/src/test/ui/parser/issues/issue-8537.stderr
@@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `invalid-ab_isize`
 LL |   "invalid-ab_isize"
    |   ^^^^^^^^^^^^^^^^^^ invalid ABI
    |
-   = help: valid ABIs: Rust, C, C-unwind, cdecl, cdecl-unwind, stdcall, stdcall-unwind, fastcall, fastcall-unwind, vectorcall, vectorcall-unwind, thiscall, thiscall-unwind, aapcs, aapcs-unwind, win64, win64-unwind, sysv64, sysv64-unwind, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
+   = help: valid ABIs: Rust, C, C-unwind, cdecl, cdecl-unwind, stdcall, stdcall-unwind, fastcall, fastcall-unwind, vectorcall, vectorcall-unwind, thiscall, thiscall-unwind, aapcs, aapcs-unwind, win64, win64-unwind, sysv64, sysv64-unwind, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted, rust-cold
 
 error: aborting due to previous error