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:
commit
85109e257a
15 changed files with 668 additions and 87 deletions
|
@ -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>(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue