about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast/src/ast.rs1
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs56
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs10
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs109
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs9
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_target/src/asm/mod.rs182
-rw-r--r--src/doc/unstable-book/src/library-features/asm.md71
-rw-r--r--src/test/codegen/asm-clobber_abi.rs35
-rw-r--r--src/test/ui/asm/bad-options.rs12
-rw-r--r--src/test/ui/asm/bad-options.stderr34
-rw-r--r--src/test/ui/asm/naked-functions.rs2
-rw-r--r--src/test/ui/asm/naked-functions.stderr2
-rw-r--r--src/test/ui/asm/parse-error.rs32
-rw-r--r--src/test/ui/asm/parse-error.stderr199
15 files changed, 668 insertions, 87 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 8cab83707dc..bd6e4c30fc3 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2027,6 +2027,7 @@ pub enum InlineAsmOperand {
 pub struct InlineAsm {
     pub template: Vec<InlineAsmTemplatePiece>,
     pub operands: Vec<(InlineAsmOperand, Span)>,
+    pub clobber_abi: Option<(Symbol, Span)>,
     pub options: InlineAsmOptions,
     pub line_spans: Vec<Span>,
 }
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 9ea09a2cf31..d94fb48d7cb 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -27,11 +27,41 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 .emit();
         }
 
+        let mut clobber_abi = None;
+        if let Some(asm_arch) = asm_arch {
+            if let Some((abi_name, abi_span)) = asm.clobber_abi {
+                match asm::InlineAsmClobberAbi::parse(asm_arch, &self.sess.target, abi_name) {
+                    Ok(abi) => clobber_abi = Some((abi, abi_span)),
+                    Err(&[]) => {
+                        self.sess
+                            .struct_span_err(
+                                abi_span,
+                                "`clobber_abi` is not supported on this target",
+                            )
+                            .emit();
+                    }
+                    Err(supported_abis) => {
+                        let mut err =
+                            self.sess.struct_span_err(abi_span, "invalid ABI for `clobber_abi`");
+                        let mut abis = format!("`{}`", supported_abis[0]);
+                        for m in &supported_abis[1..] {
+                            let _ = write!(abis, ", `{}`", m);
+                        }
+                        err.note(&format!(
+                            "the following ABIs are supported on this target: {}",
+                            abis
+                        ));
+                        err.emit();
+                    }
+                }
+            }
+        }
+
         // Lower operands to HIR. We use dummy register classes if an error
         // occurs during lowering because we still need to be able to produce a
         // valid HIR.
         let sess = self.sess;
-        let operands: Vec<_> = asm
+        let mut operands: Vec<_> = asm
             .operands
             .iter()
             .map(|(op, op_sp)| {
@@ -336,6 +366,30 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             }
         }
 
+        // If a clobber_abi is specified, add the necessary clobbers to the
+        // operands list.
+        if let Some((abi, abi_span)) = clobber_abi {
+            for &clobber in abi.clobbered_regs() {
+                let mut output_used = false;
+                clobber.overlapping_regs(|reg| {
+                    if used_output_regs.contains_key(&reg) {
+                        output_used = true;
+                    }
+                });
+
+                if !output_used {
+                    operands.push((
+                        hir::InlineAsmOperand::Out {
+                            reg: asm::InlineAsmRegOrRegClass::Reg(clobber),
+                            late: true,
+                            expr: None,
+                        },
+                        abi_span,
+                    ));
+                }
+            }
+        }
+
         let operands = self.arena.alloc_from_iter(operands);
         let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
         let line_spans = self.arena.alloc_slice(&asm.line_spans[..]);
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index b910431b1dd..763c1b12bf8 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -2186,11 +2186,15 @@ impl<'a> State<'a> {
         enum AsmArg<'a> {
             Template(String),
             Operand(&'a InlineAsmOperand),
+            ClobberAbi(Symbol),
             Options(InlineAsmOptions),
         }
 
         let mut args = vec![AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template))];
         args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
+        if let Some((abi, _)) = asm.clobber_abi {
+            args.push(AsmArg::ClobberAbi(abi));
+        }
         if !asm.options.is_empty() {
             args.push(AsmArg::Options(asm.options));
         }
@@ -2257,6 +2261,12 @@ impl<'a> State<'a> {
                     }
                 }
             }
+            AsmArg::ClobberAbi(abi) => {
+                s.word("clobber_abi");
+                s.popen();
+                s.print_symbol(*abi, ast::StrStyle::Cooked);
+                s.pclose();
+            }
             AsmArg::Options(opts) => {
                 s.word("options");
                 s.popen();
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 018a2ce6c2e..cb0cfdcefdc 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -19,6 +19,7 @@ struct AsmArgs {
     operands: Vec<(ast::InlineAsmOperand, Span)>,
     named_args: FxHashMap<Symbol, usize>,
     reg_args: FxHashSet<usize>,
+    clobber_abi: Option<(Symbol, Span)>,
     options: ast::InlineAsmOptions,
     options_spans: Vec<Span>,
 }
@@ -63,6 +64,7 @@ fn parse_args<'a>(
         operands: vec![],
         named_args: FxHashMap::default(),
         reg_args: FxHashSet::default(),
+        clobber_abi: None,
         options: ast::InlineAsmOptions::empty(),
         options_spans: vec![],
     };
@@ -85,6 +87,13 @@ fn parse_args<'a>(
             break;
         } // accept trailing commas
 
