]> git.lizzy.rs Git - rust.git/commitdiff
asm: Allow multiple template strings; interpret them as newline-separated
authorJosh Triplett <josh@joshtriplett.org>
Mon, 15 Jun 2020 06:33:55 +0000 (23:33 -0700)
committerJosh Triplett <josh@joshtriplett.org>
Mon, 15 Jun 2020 19:35:27 +0000 (12:35 -0700)
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.

In order to get srcloc mappings right for macros that generate
multi-line string literals, create one line_span for each
line in the string literal, each pointing to the macro.

Make `rustc_parse_format::Parser::curarg` `pub`, so that we can
propagate it from one template string argument to the next.

src/librustc_builtin_macros/asm.rs
src/librustc_parse_format/lib.rs
src/test/pretty/asm.pp
src/test/pretty/asm.rs
src/test/ui/asm/parse-error.rs
src/test/ui/asm/parse-error.stderr
src/test/ui/asm/srcloc.rs
src/test/ui/asm/srcloc.stderr

index afe2231cec67fd3330361d247f92385b7811256d..29885679604647db049912609842e20d07d7de52 100644 (file)
@@ -11,7 +11,7 @@
 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
@@ -146,10 +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));
@@ -331,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()))
+    let mut line_spans = Vec::with_capacity(args.templates.len());
+    let mut curarg = 0;
+
+    for template_expr in args.templates.into_iter() {
+        if !template.is_empty() {
+            template.push(ast::InlineAsmTemplatePiece::String("\n".to_string()));
+        }
+
+        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();
+                    }
+                    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,
+        );
+        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);
             }
-            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 "
+            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 {
-                                ""
-                            };
-                            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",
-                                );
+                                Some(idx)
                             }
-                            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")
+                        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;
-                }
+                        modifier = None;
+                    }
 
-                if let Some(operand_idx) = operand_idx {
-                    used[operand_idx] = true;
-                    template.push(ast::InlineAsmTemplatePiece::Placeholder {
-                        operand_idx,
-                        modifier,
-                        span,
-                    });
+                    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![];
@@ -526,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 {
index 23bf7b35419db9767d20d7340bd20320dc124ca1..a5b5a1090cbfd5052e0d6beb64b0b79581a87698 100644 (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 @@ fn next(&mut self) -> Option<Piece<'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
         }
index 4903050e08ed266e9f30b64864822be165d7cc86..b3d188dd708814cf50c3937d59eb7334ef73cb09 100644 (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");
     }
 }
index 12c32e6721b334b6e69dd891819cde93989e0ffc..33f25e5216b4eafab08089c2f779fd22ba736544 100644 (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");
     }
 }
index 2b1f018f3642eedc253a3cab86e43c924803905f..fbf399d8b075c311b51cb36e3760789f7d604344 100644 (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
     }
 }
index 583a10570360bf6d0af09b7e21e202db94a549b1..ba7e8f7a03ccad682165367ca555d91352895908 100644 (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
 
index 402adc50d5b44a7935647735b2b6789f4aec34a9..1477e3dd5665ce6230819888ed21c50c8c4824da 100644 (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'
     }
 }
index d5d12b004816f56cb1b90650bbaacac4c5e788c9..b62c8948289ddf1bf55575457bdc2e178c99d50c 100644 (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