Skip to content

Add support for clobber_abi to asm! #87581

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
109 changes: 101 additions & 8 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,24 +275,31 @@ 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);
}
}
_ => {}
}
}
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();
}
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
9 changes: 8 additions & 1 deletion compiler/rustc_codegen_llvm/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,14 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
"~{flags}".to_string(),
]);
}
InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {}
InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
constraints.extend_from_slice(&[
"~{vtype}".to_string(),
"~{vl}".to_string(),
"~{vxsat}".to_string(),
"~{vxrm}".to_string(),
]);
}
InlineAsmArch::Nvptx64 => {}
InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => {}
InlineAsmArch::Hexagon => {}
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