summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/test.rs14
-rw-r--r--src/ci/azure-pipelines/auto.yml2
-rw-r--r--src/ci/azure-pipelines/steps/run.yml4
-rw-r--r--src/ci/azure-pipelines/try.yml2
-rwxr-xr-xsrc/ci/scripts/install-clang.sh4
-rwxr-xr-xsrc/ci/scripts/switch-xcode.sh13
-rw-r--r--src/liballoc/string.rs15
-rw-r--r--src/libcore/alloc.rs12
-rw-r--r--src/libcore/str/mod.rs2
-rw-r--r--src/librustc/dep_graph/dep_node.rs139
-rw-r--r--src/librustc/dep_graph/graph.rs1
-rw-r--r--src/librustc/mir/mono.rs2
-rw-r--r--src/librustc/ty/context.rs5
-rw-r--r--src/librustc/ty/query/caches.rs112
-rw-r--r--src/librustc/ty/query/config.rs13
-rw-r--r--src/librustc/ty/query/keys.rs51
-rw-r--r--src/librustc/ty/query/mod.rs9
-rw-r--r--src/librustc/ty/query/on_disk_cache.rs32
-rw-r--r--src/librustc/ty/query/plumbing.rs523
-rw-r--r--src/librustc/ty/query/profiling_support.rs43
-rw-r--r--src/librustc/ty/query/stats.rs139
-rw-r--r--src/librustc_data_structures/profiling.rs9
-rw-r--r--src/librustc_data_structures/stable_hasher.rs1
-rw-r--r--src/librustc_interface/passes.rs4
-rw-r--r--src/librustc_interface/queries.rs2
-rw-r--r--src/librustc_macros/src/query.rs21
-rw-r--r--src/librustc_mir/const_eval/eval_queries.rs4
-rw-r--r--src/librustc_mir/dataflow/generic/mod.rs11
-rw-r--r--src/librustc_mir/dataflow/impls/borrowed_locals.rs292
-rw-r--r--src/librustc_mir/dataflow/impls/indirect_mutation.rs136
-rw-r--r--src/librustc_mir/dataflow/impls/mod.rs2
-rw-r--r--src/librustc_mir/dataflow/impls/storage_liveness.rs46
-rw-r--r--src/librustc_mir/dataflow/mod.rs3
-rw-r--r--src/librustc_mir/transform/check_consts/resolver.rs2
-rw-r--r--src/librustc_mir/transform/check_consts/validation.rs37
-rw-r--r--src/librustc_mir/transform/generator.rs20
-rw-r--r--src/librustc_mir/transform/promote_consts.rs24
-rw-r--r--src/librustc_mir/transform/rustc_peek.rs26
-rw-r--r--src/librustc_session/session.rs1
-rw-r--r--src/librustc_span/symbol.rs2
-rw-r--r--src/librustc_target/spec/thumb_base.rs3
-rw-r--r--src/libserialize/serialize.rs19
-rw-r--r--src/libstd/fs.rs12
-rw-r--r--src/test/incremental/issue-61530.rs3
-rw-r--r--src/test/ui/issues/issue-38074.rs3
-rw-r--r--src/test/ui/issues/issue-69225-layout-repeated-checked-add.rs31
-rw-r--r--src/test/ui/mir-dataflow/indirect-mutation-offset.rs7
-rw-r--r--src/test/ui/mir-dataflow/indirect-mutation-offset.stderr2
-rw-r--r--src/test/ui/simd-intrinsic/simd-intrinsic-generic-elements.rs4
-rw-r--r--src/test/ui/simd-intrinsic/simd-intrinsic-generic-elements.stderr30
-rw-r--r--src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557-ice.rs3
-rw-r--r--src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557.rs3
-rw-r--r--src/test/ui/simd/simd-intrinsic-generic-elements.rs6
m---------src/tools/cargo0
m---------src/tools/clippy16
-rwxr-xr-xsrc/tools/linkchecker/linkcheck.sh113
56 files changed, 1235 insertions, 800 deletions
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 43561fa4f2f..f939f79e4f4 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -662,7 +662,7 @@ impl Step for RustdocJSNotStd {
                 target: self.target,
                 mode: "js-doc-test",
                 suite: "rustdoc-js",
-                path: None,
+                path: "src/test/rustdoc-js",
                 compare_mode: None,
             });
         } else {
@@ -698,7 +698,7 @@ impl Step for RustdocUi {
             target: self.target,
             mode: "ui",
             suite: "rustdoc-ui",
-            path: Some("src/test/rustdoc-ui"),
+            path: "src/test/rustdoc-ui",
             compare_mode: None,
         })
     }
@@ -843,7 +843,7 @@ macro_rules! test_definitions {
                     target: self.target,
                     mode: $mode,
                     suite: $suite,
-                    path: Some($path),
+                    path: $path,
                     compare_mode: $compare_mode,
                 })
             }
@@ -926,7 +926,7 @@ struct Compiletest {
     target: Interned<String>,
     mode: &'static str,
     suite: &'static str,
-    path: Option<&'static str>,
+    path: &'static str,
     compare_mode: Option<&'static str>,
 }
 
