diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml
index 3dc8c84e0bfde..d356171938bdf 100644
--- a/library/core/Cargo.toml
+++ b/library/core/Cargo.toml
@@ -7,7 +7,7 @@ description = "The Rust Core Library"
 autotests = false
 autobenches = false
 # If you update this, be sure to update it in a bunch of other places too!
-# As of 2022, it was the ci/pgo.sh script and the core-no-fp-fmt-parse test.
+# As of 2023, it was the ci/pgo.sh script and the core-no-fp-fmt-parse/core-no-128-bit tests.
 edition = "2021"
 
 [lib]
diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs
index d8365ae9bf920..061f41d452687 100644
--- a/library/core/src/fmt/num.rs
+++ b/library/core/src/fmt/num.rs
@@ -176,9 +176,12 @@ integer! { i8, u8 }
 integer! { i16, u16 }
 integer! { i32, u32 }
 integer! { i64, u64 }
+#[cfg(not(no_128_bit))]
 integer! { i128, u128 }
+
 macro_rules! debug {
-    ($($T:ident)*) => {$(
+    ($($( #[$cfg:meta] )? $T:ident)*) => {$(
+        $( #[$cfg] )?
         #[stable(feature = "rust1", since = "1.0.0")]
         impl fmt::Debug for $T {
             #[inline]
@@ -195,8 +198,30 @@ macro_rules! debug {
     )*};
 }
 debug! {
-  i8 i16 i32 i64 i128 isize
-  u8 u16 u32 u64 u128 usize
+  i8 i16 i32 i64 #[cfg(not(no_128_bit))] i128 isize
+  u8 u16 u32 u64 #[cfg(not(no_128_bit))] u128 usize
+}
+
+macro_rules! fake_debug {
+    ($($( #[$cfg:meta] )? $T:ident)*) => {$(
+        $( #[$cfg] )?
+        #[stable(feature = "rust1", since = "1.0.0")]
+        impl fmt::Debug for $T {
+            #[inline]
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                // Debug formats are not stable so this is a legitimate implementation.
+                // It would be possible to hexdump the contents instead, or to
+                // fail or panic, but for an application which has specifically
+                // recompiled core to avoid i128, the former seems unnecessary
+                // and the latter needlessly harmful.
+                f.write_str(stringify!($T))
+            }
+        }
+    )*}
+}
+fake_debug! {
+    #[cfg(no_128_bit)] i128
+    #[cfg(no_128_bit)] u128
 }
 
 // 2 digit decimal look up table
@@ -476,9 +501,11 @@ mod imp {
     impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32);
     impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64);
 }
+#[cfg(not(no_128_bit))]
 impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128);
 
 /// Helper function for writing a u64 into `buf` going from last to first, with `curr`.
+#[cfg(not(no_128_bit))] // unused for `no_128_bit`
 fn parse_u64_into<const N: usize>(mut n: u64, buf: &mut [MaybeUninit<u8>; N], curr: &mut usize) {
     let buf_ptr = MaybeUninit::slice_as_mut_ptr(buf);
     let lut_ptr = DEC_DIGITS_LUT.as_ptr();
@@ -566,6 +593,7 @@ fn parse_u64_into<const N: usize>(mut n: u64, buf: &mut [MaybeUninit<u8>; N], cu
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg(not(no_128_bit))]
 impl fmt::Display for u128 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         fmt_u128(*self, true, f)
@@ -573,6 +601,7 @@ impl fmt::Display for u128 {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg(not(no_128_bit))]
 impl fmt::Display for i128 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let is_nonnegative = *self >= 0;
@@ -590,6 +619,7 @@ impl fmt::Display for i128 {
 /// into at most 2 u64s, and then chunks by 10e16, 10e8, 10e4, 10e2, and then 10e1.
 /// It also has to handle 1 last item, as 10^40 > 2^128 > 10^39, whereas
 /// 10^20 > 2^64 > 10^19.
+#[cfg(not(no_128_bit))]
 fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     // 2^128 is about 3*10^38, so 39 gives an extra byte of space
     let mut buf = [MaybeUninit::<u8>::uninit(); 39];
@@ -649,6 +679,7 @@ fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::R
 ///   in Proc. of the SIGPLAN94 Conference on Programming Language Design and
 ///   Implementation, 1994, pp. 61–72
 ///
+#[cfg(not(no_128_bit))]
 fn udiv_1e19(n: u128) -> (u128, u64) {
     const DIV: u64 = 1e19 as u64;
     const FACTOR: u128 = 156927543384667019095894735580191660403;
@@ -665,6 +696,7 @@ fn udiv_1e19(n: u128) -> (u128, u64) {
 
 /// Multiply unsigned 128 bit integers, return upper 128 bits of the result
 #[inline]
+#[cfg(not(no_128_bit))]
 fn u128_mulhi(x: u128, y: u128) -> u128 {
     let x_lo = x as u64;
     let x_hi = (x >> 64) as u64;
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 8790649abe6f1..f9c38bec30967 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -64,6 +64,7 @@
     not(test),
     any(not(feature = "miri-test-libstd"), test, doctest),
     no_fp_fmt_parse,
+    no_128_bit,
     target_pointer_width = "16",
     target_pointer_width = "32",
     target_pointer_width = "64",
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index 2cae98b8e4943..5258faa16a7ed 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -3,7 +3,7 @@ macro_rules! int_impl {
      $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr,
      $reversed:expr, $le_bytes:expr, $be_bytes:expr,
      $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr,
-     $bound_condition:expr) => {
+     $bound_condition:expr $(, #[$intrinsics_cfg:meta])?) => {
         /// The smallest value that can be represented by this integer type
         #[doc = concat!("(&minus;2<sup>", $BITS_MINUS_ONE, "</sup>", $bound_condition, ")")]
         ///
@@ -62,6 +62,7 @@ macro_rules! int_impl {
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")]
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
+        $( #[$intrinsics_cfg] )?
         pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
             from_str_radix(src, radix)
         }
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index ac7f579ebb5aa..c79ee0417aed7 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -268,7 +268,7 @@ impl i128 {
     "[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, \
       0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]",
     "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, \
-      0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12]", "", "", "" }
+      0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12]", "", "", "", #[cfg(not(no_128_bit))] }
 }
 
 #[cfg(target_pointer_width = "16")]
@@ -940,7 +940,7 @@ impl u128 {
       0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]",
     "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, \
       0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12]",
-     "", "", ""}
+     "", "", "", #[cfg(not(no_128_bit))]}
 }
 
 #[cfg(target_pointer_width = "16")]
@@ -1078,7 +1078,9 @@ macro_rules! from_str_radix_int_impl {
         }
     )*}
 }
-from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }
+from_str_radix_int_impl! { isize i8 i16 i32 i64 usize u8 u16 u32 u64 }
+#[cfg(not(no_128_bit))]
+from_str_radix_int_impl! { i128 u128 }
 
 macro_rules! impl_helper_for {
     ($($t:ty)*) => ($(impl FromStrRadixHelper for $t {
@@ -1099,7 +1101,9 @@ macro_rules! impl_helper_for {
         }
     })*)
 }
-impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
+impl_helper_for! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize }
+#[cfg(not(no_128_bit))]
+impl_helper_for! { i128 u128 }
 
 /// Determines if a string of text of that length of that radix could be guaranteed to be
 /// stored in the given type T.
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index fbda8f82b1bd9..f2179858d3b2a 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -23,7 +23,7 @@ macro_rules! impl_nonzero_fmt {
 }
 
 macro_rules! nonzero_integers {
-    ( $( #[$stability: meta] #[$const_new_unchecked_stability: meta] $Ty: ident($Int: ty); )+ ) => {
+    ( $( #[$stability: meta] #[$const_new_unchecked_stability: meta] $(#[$cfg: meta])? $Ty: ident($Int: ty); )+ ) => {
         $(
             /// An integer that is known not to equal zero.
             ///
@@ -154,9 +154,12 @@ macro_rules! nonzero_integers {
                 }
             }
 
+            $(#[$cfg])?
             impl_nonzero_fmt! {
-                #[$stability] (Debug, Display, Binary, Octal, LowerHex, UpperHex) for $Ty
+                #[$stability] (Display, Binary, Octal, LowerHex, UpperHex) for $Ty
             }
+
+            impl_nonzero_fmt!(#[$stability] (Debug) for $Ty);
         )+
     }
 }
@@ -166,13 +169,15 @@ nonzero_integers! {
     #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16);
     #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32);
     #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU64(u64);
-    #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU128(u128);
+    #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")]
+        #[cfg(not(no_128_bit))] NonZeroU128(u128);
     #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroUsize(usize);
     #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI8(i8);
     #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI16(i16);
     #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI32(i32);
     #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI64(i64);
-    #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI128(i128);
+    #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")]
+        #[cfg(not(no_128_bit))] NonZeroI128(i128);
     #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIsize(isize);
 }
 
@@ -191,8 +196,11 @@ macro_rules! from_str_radix_nzint_impl {
     )*}
 }
 
-from_str_radix_nzint_impl! { NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize
-NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize }
+from_str_radix_nzint_impl! { NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroUsize
+NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroIsize }
+
+#[cfg(not(no_128_bit))]
+from_str_radix_nzint_impl! { NonZeroU128 NonZeroI128 }
 
 macro_rules! nonzero_leading_trailing_zeros {
     ( $( $Ty: ident($Uint: ty) , $LeadingTestExpr:expr ;)+ ) => {
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index 1c97c46862833..9ea8ca0638a79 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -4,7 +4,7 @@ macro_rules! uint_impl {
         $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr,
         $reversed:expr, $le_bytes:expr, $be_bytes:expr,
         $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr,
-        $bound_condition:expr) => {
+        $bound_condition:expr $(, #[$intrinsics_cfg:meta])?) => {
         /// The smallest value that can be represented by this integer type.
         ///
         /// # Examples
@@ -63,6 +63,7 @@ macro_rules! uint_impl {
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")]
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
+        $( #[$intrinsics_cfg] )?
         pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
             from_str_radix(src, radix)
         }
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index d44b96cfb991e..ac38b54192be0 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -207,6 +207,7 @@ const EXTRA_CHECK_CFGS: &[(Option<Mode>, &'static str, Option<&[&'static str]>)]
     (Some(Mode::Codegen), "parallel_compiler", None),
     (Some(Mode::Std), "stdarch_intel_sde", None),
     (Some(Mode::Std), "no_fp_fmt_parse", None),
+    (Some(Mode::Std), "no_128_bit", None),
     (Some(Mode::Std), "no_global_oom_handling", None),
     (Some(Mode::Std), "no_rc", None),
     (Some(Mode::Std), "no_sync", None),
diff --git a/tests/run-make-fulldeps/core-no-128-bit/Makefile b/tests/run-make-fulldeps/core-no-128-bit/Makefile
new file mode 100644
index 0000000000000..4d9784e65abbf
--- /dev/null
+++ b/tests/run-make-fulldeps/core-no-128-bit/Makefile
@@ -0,0 +1,8 @@
+include ../tools.mk
+
+all:
+	$(RUSTC) --edition=2021 --crate-type=rlib --crate-name=core ../../../library/core/src/lib.rs --cfg no_128_bit
+	$(RUSTC) --edition=2021 --crate-type=staticlib --crate-name=demo --sysroot=. -C panic=abort lib.rs
+	# Expect that objdump succeeds and grep fails. The grep pattern is for names like __multi3.
+	# There is no pipefail on dash so echo a string that will fail the test if objump fails.
+	(objdump -t $(TMPDIR)/libdemo.a || echo __objdumpfailedti) | (! grep -w '__[a-z]\+ti[0-9]\?')
diff --git a/tests/run-make-fulldeps/core-no-128-bit/lib.rs b/tests/run-make-fulldeps/core-no-128-bit/lib.rs
new file mode 100644
index 0000000000000..f52dc453bf97c
--- /dev/null
+++ b/tests/run-make-fulldeps/core-no-128-bit/lib.rs
@@ -0,0 +1,31 @@
+#![feature(no_core)]
+
+#![no_std]
+#![no_core] // supress compiler-builtins
+extern crate core;
+use core::prelude::rust_2021::*;
+use core::fmt::Write;
+
+// An empty file might be sufficient here, but since formatting is one of the
+// features affected by no_128_bit it seems worth including some.
+
+struct X(pub usize);
+impl core::fmt::Write for X {
+    fn write_str(&mut self, s: &str) -> core::fmt::Result {
+        self.0 += s.len();
+        Ok(())
+    }
+}
+
+#[no_mangle]
+extern "C" fn demo() -> usize {
+    let mut x = X(0);
+    // Writes "i128 u128 foo" due to the removal of u/i128 formatting.
+    core::write!(x, "{:?} {:?} {}", i128::MAX, u128::MIN, "foo").unwrap();
+    x.0
+}
+
+#[panic_handler]
+fn panic(_: &core::panic::PanicInfo) -> ! {
+    loop {}
+}