about summary refs log tree commit diff
path: root/compiler/rustc_passes/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_passes/src')
-rw-r--r--compiler/rustc_passes/src/check_attr.rs339
-rw-r--r--compiler/rustc_passes/src/dead.rs38
-rw-r--r--compiler/rustc_passes/src/diagnostic_items.rs18
-rw-r--r--compiler/rustc_passes/src/entry.rs6
-rw-r--r--compiler/rustc_passes/src/hir_id_validator.rs12
-rw-r--r--compiler/rustc_passes/src/intrinsicck.rs2
-rw-r--r--compiler/rustc_passes/src/lang_items.rs2
-rw-r--r--compiler/rustc_passes/src/layout_test.rs1
-rw-r--r--compiler/rustc_passes/src/lib.rs3
-rw-r--r--compiler/rustc_passes/src/liveness.rs254
-rw-r--r--compiler/rustc_passes/src/liveness/rwu_table.rs144
-rw-r--r--compiler/rustc_passes/src/naked_functions.rs322
-rw-r--r--compiler/rustc_passes/src/reachable.rs6
-rw-r--r--compiler/rustc_passes/src/stability.rs4
14 files changed, 779 insertions, 372 deletions
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 7679582f881..73fb28e5c9a 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -78,7 +78,7 @@ impl CheckAttrVisitor<'tcx> {
             } else if self.tcx.sess.check_name(attr, sym::track_caller) {
                 self.check_track_caller(&attr.span, attrs, span, target)
             } else if self.tcx.sess.check_name(attr, sym::doc) {
-                self.check_doc_alias(attr, hir_id, target)
+                self.check_doc_attrs(attr, hir_id, target)
             } else if self.tcx.sess.check_name(attr, sym::no_link) {
                 self.check_no_link(&attr, span, target)
             } else if self.tcx.sess.check_name(attr, sym::export_name) {
@@ -89,6 +89,8 @@ impl CheckAttrVisitor<'tcx> {
                 self.check_allow_internal_unstable(&attr, span, target, &attrs)
             } else if self.tcx.sess.check_name(attr, sym::rustc_allow_const_fn_unstable) {
                 self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
+            } else if self.tcx.sess.check_name(attr, sym::naked) {
+                self.check_naked(attr, span, target)
             } else {
                 // lint-only checks
                 if self.tcx.sess.check_name(attr, sym::cold) {
@@ -162,6 +164,25 @@ impl CheckAttrVisitor<'tcx> {
         }
     }
 
