diff --git a/Cargo.lock b/Cargo.lock
index f0c6e371c38be..716f4d7501467 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -221,7 +221,6 @@ dependencies = [
  "getopts",
  "ignore",
  "libc",
- "num_cpus",
  "once_cell",
  "opener",
  "pretty_assertions",
@@ -249,7 +248,6 @@ dependencies = [
  "anyhow",
  "flate2",
  "hex 0.4.2",
- "num_cpus",
  "rayon",
  "serde",
  "serde_json",
@@ -4241,7 +4239,6 @@ name = "rustc_session"
 version = "0.0.0"
 dependencies = [
  "getopts",
- "num_cpus",
  "rustc_ast",
  "rustc_data_structures",
  "rustc_errors",
diff --git a/RELEASES.md b/RELEASES.md
index 971a63b240f3e..08040f4815836 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1060,7 +1060,7 @@ Version 1.52.1 (2021-05-10)
 This release disables incremental compilation, unless the user has explicitly
 opted in via the newly added RUSTC_FORCE_INCREMENTAL=1 environment variable.
 
-This is due to the widespread, and frequently occuring, breakage encountered by
+This is due to the widespread, and frequently occurring, breakage encountered by
 Rust users due to newly enabled incremental verification in 1.52.0. Notably,
 Rust users **should** upgrade to 1.52.0 or 1.52.1: the bugs that are detected by
 newly added incremental verification are still present in past stable versions,
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index 974e59b65ec91..be3f6a12706f8 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -1064,7 +1064,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
         let val_type = value.get_type();
         match (type_is_pointer(val_type), type_is_pointer(dest_ty)) {
             (false, true) => {
-                // NOTE: Projecting a field of a pointer type will attemp a cast from a signed char to
+                // NOTE: Projecting a field of a pointer type will attempt a cast from a signed char to
                 // a pointer, which is not supported by gccjit.
                 return self.cx.context.new_cast(None, self.inttoptr(value, val_type.make_pointer()), dest_ty);
             },
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index f8f6956c47e4d..b14a4f28c756c 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -561,8 +561,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
         if self.conv == Conv::CCmseNonSecureCall {
             // This will probably get ignored on all targets but those supporting the TrustZone-M
             // extension (thumbv8m targets).
-            let cmse_nonsecure_call =
-                llvm::CreateAttrString(bx.cx.llcx, cstr::cstr!("cmse_nonsecure_call"));
+            let cmse_nonsecure_call = llvm::CreateAttrString(bx.cx.llcx, "cmse_nonsecure_call");
             attributes::apply_to_callsite(
                 callsite,
                 llvm::AttributePlace::Function,
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index e382af8880b8d..6fd836946ffb4 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -1,10 +1,6 @@
 //! Set and unset common attributes on LLVM values.
 
-use std::ffi::CString;
-
-use cstr::cstr;
 use rustc_codegen_ssa::traits::*;
-use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_hir::def_id::DefId;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::ty::{self, TyCtxt};
@@ -103,11 +99,11 @@ pub fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attr
         fp = FramePointer::Always;
     }
     let attr_value = match fp {
-        FramePointer::Always => cstr!("all"),
-        FramePointer::NonLeaf => cstr!("non-leaf"),
+        FramePointer::Always => "all",
+        FramePointer::NonLeaf => "non-leaf",
         FramePointer::MayOmit => return None,
     };
-    Some(llvm::CreateAttrStringValue(cx.llcx, cstr!("frame-pointer"), attr_value))
+    Some(llvm::CreateAttrStringValue(cx.llcx, "frame-pointer", attr_value))
 }
 
 /// Tell LLVM what instrument function to insert.
@@ -119,11 +115,11 @@ fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribu
 
         // The function name varies on platforms.
         // See test/CodeGen/mcount.c in clang.
-        let mcount_name = CString::new(cx.sess().target.mcount.as_str().as_bytes()).unwrap();
+        let mcount_name = cx.sess().target.mcount.as_str();
 
         Some(llvm::CreateAttrStringValue(
             cx.llcx,
-            cstr!("instrument-function-entry-inlined"),
+            "instrument-function-entry-inlined",
             &mcount_name,
         ))
     } else {
@@ -159,20 +155,20 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
         StackProbeType::None => return None,
         // Request LLVM to generate the probes inline. If the given LLVM version does not support
         // this, no probe is generated at all (even if the attribute is specified).
-        StackProbeType::Inline => cstr!("inline-asm"),
+        StackProbeType::Inline => "inline-asm",
         // Flag our internal `__rust_probestack` function as the stack probe symbol.
         // This is defined in the `compiler-builtins` crate for each architecture.
-        StackProbeType::Call => cstr!("__rust_probestack"),
+        StackProbeType::Call => "__rust_probestack",
         // Pick from the two above based on the LLVM version.
         StackProbeType::InlineOrCall { min_llvm_version_for_inline } => {
             if llvm_util::get_version() < min_llvm_version_for_inline {
-                cstr!("__rust_probestack")
+                "__rust_probestack"
             } else {
-                cstr!("inline-asm")
+                "inline-asm"
             }
         }
     };
-    Some(llvm::CreateAttrStringValue(cx.llcx, cstr!("probe-stack"), attr_value))
+    Some(llvm::CreateAttrStringValue(cx.llcx, "probe-stack", attr_value))
 }
 
 fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
@@ -187,15 +183,13 @@ fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
 }
 
 pub fn target_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll Attribute {
-    let target_cpu = SmallCStr::new(llvm_util::target_cpu(cx.tcx.sess));
-    llvm::CreateAttrStringValue(cx.llcx, cstr!("target-cpu"), target_cpu.as_c_str())
+    let target_cpu = llvm_util::target_cpu(cx.tcx.sess);
+    llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu)
 }
 
 pub fn tune_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
-    llvm_util::tune_cpu(cx.tcx.sess).map(|tune| {
-        let tune_cpu = SmallCStr::new(tune);
-        llvm::CreateAttrStringValue(cx.llcx, cstr!("tune-cpu"), tune_cpu.as_c_str())
-    })
+    llvm_util::tune_cpu(cx.tcx.sess)
+        .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu))
 }
 
 /// Get the `NonLazyBind` LLVM attribute,
@@ -280,7 +274,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
     }
 
     if cx.sess().opts.debugging_opts.profile_sample_use.is_some() {
-        to_add.push(llvm::CreateAttrString(cx.llcx, cstr!("use-sample-profile")));
+        to_add.push(llvm::CreateAttrString(cx.llcx, "use-sample-profile"));
     }
 
     // FIXME: none of these three functions interact with source level attributes.
