1
Fork 0

Auto merge of #87581 - Amanieu:asm_clobber_abi, r=nagisa

Add support for clobber_abi to asm!

This PR adds the `clobber_abi` feature that was proposed in #81092.

Fixes #81092

cc `@rust-lang/wg-inline-asm`

r? `@nagisa`
This commit is contained in:
bors 2021-08-14 22:29:27 +00:00
commit 85109e257a
15 changed files with 668 additions and 87 deletions

View file

@ -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);
}
}
_ => {}
}
@ -273,7 +299,7 @@ fn parse_args<'a>(
if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
ecx.struct_span_err(
args.options_spans.clone(),
"asm with `pure` option must have at least one output",
"asm with the `pure` option must have at least one output",
)
.emit();
}
@ -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,
@ -730,7 +817,13 @@ fn expand_preparsed_asm(
}
}
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>(