+    /// Checks if `#[naked]` is applied to a function definition.
+    fn check_naked(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+        match target {
+            Target::Fn
+            | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
+            _ => {
+                self.tcx
+                    .sess
+                    .struct_span_err(
+                        attr.span,
+                        "attribute should be applied to a function definition",
+                    )
+                    .span_label(*span, "not a function definition")
+                    .emit();
+                false
+            }
+        }
+    }
+
     /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
     fn check_track_caller(
         &self,
@@ -171,7 +192,7 @@ impl CheckAttrVisitor<'tcx> {
         target: Target,
     ) -> bool {
         match target {
-            _ if self.tcx.sess.contains_name(attrs, sym::naked) => {
+            _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
                 struct_span_err!(
                     self.tcx.sess,
                     *attr_span,
@@ -266,99 +287,159 @@ impl CheckAttrVisitor<'tcx> {
         }
     }
 
-    fn doc_alias_str_error(&self, meta: &NestedMetaItem) {
+    fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
         self.tcx
             .sess
             .struct_span_err(
                 meta.span(),
-                "doc alias attribute expects a string: #[doc(alias = \"0\")]",
+                &format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name),
             )
             .emit();
     }
 
-    fn check_doc_alias(&self, attr: &Attribute, hir_id: HirId, target: Target) -> bool {
+    fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool {
+        let doc_alias = meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
+        if doc_alias.is_empty() {
+            self.doc_attr_str_error(meta, "alias");
+            return false;
+        }
+        if let Some(c) =
+            doc_alias.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
+        {
+            self.tcx
+                .sess
+                .struct_span_err(
+                    meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
+                    &format!("{:?} character isn't allowed in `#[doc(alias = \"...\")]`", c,),
+                )
+                .emit();
+            return false;
+        }
+        if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') {
+            self.tcx
+                .sess
+                .struct_span_err(
+                    meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
+                    "`#[doc(alias = \"...\")]` cannot start or end with ' '",
+                )
+                .emit();
+            return false;
+        }
+        if let Some(err) = match target {
+            Target::Impl => Some("implementation block"),
+            Target::ForeignMod => Some("extern block"),
+            Target::AssocTy => {
+                let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
+                let containing_item = self.tcx.hir().expect_item(parent_hir_id);
+                if Target::from_item(containing_item) == Target::Impl {
+                    Some("type alias in implementation block")
+                } else {
+                    None
+                }
+            }
+            Target::AssocConst => {
+                let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
+                let containing_item = self.tcx.hir().expect_item(parent_hir_id);
+                // We can't link to trait impl's consts.
+                let err = "associated constant in trait implementation block";
+                match containing_item.kind {
+                    ItemKind::Impl { of_trait: Some(_), .. } => Some(err),
+                    _ => None,
+                }
+            }
+            _ => None,
+        } {
+            self.tcx
+                .sess
+                .struct_span_err(
+                    meta.span(),
+                    &format!("`#[doc(alias = \"...\")]` isn't allowed on {}", err),
+                )
+                .emit();
+            return false;
+        }
+        true
+    }
+
+    fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
+        let doc_keyword = meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
+        if doc_keyword.is_empty() {
+            self.doc_attr_str_error(meta, "keyword");
+            return false;
+        }
+        match self.tcx.hir().expect_item(hir_id).kind {
+            ItemKind::Mod(ref module) => {
+                if !module.item_ids.is_empty() {
+                    self.tcx
+                        .sess
+                        .struct_span_err(
+                            meta.span(),
+                            "`#[doc(keyword = \"...\")]` can only be used on empty modules",
+                        )
+                        .emit();
+                    return false;
+                }
+            }
+            _ => {
+                self.tcx
+                    .sess
+                    .struct_span_err(
+                        meta.span(),
+                        "`#[doc(keyword = \"...\")]` can only be used on modules",
+                    )
+                    .emit();
+                return false;
+            }
+        }
+        if !rustc_lexer::is_ident(&doc_keyword) {
+            self.tcx
+                .sess
+                .struct_span_err(
+                    meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
+                    &format!("`{}` is not a valid identifier", doc_keyword),
+                )
+                .emit();
+            return false;
+        }
+        true
+    }
+
+    fn check_attr_crate_level(
+        &self,
+        meta: &NestedMetaItem,
+        hir_id: HirId,
+        attr_name: &str,
+    ) -> bool {
+        if CRATE_HIR_ID == hir_id {
+            self.tcx
+                .sess
+                .struct_span_err(
+                    meta.span(),
+                    &format!(
+                        "`#![doc({} = \"...\")]` isn't allowed as a crate level attribute",
+                        attr_name,
+                    ),
+                )
+                .emit();
+            return false;
+        }
+        true
+    }
+
+    fn check_doc_attrs(&self, attr: &Attribute, hir_id: HirId, target: Target) -> bool {
         if let Some(mi) = attr.meta() {
             if let Some(list) = mi.meta_item_list() {
                 for meta in list {
                     if meta.has_name(sym::alias) {
-                        if !meta.is_value_str() {
-                            self.doc_alias_str_error(meta);
-                            return false;
-                        }
-                        let doc_alias =
-                            meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
-                        if doc_alias.is_empty() {
-                            self.doc_alias_str_error(meta);
-                            return false;
-                        }
-                        if let Some(c) = doc_alias
-                            .chars()
-                            .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
+                        if !self.check_attr_crate_level(meta, hir_id, "alias")
+                            || !self.check_doc_alias(meta, hir_id, target)
                         {
-                            self.tcx
-                                .sess
-                                .struct_span_err(
-                                    meta.span(),
-                                    &format!(
-                                        "{:?} character isn't allowed in `#[doc(alias = \"...\")]`",
-                                        c,
-                                    ),
-                                )
-                                .emit();
-                            return false;
-                        }
-                        if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') {
-                            self.tcx
-                                .sess
-                                .struct_span_err(
-                                    meta.span(),
-                                    "`#[doc(alias = \"...\")]` cannot start or end with ' '",
-                                )
-                                .emit();
-                            return false;
-                        }
-                        if let Some(err) = match target {
-                            Target::Impl => Some("implementation block"),
-                            Target::ForeignMod => Some("extern block"),
-                            Target::AssocTy => {
-                                let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
-                                let containing_item = self.tcx.hir().expect_item(parent_hir_id);
-                                if Target::from_item(containing_item) == Target::Impl {
-                                    Some("type alias in implementation block")
-                                } else {
-                                    None
-                                }
-                            }
-                            Target::AssocConst => {
-                                let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
-                                let containing_item = self.tcx.hir().expect_item(parent_hir_id);
-                                // We can't link to trait impl's consts.
-                                let err = "associated constant in trait implementation block";
-                                match containing_item.kind {
-                                    ItemKind::Impl { of_trait: Some(_), .. } => Some(err),
-                                    _ => None,
-                                }
-                            }
-                            _ => None,
-                        } {
-                            self.tcx
-                                .sess
-                                .struct_span_err(
-                                    meta.span(),
-                                    &format!("`#[doc(alias = \"...\")]` isn't allowed on {}", err),
-                                )
-                                .emit();
                             return false;
                         }
-                        if CRATE_HIR_ID == hir_id {
-                            self.tcx
-                                .sess
-                                .struct_span_err(
-                                    meta.span(),
-                                    "`#![doc(alias = \"...\")]` isn't allowed as a crate \
-                                     level attribute",
-                                )
-                                .emit();
+                    } else if meta.has_name(sym::keyword) {
+                        if !self.check_attr_crate_level(meta, hir_id, "keyword")
+                            || !self.check_doc_keyword(meta, hir_id)
+                        {
                             return false;
                         }
                     }
@@ -464,60 +545,68 @@ impl CheckAttrVisitor<'tcx> {
         target: Target,
         item: Option<ItemLike<'_>>,
     ) -> bool {
-        if let Target::Fn | Target::Method(..) | Target::ForeignFn = target {
-            let mut invalid_args = vec![];
-            for meta in attr.meta_item_list().expect("no meta item list") {
-                if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
-                    if let Some(ItemLike::Item(Item {
-                        kind: ItemKind::Fn(FnSig { decl, .. }, ..),
-                        ..
-                    }))
-                    | Some(ItemLike::ForeignItem(ForeignItem {
-                        kind: ForeignItemKind::Fn(decl, ..),
-                        ..
-                    })) = item
-                    {
-                        let arg_count = decl.inputs.len() as u128;
-                        if *val >= arg_count {
-                            let span = meta.span();
-                            self.tcx
-                                .sess
-                                .struct_span_err(span, "index exceeds number of arguments")
-                                .span_label(
-                                    span,
-                                    format!(
-                                        "there {} only {} argument{}",
-                                        if arg_count != 1 { "are" } else { "is" },
-                                        arg_count,
-                                        pluralize!(arg_count)
-                                    ),
-                                )
-                                .emit();
-                            return false;
-                        }
-                    } else {
-                        bug!("should be a function item");
+        let is_function = matches!(target, Target::Fn | Target::Method(..) | Target::ForeignFn);
+        if !is_function {
+            self.tcx
+                .sess
+                .struct_span_err(attr.span, "attribute should be applied to a function")
+                .span_label(*span, "not a function")
+                .emit();
+            return false;
+        }
+
+        let list = match attr.meta_item_list() {
+            // The attribute form is validated on AST.
+            None => return false,
+            Some(it) => it,
+        };
+
+        let mut invalid_args = vec![];
+        for meta in list {
+            if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
+                if let Some(ItemLike::Item(Item {
+                    kind: ItemKind::Fn(FnSig { decl, .. }, ..),
+                    ..
+                }))
+                | Some(ItemLike::ForeignItem(ForeignItem {
+                    kind: ForeignItemKind::Fn(decl, ..),
+                    ..
+                })) = item
+                {
+                    let arg_count = decl.inputs.len() as u128;
+                    if *val >= arg_count {
+                        let span = meta.span();
+                        self.tcx
+                            .sess
+                            .struct_span_err(span, "index exceeds number of arguments")
+                            .span_label(
+                                span,
+                                format!(
+                                    "there {} only {} argument{}",
+                                    if arg_count != 1 { "are" } else { "is" },
+                                    arg_count,
+                                    pluralize!(arg_count)
+                                ),
+                            )
+                            .emit();
+                        return false;
                     }
                 } else {
-                    invalid_args.push(meta.span());
+                    bug!("should be a function item");
                 }
-            }
-            if !invalid_args.is_empty() {
-                self.tcx
-                    .sess
-                    .struct_span_err(invalid_args, "arguments should be non-negative integers")
-                    .emit();
-                false
             } else {
-                true
+                invalid_args.push(meta.span());
             }
-        } else {
+        }
+
+        if !invalid_args.is_empty() {
             self.tcx
                 .sess
-                .struct_span_err(attr.span, "attribute should be applied to a function")
-                .span_label(*span, "not a function")
+                .struct_span_err(invalid_args, "arguments should be non-negative integers")
                 .emit();
             false
+        } else {
+            true
         }
     }
 
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index f567dd83bc1..00152878d6d 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -190,7 +190,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
 
                     intravisit::walk_item(self, &item);
                 }
-                hir::ItemKind::ForeignMod(..) => {}
+                hir::ItemKind::ForeignMod { .. } => {}
                 _ => {
                     intravisit::walk_item(self, &item);
                 }
@@ -396,24 +396,6 @@ impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> {
                     }
                 }
             }
-            hir::ItemKind::Trait(.., trait_item_refs) => {
-                for trait_item_ref in trait_item_refs {
-                    let trait_item = self.krate.trait_item(trait_item_ref.id);
-                    match trait_item.kind {
-                        hir::TraitItemKind::Const(_, Some(_))
-                        | hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => {
-                            if has_allow_dead_code_or_lang_attr(
-                                self.tcx,
-                                trait_item.hir_id,
-                                &trait_item.attrs,
-                            ) {
-                                self.worklist.push(trait_item.hir_id);
-                            }
-                        }
-                        _ => {}
-                    }
-                }
-            }
             hir::ItemKind::Impl { ref of_trait, items, .. } => {
                 if of_trait.is_some() {
                     self.worklist.push(item.hir_id);
@@ -440,13 +422,27 @@ impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> {
         }
     }
 
-    fn visit_trait_item(&mut self, _item: &hir::TraitItem<'_>) {
-        // ignore: we are handling this in `visit_item` above
+    fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) {
+        use hir::TraitItemKind::{Const, Fn};
+        if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_)))
+            && has_allow_dead_code_or_lang_attr(self.tcx, trait_item.hir_id, &trait_item.attrs)
+        {
+            self.worklist.push(trait_item.hir_id);
+        }
     }
 
     fn visit_impl_item(&mut self, _item: &hir::ImplItem<'_>) {
         // ignore: we are handling this in `visit_item` above
     }
