Skip to content

Commit

Permalink
Add support for clobber_abi to asm!
Browse files Browse the repository at this point in the history
  • Loading branch information
Amanieu committed Aug 12, 2021
1 parent 2f46122 commit 3fd463a
Show file tree
Hide file tree
Showing 12 changed files with 652 additions and 79 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2027,6 +2027,7 @@ pub enum InlineAsmOperand {
pub struct InlineAsm {
pub template: Vec<InlineAsmTemplatePiece>,
pub operands: Vec<(InlineAsmOperand, Span)>,
pub clobber_abi: Option<(Symbol, Span)>,
pub options: InlineAsmOptions,
pub line_spans: Vec<Span>,
}
Expand Down
56 changes: 55 additions & 1 deletion compiler/rustc_ast_lowering/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,41 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.emit();
}

let mut clobber_abi = None;
if let Some(asm_arch) = asm_arch {
if let Some((abi_name, abi_span)) = asm.clobber_abi {
match asm::InlineAsmClobberAbi::parse(asm_arch, &self.sess.target, abi_name) {
Ok(abi) => clobber_abi = Some((abi, abi_span)),
Err(&[]) => {
self.sess
.struct_span_err(
abi_span,
"`clobber_abi` is not supported on this target",
)
.emit();
}
Err(supported_abis) => {
let mut err =
self.sess.struct_span_err(abi_span, "invalid ABI for `clobber_abi`");
let mut abis = format!("`{}`", supported_abis[0]);
for m in &supported_abis[1..] {
let _ = write!(abis, ", `{}`", m);
}
err.note(&format!(
"the following ABIs are supported on this target: {}",
abis
));
err.emit();
}
}
}
}

// Lower operands to HIR. We use dummy register classes if an error
// occurs during lowering because we still need to be able to produce a
// valid HIR.
let sess = self.sess;
let operands: Vec<_> = asm
let mut operands: Vec<_> = asm
.operands
.iter()
.map(|(op, op_sp)| {
Expand Down Expand Up @@ -336,6 +366,30 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}

// If a clobber_abi is specified, add the necessary clobbers to the
// operands list.
if let Some((abi, abi_span)) = clobber_abi {
for &clobber in abi.clobbered_regs() {
let mut output_used = false;
clobber.overlapping_regs(|reg| {
if used_output_regs.contains_key(&reg) {
output_used = true;
}
});

if !output_used {
operands.push((
hir::InlineAsmOperand::Out {
reg: asm::InlineAsmRegOrRegClass::Reg(clobber),
late: true,
expr: None,
},
abi_span,
));
}
}
}

let operands = self.arena.alloc_from_iter(operands);
let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
let line_spans = self.arena.alloc_slice(&asm.line_spans[..]);
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2186,11 +2186,15 @@ impl<'a> State<'a> {
enum AsmArg<'a> {
Template(String),
Operand(&'a InlineAsmOperand),
ClobberAbi(Symbol),
Options(InlineAsmOptions),
}

let mut args = vec![AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template))];
args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
if let Some((abi, _)) = asm.clobber_abi {
args.push(AsmArg::ClobberAbi(abi));
}
if !asm.options.is_empty() {
args.push(AsmArg::Options(asm.options));
}
Expand Down Expand Up @@ -2257,6 +2261,12 @@ impl<'a> State<'a> {
}
}
}
AsmArg::ClobberAbi(abi) => {
s.word("clobber_abi");
s.popen();
s.print_symbol(*abi, ast::StrStyle::Cooked);
s.pclose();
}
AsmArg::Options(opts) => {
s.word("options");
s.popen();
Expand Down
107 changes: 100 additions & 7 deletions compiler/rustc_builtin_macros/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
}
Expand Down Expand Up @@ -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![],
};
Expand All @@ -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)?;
Expand Down Expand Up @@ -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);
Expand All @@ -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() {
Expand Down Expand Up @@ -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);
}
}
_ => {}
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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>(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ symbols! {
char,
client,
clippy,
clobber_abi,
clone,
clone_closures,
clone_from,
Expand Down
Loading

0 comments on commit 3fd463a

Please sign in to comment.