about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAmanieu d'Antras <amanieu@gmail.com>2020-02-12 15:47:43 +0000
committerAmanieu d'Antras <amanieu@gmail.com>2020-05-18 14:39:54 +0100
commita0adf53bc97d6e79c8c2e9b3f8a548bc65c7adf6 (patch)
tree773aa499e291a93e8d1536a806fee779c42b9b24
parent813a9fc4f121d808c48ebee47a25a773120edd45 (diff)
downloadrust-a0adf53bc97d6e79c8c2e9b3f8a548bc65c7adf6.tar.gz
rust-a0adf53bc97d6e79c8c2e9b3f8a548bc65c7adf6.zip
Implement asm! in librustc_builtin_macros
-rw-r--r--src/libfmt_macros/lib.rs155
-rw-r--r--src/libfmt_macros/tests.rs4
-rw-r--r--src/librustc_builtin_macros/asm.rs527
-rw-r--r--src/librustc_builtin_macros/format.rs119
-rw-r--r--src/librustc_builtin_macros/lib.rs3
-rw-r--r--src/librustc_span/symbol.rs18
-rw-r--r--src/librustc_trait_selection/traits/on_unimplemented.rs6
7 files changed, 712 insertions, 120 deletions
diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs
index 29276eead71..677c027f17b 100644
--- a/src/libfmt_macros/lib.rs
+++ b/src/libfmt_macros/lib.rs
@@ -27,6 +27,15 @@ use std::string;
 
 use rustc_span::{InnerSpan, Symbol};
 
+/// The type of format string that we are parsing.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum ParseMode {
+    /// A normal format string as per `format_args!`.
+    Format,
+    /// An inline assembly template string for `asm!`.
+    InlineAsm,
+}
+
 #[derive(Copy, Clone)]
 struct InnerOffset(usize);
 
