about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAmanieu d'Antras <amanieu@gmail.com>2021-08-30 01:23:33 +0100
committercynecx <me@cynecx.net>2021-12-03 23:51:46 +0100
commit940b2eabad13d7781316d077ea4b64d559242f50 (patch)
tree3e5bbda4838f6260a06e1c4e605d5c0dfce42ffc
parent532d2b14c05f9bc20b2d27cbb5f4550d28343a36 (diff)
downloadrust-940b2eabad13d7781316d077ea4b64d559242f50.tar.gz
rust-940b2eabad13d7781316d077ea4b64d559242f50.zip
Add initial AST and MIR support for unwinding from inline assembly
-rw-r--r--compiler/rustc_ast/src/ast.rs3
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs3
-rw-r--r--compiler/rustc_borrowck/src/dataflow.rs6
-rw-r--r--compiler/rustc_borrowck/src/def_use.rs3
-rw-r--r--compiler/rustc_borrowck/src/invalidation.rs1
-rw-r--r--compiler/rustc_borrowck/src/lib.rs1
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/analyze.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs1
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/resolver.rs31
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs5
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs3
-rw-r--r--compiler/rustc_middle/src/mir/terminator.rs44
-rw-r--r--compiler/rustc_middle/src/mir/type_foldable.rs17
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs12
-rw-r--r--compiler/rustc_mir_build/src/build/expr/into.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/scope.rs7
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/direction.rs59
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/graphviz.rs40
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/mod.rs38
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/tests.rs4
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs6
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/init_locals.rs14
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/liveness.rs22
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/mod.rs90
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs20
-rw-r--r--compiler/rustc_mir_dataflow/src/lib.rs6
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/builder.rs1
-rw-r--r--compiler/rustc_mir_transform/src/check_unsafety.rs1
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs3
-rw-r--r--compiler/rustc_mir_transform/src/dest_prop.rs1
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs13
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--src/test/ui/asm/aarch64/bad-options.stderr24
-rw-r--r--src/test/ui/asm/aarch64/parse-error.stderr20
-rw-r--r--src/test/ui/asm/x86_64/bad-options.stderr24
-rw-r--r--src/test/ui/asm/x86_64/parse-error.stderr20
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_clone.rs8
39 files changed, 355 insertions, 212 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 4d4c6217ad0..03b957adeda 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1981,7 +1981,7 @@ pub enum InlineAsmRegOrRegClass {
 
 bitflags::bitflags! {
     #[derive(Encodable, Decodable, HashStable_Generic)]
-    pub struct InlineAsmOptions: u8 {
+    pub struct InlineAsmOptions: u16 {
         const PURE = 1 << 0;
         const NOMEM = 1 << 1;
         const READONLY = 1 << 2;
@@ -1990,6 +1990,7 @@ bitflags::bitflags! {
         const NOSTACK = 1 << 5;
         const ATT_SYNTAX = 1 << 6;
         const RAW = 1 << 7;
+        const MAY_UNWIND = 1 << 8;
     }
 }
 
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index f1f2387866d..0dc1f093947 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -2338,6 +2338,9 @@ impl<'a> State<'a> {
                 if opts.contains(InlineAsmOptions::RAW) {
                     options.push("raw");
                 }
+                if opts.contains(InlineAsmOptions::MAY_UNWIND) {
+                    options.push("may_unwind");
+                }
                 s.commasep(Inconsistent, &options, |s, &opt| {
                     s.word(opt);
                 });
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index 7db8d4520d4..15372ec1534 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -5,7 +5,7 @@ use rustc_middle::ty::RegionVid;
 use rustc_middle::ty::TyCtxt;
 use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
 use rustc_mir_dataflow::ResultsVisitable;
-use rustc_mir_dataflow::{self, fmt::DebugWithContext, GenKill};
+use rustc_mir_dataflow::{self, fmt::DebugWithContext, CallReturnPlaces, GenKill};
 use rustc_mir_dataflow::{Analysis, Direction, Results};
 use std::fmt;
 use std::iter;
@@ -434,9 +434,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
         &self,
         _trans: &mut impl GenKill<Self::Idx>,
         _block: mir::BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        _dest_place: mir::Place<'tcx>,
+        _return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
     }
 }
diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs
index 689ec249a2f..70acbc9ee2d 100644
--- a/compiler/rustc_borrowck/src/def_use.rs
+++ b/compiler/rustc_borrowck/src/def_use.rs
@@ -17,7 +17,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
         PlaceContext::MutatingUse(MutatingUseContext::Store) |
 
         // This is potentially both a def and a use...
-        PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
+        PlaceContext::MutatingUse(MutatingUseContext::LlvmAsmOutput) |
 
         // We let Call define the result in both the success and
         // unwind cases. This is not really correct, however it
@@ -26,6 +26,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
         // the def in call only to the input from the success
         // path and not the unwind path. -nmatsakis
         PlaceContext::MutatingUse(MutatingUseContext::Call) |
+        PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
         PlaceContext::MutatingUse(MutatingUseContext::Yield) |
 
         // Storage live and storage dead aren't proper defines, but we can ignore
diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs
index efd34f4e0a5..c03e4d8a448 100644
--- a/compiler/rustc_borrowck/src/invalidation.rs
+++ b/compiler/rustc_borrowck/src/invalidation.rs
@@ -199,6 +199,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
                 options: _,
                 line_spans: _,
                 destination: _,
+                cleanup: _,
             } => {
                 for op in operands {
                     match *op {
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index ceaae9d66cd..88fab269109 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -791,6 +791,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
                 options: _,
                 line_spans: _,
                 destination: _,
+                cleanup: _,
             } => {
                 for op in operands {
                     match *op {
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index da26d9c7b87..6a263bd63ad 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1828,10 +1828,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     self.assert_iscleanup(body, block_data, unwind, true);
                 }
             }
-            TerminatorKind::InlineAsm { destination, .. } => {
+            TerminatorKind::InlineAsm { destination, cleanup, .. } => {
                 if let Some(target) = destination {
                     self.assert_iscleanup(body, block_data, target, is_cleanup);
                 }
+                if let Some(cleanup) = cleanup {
+                    if is_cleanup {
+                        span_mirbug!(self, block_data, "cleanup on cleanup block")
+                    }
+                    self.assert_iscleanup(body, block_data, cleanup, true);
+                }
             }
         }
     }
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 41662f46f11..b374769cbea 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -420,6 +420,8 @@ fn parse_options<'a>(
             try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX);
         } else if p.eat_keyword(kw::Raw) {
             try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::RAW);
+        } else if p.eat_keyword(sym::may_unwind) {
+            try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::MAY_UNWIND);
         } else {
             return p.unexpected();
         }
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index 8d75b2e7a3d..c486c19f9c9 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -211,6 +211,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
 
             PlaceContext::MutatingUse(
                 MutatingUseContext::Store
+                | MutatingUseContext::LlvmAsmOutput
                 | MutatingUseContext::AsmOutput
                 | MutatingUseContext::Borrow
                 | MutatingUseContext::AddressOf
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index c8f388bfa1d..495fe13f18c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1041,6 +1041,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 options,
                 line_spans,
                 destination,
+                cleanup: _, // TODO
             } => {
                 self.codegen_asm_terminator(
                     helper,
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
index b70b38754c9..249b4ea0602 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
@@ -7,6 +7,7 @@ use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::{self, BasicBlock, Local, Location, Statement, StatementKind};
 use rustc_mir_dataflow::fmt::DebugWithContext;
 use rustc_mir_dataflow::JoinSemiLattice;
+use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces};
 use rustc_span::DUMMY_SP;
 
 use std::fmt;
@@ -80,18 +81,18 @@ where
     fn apply_call_return_effect(
         &mut self,
         _block: BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        return_place: mir::Place<'tcx>,
+        return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
-        // We cannot reason about another function's internals, so use conservative type-based
-        // qualification for the result of a function call.
-        let return_ty = return_place.ty(self.ccx.body, self.ccx.tcx).ty;
-        let qualif = Q::in_any_value_of_ty(self.ccx, return_ty);
+        return_places.for_each(|place| {
+            // We cannot reason about another function's internals, so use conservative type-based
+            // qualification for the result of a function call.
+            let return_ty = place.ty(self.ccx.body, self.ccx.tcx).ty;
+            let qualif = Q::in_any_value_of_ty(self.ccx, return_ty);
 
-        if !return_place.is_indirect() {
-            self.assign_qualif_direct(&return_place, qualif);
-        }
+            if !place.is_indirect() {
+                self.assign_qualif_direct(&place, qualif);
+            }
+        });
     }
 
     fn address_of_allows_mutation(&self, _mt: mir::Mutability, _place: mir::Place<'tcx>) -> bool {
@@ -329,7 +330,7 @@ impl JoinSemiLattice for State {
     }
 }
 
-impl<Q> rustc_mir_dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
+impl<Q> AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
 where
     Q: Qualif,
 {
@@ -349,7 +350,7 @@ where
     }
 }
 