@@ -949,7 +949,7 @@ impl Step for Compiletest {
         let suite = self.suite;
 
         // Path for test suite
-        let suite_path = self.path.unwrap_or("");
+        let suite_path = self.path;
 
         // Skip codegen tests if they aren't enabled in configuration.
         if !builder.config.codegen_tests && suite == "codegen" {
@@ -1051,10 +1051,10 @@ impl Step for Compiletest {
         cmd.arg("--docck-python").arg(builder.python());
 
         if builder.config.build.ends_with("apple-darwin") {
-            // Force /usr/bin/python on macOS for LLDB tests because we're loading the
+            // Force /usr/bin/python3 on macOS for LLDB tests because we're loading the
             // LLDB plugin's compiled module which only works with the system python
             // (namely not Homebrew-installed python)
-            cmd.arg("--lldb-python").arg("/usr/bin/python");
+            cmd.arg("--lldb-python").arg("/usr/bin/python3");
         } else {
             cmd.arg("--lldb-python").arg(builder.python());
         }
diff --git a/src/ci/azure-pipelines/auto.yml b/src/ci/azure-pipelines/auto.yml
index 79a49fc48be..74b7469ea27 100644
--- a/src/ci/azure-pipelines/auto.yml
+++ b/src/ci/azure-pipelines/auto.yml
@@ -63,7 +63,7 @@ jobs:
 - job: macOS
   timeoutInMinutes: 600
   pool:
-    vmImage: macos-10.13
+    vmImage: macos-10.15
   steps:
   - template: steps/run.yml
   strategy:
diff --git a/src/ci/azure-pipelines/steps/run.yml b/src/ci/azure-pipelines/steps/run.yml
index c39f75aba89..ee9425aa1c5 100644
--- a/src/ci/azure-pipelines/steps/run.yml
+++ b/src/ci/azure-pipelines/steps/run.yml
@@ -51,10 +51,6 @@ steps:
   displayName: Install clang
   condition: and(succeeded(), not(variables.SKIP_JOB))
 
-- bash: src/ci/scripts/switch-xcode.sh
-  displayName: Switch to Xcode 9.3
-  condition: and(succeeded(), not(variables.SKIP_JOB))
-
 - bash: src/ci/scripts/install-wix.sh
   displayName: Install wix
   condition: and(succeeded(), not(variables.SKIP_JOB))
diff --git a/src/ci/azure-pipelines/try.yml b/src/ci/azure-pipelines/try.yml
index b6177b2cc9b..f8ddf0eb46c 100644
--- a/src/ci/azure-pipelines/try.yml
+++ b/src/ci/azure-pipelines/try.yml
@@ -25,7 +25,7 @@ jobs:
 # - job: macOS
 #   timeoutInMinutes: 600
 #   pool:
-#     vmImage: macos-10.13
+#     vmImage: macos-10.15
 #   steps:
 #   - template: steps/run.yml
 #   strategy:
diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh
index e16a4814197..c242f5d4562 100755
--- a/src/ci/scripts/install-clang.sh
+++ b/src/ci/scripts/install-clang.sh
@@ -19,9 +19,7 @@ if isMacOS; then
     # native clang is configured to use the correct path, but our custom one
     # doesn't. This sets the SDKROOT environment variable to the SDK so that
     # our own clang can figure out the correct include path on its own.
-    if ! [[ -d "/usr/include" ]]; then
-        ciCommandSetEnv SDKROOT "$(xcrun --sdk macosx --show-sdk-path)"
-    fi
+    ciCommandSetEnv SDKROOT "$(xcrun --sdk macosx --show-sdk-path)"
 
     # Configure `AR` specifically so rustbuild doesn't try to infer it as
     # `clang-ar` by accident.
diff --git a/src/ci/scripts/switch-xcode.sh b/src/ci/scripts/switch-xcode.sh
deleted file mode 100755
index 2cbb2ddbc70..00000000000
--- a/src/ci/scripts/switch-xcode.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-# Switch to XCode 9.3 on OSX since it seems to be the last version that supports
-# i686-apple-darwin. We'll eventually want to upgrade this and it will probably
-# force us to drop i686-apple-darwin, but let's keep the wheels turning for now.
-
-set -euo pipefail
-IFS=$'\n\t'
-
-source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
-
-if isMacOS; then
-    sudo xcode-select --switch /Applications/Xcode_9.3.app
-fi
diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs
index 3cb1f259a0b..f5afea15d65 100644
--- a/src/liballoc/string.rs
+++ b/src/liballoc/string.rs
@@ -2106,18 +2106,11 @@ impl ops::DerefMut for String {
     }
 }
 
-/// An error when parsing a `String`.
+/// A type alias for [`Infallible`].
 ///
-/// This `enum` is slightly awkward: it will never actually exist. This error is
-/// part of the type signature of the implementation of [`FromStr`] on
-/// [`String`]. The return type of [`from_str`], requires that an error be
-/// defined, but, given that a [`String`] can always be made into a new
-/// [`String`] without error, this type will never actually be returned. As
-/// such, it is only here to satisfy said signature, and is useless otherwise.
+/// This alias exists for backwards compatibility, and may be eventually deprecated.
 ///
-/// [`FromStr`]: ../../std/str/trait.FromStr.html
-/// [`String`]: struct.String.html
-/// [`from_str`]: ../../std/str/trait.FromStr.html#tymethod.from_str
+/// [`Infallible`]: ../../core/convert/enum.Infallible.html
 #[stable(feature = "str_parse_error", since = "1.5.0")]
 pub type ParseError = core::convert::Infallible;
 
@@ -2125,7 +2118,7 @@ pub type ParseError = core::convert::Infallible;
 impl FromStr for String {
     type Err = core::convert::Infallible;
     #[inline]
-    fn from_str(s: &str) -> Result<String, ParseError> {
+    fn from_str(s: &str) -> Result<String, Self::Err> {
         Ok(String::from(s))
     }
 }
diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs
index 71f7f971eab..a04e75bc7ce 100644
--- a/src/libcore/alloc.rs
+++ b/src/libcore/alloc.rs
@@ -241,11 +241,13 @@ impl Layout {
     #[unstable(feature = "alloc_layout_extra", issue = "55724")]
     #[inline]
     pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutErr> {
-        // This cannot overflow. Quoting from the invariant of Layout:
-        // > `size`, when rounded up to the nearest multiple of `align`,
-        // > must not overflow (i.e., the rounded value must be less than
-        // > `usize::MAX`)
-        let padded_size = self.size() + self.padding_needed_for(self.align());
+        // Warning, removing the checked_add here led to segfaults in #67174. Further
+        // analysis in #69225 seems to indicate that this is an LTO-related
+        // miscompilation, so #67174 might be able to be reapplied in the future.
+        let padded_size = self
+            .size()
+            .checked_add(self.padding_needed_for(self.align()))
+            .ok_or(LayoutErr { private: () })?;
         let alloc_size = padded_size.checked_mul(n).ok_or(LayoutErr { private: () })?;
 
         unsafe {
diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs
index e5b8412e117..7c4acb0edb7 100644
--- a/src/libcore/str/mod.rs
+++ b/src/libcore/str/mod.rs
@@ -1499,7 +1499,7 @@ fn contains_nonascii(x: usize) -> bool {
 
 /// Walks through `v` checking that it's a valid UTF-8 sequence,
 /// returning `Ok(())` in that case, or, if it is invalid, `Err(err)`.
-#[inline]
+#[inline(always)]
 fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> {
     let mut index = 0;
     let len = v.len();
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index 29b94986a5f..eb7e2871bfc 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -76,10 +76,6 @@ macro_rules! erase {
     ($x:tt) => {{}};
 }
 
-macro_rules! replace {
-    ($x:tt with $($y:tt)*) => ($($y)*)
-}
-
 macro_rules! is_anon_attr {
     (anon) => {
         true
@@ -99,19 +95,18 @@ macro_rules! is_eval_always_attr {
 }
 
 macro_rules! contains_anon_attr {
-    ($($attr:ident),*) => ({$(is_anon_attr!($attr) | )* false});
+    ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_anon_attr!($attr) | )* false});
 }
 
 macro_rules! contains_eval_always_attr {
-    ($($attr:ident),*) => ({$(is_eval_always_attr!($attr) | )* false});
+    ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_eval_always_attr!($attr) | )* false});
 }
 
 macro_rules! define_dep_nodes {
     (<$tcx:tt>
     $(
-        [$($attr:ident),* ]
+        [$($attrs:tt)*]
         $variant:ident $(( $tuple_arg_ty:ty $(,)? ))*
-                       $({ $($struct_arg_name:ident : $struct_arg_ty:ty),* })*
       ,)*
     ) => (
         #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash,
@@ -126,7 +121,7 @@ macro_rules! define_dep_nodes {
                 match *self {
                     $(
                         DepKind :: $variant => {
-                            if contains_anon_attr!($($attr),*) {
+                            if contains_anon_attr!($($attrs)*) {
                                 return false;
                             }
 
@@ -136,13 +131,6 @@ macro_rules! define_dep_nodes {
                                     ::CAN_RECONSTRUCT_QUERY_KEY;
                             })*
 
-                            // struct args
-                            $({
-
-                                return <( $($struct_arg_ty,)* ) as DepNodeParams>
-                                    ::CAN_RECONSTRUCT_QUERY_KEY;
-                            })*
-
                             true
                         }
                     )*
@@ -152,7 +140,7 @@ macro_rules! define_dep_nodes {
             pub fn is_anon(&self) -> bool {
                 match *self {
                     $(
-                        DepKind :: $variant => { contains_anon_attr!($($attr),*) }
+                        DepKind :: $variant => { contains_anon_attr!($($attrs)*) }
                     )*
                 }
             }
@@ -160,7 +148,7 @@ macro_rules! define_dep_nodes {
             pub fn is_eval_always(&self) -> bool {
                 match *self {
                     $(
-                        DepKind :: $variant => { contains_eval_always_attr!($($attr), *) }
+                        DepKind :: $variant => { contains_eval_always_attr!($($attrs)*) }
                     )*
                 }
             }
@@ -176,12 +164,6 @@ macro_rules! define_dep_nodes {
                                 return true;
                             })*
 
-                            // struct args
-                            $({
-                                $(erase!($struct_arg_name);)*
-                                return true;
-                            })*
-
                             false
                         }
                     )*
@@ -189,11 +171,43 @@ macro_rules! define_dep_nodes {
             }
         }
 
-        pub enum DepConstructor<$tcx> {
+        pub struct DepConstructor;
+
+        impl DepConstructor {
             $(
-                $variant $(( $tuple_arg_ty ))*
-                         $({ $($struct_arg_name : $struct_arg_ty),* })*
-            ),*
+                #[inline(always)]
+                #[allow(unreachable_code, non_snake_case)]
+                pub fn $variant<'tcx>(_tcx: TyCtxt<'tcx>, $(arg: $tuple_arg_ty)*) -> DepNode {
+                    // tuple args
+                    $({
+                        erase!($tuple_arg_ty);
+                        let hash = DepNodeParams::to_fingerprint(&arg, _tcx);
+                        let dep_node = DepNode {
+                            kind: DepKind::$variant,
+                            hash
+                        };
+
+                        #[cfg(debug_assertions)]
+                        {
+                            if !dep_node.kind.can_reconstruct_query_key() &&
+                            (_tcx.sess.opts.debugging_opts.incremental_info ||
+                                _tcx.sess.opts.debugging_opts.query_dep_graph)
+                            {
+                                _tcx.dep_graph.register_dep_node_debug_str(dep_node, || {
+                                    arg.to_debug_str(_tcx)
+                                });
+                            }
+                        }
+
+                        return dep_node;
+                    })*
+
+                    DepNode {
+                        kind: DepKind::$variant,
+                        hash: Fingerprint::ZERO,
+                    }
+                }
+            )*
         }
 
         #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash,
@@ -204,75 +218,6 @@ macro_rules! define_dep_nodes {
         }
 
         impl DepNode {
-            #[allow(unreachable_code, non_snake_case)]
-            pub fn new<'tcx>(tcx: TyCtxt<'tcx>,
-                                       dep: DepConstructor<'tcx>)
-                                       -> DepNode
-            {
-                match dep {
-                    $(
-                        DepConstructor :: $variant $(( replace!(($tuple_arg_ty) with arg) ))*
-                                                   $({ $($struct_arg_name),* })*
-                            =>
-                        {
-                            // tuple args
-                            $({
-                                erase!($tuple_arg_ty);
-                                let hash = DepNodeParams::to_fingerprint(&arg, tcx);
-                                let dep_node = DepNode {
-                                    kind: DepKind::$variant,
-                                    hash
-                                };
-
-                                #[cfg(debug_assertions)]
-                                {
-                                    if !dep_node.kind.can_reconstruct_query_key() &&
-                                    (tcx.sess.opts.debugging_opts.incremental_info ||
-                                        tcx.sess.opts.debugging_opts.query_dep_graph)
-                                    {
-                                        tcx.dep_graph.register_dep_node_debug_str(dep_node, || {
-                                            arg.to_debug_str(tcx)
-                                        });
-                                    }
-                                }
-
-                                return dep_node;
-                            })*
-
-                            // struct args
-                            $({
-                                let tupled_args = ( $($struct_arg_name,)* );
-                                let hash = DepNodeParams::to_fingerprint(&tupled_args,
-                                                                         tcx);
-                                let dep_node = DepNode {
-                                    kind: DepKind::$variant,
-                                    hash
-                                };
-
-                                #[cfg(debug_assertions)]
-                                {
-                                    if !dep_node.kind.can_reconstruct_query_key() &&
-                                    (tcx.sess.opts.debugging_opts.incremental_info ||
-                                        tcx.sess.opts.debugging_opts.query_dep_graph)
-                                    {
-                                        tcx.dep_graph.register_dep_node_debug_str(dep_node, || {
-                                            tupled_args.to_debug_str(tcx)
-                                        });
-                                    }
-                                }
-
-                                return dep_node;
-                            })*
-
-                            DepNode {
-                                kind: DepKind::$variant,
-                                hash: Fingerprint::ZERO,
-                            }
-                        }
-                    )*
-                }
-            }
-
             /// Construct a DepNode from the given DepKind and DefPathHash. This
             /// method will assert that the given DepKind actually requires a
             /// single DefId/DefPathHash parameter.
diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs
index 258723bb39d..531a45b120c 100644
--- a/src/librustc/dep_graph/graph.rs
+++ b/src/librustc/dep_graph/graph.rs
@@ -1122,6 +1122,7 @@ impl CurrentDepGraph {
 }
 
 impl DepGraphData {
+    #[inline(never)]
     fn read_index(&self, source: DepNodeIndex) {
         ty::tls::with_context_opt(|icx| {
             let icx = if let Some(icx) = icx { icx } else { return };
diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs
index 6da7c09c7df..9a3ddfb0e82 100644
--- a/src/librustc/mir/mono.rs
+++ b/src/librustc/mir/mono.rs
@@ -362,7 +362,7 @@ impl<'tcx> CodegenUnit<'tcx> {
     }
 
     pub fn codegen_dep_node(&self, tcx: TyCtxt<'tcx>) -> DepNode {
-        DepNode::new(tcx, DepConstructor::CompileCodegenUnit(self.name()))
+        DepConstructor::CompileCodegenUnit(tcx, self.name())
     }
 }
 
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 5a415fa954f..e59738d8886 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -2,7 +2,7 @@
 
 use crate::arena::Arena;
 use crate::dep_graph::DepGraph;
-use crate::dep_graph::{self, DepConstructor, DepNode};
+use crate::dep_graph::{self, DepConstructor};
 use crate::hir::exports::Export;
 use crate::hir::map as hir_map;
 use crate::hir::map::DefPathHash;
@@ -1347,7 +1347,7 @@ impl<'tcx> TyCtxt<'tcx> {
         // We cannot use the query versions of crates() and crate_hash(), since
         // those would need the DepNodes that we are allocating here.
         for cnum in self.cstore.crates_untracked() {
-            let dep_node = DepNode::new(self, DepConstructor::CrateMetadata(cnum));
+            let dep_node = DepConstructor::CrateMetadata(self, cnum);
             let crate_hash = self.cstore.crate_hash_untracked(cnum);
             self.dep_graph.with_task(
                 dep_node,
@@ -1688,6 +1688,7 @@ pub mod tls {
 
     /// Gets the pointer to the current `ImplicitCtxt`.
     #[cfg(not(parallel_compiler))]
+    #[inline]
     fn get_tlv() -> usize {
         TLV.with(|tlv| tlv.get())
     }
diff --git a/src/librustc/ty/query/caches.rs b/src/librustc/ty/query/caches.rs
new file mode 100644
index 00000000000..efc2804bd4d
--- /dev/null
+++ b/src/librustc/ty/query/caches.rs
@@ -0,0 +1,112 @@
+use crate::dep_graph::DepNodeIndex;
+use crate::ty::query::config::QueryAccessors;
+use crate::ty::query::plumbing::{QueryLookup, QueryState, QueryStateShard};
+use crate::ty::TyCtxt;
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::sharded::Sharded;
+use std::default::Default;
+use std::hash::Hash;
+
+pub(crate) trait CacheSelector<K, V> {
+    type Cache: QueryCache<K, V>;
+}
+
+pub(crate) trait QueryCache<K, V>: Default {
+    type Sharded: Default;
+
+    /// Checks if the query is already computed and in the cache.
+    /// It returns the shard index and a lock guard to the shard,
+    /// which will be used if the query is not in the cache and we need
+    /// to compute it.
+    fn lookup<'tcx, R, GetCache, OnHit, OnMiss, Q>(
+        &self,
+        state: &'tcx QueryState<'tcx, Q>,
+        get_cache: GetCache,
+        key: K,
+        // `on_hit` can be called while holding a lock to the query state shard.
+        on_hit: OnHit,
+        on_miss: OnMiss,
+    ) -> R
+    where
+        Q: QueryAccessors<'tcx>,
+        GetCache: for<'a> Fn(&'a mut QueryStateShard<'tcx, Q>) -> &'a mut Self::Sharded,
+        OnHit: FnOnce(&V, DepNodeIndex) -> R,
+        OnMiss: FnOnce(K, QueryLookup<'tcx, Q>) -> R;
+
+    fn complete(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        lock_sharded_storage: &mut Self::Sharded,
+        key: K,
+        value: V,
+        index: DepNodeIndex,
+    );
+
+    fn iter<R, L>(
+        &self,
+        shards: &Sharded<L>,
+        get_shard: impl Fn(&mut L) -> &mut Self::Sharded,
+        f: impl for<'a> FnOnce(Box<dyn Iterator<Item = (&'a K, &'a V, DepNodeIndex)> + 'a>) -> R,
+    ) -> R;
+}
+
+pub struct DefaultCacheSelector;
+
+impl<K: Eq + Hash, V: Clone> CacheSelector<K, V> for DefaultCacheSelector {
+    type Cache = DefaultCache;
+}
+
+#[derive(Default)]
+pub struct DefaultCache;
+
+impl<K: Eq + Hash, V: Clone> QueryCache<K, V> for DefaultCache {
+    type Sharded = FxHashMap<K, (V, DepNodeIndex)>;
+
+    #[inline(always)]
+    fn lookup<'tcx, R, GetCache, OnHit, OnMiss, Q>(
+        &self,
+        state: &'tcx QueryState<'tcx, Q>,
+        get_cache: GetCache,
+        key: K,
+        on_hit: OnHit,
+        on_miss: OnMiss,
+    ) -> R
+    where
+        Q: QueryAccessors<'tcx>,
+        GetCache: for<'a> Fn(&'a mut QueryStateShard<'tcx, Q>) -> &'a mut Self::Sharded,
+        OnHit: FnOnce(&V, DepNodeIndex) -> R,
+        OnMiss: FnOnce(K, QueryLookup<'tcx, Q>) -> R,
+    {
+        let mut lookup = state.get_lookup(&key);
+        let lock = &mut *lookup.lock;
+
+        let result = get_cache(lock).raw_entry().from_key_hashed_nocheck(lookup.key_hash, &key);
+
+        if let Some((_, value)) = result { on_hit(&value.0, value.1) } else { on_miss(key, lookup) }
+    }
+
+    #[inline]
+    fn complete(
+        &self,
+        _: TyCtxt<'tcx>,
+        lock_sharded_storage: &mut Self::Sharded,
+        key: K,
+        value: V,
+        index: DepNodeIndex,
+    ) {
+        lock_sharded_storage.insert(key, (value, index));
+    }
+
+    fn iter<R, L>(
+        &self,
+        shards: &Sharded<L>,
+        get_shard: impl Fn(&mut L) -> &mut Self::Sharded,
+        f: impl for<'a> FnOnce(Box<dyn Iterator<Item = (&'a K, &'a V, DepNodeIndex)> + 'a>) -> R,
+    ) -> R {
+        let mut shards = shards.lock_shards();
+        let mut shards: Vec<_> = shards.iter_mut().map(|shard| get_shard(shard)).collect();
+        let results = shards.iter_mut().flat_map(|shard| shard.iter()).map(|(k, v)| (k, &v.0, v.1));
+        f(Box::new(results))
+    }
+}
diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs
index dbb6a1080e6..e0e1ca374d9 100644
--- a/src/librustc/ty/query/config.rs
+++ b/src/librustc/ty/query/config.rs
@@ -1,15 +1,15 @@
 use crate::dep_graph::SerializedDepNodeIndex;
 use crate::dep_graph::{DepKind, DepNode};
+use crate::ty::query::caches::QueryCache;
 use crate::ty::query::plumbing::CycleError;
 use crate::ty::query::queries;
-use crate::ty::query::{Query, QueryCache};
+use crate::ty::query::{Query, QueryState};
 use crate::ty::TyCtxt;
 use rustc_data_structures::profiling::ProfileCategory;
 use rustc_hir::def_id::{CrateNum, DefId};
 
 use crate::ich::StableHashingContext;
 use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_data_structures::sharded::Sharded;
 use std::borrow::Cow;
 use std::fmt::Debug;
 use std::hash::Hash;
@@ -30,10 +30,12 @@ pub(crate) trait QueryAccessors<'tcx>: QueryConfig<'tcx> {
     const ANON: bool;
     const EVAL_ALWAYS: bool;
 
+    type Cache: QueryCache<Self::Key, Self::Value>;
+
     fn query(key: Self::Key) -> Query<'tcx>;
 
     // Don't use this method to access query results, instead use the methods on TyCtxt
-    fn query_cache<'a>(tcx: TyCtxt<'tcx>) -> &'a Sharded<QueryCache<'tcx, Self>>;
+    fn query_state<'a>(tcx: TyCtxt<'tcx>) -> &'a QueryState<'tcx, Self>;
 
     fn to_dep_node(tcx: TyCtxt<'tcx>, key: &Self::Key) -> DepNode;
 
@@ -61,7 +63,10 @@ pub(crate) trait QueryDescription<'tcx>: QueryAccessors<'tcx> {
     }
 }
 
-impl<'tcx, M: QueryAccessors<'tcx, Key = DefId>> QueryDescription<'tcx> for M {
+impl<'tcx, M: QueryAccessors<'tcx, Key = DefId>> QueryDescription<'tcx> for M
+where
+    <M as QueryAccessors<'tcx>>::Cache: QueryCache<DefId, <M as QueryConfig<'tcx>>::Value>,
+{
     default fn describe(tcx: TyCtxt<'_>, def_id: DefId) -> Cow<'static, str> {
         if !tcx.sess.verbose() {
             format!("processing `{}`", tcx.def_path_str(def_id)).into()
diff --git a/src/librustc/ty/query/keys.rs b/src/librustc/ty/query/keys.rs
index c1c88e96f94..09fb307a1ce 100644
--- a/src/librustc/ty/query/keys.rs
+++ b/src/librustc/ty/query/keys.rs
@@ -4,6 +4,7 @@ use crate::infer::canonical::Canonical;
 use crate::mir;
 use crate::traits;
 use crate::ty::fast_reject::SimplifiedType;
+use crate::ty::query::caches::DefaultCacheSelector;
 use crate::ty::subst::SubstsRef;
 use crate::ty::{self, Ty, TyCtxt};
 use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE};
@@ -12,7 +13,9 @@ use rustc_span::{Span, DUMMY_SP};
 
 /// The `Key` trait controls what types can legally be used as the key
 /// for a query.
-pub(super) trait Key {
+pub trait Key {
+    type CacheSelector;
+
     /// Given an instance of this key, what crate is it referring to?
     /// This is used to find the provider.
     fn query_crate(&self) -> CrateNum;
@@ -23,6 +26,8 @@ pub(super) trait Key {
 }
 
 impl<'tcx> Key for ty::InstanceDef<'tcx> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -33,6 +38,8 @@ impl<'tcx> Key for ty::InstanceDef<'tcx> {
 }
 
 impl<'tcx> Key for ty::Instance<'tcx> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -43,6 +50,8 @@ impl<'tcx> Key for ty::Instance<'tcx> {
 }
 
 impl<'tcx> Key for mir::interpret::GlobalId<'tcx> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         self.instance.query_crate()
     }
@@ -53,6 +62,8 @@ impl<'tcx> Key for mir::interpret::GlobalId<'tcx> {
 }
 
 impl<'tcx> Key for mir::interpret::LitToConstInput<'tcx> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -63,6 +74,8 @@ impl<'tcx> Key for mir::interpret::LitToConstInput<'tcx> {
 }
 
 impl Key for CrateNum {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         *self
     }
@@ -72,6 +85,8 @@ impl Key for CrateNum {
 }
 
 impl Key for DefIndex {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -81,6 +96,8 @@ impl Key for DefIndex {
 }
 
 impl Key for DefId {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         self.krate
     }
@@ -90,6 +107,8 @@ impl Key for DefId {
 }
 
 impl Key for (DefId, DefId) {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         self.0.krate
     }
@@ -99,6 +118,8 @@ impl Key for (DefId, DefId) {
 }
 
 impl Key for (CrateNum, DefId) {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         self.0
     }
@@ -108,6 +129,8 @@ impl Key for (CrateNum, DefId) {
 }
 
 impl Key for (DefId, SimplifiedType) {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         self.0.krate
     }
@@ -117,6 +140,8 @@ impl Key for (DefId, SimplifiedType) {
 }
 
 impl<'tcx> Key for SubstsRef<'tcx> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -126,6 +151,8 @@ impl<'tcx> Key for SubstsRef<'tcx> {
 }
 
 impl<'tcx> Key for (DefId, SubstsRef<'tcx>) {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         self.0.krate
     }
@@ -135,6 +162,8 @@ impl<'tcx> Key for (DefId, SubstsRef<'tcx>) {
 }
 
 impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         self.1.def_id().krate
     }
@@ -144,6 +173,8 @@ impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) {
 }
 
 impl<'tcx> Key for (&'tcx ty::Const<'tcx>, mir::Field) {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -153,6 +184,8 @@ impl<'tcx> Key for (&'tcx ty::Const<'tcx>, mir::Field) {
 }
 
 impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         self.def_id().krate
     }
@@ -162,6 +195,8 @@ impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
 }
 
 impl<'tcx> Key for &'tcx ty::Const<'tcx> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -171,6 +206,8 @@ impl<'tcx> Key for &'tcx ty::Const<'tcx> {
 }
 
 impl<'tcx> Key for Ty<'tcx> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -180,6 +217,8 @@ impl<'tcx> Key for Ty<'tcx> {
 }
 
 impl<'tcx> Key for ty::ParamEnv<'tcx> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -189,6 +228,8 @@ impl<'tcx> Key for ty::ParamEnv<'tcx> {
 }
 
 impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         self.value.query_crate()
     }
@@ -198,6 +239,8 @@ impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> {
 }
 
 impl<'tcx> Key for traits::Environment<'tcx> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -207,6 +250,8 @@ impl<'tcx> Key for traits::Environment<'tcx> {
 }
 
 impl Key for Symbol {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -218,6 +263,8 @@ impl Key for Symbol {
 /// Canonical query goals correspond to abstract trait operations that
 /// are not tied to any crate in particular.
 impl<'tcx, T> Key for Canonical<'tcx, T> {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
@@ -228,6 +275,8 @@ impl<'tcx, T> Key for Canonical<'tcx, T> {
 }
 
 impl Key for (Symbol, u32, u32) {
+    type CacheSelector = DefaultCacheSelector;
+
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
     }
diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs
index 21698cd7374..381a7b1f03f 100644
--- a/src/librustc/ty/query/mod.rs
+++ b/src/librustc/ty/query/mod.rs
@@ -1,4 +1,4 @@
-use crate::dep_graph::{self, DepNode};
+use crate::dep_graph::{self, DepConstructor, DepNode};
 use crate::hir::exports::Export;
 use crate::infer::canonical::{self, Canonical};
 use crate::lint::LintLevelMap;
@@ -52,7 +52,6 @@ use rustc_target::spec::PanicStrategy;
 use rustc_attr as attr;
 use rustc_span::symbol::Symbol;
 use rustc_span::{Span, DUMMY_SP};
-use std::any::type_name;
 use std::borrow::Cow;
 use std::convert::TryFrom;
 use std::ops::Deref;
@@ -64,6 +63,9 @@ mod plumbing;
 use self::plumbing::*;
 pub use self::plumbing::{force_from_dep_node, CycleError};
 
+mod stats;
+pub use self::stats::print_stats;
+
 mod job;
 #[cfg(parallel_compiler)]
 pub use self::job::handle_deadlock;
@@ -76,6 +78,9 @@ use self::keys::Key;
 mod values;
 use self::values::Value;
 
+mod caches;
+use self::caches::CacheSelector;
+
 mod config;
 use self::config::QueryAccessors;
 pub use self::config::QueryConfig;
diff --git a/src/librustc/ty/query/on_disk_cache.rs b/src/librustc/ty/query/on_disk_cache.rs
index 45d95e97a9c..b92081ff7c0 100644
--- a/src/librustc/ty/query/on_disk_cache.rs
+++ b/src/librustc/ty/query/on_disk_cache.rs
@@ -1035,20 +1035,22 @@ where
         .prof
         .extra_verbose_generic_activity("encode_query_results_for", ::std::any::type_name::<Q>());
 
-    let shards = Q::query_cache(tcx).lock_shards();
-    assert!(shards.iter().all(|shard| shard.active.is_empty()));
-    for (key, entry) in shards.iter().flat_map(|shard| shard.results.iter()) {
-        if Q::cache_on_disk(tcx, key.clone(), Some(&entry.value)) {
-            let dep_node = SerializedDepNodeIndex::new(entry.index.index());
-
-            // Record position of the cache entry.
-            query_result_index.push((dep_node, AbsoluteBytePos::new(encoder.position())));
-
-            // Encode the type check tables with the `SerializedDepNodeIndex`
-            // as tag.
-            encoder.encode_tagged(dep_node, &entry.value)?;
-        }
-    }
+    let state = Q::query_state(tcx);
+    assert!(state.all_inactive());
+
+    state.iter_results(|results| {
+        for (key, value, dep_node) in results {
+            if Q::cache_on_disk(tcx, key.clone(), Some(&value)) {
+                let dep_node = SerializedDepNodeIndex::new(dep_node.index());
+
+                // Record position of the cache entry.
+                query_result_index.push((dep_node, AbsoluteBytePos::new(encoder.position())));
 
-    Ok(())
+                // Encode the type check tables with the `SerializedDepNodeIndex`
+                // as tag.
+                encoder.encode_tagged(dep_node, &value)?;
+            }
+        }
+        Ok(())
+    })
 }
diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs
index 8b787915de6..a61256b9fcb 100644
--- a/src/librustc/ty/query/plumbing.rs
+++ b/src/librustc/ty/query/plumbing.rs
@@ -3,7 +3,8 @@
 //! manage the caches, and so forth.
 
 use crate::dep_graph::{DepKind, DepNode, DepNodeIndex, SerializedDepNodeIndex};
-use crate::ty::query::config::{QueryConfig, QueryDescription};
+use crate::ty::query::caches::QueryCache;
+use crate::ty::query::config::{QueryAccessors, QueryDescription};
 use crate::ty::query::job::{QueryInfo, QueryJob, QueryJobId, QueryShardJobId};
 use crate::ty::query::Query;
 use crate::ty::tls;
@@ -12,10 +13,8 @@ use crate::ty::{self, TyCtxt};
 #[cfg(not(parallel_compiler))]
 use rustc_data_structures::cold_path;
 use rustc_data_structures::fx::{FxHashMap, FxHasher};
-#[cfg(parallel_compiler)]
-use rustc_data_structures::profiling::TimingGuard;
 use rustc_data_structures::sharded::Sharded;
-use rustc_data_structures::sync::Lock;
+use rustc_data_structures::sync::{Lock, LockGuard};
 use rustc_data_structures::thin_vec::ThinVec;
 use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, FatalError, Handler, Level};
 use rustc_span::source_map::DUMMY_SP;
@@ -25,26 +24,50 @@ use std::hash::{Hash, Hasher};
 use std::mem;
 use std::num::NonZeroU32;
 use std::ptr;
+#[cfg(debug_assertions)]
+use std::sync::atomic::{AtomicUsize, Ordering};
 
-pub struct QueryCache<'tcx, D: QueryConfig<'tcx> + ?Sized> {
-    pub(super) results: FxHashMap<D::Key, QueryValue<D::Value>>,
+pub(crate) struct QueryStateShard<'tcx, D: QueryAccessors<'tcx> + ?Sized> {
+    pub(super) cache: <<D as QueryAccessors<'tcx>>::Cache as QueryCache<D::Key, D::Value>>::Sharded,
     pub(super) active: FxHashMap<D::Key, QueryResult<'tcx>>,
 
     /// Used to generate unique ids for active jobs.
     pub(super) jobs: u32,
+}
 
-    #[cfg(debug_assertions)]
-    pub(super) cache_hits: usize,
+impl<'tcx, Q: QueryAccessors<'tcx>> QueryStateShard<'tcx, Q> {
+    fn get_cache(
+        &mut self,
+    ) -> &mut <<Q as QueryAccessors<'tcx>>::Cache as QueryCache<Q::Key, Q::Value>>::Sharded {
+        &mut self.cache
+    }
 }
 
-pub(super) struct QueryValue<T> {
-    pub(super) value: T,
-    pub(super) index: DepNodeIndex,
+impl<'tcx, Q: QueryAccessors<'tcx>> Default for QueryStateShard<'tcx, Q> {
+    fn default() -> QueryStateShard<'tcx, Q> {
+        QueryStateShard { cache: Default::default(), active: Default::default(), jobs: 0 }
+    }
+}
+
+pub(crate) struct QueryState<'tcx, D: QueryAccessors<'tcx> + ?Sized> {
+    pub(super) cache: D::Cache,
+    pub(super) shards: Sharded<QueryStateShard<'tcx, D>>,
+    #[cfg(debug_assertions)]
+    pub(super) cache_hits: AtomicUsize,
 }
 
-impl<T> QueryValue<T> {
-    pub(super) fn new(value: T, dep_node_index: DepNodeIndex) -> QueryValue<T> {
-        QueryValue { value, index: dep_node_index }
+impl<'tcx, Q: QueryAccessors<'tcx>> QueryState<'tcx, Q> {
+    pub(super) fn get_lookup<K: Hash>(&'tcx self, key: &K) -> QueryLookup<'tcx, Q> {
+        // We compute the key's hash once and then use it for both the
+        // shard lookup and the hashmap lookup. This relies on the fact
+        // that both of them use `FxHasher`.
+        let mut hasher = FxHasher::default();
+        key.hash(&mut hasher);
+        let key_hash = hasher.finish();
+
+        let shard = self.shards.get_shard_index_by_hash(key_hash);
+        let lock = self.shards.get_shard_by_index(shard).lock();
+        QueryLookup { key_hash, shard, lock }
     }
 }
 
@@ -58,142 +81,134 @@ pub(super) enum QueryResult<'tcx> {
     Poisoned,
 }
 
-impl<'tcx, M: QueryConfig<'tcx>> Default for QueryCache<'tcx, M> {
-    fn default() -> QueryCache<'tcx, M> {
-        QueryCache {
-            results: FxHashMap::default(),
-            active: FxHashMap::default(),
-            jobs: 0,
+impl<'tcx, M: QueryAccessors<'tcx>> QueryState<'tcx, M> {
+    pub fn iter_results<R>(
+        &self,
+        f: impl for<'a> FnOnce(
+            Box<dyn Iterator<Item = (&'a M::Key, &'a M::Value, DepNodeIndex)> + 'a>,
+        ) -> R,
+    ) -> R {
+        self.cache.iter(&self.shards, |shard| &mut shard.cache, f)
+    }
+    pub fn all_inactive(&self) -> bool {
+        let shards = self.shards.lock_shards();
+        shards.iter().all(|shard| shard.active.is_empty())
+    }
+}
+
+impl<'tcx, M: QueryAccessors<'tcx>> Default for QueryState<'tcx, M> {
+    fn default() -> QueryState<'tcx, M> {
+        QueryState {
+            cache: M::Cache::default(),
+            shards: Default::default(),
             #[cfg(debug_assertions)]
-            cache_hits: 0,
+            cache_hits: AtomicUsize::new(0),
         }
     }
 }
 
+/// Values used when checking a query cache which can be reused on a cache-miss to execute the query.
+pub(crate) struct QueryLookup<'tcx, Q: QueryAccessors<'tcx>> {
+    pub(super) key_hash: u64,
+    pub(super) shard: usize,
+    pub(super) lock: LockGuard<'tcx, QueryStateShard<'tcx, Q>>,
+}
+
 /// A type representing the responsibility to execute the job in the `job` field.
 /// This will poison the relevant query if dropped.
-pub(super) struct JobOwner<'a, 'tcx, Q: QueryDescription<'tcx>> {
-    cache: &'a Sharded<QueryCache<'tcx, Q>>,
+pub(super) struct JobOwner<'tcx, Q: QueryDescription<'tcx>> {
+    tcx: TyCtxt<'tcx>,
     key: Q::Key,
     id: QueryJobId,
 }
 
-impl<'a, 'tcx, Q: QueryDescription<'tcx>> JobOwner<'a, 'tcx, Q> {
+impl<'tcx, Q: QueryDescription<'tcx>> JobOwner<'tcx, Q> {
     /// Either gets a `JobOwner` corresponding the query, allowing us to
     /// start executing the query, or returns with the result of the query.
-    /// If the query is executing elsewhere, this will wait for it.
+    /// This function assumes that `try_get_cached` is already called and returned `lookup`.
+    /// If the query is executing elsewhere, this will wait for it and return the result.
     /// If the query panicked, this will silently panic.
     ///
     /// This function is inlined because that results in a noticeable speed-up
     /// for some compile-time benchmarks.
     #[inline(always)]
-    pub(super) fn try_get(tcx: TyCtxt<'tcx>, span: Span, key: &Q::Key) -> TryGetJob<'a, 'tcx, Q> {
-        // Handling the `query_blocked_prof_timer` is a bit weird because of the
-        // control flow in this function: Blocking is implemented by
-        // awaiting a running job and, once that is done, entering the loop below
-        // again from the top. In that second iteration we will hit the
-        // cache which provides us with the information we need for
-        // finishing the "query-blocked" event.
-        //
-        // We thus allocate `query_blocked_prof_timer` outside the loop,
-        // initialize it during the first iteration and finish it during the
-        // second iteration.
-        #[cfg(parallel_compiler)]
-        let mut query_blocked_prof_timer: Option<TimingGuard<'_>> = None;
-
-        let cache = Q::query_cache(tcx);
-        loop {
-            // We compute the key's hash once and then use it for both the
-            // shard lookup and the hashmap lookup. This relies on the fact
-            // that both of them use `FxHasher`.
-            let mut state = FxHasher::default();
-            key.hash(&mut state);
-            let key_hash = state.finish();
-
-            let shard = cache.get_shard_index_by_hash(key_hash);
-            let mut lock_guard = cache.get_shard_by_index(shard).lock();
-            let lock = &mut *lock_guard;
-
-            if let Some((_, value)) =
-                lock.results.raw_entry().from_key_hashed_nocheck(key_hash, key)
-            {
-                if unlikely!(tcx.prof.enabled()) {
-                    tcx.prof.query_cache_hit(value.index.into());
+    pub(super) fn try_start(
+        tcx: TyCtxt<'tcx>,
+        span: Span,
+        key: &Q::Key,
+        mut lookup: QueryLookup<'tcx, Q>,
+    ) -> TryGetJob<'tcx, Q> {
+        let lock = &mut *lookup.lock;
+
+        let (latch, mut _query_blocked_prof_timer) = match lock.active.entry((*key).clone()) {
+            Entry::Occupied(mut entry) => {
+                match entry.get_mut() {
+                    QueryResult::Started(job) => {
+                        // For parallel queries, we'll block and wait until the query running
+                        // in another thread has completed. Record how long we wait in the
+                        // self-profiler.
+                        let _query_blocked_prof_timer = if cfg!(parallel_compiler) {
+                            Some(tcx.prof.query_blocked())
+                        } else {
+                            None
+                        };
 
-                    #[cfg(parallel_compiler)]
-                    {
-                        if let Some(prof_timer) = query_blocked_prof_timer.take() {
-                            prof_timer.finish_with_query_invocation_id(value.index.into());
-                        }
-                    }
-                }
+                        // Create the id of the job we're waiting for
+                        let id = QueryJobId::new(job.id, lookup.shard, Q::dep_kind());
 
-                let result = (value.value.clone(), value.index);
-                #[cfg(debug_assertions)]
-                {
-                    lock.cache_hits += 1;
+                        (job.latch(id), _query_blocked_prof_timer)
+                    }
+                    QueryResult::Poisoned => FatalError.raise(),
                 }
-                return TryGetJob::JobCompleted(result);
             }
+            Entry::Vacant(entry) => {
+                // No job entry for this query. Return a new one to be started later.
 
-            let latch = match lock.active.entry((*key).clone()) {
-                Entry::Occupied(mut entry) => {
-                    match entry.get_mut() {
-                        QueryResult::Started(job) => {
-                            // For parallel queries, we'll block and wait until the query running
-                            // in another thread has completed. Record how long we wait in the
-                            // self-profiler.
-                            #[cfg(parallel_compiler)]
-                            {
-                                query_blocked_prof_timer = Some(tcx.prof.query_blocked());
-                            }
-
-                            // Create the id of the job we're waiting for
-                            let id = QueryJobId::new(job.id, shard, Q::dep_kind());
-
-                            job.latch(id)
-                        }
-                        QueryResult::Poisoned => FatalError.raise(),
-                    }
-                }
-                Entry::Vacant(entry) => {
-                    // No job entry for this query. Return a new one to be started later.
+                // Generate an id unique within this shard.
+                let id = lock.jobs.checked_add(1).unwrap();
+                lock.jobs = id;
+                let id = QueryShardJobId(NonZeroU32::new(id).unwrap());
+
+                let global_id = QueryJobId::new(id, lookup.shard, Q::dep_kind());
 
-                    // Generate an id unique within this shard.
-                    let id = lock.jobs.checked_add(1).unwrap();
-                    lock.jobs = id;
-                    let id = QueryShardJobId(NonZeroU32::new(id).unwrap());
+                let job = tls::with_related_context(tcx, |icx| QueryJob::new(id, span, icx.query));
 
-                    let global_id = QueryJobId::new(id, shard, Q::dep_kind());
+                entry.insert(QueryResult::Started(job));
 
-                    let job =
-                        tls::with_related_context(tcx, |icx| QueryJob::new(id, span, icx.query));
+                let owner = JobOwner { tcx, id: global_id, key: (*key).clone() };
+                return TryGetJob::NotYetStarted(owner);
+            }
+        };
+        mem::drop(lookup.lock);
 
-                    entry.insert(QueryResult::Started(job));
+        // If we are single-threaded we know that we have cycle error,
+        // so we just return the error.
+        #[cfg(not(parallel_compiler))]
+        return TryGetJob::Cycle(cold_path(|| {
+            Q::handle_cycle_error(tcx, latch.find_cycle_in_stack(tcx, span))
+        }));
 
-                    let owner = JobOwner { cache, id: global_id, key: (*key).clone() };
-                    return TryGetJob::NotYetStarted(owner);
-                }
-            };
-            mem::drop(lock_guard);
+        // With parallel queries we might just have to wait on some other
+        // thread.
+        #[cfg(parallel_compiler)]
+        {
+            let result = latch.wait_on(tcx, span);
 
-            // If we are single-threaded we know that we have cycle error,
-            // so we just return the error.
-            #[cfg(not(parallel_compiler))]
-            return TryGetJob::Cycle(cold_path(|| {
-                Q::handle_cycle_error(tcx, latch.find_cycle_in_stack(tcx, span))
-            }));
+            if let Err(cycle) = result {
+                return TryGetJob::Cycle(Q::handle_cycle_error(tcx, cycle));
+            }
 
-            // With parallel queries we might just have to wait on some other
-            // thread.
-            #[cfg(parallel_compiler)]
-            {
-                let result = latch.wait_on(tcx, span);
+            let cached = tcx.try_get_cached::<Q, _, _, _>(
+                (*key).clone(),
+                |value, index| (value.clone(), index),
+                |_, _| panic!("value must be in cache after waiting"),
+            );
 
-                if let Err(cycle) = result {
-                    return TryGetJob::Cycle(Q::handle_cycle_error(tcx, cycle));
-                }
+            if let Some(prof_timer) = _query_blocked_prof_timer.take() {
+                prof_timer.finish_with_query_invocation_id(cached.1.into());
             }
+
+            return TryGetJob::JobCompleted(cached);
         }
     }
 
@@ -203,19 +218,20 @@ impl<'a, 'tcx, Q: QueryDescription<'tcx>> JobOwner<'a, 'tcx, Q> {
     pub(super) fn complete(self, result: &Q::Value, dep_node_index: DepNodeIndex) {
         // We can move out of `self` here because we `mem::forget` it below
         let key = unsafe { ptr::read(&self.key) };
-        let cache = self.cache;
+        let tcx = self.tcx;
 
         // Forget ourself so our destructor won't poison the query
         mem::forget(self);
 
-        let value = QueryValue::new(result.clone(), dep_node_index);
         let job = {
-            let mut lock = cache.get_shard_by_value(&key).lock();
+            let state = Q::query_state(tcx);
+            let result = result.clone();
+            let mut lock = state.shards.get_shard_by_value(&key).lock();
             let job = match lock.active.remove(&key).unwrap() {
                 QueryResult::Started(job) => job,
                 QueryResult::Poisoned => panic!(),
             };
-            lock.results.insert(key, value);
+            state.cache.complete(tcx, &mut lock.cache, key, result, dep_node_index);
             job
         };
 
@@ -233,12 +249,13 @@ where
     (result, diagnostics.into_inner())
 }
 
-impl<'a, 'tcx, Q: QueryDescription<'tcx>> Drop for JobOwner<'a, 'tcx, Q> {
+impl<'tcx, Q: QueryDescription<'tcx>> Drop for JobOwner<'tcx, Q> {
     #[inline(never)]
     #[cold]
     fn drop(&mut self) {
         // Poison the query so jobs waiting on it panic.
-        let shard = self.cache.get_shard_by_value(&self.key);
+        let state = Q::query_state(self.tcx);
+        let shard = state.shards.get_shard_by_value(&self.key);
         let job = {
             let mut shard = shard.lock();
             let job = match shard.active.remove(&self.key).unwrap() {
@@ -261,14 +278,15 @@ pub struct CycleError<'tcx> {
     pub(super) cycle: Vec<QueryInfo<'tcx>>,
 }
 
-/// The result of `try_get_lock`.
-pub(super) enum TryGetJob<'a, 'tcx, D: QueryDescription<'tcx>> {
+/// The result of `try_start`.
+pub(super) enum TryGetJob<'tcx, D: QueryDescription<'tcx>> {
     /// The query is not yet started. Contains a guard to the cache eventually used to start it.
-    NotYetStarted(JobOwner<'a, 'tcx, D>),
+    NotYetStarted(JobOwner<'tcx, D>),
 
     /// The query was already completed.
     /// Returns the result of the query and its dep-node index
     /// if it succeeded or a cycle error if it failed.
+    #[cfg(parallel_compiler)]
     JobCompleted((D::Value, DepNodeIndex)),
 
     /// Trying to execute the query resulted in a cycle.
@@ -396,13 +414,72 @@ impl<'tcx> TyCtxt<'tcx> {
         eprintln!("end of query stack");
     }
 
+    /// Checks if the query is already computed and in the cache.
+    /// It returns the shard index and a lock guard to the shard,
+    /// which will be used if the query is not in the cache and we need
+    /// to compute it.
+    #[inline(always)]
+    fn try_get_cached<Q, R, OnHit, OnMiss>(
+        self,
+        key: Q::Key,
+        // `on_hit` can be called while holding a lock to the query cache
+        on_hit: OnHit,
+        on_miss: OnMiss,
+    ) -> R
+    where
+        Q: QueryDescription<'tcx> + 'tcx,
+        OnHit: FnOnce(&Q::Value, DepNodeIndex) -> R,
+        OnMiss: FnOnce(Q::Key, QueryLookup<'tcx, Q>) -> R,
+    {
+        let state = Q::query_state(self);
+
+        state.cache.lookup(
+            state,
+            QueryStateShard::<Q>::get_cache,
+            key,
+            |value, index| {
+                if unlikely!(self.prof.enabled()) {
+                    self.prof.query_cache_hit(index.into());
+                }
+                #[cfg(debug_assertions)]
+                {
+                    state.cache_hits.fetch_add(1, Ordering::Relaxed);
+                }
+                on_hit(value, index)
+            },
+            on_miss,
+        )
+    }
+
     #[inline(never)]
-    pub(super) fn get_query<Q: QueryDescription<'tcx>>(self, span: Span, key: Q::Key) -> Q::Value {
+    pub(super) fn get_query<Q: QueryDescription<'tcx> + 'tcx>(
+        self,
+        span: Span,
+        key: Q::Key,
+    ) -> Q::Value {
         debug!("ty::query::get_query<{}>(key={:?}, span={:?})", Q::NAME, key, span);
 
-        let job = match JobOwner::try_get(self, span, &key) {
+        self.try_get_cached::<Q, _, _, _>(
+            key,
+            |value, index| {
+                self.dep_graph.read_index(index);
+                value.clone()
+            },
+            |key, lookup| self.try_execute_query::<Q>(span, key, lookup),
+        )
+    }
+
+    #[inline(always)]
+    pub(super) fn try_execute_query<Q: QueryDescription<'tcx>>(
+        self,
+        span: Span,
+        key: Q::Key,
+        lookup: QueryLookup<'tcx, Q>,
+    ) -> Q::Value {
+        let job = match JobOwner::try_start(self, span, &key, lookup) {
             TryGetJob::NotYetStarted(job) => job,
             TryGetJob::Cycle(result) => return result,
+            #[cfg(parallel_compiler)]
             TryGetJob::JobCompleted((v, index)) => {
                 self.dep_graph.read_index(index);
                 return v;
@@ -560,7 +637,7 @@ impl<'tcx> TyCtxt<'tcx> {
     fn force_query_with_job<Q: QueryDescription<'tcx>>(
         self,
         key: Q::Key,
-        job: JobOwner<'_, 'tcx, Q>,
+        job: JobOwner<'tcx, Q>,
         dep_node: DepNode,
     ) -> (Q::Value, DepNodeIndex) {
         // If the following assertion triggers, it can have two reasons:
@@ -615,7 +692,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// side-effects -- e.g., in order to report errors for erroneous programs.
     ///
     /// Note: The optimization is only available during incr. comp.
-    pub(super) fn ensure_query<Q: QueryDescription<'tcx>>(self, key: Q::Key) -> () {
+    pub(super) fn ensure_query<Q: QueryDescription<'tcx> + 'tcx>(self, key: Q::Key) -> () {
         if Q::EVAL_ALWAYS {
             let _ = self.get_query::<Q>(DUMMY_SP, key);
             return;
@@ -643,14 +720,30 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     #[allow(dead_code)]
-    fn force_query<Q: QueryDescription<'tcx>>(self, key: Q::Key, span: Span, dep_node: DepNode) {
+    fn force_query<Q: QueryDescription<'tcx> + 'tcx>(
+        self,
+        key: Q::Key,
+        span: Span,
+        dep_node: DepNode,
+    ) {
         // We may be concurrently trying both execute and force a query.
         // Ensure that only one of them runs the query.
-        let job = match JobOwner::try_get(self, span, &key) {
-            TryGetJob::NotYetStarted(job) => job,
-            TryGetJob::Cycle(_) | TryGetJob::JobCompleted(_) => return,
-        };
-        self.force_query_with_job::<Q>(key, job, dep_node);
+
+        self.try_get_cached::<Q, _, _, _>(
+            key,
+            |_, _| {
+                // Cache hit, do nothing
+            },
+            |key, lookup| {
+                let job = match JobOwner::try_start(self, span, &key, lookup) {
+                    TryGetJob::NotYetStarted(job) => job,
+                    TryGetJob::Cycle(_) => return,
+                    #[cfg(parallel_compiler)]
+                    TryGetJob::JobCompleted(_) => return,
+                };
+                self.force_query_with_job::<Q>(key, job, dep_node);
+            },
+        );
     }
 }
 
@@ -659,17 +752,17 @@ macro_rules! handle_cycle_error {
         $tcx.report_cycle($error).emit();
         Value::from_cycle_error($tcx)
     }};
-    ([fatal_cycle$(, $modifiers:ident)*][$tcx:expr, $error:expr]) => {{
+    ([fatal_cycle $($rest:tt)*][$tcx:expr, $error:expr]) => {{
         $tcx.report_cycle($error).emit();
         $tcx.sess.abort_if_errors();
         unreachable!()
     }};
-    ([cycle_delay_bug$(, $modifiers:ident)*][$tcx:expr, $error:expr]) => {{
+    ([cycle_delay_bug $($rest:tt)*][$tcx:expr, $error:expr]) => {{
         $tcx.report_cycle($error).delay_as_bug();
         Value::from_cycle_error($tcx)
     }};
-    ([$other:ident$(, $modifiers:ident)*][$($args:tt)*]) => {
-        handle_cycle_error!([$($modifiers),*][$($args)*])
+    ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*][$($args:tt)*]) => {
+        handle_cycle_error!([$($($modifiers)*)*][$($args)*])
     };
 }
 
@@ -677,11 +770,11 @@ macro_rules! is_anon {
     ([]) => {{
         false
     }};
-    ([anon$(, $modifiers:ident)*]) => {{
+    ([anon $($rest:tt)*]) => {{
         true
     }};
-    ([$other:ident$(, $modifiers:ident)*]) => {
-        is_anon!([$($modifiers),*])
+    ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*]) => {
+        is_anon!([$($($modifiers)*)*])
     };
 }
 
@@ -689,11 +782,23 @@ macro_rules! is_eval_always {
     ([]) => {{
         false
     }};
-    ([eval_always$(, $modifiers:ident)*]) => {{
+    ([eval_always $($rest:tt)*]) => {{
         true
     }};
-    ([$other:ident$(, $modifiers:ident)*]) => {
-        is_eval_always!([$($modifiers),*])
+    ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*]) => {
+        is_eval_always!([$($($modifiers)*)*])
+    };
+}
+
+macro_rules! query_storage {
+    ([][$K:ty, $V:ty]) => {
+        <<$K as Key>::CacheSelector as CacheSelector<$K, $V>>::Cache
+    };
+    ([storage($ty:ty) $($rest:tt)*][$K:ty, $V:ty]) => {
+        $ty
+    };
+    ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*][$($args:tt)*]) => {
+        query_storage!([$($($modifiers)*)*][$($args)*])
     };
 }
 
@@ -701,11 +806,11 @@ macro_rules! hash_result {
     ([][$hcx:expr, $result:expr]) => {{
         dep_graph::hash_result($hcx, &$result)
     }};
-    ([no_hash$(, $modifiers:ident)*][$hcx:expr, $result:expr]) => {{
+    ([no_hash $($rest:tt)*][$hcx:expr, $result:expr]) => {{
         None
     }};
-    ([$other:ident$(, $modifiers:ident)*][$($args:tt)*]) => {
-        hash_result!([$($modifiers),*][$($args)*])
+    ([$other:ident $(($($other_args:tt)*))* $(, $($modifiers:tt)*)*][$($args:tt)*]) => {
+        hash_result!([$($($modifiers)*)*][$($args)*])
     };
 }
 
@@ -725,7 +830,6 @@ macro_rules! define_queries_inner {
         [$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)*) => {
 
         use std::mem;
-        use rustc_data_structures::sharded::Sharded;
         use crate::{
             rustc_data_structures::stable_hasher::HashStable,
             rustc_data_structures::stable_hasher::StableHasher,
@@ -760,11 +864,11 @@ macro_rules! define_queries_inner {
                 $(
                     // We use try_lock_shards here since we are called from the
                     // deadlock handler, and this shouldn't be locked.
-                    let shards = self.$name.try_lock_shards()?;
+                    let shards = self.$name.shards.try_lock_shards()?;
                     let shards = shards.iter().enumerate();
                     jobs.extend(shards.flat_map(|(shard_id, shard)| {
                         shard.active.iter().filter_map(move |(k, v)| {
-                            if let QueryResult::Started(ref job) = *v {
+                        if let QueryResult::Started(ref job) = *v {
                                 let id = QueryJobId {
                                     job: job.id,
                                     shard:  u16::try_from(shard_id).unwrap(),
@@ -776,111 +880,15 @@ macro_rules! define_queries_inner {
                                     query: queries::$name::query(k.clone())
                                 };
                                 Some((id, QueryJobInfo { info,  job: job.clone() }))
-                            } else {
-                                None
-                            }
+                        } else {
+                            None
+                        }
                         })
                     }));
                 )*
 
                 Some(jobs)
             }
-
-            pub fn print_stats(&self) {
-                let mut queries = Vec::new();
-
-                #[derive(Clone)]
-                struct QueryStats {
-                    name: &'static str,
-                    cache_hits: usize,
-                    key_size: usize,
-                    key_type: &'static str,
-                    value_size: usize,
-                    value_type: &'static str,
-                    entry_count: usize,
-                }
-
-                fn stats<'tcx, Q: QueryConfig<'tcx>>(
-                    name: &'static str,
-                    map: &Sharded<QueryCache<'tcx, Q>>,
-                ) -> QueryStats {
-                    let map = map.lock_shards();
-                    QueryStats {
-                        name,
-                        #[cfg(debug_assertions)]
-                        cache_hits: map.iter().map(|shard| shard.cache_hits).sum(),
-                        #[cfg(not(debug_assertions))]
-                        cache_hits: 0,
-                        key_size: mem::size_of::<Q::Key>(),
-                        key_type: type_name::<Q::Key>(),
-                        value_size: mem::size_of::<Q::Value>(),
-                        value_type: type_name::<Q::Value>(),
-                        entry_count: map.iter().map(|shard| shard.results.len()).sum(),
-                    }
-                }
-
-                $(
-                    queries.push(stats::<queries::$name<'_>>(
-                        stringify!($name),
-                        &self.$name,
-                    ));
-                )*
-
-                if cfg!(debug_assertions) {
-                    let hits: usize = queries.iter().map(|s| s.cache_hits).sum();
-                    let results: usize = queries.iter().map(|s| s.entry_count).sum();
-                    println!("\nQuery cache hit rate: {}", hits as f64 / (hits + results) as f64);
-                }
-
-                let mut query_key_sizes = queries.clone();
-                query_key_sizes.sort_by_key(|q| q.key_size);
-                println!("\nLarge query keys:");
-                for q in query_key_sizes.iter().rev()
-                                        .filter(|q| q.key_size > 8) {
-                    println!(
-                        "   {} - {} x {} - {}",
-                        q.name,
-                        q.key_size,
-                        q.entry_count,
-                        q.key_type
-                    );
-                }
-
-                let mut query_value_sizes = queries.clone();
-                query_value_sizes.sort_by_key(|q| q.value_size);
-                println!("\nLarge query values:");
-                for q in query_value_sizes.iter().rev()
-                                          .filter(|q| q.value_size > 8) {
-                    println!(
-                        "   {} - {} x {} - {}",
-                        q.name,
-                        q.value_size,
-                        q.entry_count,
-                        q.value_type
-                    );
-                }
-
-                if cfg!(debug_assertions) {
-                    let mut query_cache_hits = queries.clone();
-                    query_cache_hits.sort_by_key(|q| q.cache_hits);
-                    println!("\nQuery cache hits:");
-                    for q in query_cache_hits.iter().rev() {
-                        println!(
-                            "   {} - {} ({}%)",
-                            q.name,
-                            q.cache_hits,
-                            q.cache_hits as f64 / (q.cache_hits + q.entry_count) as f64
-                        );
-                    }
-                }
-
-                let mut query_value_count = queries.clone();
-                query_value_count.sort_by_key(|q| q.entry_count);
-                println!("\nQuery value count:");
-                for q in query_value_count.iter().rev() {
-                    println!("   {} - {}", q.name, q.entry_count);
-                }
-            }
         }
 
         #[allow(nonstandard_style)]
@@ -956,7 +964,6 @@ macro_rules! define_queries_inner {
         $(impl<$tcx> QueryConfig<$tcx> for queries::$name<$tcx> {
             type Key = $K;
             type Value = $V;
-
             const NAME: &'static str = stringify!($name);
             const CATEGORY: ProfileCategory = $category;
         }
@@ -965,22 +972,22 @@ macro_rules! define_queries_inner {
             const ANON: bool = is_anon!([$($modifiers)*]);
             const EVAL_ALWAYS: bool = is_eval_always!([$($modifiers)*]);
 
+            type Cache = query_storage!([$($modifiers)*][$K, $V]);
+
             #[inline(always)]
             fn query(key: Self::Key) -> Query<'tcx> {
                 Query::$name(key)
             }
 
             #[inline(always)]
-            fn query_cache<'a>(tcx: TyCtxt<$tcx>) -> &'a Sharded<QueryCache<$tcx, Self>> {
+            fn query_state<'a>(tcx: TyCtxt<$tcx>) -> &'a QueryState<$tcx, Self> {
                 &tcx.queries.$name
             }
 
             #[allow(unused)]
             #[inline(always)]
             fn to_dep_node(tcx: TyCtxt<$tcx>, key: &Self::Key) -> DepNode {
-                use crate::dep_graph::DepConstructor::*;
-
-                DepNode::new(tcx, $node(*key))
+                DepConstructor::$node(tcx, *key)
             }
 
             #[inline(always)]
@@ -1132,7 +1139,7 @@ macro_rules! define_queries_struct {
             providers: IndexVec<CrateNum, Providers<$tcx>>,
             fallback_extern_providers: Box<Providers<$tcx>>,
 
-            $($(#[$attr])*  $name: Sharded<QueryCache<$tcx, queries::$name<$tcx>>>,)*
+            $($(#[$attr])*  $name: QueryState<$tcx, queries::$name<$tcx>>,)*
         }
     };
 }
diff --git a/src/librustc/ty/query/profiling_support.rs b/src/librustc/ty/query/profiling_support.rs
index 79b32ba83ae..99ada34d59e 100644
--- a/src/librustc/ty/query/profiling_support.rs
+++ b/src/librustc/ty/query/profiling_support.rs
@@ -1,11 +1,10 @@
 use crate::hir::map::definitions::DefPathData;
 use crate::ty::context::TyCtxt;
-use crate::ty::query::config::QueryConfig;
-use crate::ty::query::plumbing::QueryCache;
+use crate::ty::query::config::QueryAccessors;
+use crate::ty::query::plumbing::QueryState;
 use measureme::{StringComponent, StringId};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::profiling::SelfProfiler;
-use rustc_data_structures::sharded::Sharded;
 use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE};
 use std::fmt::Debug;
 use std::io::Write;
@@ -161,10 +160,10 @@ where
 pub(super) fn alloc_self_profile_query_strings_for_query_cache<'tcx, Q>(
     tcx: TyCtxt<'tcx>,
     query_name: &'static str,
-    query_cache: &Sharded<QueryCache<'tcx, Q>>,
+    query_state: &QueryState<'tcx, Q>,
     string_cache: &mut QueryKeyStringCache,
 ) where
-    Q: QueryConfig<'tcx>,
+    Q: QueryAccessors<'tcx>,
 {
     tcx.prof.with_profiler(|profiler| {
         let event_id_builder = profiler.event_id_builder();
@@ -181,20 +180,8 @@ pub(super) fn alloc_self_profile_query_strings_for_query_cache<'tcx, Q>(
             // need to invoke queries itself, we cannot keep the query caches
             // locked while doing so. Instead we copy out the
             // `(query_key, dep_node_index)` pairs and release the lock again.
-            let query_keys_and_indices = {
-                let shards = query_cache.lock_shards();
-                let len = shards.iter().map(|shard| shard.results.len()).sum();
-
-                let mut query_keys_and_indices = Vec::with_capacity(len);
-
-                for shard in &shards {
-                    query_keys_and_indices.extend(
-                        shard.results.iter().map(|(q_key, q_val)| (q_key.clone(), q_val.index)),
-                    );
-                }
-
-                query_keys_and_indices
-            };
+            let query_keys_and_indices: Vec<_> = query_state
+                .iter_results(|results| results.map(|(k, _, i)| (k.clone(), i)).collect());
 
             // Now actually allocate the strings. If allocating the strings
             // generates new entries in the query cache, we'll miss them but
@@ -218,18 +205,14 @@ pub(super) fn alloc_self_profile_query_strings_for_query_cache<'tcx, Q>(
             let query_name = profiler.get_or_alloc_cached_string(query_name);
             let event_id = event_id_builder.from_label(query_name).to_string_id();
 
-            let shards = query_cache.lock_shards();
+            query_state.iter_results(|results| {
+                let query_invocation_ids: Vec<_> = results.map(|v| v.2.into()).collect();
 
-            for shard in shards.iter() {
-                let query_invocation_ids = shard
-                    .results
-                    .values()
-                    .map(|v| v.index)
-                    .map(|dep_node_index| dep_node_index.into());
-
-                profiler
-                    .bulk_map_query_invocation_id_to_single_string(query_invocation_ids, event_id);
-            }
+                profiler.bulk_map_query_invocation_id_to_single_string(
+                    query_invocation_ids.into_iter(),
+                    event_id,
+                );
+            });
         }
     });
 }
diff --git a/src/librustc/ty/query/stats.rs b/src/librustc/ty/query/stats.rs
new file mode 100644
index 00000000000..d257320d4ea
--- /dev/null
+++ b/src/librustc/ty/query/stats.rs
@@ -0,0 +1,139 @@
+use crate::ty::query::config::QueryAccessors;
+use crate::ty::query::plumbing::QueryState;
+use crate::ty::query::queries;
+use crate::ty::TyCtxt;
+use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+
+use std::any::type_name;
+use std::mem;
+#[cfg(debug_assertions)]
+use std::sync::atomic::Ordering;
+
+trait KeyStats {
+    fn key_stats(&self, stats: &mut QueryStats);
+}
+
+impl<T> KeyStats for T {
+    default fn key_stats(&self, _: &mut QueryStats) {}
+}
+
+impl KeyStats for DefId {
+    fn key_stats(&self, stats: &mut QueryStats) {
+        if self.krate == LOCAL_CRATE {
+            stats.local_def_id_keys = Some(stats.local_def_id_keys.unwrap_or(0) + 1);
+        }
+    }
+}
+
+#[derive(Clone)]
+struct QueryStats {
+    name: &'static str,
+    cache_hits: usize,
+    key_size: usize,
+    key_type: &'static str,
+    value_size: usize,
+    value_type: &'static str,
+    entry_count: usize,
+    local_def_id_keys: Option<usize>,
+}
+
+fn stats<'tcx, Q: QueryAccessors<'tcx>>(
+    name: &'static str,
+    map: &QueryState<'tcx, Q>,
+) -> QueryStats {
+    let mut stats = QueryStats {
+        name,
+        #[cfg(debug_assertions)]
+        cache_hits: map.cache_hits.load(Ordering::Relaxed),
+        #[cfg(not(debug_assertions))]
+        cache_hits: 0,
+        key_size: mem::size_of::<Q::Key>(),
+        key_type: type_name::<Q::Key>(),
+        value_size: mem::size_of::<Q::Value>(),
+        value_type: type_name::<Q::Value>(),
+        entry_count: map.iter_results(|results| results.count()),
+        local_def_id_keys: None,
+    };
+    map.iter_results(|results| {
+        for (key, _, _) in results {
+            key.key_stats(&mut stats)
+        }
+    });
+    stats
+}
+
+pub fn print_stats(tcx: TyCtxt<'_>) {
+    let queries = query_stats(tcx);
+
+    if cfg!(debug_assertions) {
+        let hits: usize = queries.iter().map(|s| s.cache_hits).sum();
+        let results: usize = queries.iter().map(|s| s.entry_count).sum();
+        println!("\nQuery cache hit rate: {}", hits as f64 / (hits + results) as f64);
+    }
+
+    let mut query_key_sizes = queries.clone();
+    query_key_sizes.sort_by_key(|q| q.key_size);
+    println!("\nLarge query keys:");
+    for q in query_key_sizes.iter().rev().filter(|q| q.key_size > 8) {
+        println!("   {} - {} x {} - {}", q.name, q.key_size, q.entry_count, q.key_type);
+    }
+
+    let mut query_value_sizes = queries.clone();
+    query_value_sizes.sort_by_key(|q| q.value_size);
+    println!("\nLarge query values:");
+    for q in query_value_sizes.iter().rev().filter(|q| q.value_size > 8) {
+        println!("   {} - {} x {} - {}", q.name, q.value_size, q.entry_count, q.value_type);
+    }
+
+    if cfg!(debug_assertions) {
+        let mut query_cache_hits = queries.clone();
+        query_cache_hits.sort_by_key(|q| q.cache_hits);
+        println!("\nQuery cache hits:");
+        for q in query_cache_hits.iter().rev() {
+            println!(
+                "   {} - {} ({}%)",
+                q.name,
+                q.cache_hits,
+                q.cache_hits as f64 / (q.cache_hits + q.entry_count) as f64
+            );
+        }
+    }
+
+    let mut query_value_count = queries.clone();
+    query_value_count.sort_by_key(|q| q.entry_count);
+    println!("\nQuery value count:");
+    for q in query_value_count.iter().rev() {
+        println!("   {} - {}", q.name, q.entry_count);
+    }
+
+    let mut def_id_density: Vec<_> =
+        queries.iter().filter(|q| q.local_def_id_keys.is_some()).collect();
+    def_id_density.sort_by_key(|q| q.local_def_id_keys.unwrap());
+    println!("\nLocal DefId density:");
+    let total = tcx.hir().definitions().def_index_count() as f64;
+    for q in def_id_density.iter().rev() {
+        let local = q.local_def_id_keys.unwrap();
+        println!("   {} - {} = ({}%)", q.name, local, (local as f64 * 100.0) / total);
+    }
+}
+
+macro_rules! print_stats {
+    (<$tcx:tt> $($category:tt {
+        $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)*
+    },)*) => {
+        fn query_stats(tcx: TyCtxt<'_>) -> Vec<QueryStats> {
+            let mut queries = Vec::new();
+
+            $($(
+                queries.push(stats::<queries::$name<'_>>(
+                    stringify!($name),
+                    &tcx.queries.$name,
+                ));
+            )*)*
+
+            queries
+        }
+    }
+}
+
+rustc_query_append! { [print_stats!][<'tcx>] }
diff --git a/src/librustc_data_structures/profiling.rs b/src/librustc_data_structures/profiling.rs
index 1d0ac4f4907..f2c80510f22 100644
--- a/src/librustc_data_structures/profiling.rs
+++ b/src/librustc_data_structures/profiling.rs
@@ -81,6 +81,7 @@
 //!
 //! [mm]: https://github.com/rust-lang/measureme/
 
+use crate::cold_path;
 use crate::fx::FxHashMap;
 
 use std::borrow::Borrow;
@@ -531,9 +532,11 @@ impl<'a> TimingGuard<'a> {
     #[inline]
     pub fn finish_with_query_invocation_id(self, query_invocation_id: QueryInvocationId) {
         if let Some(guard) = self.0 {
-            let event_id = StringId::new_virtual(query_invocation_id.0);
-            let event_id = EventId::from_virtual(event_id);
-            guard.finish_with_override_event_id(event_id);
+            cold_path(|| {
+                let event_id = StringId::new_virtual(query_invocation_id.0);
+                let event_id = EventId::from_virtual(event_id);
+                guard.finish_with_override_event_id(event_id);
+            });
         }
     }
 
diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs
index 0a26bf3bdc9..a98e77cebd8 100644
--- a/src/librustc_data_structures/stable_hasher.rs
+++ b/src/librustc_data_structures/stable_hasher.rs
@@ -27,6 +27,7 @@ pub trait StableHasherResult: Sized {
 }
 
 impl StableHasher {
+    #[inline]
     pub fn new() -> Self {
         StableHasher { state: SipHasher128::new_with_keys(0, 0) }
     }
diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs
index 0b4a337051f..96a2ac08f2c 100644
--- a/src/librustc_interface/passes.rs
+++ b/src/librustc_interface/passes.rs
@@ -696,8 +696,8 @@ impl<'tcx> QueryContext<'tcx> {
         ty::tls::enter_global(self.0, |tcx| f(tcx))
     }
 
-    pub fn print_stats(&self) {
-        self.0.queries.print_stats()
+    pub fn print_stats(&mut self) {
+        self.enter(|tcx| ty::query::print_stats(tcx))
     }
 }
 
diff --git a/src/librustc_interface/queries.rs b/src/librustc_interface/queries.rs
index 720d162ac81..0c77ab57500 100644
--- a/src/librustc_interface/queries.rs
+++ b/src/librustc_interface/queries.rs
@@ -340,7 +340,7 @@ impl Compiler {
 
         if self.session().opts.debugging_opts.query_stats {
             if let Ok(gcx) = queries.global_ctxt() {
-                gcx.peek().print_stats();
+                gcx.peek_mut().print_stats();
             }
         }
 
diff --git a/src/librustc_macros/src/query.rs b/src/librustc_macros/src/query.rs
index 294cdb7643f..6362f3c2c49 100644
--- a/src/librustc_macros/src/query.rs
+++ b/src/librustc_macros/src/query.rs
@@ -33,6 +33,9 @@ enum QueryModifier {
     /// The description of the query.
     Desc(Option<Ident>, Punctuated<Expr, Token![,]>),
 
+    /// Use this type for the in-memory cache.
+    Storage(Type),
+
     /// Cache the query to disk if the `Expr` returns true.
     Cache(Option<(IdentOrWild, IdentOrWild)>, Block),
 
@@ -106,6 +109,9 @@ impl Parse for QueryModifier {
             let id = args.parse()?;
             let block = input.parse()?;
             Ok(QueryModifier::LoadCached(tcx, id, block))
+        } else if modifier == "storage" {
+            let ty = input.parse()?;
+            Ok(QueryModifier::Storage(ty))
         } else if modifier == "fatal_cycle" {
             Ok(QueryModifier::FatalCycle)
         } else if modifier == "cycle_delay_bug" {
@@ -198,6 +204,9 @@ struct QueryModifiers {
     /// The description of the query.
     desc: Option<(Option<Ident>, Punctuated<Expr, Token![,]>)>,
 
+    /// Use this type for the in-memory cache.
+    storage: Option<Type>,
+
     /// Cache the query to disk if the `Block` returns true.
     cache: Option<(Option<(IdentOrWild, IdentOrWild)>, Block)>,
 
@@ -226,6 +235,7 @@ struct QueryModifiers {
 /// Process query modifiers into a struct, erroring on duplicates
 fn process_modifiers(query: &mut Query) -> QueryModifiers {
     let mut load_cached = None;
+    let mut storage = None;
     let mut cache = None;
     let mut desc = None;
     let mut fatal_cycle = false;
@@ -242,6 +252,12 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers {
                 }
                 load_cached = Some((tcx, id, block));
             }
+            QueryModifier::Storage(ty) => {
+                if storage.is_some() {
+                    panic!("duplicate modifier `storage` for query `{}`", query.name);
+                }
+                storage = Some(ty);
+            }
             QueryModifier::Cache(args, expr) => {
                 if cache.is_some() {
                     panic!("duplicate modifier `cache` for query `{}`", query.name);
@@ -294,6 +310,7 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers {
     }
     QueryModifiers {
         load_cached,
+        storage,
         cache,
         desc,
         fatal_cycle,
@@ -451,6 +468,10 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
             if modifiers.fatal_cycle {
                 attributes.push(quote! { fatal_cycle });
             };
+            // Pass on the storage modifier
+            if let Some(ref ty) = modifiers.storage {
+                attributes.push(quote! { storage(#ty) });
+            };
             // Pass on the cycle_delay_bug modifier
             if modifiers.cycle_delay_bug {
                 attributes.push(quote! { cycle_delay_bug });
diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs
index 4d5464f774f..1cec5d30e9b 100644
--- a/src/librustc_mir/const_eval/eval_queries.rs
+++ b/src/librustc_mir/const_eval/eval_queries.rs
@@ -72,8 +72,8 @@ fn eval_body_using_ecx<'mir, 'tcx>(
     Ok(ret)
 }
 
-/// The `InterpCx` is only meant to be used to do field and index projections into constants for
-/// `simd_shuffle` and const patterns in match arms.
+/// The `InterpCx` is only meant to be used to do field and index projections into promoteds
+/// and const patterns in match arms.
 ///
 /// The function containing the `match` that is currently being analyzed may have generic bounds
 /// that inform us about the generic bounds of the constant. E.g., using an associated constant
diff --git a/src/librustc_mir/dataflow/generic/mod.rs b/src/librustc_mir/dataflow/generic/mod.rs
index ea643042c5f..23b22550a3b 100644
--- a/src/librustc_mir/dataflow/generic/mod.rs
+++ b/src/librustc_mir/dataflow/generic/mod.rs
@@ -395,5 +395,16 @@ impl<T: Idx> GenKill<T> for BitSet<T> {
     }
 }
 
+// For compatibility with old framework
+impl<T: Idx> GenKill<T> for crate::dataflow::GenKillSet<T> {
+    fn gen(&mut self, elem: T) {
+        self.gen(elem);
+    }
+
+    fn kill(&mut self, elem: T) {
+        self.kill(elem);
+    }
+}
+
 #[cfg(test)]
 mod tests;
diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/src/librustc_mir/dataflow/impls/borrowed_locals.rs
index 63834d0ecda..95a676c0892 100644
--- a/src/librustc_mir/dataflow/impls/borrowed_locals.rs
+++ b/src/librustc_mir/dataflow/impls/borrowed_locals.rs
@@ -1,102 +1,274 @@
 pub use super::*;
 
-use crate::dataflow::{BitDenotation, GenKillSet};
+use crate::dataflow::generic::{AnalysisDomain, GenKill, GenKillAnalysis};
 use rustc::mir::visit::Visitor;
 use rustc::mir::*;
+use rustc::ty::{ParamEnv, TyCtxt};
+use rustc_span::DUMMY_SP;
 
-/// This calculates if any part of a MIR local could have previously been borrowed.
-/// This means that once a local has been borrowed, its bit will be set
-/// from that point and onwards, until we see a StorageDead statement for the local,
-/// at which points there is no memory associated with the local, so it cannot be borrowed.
-/// This is used to compute which locals are live during a yield expression for
-/// immovable generators.
-#[derive(Copy, Clone)]
-pub struct HaveBeenBorrowedLocals<'a, 'tcx> {
-    body: &'a Body<'tcx>,
+pub type MaybeMutBorrowedLocals<'mir, 'tcx> = MaybeBorrowedLocals<MutBorrow<'mir, 'tcx>>;
+
+/// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
+/// to a given local.
+///
+/// The `K` parameter determines what kind of borrows are tracked. By default,
+/// `MaybeBorrowedLocals` looks for *any* borrow of a local. If you are only interested in borrows
+/// that might allow mutation, use the `MaybeMutBorrowedLocals` type alias instead.
+///
+/// At present, this is used as a very limited form of alias analysis. For example,
+/// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for
+/// immovable generators. `MaybeMutBorrowedLocals` is used during const checking to prove that a
+/// local has not been mutated via indirect assignment (e.g., `*p = 42`), the side-effects of a
+/// function call or inline assembly.
+pub struct MaybeBorrowedLocals<K = AnyBorrow> {
+    kind: K,
+    ignore_borrow_on_drop: bool,
+}
+
+impl MaybeBorrowedLocals {
+    /// A dataflow analysis that records whether a pointer or reference exists that may alias the
+    /// given local.
+    pub fn all_borrows() -> Self {
+        MaybeBorrowedLocals { kind: AnyBorrow, ignore_borrow_on_drop: false }
+    }
+}
+
+impl MaybeMutBorrowedLocals<'mir, 'tcx> {
+    /// A dataflow analysis that records whether a pointer or reference exists that may *mutably*
+    /// alias the given local.
+    ///
+    /// This includes `&mut` and pointers derived from an `&mut`, as well as shared borrows of
+    /// types with interior mutability.
+    pub fn mut_borrows_only(
+        tcx: TyCtxt<'tcx>,
+        body: &'mir mir::Body<'tcx>,
+        param_env: ParamEnv<'tcx>,
+    ) -> Self {
+        MaybeBorrowedLocals {
+            kind: MutBorrow { body, tcx, param_env },
+            ignore_borrow_on_drop: false,
+        }
+    }
 }
 
-impl<'a, 'tcx> HaveBeenBorrowedLocals<'a, 'tcx> {
-    pub fn new(body: &'a Body<'tcx>) -> Self {
-        HaveBeenBorrowedLocals { body }
+impl<K> MaybeBorrowedLocals<K> {
+    /// During dataflow analysis, ignore the borrow that may occur when a place is dropped.
+    ///
+    /// Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut self` as a
+    /// parameter. In the general case, a drop impl could launder that reference into the
+    /// surrounding environment through a raw pointer, thus creating a valid `*mut` pointing to the
+    /// dropped local. We are not yet willing to declare this particular case UB, so we must treat
+    /// all dropped locals as mutably borrowed for now. See discussion on [#61069].
+    ///
+    /// In some contexts, we know that this borrow will never occur. For example, during
+    /// const-eval, custom drop glue cannot be run. Code that calls this should document the
+    /// assumptions that justify ignoring `Drop` terminators in this way.
+    ///
+    /// [#61069]: https://github.com/rust-lang/rust/pull/61069
+    pub fn unsound_ignore_borrow_on_drop(self) -> Self {
+        MaybeBorrowedLocals { ignore_borrow_on_drop: true, ..self }
     }
 
-    pub fn body(&self) -> &Body<'tcx> {
-        self.body
+    fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T, K> {
+        TransferFunction {
+            kind: &self.kind,
+            trans,
+            ignore_borrow_on_drop: self.ignore_borrow_on_drop,
+        }
     }
 }
 
-impl<'a, 'tcx> BitDenotation<'tcx> for HaveBeenBorrowedLocals<'a, 'tcx> {
+impl<K> AnalysisDomain<'tcx> for MaybeBorrowedLocals<K>
+where
+    K: BorrowAnalysisKind<'tcx>,
+{
     type Idx = Local;
-    fn name() -> &'static str {
-        "has_been_borrowed_locals"
+
+    const NAME: &'static str = K::ANALYSIS_NAME;
+
+    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
+        body.local_decls().len()
+    }
+
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
+        // No locals are aliased on function entry
+    }
+}
+
+impl<K> GenKillAnalysis<'tcx> for MaybeBorrowedLocals<K>
+where
+    K: BorrowAnalysisKind<'tcx>,
+{
+    fn statement_effect(
+        &self,
+        trans: &mut impl GenKill<Self::Idx>,
+        statement: &mir::Statement<'tcx>,
+        location: Location,
+    ) {
+        self.transfer_function(trans).visit_statement(statement, location);
     }
-    fn bits_per_block(&self) -> usize {
-        self.body.local_decls.len()
+
+    fn terminator_effect(
+        &self,
+        trans: &mut impl GenKill<Self::Idx>,
+        terminator: &mir::Terminator<'tcx>,
+        location: Location,
+    ) {
+        self.transfer_function(trans).visit_terminator(terminator, location);
     }
 
-    fn start_block_effect(&self, _on_entry: &mut BitSet<Local>) {
-        // Nothing is borrowed on function entry
+    fn call_return_effect(
+        &self,
+        _trans: &mut impl GenKill<Self::Idx>,
+        _block: mir::BasicBlock,
+        _func: &mir::Operand<'tcx>,
+        _args: &[mir::Operand<'tcx>],
+        _dest_place: &mir::Place<'tcx>,
+    ) {
     }
+}
+
+impl<K> BottomValue for MaybeBorrowedLocals<K> {
+    // bottom = unborrowed
+    const BOTTOM_VALUE: bool = false;
+}
 
-    fn statement_effect(&self, trans: &mut GenKillSet<Local>, loc: Location) {
-        let stmt = &self.body[loc.block].statements[loc.statement_index];
+/// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
+struct TransferFunction<'a, T, K> {
+    trans: &'a mut T,
+    kind: &'a K,
+    ignore_borrow_on_drop: bool,
+}
 
-        BorrowedLocalsVisitor { trans }.visit_statement(stmt, loc);
+impl<T, K> Visitor<'tcx> for TransferFunction<'a, T, K>
+where
+    T: GenKill<Local>,
+    K: BorrowAnalysisKind<'tcx>,
+{
+    fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) {
+        self.super_statement(stmt, location);
 
-        // StorageDead invalidates all borrows and raw pointers to a local
-        match stmt.kind {
-            StatementKind::StorageDead(l) => trans.kill(l),
-            _ => (),
+        // When we reach a `StorageDead` statement, we can assume that any pointers to this memory
+        // are now invalid.
+        if let StatementKind::StorageDead(local) = stmt.kind {
+            self.trans.kill(local);
         }
     }
 
-    fn terminator_effect(&self, trans: &mut GenKillSet<Local>, loc: Location) {
-        let terminator = self.body[loc.block].terminator();
-        BorrowedLocalsVisitor { trans }.visit_terminator(terminator, loc);
-        match &terminator.kind {
-            // Drop terminators borrows the location
-            TerminatorKind::Drop { location, .. }
-            | TerminatorKind::DropAndReplace { location, .. } => {
-                if let Some(local) = find_local(location) {
-                    trans.gen(local);
+    fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
+        self.super_rvalue(rvalue, location);
+
+        match rvalue {
+            mir::Rvalue::AddressOf(mt, borrowed_place) => {
+                if !borrowed_place.is_indirect() && self.kind.in_address_of(*mt, borrowed_place) {
+                    self.trans.gen(borrowed_place.local);
                 }
             }
-            _ => (),
+
+            mir::Rvalue::Ref(_, kind, borrowed_place) => {
+                if !borrowed_place.is_indirect() && self.kind.in_ref(*kind, borrowed_place) {
+                    self.trans.gen(borrowed_place.local);
+                }
+            }
+
+            mir::Rvalue::Cast(..)
+            | mir::Rvalue::Use(..)
+            | mir::Rvalue::Repeat(..)
+            | mir::Rvalue::Len(..)
+            | mir::Rvalue::BinaryOp(..)
+            | mir::Rvalue::CheckedBinaryOp(..)
+            | mir::Rvalue::NullaryOp(..)
+            | mir::Rvalue::UnaryOp(..)
+            | mir::Rvalue::Discriminant(..)
+            | mir::Rvalue::Aggregate(..) => {}
         }
     }
 
-    fn propagate_call_return(
-        &self,
-        _in_out: &mut BitSet<Local>,
-        _call_bb: mir::BasicBlock,
-        _dest_bb: mir::BasicBlock,
-        _dest_place: &mir::Place<'tcx>,
-    ) {
-        // Nothing to do when a call returns successfully
+    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
+        self.super_terminator(terminator, location);
+
+        match terminator.kind {
+            mir::TerminatorKind::Drop { location: dropped_place, .. }
+            | mir::TerminatorKind::DropAndReplace { location: dropped_place, .. } => {
+                // See documentation for `unsound_ignore_borrow_on_drop` for an explanation.
+                if !self.ignore_borrow_on_drop {
+                    self.trans.gen(dropped_place.local);
+                }
+            }
+
+            TerminatorKind::Abort
+            | TerminatorKind::Assert { .. }
+            | TerminatorKind::Call { .. }
+            | TerminatorKind::FalseEdges { .. }
+            | TerminatorKind::FalseUnwind { .. }
+            | TerminatorKind::GeneratorDrop
+            | TerminatorKind::Goto { .. }
+            | TerminatorKind::Resume
+            | TerminatorKind::Return
+            | TerminatorKind::SwitchInt { .. }
+            | TerminatorKind::Unreachable
+            | TerminatorKind::Yield { .. } => {}
+        }
     }
 }
 
-impl<'a, 'tcx> BottomValue for HaveBeenBorrowedLocals<'a, 'tcx> {
-    // bottom = unborrowed
-    const BOTTOM_VALUE: bool = false;
+pub struct AnyBorrow;
+
+pub struct MutBorrow<'mir, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    body: &'mir Body<'tcx>,
+    param_env: ParamEnv<'tcx>,
+}
+
+impl MutBorrow<'mir, 'tcx> {
+    /// `&` and `&raw` only allow mutation if the borrowed place is `!Freeze`.
+    ///
+    /// This assumes that it is UB to take the address of a struct field whose type is
+    /// `Freeze`, then use pointer arithmetic to derive a pointer to a *different* field of
+    /// that same struct whose type is `!Freeze`. If we decide that this is not UB, we will
+    /// have to check the type of the borrowed **local** instead of the borrowed **place**
+    /// below. See [rust-lang/unsafe-code-guidelines#134].
+    ///
+    /// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134
+    fn shared_borrow_allows_mutation(&self, place: &Place<'tcx>) -> bool {
+        !place.ty(self.body, self.tcx).ty.is_freeze(self.tcx, self.param_env, DUMMY_SP)
+    }
 }
 
-struct BorrowedLocalsVisitor<'gk> {
-    trans: &'gk mut GenKillSet<Local>,
+pub trait BorrowAnalysisKind<'tcx> {
+    const ANALYSIS_NAME: &'static str;
+
+    fn in_address_of(&self, mt: Mutability, place: &Place<'tcx>) -> bool;
+    fn in_ref(&self, kind: mir::BorrowKind, place: &Place<'tcx>) -> bool;
 }
 
-fn find_local(place: &Place<'_>) -> Option<Local> {
-    if !place.is_indirect() { Some(place.local) } else { None }
+impl BorrowAnalysisKind<'tcx> for AnyBorrow {
+    const ANALYSIS_NAME: &'static str = "maybe_borrowed_locals";
+
+    fn in_ref(&self, _: mir::BorrowKind, _: &Place<'_>) -> bool {
+        true
+    }
+    fn in_address_of(&self, _: Mutability, _: &Place<'_>) -> bool {
+        true
+    }
 }
 
-impl<'tcx> Visitor<'tcx> for BorrowedLocalsVisitor<'_> {
-    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
-        if let Rvalue::Ref(_, _, ref place) = *rvalue {
-            if let Some(local) = find_local(place) {
-                self.trans.gen(local);
+impl BorrowAnalysisKind<'tcx> for MutBorrow<'mir, 'tcx> {
+    const ANALYSIS_NAME: &'static str = "maybe_mut_borrowed_locals";
+
+    fn in_ref(&self, kind: mir::BorrowKind, place: &Place<'tcx>) -> bool {
+        match kind {
+            mir::BorrowKind::Mut { .. } => true,
+            mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => {
+                self.shared_borrow_allows_mutation(place)
             }
         }
+    }
 
-        self.super_rvalue(rvalue, location)
+    fn in_address_of(&self, mt: Mutability, place: &Place<'tcx>) -> bool {
+        match mt {
+            Mutability::Mut => true,
+            Mutability::Not => self.shared_borrow_allows_mutation(place),
+        }
     }
 }
diff --git a/src/librustc_mir/dataflow/impls/indirect_mutation.rs b/src/librustc_mir/dataflow/impls/indirect_mutation.rs
deleted file mode 100644
index 85bf342c8a3..00000000000
--- a/src/librustc_mir/dataflow/impls/indirect_mutation.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-use rustc::mir::visit::Visitor;
-use rustc::mir::{self, Local, Location};
-use rustc::ty::{self, TyCtxt};
-use rustc_index::bit_set::BitSet;
-use rustc_span::DUMMY_SP;
-
-use crate::dataflow::{self, GenKillSet};
-
-/// Whether a borrow to a `Local` has been created that could allow that `Local` to be mutated
-/// indirectly. This could either be a mutable reference (`&mut`) or a shared borrow if the type of
-/// that `Local` allows interior mutability. Operations that can mutate local's indirectly include:
-/// assignments through a pointer (`*p = 42`), function calls, drop terminators and inline assembly.
-///
-/// If this returns false for a `Local` at a given statement (or terminator), that `Local` could
-/// not possibly have been mutated indirectly prior to that statement.
-#[derive(Copy, Clone)]
-pub struct IndirectlyMutableLocals<'mir, 'tcx> {
-    body: &'mir mir::Body<'tcx>,
-    tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-}
-
-impl<'mir, 'tcx> IndirectlyMutableLocals<'mir, 'tcx> {
-    pub fn new(
-        tcx: TyCtxt<'tcx>,
-        body: &'mir mir::Body<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-    ) -> Self {
-        IndirectlyMutableLocals { body, tcx, param_env }
-    }
-
-    fn transfer_function<'a>(
-        &self,
-        trans: &'a mut GenKillSet<Local>,
-    ) -> TransferFunction<'a, 'mir, 'tcx> {
-        TransferFunction { body: self.body, tcx: self.tcx, param_env: self.param_env, trans }
-    }
-}
-
-impl<'mir, 'tcx> dataflow::BitDenotation<'tcx> for IndirectlyMutableLocals<'mir, 'tcx> {
-    type Idx = Local;
-
-    fn name() -> &'static str {
-        "mut_borrowed_locals"
-    }
-
-    fn bits_per_block(&self) -> usize {
-        self.body.local_decls.len()
-    }
-
-    fn start_block_effect(&self, _entry_set: &mut BitSet<Local>) {
-        // Nothing is borrowed on function entry
-    }
-
-    fn statement_effect(&self, trans: &mut GenKillSet<Local>, loc: Location) {
-        let stmt = &self.body[loc.block].statements[loc.statement_index];
-        self.transfer_function(trans).visit_statement(stmt, loc);
-    }
-
-    fn terminator_effect(&self, trans: &mut GenKillSet<Local>, loc: Location) {
-        let terminator = self.body[loc.block].terminator();
-        self.transfer_function(trans).visit_terminator(terminator, loc);
-    }
-
-    fn propagate_call_return(
-        &self,
-        _in_out: &mut BitSet<Local>,
-        _call_bb: mir::BasicBlock,
-        _dest_bb: mir::BasicBlock,
-        _dest_place: &mir::Place<'tcx>,
-    ) {
-        // Nothing to do when a call returns successfully
-    }
-}
-
-impl<'mir, 'tcx> dataflow::BottomValue for IndirectlyMutableLocals<'mir, 'tcx> {
-    // bottom = unborrowed
-    const BOTTOM_VALUE: bool = false;
-}
-
-/// A `Visitor` that defines the transfer function for `IndirectlyMutableLocals`.
-struct TransferFunction<'a, 'mir, 'tcx> {
-    trans: &'a mut GenKillSet<Local>,
-    body: &'mir mir::Body<'tcx>,
-    tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-}
-
-impl<'tcx> TransferFunction<'_, '_, 'tcx> {
-    /// Returns `true` if this borrow would allow mutation of the `borrowed_place`.
-    fn borrow_allows_mutation(
-        &self,
-        kind: mir::BorrowKind,
-        borrowed_place: &mir::Place<'tcx>,
-    ) -> bool {
-        match kind {
-            mir::BorrowKind::Mut { .. } => true,
-
-            mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => {
-                !borrowed_place.ty(self.body, self.tcx).ty.is_freeze(
-                    self.tcx,
-                    self.param_env,
-                    DUMMY_SP,
-                )
-            }
-        }
-    }
-}
-
-impl<'tcx> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx> {
-    fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
-        if let mir::Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
-            if self.borrow_allows_mutation(kind, borrowed_place) {
-                if !borrowed_place.is_indirect() {
-                    self.trans.gen(borrowed_place.local);
-                }
-            }
-        }
-
-        self.super_rvalue(rvalue, location);
-    }
-
-    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
-        // This method purposely does nothing except call `super_terminator`. It exists solely to
-        // document the subtleties around drop terminators.
-
-        self.super_terminator(terminator, location);
-
-        if let mir::TerminatorKind::Drop { location: _, .. }
-        | mir::TerminatorKind::DropAndReplace { location: _, .. } = &terminator.kind
-        {
-            // Although drop terminators mutably borrow the location being dropped, that borrow
-            // cannot live beyond the drop terminator because the dropped location is invalidated.
-        }
-    }
-}
diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs
index 5b2264c2a65..acea3185781 100644
--- a/src/librustc_mir/dataflow/impls/mod.rs
+++ b/src/librustc_mir/dataflow/impls/mod.rs
@@ -20,11 +20,9 @@ use super::drop_flag_effects_for_location;
 use super::on_lookup_result_bits;
 
 mod borrowed_locals;
-mod indirect_mutation;
 mod storage_liveness;
 
 pub use self::borrowed_locals::*;
-pub use self::indirect_mutation::IndirectlyMutableLocals;
 pub use self::storage_liveness::*;
 
 pub(super) mod borrows;
diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/src/librustc_mir/dataflow/impls/storage_liveness.rs
index 040c13e8210..7508d71945e 100644
--- a/src/librustc_mir/dataflow/impls/storage_liveness.rs
+++ b/src/librustc_mir/dataflow/impls/storage_liveness.rs
@@ -1,8 +1,8 @@
 pub use super::*;
 
+use crate::dataflow::generic::{Results, ResultsRefCursor};
 use crate::dataflow::BitDenotation;
-use crate::dataflow::HaveBeenBorrowedLocals;
-use crate::dataflow::{DataflowResults, DataflowResultsCursor, DataflowResultsRefCursor};
+use crate::dataflow::MaybeBorrowedLocals;
 use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
 use rustc::mir::*;
 use std::cell::RefCell;
@@ -69,22 +69,23 @@ impl<'a, 'tcx> BottomValue for MaybeStorageLive<'a, 'tcx> {
     const BOTTOM_VALUE: bool = false;
 }
 
+type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
+
 /// Dataflow analysis that determines whether each local requires storage at a
 /// given location; i.e. whether its storage can go away without being observed.
 pub struct RequiresStorage<'mir, 'tcx> {
     body: ReadOnlyBodyAndCache<'mir, 'tcx>,
-    borrowed_locals:
-        RefCell<DataflowResultsRefCursor<'mir, 'tcx, HaveBeenBorrowedLocals<'mir, 'tcx>>>,
+    borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
 }
 
 impl<'mir, 'tcx: 'mir> RequiresStorage<'mir, 'tcx> {
     pub fn new(
         body: ReadOnlyBodyAndCache<'mir, 'tcx>,
-        borrowed_locals: &'mir DataflowResults<'tcx, HaveBeenBorrowedLocals<'mir, 'tcx>>,
+        borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
     ) -> Self {
         RequiresStorage {
             body,
-            borrowed_locals: RefCell::new(DataflowResultsCursor::new(borrowed_locals, *body)),
+            borrowed_locals: RefCell::new(ResultsRefCursor::new(*body, borrowed_locals)),
         }
     }
 
@@ -111,11 +112,12 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
     }
 
     fn before_statement_effect(&self, sets: &mut GenKillSet<Self::Idx>, loc: Location) {
-        // If we borrow or assign to a place then it needs storage for that
-        // statement.
-        self.check_for_borrow(sets, loc);
-
         let stmt = &self.body[loc.block].statements[loc.statement_index];
+
+        // If a place is borrowed in a statement, it needs storage for that statement.
+        self.borrowed_locals.borrow().analysis().statement_effect(sets, stmt, loc);
+
+        // If a place is assigned to in a statement, it needs storage for that statement.
         match stmt.kind {
             StatementKind::StorageDead(l) => sets.kill(l),
             StatementKind::Assign(box (ref place, _))
@@ -138,12 +140,13 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
     }
 
     fn before_terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
-        self.check_for_borrow(sets, loc);
+        let terminator = self.body[loc.block].terminator();
 
-        if let TerminatorKind::Call { destination: Some((Place { local, .. }, _)), .. } =
-            self.body[loc.block].terminator().kind
-        {
-            sets.gen(local);
+        // If a place is borrowed in a terminator, it needs storage for that terminator.
+        self.borrowed_locals.borrow().analysis().terminator_effect(sets, terminator, loc);
+
+        if let TerminatorKind::Call { destination: Some((place, _)), .. } = terminator.kind {
+            sets.gen(place.local);
         }
     }
 
@@ -179,14 +182,6 @@ impl<'mir, 'tcx> RequiresStorage<'mir, 'tcx> {
         let mut visitor = MoveVisitor { sets, borrowed_locals: &self.borrowed_locals };
         visitor.visit_location(self.body, loc);
     }
-
-    /// Gen locals that are newly borrowed. This includes borrowing any part of
-    /// a local (we rely on this behavior of `HaveBeenBorrowedLocals`).
-    fn check_for_borrow(&self, sets: &mut GenKillSet<Local>, loc: Location) {
-        let mut borrowed_locals = self.borrowed_locals.borrow_mut();
-        borrowed_locals.seek(loc);
-        borrowed_locals.each_gen_bit(|l| sets.gen(l));
-    }
 }
 
 impl<'mir, 'tcx> BottomValue for RequiresStorage<'mir, 'tcx> {
@@ -195,8 +190,7 @@ impl<'mir, 'tcx> BottomValue for RequiresStorage<'mir, 'tcx> {
 }
 
 struct MoveVisitor<'a, 'mir, 'tcx> {
-    borrowed_locals:
-        &'a RefCell<DataflowResultsRefCursor<'mir, 'tcx, HaveBeenBorrowedLocals<'mir, 'tcx>>>,
+    borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
     sets: &'a mut GenKillSet<Local>,
 }
 
@@ -204,7 +198,7 @@ impl<'a, 'mir: 'a, 'tcx> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx> {
     fn visit_local(&mut self, local: &Local, context: PlaceContext, loc: Location) {
         if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
             let mut borrowed_locals = self.borrowed_locals.borrow_mut();
-            borrowed_locals.seek(loc);
+            borrowed_locals.seek_before(loc);
             if !borrowed_locals.contains(*local) {
                 self.sets.kill(*local);
             }
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index 7cd7fc309b6..41bac894e48 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -23,8 +23,7 @@ pub(crate) use self::drop_flag_effects::*;
 pub use self::impls::borrows::Borrows;
 pub use self::impls::DefinitelyInitializedPlaces;
 pub use self::impls::EverInitializedPlaces;
-pub use self::impls::HaveBeenBorrowedLocals;
-pub use self::impls::IndirectlyMutableLocals;
+pub use self::impls::{MaybeBorrowedLocals, MaybeMutBorrowedLocals};
 pub use self::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
 pub use self::impls::{MaybeStorageLive, RequiresStorage};
 
diff --git a/src/librustc_mir/transform/check_consts/resolver.rs b/src/librustc_mir/transform/check_consts/resolver.rs
index b804dc4b5b6..3e14cc6d32a 100644
--- a/src/librustc_mir/transform/check_consts/resolver.rs
+++ b/src/librustc_mir/transform/check_consts/resolver.rs
@@ -15,7 +15,7 @@ use crate::dataflow::{self as old_dataflow, generic as dataflow};
 /// `FlowSensitiveAnalysis`.
 ///
 /// This transfer does nothing when encountering an indirect assignment. Consumers should rely on
-/// the `IndirectlyMutableLocals` dataflow pass to see if a `Local` may have become qualified via
+/// the `MaybeMutBorrowedLocals` dataflow pass to see if a `Local` may have become qualified via
 /// an indirect assignment or function call.
 struct TransferFunction<'a, 'mir, 'tcx, Q> {
     item: &'a Item<'mir, 'tcx>,
diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs
index 1a1c34e1c67..1553f826c7e 100644
--- a/src/librustc_mir/transform/check_consts/validation.rs
+++ b/src/librustc_mir/transform/check_consts/validation.rs
@@ -16,17 +16,19 @@ use rustc_span::Span;
 use std::borrow::Cow;
 use std::ops::Deref;
 
-use self::old_dataflow::IndirectlyMutableLocals;
 use super::ops::{self, NonConstOp};
 use super::qualifs::{self, HasMutInterior, NeedsDrop};
 use super::resolver::FlowSensitiveAnalysis;
 use super::{is_lang_panic_fn, ConstKind, Item, Qualif};
 use crate::const_eval::{is_const_fn, is_unstable_const_fn};
-use crate::dataflow::{self as old_dataflow, generic as dataflow};
-use dataflow::Analysis;
+use crate::dataflow::generic::{self as dataflow, Analysis};
+use crate::dataflow::MaybeMutBorrowedLocals;
 
+// We are using `MaybeMutBorrowedLocals` as a proxy for whether an item may have been mutated
+// through a pointer prior to the given point. This is okay even though `MaybeMutBorrowedLocals`
+// kills locals upon `StorageDead` because a local will never be used after a `StorageDead`.
 pub type IndirectlyMutableResults<'mir, 'tcx> =
-    old_dataflow::DataflowResultsCursor<'mir, 'tcx, IndirectlyMutableLocals<'mir, 'tcx>>;
+    dataflow::ResultsCursor<'mir, 'tcx, MaybeMutBorrowedLocals<'mir, 'tcx>>;
 
 struct QualifCursor<'a, 'mir, 'tcx, Q: Qualif> {
     cursor: dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>>,
@@ -59,7 +61,7 @@ pub struct Qualifs<'a, 'mir, 'tcx> {
 
 impl Qualifs<'a, 'mir, 'tcx> {
     fn indirectly_mutable(&mut self, local: Local, location: Location) -> bool {
-        self.indirectly_mutable.seek(location);
+        self.indirectly_mutable.seek_before(location);
         self.indirectly_mutable.get().contains(local)
     }
 
@@ -135,22 +137,21 @@ impl Deref for Validator<'_, 'mir, 'tcx> {
 
 impl Validator<'a, 'mir, 'tcx> {
     pub fn new(item: &'a Item<'mir, 'tcx>) -> Self {
+        let Item { tcx, body, def_id, param_env, .. } = *item;
+
         let needs_drop = QualifCursor::new(NeedsDrop, item);
         let has_mut_interior = QualifCursor::new(HasMutInterior, item);
 
-        let dead_unwinds = BitSet::new_empty(item.body.basic_blocks().len());
-        let indirectly_mutable = old_dataflow::do_dataflow(
-            item.tcx,
-            &*item.body,
-            item.def_id,
-            &item.tcx.get_attrs(item.def_id),
-            &dead_unwinds,
-            old_dataflow::IndirectlyMutableLocals::new(item.tcx, *item.body, item.param_env),
-            |_, local| old_dataflow::DebugFormatted::new(&local),
-        );
-
-        let indirectly_mutable =
-            old_dataflow::DataflowResultsCursor::new(indirectly_mutable, *item.body);
+        // We can use `unsound_ignore_borrow_on_drop` here because custom drop impls are not
+        // allowed in a const.
+        //
+        // FIXME(ecstaticmorse): Someday we want to allow custom drop impls. How do we do this
+        // without breaking stable code?
+        let indirectly_mutable = MaybeMutBorrowedLocals::mut_borrows_only(tcx, *body, param_env)
+            .unsound_ignore_borrow_on_drop()
+            .into_engine(tcx, *body, def_id)
+            .iterate_to_fixpoint()
+            .into_results_cursor(*body);
 
         let qualifs = Qualifs { needs_drop, has_mut_interior, indirectly_mutable };
 
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index d5d9a8c83cd..b3dc87d1a16 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -49,9 +49,10 @@
 //! For generators with state 1 (returned) and state 2 (poisoned) it does nothing.
 //! Otherwise it drops all the values in scope at the last suspension point.
 
+use crate::dataflow::generic::{Analysis, ResultsCursor};
 use crate::dataflow::{do_dataflow, DataflowResultsCursor, DebugFormatted};
 use crate::dataflow::{DataflowResults, DataflowResultsConsumer, FlowAtLocation};
-use crate::dataflow::{HaveBeenBorrowedLocals, MaybeStorageLive, RequiresStorage};
+use crate::dataflow::{MaybeBorrowedLocals, MaybeStorageLive, RequiresStorage};
 use crate::transform::no_landing_pads::no_landing_pads;
 use crate::transform::simplify;
 use crate::transform::{MirPass, MirSource};
@@ -471,17 +472,10 @@ fn locals_live_across_suspend_points(
 
     // Calculate the MIR locals which have been previously
     // borrowed (even if they are still active).
-    let borrowed_locals_analysis = HaveBeenBorrowedLocals::new(body_ref);
-    let borrowed_locals_results = do_dataflow(
-        tcx,
-        body_ref,
-        def_id,
-        &[],
-        &dead_unwinds,
-        borrowed_locals_analysis,
-        |bd, p| DebugFormatted::new(&bd.body().local_decls[p]),
-    );
-    let mut borrowed_locals_cursor = DataflowResultsCursor::new(&borrowed_locals_results, body_ref);
+    let borrowed_locals_results =
+        MaybeBorrowedLocals::all_borrows().into_engine(tcx, body_ref, def_id).iterate_to_fixpoint();
+
+    let mut borrowed_locals_cursor = ResultsCursor::new(body_ref, &borrowed_locals_results);
 
     // Calculate the MIR locals that we actually need to keep storage around
     // for.
@@ -521,7 +515,7 @@ fn locals_live_across_suspend_points(
                 // If a borrow is converted to a raw reference, we must also assume that it lives
                 // forever. Note that the final liveness is still bounded by the storage liveness
                 // of the local, which happens using the `intersect` operation below.
-                borrowed_locals_cursor.seek(loc);
+                borrowed_locals_cursor.seek_before(loc);
                 liveness.outs[block].union(borrowed_locals_cursor.get());
             }
 
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
index a5d59860c3d..a11ee57f465 100644
--- a/src/librustc_mir/transform/promote_consts.rs
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -24,7 +24,6 @@ use rustc_span::{Span, DUMMY_SP};
 use syntax::ast::LitKind;
 
 use rustc_index::vec::{Idx, IndexVec};
-use rustc_target::spec::abi::Abi;
 
 use std::cell::Cell;
 use std::{cmp, iter, mem, usize};
@@ -106,11 +105,10 @@ pub enum Candidate {
     /// Promotion of the `x` in `[x; 32]`.
     Repeat(Location),
 
-    /// Currently applied to function calls where the callee has the unstable
-    /// `#[rustc_args_required_const]` attribute as well as the SIMD shuffle
-    /// intrinsic. The intrinsic requires the arguments are indeed constant and
-    /// the attribute currently provides the semantic requirement that arguments
-    /// must be constant.
+    /// Function calls where the callee has the unstable
+    /// `#[rustc_args_required_const]` attribute. The attribute requires that
+    /// the arguments be constant, usually because they are encoded as an
+    /// immediate operand in a platform intrinsic.
     Argument { bb: BasicBlock, index: usize },
 }
 
@@ -218,17 +216,6 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
 
         if let TerminatorKind::Call { ref func, .. } = *kind {
             if let ty::FnDef(def_id, _) = func.ty(self.body, self.tcx).kind {
-                let fn_sig = self.tcx.fn_sig(def_id);
-                if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() {
-                    let name = self.tcx.item_name(def_id);
-                    // FIXME(eddyb) use `#[rustc_args_required_const(2)]` for shuffles.
-                    if name.as_str().starts_with("simd_shuffle") {
-                        self.candidates.push(Candidate::Argument { bb: location.block, index: 2 });
-
-                        return; // Don't double count `simd_shuffle` candidates
-                    }
-                }
-
                 if let Some(constant_args) = args_required_const(self.tcx, def_id) {
                     for index in constant_args {
                         self.candidates.push(Candidate::Argument { bb: location.block, index });
@@ -730,8 +717,7 @@ pub fn validate_candidates(
         .filter(|&candidate| {
             validator.explicit = candidate.forces_explicit_promotion();
 
-            // FIXME(eddyb) also emit the errors for shuffle indices
-            // and `#[rustc_args_required_const]` arguments here.
+            // FIXME(eddyb) also emit the errors for `#[rustc_args_required_const]` arguments here.
 
             let is_promotable = validator.validate_candidate(candidate).is_ok();
             match candidate {
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index 7d8506eb281..6176cf8bc0f 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -12,9 +12,8 @@ use rustc_index::bit_set::BitSet;
 use crate::dataflow::generic::{Analysis, Results, ResultsCursor};
 use crate::dataflow::move_paths::{HasMoveData, MoveData};
 use crate::dataflow::move_paths::{LookupResult, MovePathIndex};
-use crate::dataflow::IndirectlyMutableLocals;
+use crate::dataflow::MaybeMutBorrowedLocals;
 use crate::dataflow::MoveDataParamEnv;
-use crate::dataflow::{do_dataflow, DebugFormatted};
 use crate::dataflow::{
     DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
 };
@@ -24,7 +23,6 @@ pub struct SanityCheck;
 impl<'tcx> MirPass<'tcx> for SanityCheck {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
         use crate::dataflow::has_rustc_mir_with;
-
         let def_id = src.def_id();
         if !tcx.has_attr(def_id, sym::rustc_mir) {
             debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
@@ -37,7 +35,6 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
         let param_env = tcx.param_env(def_id);
         let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
         let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
-        let dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
 
         let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
             .into_engine(tcx, body, def_id)
@@ -48,15 +45,9 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
         let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe)
             .into_engine(tcx, body, def_id)
             .iterate_to_fixpoint();
-        let _flow_indirectly_mut = do_dataflow(
-            tcx,
-            body,
-            def_id,
-            &attributes,
-            &dead_unwinds,
-            IndirectlyMutableLocals::new(tcx, body, param_env),
-            |_, i| DebugFormatted::new(&i),
-        );
+        let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env)
+            .into_engine(tcx, body, def_id)
+            .iterate_to_fixpoint();
 
         if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() {
             sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits);
@@ -67,12 +58,9 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
         if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
             sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits);
         }
-        // FIXME: Uncomment these as analyses are migrated to the new framework
-        /*
         if has_rustc_mir_with(&attributes, sym::rustc_peek_indirectly_mutable).is_some() {
-            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_indirectly_mut);
+            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_mut_borrowed);
         }
-        */
         if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
             tcx.sess.fatal("stop_after_dataflow ended compilation");
         }
@@ -276,8 +264,7 @@ where
     }
 }
 
-/* FIXME: Add this back once `IndirectlyMutableLocals` uses the new dataflow framework.
-impl<'tcx> RustcPeekAt<'tcx> for IndirectlyMutableLocals<'_, 'tcx> {
+impl<'tcx> RustcPeekAt<'tcx> for MaybeMutBorrowedLocals<'_, 'tcx> {
     fn peek_at(
         &self,
         tcx: TyCtxt<'tcx>,
@@ -298,4 +285,3 @@ impl<'tcx> RustcPeekAt<'tcx> for IndirectlyMutableLocals<'_, 'tcx> {
         }
     }
 }
-*/
diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs
index 6f003043aa9..2fb7977dce9 100644
--- a/src/librustc_session/session.rs
+++ b/src/librustc_session/session.rs
@@ -392,6 +392,7 @@ impl Session {
         );
     }
 
+    #[inline]
     pub fn source_map(&self) -> &source_map::SourceMap {
         self.parse_sess.source_map()
     }
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index 3c419334d42..97708d91d7e 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -993,6 +993,7 @@ impl Encodable for Symbol {
 }
 
 impl Decodable for Symbol {
+    #[inline]
     fn decode<D: Decoder>(d: &mut D) -> Result<Symbol, D::Error> {
         Ok(Symbol::intern(&d.read_str()?))
     }
@@ -1031,6 +1032,7 @@ impl Interner {
         }
     }
 
+    #[inline]
     pub fn intern(&mut self, string: &str) -> Symbol {
         if let Some(&name) = self.names.get(string) {
             return name;
diff --git a/src/librustc_target/spec/thumb_base.rs b/src/librustc_target/spec/thumb_base.rs
index 204f4723b70..99ab996be95 100644
--- a/src/librustc_target/spec/thumb_base.rs
+++ b/src/librustc_target/spec/thumb_base.rs
@@ -50,6 +50,9 @@ pub fn opts() -> TargetOptions {
         // until we figure a way to add the pretty printers without requiring a volatile load cf.
         // rust-lang/rust#44993.
         emit_debug_gdb_scripts: false,
+        // LLVM is eager to trash the link register when calling `noreturn` functions, which
+        // breaks debugging. Preserve LR by default to prevent that from happening.
+        eliminate_frame_pointer: false,
         ..Default::default()
     }
 }
diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs
index 19283ffc438..8c6548cd3c5 100644
--- a/src/libserialize/serialize.rs
+++ b/src/libserialize/serialize.rs
@@ -36,6 +36,7 @@ pub trait Encoder {
     fn emit_str(&mut self, v: &str) -> Result<(), Self::Error>;
 
     // Compound types:
+    #[inline]
     fn emit_enum<F>(&mut self, _name: &str, f: F) -> Result<(), Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<(), Self::Error>,
@@ -57,6 +58,7 @@ pub trait Encoder {
         f(self)
     }
 
+    #[inline]
     fn emit_enum_variant_arg<F>(&mut self, _a_idx: usize, f: F) -> Result<(), Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<(), Self::Error>,
@@ -89,6 +91,7 @@ pub trait Encoder {
         self.emit_enum_variant_arg(f_idx, f)
     }
 
+    #[inline]
     fn emit_struct<F>(&mut self, _name: &str, _len: usize, f: F) -> Result<(), Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<(), Self::Error>,
@@ -96,6 +99,7 @@ pub trait Encoder {
         f(self)
     }
 
+    #[inline]
     fn emit_struct_field<F>(
         &mut self,
         _f_name: &str,
@@ -108,6 +112,7 @@ pub trait Encoder {
         f(self)
     }
 
+    #[inline]
     fn emit_tuple<F>(&mut self, _len: usize, f: F) -> Result<(), Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<(), Self::Error>,
@@ -115,6 +120,7 @@ pub trait Encoder {
         f(self)
     }
 
+    #[inline]
     fn emit_tuple_arg<F>(&mut self, _idx: usize, f: F) -> Result<(), Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<(), Self::Error>,
@@ -164,6 +170,7 @@ pub trait Encoder {
         f(self)
     }
 
+    #[inline]
     fn emit_seq_elt<F>(&mut self, _idx: usize, f: F) -> Result<(), Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<(), Self::Error>,
@@ -179,6 +186,7 @@ pub trait Encoder {
         f(self)
     }
 
+    #[inline]
     fn emit_map_elt_key<F>(&mut self, _idx: usize, f: F) -> Result<(), Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<(), Self::Error>,
@@ -186,6 +194,7 @@ pub trait Encoder {
         f(self)
     }
 
+    #[inline]
     fn emit_map_elt_val<F>(&mut self, _idx: usize, f: F) -> Result<(), Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<(), Self::Error>,
@@ -218,6 +227,7 @@ pub trait Decoder {
     fn read_str(&mut self) -> Result<Cow<'_, str>, Self::Error>;
 
     // Compound types:
+    #[inline]
     fn read_enum<T, F>(&mut self, _name: &str, f: F) -> Result<T, Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<T, Self::Error>,
@@ -225,6 +235,7 @@ pub trait Decoder {
         f(self)
     }
 
+    #[inline]
     fn read_enum_variant<T, F>(&mut self, _names: &[&str], mut f: F) -> Result<T, Self::Error>
     where
         F: FnMut(&mut Self, usize) -> Result<T, Self::Error>,
@@ -233,6 +244,7 @@ pub trait Decoder {
         f(self, disr)
     }
 
+    #[inline]
     fn read_enum_variant_arg<T, F>(&mut self, _a_idx: usize, f: F) -> Result<T, Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<T, Self::Error>,
@@ -259,6 +271,7 @@ pub trait Decoder {
         self.read_enum_variant_arg(f_idx, f)
     }
 
+    #[inline]
     fn read_struct<T, F>(&mut self, _s_name: &str, _len: usize, f: F) -> Result<T, Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<T, Self::Error>,
@@ -266,6 +279,7 @@ pub trait Decoder {
         f(self)
     }
 
+    #[inline]
     fn read_struct_field<T, F>(
         &mut self,
         _f_name: &str,
@@ -278,6 +292,7 @@ pub trait Decoder {
         f(self)
     }
 
+    #[inline]
     fn read_tuple<T, F>(&mut self, _len: usize, f: F) -> Result<T, Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<T, Self::Error>,
@@ -285,6 +300,7 @@ pub trait Decoder {
         f(self)
     }
 
+    #[inline]
     fn read_tuple_arg<T, F>(&mut self, _a_idx: usize, f: F) -> Result<T, Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<T, Self::Error>,
@@ -328,6 +344,7 @@ pub trait Decoder {
         f(self, len)
     }
 
+    #[inline]
     fn read_seq_elt<T, F>(&mut self, _idx: usize, f: F) -> Result<T, Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<T, Self::Error>,
@@ -343,6 +360,7 @@ pub trait Decoder {
         f(self, len)
     }
 
+    #[inline]
     fn read_map_elt_key<T, F>(&mut self, _idx: usize, f: F) -> Result<T, Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<T, Self::Error>,
@@ -350,6 +368,7 @@ pub trait Decoder {
         f(self)
     }
 
+    #[inline]
     fn read_map_elt_val<T, F>(&mut self, _idx: usize, f: F) -> Result<T, Self::Error>
     where
         F: FnOnce(&mut Self) -> Result<T, Self::Error>,
diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index cff7bbe5ef1..09be3f13050 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -844,10 +844,7 @@ impl OpenOptions {
         self
     }
 
-    /// Sets the option for creating a new file.
-    ///
-    /// This option indicates whether a new file will be created if the file
-    /// does not yet already exist.
+    /// Sets the option to create a new file, or open it if it already exists.
     ///
     /// In order for the file to be created, [`write`] or [`append`] access must
     /// be used.
@@ -868,11 +865,10 @@ impl OpenOptions {
         self
     }
 
-    /// Sets the option to always create a new file.
+    /// Sets the option to create a new file, failing if it already exists.
     ///
-    /// This option indicates whether a new file will be created.
-    /// No file is allowed to exist at the target location, also no (dangling)
-    /// symlink.
+    /// No file is allowed to exist at the target location, also no (dangling) symlink. In this
+    /// way, if the call succeeds, the file returned is guaranteed to be new.
     ///
     /// This option is useful because it is atomic. Otherwise between checking
     /// whether a file exists and creating a new one, the file may have been
diff --git a/src/test/incremental/issue-61530.rs b/src/test/incremental/issue-61530.rs
index 06b2957ac62..c9c600efed8 100644
--- a/src/test/incremental/issue-61530.rs
+++ b/src/test/incremental/issue-61530.rs
@@ -1,4 +1,4 @@
-#![feature(repr_simd, platform_intrinsics)]
+#![feature(repr_simd, platform_intrinsics, rustc_attrs)]
 
 // revisions:rpass1 rpass2
 
@@ -6,6 +6,7 @@
 struct I32x2(i32, i32);
 
 extern "platform-intrinsic" {
+    #[rustc_args_required_const(2)]
     fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
 }
 
diff --git a/src/test/ui/issues/issue-38074.rs b/src/test/ui/issues/issue-38074.rs
index 214d6752cef..8f7905b31ec 100644
--- a/src/test/ui/issues/issue-38074.rs
+++ b/src/test/ui/issues/issue-38074.rs
@@ -1,9 +1,10 @@
 // run-pass
 // ignore-emscripten FIXME(#45351)
 
-#![feature(platform_intrinsics, repr_simd)]
+#![feature(platform_intrinsics, repr_simd, rustc_attrs)]
 
 extern "platform-intrinsic" {
+    #[rustc_args_required_const(2)]
     fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
 }
 
diff --git a/src/test/ui/issues/issue-69225-layout-repeated-checked-add.rs b/src/test/ui/issues/issue-69225-layout-repeated-checked-add.rs
new file mode 100644
index 00000000000..7f43e4d1a51
--- /dev/null
+++ b/src/test/ui/issues/issue-69225-layout-repeated-checked-add.rs
@@ -0,0 +1,31 @@
+// Ensure we appropriately error instead of overflowing a calculation when creating a new Alloc
+// Layout
+
+// run-fail
+// compile-flags: -C opt-level=3
+// error-pattern: index out of bounds: the len is 0 but the index is 16777216
+// ignore-wasm no panic or subprocess support
+// ignore-emscripten no panic or subprocess support
+
+fn do_test(x: usize) {
+    let arr = vec![vec![0u8; 3]];
+
+    let mut z = Vec::new();
+    for arr_ref in arr {
+        for y in 0..x {
+            for _ in 0..1 {
+                z.extend(std::iter::repeat(0).take(x));
+                let a = y * x;
+                let b = (y + 1) * x - 1;
+                let slice = &arr_ref[a..b];
+                eprintln!("{} {} {} {}", a, b, arr_ref.len(), slice.len());
+                eprintln!("{:?}", slice[1 << 24]);
+            }
+        }
+    }
+}
+
+fn main() {
+    do_test(1);
+    do_test(2);
+}
diff --git a/src/test/ui/mir-dataflow/indirect-mutation-offset.rs b/src/test/ui/mir-dataflow/indirect-mutation-offset.rs
index 884c83b6616..caa307e269f 100644
--- a/src/test/ui/mir-dataflow/indirect-mutation-offset.rs
+++ b/src/test/ui/mir-dataflow/indirect-mutation-offset.rs
@@ -1,6 +1,11 @@
 // compile-flags: -Zunleash-the-miri-inside-of-you
 
-// ignore-test Temporarily ignored while this analysis is migrated to the new framework.
+// This test demonstrates a shortcoming of the `MaybeMutBorrowedLocals` analysis. It does not
+// handle code that takes a reference to one field of a struct, then use pointer arithmetic to
+// transform it to another field of that same struct that may have interior mutability. For now,
+// this is UB, but this may change in the future. See [rust-lang/unsafe-code-guidelines#134].
+//
+// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134
 
 #![feature(core_intrinsics, rustc_attrs, const_raw_ptr_deref)]
 
diff --git a/src/test/ui/mir-dataflow/indirect-mutation-offset.stderr b/src/test/ui/mir-dataflow/indirect-mutation-offset.stderr
index 0ae9a40c96a..8d3548ececd 100644
--- a/src/test/ui/mir-dataflow/indirect-mutation-offset.stderr
+++ b/src/test/ui/mir-dataflow/indirect-mutation-offset.stderr
@@ -1,5 +1,5 @@
 error: rustc_peek: bit not set
-  --> $DIR/indirect-mutation-offset.rs:34:14
+  --> $DIR/indirect-mutation-offset.rs:41:14
    |
 LL |     unsafe { rustc_peek(x) };
    |              ^^^^^^^^^^^^^
diff --git a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-elements.rs b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-elements.rs
index 5929d05f4de..c9c9ab879f2 100644
--- a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-elements.rs
+++ b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-elements.rs
@@ -42,9 +42,13 @@ extern "platform-intrinsic" {
     fn simd_insert<T, E>(x: T, idx: u32, y: E) -> T;
     fn simd_extract<T, E>(x: T, idx: u32) -> E;
 
+    #[rustc_args_required_const(2)]
     fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
+    #[rustc_args_required_const(2)]
     fn simd_shuffle3<T, U>(x: T, y: T, idx: [u32; 3]) -> U;
+    #[rustc_args_required_const(2)]
     fn simd_shuffle4<T, U>(x: T, y: T, idx: [u32; 4]) -> U;
+    #[rustc_args_required_const(2)]
     fn simd_shuffle8<T, U>(x: T, y: T, idx: [u32; 8]) -> U;
 }
 
diff --git a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-elements.stderr b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-elements.stderr
index 78022c0c8bd..29916f85902 100644
--- a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-elements.stderr
+++ b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-elements.stderr
@@ -1,89 +1,89 @@
 error[E0511]: invalid monomorphization of `simd_insert` intrinsic: expected SIMD input type, found non-SIMD `i32`
-  --> $DIR/simd-intrinsic-generic-elements.rs:55:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:59:9
    |
 LL |         simd_insert(0, 0, 0);
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_insert` intrinsic: expected inserted type `i32` (element of input `i32x4`), found `f64`
-  --> $DIR/simd-intrinsic-generic-elements.rs:57:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:61:9
    |
 LL |         simd_insert(x, 0, 1.0);
    |         ^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_extract` intrinsic: expected return type `i32` (element of input `i32x4`), found `f32`
-  --> $DIR/simd-intrinsic-generic-elements.rs:59:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:63:9
    |
 LL |         simd_extract::<_, f32>(x, 0);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle2` intrinsic: expected SIMD input type, found non-SIMD `i32`
-  --> $DIR/simd-intrinsic-generic-elements.rs:62:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:66:9
    |
 LL |         simd_shuffle2::<i32, i32>(0, 0, [0; 2]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle3` intrinsic: expected SIMD input type, found non-SIMD `i32`
-  --> $DIR/simd-intrinsic-generic-elements.rs:64:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:68:9
    |
 LL |         simd_shuffle3::<i32, i32>(0, 0, [0; 3]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle4` intrinsic: expected SIMD input type, found non-SIMD `i32`
-  --> $DIR/simd-intrinsic-generic-elements.rs:66:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:70:9
    |
 LL |         simd_shuffle4::<i32, i32>(0, 0, [0; 4]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle8` intrinsic: expected SIMD input type, found non-SIMD `i32`
-  --> $DIR/simd-intrinsic-generic-elements.rs:68:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:72:9
    |
 LL |         simd_shuffle8::<i32, i32>(0, 0, [0; 8]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle2` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x2` with element type `f32`
-  --> $DIR/simd-intrinsic-generic-elements.rs:71:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:75:9
    |
 LL |         simd_shuffle2::<_, f32x2>(x, x, [0; 2]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle3` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x3` with element type `f32`
-  --> $DIR/simd-intrinsic-generic-elements.rs:73:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:77:9
    |
 LL |         simd_shuffle3::<_, f32x3>(x, x, [0; 3]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle4` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x4` with element type `f32`
-  --> $DIR/simd-intrinsic-generic-elements.rs:75:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:79:9
    |
 LL |         simd_shuffle4::<_, f32x4>(x, x, [0; 4]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle8` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x8` with element type `f32`
-  --> $DIR/simd-intrinsic-generic-elements.rs:77:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:81:9
    |
 LL |         simd_shuffle8::<_, f32x8>(x, x, [0; 8]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle2` intrinsic: expected return type of length 2, found `i32x8` with length 8
-  --> $DIR/simd-intrinsic-generic-elements.rs:80:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:84:9
    |
 LL |         simd_shuffle2::<_, i32x8>(x, x, [0; 2]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle3` intrinsic: expected return type of length 3, found `i32x4` with length 4
-  --> $DIR/simd-intrinsic-generic-elements.rs:82:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:86:9
    |
 LL |         simd_shuffle3::<_, i32x4>(x, x, [0; 3]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle4` intrinsic: expected return type of length 4, found `i32x3` with length 3
-  --> $DIR/simd-intrinsic-generic-elements.rs:84:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:88:9
    |
 LL |         simd_shuffle4::<_, i32x3>(x, x, [0; 4]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0511]: invalid monomorphization of `simd_shuffle8` intrinsic: expected return type of length 8, found `i32x2` with length 2
-  --> $DIR/simd-intrinsic-generic-elements.rs:86:9
+  --> $DIR/simd-intrinsic-generic-elements.rs:90:9
    |
 LL |         simd_shuffle8::<_, i32x2>(x, x, [0; 8]);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557-ice.rs b/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557-ice.rs
index 4c09ae25c5f..b03b0ef5089 100644
--- a/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557-ice.rs
+++ b/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557-ice.rs
@@ -3,9 +3,10 @@
 //
 // run-pass
 // compile-flags: -Zmir-opt-level=3
-#![feature(platform_intrinsics, repr_simd)]
+#![feature(platform_intrinsics, repr_simd, rustc_attrs)]
 
 extern "platform-intrinsic" {
+    #[rustc_args_required_const(2)]
     fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
 }
 
diff --git a/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557.rs b/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557.rs
index 7a0d955686b..2741dc13336 100644
--- a/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557.rs
+++ b/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557.rs
@@ -3,9 +3,10 @@
 //
 // run-pass
 // compile-flags: -Zmir-opt-level=3
-#![feature(platform_intrinsics, repr_simd)]
+#![feature(platform_intrinsics, repr_simd, rustc_attrs)]
 
 extern "platform-intrinsic" {
+    #[rustc_args_required_const(2)]
     fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
 }
 
diff --git a/src/test/ui/simd/simd-intrinsic-generic-elements.rs b/src/test/ui/simd/simd-intrinsic-generic-elements.rs
index ea3d4b18944..abff59fea78 100644
--- a/src/test/ui/simd/simd-intrinsic-generic-elements.rs
+++ b/src/test/ui/simd/simd-intrinsic-generic-elements.rs
@@ -1,7 +1,7 @@
 // run-pass
 // ignore-emscripten FIXME(#45351) hits an LLVM assert
 
-#![feature(repr_simd, platform_intrinsics)]
+#![feature(repr_simd, platform_intrinsics, rustc_attrs)]
 
 #[repr(simd)]
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -25,9 +25,13 @@ extern "platform-intrinsic" {
     fn simd_insert<T, E>(x: T, idx: u32, y: E) -> T;
     fn simd_extract<T, E>(x: T, idx: u32) -> E;
 
+    #[rustc_args_required_const(2)]
     fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
+    #[rustc_args_required_const(2)]
     fn simd_shuffle3<T, U>(x: T, y: T, idx: [u32; 3]) -> U;
+    #[rustc_args_required_const(2)]
     fn simd_shuffle4<T, U>(x: T, y: T, idx: [u32; 4]) -> U;
+    #[rustc_args_required_const(2)]
     fn simd_shuffle8<T, U>(x: T, y: T, idx: [u32; 8]) -> U;
 }
 
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 3c53211c3d7fee4f430f170115af5baad17a3da
+Subproject e02974078a692d7484f510eaec0e88d1b6cc020
diff --git a/src/tools/clippy b/src/tools/clippy
-Subproject b91ae16eb1ab03b996de5ffc44db054244c1d2f
+Subproject 2855b2143972df7102333193aa3c83ddce227e3
diff --git a/src/tools/linkchecker/linkcheck.sh b/src/tools/linkchecker/linkcheck.sh
new file mode 100755
index 00000000000..bbccc17e494
--- /dev/null
+++ b/src/tools/linkchecker/linkcheck.sh
@@ -0,0 +1,113 @@
+#!/bin/sh
+#
+# This is a script that can be used in each book's CI to validate links using
+# the same tool as rust-lang/rust.
+#
+# This requires the rust-docs rustup component to be installed in the nightly
+# toolchain.
+#
+# Usage:
+#   ./linkcheck.sh <name-of-book>
+#
+# Options:
+#
+# -i        "Iterative" mode. The script will not clean up after it is done so
+#           you can inspect the result, and re-run more quickly.
+#
+# --all     Check all books. This can help make sure you don't break links
+#           from other books into your book.
+
+set -e
+
+if [ ! -f book.toml ] && [ ! -f src/SUMMARY.md ]
+then
+    echo "Run command in root directory of the book."
+    exit 1
+fi
+
+html_dir="$(rustc +nightly --print sysroot)/share/doc/rust/html"
+
+if [ ! -d "$html_dir" ]
+then
+    echo "HTML docs are missing from sysroot: $html_dir"
+    echo "Make sure the nightly rust-docs rustup component is installed."
+    exit 1
+fi
+
+book_name=""
+# Iterative will avoid cleaning up, so you can quickly run it repeatedly.
+iterative=0
+# If "1", test all books, else only this book.
+all_books=0
+
+while [ "$1" != "" ]
+do
+    case "$1" in
+        -i)
+            iterative=1
+            ;;
+        --all)
+            all_books=1
+            ;;
+        *)
+            if [ -n "$book_name" ]
+            then
+                echo "only one argument allowed"
+                exit 1
+            fi
+            book_name="$1"
+            ;;
+    esac
+    shift
+done
+
+if [ -z "$book_name" ]
+then
+    echo "usage: $0 <name-of-book>"
+    exit 1
+fi
+
+if [ ! -d "$html_dir/$book_name" ]
+then
+    echo "book name \"$book_name\" not found in sysroot \"$html_dir\""
+    exit 1
+fi
+
+if [ "$iterative" = "0" ]
+then
+    echo "Cleaning old directories..."
+    rm -rf linkcheck linkchecker
+fi
+
+if [ ! -e "linkchecker/main.rs" ] || [ "$iterative" = "0" ]
+then
+    echo "Downloading linkchecker source..."
+    mkdir linkchecker
+    curl -o linkchecker/Cargo.toml \
+        https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/Cargo.toml
+    curl -o linkchecker/main.rs \
+        https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/main.rs
+fi
+
+echo "Building book \"$book_name\"..."
+mdbook build
+
+cp -R "$html_dir" linkcheck
+rm -rf "linkcheck/$book_name"
+cp -R book "linkcheck/$book_name"
+
+if [ "$all_books" = "1" ]
+then
+    check_path="linkcheck"
+else
+    check_path="linkcheck/$book_name"
+fi
+echo "Running linkchecker on \"$check_path\"..."
+cargo run --manifest-path=linkchecker/Cargo.toml -- "$check_path"
+
+if [ "$iterative" = "0" ]
+then
+    rm -rf linkcheck linkchecker
+fi
+
+echo "Link check completed successfully!"