mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 23:04:33 +00:00
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:
commit
687f929c9b
@ -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.
|
||||
|
@ -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(())
|
||||
|
@ -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(¬e);
|
||||
}
|
||||
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(¬e);
|
||||
}
|
||||
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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user