+
+    fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) {
+        use hir::ForeignItemKind::{Fn, Static};
+        if matches!(foreign_item.kind, Static(..) | Fn(..))
+            && has_allow_dead_code_or_lang_attr(self.tcx, foreign_item.hir_id, &foreign_item.attrs)
+        {
+            self.worklist.push(foreign_item.hir_id);
+        }
+    }
 }
 
 fn create_and_seed_worklist<'tcx>(
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index 0f4aa72d5c4..699c96bc49d 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -37,6 +37,10 @@ impl<'v, 'tcx> ItemLikeVisitor<'v> for DiagnosticItemCollector<'tcx> {
     fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) {
         self.observe_item(&impl_item.attrs, impl_item.hir_id);
     }
+
+    fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) {
+        self.observe_item(foreign_item.attrs, foreign_item.hir_id);
+    }
 }
 
 impl<'tcx> DiagnosticItemCollector<'tcx> {
@@ -100,17 +104,9 @@ fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> FxHashMap<Symbol, DefId> {
 
     // Collect diagnostic items in this crate.
     tcx.hir().krate().visit_all_item_likes(&mut collector);
-    // FIXME(visit_all_item_likes): Foreign items are not visited
-    // here, so we have to manually look at them for now.
-    for (_, foreign_module) in tcx.foreign_modules(LOCAL_CRATE).iter() {
-        for &foreign_item in foreign_module.foreign_items.iter() {
-            match tcx.hir().get(tcx.hir().local_def_id_to_hir_id(foreign_item.expect_local())) {
-                hir::Node::ForeignItem(item) => {
-                    collector.observe_item(item.attrs, item.hir_id);
-                }
-                item => bug!("unexpected foreign item {:?}", item),
-            }
-        }
+
+    for m in tcx.hir().krate().exported_macros {
+        collector.observe_item(m.attrs, m.hir_id);
     }
 
     collector.items
diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs
index e87adb378e7..5ff631a2457 100644
--- a/compiler/rustc_passes/src/entry.rs
+++ b/compiler/rustc_passes/src/entry.rs
@@ -2,7 +2,7 @@ use rustc_ast::entry::EntryPointType;
 use rustc_errors::struct_span_err;
 use rustc_hir::def_id::{CrateNum, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
-use rustc_hir::{HirId, ImplItem, Item, ItemKind, TraitItem};
+use rustc_hir::{ForeignItem, HirId, ImplItem, Item, ItemKind, TraitItem};
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
@@ -45,6 +45,10 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> {
     fn visit_impl_item(&mut self, _impl_item: &'tcx ImplItem<'tcx>) {
         // Entry fn is never a trait item.
     }
+
+    fn visit_foreign_item(&mut self, _: &'tcx ForeignItem<'tcx>) {
+        // Entry fn is never a foreign item.
+    }
 }
 
 fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(LocalDefId, EntryFnType)> {
diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs
index 6d1a5fcc10b..fdd6c238055 100644
--- a/compiler/rustc_passes/src/hir_id_validator.rs
+++ b/compiler/rustc_passes/src/hir_id_validator.rs
@@ -68,6 +68,11 @@ impl<'a, 'hir> ItemLikeVisitor<'hir> for OuterVisitor<'a, 'hir> {
         let mut inner_visitor = self.new_inner_visitor(self.hir_map);
         inner_visitor.check(i.hir_id, |this| intravisit::walk_impl_item(this, i));
     }
+
+    fn visit_foreign_item(&mut self, i: &'hir hir::ForeignItem<'hir>) {
+        let mut inner_visitor = self.new_inner_visitor(self.hir_map);
+        inner_visitor.check(i.hir_id, |this| intravisit::walk_foreign_item(this, i));
+    }
 }
 
 impl<'a, 'hir> HirIdValidator<'a, 'hir> {
@@ -164,6 +169,13 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> {
         // different owner.
     }
 
+    fn visit_foreign_item_ref(&mut self, _: &'hir hir::ForeignItemRef<'hir>) {
+        // Explicitly do nothing here. ForeignItemRefs contain hir::Visibility
+        // values that actually belong to an ForeignItem instead of the ItemKind::ForeignMod
+        // we are currently in. So for those it's correct that they have a
+        // different owner.
+    }
+
     fn visit_generic_param(&mut self, param: &'hir hir::GenericParam<'hir>) {
         if let hir::GenericParamKind::Type {
             synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs
index 956be925be8..711e8e87c6c 100644
--- a/compiler/rustc_passes/src/intrinsicck.rs
+++ b/compiler/rustc_passes/src/intrinsicck.rs
@@ -347,7 +347,7 @@ impl ExprVisitor<'tcx> {
     }
 
     fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) {
-        for (idx, op) in asm.operands.iter().enumerate() {
+        for (idx, (op, _op_sp)) in asm.operands.iter().enumerate() {
             match *op {
                 hir::InlineAsmOperand::In { reg, ref expr } => {
                     self.check_asm_operand_type(idx, reg, expr, asm.template, None);
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index 0ae0c381a11..3132661e5f5 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -54,6 +54,8 @@ impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> {
             impl_item.attrs,
         )
     }
+
+    fn visit_foreign_item(&mut self, _: &hir::ForeignItem<'_>) {}
 }
 
 impl LanguageItemCollector<'tcx> {
diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs
index 504cbbfcb76..9e83cbd6680 100644
--- a/compiler/rustc_passes/src/layout_test.rs
+++ b/compiler/rustc_passes/src/layout_test.rs
@@ -40,6 +40,7 @@ impl ItemLikeVisitor<'tcx> for LayoutTest<'tcx> {
 
     fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {}
     fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {}
+    fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {}
 }
 
 impl LayoutTest<'tcx> {
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index c32c9c8eaa6..9759a500e06 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -7,6 +7,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(const_fn)]
 #![feature(const_panic)]
+#![feature(crate_visibility_modifier)]
 #![feature(in_band_lifetimes)]
 #![feature(nll)]
 #![feature(or_patterns)]
@@ -32,6 +33,7 @@ pub mod layout_test;
 mod lib_features;
 mod liveness;
 pub mod loops;
+mod naked_functions;
 mod reachable;
 mod region;
 pub mod stability;
@@ -46,6 +48,7 @@ pub fn provide(providers: &mut Providers) {
     lang_items::provide(providers);
     lib_features::provide(providers);
     loops::provide(providers);
+    naked_functions::provide(providers);
     liveness::provide(providers);
     intrinsicck::provide(providers);
     reachable::provide(providers);
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index debb873beb9..a161ad16b8c 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -105,6 +105,8 @@ use std::io;
 use std::io::prelude::*;
 use std::rc::Rc;
 
+mod rwu_table;
+
 rustc_index::newtype_index! {
     pub struct Variable {
         DEBUG_FORMAT = "v({})",
@@ -468,101 +470,6 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
 // Actually we compute just a bit more than just liveness, but we use
 // the same basic propagation framework in all cases.
 
-#[derive(Clone, Copy)]
-struct RWU {
-    reader: Option<LiveNode>,
-    writer: Option<LiveNode>,
-    used: bool,
-}
-
-/// Conceptually, this is like a `Vec<RWU>`. But the number of `RWU`s can get
-/// very large, so it uses a more compact representation that takes advantage
-/// of the fact that when the number of `RWU`s is large, most of them have an
-/// invalid reader and an invalid writer.
-struct RWUTable {
-    /// Each entry in `packed_rwus` is either INV_INV_FALSE, INV_INV_TRUE, or
-    /// an index into `unpacked_rwus`. In the common cases, this compacts the
-    /// 65 bits of data into 32; in the uncommon cases, it expands the 65 bits
-    /// in 96.
-    ///
-    /// More compact representations are possible -- e.g., use only 2 bits per
-    /// packed `RWU` and make the secondary table a HashMap that maps from
-    /// indices to `RWU`s -- but this one strikes a good balance between size
-    /// and speed.
-    packed_rwus: Vec<u32>,
-    unpacked_rwus: Vec<RWU>,
-}
-
-// A constant representing `RWU { reader: None; writer: None; used: false }`.
-const INV_INV_FALSE: u32 = u32::MAX;
-
-// A constant representing `RWU { reader: None; writer: None; used: true }`.
-const INV_INV_TRUE: u32 = u32::MAX - 1;
-
-impl RWUTable {
-    fn new(num_rwus: usize) -> RWUTable {
-        Self { packed_rwus: vec![INV_INV_FALSE; num_rwus], unpacked_rwus: vec![] }
-    }
-
-    fn get(&self, idx: usize) -> RWU {
-        let packed_rwu = self.packed_rwus[idx];
-        match packed_rwu {
-            INV_INV_FALSE => RWU { reader: None, writer: None, used: false },
-            INV_INV_TRUE => RWU { reader: None, writer: None, used: true },
-            _ => self.unpacked_rwus[packed_rwu as usize],
-        }
-    }
-
-    fn get_reader(&self, idx: usize) -> Option<LiveNode> {
-        let packed_rwu = self.packed_rwus[idx];
-        match packed_rwu {
-            INV_INV_FALSE | INV_INV_TRUE => None,
-            _ => self.unpacked_rwus[packed_rwu as usize].reader,
-        }
-    }
-
-    fn get_writer(&self, idx: usize) -> Option<LiveNode> {
-        let packed_rwu = self.packed_rwus[idx];
-        match packed_rwu {
-            INV_INV_FALSE | INV_INV_TRUE => None,
-            _ => self.unpacked_rwus[packed_rwu as usize].writer,
-        }
-    }
-
-    fn get_used(&self, idx: usize) -> bool {
-        let packed_rwu = self.packed_rwus[idx];
-        match packed_rwu {
-            INV_INV_FALSE => false,
-            INV_INV_TRUE => true,
-            _ => self.unpacked_rwus[packed_rwu as usize].used,
-        }
-    }
-
-    #[inline]
-    fn copy_packed(&mut self, dst_idx: usize, src_idx: usize) {
-        self.packed_rwus[dst_idx] = self.packed_rwus[src_idx];
-    }
-
-    fn assign_unpacked(&mut self, idx: usize, rwu: RWU) {
-        if rwu.reader == None && rwu.writer == None {
-            // When we overwrite an indexing entry in `self.packed_rwus` with
-            // `INV_INV_{TRUE,FALSE}` we don't remove the corresponding entry
-            // from `self.unpacked_rwus`; it's not worth the effort, and we
-            // can't have entries shifting around anyway.
-            self.packed_rwus[idx] = if rwu.used { INV_INV_TRUE } else { INV_INV_FALSE }
-        } else {
-            // Add a new RWU to `unpacked_rwus` and make `packed_rwus[idx]`
-            // point to it.
-            self.packed_rwus[idx] = self.unpacked_rwus.len() as u32;
-            self.unpacked_rwus.push(rwu);
-        }
-    }
-
-    fn assign_inv_inv(&mut self, idx: usize) {
-        self.packed_rwus[idx] = if self.get_used(idx) { INV_INV_TRUE } else { INV_INV_FALSE };
-    }
-}
-
 const ACC_READ: u32 = 1;
 const ACC_WRITE: u32 = 2;
 const ACC_USE: u32 = 4;
@@ -575,7 +482,7 @@ struct Liveness<'a, 'tcx> {
     upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
     closure_captures: Option<&'tcx FxIndexMap<hir::HirId, ty::UpvarId>>,
     successors: IndexVec<LiveNode, Option<LiveNode>>,
-    rwu_table: RWUTable,
+    rwu_table: rwu_table::RWUTable,
 
     /// A live node representing a point of execution before closure entry &
     /// after closure exit. Used to calculate liveness of captured variables
@@ -613,7 +520,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
             upvars,
             closure_captures,
             successors: IndexVec::from_elem_n(None, num_live_nodes),
-            rwu_table: RWUTable::new(num_live_nodes * num_vars),
+            rwu_table: rwu_table::RWUTable::new(num_live_nodes, num_vars),
             closure_ln,
             exit_ln,
             break_ln: Default::default(),
@@ -652,61 +559,37 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         succ
     }
 
-    fn idx(&self, ln: LiveNode, var: Variable) -> usize {
-        ln.index() * self.ir.var_kinds.len() + var.index()
-    }
-
-    fn live_on_entry(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
-        if let Some(reader) = self.rwu_table.get_reader(self.idx(ln, var)) {
-            Some(self.ir.lnks[reader])
-        } else {
-            None
-        }
+    fn live_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
+        self.rwu_table.get_reader(ln, var)
     }
 
     // Is this variable live on entry to any of its successor nodes?
-    fn live_on_exit(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
+    fn live_on_exit(&self, ln: LiveNode, var: Variable) -> bool {
         let successor = self.successors[ln].unwrap();
         self.live_on_entry(successor, var)
     }
 
     fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
-        self.rwu_table.get_used(self.idx(ln, var))
+        self.rwu_table.get_used(ln, var)
     }
 
-    fn assigned_on_entry(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
-        if let Some(writer) = self.rwu_table.get_writer(self.idx(ln, var)) {
-            Some(self.ir.lnks[writer])
-        } else {
-            None
-        }
+    fn assigned_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
+        self.rwu_table.get_writer(ln, var)
     }
 
-    fn assigned_on_exit(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
+    fn assigned_on_exit(&self, ln: LiveNode, var: Variable) -> bool {
         let successor = self.successors[ln].unwrap();
         self.assigned_on_entry(successor, var)
     }
 
-    fn indices2<F>(&mut self, ln: LiveNode, succ_ln: LiveNode, mut op: F)
-    where
-        F: FnMut(&mut Liveness<'a, 'tcx>, usize, usize),
-    {
-        let node_base_idx = self.idx(ln, Variable::from(0u32));
-        let succ_base_idx = self.idx(succ_ln, Variable::from(0u32));
-        for var_idx in 0..self.ir.var_kinds.len() {
-            op(self, node_base_idx + var_idx, succ_base_idx + var_idx);
-        }
-    }
-
-    fn write_vars<F>(&self, wr: &mut dyn Write, ln: LiveNode, mut test: F) -> io::Result<()>
+    fn write_vars<F>(&self, wr: &mut dyn Write, mut test: F) -> io::Result<()>
     where
-        F: FnMut(usize) -> bool,
+        F: FnMut(Variable) -> bool,
     {
-        let node_base_idx = self.idx(ln, Variable::from(0u32));
         for var_idx in 0..self.ir.var_kinds.len() {
-            let idx = node_base_idx + var_idx;
-            if test(idx) {
-                write!(wr, " {:?}", Variable::from(var_idx))?;
+            let var = Variable::from(var_idx);
+            if test(var) {
+                write!(wr, " {:?}", var)?;
             }
         }
         Ok(())
@@ -718,11 +601,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         {
             let wr = &mut wr as &mut dyn Write;
             write!(wr, "[{:?} of kind {:?} reads", ln, self.ir.lnks[ln]);
-            self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx).is_some());
+            self.write_vars(wr, |var| self.rwu_table.get_reader(ln, var));
             write!(wr, "  writes");
-            self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx).is_some());
+            self.write_vars(wr, |var| self.rwu_table.get_writer(ln, var));
             write!(wr, "  uses");
-            self.write_vars(wr, ln, |idx| self.rwu_table.get_used(idx));
+            self.write_vars(wr, |var| self.rwu_table.get_used(ln, var));
 
             write!(wr, "  precedes {:?}]", self.successors[ln]);
         }
@@ -747,100 +630,57 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         self.successors[ln] = Some(succ_ln);
 
         // It is not necessary to initialize the RWUs here because they are all
-        // set to INV_INV_FALSE when they are created, and the sets only grow
-        // during iterations.
+        // empty when created, and the sets only grow during iterations.
     }
 
     fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) {
         // more efficient version of init_empty() / merge_from_succ()
         self.successors[ln] = Some(succ_ln);
-
-        self.indices2(ln, succ_ln, |this, idx, succ_idx| {
-            this.rwu_table.copy_packed(idx, succ_idx);
-        });
+        self.rwu_table.copy(ln, succ_ln);
         debug!("init_from_succ(ln={}, succ={})", self.ln_str(ln), self.ln_str(succ_ln));
     }
 
-    fn merge_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode, first_merge: bool) -> bool {
+    fn merge_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) -> bool {
         if ln == succ_ln {
             return false;
         }
 
-        let mut any_changed = false;
-        self.indices2(ln, succ_ln, |this, idx, succ_idx| {
-            // This is a special case, pulled out from the code below, where we
-            // don't have to do anything. It occurs about 60-70% of the time.
-            if this.rwu_table.packed_rwus[succ_idx] == INV_INV_FALSE {
-                return;
-            }
-
-            let mut changed = false;
-            let mut rwu = this.rwu_table.get(idx);
-            let succ_rwu = this.rwu_table.get(succ_idx);
-            if succ_rwu.reader.is_some() && rwu.reader.is_none() {
-                rwu.reader = succ_rwu.reader;
-                changed = true
-            }
-
-            if succ_rwu.writer.is_some() && rwu.writer.is_none() {
-                rwu.writer = succ_rwu.writer;
-                changed = true
-            }
-
-            if succ_rwu.used && !rwu.used {
-                rwu.used = true;
-                changed = true;
-            }
-
-            if changed {
-                this.rwu_table.assign_unpacked(idx, rwu);
-                any_changed = true;
-            }
-        });
-
-        debug!(
-            "merge_from_succ(ln={:?}, succ={}, first_merge={}, changed={})",
-            ln,
-            self.ln_str(succ_ln),
-            first_merge,
-            any_changed
-        );
-        any_changed
+        let changed = self.rwu_table.union(ln, succ_ln);
+        debug!("merge_from_succ(ln={:?}, succ={}, changed={})", ln, self.ln_str(succ_ln), changed);
+        changed
     }
 
     // Indicates that a local variable was *defined*; we know that no
     // uses of the variable can precede the definition (resolve checks
     // this) so we just clear out all the data.
     fn define(&mut self, writer: LiveNode, var: Variable) {
-        let idx = self.idx(writer, var);
-        self.rwu_table.assign_inv_inv(idx);
-
-        debug!("{:?} defines {:?} (idx={}): {}", writer, var, idx, self.ln_str(writer));
+        let used = self.rwu_table.get_used(writer, var);
+        self.rwu_table.set(writer, var, rwu_table::RWU { reader: false, writer: false, used });
+        debug!("{:?} defines {:?}: {}", writer, var, self.ln_str(writer));
     }
 
     // Either read, write, or both depending on the acc bitset
     fn acc(&mut self, ln: LiveNode, var: Variable, acc: u32) {
         debug!("{:?} accesses[{:x}] {:?}: {}", ln, acc, var, self.ln_str(ln));
 
-        let idx = self.idx(ln, var);
-        let mut rwu = self.rwu_table.get(idx);
+        let mut rwu = self.rwu_table.get(ln, var);
 
         if (acc & ACC_WRITE) != 0 {
-            rwu.reader = None;
-            rwu.writer = Some(ln);
+            rwu.reader = false;
+            rwu.writer = true;
         }
 
         // Important: if we both read/write, must do read second
         // or else the write will override.
         if (acc & ACC_READ) != 0 {
-            rwu.reader = Some(ln);
+            rwu.reader = true;
         }
 
         if (acc & ACC_USE) != 0 {
             rwu.used = true;
         }
 
-        self.rwu_table.assign_unpacked(idx, rwu);
+        self.rwu_table.set(ln, var, rwu);
     }
 
     fn compute(&mut self, body: &hir::Body<'_>, hir_id: HirId) -> LiveNode {
@@ -906,7 +746,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         };
 
         // Propagate through calls to the closure.
-        let mut first_merge = true;
         loop {
             self.init_from_succ(self.closure_ln, succ);
             for param in body.params {
@@ -916,10 +755,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                 })
             }
 
-            if !self.merge_from_succ(self.exit_ln, self.closure_ln, first_merge) {
+            if !self.merge_from_succ(self.exit_ln, self.closure_ln) {
                 break;
             }
-            first_merge = false;
             assert_eq!(succ, self.propagate_through_expr(&body.value, self.exit_ln));
         }
 
@@ -1025,7 +863,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                 //
                 let ln = self.live_node(expr.hir_id, expr.span);
                 self.init_empty(ln, succ);
-                let mut first_merge = true;
                 for arm in arms {
                     let body_succ = self.propagate_through_expr(&arm.body, succ);
 
@@ -1034,8 +871,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                         body_succ,
                     );
                     let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ);
-                    self.merge_from_succ(ln, arm_succ, first_merge);
-                    first_merge = false;
+                    self.merge_from_succ(ln, arm_succ);
                 }
                 self.propagate_through_expr(&e, ln)
             }
@@ -1146,7 +982,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
 
                 let ln = self.live_node(expr.hir_id, expr.span);
                 self.init_from_succ(ln, succ);
-                self.merge_from_succ(ln, r_succ, false);
+                self.merge_from_succ(ln, r_succ);
 
                 self.propagate_through_expr(&l, ln)
             }
@@ -1174,7 +1010,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                 };
 
                 // Do a first pass for writing outputs only