+        // Parse clobber_abi
+        if p.eat_keyword(sym::clobber_abi) {
+            parse_clobber_abi(&mut p, &mut args)?;
+            allow_templates = false;
+            continue;
+        }
+
         // Parse options
         if p.eat_keyword(sym::options) {
             parse_options(&mut p, &mut args, is_global_asm)?;
@@ -160,7 +169,11 @@ fn parse_args<'a>(
                 ast::ExprKind::Lit(ast::Lit { kind: ast::LitKind::Str(..), .. }) => {}
                 ast::ExprKind::MacCall(..) => {}
                 _ => {
-                    let errstr = "expected operand, options, or additional template string";
+                    let errstr = if is_global_asm {
+                        "expected operand, options, or additional template string"
+                    } else {
+                        "expected operand, clobber_abi, options, or additional template string"
+                    };
                     let mut err = ecx.struct_span_err(template.span, errstr);
                     err.span_label(template.span, errstr);
                     return Err(err);
@@ -177,13 +190,19 @@ fn parse_args<'a>(
         let slot = args.operands.len();
         args.operands.push((op, span));
 
-        // Validate the order of named, positional & explicit register operands and options. We do
-        // this at the end once we have the full span of the argument available.
+        // Validate the order of named, positional & explicit register operands and
+        // clobber_abi/options. We do this at the end once we have the full span
+        // of the argument available.
         if !args.options_spans.is_empty() {
             ecx.struct_span_err(span, "arguments are not allowed after options")
                 .span_labels(args.options_spans.clone(), "previous options")
                 .span_label(span, "argument")
                 .emit();
+        } else if let Some((_, abi_span)) = args.clobber_abi {
+            ecx.struct_span_err(span, "arguments are not allowed after clobber_abi")
+                .span_label(abi_span, "clobber_abi")
+                .span_label(span, "argument")
+                .emit();
         }
         if explicit_reg {
             if name.is_some() {
@@ -256,16 +275,23 @@ fn parse_args<'a>(
 
     let mut have_real_output = false;
     let mut outputs_sp = vec![];
+    let mut regclass_outputs = vec![];
     for (op, op_sp) in &args.operands {
         match op {
-            ast::InlineAsmOperand::Out { expr, .. }
-            | ast::InlineAsmOperand::SplitInOut { out_expr: expr, .. } => {
+            ast::InlineAsmOperand::Out { reg, expr, .. }
+            | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => {
                 outputs_sp.push(*op_sp);
                 have_real_output |= expr.is_some();
+                if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
+                    regclass_outputs.push(*op_sp);
+                }
             }
-            ast::InlineAsmOperand::InOut { .. } => {
+            ast::InlineAsmOperand::InOut { reg, .. } => {
                 outputs_sp.push(*op_sp);
                 have_real_output = true;
+                if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
+                    regclass_outputs.push(*op_sp);
+                }
             }
             _ => {}
         }
@@ -273,7 +299,7 @@ fn parse_args<'a>(
     if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
         ecx.struct_span_err(
             args.options_spans.clone(),
-            "asm with `pure` option must have at least one output",
+            "asm with the `pure` option must have at least one output",
         )
         .emit();
     }
@@ -284,6 +310,24 @@ fn parse_args<'a>(
         // Bail out now since this is likely to confuse MIR
         return Err(err);
     }
+    if let Some((_, abi_span)) = args.clobber_abi {
+        if is_global_asm {
+            let err =
+                ecx.struct_span_err(abi_span, "`clobber_abi` cannot be used with `global_asm!`");
+
+            // Bail out now since this is likely to confuse later stages
+            return Err(err);
+        }
+        if !regclass_outputs.is_empty() {
+            ecx.struct_span_err(
+                regclass_outputs.clone(),
+                "asm with `clobber_abi` must specify explicit registers for outputs",
+            )
+            .span_label(abi_span, "clobber_abi")
+            .span_labels(regclass_outputs, "generic outputs")
+            .emit();
+        }
+    }
 
     Ok(args)
 }
@@ -375,6 +419,49 @@ fn parse_options<'a>(
     Ok(())
 }
 
+fn parse_clobber_abi<'a>(
+    p: &mut Parser<'a>,
+    args: &mut AsmArgs,
+) -> Result<(), DiagnosticBuilder<'a>> {
+    let span_start = p.prev_token.span;
+
+    p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
+
+    let clobber_abi = match p.parse_str_lit() {
+        Ok(str_lit) => str_lit.symbol_unescaped,
+        Err(opt_lit) => {
+            let span = opt_lit.map_or(p.token.span, |lit| lit.span);
+            let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
+            err.span_label(span, "not a string literal");
+            return Err(err);
+        }
+    };
+
+    p.expect(&token::CloseDelim(token::DelimToken::Paren))?;
+
+    let new_span = span_start.to(p.prev_token.span);
+
+    if let Some((_, prev_span)) = args.clobber_abi {
+        let mut err = p
+            .sess
+            .span_diagnostic
+            .struct_span_err(new_span, "clobber_abi specified multiple times");
+        err.span_label(prev_span, "clobber_abi previously specified here");
+        return Err(err);
+    } else if !args.options_spans.is_empty() {
+        let mut err = p
+            .sess
+            .span_diagnostic
+            .struct_span_err(new_span, "clobber_abi is not allowed after options");
+        err.span_labels(args.options_spans.clone(), "options");
+        return Err(err);
+    }
+
+    args.clobber_abi = Some((clobber_abi, new_span));
+
+    Ok(())
+}
+
 fn parse_reg<'a>(
     p: &mut Parser<'a>,
     explicit_reg: &mut bool,
@@ -730,7 +817,13 @@ fn expand_preparsed_asm(
         }
     }
 
-    Some(ast::InlineAsm { template, operands: args.operands, options: args.options, line_spans })
+    Some(ast::InlineAsm {
+        template,
+        operands: args.operands,
+        clobber_abi: args.clobber_abi,
+        options: args.options,
+        line_spans,
+    })
 }
 
 pub fn expand_asm<'cx>(
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index 4790b44bd19..4387f5301a5 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -302,7 +302,14 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
                         "~{flags}".to_string(),
                     ]);
                 }
-                InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {}
+                InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
+                    constraints.extend_from_slice(&[
+                        "~{vtype}".to_string(),
+                        "~{vl}".to_string(),
+                        "~{vxsat}".to_string(),
+                        "~{vxrm}".to_string(),
+                    ]);
+                }
                 InlineAsmArch::Nvptx64 => {}
                 InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => {}
                 InlineAsmArch::Hexagon => {}
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 6d03f1a3732..1d91b327901 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -378,6 +378,7 @@ symbols! {
         char,
         client,
         clippy,
+        clobber_abi,
         clone,
         clone_closures,
         clone_from,
diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs
index b52fa5bbcb2..9ebf8235e20 100644
--- a/compiler/rustc_target/src/asm/mod.rs
+++ b/compiler/rustc_target/src/asm/mod.rs
@@ -712,3 +712,185 @@ pub fn allocatable_registers(
         }
     }
 }
