about summary refs log tree commit diff
path: root/compiler/rustc_builtin_macros
diff options
context:
space:
mode:
authorAmanieu d'Antras <amanieu@gmail.com>2021-07-29 13:43:26 +0200
committerAmanieu d'Antras <amanieu@gmail.com>2021-08-12 12:43:11 +0100
commit3fd463a5ca98c54b4e79271280c69ce5468e1173 (patch)
treea89ee858ba88f4d0866b4e11093fd3dc850dacb5 /compiler/rustc_builtin_macros
parent2f4612260d35ef162c088b6b1a87f1e75717acb6 (diff)
downloadrust-3fd463a5ca98c54b4e79271280c69ce5468e1173.tar.gz
rust-3fd463a5ca98c54b4e79271280c69ce5468e1173.zip
Add support for clobber_abi to asm!
Diffstat (limited to 'compiler/rustc_builtin_macros')
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs107
1 files changed, 100 insertions, 7 deletions
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index df3aed3bca2..3eaa629448a 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -19,6 +19,7 @@ struct AsmArgs {
     operands: Vec<(ast::InlineAsmOperand, Span)>,
     named_args: FxHashMap<Symbol, usize>,
     reg_args: FxHashSet<usize>,
+    clobber_abi: Option<(Symbol, Span)>,
     options: ast::InlineAsmOptions,
     options_spans: Vec<Span>,
 }
@@ -63,6 +64,7 @@ fn parse_args<'a>(
         operands: vec![],
         named_args: FxHashMap::default(),
         reg_args: FxHashSet::default(),
+        clobber_abi: None,
         options: ast::InlineAsmOptions::empty(),
         options_spans: vec![],
     };
@@ -85,6 +87,13 @@ fn parse_args<'a>(
             break;
         } // accept trailing commas
 
+        // Parse clobber_abi
+        if p.eat_keyword(sym::clobber_abi) {
+            parse_clobber_abi(&mut p, &mut args)?;
+            allow_templates = false;
+            continue;
+        }
+
         // Parse options
         if p.eat_keyword(sym::options) {
             parse_options(&mut p, &mut args, is_global_asm)?;
@@ -160,7 +169,11 @@ fn parse_args<'a>(
                 ast::ExprKind::Lit(ast::Lit { kind: ast::LitKind::Str(..), .. }) => {}
                 ast::ExprKind::MacCall(..) => {}
                 _ => {
-                    let errstr = "expected operand, options, or additional template string";
+                    let errstr = if is_global_asm {
+                        "expected operand, options, or additional template string"
+                    } else {
+                        "expected operand, clobber_abi, options, or additional template string"
+                    };
                     let mut err = ecx.struct_span_err(template.span, errstr);
                     err.span_label(template.span, errstr);
                     return Err(err);
@@ -177,13 +190,19 @@ fn parse_args<'a>(
         let slot = args.operands.len();
         args.operands.push((op, span));
 
-        // Validate the order of named, positional & explicit register operands and options. We do
-        // this at the end once we have the full span of the argument available.
+        // Validate the order of named, positional & explicit register operands and
+        // clobber_abi/options. We do this at the end once we have the full span
+        // of the argument available.
         if !args.options_spans.is_empty() {
             ecx.struct_span_err(span, "arguments are not allowed after options")
                 .span_labels(args.options_spans.clone(), "previous options")
                 .span_label(span, "argument")
                 .emit();
+        } else if let Some((_, abi_span)) = args.clobber_abi {
+            ecx.struct_span_err(span, "arguments are not allowed after clobber_abi")
+                .span_label(abi_span, "clobber_abi")
+                .span_label(span, "argument")
+                .emit();
         }
         if explicit_reg {
             if name.is_some() {
@@ -256,16 +275,23 @@ fn parse_args<'a>(
 
     let mut have_real_output = false;
     let mut outputs_sp = vec![];
+    let mut regclass_outputs = vec![];
     for (op, op_sp) in &args.operands {
         match op {
-            ast::InlineAsmOperand::Out { expr, .. }
-            | ast::InlineAsmOperand::SplitInOut { out_expr: expr, .. } => {
+            ast::InlineAsmOperand::Out { reg, expr, .. }
+            | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => {
                 outputs_sp.push(*op_sp);
                 have_real_output |= expr.is_some();
+                if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
+                    regclass_outputs.push(*op_sp);
+                }
             }
-            ast::InlineAsmOperand::InOut { .. } => {
+            ast::InlineAsmOperand::InOut { reg, .. } => {
                 outputs_sp.push(*op_sp);
                 have_real_output = true;
+                if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
+                    regclass_outputs.push(*op_sp);
+                }
             }
             _ => {}
         }
@@ -284,6 +310,24 @@ fn parse_args<'a>(
         // Bail out now since this is likely to confuse MIR
         return Err(err);
     }
+    if let Some((_, abi_span)) = args.clobber_abi {
+        if is_global_asm {
+            let err =
+                ecx.struct_span_err(abi_span, "`clobber_abi` cannot be used with `global_asm!`");
+
+            // Bail out now since this is likely to confuse later stages
+            return Err(err);
+        }
+        if !regclass_outputs.is_empty() {
+            ecx.struct_span_err(
+                regclass_outputs.clone(),
+                "asm with `clobber_abi` must specify explicit registers for outputs",
+            )
+            .span_label(abi_span, "clobber_abi")
+            .span_labels(regclass_outputs, "generic outputs")
+            .emit();
+        }
+    }
 
     Ok(args)
 }
@@ -375,6 +419,49 @@ fn parse_options<'a>(
     Ok(())
 }
 
+fn parse_clobber_abi<'a>(
+    p: &mut Parser<'a>,
+    args: &mut AsmArgs,
+) -> Result<(), DiagnosticBuilder<'a>> {
+    let span_start = p.prev_token.span;
+
+    p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
+
+    let clobber_abi = match p.parse_str_lit() {
+        Ok(str_lit) => str_lit.symbol_unescaped,
+        Err(opt_lit) => {
+            let span = opt_lit.map_or(p.token.span, |lit| lit.span);
+            let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
+            err.span_label(span, "not a string literal");
+            return Err(err);
+        }
+    };
+
+    p.expect(&token::CloseDelim(token::DelimToken::Paren))?;
+
+    let new_span = span_start.to(p.prev_token.span);
+
+    if let Some((_, prev_span)) = args.clobber_abi {
+        let mut err = p
+            .sess
+            .span_diagnostic
+            .struct_span_err(new_span, "clobber_abi specified multiple times");
+        err.span_label(prev_span, "clobber_abi previously specified here");
+        return Err(err);
+    } else if !args.options_spans.is_empty() {
+        let mut err = p
+            .sess
+            .span_diagnostic
+            .struct_span_err(new_span, "clobber_abi is not allowed after options");
+        err.span_labels(args.options_spans.clone(), "options");
+        return Err(err);
+    }
+
+    args.clobber_abi = Some((clobber_abi, new_span));
+
+    Ok(())
+}
+
 fn parse_reg<'a>(
     p: &mut Parser<'a>,
     explicit_reg: &mut bool,
@@ -660,7 +747,13 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
         }
     }
 
-    Some(ast::InlineAsm { template, operands: args.operands, options: args.options, line_spans })
+    Some(ast::InlineAsm {
+        template,
+        operands: args.operands,
+        clobber_abi: args.clobber_abi,
+        options: args.options,
+        line_spans,
+    })
 }
 
 pub fn expand_asm<'cx>(