-impl<Q> rustc_mir_dataflow::Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
+impl<Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
 where
     Q: Qualif,
 {
@@ -375,10 +376,8 @@ where
         &self,
         state: &mut Self::Domain,
         block: BasicBlock,
-        func: &mir::Operand<'tcx>,
-        args: &[mir::Operand<'tcx>],
-        return_place: mir::Place<'tcx>,
+        return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
-        self.transfer_function(state).apply_call_return_effect(block, func, args, return_place)
+        self.transfer_function(state).apply_call_return_effect(block, return_places)
     }
 }
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index b4bb6390db4..448a04f76b1 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -495,10 +495,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     self.check_edge(location, *unwind, EdgeKind::Unwind);
                 }
             }
-            TerminatorKind::InlineAsm { destination, .. } => {
+            TerminatorKind::InlineAsm { destination, cleanup, .. } => {
                 if let Some(destination) = destination {
                     self.check_edge(location, *destination, EdgeKind::Normal);
                 }
+                if let Some(cleanup) = cleanup {
+                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
+                }
             }
             // Nothing to validate for these.
             TerminatorKind::Resume
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 9c2927111a6..18bc5bb64d6 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1433,6 +1433,9 @@ impl<'a> State<'a> {
                 if opts.contains(ast::InlineAsmOptions::RAW) {
                     options.push("raw");
                 }
+                if opts.contains(ast::InlineAsmOptions::MAY_UNWIND) {
+                    options.push("may_unwind");
+                }
                 s.commasep(Inconsistent, &options, |s, &opt| {
                     s.word(opt);
                 });
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index e78b6fd092d..a82f98d28e7 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -260,6 +260,10 @@ pub enum TerminatorKind<'tcx> {
         /// Destination block after the inline assembly returns, unless it is
         /// diverging (InlineAsmOptions::NORETURN).
         destination: Option<BasicBlock>,
+
+        /// Cleanup to be done if the inline assembly unwinds. This is present
+        /// if and only if InlineAsmOptions::MAY_UNWIND is set.
+        cleanup: Option<BasicBlock>,
     },
 }
 #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
@@ -309,7 +313,7 @@ impl<'tcx> TerminatorKind<'tcx> {
             | Return
             | Unreachable
             | Call { destination: None, cleanup: None, .. }
-            | InlineAsm { destination: None, .. } => None.into_iter().chain(&[]),
+            | InlineAsm { destination: None, cleanup: None, .. } => None.into_iter().chain(&[]),
             Goto { target: ref t }
             | Call { destination: None, cleanup: Some(ref t), .. }
             | Call { destination: Some((_, ref t)), cleanup: None, .. }
@@ -318,13 +322,17 @@ impl<'tcx> TerminatorKind<'tcx> {
             | Drop { target: ref t, unwind: None, .. }
             | Assert { target: ref t, cleanup: None, .. }
             | FalseUnwind { real_target: ref t, unwind: None }
-            | InlineAsm { destination: Some(ref t), .. } => Some(t).into_iter().chain(&[]),
+            | InlineAsm { destination: Some(ref t), cleanup: None, .. }
+            | InlineAsm { destination: None, cleanup: Some(ref t), .. } => {
+                Some(t).into_iter().chain(&[])
+            }
             Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. }
             | Yield { resume: ref t, drop: Some(ref u), .. }
             | DropAndReplace { target: ref t, unwind: Some(ref u), .. }
             | Drop { target: ref t, unwind: Some(ref u), .. }
             | Assert { target: ref t, cleanup: Some(ref u), .. }
-            | FalseUnwind { real_target: ref t, unwind: Some(ref u) } => {
+            | FalseUnwind { real_target: ref t, unwind: Some(ref u) }
+            | InlineAsm { destination: Some(ref t), cleanup: Some(ref u), .. } => {
                 Some(t).into_iter().chain(slice::from_ref(u))
             }
             SwitchInt { ref targets, .. } => None.into_iter().chain(&targets.targets[..]),
@@ -343,7 +351,7 @@ impl<'tcx> TerminatorKind<'tcx> {
             | Return
             | Unreachable
             | Call { destination: None, cleanup: None, .. }
-            | InlineAsm { destination: None, .. } => None.into_iter().chain(&mut []),
+            | InlineAsm { destination: None, cleanup: None, .. } => None.into_iter().chain(&mut []),
             Goto { target: ref mut t }
             | Call { destination: None, cleanup: Some(ref mut t), .. }
             | Call { destination: Some((_, ref mut t)), cleanup: None, .. }
@@ -352,13 +360,17 @@ impl<'tcx> TerminatorKind<'tcx> {
             | Drop { target: ref mut t, unwind: None, .. }
             | Assert { target: ref mut t, cleanup: None, .. }
             | FalseUnwind { real_target: ref mut t, unwind: None }
-            | InlineAsm { destination: Some(ref mut t), .. } => Some(t).into_iter().chain(&mut []),
+            | InlineAsm { destination: Some(ref mut t), cleanup: None, .. }
+            | InlineAsm { destination: None, cleanup: Some(ref mut t), .. } => {
+                Some(t).into_iter().chain(&mut [])
+            }
             Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. }
             | Yield { resume: ref mut t, drop: Some(ref mut u), .. }
             | DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. }
             | Drop { target: ref mut t, unwind: Some(ref mut u), .. }
             | Assert { target: ref mut t, cleanup: Some(ref mut u), .. }
-            | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => {
+            | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) }
+            | InlineAsm { destination: Some(ref mut t), cleanup: Some(ref mut u), .. } => {
                 Some(t).into_iter().chain(slice::from_mut(u))
             }
             SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets[..]),