+
+#[derive(
+    Copy,
+    Clone,
+    Encodable,
+    Decodable,
+    Debug,
+    Eq,
+    PartialEq,
+    PartialOrd,
+    Hash,
+    HashStable_Generic
+)]
+pub enum InlineAsmClobberAbi {
+    X86,
+    X86_64Win,
+    X86_64SysV,
+    Arm,
+    AArch64,
+    RiscV,
+}
+
+impl InlineAsmClobberAbi {
+    /// Parses a clobber ABI for the given target, or returns a list of supported
+    /// clobber ABIs for the target.
+    pub fn parse(
+        arch: InlineAsmArch,
+        target: &Target,
+        name: Symbol,
+    ) -> Result<Self, &'static [&'static str]> {
+        let name = &*name.as_str();
+        match arch {
+            InlineAsmArch::X86 => match name {
+                "C" | "system" | "efiapi" | "cdecl" | "stdcall" | "fastcall" => {
+                    Ok(InlineAsmClobberAbi::X86)
+                }
+                _ => Err(&["C", "system", "efiapi", "cdecl", "stdcall", "fastcall"]),
+            },
+            InlineAsmArch::X86_64 => match name {
+                "C" | "system" if !target.is_like_windows => Ok(InlineAsmClobberAbi::X86_64SysV),
+                "C" | "system" if target.is_like_windows => Ok(InlineAsmClobberAbi::X86_64Win),
+                "win64" | "efiapi" => Ok(InlineAsmClobberAbi::X86_64Win),
+                "sysv64" => Ok(InlineAsmClobberAbi::X86_64SysV),
+                _ => Err(&["C", "system", "efiapi", "win64", "sysv64"]),
+            },
+            InlineAsmArch::Arm => match name {
+                "C" | "system" | "efiapi" | "aapcs" => Ok(InlineAsmClobberAbi::Arm),
+                _ => Err(&["C", "system", "efiapi", "aapcs"]),
+            },
+            InlineAsmArch::AArch64 => match name {
+                "C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::AArch64),
+                _ => Err(&["C", "system", "efiapi"]),
+            },
+            InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name {
+                "C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::RiscV),
+                _ => Err(&["C", "system", "efiapi"]),
+            },
+            _ => Err(&[]),
+        }
+    }
+
+    /// Returns the set of registers which are clobbered by this ABI.
+    pub fn clobbered_regs(self) -> &'static [InlineAsmReg] {
+        macro_rules! clobbered_regs {
+            ($arch:ident $arch_reg:ident {
+                $(
+                    $reg:ident,
+                )*
+            }) => {
+                &[
+                    $(InlineAsmReg::$arch($arch_reg::$reg),)*
+                ]
+            };
+        }
+        match self {
+            InlineAsmClobberAbi::X86 => clobbered_regs! {
+                X86 X86InlineAsmReg {
+                    ax, cx, dx,
+
+                    xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
+
+                    k1, k2, k3, k4, k5, k6, k7,
+
+                    mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
+                    st0, st1, st2, st3, st4, st5, st6, st7,
+                }
+            },
+            InlineAsmClobberAbi::X86_64SysV => clobbered_regs! {
+                X86 X86InlineAsmReg {
+                    ax, cx, dx, si, di, r8, r9, r10, r11,
+
+                    xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
+                    xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
+                    zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23,
+                    zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31,
+
+                    k1, k2, k3, k4, k5, k6, k7,
+
+                    mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
+                    st0, st1, st2, st3, st4, st5, st6, st7,
+                }
+            },
+            InlineAsmClobberAbi::X86_64Win => clobbered_regs! {
+                X86 X86InlineAsmReg {
+                    // rdi and rsi are callee-saved on windows
+                    ax, cx, dx, r8, r9, r10, r11,
+
+                    // xmm6-xmm15 are callee-saved on windows, but we need to
+                    // mark them as clobbered anyways because the upper portions
+                    // of ymm6-ymm15 are volatile.
+                    xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
+                    xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
+                    zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23,
+                    zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31,
+
+                    k1, k2, k3, k4, k5, k6, k7,
+
+                    mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
+                    st0, st1, st2, st3, st4, st5, st6, st7,
+                }
+            },
+            InlineAsmClobberAbi::AArch64 => clobbered_regs! {
+                AArch64 AArch64InlineAsmReg {
+                    x0, x1, x2, x3, x4, x5, x6, x7,
+                    x8, x9, x10, x11, x12, x13, x14, x15,
+                    // x18 is platform-reserved or temporary, but we exclude it
+                    // here since it is a reserved register.
+                    x16, x17, x30,
+
+                    // Technically the low 64 bits of v8-v15 are preserved, but
+                    // we have no way of expressing this using clobbers.
+                    v0, v1, v2, v3, v4, v5, v6, v7,
+                    v8, v9, v10, v11, v12, v13, v14, v15,
+                    v16, v17, v18, v19, v20, v21, v22, v23,
+                    v24, v25, v26, v27, v28, v29, v30, v31,
+
+                    p0, p1, p2, p3, p4, p5, p6, p7,
+                    p8, p9, p10, p11, p12, p13, p14, p15,
+                    ffr,
+
+                }
+            },
+            InlineAsmClobberAbi::Arm => clobbered_regs! {
+                Arm ArmInlineAsmReg {
+                    // r9 is platform-reserved and is treated as callee-saved.
+                    r0, r1, r2, r3, r12, r14,
+
+                    // The finest-grained register variant is used here so that
+                    // partial uses of larger registers are properly handled.
+                    s0, s1, s2, s3, s4, s5, s6, s7,
+                    s8, s9, s10, s11, s12, s13, s14, s15,
+                    // s16-s31 are callee-saved
+                    d16, d17, d18, d19, d20, d21, d22, d23,
+                    d24, d25, d26, d27, d28, d29, d30, d31,
+                }
+            },
+            InlineAsmClobberAbi::RiscV => clobbered_regs! {
+                RiscV RiscVInlineAsmReg {
+                    // ra
+                    x1,
+                    // t0-t2
+                    x5, x6, x7,
+                    // a0-a7
+                    x10, x11, x12, x13, x14, x15, x16, x17,
+                    // t3-t6
+                    x28, x29, x30, x31,
+                    // ft0-ft7
+                    f0, f1, f2, f3, f4, f5, f6, f7,
+                    // fa0-fa7
+                    f10, f11, f12, f13, f14, f15, f16, f17,
+                    // ft8-ft11
+                    f28, f29, f30, f31,
+
+                    v0, v1, v2, v3, v4, v5, v6, v7,
+                    v8, v9, v10, v11, v12, v13, v14, v15,
+                    v16, v17, v18, v19, v20, v21, v22, v23,
+                    v24, v25, v26, v27, v28, v29, v30, v31,
+                }
+            },
+        }
+    }
+}
diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md
index 8ff600d5334..220b74ca6e6 100644
--- a/src/doc/unstable-book/src/library-features/asm.md
+++ b/src/doc/unstable-book/src/library-features/asm.md
@@ -290,38 +290,33 @@ unsafe {
 assert_eq!(x, 4 * 6);
 ```
 
-## Symbol operands
+## Symbol operands and ABI clobbers
 
 A special operand type, `sym`, allows you to use the symbol name of a `fn` or `static` in inline assembly code.
 This allows you to call a function or access a global variable without needing to keep its address in a register.
 
 ```rust,allow_fail
 #![feature(asm)]
-extern "C" fn foo(arg: i32) {
+extern "C" fn foo(arg: i32) -> i32 {
     println!("arg = {}", arg);
+    arg * 2
 }
 
-fn call_foo(arg: i32) {
+fn call_foo(arg: i32) -> i32 {
     unsafe {
+        let result;
         asm!(
             "call {}",
             sym foo,
-            // 1st argument in rdi, which is caller-saved
-            inout("rdi") arg => _,
-            // All caller-saved registers must be marked as clobbered
-            out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _,
-            out("r8") _, out("r9") _, out("r10") _, out("r11") _,
-            out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _,
-            out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _,
-            out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _,
-            out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _,
-            // Also mark AVX-512 registers as clobbered. This is accepted by the
-            // compiler even if AVX-512 is not enabled on the current target.
-            out("xmm16") _, out("xmm17") _, out("xmm18") _, out("xmm19") _,
-            out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm23") _,
-            out("xmm24") _, out("xmm25") _, out("xmm26") _, out("xmm27") _,
-            out("xmm28") _, out("xmm29") _, out("xmm30") _, out("xmm31") _,
-        )
+            // 1st argument in rdi
+            in("rdi") arg,
+            // Return value in rax
+            out("rax") result,
+            // Mark all registers which are not preserved by the "C" calling
+            // convention as clobbered.
+            clobber_abi("C"),
+        );
+        result
     }
 }
 ```
@@ -329,6 +324,8 @@ fn call_foo(arg: i32) {
 Note that the `fn` or `static` item does not need to be public or `#[no_mangle]`:
 the compiler will automatically insert the appropriate mangled symbol name into the assembly code.
 
+By default, `asm!` assumes that any register not specified as an output will have its contents preserved by the assembly code. The [`clobber_abi`](#abi-clobbers) argument to `asm!` tells the compiler to automatically insert the necessary clobber operands according to the given calling convention ABI: any register which is not fully preserved in that ABI will be treated as clobbered.
+
 ## Register template modifiers
 
 In some cases, fine control is needed over the way a register name is formatted when inserted into the template string. This is needed when an architecture's assembly language has several names for the same register, each typically being a "view" over a subset of the register (e.g. the low 32 bits of a 64-bit register).
@@ -456,12 +453,25 @@ reg_spec := <register class> / "<explicit register>"
 operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
 reg_operand := dir_spec "(" reg_spec ")" operand_expr
 operand := reg_operand / "const" const_expr / "sym" path
+clobber_abi := "clobber_abi(" <abi> ")"
 option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw"
 options := "options(" option *["," option] [","] ")"
-asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," options] [","] ")"
+asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," clobber_abi]  ["," options] [","] ")"
 ```
 
-The macro will initially be supported only on ARM, AArch64, Hexagon, PowerPC, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target.
+Inline assembly is currently supported on the following architectures:
+- x86 and x86-64
+- ARM
+- AArch64
+- RISC-V
+- NVPTX
+- PowerPC
+- Hexagon
+- MIPS32r2 and MIPS64r2
+- wasm32
+- BPF
+
+Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target.
 
 [format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax
 
@@ -780,6 +790,24 @@ As stated in the previous section, passing an input value smaller than the regis
 
 [llvm-argmod]: http://llvm.org/docs/LangRef.html#asm-template-argument-modifiers
 
+## ABI clobbers
+
+The `clobber_abi` keyword can be used to apply a default set of clobbers to an `asm` block. This will automatically insert the necessary clobber constraints as needed for calling a function with a particular calling convention: if the calling convention does not fully preserve the value of a register across a call then a `lateout("reg") _` is implicitly added to the operands list.
+
+Generic register class outputs are disallowed by the compiler when `clobber_abi` is used: all outputs must specify an explicit register. Explicit register outputs have precedence over the implicit clobbers inserted by `clobber_abi`: a clobber will only be inserted for a register if that register is not used as an output.
+The following ABIs can be used with `clobber_abi`:
+
+| Architecture | ABI name | Clobbered registers |
+| ------------ | -------- | ------------------- |
+| x86-32 | `"C"`, `"system"`, `"efiapi"`, `"cdecl"`, `"stdcall"`, `"fastcall"` | `ax`, `cx`, `dx`, `xmm[0-7]`, `mm[0-7]`, `st([0-7])` |
+| x86-64 | `"C"`, `"system"` (on Windows), `"efiapi"`, `"win64"` | `ax`, `cx`, `dx`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `st([0-7])` |
+| x86-64 | `"C"`, `"system"` (on non-Windows), `"sysv64"` | `ax`, `cx`, `dx`, `si`, `di`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `st([0-7])` |
+| AArch64 | `"C"`, `"system"`, `"efiapi"` | `x[0-17]`, `x30`, `v[0-31]`, `p[0-15]`, `ffr` |
+| ARM | `"C"`, `"system"`, `"efiapi"`, `"aapcs"` | `r[0-3]`, `r12`, `r14`, `s[0-15]`, `d[0-7]`, `d[16-31]` |
+| RISC-V | `"C"`, `"system"`, `"efiapi"` | `x1`, `x[5-7]`, `x[10-17]`, `x[28-31]`, `f[0-7]`, `f[10-17]`, `f[28-31]`, `v[0-31]` |
+
+The list of clobbered registers for each ABI is updated in rustc as architectures gain new registers: this ensures that `asm` clobbers will continue to be correct when LLVM starts using these new registers in its generated code.
+
 ## Options
 
 Flags are used to further influence the behavior of the inline assembly block.
@@ -842,6 +870,7 @@ The compiler performs some additional checks on options:
     - Floating-point status (`FPSR` register).
   - RISC-V
     - Floating-point exception flags in `fcsr` (`fflags`).
+    - Vector extension state (`vtype`, `vl`, `vcsr`).
 - On x86, the direction flag (DF in `EFLAGS`) is clear on entry to an asm block and must be clear on exit.
   - Behavior is undefined if the direction flag is set on exiting an asm block.
 - The requirement of restoring the stack pointer and non-output registers to their original value only applies when exiting an `asm!` block.
diff --git a/src/test/codegen/asm-clobber_abi.rs b/src/test/codegen/asm-clobber_abi.rs
new file mode 100644
index 00000000000..d589a7c6688
--- /dev/null
+++ b/src/test/codegen/asm-clobber_abi.rs
@@ -0,0 +1,35 @@
+// compile-flags: -O
+// only-x86_64
+
+#![crate_type = "rlib"]
+#![feature(asm)]
+
+// CHECK-LABEL: @clobber_sysv64
+// CHECK: ={ax},={cx},={dx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+#[no_mangle]
+pub unsafe fn clobber_sysv64() {
+    asm!("", clobber_abi("sysv64"));
+}
+
+// CHECK-LABEL: @clobber_win64
+// CHECK: ={ax},={cx},={dx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+#[no_mangle]
+pub unsafe fn clobber_win64() {
+    asm!("", clobber_abi("win64"));
+}
+
+// CHECK-LABEL: @clobber_sysv64
+// CHECK: =&{dx},={ax},={cx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+#[no_mangle]
+pub unsafe fn clobber_sysv64_edx() {
+    let foo: i32;
+    asm!("", out("edx") foo, clobber_abi("sysv64"));
+}
+
+// CHECK-LABEL: @clobber_win64
+// CHECK: =&{dx},={ax},={cx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
+#[no_mangle]
+pub unsafe fn clobber_win64_edx() {
+    let foo: i32;
+    asm!("", out("edx") foo, clobber_abi("win64"));
+}
diff --git a/src/test/ui/asm/bad-options.rs b/src/test/ui/asm/bad-options.rs
index 923d65bfd96..dc61d1612e8 100644
--- a/src/test/ui/asm/bad-options.rs
+++ b/src/test/ui/asm/bad-options.rs
@@ -9,12 +9,20 @@ fn main() {
         //~^ ERROR the `nomem` and `readonly` options are mutually exclusive
         asm!("", options(pure, nomem, noreturn));
         //~^ ERROR the `pure` and `noreturn` options are mutually exclusive
-        //~^^ ERROR asm with `pure` option must have at least one output
+        //~^^ ERROR asm with the `pure` option must have at least one output
         asm!("{}", in(reg) foo, options(pure, nomem));
-        //~^ ERROR asm with `pure` option must have at least one output
+        //~^ ERROR asm with the `pure` option must have at least one output
         asm!("{}", out(reg) foo, options(noreturn));
         //~^ ERROR asm outputs are not allowed with the `noreturn` option
     }
+
+    unsafe {
+        asm!("", clobber_abi("foo"));
+        //~^ ERROR invalid ABI for `clobber_abi`
+        asm!("{}", out(reg) foo, clobber_abi("C"));
+        //~^ ERROR asm with `clobber_abi` must specify explicit registers for outputs
+        asm!("", out("eax") foo, clobber_abi("C"));
+    }
 }
 
 global_asm!("", options(nomem));
diff --git a/src/test/ui/asm/bad-options.stderr b/src/test/ui/asm/bad-options.stderr
index 9039483be4b..8cfd450ab02 100644
--- a/src/test/ui/asm/bad-options.stderr
+++ b/src/test/ui/asm/bad-options.stderr
@@ -10,13 +10,13 @@ error: the `pure` and `noreturn` options are mutually exclusive
 LL |         asm!("", options(pure, nomem, noreturn));
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: asm with `pure` option must have at least one output
+error: asm with the `pure` option must have at least one output
   --> $DIR/bad-options.rs:10:18
    |
 LL |         asm!("", options(pure, nomem, noreturn));
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: asm with `pure` option must have at least one output
+error: asm with the `pure` option must have at least one output
   --> $DIR/bad-options.rs:13:33
    |
 LL |         asm!("{}", in(reg) foo, options(pure, nomem));
@@ -28,41 +28,57 @@ error: asm outputs are not allowed with the `noreturn` option
 LL |         asm!("{}", out(reg) foo, options(noreturn));
    |                    ^^^^^^^^^^^^
 
+error: asm with `clobber_abi` must specify explicit registers for outputs
+  --> $DIR/bad-options.rs:22:20
+   |
+LL |         asm!("{}", out(reg) foo, clobber_abi("C"));
+   |                    ^^^^^^^^^^^^  ---------------- clobber_abi
+   |                    |
+   |                    generic outputs
+
 error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
-  --> $DIR/bad-options.rs:20:25
+  --> $DIR/bad-options.rs:28:25
    |
 LL | global_asm!("", options(nomem));
    |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `readonly`
-  --> $DIR/bad-options.rs:22:25
+  --> $DIR/bad-options.rs:30:25
    |
 LL | global_asm!("", options(readonly));
    |                         ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `noreturn`
-  --> $DIR/bad-options.rs:24:25
+  --> $DIR/bad-options.rs:32:25
    |
 LL | global_asm!("", options(noreturn));
    |                         ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `pure`
-  --> $DIR/bad-options.rs:26:25
+  --> $DIR/bad-options.rs:34:25
    |
 LL | global_asm!("", options(pure));
    |                         ^^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `nostack`
-  --> $DIR/bad-options.rs:28:25
+  --> $DIR/bad-options.rs:36:25
    |
 LL | global_asm!("", options(nostack));
    |                         ^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `preserves_flags`
-  --> $DIR/bad-options.rs:30:25
+  --> $DIR/bad-options.rs:38:25
    |
 LL | global_asm!("", options(preserves_flags));
    |                         ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
 
-error: aborting due to 11 previous errors
+error: invalid ABI for `clobber_abi`
+  --> $DIR/bad-options.rs:20:18
+   |
+LL |         asm!("", clobber_abi("foo"));
+   |                  ^^^^^^^^^^^^^^^^^^
+   |
+   = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64`
+
+error: aborting due to 13 previous errors
 
diff --git a/src/test/ui/asm/naked-functions.rs b/src/test/ui/asm/naked-functions.rs
index 900f4443a1f..fbf187040f9 100644
--- a/src/test/ui/asm/naked-functions.rs
+++ b/src/test/ui/asm/naked-functions.rs
@@ -124,7 +124,7 @@ unsafe extern "C" fn invalid_options() {
 #[naked]
 unsafe extern "C" fn invalid_options_continued() {
     asm!("", options(readonly, nostack), options(pure));
-    //~^ ERROR asm with `pure` option must have at least one output
+    //~^ ERROR asm with the `pure` option must have at least one output
     //~| WARN asm options unsupported in naked functions: `nostack`, `pure`, `readonly`
     //~| WARN this was previously accepted
     //~| WARN asm in naked functions must use `noreturn` option
diff --git a/src/test/ui/asm/naked-functions.stderr b/src/test/ui/asm/naked-functions.stderr
index 231a6239cbd..16d3472c28a 100644
--- a/src/test/ui/asm/naked-functions.stderr
+++ b/src/test/ui/asm/naked-functions.stderr
@@ -1,4 +1,4 @@
-error: asm with `pure` option must have at least one output
+error: asm with the `pure` option must have at least one output
   --> $DIR/naked-functions.rs:126:14
    |
 LL |     asm!("", options(readonly, nostack), options(pure));
diff --git a/src/test/ui/asm/parse-error.rs b/src/test/ui/asm/parse-error.rs
index e62e6668415..3ec3c72d38d 100644
--- a/src/test/ui/asm/parse-error.rs
+++ b/src/test/ui/asm/parse-error.rs
@@ -13,7 +13,7 @@ fn main() {
         asm!("{}" foo);
         //~^ ERROR expected token: `,`
         asm!("{}", foo);
-        //~^ ERROR expected operand, options, or additional template string
+        //~^ ERROR expected operand, clobber_abi, options, or additional template string
         asm!("{}", in foo);
         //~^ ERROR expected `(`, found `foo`
         asm!("{}", in(reg foo));
@@ -37,6 +37,21 @@ fn main() {
         asm!("{}", options(), const foo);
         //~^ ERROR arguments are not allowed after options
         //~^^ ERROR attempt to use a non-constant value in a constant
+        asm!("", clobber_abi(foo));
+        //~^ ERROR expected string literal
+        asm!("", clobber_abi("C" foo));
+        //~^ ERROR expected `)`, found `foo`
+        asm!("", clobber_abi("C", foo));
+        //~^ ERROR expected `)`, found `,`
+        asm!("{}", clobber_abi("C"), const foo);
+        //~^ ERROR arguments are not allowed after clobber_abi
+        //~^^ ERROR attempt to use a non-constant value in a constant
+        asm!("", options(), clobber_abi("C"));
+        //~^ ERROR clobber_abi is not allowed after options
+        asm!("{}", options(), clobber_abi("C"), const foo);
+        //~^ ERROR clobber_abi is not allowed after options
+        asm!("", clobber_abi("C"), clobber_abi("C"));
+        //~^ ERROR clobber_abi specified multiple times
         asm!("{a}", a = const foo, a = const bar);
         //~^ ERROR duplicate argument named `a`
         //~^^ ERROR argument never used
@@ -86,6 +101,21 @@ global_asm!("", options(nomem, FOO));
 //~^ ERROR expected one of
 global_asm!("{}", options(), const FOO);
 //~^ ERROR arguments are not allowed after options
+global_asm!("", clobber_abi(FOO));
+//~^ ERROR expected string literal
+global_asm!("", clobber_abi("C" FOO));
+//~^ ERROR expected `)`, found `FOO`
+global_asm!("", clobber_abi("C", FOO));
+//~^ ERROR expected `)`, found `,`
+global_asm!("{}", clobber_abi("C"), const FOO);
+//~^ ERROR arguments are not allowed after clobber_abi
+//~^^ ERROR `clobber_abi` cannot be used with `global_asm!`
+global_asm!("", options(), clobber_abi("C"));
+//~^ ERROR clobber_abi is not allowed after options
+global_asm!("{}", options(), clobber_abi("C"), const FOO);
+//~^ ERROR clobber_abi is not allowed after options
+global_asm!("", clobber_abi("C"), clobber_abi("C"));
+//~^ ERROR clobber_abi specified multiple times
 global_asm!("{a}", a = const FOO, a = const BAR);
 //~^ ERROR duplicate argument named `a`
 //~^^ ERROR argument never used
diff --git a/src/test/ui/asm/parse-error.stderr b/src/test/ui/asm/parse-error.stderr
index 33dca61dc8e..423003c99d4 100644
--- a/src/test/ui/asm/parse-error.stderr
+++ b/src/test/ui/asm/parse-error.stderr
@@ -16,11 +16,11 @@ error: expected token: `,`
 LL |         asm!("{}" foo);
    |                   ^^^ expected `,`
 
-error: expected operand, options, or additional template string
+error: expected operand, clobber_abi, options, or additional template string
   --> $DIR/parse-error.rs:15:20
    |
 LL |         asm!("{}", foo);
-   |                    ^^^ expected operand, options, or additional template string
+   |                    ^^^ expected operand, clobber_abi, options, or additional template string
 
 error: expected `(`, found `foo`
   --> $DIR/parse-error.rs:17:23
@@ -90,8 +90,58 @@ LL |         asm!("{}", options(), const foo);
    |                    |
    |                    previous options
 
+error: expected string literal
+  --> $DIR/parse-error.rs:40:30
+   |
+LL |         asm!("", clobber_abi(foo));
+   |                              ^^^ not a string literal
+
+error: expected `)`, found `foo`
+  --> $DIR/parse-error.rs:42:34
+   |
+LL |         asm!("", clobber_abi("C" foo));
+   |                                  ^^^ expected `)`
+
+error: expected `)`, found `,`
+  --> $DIR/parse-error.rs:44:33
+   |
+LL |         asm!("", clobber_abi("C", foo));
+   |                                 ^ expected `)`
+
+error: arguments are not allowed after clobber_abi
+  --> $DIR/parse-error.rs:46:38
+   |
+LL |         asm!("{}", clobber_abi("C"), const foo);
+   |                    ----------------  ^^^^^^^^^ argument
+   |                    |
+   |                    clobber_abi
+
+error: clobber_abi is not allowed after options
+  --> $DIR/parse-error.rs:49:29
+   |
+LL |         asm!("", options(), clobber_abi("C"));
+   |                  ---------  ^^^^^^^^^^^^^^^^
+   |                  |
+   |                  options
+
+error: clobber_abi is not allowed after options
+  --> $DIR/parse-error.rs:51:31
+   |
+LL |         asm!("{}", options(), clobber_abi("C"), const foo);
+   |                    ---------  ^^^^^^^^^^^^^^^^
+   |                    |
+   |                    options
+
+error: clobber_abi specified multiple times
+  --> $DIR/parse-error.rs:53:36
+   |
+LL |         asm!("", clobber_abi("C"), clobber_abi("C"));
+   |                  ----------------  ^^^^^^^^^^^^^^^^
+   |                  |
+   |                  clobber_abi previously specified here
+
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:40:36
+  --> $DIR/parse-error.rs:55:36
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                     -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -99,7 +149,7 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    |                     previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:40:36
+  --> $DIR/parse-error.rs:55:36
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                                    ^^^^^^^^^^^^^ argument never used
@@ -107,13 +157,13 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
 error: explicit register arguments cannot have names
-  --> $DIR/parse-error.rs:45:18
+  --> $DIR/parse-error.rs:60:18
    |
 LL |         asm!("", a = in("eax") foo);
    |                  ^^^^^^^^^^^^^^^^^
 
 error: named arguments cannot follow explicit register arguments
-  --> $DIR/parse-error.rs:47:36
+  --> $DIR/parse-error.rs:62:36
    |
 LL |         asm!("{a}", in("eax") foo, a = const bar);
    |                     -------------  ^^^^^^^^^^^^^ named argument
@@ -121,7 +171,7 @@ LL |         asm!("{a}", in("eax") foo, a = const bar);
    |                     explicit register argument
 
 error: named arguments cannot follow explicit register arguments
-  --> $DIR/parse-error.rs:50:36
+  --> $DIR/parse-error.rs:65:36
    |
 LL |         asm!("{a}", in("eax") foo, a = const bar);
    |                     -------------  ^^^^^^^^^^^^^ named argument
@@ -129,27 +179,27 @@ LL |         asm!("{a}", in("eax") foo, a = const bar);
    |                     explicit register argument
 
 error: positional arguments cannot follow named arguments or explicit register arguments
-  --> $DIR/parse-error.rs:53:36
+  --> $DIR/parse-error.rs:68:36
    |
 LL |         asm!("{1}", in("eax") foo, const bar);
    |                     -------------  ^^^^^^^^^ positional argument
    |                     |
    |                     explicit register argument
 
-error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""`
-  --> $DIR/parse-error.rs:56:29
+error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""`
+  --> $DIR/parse-error.rs:71:29
    |
 LL |         asm!("", options(), "");
-   |                             ^^ expected one of 8 possible tokens
+   |                             ^^ expected one of 9 possible tokens
 
-error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
-  --> $DIR/parse-error.rs:58:33
+error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
+  --> $DIR/parse-error.rs:73:33
    |
 LL |         asm!("{}", in(reg) foo, "{}", out(reg) foo);
-   |                                 ^^^^ expected one of 8 possible tokens
+   |                                 ^^^^ expected one of 9 possible tokens
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:60:14
+  --> $DIR/parse-error.rs:75:14
    |
 LL |         asm!(format!("{{{}}}", 0), in(reg) foo);
    |              ^^^^^^^^^^^^^^^^^^^^
@@ -157,7 +207,7 @@ LL |         asm!(format!("{{{}}}", 0), in(reg) foo);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:62:21
+  --> $DIR/parse-error.rs:77:21
    |
 LL |         asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
    |                     ^^^^^^^^^^^^^^^^^^^^
@@ -165,69 +215,125 @@ LL |         asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: requires at least a template string argument
-  --> $DIR/parse-error.rs:69:1
+  --> $DIR/parse-error.rs:84:1
    |
 LL | global_asm!();
    | ^^^^^^^^^^^^^^
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:71:13
+  --> $DIR/parse-error.rs:86:13
    |
 LL | global_asm!(FOO);
    |             ^^^
 
 error: expected token: `,`
-  --> $DIR/parse-error.rs:73:18
+  --> $DIR/parse-error.rs:88:18
    |
 LL | global_asm!("{}" FOO);
    |                  ^^^ expected `,`
 
 error: expected operand, options, or additional template string
-  --> $DIR/parse-error.rs:75:19
+  --> $DIR/parse-error.rs:90:19
    |
 LL | global_asm!("{}", FOO);
    |                   ^^^ expected operand, options, or additional template string
 
 error: expected expression, found end of macro arguments
-  --> $DIR/parse-error.rs:77:24
+  --> $DIR/parse-error.rs:92:24
    |
 LL | global_asm!("{}", const);
    |                        ^ expected expression
 
 error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
-  --> $DIR/parse-error.rs:79:30
+  --> $DIR/parse-error.rs:94:30
    |
 LL | global_asm!("{}", const(reg) FOO);
    |                              ^^^ expected one of `,`, `.`, `?`, or an operator
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
-  --> $DIR/parse-error.rs:81:25
+  --> $DIR/parse-error.rs:96:25
    |
 LL | global_asm!("", options(FOO));
    |                         ^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
-  --> $DIR/parse-error.rs:83:25
+  --> $DIR/parse-error.rs:98:25
    |
 LL | global_asm!("", options(nomem FOO));
    |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
-  --> $DIR/parse-error.rs:85:25
+  --> $DIR/parse-error.rs:100:25
    |
 LL | global_asm!("", options(nomem, FOO));
    |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: arguments are not allowed after options
-  --> $DIR/parse-error.rs:87:30
+  --> $DIR/parse-error.rs:102:30
    |
 LL | global_asm!("{}", options(), const FOO);
    |                   ---------  ^^^^^^^^^ argument
    |                   |
    |                   previous options
 
+error: expected string literal
+  --> $DIR/parse-error.rs:104:29
+   |
+LL | global_asm!("", clobber_abi(FOO));
+   |                             ^^^ not a string literal
+
+error: expected `)`, found `FOO`
+  --> $DIR/parse-error.rs:106:33
+   |
+LL | global_asm!("", clobber_abi("C" FOO));
+   |                                 ^^^ expected `)`
+
+error: expected `)`, found `,`
+  --> $DIR/parse-error.rs:108:32
+   |
+LL | global_asm!("", clobber_abi("C", FOO));
+   |                                ^ expected `)`
+
+error: arguments are not allowed after clobber_abi
+  --> $DIR/parse-error.rs:110:37
+   |
+LL | global_asm!("{}", clobber_abi("C"), const FOO);
+   |                   ----------------  ^^^^^^^^^ argument
+   |                   |
+   |                   clobber_abi
+
+error: `clobber_abi` cannot be used with `global_asm!`
+  --> $DIR/parse-error.rs:110:19
+   |
+LL | global_asm!("{}", clobber_abi("C"), const FOO);
+   |                   ^^^^^^^^^^^^^^^^
+
+error: clobber_abi is not allowed after options
+  --> $DIR/parse-error.rs:113:28
+   |
+LL | global_asm!("", options(), clobber_abi("C"));
+   |                 ---------  ^^^^^^^^^^^^^^^^
+   |                 |
+   |                 options
+
+error: clobber_abi is not allowed after options
+  --> $DIR/parse-error.rs:115:30
+   |
+LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
+   |                   ---------  ^^^^^^^^^^^^^^^^
+   |                   |
+   |                   options
+
+error: clobber_abi specified multiple times
+  --> $DIR/parse-error.rs:117:35
+   |
+LL | global_asm!("", clobber_abi("C"), clobber_abi("C"));
+   |                 ----------------  ^^^^^^^^^^^^^^^^
+   |                 |
+   |                 clobber_abi previously specified here
+
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:89:35
+  --> $DIR/parse-error.rs:119:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -235,27 +341,27 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:89:35
+  --> $DIR/parse-error.rs:119:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                                   ^^^^^^^^^^^^^ argument never used
    |
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
-error: expected one of `const` or `options`, found `""`
-  --> $DIR/parse-error.rs:92:28
+error: expected one of `clobber_abi`, `const`, or `options`, found `""`
+  --> $DIR/parse-error.rs:122:28
    |
 LL | global_asm!("", options(), "");
-   |                            ^^ expected one of `const` or `options`
+   |                            ^^ expected one of `clobber_abi`, `const`, or `options`
 
-error: expected one of `const` or `options`, found `"{}"`
-  --> $DIR/parse-error.rs:94:30
+error: expected one of `clobber_abi`, `const`, or `options`, found `"{}"`
+  --> $DIR/parse-error.rs:124:30
    |
 LL | global_asm!("{}", const FOO, "{}", const FOO);
-   |                              ^^^^ expected one of `const` or `options`
+   |                              ^^^^ expected one of `clobber_abi`, `const`, or `options`
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:96:13
+  --> $DIR/parse-error.rs:126:13
    |
 LL | global_asm!(format!("{{{}}}", 0), const FOO);
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -263,7 +369,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:98:20
+  --> $DIR/parse-error.rs:128:20
    |
 LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    |                    ^^^^^^^^^^^^^^^^^^^^
@@ -280,7 +386,16 @@ LL |         asm!("{}", options(), const foo);
    |                                     ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:40:31
+  --> $DIR/parse-error.rs:46:44
+   |
+LL |     let mut foo = 0;
+   |      ---------- help: consider using `const` instead of `let`: `const foo`
+...
+LL |         asm!("{}", clobber_abi("C"), const foo);
+   |                                            ^^^ non-constant value
+
+error[E0435]: attempt to use a non-constant value in a constant
+  --> $DIR/parse-error.rs:55:31
    |
 LL |     let mut foo = 0;
    |      ---------- help: consider using `const` instead of `let`: `const foo`
@@ -289,7 +404,7 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    |                               ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:40:46
+  --> $DIR/parse-error.rs:55:46
    |
 LL |     let mut bar = 0;
    |      ---------- help: consider using `const` instead of `let`: `const bar`
@@ -298,7 +413,7 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    |                                              ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:47:46
+  --> $DIR/parse-error.rs:62:46
    |
 LL |     let mut bar = 0;
    |      ---------- help: consider using `const` instead of `let`: `const bar`
@@ -307,7 +422,7 @@ LL |         asm!("{a}", in("eax") foo, a = const bar);
    |                                              ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:50:46
+  --> $DIR/parse-error.rs:65:46
    |
 LL |     let mut bar = 0;
    |      ---------- help: consider using `const` instead of `let`: `const bar`
@@ -316,7 +431,7 @@ LL |         asm!("{a}", in("eax") foo, a = const bar);
    |                                              ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:53:42
+  --> $DIR/parse-error.rs:68:42
    |
 LL |     let mut bar = 0;
    |      ---------- help: consider using `const` instead of `let`: `const bar`
@@ -324,6 +439,6 @@ LL |     let mut bar = 0;
 LL |         asm!("{1}", in("eax") foo, const bar);
    |                                          ^^^ non-constant value
 
-error: aborting due to 47 previous errors
+error: aborting due to 63 previous errors
 
 For more information about this error, try `rustc --explain E0435`.