@@ -163,6 +172,7 @@ pub struct ParseError {
 /// This is a recursive-descent parser for the sake of simplicity, and if
 /// necessary there's probably lots of room for improvement performance-wise.
 pub struct Parser<'a> {
+    mode: ParseMode,
     input: &'a str,
     cur: iter::Peekable<str::CharIndices<'a>>,
     /// Error messages accumulated during parsing
@@ -179,6 +189,8 @@ pub struct Parser<'a> {
     last_opening_brace: Option<InnerSpan>,
     /// Whether the source string is comes from `println!` as opposed to `format!` or `print!`
     append_newline: bool,
+    /// Whether this formatting string is a literal or it comes from a macro.
+    is_literal: bool,
 }
 
 impl<'a> Iterator for Parser<'a> {
@@ -201,7 +213,9 @@ impl<'a> Iterator for Parser<'a> {
                         if let Some(end) = self.must_consume('}') {
                             let start = self.to_span_index(pos);
                             let end = self.to_span_index(end + 1);
-                            self.arg_places.push(start.to(end));
+                            if self.is_literal {
+                                self.arg_places.push(start.to(end));
+                            }
                         }
                         Some(NextArgument(arg))
                     }
@@ -235,10 +249,13 @@ impl<'a> Parser<'a> {
     pub fn new(
         s: &'a str,
         style: Option<usize>,
-        skips: Vec<usize>,
+        snippet: Option<string::String>,
         append_newline: bool,
+        mode: ParseMode,
     ) -> Parser<'a> {
+        let (skips, is_literal) = find_skips_from_snippet(snippet, style);
         Parser {
+            mode,
             input: s,
             cur: s.char_indices().peekable(),
             errors: vec![],
@@ -248,6 +265,7 @@ impl<'a> Parser<'a> {
             skips,
             last_opening_brace: None,
             append_newline,
+            is_literal,
         }
     }
 
@@ -426,7 +444,10 @@ impl<'a> Parser<'a> {
     /// Parses an `Argument` structure, or what's contained within braces inside the format string.
     fn argument(&mut self) -> Argument<'a> {
         let pos = self.position();
-        let format = self.format();
+        let format = match self.mode {
+            ParseMode::Format => self.format(),
+            ParseMode::InlineAsm => self.inline_asm(),
+        };
 
         // Resolve position after parsing format spec.
         let pos = match pos {
@@ -574,6 +595,36 @@ impl<'a> Parser<'a> {
         spec
     }
 
+    /// Parses an inline assembly template modifier at the current position, returning the modifier
+    /// in the `ty` field of the `FormatSpec` struct.
+    fn inline_asm(&mut self) -> FormatSpec<'a> {
+        let mut spec = FormatSpec {
+            fill: None,
+            align: AlignUnknown,
+            flags: 0,
+            precision: CountImplied,
+            precision_span: None,
+            width: CountImplied,
+            width_span: None,
+            ty: &self.input[..0],
+            ty_span: None,
+        };
+        if !self.consume(':') {
+            return spec;
+        }
+
+        let ty_span_start = self.cur.peek().map(|(pos, _)| *pos);
+        spec.ty = self.word();
+        let ty_span_end = self.cur.peek().map(|(pos, _)| *pos);
+        if !spec.ty.is_empty() {
+            spec.ty_span = ty_span_start
+                .and_then(|s| ty_span_end.map(|e| (s, e)))
+                .map(|(start, end)| self.to_span_index(start).to(self.to_span_index(end)));
+        }
+
+        spec
+    }
+
     /// Parses a `Count` parameter at the current position. This does not check
     /// for 'CountIsNextParam' because that is only used in precision, not
     /// width.
@@ -652,5 +703,103 @@ impl<'a> Parser<'a> {
     }
 }
 
+/// Finds the indices of all characters that have been processed and differ between the actual
+/// written code (code snippet) and the `InternedString` that gets processed in the `Parser`
+/// in order to properly synthethise the intra-string `Span`s for error diagnostics.
+fn find_skips_from_snippet(
+    snippet: Option<string::String>,
+    str_style: Option<usize>,
+) -> (Vec<usize>, bool) {
+    let snippet = match snippet {
+        Some(ref s) if s.starts_with('"') || s.starts_with("r#") => s,
+        _ => return (vec![], false),
+    };
+
+    fn find_skips(snippet: &str, is_raw: bool) -> Vec<usize> {
+        let mut eat_ws = false;
+        let mut s = snippet.chars().enumerate().peekable();
+        let mut skips = vec![];
+        while let Some((pos, c)) = s.next() {
+            match (c, s.peek()) {
+                // skip whitespace and empty lines ending in '\\'
+                ('\\', Some((next_pos, '\n'))) if !is_raw => {
+                    eat_ws = true;
+                    skips.push(pos);
+                    skips.push(*next_pos);
+                    let _ = s.next();
+                }
+                ('\\', Some((next_pos, '\n' | 'n' | 't'))) if eat_ws => {
+                    skips.push(pos);
+                    skips.push(*next_pos);
+                    let _ = s.next();
+                }
+                (' ' | '\n' | '\t', _) if eat_ws => {
+                    skips.push(pos);
+                }
+                ('\\', Some((next_pos, 'n' | 't' | '0' | '\\' | '\'' | '\"'))) => {
+                    skips.push(*next_pos);
+                    let _ = s.next();
+                }
+                ('\\', Some((_, 'x'))) if !is_raw => {
+                    for _ in 0..3 {
+                        // consume `\xAB` literal
+                        if let Some((pos, _)) = s.next() {
+                            skips.push(pos);
+                        } else {
+                            break;
+                        }
+                    }
+                }
+                ('\\', Some((_, 'u'))) if !is_raw => {
+                    if let Some((pos, _)) = s.next() {
+                        skips.push(pos);
+                    }
+                    if let Some((next_pos, next_c)) = s.next() {
+                        if next_c == '{' {
+                            skips.push(next_pos);
+                            let mut i = 0; // consume up to 6 hexanumeric chars + closing `}`
+                            while let (Some((next_pos, c)), true) = (s.next(), i < 7) {
+                                if c.is_digit(16) {
+                                    skips.push(next_pos);
+                                } else if c == '}' {
+                                    skips.push(next_pos);
+                                    break;
+                                } else {
+                                    break;
+                                }
+                                i += 1;
+                            }
+                        } else if next_c.is_digit(16) {
+                            skips.push(next_pos);
+                            // We suggest adding `{` and `}` when appropriate, accept it here as if
+                            // it were correct
+                            let mut i = 0; // consume up to 6 hexanumeric chars
+                            while let (Some((next_pos, c)), _) = (s.next(), i < 6) {
+                                if c.is_digit(16) {
+                                    skips.push(next_pos);
+                                } else {
+                                    break;
+                                }
+                                i += 1;
+                            }
+                        }
+                    }
+                }
+                _ if eat_ws => {
+                    // `take_while(|c| c.is_whitespace())`
+                    eat_ws = false;
+                }
+                _ => {}
+            }
+        }
+        skips
+    }
+
+    let r_start = str_style.map(|r| r + 1).unwrap_or(0);
+    let r_end = str_style.map(|r| r).unwrap_or(0);
+    let s = &snippet[r_start + 1..snippet.len() - r_end - 1];
+    (find_skips(s, str_style.is_some()), true)
+}
+
 #[cfg(test)]
 mod tests;
diff --git a/src/libfmt_macros/tests.rs b/src/libfmt_macros/tests.rs
index 98c2a17f0dd..a0cef78f924 100644
--- a/src/libfmt_macros/tests.rs
+++ b/src/libfmt_macros/tests.rs
@@ -1,7 +1,7 @@
 use super::*;
 
 fn same(fmt: &'static str, p: &[Piece<'static>]) {
-    let parser = Parser::new(fmt, None, vec![], false);
+    let parser = Parser::new(fmt, None, vec![], false, ParseMode::Format);
     assert_eq!(parser.collect::<Vec<Piece<'static>>>(), p);
 }
 
@@ -20,7 +20,7 @@ fn fmtdflt() -> FormatSpec<'static> {
 }
 
 fn musterr(s: &str) {
-    let mut p = Parser::new(s, None, vec![], false);
+    let mut p = Parser::new(s, None, vec![], false, ParseMode::Format);
     p.next();
     assert!(!p.errors.is_empty());
 }
diff --git a/src/librustc_builtin_macros/asm.rs b/src/librustc_builtin_macros/asm.rs
new file mode 100644
index 00000000000..943ed42e202
--- /dev/null
+++ b/src/librustc_builtin_macros/asm.rs
@@ -0,0 +1,527 @@
+use fmt_macros as parse;
+
+use rustc_ast::ast;
+use rustc_ast::ptr::P;
+use rustc_ast::token;
+use rustc_ast::tokenstream::TokenStream;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_expand::base::{self, *};
+use rustc_parse::parser::Parser;
+use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::{InnerSpan, Span};
+use rustc_target::asm::{InlineAsmOptions, InlineAsmTemplatePiece};
+
+struct AsmArgs {
+    template: P<ast::Expr>,
+    operands: Vec<(ast::InlineAsmOperand, Span)>,
+    named_args: FxHashMap<Symbol, usize>,
+    reg_args: FxHashSet<usize>,
+    options: InlineAsmOptions,
+    options_span: Option<Span>,
+}
+
+fn parse_args<'a>(
+    ecx: &mut ExtCtxt<'a>,
+    sp: Span,
+    tts: TokenStream,
+) -> Result<AsmArgs, DiagnosticBuilder<'a>> {
+    let mut p = ecx.new_parser_from_tts(tts);
+
+    if p.token == token::Eof {
+        return Err(ecx.struct_span_err(sp, "requires at least a template string argument"));
+    }
+
+    // Detect use of the legacy llvm_asm! syntax (which used to be called asm!)
+    if p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) {
+        let mut err = ecx.struct_span_err(sp, "legacy asm! syntax is no longer supported");
+
+        // Find the span of the "asm!" so that we can offer an automatic suggestion
+        let asm_span = sp.from_inner(InnerSpan::new(0, 4));
+        if let Ok(s) = ecx.source_map().span_to_snippet(asm_span) {
+            if s == "asm!" {
+                err.span_suggestion(
+                    asm_span,
+                    "replace with",
+                    "llvm_asm!".into(),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+        return Err(err);
+    }
+
+    let template = p.parse_expr()?;
+    let mut args = AsmArgs {
+        template,
+        operands: vec![],
+        named_args: FxHashMap::default(),
+        reg_args: FxHashSet::default(),
+        options: InlineAsmOptions::empty(),
+        options_span: None,
+    };
+
+    let mut first = true;
+    while p.token != token::Eof {
+        if !p.eat(&token::Comma) {
+            if first {
+                // After `asm!(""` 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);
+                return Err(err);
+            } else {
+                // ...after that delegate to `expect` to also include the other expected tokens.
+                return Err(p.expect(&token::Comma).err().unwrap());
+            }
+        }
+        first = false;
+        if p.token == token::Eof {
+            break;
+        } // accept trailing commas
+
+        let span_start = p.token.span;
+
+        // Parse options
+        if p.eat(&token::Ident(sym::options, false)) {
+            p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
+
+            while !p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+                if p.eat(&token::Ident(sym::pure, false)) {
+                    args.options |= InlineAsmOptions::PURE;
+                } else if p.eat(&token::Ident(sym::nomem, false)) {
+                    args.options |= InlineAsmOptions::NOMEM;
+                } else if p.eat(&token::Ident(sym::readonly, false)) {
+                    args.options |= InlineAsmOptions::READONLY;
+                } else if p.eat(&token::Ident(sym::preserves_flags, false)) {
+                    args.options |= InlineAsmOptions::PRESERVES_FLAGS;
+                } else if p.eat(&token::Ident(sym::noreturn, false)) {
+                    args.options |= InlineAsmOptions::NORETURN;
+                } else {
+                    p.expect(&token::Ident(sym::nostack, false))?;
+                    args.options |= InlineAsmOptions::NOSTACK;
+                }
+
+                // Allow trailing commas
+                if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+                    break;
+                }
+                p.expect(&token::Comma)?;
+            }
+
+            let new_span = span_start.to(p.prev_token.span);
+            if let Some(options_span) = args.options_span {
+                ecx.struct_span_err(new_span, "asm options cannot be specified twice")
+                    .span_label(options_span, "previously here")
+                    .span_label(new_span, "duplicate options")
+                    .emit();
+            } else {
+                args.options_span = Some(new_span);
+            }
+            continue;
+        }
+
+        // Parse operand names
+        let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
+            let (ident, _) = p.token.ident().unwrap();
+            p.bump();
+            p.expect(&token::Eq)?;
+            Some(ident.name)
+        } else {
+            None
+        };
+
+        fn parse_reg<'a>(
+            p: &mut Parser<'a>,
+            explicit_reg: &mut bool,
+        ) -> Result<ast::InlineAsmRegOrRegClass, DiagnosticBuilder<'a>> {
+            p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
+            let result = match p.token.kind {
+                token::Ident(name, false) => ast::InlineAsmRegOrRegClass::RegClass(name),
+                token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
+                    *explicit_reg = true;
+                    ast::InlineAsmRegOrRegClass::Reg(symbol)
+                }
+                _ => {
+                    return Err(p.struct_span_err(
+                        p.token.span,
+                        "expected register class or explicit register",
+                    ));
+                }
+            };
+            p.bump();
+            p.expect(&token::CloseDelim(token::DelimToken::Paren))?;
+            Ok(result)
+        };
+
+        let mut explicit_reg = false;
+        let op = if p.eat(&token::Ident(kw::In, false)) {
+            let reg = parse_reg(&mut p, &mut explicit_reg)?;
+            let expr = p.parse_expr()?;
+            ast::InlineAsmOperand::In { reg, expr }
+        } else if p.eat(&token::Ident(sym::out, false)) {
+            let reg = parse_reg(&mut p, &mut explicit_reg)?;
+            let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
+            ast::InlineAsmOperand::Out { reg, expr, late: false }
+        } else if p.eat(&token::Ident(sym::lateout, false)) {
+            let reg = parse_reg(&mut p, &mut explicit_reg)?;
+            let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
+            ast::InlineAsmOperand::Out { reg, expr, late: true }
+        } else if p.eat(&token::Ident(sym::inout, false)) {
+            let reg = parse_reg(&mut p, &mut explicit_reg)?;
+            let expr = p.parse_expr()?;
+            if p.eat(&token::FatArrow) {
+                let out_expr =
+                    if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
+                ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
+            } else {
+                ast::InlineAsmOperand::InOut { reg, expr, late: false }
+            }
+        } else if p.eat(&token::Ident(sym::inlateout, false)) {
+            let reg = parse_reg(&mut p, &mut explicit_reg)?;
+            let expr = p.parse_expr()?;
+            if p.eat(&token::FatArrow) {
+                let out_expr =
+                    if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
+                ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
+            } else {
+                ast::InlineAsmOperand::InOut { reg, expr, late: true }
+            }
+        } 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))?;
+            let expr = p.parse_expr()?;
+            match expr.kind {
+                ast::ExprKind::Path(..) => {}
+                _ => {
+                    let err = ecx
+                        .struct_span_err(expr.span, "argument to `sym` must be a path expression");
+                    return Err(err);
+                }
+            }
+            ast::InlineAsmOperand::Sym { expr }
+        };
+
+        let span = span_start.to(p.prev_token.span);
+        let slot = args.operands.len();
+        args.operands.push((op, span));
+
+        // Validate the order of named, positional & explicit register operands and options. We do
+        // this at the end once we have the full span of the argument available.
+        if let Some(options_span) = args.options_span {
+            ecx.struct_span_err(span, "arguments are not allowed after options")
+                .span_label(options_span, "previous options")
+                .span_label(span, "argument")
+                .emit();
+        }
+        if explicit_reg {
+            if name.is_some() {
+                ecx.struct_span_err(span, "explicit register arguments cannot have names").emit();
+            }
+            args.reg_args.insert(slot);
+        } else if let Some(name) = name {
+            if let Some(&prev) = args.named_args.get(&name) {
+                ecx.struct_span_err(span, &format!("duplicate argument named `{}`", name))
+                    .span_label(args.operands[prev].1, "previously here")
+                    .span_label(span, "duplicate argument")
+                    .emit();
+                continue;
+            }
+            if !args.reg_args.is_empty() {
+                let mut err = ecx.struct_span_err(
+                    span,
+                    "named arguments cannot follow explicit register arguments",
+                );
+                err.span_label(span, "named argument");
+                for pos in &args.reg_args {
+                    err.span_label(args.operands[*pos].1, "explicit register argument");
+                }
+                err.emit();
+            }
+            args.named_args.insert(name, slot);
+        } else {
+            if !args.named_args.is_empty() || !args.reg_args.is_empty() {
+                let mut err = ecx.struct_span_err(
+                    span,
+                    "positional arguments cannot follow named arguments \
+                     or explicit register arguments",
+                );
+                err.span_label(span, "positional argument");
+                for pos in args.named_args.values() {
+                    err.span_label(args.operands[*pos].1, "named argument");
+                }
+                for pos in &args.reg_args {
+                    err.span_label(args.operands[*pos].1, "explicit register argument");
+                }
+                err.emit();
+            }
+        }
+    }
+
+    if args.options.contains(InlineAsmOptions::NOMEM)
+        && args.options.contains(InlineAsmOptions::READONLY)
+    {
+        let span = args.options_span.unwrap();
+        ecx.struct_span_err(span, "the `nomem` and `readonly` options are mutually exclusive")
+            .emit();
+    }
+    if args.options.contains(InlineAsmOptions::PURE)
+        && args.options.contains(InlineAsmOptions::NORETURN)
+    {
+        let span = args.options_span.unwrap();
+        ecx.struct_span_err(span, "the `pure` and `noreturn` options are mutually exclusive")
+            .emit();
+    }
+    if args.options.contains(InlineAsmOptions::PURE)
+        && !args.options.intersects(InlineAsmOptions::NOMEM | InlineAsmOptions::READONLY)
+    {
+        let span = args.options_span.unwrap();
+        ecx.struct_span_err(
+            span,
+            "the `pure` option must be combined with either `nomem` or `readonly`",
+        )
+        .emit();
+    }
+
+    let mut have_real_output = false;
+    let mut outputs_sp = vec![];
+    for (op, op_sp) in &args.operands {
+        match op {
+            ast::InlineAsmOperand::Out { expr, .. }
+            | ast::InlineAsmOperand::SplitInOut { out_expr: expr, .. } => {
+                outputs_sp.push(*op_sp);
+                have_real_output |= expr.is_some();
+            }
+            ast::InlineAsmOperand::InOut { .. } => {
+                outputs_sp.push(*op_sp);
+                have_real_output = true;
+            }
+            _ => {}
+        }
+    }
+    if args.options.contains(InlineAsmOptions::PURE) && !have_real_output {
+        ecx.struct_span_err(
+            args.options_span.unwrap(),
+            "asm with `pure` option must have at least one output",
+        )
+        .emit();
+    }
+    if args.options.contains(InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() {
+        let err = ecx
+            .struct_span_err(outputs_sp, "asm outputs are not allowed with the `noreturn` option");
+
+        // Bail out now since this is likely to confuse MIR
+        return Err(err);
+    }
+
+    Ok(args)
+}
+
+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);
+    }
+
+    // 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: FxHashSet<usize> = args.named_args.values().cloned().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(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(&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(&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_none() {
+                    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(InlineAsmTemplatePiece::Placeholder {
+                        operand_idx,
+                        modifier,
+                        span,
+                    });
+                }
+            }
+        }
+    }
+
+    let operands = args.operands;
+    let unused_operands: Vec<_> = used
+        .into_iter()
+        .enumerate()
+        .filter(|&(_, used)| !used)
+        .map(|(idx, _)| {
+            if named_pos.contains(&idx) {
+                // named argument
+                (operands[idx].1, "named argument never used")
+            } else {
+                // positional argument
+                (operands[idx].1, "argument never used")
+            }
+        })
+        .collect();
+    match unused_operands.len() {
+        0 => {}
+        1 => {
+            let (sp, msg) = unused_operands.into_iter().next().unwrap();
+            let mut err = ecx.struct_span_err(sp, msg);
+            err.span_label(sp, msg);
+            err.emit();
+        }
+        _ => {
+            let mut err = ecx.struct_span_err(
+                unused_operands.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(),
+                "multiple unused asm arguments",
+            );
+            for (sp, msg) in unused_operands {
+                err.span_label(sp, msg);
+            }
+            err.emit();
+        }
+    }
+
+    let inline_asm = ast::InlineAsm { template, operands, options: args.options };
+    P(ast::Expr {
+        id: ast::DUMMY_NODE_ID,
+        kind: ast::ExprKind::InlineAsm(inline_asm),
+        span: sp,
+        attrs: ast::AttrVec::new(),
+    })
+}
+
+pub fn expand_asm<'cx>(
+    ecx: &'cx mut ExtCtxt<'_>,
+    sp: Span,
+    tts: TokenStream,
+) -> Box<dyn base::MacResult + 'cx> {
+    match parse_args(ecx, sp, tts) {
+        Ok(args) => MacEager::expr(expand_preparsed_asm(ecx, sp, args)),
+        Err(mut err) => {
+            err.emit();
+            DummyResult::any(sp)
+        }
+    }
+}
diff --git a/src/librustc_builtin_macros/format.rs b/src/librustc_builtin_macros/format.rs
index efce6288198..eed01b262bf 100644
--- a/src/librustc_builtin_macros/format.rs
+++ b/src/librustc_builtin_macros/format.rs
@@ -108,8 +108,6 @@ struct Context<'a, 'b> {
     arg_spans: Vec<Span>,
     /// All the formatting arguments that have formatting flags set, in order for diagnostics.
     arg_with_formatting: Vec<parse::FormatSpec<'a>>,
-    /// Whether this formatting string is a literal or it comes from a macro.
-    is_literal: bool,
 }
 
 /// Parses the arguments from the given list of tokens, returning the diagnostic