@@ -310,7 +304,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
         attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
     }
     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) {
-        to_add.push(llvm::CreateAttrString(cx.llcx, cstr!("cmse_nonsecure_entry")));
+        to_add.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry"));
     }
     if let Some(align) = codegen_fn_attrs.alignment {
         llvm::set_alignment(llfn, align as usize);
@@ -363,12 +357,12 @@ pub fn from_fn_attrs<'ll, 'tcx>(
         // If this function is an import from the environment but the wasm
         // import has a specific module/name, apply them here.
         if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) {
-            to_add.push(llvm::CreateAttrStringValue(cx.llcx, cstr!("wasm-import-module"), &module));
+            to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", &module));
 
             let name =
                 codegen_fn_attrs.link_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id()));
-            let name = CString::new(name.as_str()).unwrap();
-            to_add.push(llvm::CreateAttrStringValue(cx.llcx, cstr!("wasm-import-name"), &name));
+            let name = name.as_str();
+            to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name));
         }
 
         // The `"wasm"` abi on wasm targets automatically enables the
@@ -388,13 +382,13 @@ pub fn from_fn_attrs<'ll, 'tcx>(
         let val = global_features
             .chain(function_features.iter().map(|s| &s[..]))
             .intersperse(",")
-            .collect::<SmallCStr>();
-        to_add.push(llvm::CreateAttrStringValue(cx.llcx, cstr!("target-features"), &val));
+            .collect::<String>();
+        to_add.push(llvm::CreateAttrStringValue(cx.llcx, "target-features", &val));
     }
 
     attributes::apply_to_llfn(llfn, Function, &to_add);
 }
 
-fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<CString> {
-    tcx.wasm_import_module_map(id.krate).get(&id).map(|s| CString::new(&s[..]).unwrap())
+fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<&String> {
+    tcx.wasm_import_module_map(id.krate).get(&id)
 }
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 6e9e0332faf60..a7dd4cea04e1b 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -1175,11 +1175,12 @@ extern "C" {
 
     // Operations on attributes
     pub fn LLVMRustCreateAttrNoValue(C: &Context, attr: AttributeKind) -> &Attribute;
-    pub fn LLVMRustCreateAttrString(C: &Context, Name: *const c_char) -> &Attribute;
-    pub fn LLVMRustCreateAttrStringValue(
+    pub fn LLVMCreateStringAttribute(
         C: &Context,
         Name: *const c_char,
+        NameLen: c_uint,
         Value: *const c_char,
+        ValueLen: c_uint,
     ) -> &Attribute;
     pub fn LLVMRustCreateAlignmentAttr(C: &Context, bytes: u64) -> &Attribute;
     pub fn LLVMRustCreateDereferenceableAttr(C: &Context, bytes: u64) -> &Attribute;
diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs
index 4892b8d4a84c6..48fbc1de8ee44 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs
@@ -47,12 +47,28 @@ pub fn AddCallSiteAttributes<'ll>(
     }
 }
 
-pub fn CreateAttrStringValue<'ll>(llcx: &'ll Context, attr: &CStr, value: &CStr) -> &'ll Attribute {
-    unsafe { LLVMRustCreateAttrStringValue(llcx, attr.as_ptr(), value.as_ptr()) }
+pub fn CreateAttrStringValue<'ll>(llcx: &'ll Context, attr: &str, value: &str) -> &'ll Attribute {
+    unsafe {
+        LLVMCreateStringAttribute(
+            llcx,
+            attr.as_ptr().cast(),
+            attr.len().try_into().unwrap(),
+            value.as_ptr().cast(),
+            value.len().try_into().unwrap(),
+        )
+    }
 }
 