@@ -378,13 +390,13 @@ impl<'tcx> TerminatorKind<'tcx> {
             | TerminatorKind::GeneratorDrop
             | TerminatorKind::Yield { .. }
             | TerminatorKind::SwitchInt { .. }
-            | TerminatorKind::FalseEdge { .. }
-            | TerminatorKind::InlineAsm { .. } => None,
+            | TerminatorKind::FalseEdge { .. } => None,
             TerminatorKind::Call { cleanup: ref unwind, .. }
             | TerminatorKind::Assert { cleanup: ref unwind, .. }
             | TerminatorKind::DropAndReplace { ref unwind, .. }
             | TerminatorKind::Drop { ref unwind, .. }
-            | TerminatorKind::FalseUnwind { ref unwind, .. } => Some(unwind),
+            | TerminatorKind::FalseUnwind { ref unwind, .. }
+            | TerminatorKind::InlineAsm { cleanup: ref unwind, .. } => Some(unwind),
         }
     }
 
@@ -398,13 +410,13 @@ impl<'tcx> TerminatorKind<'tcx> {
             | TerminatorKind::GeneratorDrop
             | TerminatorKind::Yield { .. }
             | TerminatorKind::SwitchInt { .. }
-            | TerminatorKind::FalseEdge { .. }
-            | TerminatorKind::InlineAsm { .. } => None,
+            | TerminatorKind::FalseEdge { .. } => None,
             TerminatorKind::Call { cleanup: ref mut unwind, .. }
             | TerminatorKind::Assert { cleanup: ref mut unwind, .. }
             | TerminatorKind::DropAndReplace { ref mut unwind, .. }
             | TerminatorKind::Drop { ref mut unwind, .. }
-            | TerminatorKind::FalseUnwind { ref mut unwind, .. } => Some(unwind),
+            | TerminatorKind::FalseUnwind { ref mut unwind, .. }
+            | TerminatorKind::InlineAsm { cleanup: ref mut unwind, .. } => Some(unwind),
         }
     }
 
@@ -583,8 +595,12 @@ impl<'tcx> TerminatorKind<'tcx> {
             FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
             FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()],
             FalseUnwind { unwind: None, .. } => vec!["real".into()],
-            InlineAsm { destination: Some(_), .. } => vec!["".into()],
-            InlineAsm { destination: None, .. } => vec![],
+            InlineAsm { destination: Some(_), cleanup: Some(_), .. } => {
+                vec!["return".into(), "unwind".into()]
+            }
+            InlineAsm { destination: Some(_), cleanup: None, .. } => vec!["return".into()],
+            InlineAsm { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()],
+            InlineAsm { destination: None, cleanup: None, .. } => vec![],
         }
     }
 }
diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs
index ad8b9d323ee..901f3bf4f7d 100644
--- a/compiler/rustc_middle/src/mir/type_foldable.rs
+++ b/compiler/rustc_middle/src/mir/type_foldable.rs
@@ -84,13 +84,16 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
                 FalseEdge { real_target, imaginary_target }
             }
             FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind },
-            InlineAsm { template, operands, options, line_spans, destination } => InlineAsm {
-                template,
-                operands: operands.try_fold_with(folder)?,
-                options,
-                line_spans,
-                destination,
-            },
+            InlineAsm { template, operands, options, line_spans, destination, cleanup } => {
+                InlineAsm {
+                    template,
+                    operands: operands.try_fold_with(folder)?,
+                    options,
+                    line_spans,
+                    destination,
+                    cleanup,
+                }
+            }
         };
         Ok(Terminator { source_info: self.source_info, kind })
     }
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 4c23ab49fa2..d783b6330e8 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -412,7 +412,7 @@ macro_rules! make_mir_visitor {
                         for output in & $($mutability)? asm.outputs[..] {
                             self.visit_place(
                                 output,
-                                PlaceContext::MutatingUse(MutatingUseContext::AsmOutput),
+                                PlaceContext::MutatingUse(MutatingUseContext::LlvmAsmOutput),
                                 location
                             );
                         }
@@ -581,6 +581,7 @@ macro_rules! make_mir_visitor {
                         options: _,
                         line_spans: _,
                         destination: _,
+                        cleanup: _,
                     } => {
                         for op in operands {
                             match op {
@@ -590,7 +591,7 @@ macro_rules! make_mir_visitor {
                                 InlineAsmOperand::Out { place: Some(place), .. } => {
                                     self.visit_place(
                                         place,
-                                        PlaceContext::MutatingUse(MutatingUseContext::Store),
+                                        PlaceContext::MutatingUse(MutatingUseContext::AsmOutput),
                                         location,
                                     );
                                 }
@@ -599,7 +600,7 @@ macro_rules! make_mir_visitor {
                                     if let Some(out_place) = out_place {
                                         self.visit_place(
                                             out_place,
-                                            PlaceContext::MutatingUse(MutatingUseContext::Store),
+                                            PlaceContext::MutatingUse(MutatingUseContext::AsmOutput),
                                             location,
                                         );
                                     }
@@ -1178,8 +1179,10 @@ pub enum MutatingUseContext {
     /// Appears as LHS of an assignment.
     Store,
     /// Can often be treated as a `Store`, but needs to be separate because
-    /// ASM is allowed to read outputs as well, so a `Store`-`AsmOutput` sequence
+    /// ASM is allowed to read outputs as well, so a `Store`-`LlvmAsmOutput` sequence
     /// cannot be simplified the way a `Store`-`Store` can be.
+    LlvmAsmOutput,
+    /// Output operand of an inline assembly block.
     AsmOutput,
     /// Destination of a call.
     Call,
@@ -1268,6 +1271,7 @@ impl PlaceContext {
             PlaceContext::MutatingUse(
                 MutatingUseContext::Store
                     | MutatingUseContext::Call
+                    | MutatingUseContext::LlvmAsmOutput
                     | MutatingUseContext::AsmOutput,
             )
         )
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
index 53868f28557..abec67af08b 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -467,8 +467,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         } else {
                             Some(destination_block)
                         },
+                        cleanup: None,
                     },
                 );
+                if options.contains(InlineAsmOptions::MAY_UNWIND) {
+                    this.diverge_from(block);
+                }
                 destination_block.unit()
             }
 
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs
index 8dadbf5f02b..fc46c54c2fc 100644
--- a/compiler/rustc_mir_build/src/build/scope.rs
+++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -1034,6 +1034,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     | TerminatorKind::Call { .. }
                     | TerminatorKind::DropAndReplace { .. }
                     | TerminatorKind::FalseUnwind { .. }
+                    | TerminatorKind::InlineAsm { .. }
             ),
             "diverge_from called on block with terminator that cannot unwind."
         );
@@ -1373,7 +1374,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
             | TerminatorKind::DropAndReplace { unwind, .. }
             | TerminatorKind::FalseUnwind { unwind, .. }
             | TerminatorKind::Call { cleanup: unwind, .. }
-            | TerminatorKind::Assert { cleanup: unwind, .. } => {
+            | TerminatorKind::Assert { cleanup: unwind, .. }
+            | TerminatorKind::InlineAsm { cleanup: unwind, .. } => {
                 *unwind = Some(to);
             }
             TerminatorKind::Goto { .. }
@@ -1384,8 +1386,7 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
             | TerminatorKind::Unreachable
             | TerminatorKind::Yield { .. }
             | TerminatorKind::GeneratorDrop
-            | TerminatorKind::FalseEdge { .. }
-            | TerminatorKind::InlineAsm { .. } => {
+            | TerminatorKind::FalseEdge { .. } => {
                 span_bug!(term.source_info.span, "cannot unwind from {:?}", term.kind)
             }
         }
diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs
index 8a9ced91eb3..6131ee79818 100644
--- a/compiler/rustc_mir_dataflow/src/framework/direction.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs
@@ -4,7 +4,9 @@ use rustc_middle::ty::TyCtxt;
 use std::ops::RangeInclusive;
 
 use super::visitor::{ResultsVisitable, ResultsVisitor};
-use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget};
+use super::{
+    Analysis, CallReturnPlaces, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget,
+};
 
 pub trait Direction {
     fn is_forward() -> bool;
@@ -235,14 +237,26 @@ impl Direction for Backward {
                 // Apply terminator-specific edge effects.
                 //
                 // FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
-                mir::TerminatorKind::Call {
-                    destination: Some((return_place, dest)),
-                    ref func,
-                    ref args,
-                    ..
+                mir::TerminatorKind::Call { destination: Some((return_place, dest)), .. }
+                    if dest == bb =>
+                {
+                    let mut tmp = exit_state.clone();
+                    analysis.apply_call_return_effect(
+                        &mut tmp,
+                        pred,
+                        CallReturnPlaces::Call(return_place),
+                    );
+                    propagate(pred, &tmp);
+                }
+                mir::TerminatorKind::InlineAsm {
+                    destination: Some(dest), ref operands, ..
                 } if dest == bb => {
                     let mut tmp = exit_state.clone();
-                    analysis.apply_call_return_effect(&mut tmp, pred, func, args, return_place);
+                    analysis.apply_call_return_effect(
+                        &mut tmp,
+                        pred,
+                        CallReturnPlaces::InlineAsm(operands),
+                    );
                     propagate(pred, &tmp);
                 }
 
@@ -258,6 +272,7 @@ impl Direction for Backward {
                 | mir::TerminatorKind::Drop { unwind: Some(unwind), .. }
                 | mir::TerminatorKind::DropAndReplace { unwind: Some(unwind), .. }
                 | mir::TerminatorKind::FalseUnwind { unwind: Some(unwind), .. }
+                | mir::TerminatorKind::InlineAsm { cleanup: Some(unwind), .. }
                     if unwind == bb =>
                 {
                     if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
@@ -467,7 +482,7 @@ impl Direction for Forward {
                 propagate(target, exit_state);
             }
 
-            Call { cleanup, destination, ref func, ref args, from_hir_call: _, fn_span: _ } => {
+            Call { cleanup, destination, func: _, args: _, from_hir_call: _, fn_span: _ } => {
                 if let Some(unwind) = cleanup {
                     if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
                         propagate(unwind, exit_state);
@@ -477,13 +492,37 @@ impl Direction for Forward {
                 if let Some((dest_place, target)) = destination {
                     // N.B.: This must be done *last*, otherwise the unwind path will see the call
                     // return effect.
-                    analysis.apply_call_return_effect(exit_state, bb, func, args, dest_place);
+                    analysis.apply_call_return_effect(
+                        exit_state,
+                        bb,
+                        CallReturnPlaces::Call(dest_place),
+                    );
                     propagate(target, exit_state);
                 }
             }
 
-            InlineAsm { template: _, operands: _, options: _, line_spans: _, destination } => {
+            InlineAsm {
+                template: _,
+                ref operands,
+                options: _,
+                line_spans: _,
+                destination,
+                cleanup,
+            } => {
+                if let Some(unwind) = cleanup {
+                    if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
+                        propagate(unwind, exit_state);
+                    }
+                }
+
                 if let Some(target) = destination {
+                    // N.B.: This must be done *last*, otherwise the unwind path will see the call
+                    // return effect.
+                    analysis.apply_call_return_effect(
+                        exit_state,
+                        bb,
+                        CallReturnPlaces::InlineAsm(operands),
+                    );
                     propagate(target, exit_state);
                 }
             }
diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
index a370f8e40f9..517bc086ef6 100644
--- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
@@ -10,7 +10,7 @@ use rustc_middle::mir::graphviz_safe_def_name;
 use rustc_middle::mir::{self, BasicBlock, Body, Location};
 
 use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
-use super::{Analysis, Direction, Results, ResultsRefCursor, ResultsVisitor};
+use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsRefCursor, ResultsVisitor};
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum OutputStyle {
@@ -231,16 +231,15 @@ where
         // for the basic block itself. That way, we could display terminator-specific effects for
         // backward dataflow analyses as well as effects for `SwitchInt` terminators.
         match terminator.kind {
-            mir::TerminatorKind::Call {
-                destination: Some((return_place, _)),
-                ref func,
-                ref args,
-                ..
-            } => {
+            mir::TerminatorKind::Call { destination: Some((return_place, _)), .. } => {
                 self.write_row(w, "", "(on successful return)", |this, w, fmt| {
                     let state_on_unwind = this.results.get().clone();
                     this.results.apply_custom_effect(|analysis, state| {
-                        analysis.apply_call_return_effect(state, block, func, args, return_place);
+                        analysis.apply_call_return_effect(
+                            state,
+                            block,
+                            CallReturnPlaces::Call(return_place),
+                        );
                     });
 
                     write!(
@@ -278,6 +277,31 @@ where
                 })?;
             }
 
+            mir::TerminatorKind::InlineAsm { destination: Some(_), ref operands, .. } => {
+                self.write_row(w, "", "(on successful return)", |this, w, fmt| {
+                    let state_on_unwind = this.results.get().clone();
+                    this.results.apply_custom_effect(|analysis, state| {
+                        analysis.apply_call_return_effect(
+                            state,
+                            block,
+                            CallReturnPlaces::InlineAsm(operands),
+                        );
+                    });
+
+                    write!(
+                        w,
+                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#,
+                        colspan = this.style.num_state_columns(),
+                        fmt = fmt,
+                        diff = diff_pretty(
+                            this.results.get(),
+                            &state_on_unwind,
+                            this.results.analysis()
+                        ),
+                    )
+                })?;
+            }
+
             _ => {}
         };
 
diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs
index f0c9ac4c504..500fba8b114 100644
--- a/compiler/rustc_mir_dataflow/src/framework/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs
@@ -160,9 +160,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
         &self,
         state: &mut Self::Domain,
         block: BasicBlock,
-        func: &mir::Operand<'tcx>,
-        args: &[mir::Operand<'tcx>],
-        return_place: mir::Place<'tcx>,
+        return_places: CallReturnPlaces<'_, 'tcx>,
     );
 
     /// Updates the current dataflow state with the effect of resuming from a `Yield` terminator.
@@ -276,9 +274,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
         &self,
         trans: &mut impl GenKill<Self::Idx>,
         block: BasicBlock,
-        func: &mir::Operand<'tcx>,
-        args: &[mir::Operand<'tcx>],
-        return_place: mir::Place<'tcx>,
+        return_places: CallReturnPlaces<'_, 'tcx>,
     );
 
     /// See `Analysis::apply_yield_resume_effect`.
@@ -347,11 +343,9 @@ where
         &self,
         state: &mut A::Domain,
         block: BasicBlock,
-        func: &mir::Operand<'tcx>,
-        args: &[mir::Operand<'tcx>],
-        return_place: mir::Place<'tcx>,
+        return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
-        self.call_return_effect(state, block, func, args, return_place);
+        self.call_return_effect(state, block, return_places);
     }
 
     fn apply_yield_resume_effect(
@@ -542,5 +536,29 @@ pub trait SwitchIntEdgeEffects<D> {
     fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget));
 }
 
+/// List of places that are written to after a successful (non-unwind) return
+/// from a `Call` or `InlineAsm`.
+pub enum CallReturnPlaces<'a, 'tcx> {
+    Call(mir::Place<'tcx>),
+    InlineAsm(&'a [mir::InlineAsmOperand<'tcx>]),
+}
+
+impl<'tcx> CallReturnPlaces<'_, 'tcx> {
+    pub fn for_each(&self, mut f: impl FnMut(mir::Place<'tcx>)) {
+        match *self {
+            Self::Call(place) => f(place),
+            Self::InlineAsm(operands) => {
+                for op in operands {
+                    match *op {
+                        mir::InlineAsmOperand::Out { place: Some(place), .. }
+                        | mir::InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
+                        _ => {}
+                    }
+                }
+            }
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests;
diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs
index 6efa8daec48..01ca8ca9258 100644
--- a/compiler/rustc_mir_dataflow/src/framework/tests.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs
@@ -220,9 +220,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
         &self,
         _state: &mut Self::Domain,
         _block: BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        _return_place: mir::Place<'tcx>,
+        _return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
     }
 }
diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
index d38b567a958..6df2c8df3ce 100644
--- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
@@ -1,6 +1,6 @@
 use super::*;
 
-use crate::{AnalysisDomain, GenKill, GenKillAnalysis};
+use crate::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis};
 use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::*;
 
@@ -84,9 +84,7 @@ impl GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
         &self,
         _trans: &mut impl GenKill<Self::Idx>,
         _block: mir::BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        _dest_place: mir::Place<'tcx>,
+        _return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
     }
 }
diff --git a/compiler/rustc_mir_dataflow/src/impls/init_locals.rs b/compiler/rustc_mir_dataflow/src/impls/init_locals.rs
index 07570e764f5..df13b5c3394 100644
--- a/compiler/rustc_mir_dataflow/src/impls/init_locals.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/init_locals.rs
@@ -2,7 +2,7 @@
 //!
 //! A local will be maybe initialized if *any* projections of that local might be initialized.
 
-use crate::GenKill;
+use crate::{CallReturnPlaces, GenKill};
 
 use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{PlaceContext, Visitor};
@@ -53,11 +53,9 @@ impl crate::GenKillAnalysis<'tcx> for MaybeInitializedLocals {
         &self,
         trans: &mut impl GenKill<Self::Idx>,
         _block: BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        return_place: mir::Place<'tcx>,
+        return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
-        trans.gen(return_place.local)
+        return_places.for_each(|place| trans.gen(place.local));
     }
 
     /// See `Analysis::apply_yield_resume_effect`.
@@ -83,7 +81,11 @@ where
         use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, NonUseContext};
         match context {
             // These are handled specially in `call_return_effect` and `yield_resume_effect`.
-            PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => {}
+            PlaceContext::MutatingUse(
+                MutatingUseContext::Call
+                | MutatingUseContext::AsmOutput
+                | MutatingUseContext::Yield,
+            ) => {}
 
             // Otherwise, when a place is mutated, we must consider it possibly initialized.
             PlaceContext::MutatingUse(_) => self.trans.gen(local),
diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
index 3e2548845e2..5be9df6c452 100644
--- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
@@ -2,7 +2,7 @@ use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::{self, Local, Location};
 
-use crate::{AnalysisDomain, Backward, GenKill, GenKillAnalysis};
+use crate::{AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKillAnalysis};
 
 /// A [live-variable dataflow analysis][liveness].
 ///
@@ -94,13 +94,13 @@ impl GenKillAnalysis<'tcx> for MaybeLiveLocals {
         &self,
         trans: &mut impl GenKill<Self::Idx>,
         _block: mir::BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        dest_place: mir::Place<'tcx>,
+        return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
-        if let Some(local) = dest_place.as_local() {
-            trans.kill(local);
-        }
+        return_places.for_each(|place| {
+            if let Some(local) = place.as_local() {
+                trans.kill(local);
+            }
+        });
     }
 
     fn yield_resume_effect(
@@ -167,12 +167,16 @@ impl DefUse {
             // destination place for a `Call` return or `Yield` resume respectively. Since this is
             // only a `Def` when the function returns successfully, we handle this case separately
             // in `call_return_effect` above.
-            PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => None,
+            PlaceContext::MutatingUse(
+                MutatingUseContext::Call
+                | MutatingUseContext::AsmOutput
+                | MutatingUseContext::Yield,
+            ) => None,
 
             // All other contexts are uses...
             PlaceContext::MutatingUse(
                 MutatingUseContext::AddressOf
-                | MutatingUseContext::AsmOutput
+                | MutatingUseContext::LlvmAsmOutput
                 | MutatingUseContext::Borrow
                 | MutatingUseContext::Drop
                 | MutatingUseContext::Retag,
diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs
index 2585701f60c..5659fd2dc70 100644
--- a/compiler/rustc_mir_dataflow/src/impls/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs
@@ -11,7 +11,7 @@ use rustc_middle::ty::{self, TyCtxt};
 use crate::drop_flag_effects_for_function_entry;
 use crate::drop_flag_effects_for_location;
 use crate::elaborate_drops::DropFlagState;
-use crate::framework::SwitchIntEdgeEffects;
+use crate::framework::{CallReturnPlaces, SwitchIntEdgeEffects};
 use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
 use crate::on_lookup_result_bits;
 use crate::MoveDataParamEnv;
@@ -354,21 +354,21 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
         &self,
         trans: &mut impl GenKill<Self::Idx>,
         _block: mir::BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        dest_place: mir::Place<'tcx>,
+        return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
-        // when a call returns successfully, that means we need to set
-        // the bits for that dest_place to 1 (initialized).
-        on_lookup_result_bits(
-            self.tcx,
-            self.body,
-            self.move_data(),
-            self.move_data().rev_lookup.find(dest_place.as_ref()),
-            |mpi| {
-                trans.gen(mpi);
-            },
-        );
+        return_places.for_each(|place| {
+            // when a call returns successfully, that means we need to set
+            // the bits for that dest_place to 1 (initialized).
+            on_lookup_result_bits(
+                self.tcx,
+                self.body,
+                self.move_data(),
+                self.move_data().rev_lookup.find(place.as_ref()),
+                |mpi| {
+                    trans.gen(mpi);
+                },
+            );
+        });
     }
 
     fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
@@ -472,21 +472,21 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
         &self,
         trans: &mut impl GenKill<Self::Idx>,
         _block: mir::BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        dest_place: mir::Place<'tcx>,
+        return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
-        // when a call returns successfully, that means we need to set
-        // the bits for that dest_place to 0 (initialized).
-        on_lookup_result_bits(
-            self.tcx,
-            self.body,
-            self.move_data(),
-            self.move_data().rev_lookup.find(dest_place.as_ref()),
-            |mpi| {
-                trans.kill(mpi);
-            },
-        );
+        return_places.for_each(|place| {
+            // when a call returns successfully, that means we need to set
+            // the bits for that dest_place to 0 (initialized).
+            on_lookup_result_bits(
+                self.tcx,
+                self.body,
+                self.move_data(),
+                self.move_data().rev_lookup.find(place.as_ref()),
+                |mpi| {
+                    trans.kill(mpi);
+                },
+            );
+        });
     }
 
     fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
@@ -591,21 +591,21 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
         &self,
         trans: &mut impl GenKill<Self::Idx>,
         _block: mir::BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        dest_place: mir::Place<'tcx>,
+        return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
-        // when a call returns successfully, that means we need to set
-        // the bits for that dest_place to 1 (initialized).
-        on_lookup_result_bits(
-            self.tcx,
-            self.body,
-            self.move_data(),
-            self.move_data().rev_lookup.find(dest_place.as_ref()),
-            |mpi| {
-                trans.gen(mpi);
-            },
-        );
+        return_places.for_each(|place| {
+            // when a call returns successfully, that means we need to set
+            // the bits for that dest_place to 1 (initialized).
+            on_lookup_result_bits(
+                self.tcx,
+                self.body,
+                self.move_data(),
+                self.move_data().rev_lookup.find(place.as_ref()),
+                |mpi| {
+                    trans.gen(mpi);
+                },
+            );
+        });
     }
 }
 
@@ -679,9 +679,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
         &self,
         trans: &mut impl GenKill<Self::Idx>,
         block: mir::BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        _dest_place: mir::Place<'tcx>,
+        _return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
         let move_data = self.move_data();
         let init_loc_map = &move_data.init_loc_map;
diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
index b468e50b391..108357abc0d 100644
--- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
@@ -1,7 +1,7 @@
 pub use super::*;
 
 use crate::storage::AlwaysLiveLocals;
-use crate::{GenKill, Results, ResultsRefCursor};
+use crate::{CallReturnPlaces, GenKill, Results, ResultsRefCursor};
 use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use std::cell::RefCell;
@@ -68,9 +68,7 @@ impl crate::GenKillAnalysis<'tcx> for MaybeStorageLive {
         &self,
         _trans: &mut impl GenKill<Self::Idx>,
         _block: BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        _return_place: mir::Place<'tcx>,
+        _return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
         // Nothing to do when a call returns successfully
     }
@@ -226,7 +224,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
         terminator: &mir::Terminator<'tcx>,
         loc: Location,
     ) {
-        match &terminator.kind {
+        match terminator.kind {
             // For call terminators the destination requires storage for the call
             // and after the call returns successfully, but not after a panic.
             // Since `propagate_call_unwind` doesn't exist, we have to kill the
@@ -235,6 +233,11 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
                 trans.kill(place.local);
             }
 
+            // The same applies to InlineAsm outputs.
+            TerminatorKind::InlineAsm { ref operands, .. } => {
+                CallReturnPlaces::InlineAsm(operands).for_each(|place| trans.kill(place.local));
+            }
+
             // Nothing to do for these. Match exhaustively so this fails to compile when new
             // variants are added.
             TerminatorKind::Call { destination: None, .. }
@@ -247,7 +250,6 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
             | TerminatorKind::FalseUnwind { .. }
             | TerminatorKind::GeneratorDrop
             | TerminatorKind::Goto { .. }
-            | TerminatorKind::InlineAsm { .. }
             | TerminatorKind::Resume
             | TerminatorKind::Return
             | TerminatorKind::SwitchInt { .. }
@@ -261,11 +263,9 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
         &self,
         trans: &mut impl GenKill<Self::Idx>,
         _block: BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        return_place: mir::Place<'tcx>,
+        return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
-        trans.gen(return_place.local);
+        return_places.for_each(|place| trans.gen(place.local));
     }
 
     fn yield_resume_effect(
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index 77a72ce63ce..10d2cf6eba0 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -28,9 +28,9 @@ pub use self::drop_flag_effects::{
     on_lookup_result_bits,
 };
 pub use self::framework::{
-    fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, Direction, Engine,
-    Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor, ResultsRefCursor,
-    ResultsVisitable, ResultsVisitor,
+    fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces,
+    Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor,
+    ResultsRefCursor, ResultsVisitable, ResultsVisitor,
 };
 
 use self::move_paths::MoveData;
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index e404b49ecb9..feb85d4ffdf 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -419,6 +419,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
                 options: _,
                 line_spans: _,
                 destination: _,
+                cleanup: _,
             } => {
                 for op in operands {
                     match *op {
diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs
index 1ff9bd15721..cdfeb957df9 100644
--- a/compiler/rustc_mir_transform/src/check_unsafety.rs
+++ b/compiler/rustc_mir_transform/src/check_unsafety.rs
@@ -208,6 +208,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                             MutatingUseContext::Store
                                 | MutatingUseContext::Drop
                                 | MutatingUseContext::AsmOutput
+                                | MutatingUseContext::LlvmAsmOutput
                         )
                     );
                 // If this is just an assignment, determine if the assigned type needs dropping.
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 63c637af5c2..4bfa1de7a3b 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -1022,6 +1022,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
             // These are just stores, where the storing is not propagatable, but there may be later
             // mutations of the same local via `Store`
             | MutatingUse(MutatingUseContext::Call)
+            | MutatingUse(MutatingUseContext::AsmOutput)
             // Actual store that can possibly even propagate a value
             | MutatingUse(MutatingUseContext::Store) => {
                 if !self.found_assignment.insert(local) {
@@ -1052,7 +1053,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
 
             // These could be propagated with a smarter analysis or just some careful thinking about
             // whether they'd be fine right now.
-            MutatingUse(MutatingUseContext::AsmOutput)
+            MutatingUse(MutatingUseContext::LlvmAsmOutput)
             | MutatingUse(MutatingUseContext::Yield)
             | MutatingUse(MutatingUseContext::Drop)
             | MutatingUse(MutatingUseContext::Retag)
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index c45946a9e2a..f3217f0b9b6 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -624,6 +624,7 @@ impl Conflicts<'a> {
                 options: _,
                 line_spans: _,
                 destination: _,
+                cleanup: _,
             } => {
                 // The intended semantics here aren't documented, we just assume that nothing that
                 // could be written to by the assembly may overlap with any other operands.
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 84a1e3fb600..4dacd4c288a 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -441,6 +441,13 @@ impl Inliner<'tcx> {
                     }
                 }
                 TerminatorKind::Resume => cost += RESUME_PENALTY,
+                TerminatorKind::InlineAsm { cleanup, .. } => {
+                    cost += INSTR_COST;
+
+                    if cleanup.is_some() {
+                        cost += LANDINGPAD_PENALTY;
+                    }
+                }
                 _ => cost += INSTR_COST,
             }
 
@@ -954,9 +961,13 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
             {
                 bug!("False unwinds should have been removed before inlining")
             }
-            TerminatorKind::InlineAsm { ref mut destination, .. } => {
+            TerminatorKind::InlineAsm { ref mut destination, ref mut cleanup, .. } => {
                 if let Some(ref mut tgt) = *destination {
                     *tgt = self.map_block(*tgt);
+                } else if !self.in_cleanup_block {
+                    // Unless this inline asm is in a cleanup block, add an unwind edge to
+                    // the original call's cleanup block
+                    *cleanup = self.cleanup_block;
                 }
             }
         }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 06e4cee30ed..5d16293c721 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -817,6 +817,7 @@ symbols! {
         maxnumf32,
         maxnumf64,
         may_dangle,
+        may_unwind,
         maybe_uninit,
         maybe_uninit_uninit,
         maybe_uninit_zeroed,
diff --git a/src/test/ui/asm/aarch64/bad-options.stderr b/src/test/ui/asm/aarch64/bad-options.stderr
index 21bcc4a9c7b..867e0433eae 100644
--- a/src/test/ui/asm/aarch64/bad-options.stderr
+++ b/src/test/ui/asm/aarch64/bad-options.stderr
@@ -36,41 +36,41 @@ LL |         asm!("{}", out(reg) foo, clobber_abi("C"));
    |                    |
    |                    generic outputs
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
   --> $DIR/bad-options.rs:28:25
    |
 LL | global_asm!("", options(nomem));
-   |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `readonly`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `readonly`
   --> $DIR/bad-options.rs:30:25
    |
 LL | global_asm!("", options(readonly));
-   |                         ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `noreturn`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `noreturn`
   --> $DIR/bad-options.rs:32:25
    |
 LL | global_asm!("", options(noreturn));
-   |                         ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `pure`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `pure`
   --> $DIR/bad-options.rs:34:25
    |
 LL | global_asm!("", options(pure));
-   |                         ^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `nostack`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nostack`
   --> $DIR/bad-options.rs:36:25
    |
 LL | global_asm!("", options(nostack));
-   |                         ^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `preserves_flags`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `preserves_flags`
   --> $DIR/bad-options.rs:38:25
    |
 LL | global_asm!("", options(preserves_flags));
-   |                         ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
 error: invalid ABI for `clobber_abi`
   --> $DIR/bad-options.rs:20:18
diff --git a/src/test/ui/asm/aarch64/parse-error.stderr b/src/test/ui/asm/aarch64/parse-error.stderr
index 3d88cef5c7d..a143c3b2b28 100644
--- a/src/test/ui/asm/aarch64/parse-error.stderr
+++ b/src/test/ui/asm/aarch64/parse-error.stderr
@@ -64,11 +64,11 @@ error: argument to `sym` must be a path expression
 LL |         asm!("{}", sym foo + bar);
    |                        ^^^^^^^^^
 
-error: expected one of `)`, `att_syntax`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
+error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
   --> $DIR/parse-error.rs:31:26
    |
 LL |         asm!("", options(foo));
-   |                          ^^^ expected one of 9 possible tokens
+   |                          ^^^ expected one of 10 possible tokens
 
 error: expected one of `)` or `,`, found `foo`
   --> $DIR/parse-error.rs:33:32
@@ -76,11 +76,11 @@ error: expected one of `)` or `,`, found `foo`
 LL |         asm!("", options(nomem foo));
    |                                ^^^ expected one of `)` or `,`
 
-error: expected one of `)`, `att_syntax`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
+error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
   --> $DIR/parse-error.rs:35:33
    |
 LL |         asm!("", options(nomem, foo));
-   |                                 ^^^ expected one of 9 possible tokens
+   |                                 ^^^ expected one of 10 possible tokens
 
 error: arguments are not allowed after options
   --> $DIR/parse-error.rs:37:31
@@ -260,23 +260,23 @@ error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
 LL | global_asm!("{}", const(reg) FOO);
    |                              ^^^ expected one of `,`, `.`, `?`, or an operator
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `FOO`
   --> $DIR/parse-error.rs:100:25
    |
 LL | global_asm!("", options(FOO));
-   |                         ^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
   --> $DIR/parse-error.rs:102:25
    |
 LL | global_asm!("", options(nomem FOO));
-   |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
   --> $DIR/parse-error.rs:104:25
    |
 LL | global_asm!("", options(nomem, FOO));
-   |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
 error: arguments are not allowed after options
   --> $DIR/parse-error.rs:106:30
diff --git a/src/test/ui/asm/x86_64/bad-options.stderr b/src/test/ui/asm/x86_64/bad-options.stderr
index e2351840eef..a63c42aac27 100644
--- a/src/test/ui/asm/x86_64/bad-options.stderr
+++ b/src/test/ui/asm/x86_64/bad-options.stderr
@@ -45,41 +45,41 @@ LL |         asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C"));
    |                    |             clobber_abi
    |                    generic outputs
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
   --> $DIR/bad-options.rs:31:25
    |
 LL | global_asm!("", options(nomem));
-   |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `readonly`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `readonly`
   --> $DIR/bad-options.rs:33:25
    |
 LL | global_asm!("", options(readonly));
-   |                         ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `noreturn`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `noreturn`
   --> $DIR/bad-options.rs:35:25
    |
 LL | global_asm!("", options(noreturn));
-   |                         ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `pure`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `pure`
   --> $DIR/bad-options.rs:37:25
    |
 LL | global_asm!("", options(pure));
-   |                         ^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `nostack`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nostack`
   --> $DIR/bad-options.rs:39:25
    |
 LL | global_asm!("", options(nostack));