@@ -324,7 +322,7 @@ impl<'a, 'b> Context<'a, 'b> {
     /// format string.
     fn report_invalid_references(&self, numbered_position_args: bool) {
         let mut e;
-        let sp = if self.is_literal {
+        let sp = if !self.arg_spans.is_empty() {
             // Point at the formatting arguments.
             MultiSpan::from_spans(self.arg_spans.clone())
         } else {
@@ -372,7 +370,7 @@ impl<'a, 'b> Context<'a, 'b> {
                 let reg = refs.pop().unwrap();
                 (format!("arguments {head} and {tail}", head = refs.join(", "), tail = reg,), pos)
             };
-            if !self.is_literal {
+            if self.arg_spans.is_empty() {
                 sp = MultiSpan::from_span(self.fmtsp);
             }
 
@@ -502,11 +500,7 @@ impl<'a, 'b> Context<'a, 'b> {
                     }
                     None => {
                         let msg = format!("there is no argument named `{}`", name);
-                        let sp = if self.is_literal {
-                            *self.arg_spans.get(self.curpiece).unwrap_or(&self.fmtsp)
-                        } else {
-                            self.fmtsp
-                        };
+                        let sp = *self.arg_spans.get(self.curpiece).unwrap_or(&self.fmtsp);
                         let mut err = self.ecx.struct_span_err(sp, &msg[..]);
                         err.emit();
                     }
@@ -892,110 +886,20 @@ pub fn expand_preparsed_format_args(
         }
     };
 
-    let (is_literal, fmt_snippet) = match ecx.source_map().span_to_snippet(fmt_sp) {
-        Ok(s) => (s.starts_with('"') || s.starts_with("r#"), Some(s)),
-        _ => (false, None),
-    };
-
     let str_style = match fmt_style {
         ast::StrStyle::Cooked => None,
         ast::StrStyle::Raw(raw) => Some(raw as usize),
     };
 
-    /// Finds the indices of all characters that have been processed and differ between the actual
-    /// written code (code snippet) and the `InternedString` that gets processed in the `Parser`
-    /// in order to properly synthethise the intra-string `Span`s for error diagnostics.
-    fn find_skips(snippet: &str, is_raw: bool) -> Vec<usize> {
-        let mut eat_ws = false;
-        let mut s = snippet.chars().enumerate().peekable();
-        let mut skips = vec![];
-        while let Some((pos, c)) = s.next() {
-            match (c, s.peek()) {
-                // skip whitespace and empty lines ending in '\\'
-                ('\\', Some((next_pos, '\n'))) if !is_raw => {
-                    eat_ws = true;
-                    skips.push(pos);
-                    skips.push(*next_pos);
-                    let _ = s.next();
-                }
-                ('\\', Some((next_pos, '\n' | 'n' | 't'))) if eat_ws => {
-                    skips.push(pos);
-                    skips.push(*next_pos);
-                    let _ = s.next();
-                }
-                (' ' | '\n' | '\t', _) if eat_ws => {
-                    skips.push(pos);
-                }
-                ('\\', Some((next_pos, 'n' | 't' | '0' | '\\' | '\'' | '\"'))) => {
-                    skips.push(*next_pos);
-                    let _ = s.next();
-                }
-                ('\\', Some((_, 'x'))) if !is_raw => {
-                    for _ in 0..3 {
-                        // consume `\xAB` literal
-                        if let Some((pos, _)) = s.next() {
-                            skips.push(pos);
-                        } else {
-                            break;
-                        }
-                    }
-                }
-                ('\\', Some((_, 'u'))) if !is_raw => {
-                    if let Some((pos, _)) = s.next() {
-                        skips.push(pos);
-                    }
-                    if let Some((next_pos, next_c)) = s.next() {
-                        if next_c == '{' {
-                            skips.push(next_pos);
-                            let mut i = 0; // consume up to 6 hexanumeric chars + closing `}`
-                            while let (Some((next_pos, c)), true) = (s.next(), i < 7) {
-                                if c.is_digit(16) {
-                                    skips.push(next_pos);
-                                } else if c == '}' {
-                                    skips.push(next_pos);
-                                    break;
-                                } else {
-                                    break;
-                                }
-                                i += 1;
-                            }
-                        } else if next_c.is_digit(16) {
-                            skips.push(next_pos);
-                            // We suggest adding `{` and `}` when appropriate, accept it here as if
-                            // it were correct
-                            let mut i = 0; // consume up to 6 hexanumeric chars
-                            while let (Some((next_pos, c)), _) = (s.next(), i < 6) {
-                                if c.is_digit(16) {
-                                    skips.push(next_pos);
-                                } else {
-                                    break;
-                                }
-                                i += 1;
-                            }
-                        }
-                    }
-                }
-                _ if eat_ws => {
-                    // `take_while(|c| c.is_whitespace())`
-                    eat_ws = false;
-                }
-                _ => {}
-            }
-        }
-        skips
-    }
-
-    let skips = if let (true, Some(ref snippet)) = (is_literal, fmt_snippet.as_ref()) {
-        let r_start = str_style.map(|r| r + 1).unwrap_or(0);
-        let r_end = str_style.map(|r| r).unwrap_or(0);
-        let s = &snippet[r_start + 1..snippet.len() - r_end - 1];
-        find_skips(s, str_style.is_some())
-    } else {
-        vec![]
-    };
-
     let fmt_str = &fmt_str.as_str(); // for the suggestions below
-    let mut parser = parse::Parser::new(fmt_str, str_style, skips, append_newline);
+    let fmt_snippet = ecx.source_map().span_to_snippet(fmt_sp).ok();
+    let mut parser = parse::Parser::new(
+        fmt_str,
+        str_style,
+        fmt_snippet,
+        append_newline,
+        parse::ParseMode::Format,
+    );
 
     let mut unverified_pieces = Vec::new();
     while let Some(piece) = parser.next() {
@@ -1048,7 +952,6 @@ pub fn expand_preparsed_format_args(
         invalid_refs: Vec::new(),
         arg_spans,
         arg_with_formatting: Vec::new(),
-        is_literal,
     };
 
     // This needs to happen *after* the Parser has consumed all pieces to create all the spans
diff --git a/src/librustc_builtin_macros/lib.rs b/src/librustc_builtin_macros/lib.rs
index cc77bb73c5a..a0f82d65618 100644
--- a/src/librustc_builtin_macros/lib.rs
+++ b/src/librustc_builtin_macros/lib.rs
@@ -19,6 +19,7 @@ use rustc_expand::proc_macro::BangProcMacro;
 use rustc_span::edition::Edition;
 use rustc_span::symbol::{sym, Ident};
 
+mod asm;
 mod assert;
 mod cfg;
 mod cfg_accessible;
@@ -61,7 +62,7 @@ pub fn register_builtin_macros(resolver: &mut dyn Resolver, edition: Edition) {
     }
 
     register_bang! {
-        asm: llvm_asm::expand_llvm_asm,
+        asm: asm::expand_asm,
         assert: assert::expand_assert,
         cfg: cfg::expand_cfg,
         column: source_util::expand_column,
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index 1f351d09bc3..1f0764b06b0 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -376,6 +376,8 @@ symbols! {
         if_let,
         if_while_or_patterns,
         ignore,
+        inlateout,
+        inout,
         impl_header_lifetime_elision,
         impl_lint_pass,
         impl_trait_in_bindings,
@@ -411,6 +413,7 @@ symbols! {
         label_break_value,
         lang,
         lang_items,
+        lateout,
         let_chains,
         lhs,
         lib,
@@ -495,12 +498,15 @@ symbols! {
         no_link,
         no_main,
         no_mangle,
+        nomem,
         non_ascii_idents,
         None,
         non_exhaustive,
         non_modrs_mods,
-        no_sanitize,
+        noreturn,
         no_niche,
+        no_sanitize,
+        nostack,
         no_stack_check,
         no_start,
         no_std,
@@ -519,11 +525,13 @@ symbols! {
         option,
         Option,
         option_env,
+        options,
         opt_out_copy,
         or,
         or_patterns,
         Ord,
         Ordering,
+        out,
         Output,
         overlapping_marker_traits,
         packed,
@@ -556,6 +564,7 @@ symbols! {
         pref_align_of,
         prelude,
         prelude_import,
+        preserves_flags,
         primitive,
         proc_dash_macro: "proc-macro",
         proc_macro,
@@ -572,6 +581,7 @@ symbols! {
         profiler_runtime,
         ptr_offset_from,
         pub_restricted,
+        pure,
         pushpop_unsafe,
         quad_precision_float,
         question_mark,
@@ -586,6 +596,7 @@ symbols! {
         raw_identifiers,
         raw_ref_op,
         Rc,
+        readonly,
         Ready,
         reason,
         recursion_limit,
@@ -723,6 +734,7 @@ symbols! {
         sty,
         sub_with_overflow,
         suggestion,
+        sym,
         sync_trait,
         target_feature,
         target_feature_11,
@@ -1187,8 +1199,8 @@ pub mod sym {
     // have a static symbol and therefore are fast.
     pub fn integer<N: TryInto<usize> + Copy + ToString>(n: N) -> Symbol {
         if let Result::Ok(idx) = n.try_into() {
-            if let Option::Some(&sym) = digits_array.get(idx) {
-                return sym;
+            if let Option::Some(&sym_) = digits_array.get(idx) {
+                return sym_;
             }
         }
         Symbol::intern(&n.to_string())
diff --git a/src/librustc_trait_selection/traits/on_unimplemented.rs b/src/librustc_trait_selection/traits/on_unimplemented.rs
index 3fbc0b7f08e..7e66e08f7e6 100644
--- a/src/librustc_trait_selection/traits/on_unimplemented.rs
+++ b/src/librustc_trait_selection/traits/on_unimplemented.rs
@@ -1,4 +1,4 @@
-use fmt_macros::{Parser, Piece, Position};
+use fmt_macros::{ParseMode, Parser, Piece, Position};
 
 use rustc_ast::ast::{MetaItem, NestedMetaItem};
 use rustc_attr as attr;
@@ -272,7 +272,7 @@ impl<'tcx> OnUnimplementedFormatString {
         let name = tcx.item_name(trait_def_id);
         let generics = tcx.generics_of(trait_def_id);
         let s = self.0.as_str();
-        let parser = Parser::new(&s, None, vec![], false);
+        let parser = Parser::new(&s, None, None, false, ParseMode::Format);
         let mut result = Ok(());
         for token in parser {
             match token {
@@ -350,7 +350,7 @@ impl<'tcx> OnUnimplementedFormatString {
         let empty_string = String::new();
 
         let s = self.0.as_str();
-        let parser = Parser::new(&s, None, vec![], false);
+        let parser = Parser::new(&s, None, None, false, ParseMode::Format);
         let item_context = (options.get(&sym::item_context)).unwrap_or(&empty_string);
         parser
             .map(|p| match p {