-pub fn CreateAttrString<'ll>(llcx: &'ll Context, attr: &CStr) -> &'ll Attribute {
-    unsafe { LLVMRustCreateAttrStringValue(llcx, attr.as_ptr(), std::ptr::null()) }
+pub fn CreateAttrString<'ll>(llcx: &'ll Context, attr: &str) -> &'ll Attribute {
+    unsafe {
+        LLVMCreateStringAttribute(
+            llcx,
+            attr.as_ptr().cast(),
+            attr.len().try_into().unwrap(),
+            std::ptr::null(),
+            0,
+        )
+    }
 }
 
 pub fn CreateAlignmentAttr(llcx: &Context, bytes: u64) -> &Attribute {
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 3b06587061d9d..e9d13a4ebaf8c 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -369,12 +369,12 @@ pub fn target_cpu(sess: &Session) -> &str {
 /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
 /// `--target` and similar).
 pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<String> {
-    // Features that come earlier are overriden by conflicting features later in the string.
+    // Features that come earlier are overridden by conflicting features later in the string.
     // Typically we'll want more explicit settings to override the implicit ones, so:
     //
-    // * Features from -Ctarget-cpu=*; are overriden by [^1]
-    // * Features implied by --target; are overriden by
-    // * Features from -Ctarget-feature; are overriden by
+    // * Features from -Ctarget-cpu=*; are overridden by [^1]
+    // * Features implied by --target; are overridden by
+    // * Features from -Ctarget-feature; are overridden by
     // * function specific features.
     //
     // [^1]: target-cpu=native is handled here, other target-cpu values are handled implicitly
@@ -383,7 +383,7 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
     // FIXME(nagisa): it isn't clear what's the best interaction between features implied by
     // `-Ctarget-cpu` and `--target` are. On one hand, you'd expect CLI arguments to always
     // override anything that's implicit, so e.g. when there's no `--target` flag, features implied
-    // the host target are overriden by `-Ctarget-cpu=*`. On the other hand, what about when both
+    // the host target are overridden by `-Ctarget-cpu=*`. On the other hand, what about when both
     // `--target` and `-Ctarget-cpu=*` are specified? Both then imply some target features and both
     // flags are specified by the user on the CLI. It isn't as clear-cut which order of precedence
     // should be taken in cases like these.
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 1561277b7049f..37e84046901f6 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -1,6 +1,6 @@
 // Type Names for Debug Info.
 
-// Notes on targetting MSVC:
+// Notes on targeting MSVC:
 // In general, MSVC's debugger attempts to parse all arguments as C++ expressions,
 // even if the argument is explicitly a symbol name.
 // As such, there are many things that cause parsing issues:
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index fd4adfea8082c..df011d6ca5fab 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -39,6 +39,7 @@ const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
     // #[target_feature].
     ("thumb-mode", Some(sym::arm_target_feature)),
     ("thumb2", Some(sym::arm_target_feature)),
+    ("d32", Some(sym::arm_target_feature)),
 ];
 
 const AARCH64_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 716c01ba78ceb..16870489176d7 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -535,7 +535,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let val =
                     self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal)?;
                 // This can still fail:
-                // * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all
+                // * During ConstProp, with `TooGeneric` or since the `required_consts` were not all
                 //   checked yet.
                 // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
 
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index b36ce63dda756..43c9e9296b90a 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -1228,7 +1228,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
 ///
 /// A custom rustc driver can skip calling this to set up a custom ICE hook.
 pub fn install_ice_hook() {
-    // If the user has not explicitly overriden "RUST_BACKTRACE", then produce
+    // If the user has not explicitly overridden "RUST_BACKTRACE", then produce
     // full backtraces. When a compiler ICE happens, we want to gather
     // as much information as possible to present in the issue opened
     // by the user. Compiler developers and other rustc users can
diff --git a/compiler/rustc_error_codes/src/error_codes/E0772.md b/compiler/rustc_error_codes/src/error_codes/E0772.md
index 262e52351ef01..3b73abaf776c2 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0772.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0772.md
@@ -45,7 +45,7 @@ trait object's internal data to be accessed safely from any trait methods. This
 rule also goes for any lifetime any struct made into a trait object may have.
 
 In the implementation for `dyn Person`, the `'2` lifetime representing the
-internal data was ommitted, meaning that the compiler inferred the lifetime
+internal data was omitted, meaning that the compiler inferred the lifetime
 `'static`. As a result, the implementation's `is_cool` is inferred by the
 compiler to look like this:
 
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 1b97618050939..4af376b983c94 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -1841,7 +1841,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
         self.flat_map_node(node)
     }
 
-    fn flat_map_stmt(&mut self, mut node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
+    fn flat_map_stmt(&mut self, node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
         // FIXME: invocations in semicolon-less expressions positions are expanded as expressions,
         // changing that requires some compatibility measures.
         if node.is_expr() {
@@ -1863,7 +1863,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                     self.cx.current_expansion.is_trailing_mac = false;
                     res
                 }
-                _ => assign_id!(self, &mut node.id, || noop_flat_map_stmt(node, self)),
+                _ => noop_flat_map_stmt(node, self),
             };
         }
 
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index a419612e315df..bb36dfd793d4a 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -154,7 +154,7 @@ type NamedMatchVec = SmallVec<[NamedMatch; 4]>;
 /// lifetime. By separating `'tt` from `'root`, we can show that.
 #[derive(Clone)]
 struct MatcherPos<'root, 'tt> {
-    /// The token or sequence of tokens that make up the matcher
+    /// The token or sequence of tokens that make up the matcher. `elts` is short for "elements".
     top_elts: TokenTreeOrTokenTreeSlice<'tt>,
 
     /// The position of the "dot" in this matcher
@@ -184,17 +184,8 @@ struct MatcherPos<'root, 'tt> {
     /// in this matcher.
     match_hi: usize,
 
-    // The following fields are used if we are matching a repetition. If we aren't, they should be
-    // `None`.
-    /// The KleeneOp of this sequence if we are in a repetition.
-    seq_op: Option<mbe::KleeneOp>,
-
-    /// The separator if we are in a repetition.
-    sep: Option<Token>,
-
-    /// The "parent" matcher position if we are in a repetition. That is, the matcher position just
-    /// before we enter the sequence.
-    up: Option<MatcherPosHandle<'root, 'tt>>,
+    /// This field is only used if we are matching a repetition.
+    repetition: Option<MatcherPosRepetition<'root, 'tt>>,
 
     /// Specifically used to "unzip" token trees. By "unzip", we mean to unwrap the delimiters from
     /// a delimited token tree (e.g., something wrapped in `(` `)`) or to get the contents of a doc
@@ -207,7 +198,38 @@ struct MatcherPos<'root, 'tt> {
     stack: SmallVec<[MatcherTtFrame<'tt>; 1]>,
 }
 
+// This type is used a lot. Make sure it doesn't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(MatcherPos<'_, '_>, 192);
+
 impl<'root, 'tt> MatcherPos<'root, 'tt> {
+    /// Generates the top-level matcher position in which the "dot" is before the first token of
+    /// the matcher `ms`.
+    fn new(ms: &'tt [TokenTree]) -> Self {
+        let match_idx_hi = count_names(ms);
+        MatcherPos {
+            // Start with the top level matcher given to us.
+            top_elts: TtSeq(ms),
+
+            // The "dot" is before the first token of the matcher.
+            idx: 0,
+
+            // Initialize `matches` to a bunch of empty `Vec`s -- one for each metavar in
+            // `top_elts`. `match_lo` for `top_elts` is 0 and `match_hi` is `match_idx_hi`.
+            // `match_cur` is 0 since we haven't actually matched anything yet.
+            matches: create_matches(match_idx_hi),
+            match_lo: 0,
+            match_cur: 0,
+            match_hi: match_idx_hi,
+
+            // Haven't descended into any delimiters, so this is empty.
+            stack: smallvec![],
+
+            // Haven't descended into any sequences, so this is `None`.
+            repetition: None,
+        }
+    }
+
     /// Adds `m` as a named match for the `idx`-th metavar.
     fn push_match(&mut self, idx: usize, m: NamedMatch) {
         let matches = Lrc::make_mut(&mut self.matches[idx]);
@@ -215,6 +237,19 @@ impl<'root, 'tt> MatcherPos<'root, 'tt> {
     }
 }
 
+#[derive(Clone)]
+struct MatcherPosRepetition<'root, 'tt> {
+    /// The KleeneOp of this sequence.
+    seq_op: mbe::KleeneOp,
+
+    /// The separator.
+    sep: Option<Token>,
+
+    /// The "parent" matcher position. That is, the matcher position just before we enter the
+    /// sequence.
+    up: MatcherPosHandle<'root, 'tt>,
+}
+
 // Lots of MatcherPos instances are created at runtime. Allocating them on the
 // heap is slow. Furthermore, using SmallVec<MatcherPos> to allocate them all
 // on the stack is also slow, because MatcherPos is quite a large type and
@@ -258,6 +293,12 @@ impl<'root, 'tt> DerefMut for MatcherPosHandle<'root, 'tt> {
     }
 }
 
+enum EofItems<'root, 'tt> {
+    None,
+    One(MatcherPosHandle<'root, 'tt>),
+    Multiple,
+}
+
 /// Represents the possible results of an attempted parse.
 crate enum ParseResult<T> {
     /// Parsed successfully.
@@ -300,35 +341,6 @@ fn create_matches(len: usize) -> Box<[Lrc<NamedMatchVec>]> {
     .into_boxed_slice()
 }
 
-/// Generates the top-level matcher position in which the "dot" is before the first token of the
-/// matcher `ms`.
-fn initial_matcher_pos<'root, 'tt>(ms: &'tt [TokenTree]) -> MatcherPos<'root, 'tt> {
-    let match_idx_hi = count_names(ms);
-    let matches = create_matches(match_idx_hi);
-    MatcherPos {
-        // Start with the top level matcher given to us
-        top_elts: TtSeq(ms), // "elts" is an abbr. for "elements"
-        // The "dot" is before the first token of the matcher
-        idx: 0,
-
-        // Initialize `matches` to a bunch of empty `Vec`s -- one for each metavar in `top_elts`.
-        // `match_lo` for `top_elts` is 0 and `match_hi` is `matches.len()`. `match_cur` is 0 since
-        // we haven't actually matched anything yet.
-        matches,
-        match_lo: 0,
-        match_cur: 0,
-        match_hi: match_idx_hi,
-
-        // Haven't descended into any delimiters, so empty stack
-        stack: smallvec![],
-
-        // Haven't descended into any sequences, so both of these are `None`.
-        seq_op: None,
-        sep: None,
-        up: None,
-    }
-}
-
 /// `NamedMatch` is a pattern-match result for a single `token::MATCH_NONTERMINAL`:
 /// so it is associated with a single ident in a parse, and all
 /// `MatchedNonterminal`s in the `NamedMatch` have the same non-terminal type
@@ -475,10 +487,10 @@ fn inner_parse_loop<'root, 'tt>(
     sess: &ParseSess,
     cur_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>,
     next_items: &mut Vec<MatcherPosHandle<'root, 'tt>>,
-    eof_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>,
     bb_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>,
+    eof_items: &mut EofItems<'root, 'tt>,
     token: &Token,
-) -> ParseResult<()> {
+) -> Result<(), (rustc_span::Span, String)> {
     // Pop items from `cur_items` until it is empty.
     while let Some(mut item) = cur_items.pop() {
         // When unzipped trees end, remove them. This corresponds to backtracking out of a
@@ -504,7 +516,7 @@ fn inner_parse_loop<'root, 'tt>(
             // We are repeating iff there is a parent. If the matcher is inside of a repetition,
             // then we could be at the end of a sequence or at the beginning of the next
             // repetition.
-            if item.up.is_some() {
+            if let Some(repetition) = &item.repetition {
                 // At this point, regardless of whether there is a separator, we should add all
                 // matches from the complete repetition of the sequence to the shared, top-level
                 // `matches` list (actually, `up.matches`, which could itself not be the top-level,
@@ -515,7 +527,7 @@ fn inner_parse_loop<'root, 'tt>(
                 // NOTE: removing the condition `idx == len` allows trailing separators.
                 if idx == len {
                     // Get the `up` matcher
-                    let mut new_pos = item.up.clone().unwrap();
+                    let mut new_pos = repetition.up.clone();
 
                     // Add matches from this repetition to the `matches` of `up`
                     for idx in item.match_lo..item.match_hi {
@@ -530,32 +542,33 @@ fn inner_parse_loop<'root, 'tt>(
                 }
 
                 // Check if we need a separator.
-                if idx == len && item.sep.is_some() {
+                if idx == len && repetition.sep.is_some() {
                     // We have a separator, and it is the current token. We can advance past the
                     // separator token.
-                    if item.sep.as_ref().map_or(false, |sep| token_name_eq(token, sep)) {
+                    if repetition.sep.as_ref().map_or(false, |sep| token_name_eq(token, sep)) {
                         item.idx += 1;
                         next_items.push(item);
                     }
-                }
-                // We don't need a separator. Move the "dot" back to the beginning of the matcher
-                // and try to match again UNLESS we are only allowed to have _one_ repetition.
-                else if item.seq_op != Some(mbe::KleeneOp::ZeroOrOne) {
+                } else if repetition.seq_op != mbe::KleeneOp::ZeroOrOne {
+                    // We don't need a separator. Move the "dot" back to the beginning of the
+                    // matcher and try to match again UNLESS we are only allowed to have _one_
+                    // repetition.
                     item.match_cur = item.match_lo;
                     item.idx = 0;
                     cur_items.push(item);
                 }
+            } else {
+                // If we are not in a repetition, then being at the end of a matcher means that we
+                // have reached the potential end of the input.
+                *eof_items = match eof_items {
+                    EofItems::None => EofItems::One(item),
+                    EofItems::One(_) | EofItems::Multiple => EofItems::Multiple,
+                }
             }
-            // If we are not in a repetition, then being at the end of a matcher means that we have
-            // reached the potential end of the input.
-            else {
-                eof_items.push(item);
-            }
-        }
-        // We are in the middle of a matcher.
-        else {
-            // Look at what token in the matcher we are trying to match the current token (`token`)
-            // against. Depending on that, we may generate new items.
+        } else {
+            // We are in the middle of a matcher. Look at what token in the matcher we are trying
+            // to match the current token (`token`) against. Depending on that, we may generate new
+            // items.
             match item.top_elts.get_tt(idx) {
                 // Need to descend into a sequence
                 TokenTree::Sequence(sp, seq) => {
@@ -578,14 +591,16 @@ fn inner_parse_loop<'root, 'tt>(
                     let matches = create_matches(item.matches.len());
                     cur_items.push(MatcherPosHandle::Box(Box::new(MatcherPos {
                         stack: smallvec![],
-                        sep: seq.separator.clone(),
-                        seq_op: Some(seq.kleene.op),
                         idx: 0,
                         matches,
                         match_lo: item.match_cur,
                         match_cur: item.match_cur,
                         match_hi: item.match_cur + seq.num_captures,
-                        up: Some(item),
+                        repetition: Some(MatcherPosRepetition {
+                            up: item,
+                            sep: seq.separator.clone(),
+                            seq_op: seq.kleene.op,
+                        }),
                         top_elts: Tt(TokenTree::Sequence(sp, seq)),
                     })));
                 }
@@ -593,7 +608,7 @@ fn inner_parse_loop<'root, 'tt>(
                 // We need to match a metavar (but the identifier is invalid)... this is an error
                 TokenTree::MetaVarDecl(span, _, None) => {
                     if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() {
-                        return Error(span, "missing fragment specifier".to_string());
+                        return Err((span, "missing fragment specifier".to_string()));
                     }
                 }
 
@@ -641,7 +656,7 @@ fn inner_parse_loop<'root, 'tt>(
     }
 
     // Yay a successful parse (so far)!
-    Success(())
+    Ok(())
 }
 
 /// Use the given sequence of token trees (`ms`) as a matcher. Match the token
@@ -659,17 +674,18 @@ pub(super) fn parse_tt(
     //
     // This MatcherPos instance is allocated on the stack. All others -- and
     // there are frequently *no* others! -- are allocated on the heap.
-    let mut initial = initial_matcher_pos(ms);
+    let mut initial = MatcherPos::new(ms);
     let mut cur_items = smallvec![MatcherPosHandle::Ref(&mut initial)];
     let mut next_items = Vec::new();
 
     loop {
+        assert!(next_items.is_empty());
+
         // Matcher positions black-box parsed by parser.rs (`parser`)
         let mut bb_items = SmallVec::new();
 
         // Matcher positions that would be valid if the macro invocation was over now
-        let mut eof_items = SmallVec::new();
-        assert!(next_items.is_empty());
+        let mut eof_items = EofItems::None;
 
         // Process `cur_items` until either we have finished the input or we need to get some
         // parsing from the black-box parser done. The result is that `next_items` will contain a
@@ -678,37 +694,34 @@ pub(super) fn parse_tt(
             parser.sess,
             &mut cur_items,
             &mut next_items,
-            &mut eof_items,
             &mut bb_items,
+            &mut eof_items,
             &parser.token,
         ) {
-            Success(_) => {}
-            Failure(token, msg) => return Failure(token, msg),
-            Error(sp, msg) => return Error(sp, msg),
-            ErrorReported => return ErrorReported,
+            Ok(()) => {}
+            Err((sp, msg)) => return Error(sp, msg),
         }
 
         // inner parse loop handled all cur_items, so it's empty
         assert!(cur_items.is_empty());
 
-        // We need to do some post processing after the `inner_parser_loop`.
+        // We need to do some post processing after the `inner_parse_loop`.
         //
         // Error messages here could be improved with links to original rules.
 
         // If we reached the EOF, check that there is EXACTLY ONE possible matcher. Otherwise,
         // either the parse is ambiguous (which should never happen) or there is a syntax error.
         if parser.token == token::Eof {
-            if eof_items.len() == 1 {
-                let matches =
-                    eof_items[0].matches.iter_mut().map(|dv| Lrc::make_mut(dv).pop().unwrap());
-                return nameize(parser.sess, ms, matches);
-            } else if eof_items.len() > 1 {
-                return Error(
-                    parser.token.span,
-                    "ambiguity: multiple successful parses".to_string(),
-                );
-            } else {
-                return Failure(
+            return match eof_items {
+                EofItems::One(mut eof_item) => {
+                    let matches =
+                        eof_item.matches.iter_mut().map(|dv| Lrc::make_mut(dv).pop().unwrap());
+                    nameize(parser.sess, ms, matches)
+                }
+                EofItems::Multiple => {
+                    Error(parser.token.span, "ambiguity: multiple successful parses".to_string())
+                }
+                EofItems::None => Failure(
                     Token::new(
                         token::Eof,
                         if parser.token.span.is_dummy() {
@@ -718,12 +731,12 @@ pub(super) fn parse_tt(
                         },
                     ),
                     "missing tokens in macro arguments",
-                );
-            }
+                ),
+            };
         }
-        // Performance hack: eof_items may share matchers via Rc with other things that we want
-        // to modify. Dropping eof_items now may drop these refcounts to 1, preventing an
-        // unnecessary implicit clone later in Rc::make_mut.
+        // Performance hack: `eof_items` may share matchers via `Rc` with other things that we want
+        // to modify. Dropping `eof_items` now may drop these refcounts to 1, preventing an
+        // unnecessary implicit clone later in `Rc::make_mut`.
         drop(eof_items);
 
         // If there are no possible next positions AND we aren't waiting for the black-box parser,
@@ -731,9 +744,10 @@ pub(super) fn parse_tt(
         if bb_items.is_empty() && next_items.is_empty() {
             return Failure(parser.token.clone(), "no rules expected this token in macro call");
         }
-        // Another possibility is that we need to call out to parse some rust nonterminal
-        // (black-box) parser. However, if there is not EXACTLY ONE of these, something is wrong.
-        else if (!bb_items.is_empty() && !next_items.is_empty()) || bb_items.len() > 1 {
+
+        if (!bb_items.is_empty() && !next_items.is_empty()) || bb_items.len() > 1 {
+            // We need to call out to parse some rust nonterminal (black-box) parser. But something
+            // is wrong, because there is not EXACTLY ONE of these.
             let nts = bb_items
                 .iter()
                 .map(|item| match item.top_elts.get_tt(item.idx) {
@@ -755,15 +769,15 @@ pub(super) fn parse_tt(
                 ),
             );
         }
-        // Dump all possible `next_items` into `cur_items` for the next iteration.
-        else if !next_items.is_empty() {
-            // Now process the next token
+
+        if !next_items.is_empty() {
+            // Dump all possible `next_items` into `cur_items` for the next iteration. Then process
+            // the next token.
             cur_items.extend(next_items.drain(..));
             parser.to_mut().bump();
-        }
-        // Finally, we have the case where we need to call the black-box parser to get some
-        // nonterminal.
-        else {
+        } else {
+            // Finally, we have the case where we need to call the black-box parser to get some
+            // nonterminal.
             assert_eq!(bb_items.len(), 1);
 
             let mut item = bb_items.pop().unwrap();
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index 54000527c15b9..760dea77f9c2b 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -233,7 +233,7 @@ pub(super) fn transcribe<'a>(
                         } else {
                             // Other variables are emitted into the output stream as groups with
                             // `Delimiter::None` to maintain parsing priorities.
-                            // `Interpolated` is currenty used for such groups in rustc parser.
+                            // `Interpolated` is currently used for such groups in rustc parser.
                             marker.visit_span(&mut sp);
                             TokenTree::token(token::Interpolated(nt.clone()), sp)
                         };
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 869cada400f0f..0aef5982cff50 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -847,7 +847,7 @@ impl server::Span for Rustc<'_, '_> {
     /// the `quote` proc-macro. This will save the span of
     /// "hello" into the metadata of `my_proc_macro`. As a result,
     /// the body of `my_proc_macro` (after expansion) will end
-    /// up containg a call that looks like this:
+    /// up containing a call that looks like this:
     /// `proc_macro::Ident::new("hello", proc_macro::Span::recover_proc_macro_span(0))`
     ///
     /// where `0` is the id returned by this function.
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index 870c3f8068245..908a936142475 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -27,7 +27,7 @@ pub enum LoadResult<T> {
     },
     /// The file either didn't exist or was produced by an incompatible compiler version.
     DataOutOfDate,
-    /// An error occured.
+    /// An error occurred.
     Error {
         #[allow(missing_docs)]
         message: String,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index 3bc30f0220d40..16f7504cbbe58 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -928,7 +928,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 /// performing that replacement, we'll turn all remaining infer type params to use their name from
 /// their definition, and replace all the `[type error]`s back to being infer so they display in
 /// the output as `_`. If we didn't go through `[type error]`, we would either show all type params
-/// by their name *or* `_`, neither of which is desireable: we want to show all types that we could
+/// by their name *or* `_`, neither of which is desirable: we want to show all types that we could
 /// infer as `_` to reduce verbosity and avoid telling the user about unnecessary type annotations.
 struct ResolvedTypeParamEraser<'tcx> {
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs
index e0420291aa3f5..4710eae6189a5 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs
@@ -58,7 +58,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
                 bug!("Node not an impl.");
             };
 
-            // Next, let's figure out the set of trait objects with implict static bounds
+            // Next, let's figure out the set of trait objects with implicit static bounds
             let ty = self.tcx().type_of(*impl_def_id);
             let mut v = super::static_impl_trait::TraitObjectVisitor(FxHashSet::default());
             v.visit_ty(ty);
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 919fe7cac5ce3..ef6c9ef6627d5 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -267,12 +267,6 @@ extern "C" LLVMAttributeRef LLVMRustCreateAttrNoValue(LLVMContextRef C,
   return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr)));
 }
 
-extern "C" LLVMAttributeRef LLVMRustCreateAttrStringValue(LLVMContextRef C,
-                                                          const char *Name,
-                                                          const char *Value) {
-  return wrap(Attribute::get(*unwrap(C), StringRef(Name), StringRef(Value)));
-}
-
 extern "C" LLVMAttributeRef LLVMRustCreateAlignmentAttr(LLVMContextRef C,
                                                         uint64_t Bytes) {
   return wrap(Attribute::getWithAlignment(*unwrap(C), llvm::Align(Bytes)));
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
index 37cfc4a0dc3c2..6b1eaa4d399d9 100644
--- a/compiler/rustc_session/Cargo.toml
+++ b/compiler/rustc_session/Cargo.toml
@@ -15,6 +15,5 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_span = { path = "../rustc_span" }
 rustc_fs_util = { path = "../rustc_fs_util" }
-num_cpus = "1.0"
 rustc_ast = { path = "../rustc_ast" }
 rustc_lint_defs = { path = "../rustc_lint_defs" }
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index c2b13346cd6c9..43123d30ad5c6 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -551,7 +551,7 @@ mod parse {
     crate fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool {
         match v.and_then(|s| s.parse().ok()) {
             Some(0) => {
-                *slot = ::num_cpus::get();
+                *slot = std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get);
                 true
             }
             Some(i) => {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 6767593bbc51a..9d452131fa6ac 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -539,6 +539,7 @@ symbols! {
         custom_inner_attributes,
         custom_test_frameworks,
         d,
+        d32,
         dbg_macro,
         dead_code,
         dealloc,
diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs
index 88f2d3f80d2c3..aaa632333db38 100644
--- a/compiler/rustc_target/src/asm/arm.rs
+++ b/compiler/rustc_target/src/asm/arm.rs
@@ -50,9 +50,12 @@ impl ArmInlineAsmRegClass {
         match self {
             Self::reg => types! { _: I8, I16, I32, F32; },
             Self::sreg | Self::sreg_low16 => types! { vfp2: I32, F32; },
-            Self::dreg | Self::dreg_low16 | Self::dreg_low8 => types! {
+            Self::dreg_low16 | Self::dreg_low8 => types! {
                 vfp2: I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2);
             },
+            Self::dreg => types! {
+                d32: I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2);
+            },
             Self::qreg | Self::qreg_low8 | Self::qreg_low4 => types! {
                 neon: VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4);
             },
diff --git a/library/core/src/iter/adapters/map.rs b/library/core/src/iter/adapters/map.rs
index b2ed82508dd29..d2077a63e150a 100644
--- a/library/core/src/iter/adapters/map.rs
+++ b/library/core/src/iter/adapters/map.rs
@@ -38,7 +38,7 @@ use crate::ops::Try;
 /// }
 /// ```
 ///
-/// This will print "('a', 1), ('b', 2), ('c', 3)".
+/// This will print `('a', 1), ('b', 2), ('c', 3)`.
 ///
 /// Now consider this twist where we add a call to `rev`. This version will
 /// print `('c', 1), ('b', 2), ('a', 3)`. Note that the letters are reversed,
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index cf8cf5ad49f73..ff01ce2733329 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -279,10 +279,15 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> {
         ))] {
             #[cfg(any(target_os = "android", target_os = "linux"))]
             {
+                let quota = cgroup2_quota().max(1);
                 let mut set: libc::cpu_set_t = unsafe { mem::zeroed() };
-                if unsafe { libc::sched_getaffinity(0, mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 {
-                    let count = unsafe { libc::CPU_COUNT(&set) };
-                    return Ok(unsafe { NonZeroUsize::new_unchecked(count as usize) });
+                unsafe {
+                    if libc::sched_getaffinity(0, mem::size_of::<libc::cpu_set_t>(), &mut set) == 0 {
+                        let count = libc::CPU_COUNT(&set) as usize;
+                        let count = count.min(quota);
+                        // SAFETY: affinity mask can't be empty and the quota gets clamped to a minimum of 1
+                        return Ok(NonZeroUsize::new_unchecked(count));
+                    }
                 }
             }
             match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } {
@@ -368,6 +373,80 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> {
     }
 }
 
+/// Returns cgroup CPU quota in core-equivalents, rounded down, or usize::MAX if the quota cannot
+/// be determined or is not set.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn cgroup2_quota() -> usize {
+    use crate::ffi::OsString;
+    use crate::fs::{try_exists, File};
+    use crate::io::Read;
+    use crate::os::unix::ffi::OsStringExt;
+    use crate::path::PathBuf;
+
+    let mut quota = usize::MAX;
+
+    let _: Option<()> = try {
+        let mut buf = Vec::with_capacity(128);
+        // find our place in the cgroup hierarchy
+        File::open("/proc/self/cgroup").ok()?.read_to_end(&mut buf).ok()?;
+        let cgroup_path = buf
+            .split(|&c| c == b'\n')
+            .filter_map(|line| {
+                let mut fields = line.splitn(3, |&c| c == b':');
+                // expect cgroupv2 which has an empty 2nd field
+                if fields.nth(1) != Some(b"") {
+                    return None;
+                }
+                let path = fields.last()?;
+                // skip leading slash
+                Some(path[1..].to_owned())
+            })
+            .next()?;
+        let cgroup_path = PathBuf::from(OsString::from_vec(cgroup_path));
+
+        let mut path = PathBuf::with_capacity(128);
+        let mut read_buf = String::with_capacity(20);
+
+        let cgroup_mount = "/sys/fs/cgroup";
+
+        path.push(cgroup_mount);
+        path.push(&cgroup_path);
+
+        path.push("cgroup.controllers");
+
+        // skip if we're not looking at cgroup2
+        if matches!(try_exists(&path), Err(_) | Ok(false)) {
+            return usize::MAX;
+        };
+
+        path.pop();
+
+        while path.starts_with(cgroup_mount) {
+            path.push("cpu.max");
+
+            read_buf.clear();
+
+            if File::open(&path).and_then(|mut f| f.read_to_string(&mut read_buf)).is_ok() {
+                let raw_quota = read_buf.lines().next()?;
+                let mut raw_quota = raw_quota.split(' ');
+                let limit = raw_quota.next()?;
+                let period = raw_quota.next()?;
+                match (limit.parse::<usize>(), period.parse::<usize>()) {
+                    (Ok(limit), Ok(period)) => {
+                        quota = quota.min(limit / period);
+                    }
+                    _ => {}
+                }
+            }
+
+            path.pop(); // pop filename
+            path.pop(); // pop dir
+        }
+    };
+
+    quota
+}
+
 #[cfg(all(
     not(target_os = "linux"),
     not(target_os = "freebsd"),
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index beb606099341e..09d1e714ab6dd 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -1524,7 +1524,10 @@ fn _assert_sync_and_send() {
 ///
 /// On Linux:
 /// - It may overcount the amount of parallelism available when limited by a
-///   process-wide affinity mask, or when affected by cgroup limits.
+///   process-wide affinity mask or cgroup quotas and cgroup2 fs or `sched_getaffinity()` can't be
+///   queried, e.g. due to sandboxing.
+/// - It may undercount the amount of parallelism if the current thread's affinity mask
+///   does not reflect the process' cpuset, e.g. due to pinned threads.
 ///
 /// On all targets:
 /// - It may overcount the amount of parallelism available when running in a VM
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index 592a137e379de..02efc08cc791f 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -37,7 +37,6 @@ test = false
 build_helper = { path = "../build_helper" }
 cmake = "0.1.38"
 filetime = "0.2"
-num_cpus = "1.0"
 getopts = "0.2.19"
 cc = "1.0.69"
 libc = "0.2"
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index d6f77fe6cd6d0..ce76ccd57551d 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -1187,7 +1187,7 @@ fn set<T>(field: &mut T, val: Option<T>) {
 
 fn threads_from_config(v: u32) -> u32 {
     match v {
-        0 => num_cpus::get() as u32,
+        0 => std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32,
         n => n,
     }
 }
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index 9180c5f03af68..74528f2752f5f 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -208,7 +208,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
         let j_msg = format!(
             "number of jobs to run in parallel; \
              defaults to {} (this host's logical CPU count)",
-            num_cpus::get()
+            std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get)
         );
         opts.optopt("j", "jobs", &j_msg, "JOBS");
         opts.optflag("h", "help", "print this help message");
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 86339c8d7f88d..6a58de40181c2 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -917,7 +917,9 @@ impl Build {
     /// Returns the number of parallel jobs that have been configured for this
     /// build.
     fn jobs(&self) -> u32 {
-        self.config.jobs.unwrap_or_else(|| num_cpus::get() as u32)
+        self.config.jobs.unwrap_or_else(|| {
+            std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32
+        })
     }
 
     fn debuginfo_map_to(&self, which: GitRepo) -> Option<String> {
diff --git a/src/test/debuginfo/basic-types-globals-metadata.rs b/src/test/debuginfo/basic-types-globals-metadata.rs
index 3934d3c7a685e..30e4b882076af 100644
--- a/src/test/debuginfo/basic-types-globals-metadata.rs
+++ b/src/test/debuginfo/basic-types-globals-metadata.rs
@@ -1,5 +1,4 @@
 // min-lldb-version: 310
-// ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 // compile-flags:-g
 // gdb-command:run
@@ -11,7 +10,7 @@
 // gdb-check:type = isize
 // gdbg-command:whatis 'basic_types_globals_metadata::C'
 // gdbr-command:whatis basic_types_globals_metadata::C
-// gdb-check:type = char
+// gdb-check:type = char32_t
 // gdbg-command:whatis 'basic_types_globals_metadata::I8'
 // gdbr-command:whatis basic_types_globals_metadata::I8
 // gdb-check:type = i8
diff --git a/src/test/debuginfo/c-style-enum.rs b/src/test/debuginfo/c-style-enum.rs
index dce34fc0dcf5e..86079e45b2727 100644
--- a/src/test/debuginfo/c-style-enum.rs
+++ b/src/test/debuginfo/c-style-enum.rs
@@ -1,5 +1,4 @@
 // ignore-aarch64
-// ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 // min-lldb-version: 310
 
 // compile-flags:-g
diff --git a/src/test/debuginfo/cross-crate-spans.rs b/src/test/debuginfo/cross-crate-spans.rs
index 7c58e1db23f35..57077f4aa802f 100644
--- a/src/test/debuginfo/cross-crate-spans.rs
+++ b/src/test/debuginfo/cross-crate-spans.rs
@@ -5,7 +5,6 @@
 
 // This fails on lldb 6.0.1 on x86-64 Fedora 28; so mark it macOS-only
 // for now.
-// only-macos
 
 // aux-build:cross_crate_spans.rs
 extern crate cross_crate_spans;
diff --git a/src/test/debuginfo/destructured-for-loop-variable.rs b/src/test/debuginfo/destructured-for-loop-variable.rs
index 15cb88ef25d5b..eb821a580564a 100644
--- a/src/test/debuginfo/destructured-for-loop-variable.rs
+++ b/src/test/debuginfo/destructured-for-loop-variable.rs
@@ -1,9 +1,5 @@
 // min-lldb-version: 310
 
-// This fails on lldb 6.0.1 on x86-64 Fedora 28; so mark it macOS-only
-// for now.
-// only-macos
-
 // compile-flags:-g
 
 // === GDB TESTS ===================================================================================
diff --git a/src/test/debuginfo/function-arg-initialization.rs b/src/test/debuginfo/function-arg-initialization.rs
index dea1339517b46..ad432d2e7d370 100644
--- a/src/test/debuginfo/function-arg-initialization.rs
+++ b/src/test/debuginfo/function-arg-initialization.rs
@@ -1,4 +1,4 @@
-// ignore-test // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
+// ignore-lldb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 // min-lldb-version: 310
 
 // This test case checks if function arguments already have the correct value
diff --git a/src/test/debuginfo/lexical-scopes-in-block-expression.rs b/src/test/debuginfo/lexical-scopes-in-block-expression.rs
index 72621ffc74267..fee4ac7bd2f6d 100644
--- a/src/test/debuginfo/lexical-scopes-in-block-expression.rs
+++ b/src/test/debuginfo/lexical-scopes-in-block-expression.rs
@@ -1,5 +1,4 @@
 // min-lldb-version: 310
-// ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 // compile-flags:-g
 
diff --git a/src/test/debuginfo/limited-debuginfo.rs b/src/test/debuginfo/limited-debuginfo.rs
index bd381cd0e22d3..bf03ac3dd29e9 100644
--- a/src/test/debuginfo/limited-debuginfo.rs
+++ b/src/test/debuginfo/limited-debuginfo.rs
@@ -1,5 +1,4 @@
 // ignore-lldb
-// ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 // compile-flags:-C debuginfo=1
 
diff --git a/src/test/debuginfo/method-on-enum.rs b/src/test/debuginfo/method-on-enum.rs
index aaa9bd9d6f97a..e746895797820 100644
--- a/src/test/debuginfo/method-on-enum.rs
+++ b/src/test/debuginfo/method-on-enum.rs
@@ -1,5 +1,5 @@
 // min-lldb-version: 310
-// ignore-test // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
+// ignore-lldb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 // compile-flags:-g
 
diff --git a/src/test/debuginfo/simple-struct.rs b/src/test/debuginfo/simple-struct.rs
index aa3cf023a718d..fc2b1f64221d1 100644
--- a/src/test/debuginfo/simple-struct.rs
+++ b/src/test/debuginfo/simple-struct.rs
@@ -1,5 +1,4 @@
 // min-lldb-version: 310
-// ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 // compile-flags:-g
 
diff --git a/src/test/debuginfo/simple-tuple.rs b/src/test/debuginfo/simple-tuple.rs
index 0807cfedce01d..b89a51437b3ba 100644
--- a/src/test/debuginfo/simple-tuple.rs
+++ b/src/test/debuginfo/simple-tuple.rs
@@ -1,5 +1,4 @@
 // min-lldb-version: 310
-// ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 // compile-flags:-g
 
diff --git a/src/test/debuginfo/union-smoke.rs b/src/test/debuginfo/union-smoke.rs
index 4d4b6cc96fb57..5d1cd03ad3e8c 100644
--- a/src/test/debuginfo/union-smoke.rs
+++ b/src/test/debuginfo/union-smoke.rs
@@ -1,5 +1,4 @@
 // min-lldb-version: 310
-// ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 // ignore-gdb-version: 7.11.90 - 7.12.9
 
diff --git a/src/test/debuginfo/vec.rs b/src/test/debuginfo/vec.rs
index 895661816b864..9bead21fe5f88 100644
--- a/src/test/debuginfo/vec.rs
+++ b/src/test/debuginfo/vec.rs
@@ -1,5 +1,4 @@
 // min-lldb-version: 310
-// ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 // compile-flags:-g
 
diff --git a/src/test/ui/check-cfg/stmt-no-ice.rs b/src/test/ui/check-cfg/stmt-no-ice.rs
new file mode 100644
index 0000000000000..cf76487ed46fb
--- /dev/null
+++ b/src/test/ui/check-cfg/stmt-no-ice.rs
@@ -0,0 +1,10 @@
+// This test checks that there is no ICE with this code
+//
+// check-pass
+// compile-flags:--check-cfg=names() -Z unstable-options
+
+fn main() {
+    #[cfg(crossbeam_loom)]
+    //~^ WARNING unexpected `cfg` condition name
+    {}
+}
diff --git a/src/test/ui/check-cfg/stmt-no-ice.stderr b/src/test/ui/check-cfg/stmt-no-ice.stderr
new file mode 100644
index 0000000000000..da65b596911d2
--- /dev/null
+++ b/src/test/ui/check-cfg/stmt-no-ice.stderr
@@ -0,0 +1,10 @@
+warning: unexpected `cfg` condition name
+  --> $DIR/stmt-no-ice.rs:7:11
+   |
+LL |     #[cfg(crossbeam_loom)]
+   |           ^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml
index c022d3aa0acd7..c437bde5ae69a 100644
--- a/src/tools/build-manifest/Cargo.toml
+++ b/src/tools/build-manifest/Cargo.toml
@@ -13,4 +13,3 @@ tar = "0.4.29"
 sha2 = "0.10.1"
 rayon = "1.5.1"
 hex = "0.4.2"
-num_cpus = "1.13.0"
diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs
index 8a62146abfc4e..378efeb644375 100644
--- a/src/tools/build-manifest/src/main.rs
+++ b/src/tools/build-manifest/src/main.rs
@@ -208,7 +208,7 @@ fn main() {
     let num_threads = if let Some(num) = env::var_os("BUILD_MANIFEST_NUM_THREADS") {
         num.to_str().unwrap().parse().expect("invalid number for BUILD_MANIFEST_NUM_THREADS")
     } else {
-        num_cpus::get()
+        std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get)
     };
     rayon::ThreadPoolBuilder::new()
         .num_threads(num_threads)