-   |                         ^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `preserves_flags`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `preserves_flags`
   --> $DIR/bad-options.rs:41:25
    |
 LL | global_asm!("", options(preserves_flags));
-   |                         ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
 error: invalid ABI for `clobber_abi`
   --> $DIR/bad-options.rs:20:18
diff --git a/src/test/ui/asm/x86_64/parse-error.stderr b/src/test/ui/asm/x86_64/parse-error.stderr
index 018df9826c6..4f16c15af38 100644
--- a/src/test/ui/asm/x86_64/parse-error.stderr
+++ b/src/test/ui/asm/x86_64/parse-error.stderr
@@ -64,11 +64,11 @@ error: argument to `sym` must be a path expression
 LL |         asm!("{}", sym foo + bar);
    |                        ^^^^^^^^^
 
-error: expected one of `)`, `att_syntax`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
+error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
   --> $DIR/parse-error.rs:31:26
    |
 LL |         asm!("", options(foo));
-   |                          ^^^ expected one of 9 possible tokens
+   |                          ^^^ expected one of 10 possible tokens
 
 error: expected one of `)` or `,`, found `foo`
   --> $DIR/parse-error.rs:33:32
@@ -76,11 +76,11 @@ error: expected one of `)` or `,`, found `foo`
 LL |         asm!("", options(nomem foo));
    |                                ^^^ expected one of `)` or `,`
 
