Auto merge of #87581 - Amanieu:asm_clobber_abi, r=nagisa

Add support for clobber_abi to asm!

This PR adds the `clobber_abi` feature that was proposed in #81092.

Fixes #81092

cc `@rust-lang/wg-inline-asm`

r? `@nagisa`
This commit is contained in:
bors 2021-08-14 22:29:27 +00:00
commit 85109e257a
15 changed files with 668 additions and 87 deletions

View File

@ -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>,
}

View File

@ -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[..]);

View File

@ -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();

View File

@ -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>(

View File

@ -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 => {}

View File

@ -378,6 +378,7 @@ symbols! {
char,
client,
clippy,
clobber_abi,
clone,
clone_closures,
clone_from,

View File

@ -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,
}
},
}
}
}

View File

@ -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.

View File

@ -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"));
}

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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

View File

@ -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`.