-                for op in asm.operands.iter().rev() {
+                for (op, _op_sp) in asm.operands.iter().rev() {
                     match op {
                         hir::InlineAsmOperand::In { .. }
                         | hir::InlineAsmOperand::Const { .. }
@@ -1197,7 +1033,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
 
                 // Then do a second pass for inputs
                 let mut succ = succ;
-                for op in asm.operands.iter().rev() {
+                for (op, _op_sp) in asm.operands.iter().rev() {
                     match op {
                         hir::InlineAsmOperand::In { expr, .. }
                         | hir::InlineAsmOperand::Const { expr, .. }
@@ -1390,7 +1226,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         */
 
         // first iteration:
-        let mut first_merge = true;
         let ln = self.live_node(expr.hir_id, expr.span);
         self.init_empty(ln, succ);
         debug!("propagate_through_loop: using id for loop body {} {:?}", expr.hir_id, body);
@@ -1402,8 +1237,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         let body_ln = self.propagate_through_block(body, ln);
 
         // repeat until fixed point is reached:
-        while self.merge_from_succ(ln, body_ln, first_merge) {
-            first_merge = false;
+        while self.merge_from_succ(ln, body_ln) {
             assert_eq!(body_ln, self.propagate_through_block(body, ln));
         }
 
@@ -1454,7 +1288,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
         }
 
         hir::ExprKind::InlineAsm(ref asm) => {
-            for op in asm.operands {
+            for (op, _op_sp) in asm.operands {
                 match op {
                     hir::InlineAsmOperand::Out { expr, .. } => {
                         if let Some(expr) = expr {
@@ -1575,7 +1409,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
                 ty::UpvarCapture::ByRef(..) => continue,
             };
             if self.used_on_entry(entry_ln, var) {
-                if self.live_on_entry(entry_ln, var).is_none() {
+                if !self.live_on_entry(entry_ln, var) {
                     if let Some(name) = self.should_warn(var) {
                         self.ir.tcx.struct_span_lint_hir(
                             lint::builtin::UNUSED_ASSIGNMENTS,
@@ -1609,7 +1443,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
     fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) {
         for p in body.params {
             self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
-                if self.live_on_entry(ln, var).is_none() {
+                if !self.live_on_entry(ln, var) {
                     self.report_unsed_assign(hir_id, spans, var, |name| {
                         format!("value passed to `{}` is never read", name)
                     });
@@ -1658,7 +1492,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
             // {ret}`, there is only one node, so asking about
             // assigned_on_exit() is not meaningful.
             let is_assigned =
-                if ln == self.exit_ln { false } else { self.assigned_on_exit(ln, var).is_some() };
+                if ln == self.exit_ln { false } else { self.assigned_on_exit(ln, var) };
 
             if is_assigned {
                 self.ir.tcx.struct_span_lint_hir(
@@ -1725,7 +1559,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
     }
 
     fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
-        if self.live_on_exit(ln, var).is_none() {
+        if !self.live_on_exit(ln, var) {
             self.report_unsed_assign(hir_id, spans, var, |name| {
                 format!("value assigned to `{}` is never read", name)
             });
diff --git a/compiler/rustc_passes/src/liveness/rwu_table.rs b/compiler/rustc_passes/src/liveness/rwu_table.rs
new file mode 100644
index 00000000000..a1a6f27398e
--- /dev/null
+++ b/compiler/rustc_passes/src/liveness/rwu_table.rs
@@ -0,0 +1,144 @@
+use crate::liveness::{LiveNode, Variable};
+
+#[derive(Clone, Copy)]
+pub(super) struct RWU {
+    pub(super) reader: bool,
+    pub(super) writer: bool,
+    pub(super) used: bool,
+}
+
+/// Conceptually, this is like a `Vec<Vec<RWU>>`. But the number of
+/// RWU`s can get very large, so it uses a more compact representation.
+pub(super) struct RWUTable {
+    /// Total number of live nodes.
+    live_nodes: usize,
+    /// Total number of variables.
+    vars: usize,
+
+    /// A compressed representation of `RWU`s.
+    ///
+    /// Each word represents 2 different `RWU`s packed together. Each packed RWU
+    /// is stored in 4 bits: a reader bit, a writer bit, a used bit and a
+    /// padding bit.
+    ///
+    /// The data for each live node is contiguous and starts at a word boundary,
+    /// so there might be an unused space left.
+    words: Vec<u8>,
+    /// Number of words per each live node.
+    live_node_words: usize,
+}
+
+impl RWUTable {
+    const RWU_READER: u8 = 0b0001;
+    const RWU_WRITER: u8 = 0b0010;
+    const RWU_USED: u8 = 0b0100;
+    const RWU_MASK: u8 = 0b1111;
+
+    /// Size of packed RWU in bits.
+    const RWU_BITS: usize = 4;
+    /// Size of a word in bits.
+    const WORD_BITS: usize = std::mem::size_of::<u8>() * 8;
+    /// Number of packed RWUs that fit into a single word.
+    const WORD_RWU_COUNT: usize = Self::WORD_BITS / Self::RWU_BITS;
+
+    pub(super) fn new(live_nodes: usize, vars: usize) -> RWUTable {
+        let live_node_words = (vars + Self::WORD_RWU_COUNT - 1) / Self::WORD_RWU_COUNT;
+        Self { live_nodes, vars, live_node_words, words: vec![0u8; live_node_words * live_nodes] }
+    }
+
+    fn word_and_shift(&self, ln: LiveNode, var: Variable) -> (usize, u32) {
+        assert!(ln.index() < self.live_nodes);
+        assert!(var.index() < self.vars);
+
+        let var = var.index();
+        let word = var / Self::WORD_RWU_COUNT;
+        let shift = Self::RWU_BITS * (var % Self::WORD_RWU_COUNT);
+        (ln.index() * self.live_node_words + word, shift as u32)
+    }
+
+    fn pick2_rows_mut(&mut self, a: LiveNode, b: LiveNode) -> (&mut [u8], &mut [u8]) {
+        assert!(a.index() < self.live_nodes);
+        assert!(b.index() < self.live_nodes);
+        assert!(a != b);
+
+        let a_start = a.index() * self.live_node_words;
+        let b_start = b.index() * self.live_node_words;
+
+        unsafe {
+            let ptr = self.words.as_mut_ptr();
+            (
+                std::slice::from_raw_parts_mut(ptr.add(a_start), self.live_node_words),
+                std::slice::from_raw_parts_mut(ptr.add(b_start), self.live_node_words),
+            )
+        }
+    }
+
+    pub(super) fn copy(&mut self, dst: LiveNode, src: LiveNode) {
+        if dst == src {
+            return;
+        }
+
+        let (dst_row, src_row) = self.pick2_rows_mut(dst, src);
+        dst_row.copy_from_slice(src_row);
+    }
+
+    /// Sets `dst` to the union of `dst` and `src`, returns true if `dst` was
+    /// changed.
+    pub(super) fn union(&mut self, dst: LiveNode, src: LiveNode) -> bool {
+        if dst == src {
+            return false;
+        }
+
+        let mut changed = false;
+        let (dst_row, src_row) = self.pick2_rows_mut(dst, src);
+        for (dst_word, src_word) in dst_row.iter_mut().zip(src_row.iter()) {
+            let old = *dst_word;
+            let new = *dst_word | src_word;
+            *dst_word = new;
+            changed |= old != new;
+        }
+        changed
+    }
+
+    pub(super) fn get_reader(&self, ln: LiveNode, var: Variable) -> bool {
+        let (word, shift) = self.word_and_shift(ln, var);
+        (self.words[word] >> shift) & Self::RWU_READER != 0
+    }
+
+    pub(super) fn get_writer(&self, ln: LiveNode, var: Variable) -> bool {
+        let (word, shift) = self.word_and_shift(ln, var);
+        (self.words[word] >> shift) & Self::RWU_WRITER != 0
+    }
+
+    pub(super) fn get_used(&self, ln: LiveNode, var: Variable) -> bool {
+        let (word, shift) = self.word_and_shift(ln, var);
+        (self.words[word] >> shift) & Self::RWU_USED != 0
+    }
+
+    pub(super) fn get(&self, ln: LiveNode, var: Variable) -> RWU {
+        let (word, shift) = self.word_and_shift(ln, var);
+        let rwu_packed = self.words[word] >> shift;
+        RWU {
+            reader: rwu_packed & Self::RWU_READER != 0,
+            writer: rwu_packed & Self::RWU_WRITER != 0,
+            used: rwu_packed & Self::RWU_USED != 0,
+        }
+    }
+
+    pub(super) fn set(&mut self, ln: LiveNode, var: Variable, rwu: RWU) {
+        let mut packed = 0;
+        if rwu.reader {
+            packed |= Self::RWU_READER;
+        }
+        if rwu.writer {
+            packed |= Self::RWU_WRITER;
+        }
+        if rwu.used {
+            packed |= Self::RWU_USED;
+        }
+
+        let (word, shift) = self.word_and_shift(ln, var);
+        let word = &mut self.words[word];
+        *word = (*word & !(Self::RWU_MASK << shift)) | (packed << shift)
+    }
+}
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs
new file mode 100644
index 00000000000..5b50ef8627b
--- /dev/null
+++ b/compiler/rustc_passes/src/naked_functions.rs
@@ -0,0 +1,322 @@
+//! Checks validity of naked functions.
+
+use rustc_ast::InlineAsmOptions;
+use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::{ErasedMap, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::{ExprKind, HirId, InlineAsmOperand, StmtKind};
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::lint::builtin::UNSUPPORTED_NAKED_FUNCTIONS;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+use rustc_target::spec::abi::Abi;
+
+fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
+    tcx.hir().visit_item_likes_in_module(
+        module_def_id,
+        &mut CheckNakedFunctions { tcx }.as_deep_visitor(),
+    );
+}
+
+crate fn provide(providers: &mut Providers) {
+    *providers = Providers { check_mod_naked_functions, ..*providers };
+}
+
+struct CheckNakedFunctions<'tcx> {
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> {
+    type Map = ErasedMap<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_fn(
+        &mut self,
+        fk: FnKind<'v>,
+        _fd: &'tcx hir::FnDecl<'tcx>,
+        body_id: hir::BodyId,
+        span: Span,
+        hir_id: HirId,
+    ) {
+        let ident_span;
+        let fn_header;
+
+        match fk {
+            FnKind::Closure(..) => {
+                // Closures with a naked attribute are rejected during attribute
+                // check. Don't validate them any further.
+                return;
+            }
+            FnKind::ItemFn(ident, _, ref header, ..) => {
+                ident_span = ident.span;
+                fn_header = header;
+            }
+
+            FnKind::Method(ident, ref sig, ..) => {
+                ident_span = ident.span;
+                fn_header = &sig.header;
+            }
+        }
+
+        let naked = fk.attrs().iter().any(|attr| attr.has_name(sym::naked));
+        if naked {
+            let body = self.tcx.hir().body(body_id);
+            check_abi(self.tcx, hir_id, fn_header.abi, ident_span);
+            check_no_patterns(self.tcx, body.params);
+            check_no_parameters_use(self.tcx, body);
+            check_asm(self.tcx, hir_id, body, span);
+        }
+    }
+}
+
+/// Checks that function uses non-Rust ABI.
+fn check_abi(tcx: TyCtxt<'_>, hir_id: HirId, abi: Abi, fn_ident_span: Span) {
+    if abi == Abi::Rust {
+        tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, fn_ident_span, |lint| {
+            lint.build("Rust ABI is unsupported in naked functions").emit();
+        });
+    }
+}
+
+/// Checks that parameters don't use patterns. Mirrors the checks for function declarations.
+fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
+    for param in params {
+        match param.pat.kind {
+            hir::PatKind::Wild
+            | hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, _, None) => {}
+            _ => {
+                tcx.sess
+                    .struct_span_err(
+                        param.pat.span,
+                        "patterns not allowed in naked function parameters",
+                    )
+                    .emit();
+            }
+        }
+    }
+}
+
+/// Checks that function parameters aren't used in the function body.
+fn check_no_parameters_use<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) {
+    let mut params = hir::HirIdSet::default();
+    for param in body.params {
+        param.pat.each_binding(|_binding_mode, hir_id, _span, _ident| {
+            params.insert(hir_id);
+        });
+    }
+    CheckParameters { tcx, params }.visit_body(body);
+}
+
+struct CheckParameters<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    params: hir::HirIdSet,
+}
+
+impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
+    type Map = ErasedMap<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+        if let hir::ExprKind::Path(hir::QPath::Resolved(
+            _,
+            hir::Path { res: hir::def::Res::Local(var_hir_id), .. },
+        )) = expr.kind
+        {
+            if self.params.contains(var_hir_id) {
+                self.tcx
+                    .sess
+                    .struct_span_err(
+                        expr.span,
+                        "referencing function parameters is not allowed in naked functions",
+                    )
+                    .help("follow the calling convention in asm block to use parameters")
+                    .emit();
+                return;
+            }
+        }
+        hir::intravisit::walk_expr(self, expr);
+    }
+}
+
+/// Checks that function body contains a single inline assembly block.
+fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, hir_id: HirId, body: &'tcx hir::Body<'tcx>, fn_span: Span) {
+    let mut this = CheckInlineAssembly { tcx, items: Vec::new() };
+    this.visit_body(body);
+    if let &[(ItemKind::Asm, _)] = &this.items[..] {
+        // Ok.
+    } else {
+        tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, fn_span, |lint| {
+            let mut diag = lint.build("naked functions must contain a single asm block");
+            let mut has_asm = false;
+            for &(kind, span) in &this.items {
+                match kind {
+                    ItemKind::Asm if has_asm => {
+                        diag.span_label(
+                            span,
+                            "multiple asm blocks are unsupported in naked functions",
+                        );
+                    }
+                    ItemKind::Asm => has_asm = true,
+                    ItemKind::NonAsm => {
+                        diag.span_label(span, "non-asm is unsupported in naked functions");
+                    }
+                }
+            }
+            diag.emit();
+        });
+    }
+}
+
+struct CheckInlineAssembly<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    items: Vec<(ItemKind, Span)>,
+}
+
+#[derive(Copy, Clone)]
+enum ItemKind {
+    Asm,
+    NonAsm,
+}
+
+impl<'tcx> CheckInlineAssembly<'tcx> {
+    fn check_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) {
+        match expr.kind {
+            ExprKind::Box(..)
+            | ExprKind::ConstBlock(..)
+            | ExprKind::Array(..)
+            | ExprKind::Call(..)
+            | ExprKind::MethodCall(..)
+            | ExprKind::Tup(..)
+            | ExprKind::Binary(..)
+            | ExprKind::Unary(..)
+            | ExprKind::Lit(..)
+            | ExprKind::Cast(..)
+            | ExprKind::Type(..)
+            | ExprKind::Loop(..)
+            | ExprKind::Match(..)
+            | ExprKind::Closure(..)
+            | ExprKind::Assign(..)
+            | ExprKind::AssignOp(..)
+            | ExprKind::Field(..)
+            | ExprKind::Index(..)
+            | ExprKind::Path(..)
+            | ExprKind::AddrOf(..)
+            | ExprKind::Break(..)
+            | ExprKind::Continue(..)
+            | ExprKind::Ret(..)
+            | ExprKind::Struct(..)
+            | ExprKind::Repeat(..)
+            | ExprKind::Yield(..) => {
+                self.items.push((ItemKind::NonAsm, span));
+            }
+
+            ExprKind::InlineAsm(ref asm) => {
+                self.items.push((ItemKind::Asm, span));
+                self.check_inline_asm(expr.hir_id, asm, span);
+            }
+
+            ExprKind::LlvmInlineAsm(..) => {
+                self.items.push((ItemKind::Asm, span));
+                self.tcx.struct_span_lint_hir(
+                    UNSUPPORTED_NAKED_FUNCTIONS,
+                    expr.hir_id,
+                    span,
+                    |lint| {
+                        lint.build(
+                            "the LLVM-style inline assembly is unsupported in naked functions",
+                        )
+                        .help("use the new asm! syntax specified in RFC 2873")
+                        .emit();
+                    },
+                );
+            }
+
+            ExprKind::DropTemps(..) | ExprKind::Block(..) | ExprKind::Err => {
+                hir::intravisit::walk_expr(self, expr);
+            }
+        }
+    }
+
+    fn check_inline_asm(&self, hir_id: HirId, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) {
+        let unsupported_operands: Vec<Span> = asm
+            .operands
+            .iter()
+            .filter_map(|&(ref op, op_sp)| match op {
+                InlineAsmOperand::Const { .. } | InlineAsmOperand::Sym { .. } => None,
+                InlineAsmOperand::In { .. }
+                | InlineAsmOperand::Out { .. }
+                | InlineAsmOperand::InOut { .. }
+                | InlineAsmOperand::SplitInOut { .. } => Some(op_sp),
+            })
+            .collect();
+        if !unsupported_operands.is_empty() {
+            self.tcx.struct_span_lint_hir(
+                UNSUPPORTED_NAKED_FUNCTIONS,
+                hir_id,
+                unsupported_operands,
+                |lint| {
+                    lint.build("only `const` and `sym` operands are supported in naked functions")
+                        .emit();
+                },
+            );
+        }
+
+        let unsupported_options: Vec<&'static str> = [
+            (InlineAsmOptions::NOMEM, "`nomem`"),
+            (InlineAsmOptions::NOSTACK, "`nostack`"),
+            (InlineAsmOptions::PRESERVES_FLAGS, "`preserves_flags`"),
+            (InlineAsmOptions::PURE, "`pure`"),
+            (InlineAsmOptions::READONLY, "`readonly`"),
+        ]
+        .iter()
+        .filter_map(|&(option, name)| if asm.options.contains(option) { Some(name) } else { None })
+        .collect();
+
+        if !unsupported_options.is_empty() {
+            self.tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, span, |lint| {
+                lint.build(&format!(
+                    "asm options unsupported in naked functions: {}",
+                    unsupported_options.join(", ")
+                ))
+                .emit();
+            });
+        }
+
+        if !asm.options.contains(InlineAsmOptions::NORETURN) {
+            self.tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, span, |lint| {
+                lint.build("asm in naked functions must use `noreturn` option").emit();
+            });
+        }
+    }
+}
+
+impl<'tcx> Visitor<'tcx> for CheckInlineAssembly<'tcx> {
+    type Map = ErasedMap<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
+        match stmt.kind {
+            StmtKind::Item(..) => {}
+            StmtKind::Local(..) => {
+                self.items.push((ItemKind::NonAsm, stmt.span));
+            }
+            StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
+                self.check_expr(expr, stmt.span);
+            }
+        }
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+        self.check_expr(&expr, expr.span);
+    }
+}
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index 8d5c980609c..fde83af99a5 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -262,7 +262,7 @@ impl<'tcx> ReachableContext<'tcx> {
                     | hir::ItemKind::TyAlias(..)
                     | hir::ItemKind::Static(..)
                     | hir::ItemKind::Mod(..)
-                    | hir::ItemKind::ForeignMod(..)
+                    | hir::ItemKind::ForeignMod { .. }
                     | hir::ItemKind::Impl { .. }
                     | hir::ItemKind::Trait(..)
                     | hir::ItemKind::TraitAlias(..)
@@ -378,6 +378,10 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx
     fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {
         // processed in visit_item above
     }
+
+    fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {
+        // We never export foreign functions as they have no body to export.
+    }
 }
 
 fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, crate_num: CrateNum) -> FxHashSet<LocalDefId> {
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 04b5c65e464..f6bbbd80bf1 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -326,7 +326,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
             // they don't have their own stability. They still can be annotated as unstable
             // and propagate this unstability to children, but this annotation is completely
             // optional. They inherit stability from their parents when unannotated.
-            hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod(..) => {
+            hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod { .. } => {
                 self.in_trait_impl = false;
                 kind = AnnotationKind::Container;
             }
@@ -499,7 +499,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
         // optional. They inherit stability from their parents when unannotated.
         if !matches!(
             i.kind,
-            hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod(..)
+            hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod { .. }
         ) {
             self.check_missing_stability(i.hir_id, i.span);
         }