Rollup merge of #73364 - joshtriplett:inline-asm, r=Amanieu

asm: Allow multiple template string arguments; interpret them as newline-separated

Allow the `asm!` macro to accept a series of template arguments, and interpret them as if they were concatenated with a '\n' between them. This allows writing an `asm!` where each line of assembly appears in a separate template string argument.

This syntax makes it possible for rustfmt to reliably format and indent each line of assembly, without risking changes to the inside of a template string. It also avoids the complexity of having the user carefully format and indent a multi-line string (including where to put the surrounding quotes), and avoids the extra indentation and lines of a call to `concat!`.

For example, rewriting the second example from the [blog post on the new inline assembly syntax](https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html) using multiple template strings:

```rust

fn main() {
    let mut bits = [0u8; 64];
    for value in 0..=1024u64 {
        let popcnt;
        unsafe {
            asm!(
                "    popcnt {popcnt}, {v}",
                "2:",
                "    blsi rax, {v}",
                "    jz 1f",
                "    xor {v}, rax",
                "    tzcnt rax, rax",
                "    stosb",
                "    jmp 2b",
                "1:",
                v = inout(reg) value => _,
                popcnt = out(reg) popcnt,
                out("rax") _, // scratch
                inout("rdi") bits.as_mut_ptr() => _,
            );
        }
        println!("bits of {}: {:?}", value, &bits[0..popcnt]);
    }
}
```

Note that all the template strings must appear before all other arguments; you cannot, for instance, provide a series of template strings intermixed with the corresponding operands.
This commit is contained in:
Manish Goregaokar 2020-06-19 09:15:14 -07:00 committed by GitHub
commit 687f929c9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 571 additions and 179 deletions

View File