-error: expected one of `)`, `att_syntax`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
+error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
   --> $DIR/parse-error.rs:35:33
    |
 LL |         asm!("", options(nomem, foo));
-   |                                 ^^^ expected one of 9 possible tokens
+   |                                 ^^^ expected one of 10 possible tokens
 
 error: arguments are not allowed after options
   --> $DIR/parse-error.rs:37:31
@@ -266,23 +266,23 @@ error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
 LL | global_asm!("{}", const(reg) FOO);
    |                              ^^^ expected one of `,`, `.`, `?`, or an operator
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `FOO`
   --> $DIR/parse-error.rs:102:25
    |
 LL | global_asm!("", options(FOO));
-   |                         ^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
   --> $DIR/parse-error.rs:104:25
    |
 LL | global_asm!("", options(nomem FOO));
-   |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
-error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
+error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
   --> $DIR/parse-error.rs:106:25
    |
 LL | global_asm!("", options(nomem, FOO));
-   |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
+   |                         ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
 
 error: arguments are not allowed after options
   --> $DIR/parse-error.rs:108:30
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index f7711b6fe94..0eba6633ee1 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -15,7 +15,7 @@ use rustc_middle::mir::{
     Mutability,
 };
 use rustc_middle::ty::{self, fold::TypeVisitor, Ty, TyCtxt};
-use rustc_mir_dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
+use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis, ResultsCursor};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::{BytePos, Span};
 use rustc_span::sym;
@@ -499,11 +499,9 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
 
     fn call_return_effect(
         &self,
-        _in_out: &mut impl GenKill<Self::Idx>,
+        _trans: &mut impl GenKill<Self::Idx>,
         _block: mir::BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        _return_place: mir::Place<'tcx>,
+        _return_places: CallReturnPlaces<'_, 'tcx>,
     ) {
         // Nothing to do when a call returns successfully
     }