From e9fb7bada1dc2d288ef44852b0e1986dbcfaaab5 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Thu, 29 Jul 2021 11:15:50 +0200 Subject: [PATCH 1/3] Fix error message typo for pure asm without outputs --- compiler/rustc_builtin_macros/src/asm.rs | 2 +- src/test/ui/asm/bad-options.rs | 4 ++-- src/test/ui/asm/bad-options.stderr | 4 ++-- src/test/ui/asm/naked-functions.rs | 2 +- src/test/ui/asm/naked-functions.stderr | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index ff13f0d4e42..df3aed3bca2 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -273,7 +273,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(); } diff --git a/src/test/ui/asm/bad-options.rs b/src/test/ui/asm/bad-options.rs index 923d65bfd96..1f5eb4afbd3 100644 --- a/src/test/ui/asm/bad-options.rs +++ b/src/test/ui/asm/bad-options.rs @@ -9,9 +9,9 @@ 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 } diff --git a/src/test/ui/asm/bad-options.stderr b/src/test/ui/asm/bad-options.stderr index 9039483be4b..20f00c13c75 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)); 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)); From 2f4612260d35ef162c088b6b1a87f1e75717acb6 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Thu, 29 Jul 2021 13:22:21 +0200 Subject: [PATCH 2/3] Mark RISC-V vector state as clobbered in inline assembly --- compiler/rustc_codegen_llvm/src/asm.rs | 9 ++++++++- src/doc/unstable-book/src/library-features/asm.md | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index ebc3773df57..43538b8ab35 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/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index 8ff600d5334..539499cc568 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -842,6 +842,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. From 3fd463a5ca98c54b4e79271280c69ce5468e1173 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Thu, 29 Jul 2021 13:43:26 +0200 Subject: [PATCH 3/3] Add support for clobber_abi to asm! --- compiler/rustc_ast/src/ast.rs | 1 + compiler/rustc_ast_lowering/src/asm.rs | 56 ++++- compiler/rustc_ast_pretty/src/pprust/state.rs | 10 + compiler/rustc_builtin_macros/src/asm.rs | 107 +++++++++- compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/asm/mod.rs | 182 ++++++++++++++++ .../unstable-book/src/library-features/asm.md | 70 ++++-- src/test/codegen/asm-clobber_abi.rs | 35 +++ src/test/ui/asm/bad-options.rs | 8 + src/test/ui/asm/bad-options.stderr | 30 ++- src/test/ui/asm/parse-error.rs | 32 ++- src/test/ui/asm/parse-error.stderr | 199 ++++++++++++++---- 12 files changed, 652 insertions(+), 79 deletions(-) create mode 100644 src/test/codegen/asm-clobber_abi.rs 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, pub operands: Vec<(InlineAsmOperand, Span)>, + pub clobber_abi: Option<(Symbol, Span)>, pub options: InlineAsmOptions, pub line_spans: Vec, } 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(®) { + 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 df3aed3bca2..3eaa629448a 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, reg_args: FxHashSet, + clobber_abi: Option<(Symbol, Span)>, options: ast::InlineAsmOptions, options_spans: Vec, } @@ -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); + } } _ => {} } @@ -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, @@ -660,7 +747,13 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3f5d8273b38..e4e7139ce73 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -377,6 +377,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 { + 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 539499cc568..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 := / "" 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(" ")" 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. 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 1f5eb4afbd3..dc61d1612e8 100644 --- a/src/test/ui/asm/bad-options.rs +++ b/src/test/ui/asm/bad-options.rs @@ -15,6 +15,14 @@ fn main() { 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 20f00c13c75..8cfd450ab02 100644 --- a/src/test/ui/asm/bad-options.stderr +++ b/src/test/ui/asm/bad-options.stderr @@ -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/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`.