@ -68,10 +68,13 @@ Let us see another example that also uses an input:
let i: u64 = 3;
let o: u64;
unsafe {
asm!("
mov {0}, {1}
add {0}, {number}
", out(reg) o, in(reg) i, number = const 5);
asm!(
"mov {0}, {1}",
"add {0}, {number}",
out(reg) o,
in(reg) i,
number = const 5,
);
}
assert_eq!(o, 8);
```
@ -82,13 +85,18 @@ and then adding `5` to it.
The example shows a few things:
First we can see that inputs are declared by writing `in` instead of `out`.
First, we can see that `asm!` allows multiple template string arguments; each
one is treated as a separate line of assembly code, as if they were all joined
together with newlines between them. This makes it easy to format assembly
code.
Second one of our operands has a type we haven't seen yet, `const`.
Second, we can see that inputs are declared by writing `in` instead of `out`.
Third, one of our operands has a type we haven't seen yet, `const`.
This tells the compiler to expand this argument to value directly inside the assembly template.
This is only possible for constants and literals.
Third we can see that we can specify an argument number, or name as in any format string.
Fourth, we can see that we can specify an argument number, or name as in any format string.
For inline assembly templates this is particularly useful as arguments are often used more than once.
For more complex inline assembly using this facility is generally recommended, as it improves
readability, and allows reordering instructions without changing the argument order.
@ -137,10 +145,13 @@ let mut a: u64 = 4;
let b: u64 = 4;
let c: u64 = 4;
unsafe {
asm!("
add {0}, {1}
add {0}, {2}
", inout(reg) a, in(reg) b, in(reg) c);
asm!(
"add {0}, {1}",
"add {0}, {2}",
inout(reg) a,
in(reg) b,
in(reg) c,
);
}
assert_eq!(a, 12);
```
@ -233,7 +244,7 @@ unsafe {
// ECX 0 selects the L0 cache information.
inout("ecx") 0 => ecx,
lateout("ebx") ebx,
lateout("edx") _
lateout("edx") _,
);
}
@ -255,12 +266,14 @@ This can also be used with a general register class (e.g. `reg`) to obtain a scr
// Multiply x by 6 using shifts and adds
let mut x: u64 = 4;
unsafe {
asm!("
mov {tmp}, {x}
shl {tmp}, 1
shl {x}, 2
add {x}, {tmp}
", x = inout(reg) x, tmp = out(reg) _);
asm!(
"mov {tmp}, {x}",
"shl {tmp}, 1",
"shl {x}, 2",
"add {x}, {tmp}",
x = inout(reg) x,
tmp = out(reg) _,
);
}
assert_eq!(x, 4 * 6);
```
@ -338,7 +351,7 @@ unsafe {
asm!(
"add {0}, {1}",
inlateout(reg) a, in(reg) b,
options(pure, nomem, nostack)
options(pure, nomem, nostack),
);
}
assert_eq!(a, 8);
@ -371,17 +384,19 @@ reg_operand := dir_spec "(" reg_spec ")" operand_expr
operand := reg_operand / "const" const_expr / "sym" path
option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "att_syntax"
options := "options(" option *["," option] [","] ")"
asm := "asm!(" format_string *("," [ident "="] operand) ["," options] [","] ")"
asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," options] [","] ")"
```
The macro will initially be supported only on ARM, AArch64, Hexagon, 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.
[format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax
## Template string
## Template string arguments
The assembler template uses the same syntax as [format strings][format-syntax] (i.e. placeholders are specified by curly braces). The corresponding arguments are accessed in order, by index, or by name. However, implicit named arguments (introduced by [RFC #2795][rfc-2795]) are not supported.
An `asm!` invocation may have one or more template string arguments; an `asm!` with multiple template string arguments is treated as if all the strings were concatenated with a `\n` between them. The expected usage is for each template string argument to correspond to a line of assembly code. All template string arguments must appear before any other arguments.
As with format strings, named arguments must appear after positional arguments. Explicit register operands must appear at the end of the operand list, after named arguments if any.
Explicit register operands cannot be used by placeholders in the template string. All other named and positional operands must appear at least once in the template string, otherwise a compiler error is generated.

View File

@ -1905,7 +1905,7 @@ impl fmt::Display for InlineAsmTemplatePiece {
match c {
'{' => f.write_str("{{")?,
'}' => f.write_str("}}")?,
_ => write!(f, "{}", c.escape_debug())?,
_ => c.fmt(f)?,
}
}
Ok(())

View File

@ -11,7 +11,7 @@ use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{InnerSpan, Span};
struct AsmArgs {
template: P<ast::Expr>,
templates: Vec<P<ast::Expr>>,
operands: Vec<(ast::InlineAsmOperand, Span)>,
named_args: FxHashMap<Symbol, usize>,
reg_args: FxHashSet<usize>,
@ -52,9 +52,9 @@ fn parse_args<'a>(
return Err(err);
}
let template = p.parse_expr()?;
let first_template = p.parse_expr()?;
let mut args = AsmArgs {
template,
templates: vec![first_template],
operands: vec![],
named_args: FxHashMap::default(),
reg_args: FxHashSet::default(),
@ -62,11 +62,11 @@ fn parse_args<'a>(
options_span: None,
};
let mut first = true;
let mut allow_templates = true;
while p.token != token::Eof {
if !p.eat(&token::Comma) {
if first {
// After `asm!(""` we always expect *only* a comma...
if allow_templates {
// After a template string, we always expect *only* a comma...
let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`");
err.span_label(p.token.span, "expected `,`");
p.maybe_annotate_with_ascription(&mut err, false);
@ -76,7 +76,6 @@ fn parse_args<'a>(
return Err(p.expect(&token::Comma).err().unwrap());
}
}
first = false;
if p.token == token::Eof {
break;
} // accept trailing commas
@ -84,6 +83,7 @@ fn parse_args<'a>(
// Parse options
if p.eat(&token::Ident(sym::options, false)) {
parse_options(&mut p, &mut args)?;
allow_templates = false;
continue;
}
@ -94,6 +94,7 @@ fn parse_args<'a>(
let (ident, _) = p.token.ident().unwrap();
p.bump();
p.expect(&token::Eq)?;
allow_templates = false;
Some(ident.name)
} else {
None
@ -135,8 +136,7 @@ fn parse_args<'a>(
} else if p.eat(&token::Ident(kw::Const, false)) {
let expr = p.parse_expr()?;
ast::InlineAsmOperand::Const { expr }
} else {
p.expect(&token::Ident(sym::sym, false))?;
} else if p.eat(&token::Ident(sym::sym, false)) {
let expr = p.parse_expr()?;
match expr.kind {
ast::ExprKind::Path(..) => {}
@ -147,8 +147,27 @@ fn parse_args<'a>(
}
}
ast::InlineAsmOperand::Sym { expr }
} else if allow_templates {
let template = p.parse_expr()?;
// If it can't possibly expand to a string, provide diagnostics here to include other
// things it could have been.
match template.kind {
ast::ExprKind::Lit(ast::Lit { kind: ast::LitKind::Str(..), .. }) => {}
ast::ExprKind::MacCall(..) => {}
_ => {
let errstr = "expected operand, options, or additional template string";
let mut err = ecx.struct_span_err(template.span, errstr);
err.span_label(template.span, errstr);
return Err(err);
}
}
args.templates.push(template);
continue;
} else {
return Err(p.expect_one_of(&[], &[]).unwrap_err());
};
allow_templates = false;
let span = span_start.to(p.prev_token.span);
let slot = args.operands.len();
args.operands.push((op, span));
@ -330,155 +349,180 @@ fn parse_reg<'a>(
}
fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast::Expr> {
let msg = "asm template must be a string literal";
let template_sp = args.template.span;
let (template_str, template_style, template_span) =
match expr_to_spanned_string(ecx, args.template, msg) {
Ok(template) => template,
Err(err) => {
if let Some(mut err) = err {
err.emit();
}
return DummyResult::raw_expr(sp, true);
}
};
let str_style = match template_style {
ast::StrStyle::Cooked => None,
ast::StrStyle::Raw(raw) => Some(raw as usize),
};
let template_str = &template_str.as_str();
let template_snippet = ecx.source_map().span_to_snippet(template_sp).ok();
let mut parser = parse::Parser::new(
template_str,
str_style,
template_snippet,
false,
parse::ParseMode::InlineAsm,
);
let mut unverified_pieces = Vec::new();
while let Some(piece) = parser.next() {
if !parser.errors.is_empty() {
break;
} else {
unverified_pieces.push(piece);
}
}
if !parser.errors.is_empty() {
let err = parser.errors.remove(0);
let err_sp = template_span.from_inner(err.span);
let mut e = ecx
.struct_span_err(err_sp, &format!("invalid asm template string: {}", err.description));
e.span_label(err_sp, err.label + " in asm template string");
if let Some(note) = err.note {
e.note(&note);
}
if let Some((label, span)) = err.secondary_label {
let err_sp = template_span.from_inner(span);
e.span_label(err_sp, label);
}
e.emit();
return DummyResult::raw_expr(sp, true);
}
let mut template = vec![];
// Register operands are implicitly used since they are not allowed to be
// referenced in the template string.
let mut used = vec![false; args.operands.len()];
for pos in &args.reg_args {
used[*pos] = true;
}
let named_pos: FxHashMap<usize, Symbol> =
args.named_args.iter().map(|(&sym, &idx)| (idx, sym)).collect();
let mut arg_spans = parser.arg_places.iter().map(|span| template_span.from_inner(*span));
let mut template = vec![];
for piece in unverified_pieces {
match piece {
parse::Piece::String(s) => {
template.push(ast::InlineAsmTemplatePiece::String(s.to_string()))
}
parse::Piece::NextArgument(arg) => {
let span = arg_spans.next().unwrap_or(template_sp);
let mut line_spans = Vec::with_capacity(args.templates.len());
let mut curarg = 0;
let operand_idx = match arg.position {
parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => {
if idx >= args.operands.len()
|| named_pos.contains_key(&idx)
|| args.reg_args.contains(&idx)
{
let msg = format!("invalid reference to argument at index {}", idx);
let mut err = ecx.struct_span_err(span, &msg);
err.span_label(span, "from here");
for template_expr in args.templates.into_iter() {
if !template.is_empty() {
template.push(ast::InlineAsmTemplatePiece::String("\n".to_string()));
}
let positional_args =
args.operands.len() - args.named_args.len() - args.reg_args.len();
let positional = if positional_args != args.operands.len() {
"positional "
} else {
""
};
let msg = match positional_args {
0 => format!("no {}arguments were given", positional),
1 => format!("there is 1 {}argument", positional),
x => format!("there are {} {}arguments", x, positional),
};
err.note(&msg);
if named_pos.contains_key(&idx) {
err.span_label(args.operands[idx].1, "named argument");
err.span_note(
args.operands[idx].1,
"named arguments cannot be referenced by position",
);
} else if args.reg_args.contains(&idx) {
err.span_label(args.operands[idx].1, "explicit register argument");
err.span_note(
args.operands[idx].1,
"explicit register arguments cannot be used in the asm template",
);
}
err.emit();
None
} else {
Some(idx)
}
let msg = "asm template must be a string literal";
let template_sp = template_expr.span;
let (template_str, template_style, template_span) =
match expr_to_spanned_string(ecx, template_expr, msg) {
Ok(template_part) => template_part,
Err(err) => {
if let Some(mut err) = err {
err.emit();
}
parse::ArgumentNamed(name) => match args.named_args.get(&name) {
Some(&idx) => Some(idx),
None => {
let msg = format!("there is no argument named `{}`", name);
ecx.struct_span_err(span, &msg[..]).emit();
None
}
},
};
let mut chars = arg.format.ty.chars();
let mut modifier = chars.next();
if chars.next().is_some() {
let span = arg
.format
.ty_span
.map(|sp| template_sp.from_inner(sp))
.unwrap_or(template_sp);
ecx.struct_span_err(span, "asm template modifier must be a single character")
.emit();
modifier = None;
return DummyResult::raw_expr(sp, true);
}
};
if let Some(operand_idx) = operand_idx {
used[operand_idx] = true;
template.push(ast::InlineAsmTemplatePiece::Placeholder {
operand_idx,
modifier,
span,
});
let str_style = match template_style {
ast::StrStyle::Cooked => None,
ast::StrStyle::Raw(raw) => Some(raw as usize),
};
let template_str = &template_str.as_str();
let template_snippet = ecx.source_map().span_to_snippet(template_sp).ok();
let mut parser = parse::Parser::new(
template_str,
str_style,
template_snippet,
false,
parse::ParseMode::InlineAsm,
);
parser.curarg = curarg;
let mut unverified_pieces = Vec::new();
while let Some(piece) = parser.next() {
if !parser.errors.is_empty() {
break;
} else {
unverified_pieces.push(piece);
}
}
if !parser.errors.is_empty() {
let err = parser.errors.remove(0);
let err_sp = template_span.from_inner(err.span);
let msg = &format!("invalid asm template string: {}", err.description);
let mut e = ecx.struct_span_err(err_sp, msg);
e.span_label(err_sp, err.label + " in asm template string");
if let Some(note) = err.note {
e.note(&note);
}
if let Some((label, span)) = err.secondary_label {
let err_sp = template_span.from_inner(span);
e.span_label(err_sp, label);
}
e.emit();
return DummyResult::raw_expr(sp, true);
}
curarg = parser.curarg;
let mut arg_spans = parser.arg_places.iter().map(|span| template_span.from_inner(*span));
for piece in unverified_pieces {
match piece {
parse::Piece::String(s) => {
template.push(ast::InlineAsmTemplatePiece::String(s.to_string()))
}
parse::Piece::NextArgument(arg) => {
let span = arg_spans.next().unwrap_or(template_sp);
let operand_idx = match arg.position {
parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => {
if idx >= args.operands.len()
|| named_pos.contains_key(&idx)
|| args.reg_args.contains(&idx)
{
let msg = format!("invalid reference to argument at index {}", idx);
let mut err = ecx.struct_span_err(span, &msg);
err.span_label(span, "from here");
let positional_args = args.operands.len()
- args.named_args.len()
- args.reg_args.len();
let positional = if positional_args != args.operands.len() {
"positional "
} else {
""
};
let msg = match positional_args {
0 => format!("no {}arguments were given", positional),
1 => format!("there is 1 {}argument", positional),
x => format!("there are {} {}arguments", x, positional),
};
err.note(&msg);
if named_pos.contains_key(&idx) {
err.span_label(args.operands[idx].1, "named argument");
err.span_note(
args.operands[idx].1,
"named arguments cannot be referenced by position",
);
} else if args.reg_args.contains(&idx) {
err.span_label(
args.operands[idx].1,
"explicit register argument",
);
err.span_note(
args.operands[idx].1,
"explicit register arguments cannot be used in the asm template",
);
}
err.emit();
None
} else {
Some(idx)
}
}
parse::ArgumentNamed(name) => match args.named_args.get(&name) {
Some(&idx) => Some(idx),
None => {
let msg = format!("there is no argument named `{}`", name);
ecx.struct_span_err(span, &msg[..]).emit();
None
}
},
};
let mut chars = arg.format.ty.chars();
let mut modifier = chars.next();
if chars.next().is_some() {
let span = arg
.format
.ty_span
.map(|sp| template_sp.from_inner(sp))
.unwrap_or(template_sp);
ecx.struct_span_err(
span,
"asm template modifier must be a single character",
)
.emit();
modifier = None;
}
if let Some(operand_idx) = operand_idx {
used[operand_idx] = true;
template.push(ast::InlineAsmTemplatePiece::Placeholder {
operand_idx,
modifier,
span,
});
}
}
}
}
if parser.line_spans.is_empty() {
let template_num_lines = 1 + template_str.matches('\n').count();
line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines));
} else {
line_spans.extend(parser.line_spans.iter().map(|span| template_span.from_inner(*span)));
};
}
let mut unused_operands = vec![];
@ -525,12 +569,6 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast
}
}
let line_spans = if parser.line_spans.is_empty() {
vec![template_sp]
} else {
parser.line_spans.iter().map(|span| template_span.from_inner(*span)).collect()
};
let inline_asm =
ast::InlineAsm { template, operands: args.operands, options: args.options, line_spans };
P(ast::Expr {

View File

@ -178,7 +178,7 @@ pub struct Parser<'a> {
/// Error messages accumulated during parsing
pub errors: Vec<ParseError>,
/// Current position of implicit positional argument pointer
curarg: usize,
pub curarg: usize,
/// `Some(raw count)` when the string is "raw", used to position spans correctly
style: Option<usize>,
/// Start and end byte offset of every successfully parsed argument
@ -243,11 +243,13 @@ impl<'a> Iterator for Parser<'a> {
_ => Some(String(self.string(pos))),
}
} else {
if self.is_literal && self.cur_line_start != self.input.len() {
if self.is_literal {
let start = self.to_span_index(self.cur_line_start);
let end = self.to_span_index(self.input.len());
self.line_spans.push(start.to(end));
self.cur_line_start = self.input.len();
let span = start.to(end);
if self.line_spans.last() != Some(&span) {
self.line_spans.push(span);
}
}
None
}

View File

@ -22,5 +22,13 @@ pub fn main() {
asm!("{0}", inout(reg) b);
asm!("{0} {1}", out(reg) _, inlateout(reg) b => _);
asm!("", out("al") _, lateout("rbx") _);
asm!("inst1\ninst2");
asm!("inst1 {0}, 42\ninst2 {1}, 24", in(reg) a, out(reg) b);
asm!("inst2 {1}, 24\ninst1 {0}, 42", in(reg) a, out(reg) b);
asm!("inst1 {0}, 42\ninst2 {1}, 24", in(reg) a, out(reg) b);
asm!("inst1\ninst2");
asm!("inst1\ninst2");
asm!("inst1\n\tinst2");
asm!("inst1\ninst2\ninst3\ninst4");
}
}

View File

@ -16,5 +16,14 @@ pub fn main() {
asm!("{name}", name = inout(reg) b);
asm!("{} {}", out(reg) _, inlateout(reg) b => _);
asm!("", out("al") _, lateout("rbx") _);
asm!("inst1", "inst2");
asm!("inst1 {}, 42", "inst2 {}, 24", in(reg) a, out(reg) b);
asm!("inst2 {1}, 24", "inst1 {0}, 42", in(reg) a, out(reg) b);
asm!("inst1 {}, 42", "inst2 {name}, 24", in(reg) a, name = out(reg) b);
asm!("inst1
inst2");
asm!("inst1\ninst2");
asm!("inst1\n\tinst2");
asm!("inst1\ninst2", "inst3\ninst4");
}
}

View File

@ -13,7 +13,7 @@ fn main() {
asm!("{}" foo);
//~^ ERROR expected token: `,`
asm!("{}", foo);
//~^ ERROR expected one of
//~^ ERROR expected operand, options, or additional template string
asm!("{}", in foo);
//~^ ERROR expected `(`, found `foo`
asm!("{}", in(reg foo));
@ -52,5 +52,13 @@ fn main() {
//~^ ERROR named arguments cannot follow explicit register arguments
asm!("{1}", in("eax") foo, const bar);
//~^ ERROR positional arguments cannot follow named arguments or explicit register arguments
asm!("", options(), "");
//~^ ERROR expected one of
asm!("{}", in(reg) foo, "{}", out(reg) foo);
//~^ ERROR expected one of
asm!(format!("{{{}}}", 0), in(reg) foo);
//~^ ERROR asm template must be a string literal
asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
//~^ ERROR asm template must be a string literal
}
}

View File

@ -16,11 +16,11 @@ error: expected token: `,`
LL | asm!("{}" foo);
| ^^^ expected `,`
error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `foo`
error: expected operand, options, or additional template string
--> $DIR/parse-error.rs:15:20
|
LL | asm!("{}", foo);
| ^^^ expected one of 8 possible tokens
| ^^^ expected operand, options, or additional template string
error: expected `(`, found `foo`
--> $DIR/parse-error.rs:17:23
@ -160,5 +160,33 @@ LL | asm!("{1}", in("eax") foo, const bar);
| |
| explicit register argument
error: aborting due to 24 previous errors
error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""`
--> $DIR/parse-error.rs:55:29
|
LL | asm!("", options(), "");
| ^^ expected one of 8 possible tokens
error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
--> $DIR/parse-error.rs:57:33
|
LL | asm!("{}", in(reg) foo, "{}", out(reg) foo);
| ^^^^ expected one of 8 possible tokens
error: asm template must be a string literal
--> $DIR/parse-error.rs:59:14
|
LL | asm!(format!("{{{}}}", 0), in(reg) foo);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: asm template must be a string literal
--> $DIR/parse-error.rs:61:21
|
LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 28 previous errors

View File

@ -40,5 +40,85 @@ fn main() {
asm!("movaps %xmm3, (%esi, 2)", options(att_syntax));
//~^ WARN: scale factor without index register is ignored
asm!(
"invalid_instruction",
);
//~^^ ERROR: invalid instruction mnemonic 'invalid_instruction'
asm!(
"mov eax, eax",
"invalid_instruction",
"mov eax, eax",
);
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction'
asm!(
"mov eax, eax\n",
"invalid_instruction",
"mov eax, eax",
);
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction'
asm!(
"mov eax, eax",
concat!("invalid", "_", "instruction"),
"mov eax, eax",
);
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction'
asm!(
concat!("mov eax", ", ", "eax"),
concat!("invalid", "_", "instruction"),
concat!("mov eax", ", ", "eax"),
);
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction'
// Make sure template strings get separated
asm!(
"invalid_instruction1",
"invalid_instruction2",
);
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1'
//~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2'
asm!(
concat!(
"invalid", "_", "instruction1", "\n",
"invalid", "_", "instruction2",
),
);
//~^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1'
//~^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2'
asm!(
concat!(
"invalid", "_", "instruction1", "\n",
"invalid", "_", "instruction2",
),
concat!(
"invalid", "_", "instruction3", "\n",
"invalid", "_", "instruction4",
),
);
//~^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1'
//~^^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2'
//~^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction3'
//~^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction4'
asm!(
concat!(
"invalid", "_", "instruction1", "\n",
"invalid", "_", "instruction2", "\n",
),
concat!(
"invalid", "_", "instruction3", "\n",
"invalid", "_", "instruction4", "\n",
),
);
//~^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1'
//~^^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2'
//~^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction3'
//~^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction4'
}
}

View File

@ -82,5 +82,209 @@ note: instantiated into assembly here
LL | movaps %xmm3, (%esi, 2)
| ^
error: aborting due to 6 previous errors; 1 warning emitted
error: invalid instruction mnemonic 'invalid_instruction'
--> $DIR/srcloc.rs:45:14
|
LL | "invalid_instruction",
| ^
|
note: instantiated into assembly here
--> <inline asm>:2:2
|
LL | invalid_instruction
| ^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction'
--> $DIR/srcloc.rs:51:14
|
LL | "invalid_instruction",
| ^
|
note: instantiated into assembly here
--> <inline asm>:3:1
|
LL | invalid_instruction
| ^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction'
--> $DIR/srcloc.rs:58:14
|
LL | "invalid_instruction",
| ^
|
note: instantiated into assembly here
--> <inline asm>:4:1
|
LL | invalid_instruction
| ^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction'
--> $DIR/srcloc.rs:65:13
|
LL | concat!("invalid", "_", "instruction"),
| ^
|
note: instantiated into assembly here
--> <inline asm>:3:1
|
LL | invalid_instruction
| ^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction'
--> $DIR/srcloc.rs:72:13
|
LL | concat!("invalid", "_", "instruction"),
| ^
|
note: instantiated into assembly here
--> <inline asm>:3:1
|
LL | invalid_instruction
| ^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction1'
--> $DIR/srcloc.rs:79:14
|
LL | "invalid_instruction1",
| ^
|
note: instantiated into assembly here
--> <inline asm>:2:2
|
LL | invalid_instruction1
| ^^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction2'
--> $DIR/srcloc.rs:80:14
|
LL | "invalid_instruction2",
| ^
|
note: instantiated into assembly here
--> <inline asm>:3:1
|
LL | invalid_instruction2
| ^^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction1'
--> $DIR/srcloc.rs:86:13
|
LL | concat!(
| ^
|
note: instantiated into assembly here
--> <inline asm>:2:2
|
LL | invalid_instruction1
| ^^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction2'
--> $DIR/srcloc.rs:86:13
|
LL | concat!(
| ^
|
note: instantiated into assembly here
--> <inline asm>:3:1
|
LL | invalid_instruction2
| ^^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction1'
--> $DIR/srcloc.rs:95:13
|
LL | concat!(
| ^
|
note: instantiated into assembly here
--> <inline asm>:2:2
|
LL | invalid_instruction1
| ^^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction2'
--> $DIR/srcloc.rs:95:13
|
LL | concat!(
| ^
|
note: instantiated into assembly here
--> <inline asm>:3:1
|
LL | invalid_instruction2
| ^^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction3'
--> $DIR/srcloc.rs:99:13
|
LL | concat!(
| ^
|
note: instantiated into assembly here
--> <inline asm>:4:1
|
LL | invalid_instruction3
| ^^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction4'
--> $DIR/srcloc.rs:99:13
|
LL | concat!(
| ^
|
note: instantiated into assembly here
--> <inline asm>:5:1
|
LL | invalid_instruction4
| ^^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction1'
--> $DIR/srcloc.rs:110:13
|
LL | concat!(
| ^
|
note: instantiated into assembly here
--> <inline asm>:2:2
|
LL | invalid_instruction1
| ^^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction2'
--> $DIR/srcloc.rs:110:13
|
LL | concat!(
| ^
|
note: instantiated into assembly here
--> <inline asm>:3:1
|
LL | invalid_instruction2
| ^^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction3'
--> $DIR/srcloc.rs:114:13
|
LL | concat!(
| ^
|
note: instantiated into assembly here
--> <inline asm>:5:1
|
LL | invalid_instruction3
| ^^^^^^^^^^^^^^^^^^^^
error: invalid instruction mnemonic 'invalid_instruction4'
--> $DIR/srcloc.rs:114:13
|
LL | concat!(
| ^
|
note: instantiated into assembly here
--> <inline asm>:6:1
|
LL | invalid_instruction4
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 23 previous errors; 1 warning emitted