Skip to content

Commit 3fd463a

Browse files
committed
Add support for clobber_abi to asm!
1 parent 2f46122 commit 3fd463a

File tree

12 files changed

+652
-79
lines changed

12 files changed

+652
-79
lines changed

compiler/rustc_ast/src/ast.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2027,6 +2027,7 @@ pub enum InlineAsmOperand {
20272027
pub struct InlineAsm {
20282028
pub template: Vec<InlineAsmTemplatePiece>,
20292029
pub operands: Vec<(InlineAsmOperand, Span)>,
2030+
pub clobber_abi: Option<(Symbol, Span)>,
20302031
pub options: InlineAsmOptions,
20312032
pub line_spans: Vec<Span>,
20322033
}

compiler/rustc_ast_lowering/src/asm.rs

+55-1
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,41 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
2727
.emit();
2828
}
2929

30+
let mut clobber_abi = None;
31+
if let Some(asm_arch) = asm_arch {
32+
if let Some((abi_name, abi_span)) = asm.clobber_abi {
33+
match asm::InlineAsmClobberAbi::parse(asm_arch, &self.sess.target, abi_name) {
34+
Ok(abi) => clobber_abi = Some((abi, abi_span)),
35+
Err(&[]) => {
36+
self.sess
37+
.struct_span_err(
38+
abi_span,
39+
"`clobber_abi` is not supported on this target",
40+
)
41+
.emit();
42+
}
43+
Err(supported_abis) => {
44+
let mut err =
45+
self.sess.struct_span_err(abi_span, "invalid ABI for `clobber_abi`");
46+
let mut abis = format!("`{}`", supported_abis[0]);
47+
for m in &supported_abis[1..] {
48+
let _ = write!(abis, ", `{}`", m);
49+
}
50+
err.note(&format!(
51+
"the following ABIs are supported on this target: {}",
52+
abis
53+
));
54+
err.emit();
55+
}
56+
}
57+
}
58+
}
59+
3060
// Lower operands to HIR. We use dummy register classes if an error
3161
// occurs during lowering because we still need to be able to produce a
3262
// valid HIR.
3363
let sess = self.sess;
34-
let operands: Vec<_> = asm
64+
let mut operands: Vec<_> = asm
3565
.operands
3666
.iter()
3767
.map(|(op, op_sp)| {
@@ -336,6 +366,30 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
336366
}
337367
}
338368

369+
// If a clobber_abi is specified, add the necessary clobbers to the
370+
// operands list.
371+
if let Some((abi, abi_span)) = clobber_abi {
372+
for &clobber in abi.clobbered_regs() {
373+
let mut output_used = false;
374+
clobber.overlapping_regs(|reg| {
375+
if used_output_regs.contains_key(&reg) {
376+
output_used = true;
377+
}
378+
});
379+
380+
if !output_used {
381+
operands.push((
382+
hir::InlineAsmOperand::Out {
383+
reg: asm::InlineAsmRegOrRegClass::Reg(clobber),
384+
late: true,
385+
expr: None,
386+
},
387+
abi_span,
388+
));
389+
}
390+
}
391+
}
392+
339393
let operands = self.arena.alloc_from_iter(operands);
340394
let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
341395
let line_spans = self.arena.alloc_slice(&asm.line_spans[..]);

compiler/rustc_ast_pretty/src/pprust/state.rs

+10
Original file line numberDiff line numberDiff line change
@@ -2186,11 +2186,15 @@ impl<'a> State<'a> {
21862186
enum AsmArg<'a> {
21872187
Template(String),
21882188
Operand(&'a InlineAsmOperand),
2189+
ClobberAbi(Symbol),
21892190
Options(InlineAsmOptions),
21902191
}
21912192

21922193
let mut args = vec![AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template))];
21932194
args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
2195+
if let Some((abi, _)) = asm.clobber_abi {
2196+
args.push(AsmArg::ClobberAbi(abi));
2197+
}
21942198
if !asm.options.is_empty() {
21952199
args.push(AsmArg::Options(asm.options));
21962200
}
@@ -2257,6 +2261,12 @@ impl<'a> State<'a> {
22572261
}
22582262
}
22592263
}
2264+
AsmArg::ClobberAbi(abi) => {
2265+
s.word("clobber_abi");
2266+
s.popen();
2267+
s.print_symbol(*abi, ast::StrStyle::Cooked);
2268+
s.pclose();
2269+
}
22602270
AsmArg::Options(opts) => {
22612271
s.word("options");
22622272
s.popen();

compiler/rustc_builtin_macros/src/asm.rs

+100-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct AsmArgs {
1919
operands: Vec<(ast::InlineAsmOperand, Span)>,
2020
named_args: FxHashMap<Symbol, usize>,
2121
reg_args: FxHashSet<usize>,
22+
clobber_abi: Option<(Symbol, Span)>,
2223
options: ast::InlineAsmOptions,
2324
options_spans: Vec<Span>,
2425
}
@@ -63,6 +64,7 @@ fn parse_args<'a>(
6364
operands: vec![],
6465
named_args: FxHashMap::default(),
6566
reg_args: FxHashSet::default(),
67+
clobber_abi: None,
6668
options: ast::InlineAsmOptions::empty(),
6769
options_spans: vec![],
6870
};
@@ -85,6 +87,13 @@ fn parse_args<'a>(
8587
break;
8688
} // accept trailing commas
8789

90+
// Parse clobber_abi
91+
if p.eat_keyword(sym::clobber_abi) {
92+
parse_clobber_abi(&mut p, &mut args)?;
93+
allow_templates = false;
94+
continue;
95+
}
96+
8897
// Parse options
8998
if p.eat_keyword(sym::options) {
9099
parse_options(&mut p, &mut args, is_global_asm)?;
@@ -160,7 +169,11 @@ fn parse_args<'a>(
160169
ast::ExprKind::Lit(ast::Lit { kind: ast::LitKind::Str(..), .. }) => {}
161170
ast::ExprKind::MacCall(..) => {}
162171
_ => {
163-
let errstr = "expected operand, options, or additional template string";
172+
let errstr = if is_global_asm {
173+
"expected operand, options, or additional template string"
174+
} else {
175+
"expected operand, clobber_abi, options, or additional template string"
176+
};
164177
let mut err = ecx.struct_span_err(template.span, errstr);
165178
err.span_label(template.span, errstr);
166179
return Err(err);
@@ -177,13 +190,19 @@ fn parse_args<'a>(
177190
let slot = args.operands.len();
178191
args.operands.push((op, span));
179192

180-
// Validate the order of named, positional & explicit register operands and options. We do
181-
// this at the end once we have the full span of the argument available.
193+
// Validate the order of named, positional & explicit register operands and
194+
// clobber_abi/options. We do this at the end once we have the full span
195+
// of the argument available.
182196
if !args.options_spans.is_empty() {
183197
ecx.struct_span_err(span, "arguments are not allowed after options")
184198
.span_labels(args.options_spans.clone(), "previous options")
185199
.span_label(span, "argument")
186200
.emit();
201+
} else if let Some((_, abi_span)) = args.clobber_abi {
202+
ecx.struct_span_err(span, "arguments are not allowed after clobber_abi")
203+
.span_label(abi_span, "clobber_abi")
204+
.span_label(span, "argument")
205+
.emit();
187206
}
188207
if explicit_reg {
189208
if name.is_some() {
@@ -256,16 +275,23 @@ fn parse_args<'a>(
256275

257276
let mut have_real_output = false;
258277
let mut outputs_sp = vec![];
278+
let mut regclass_outputs = vec![];
259279
for (op, op_sp) in &args.operands {
260280
match op {
261-
ast::InlineAsmOperand::Out { expr, .. }
262-
| ast::InlineAsmOperand::SplitInOut { out_expr: expr, .. } => {
281+
ast::InlineAsmOperand::Out { reg, expr, .. }
282+
| ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => {
263283
outputs_sp.push(*op_sp);
264284
have_real_output |= expr.is_some();
285+
if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
286+
regclass_outputs.push(*op_sp);
287+
}
265288
}
266-
ast::InlineAsmOperand::InOut { .. } => {
289+
ast::InlineAsmOperand::InOut { reg, .. } => {
267290
outputs_sp.push(*op_sp);
268291
have_real_output = true;
292+
if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
293+
regclass_outputs.push(*op_sp);
294+
}
269295
}
270296
_ => {}
271297
}
@@ -284,6 +310,24 @@ fn parse_args<'a>(
284310
// Bail out now since this is likely to confuse MIR
285311
return Err(err);
286312
}
313+
if let Some((_, abi_span)) = args.clobber_abi {
314+
if is_global_asm {
315+
let err =
316+
ecx.struct_span_err(abi_span, "`clobber_abi` cannot be used with `global_asm!`");
317+
318+
// Bail out now since this is likely to confuse later stages
319+
return Err(err);
320+
}
321+
if !regclass_outputs.is_empty() {
322+
ecx.struct_span_err(
323+
regclass_outputs.clone(),
324+
"asm with `clobber_abi` must specify explicit registers for outputs",
325+
)
326+
.span_label(abi_span, "clobber_abi")
327+
.span_labels(regclass_outputs, "generic outputs")
328+
.emit();
329+
}
330+
}
287331

288332
Ok(args)
289333
}
@@ -375,6 +419,49 @@ fn parse_options<'a>(
375419
Ok(())
376420
}
377421

422+
fn parse_clobber_abi<'a>(
423+
p: &mut Parser<'a>,
424+
args: &mut AsmArgs,
425+
) -> Result<(), DiagnosticBuilder<'a>> {
426+
let span_start = p.prev_token.span;
427+
428+
p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
429+
430+
let clobber_abi = match p.parse_str_lit() {
431+
Ok(str_lit) => str_lit.symbol_unescaped,
432+
Err(opt_lit) => {
433+
let span = opt_lit.map_or(p.token.span, |lit| lit.span);
434+
let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
435+
err.span_label(span, "not a string literal");
436+
return Err(err);
437+
}
438+
};
439+
440+
p.expect(&token::CloseDelim(token::DelimToken::Paren))?;
441+
442+
let new_span = span_start.to(p.prev_token.span);
443+
444+
if let Some((_, prev_span)) = args.clobber_abi {
445+
let mut err = p
446+
.sess
447+
.span_diagnostic
448+
.struct_span_err(new_span, "clobber_abi specified multiple times");
449+
err.span_label(prev_span, "clobber_abi previously specified here");
450+
return Err(err);
451+
} else if !args.options_spans.is_empty() {
452+
let mut err = p
453+
.sess
454+
.span_diagnostic
455+
.struct_span_err(new_span, "clobber_abi is not allowed after options");
456+
err.span_labels(args.options_spans.clone(), "options");
457+
return Err(err);
458+
}
459+
460+
args.clobber_abi = Some((clobber_abi, new_span));
461+
462+
Ok(())
463+
}
464+
378465
fn parse_reg<'a>(
379466
p: &mut Parser<'a>,
380467
explicit_reg: &mut bool,
@@ -660,7 +747,13 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
660747
}
661748
}
662749

663-
Some(ast::InlineAsm { template, operands: args.operands, options: args.options, line_spans })
750+
Some(ast::InlineAsm {
751+
template,
752+
operands: args.operands,
753+
clobber_abi: args.clobber_abi,
754+
options: args.options,
755+
line_spans,
756+
})
664757
}
665758

666759
pub fn expand_asm<'cx>(

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ symbols! {
377377
char,
378378
client,
379379
clippy,
380+
clobber_abi,
380381
clone,
381382
clone_closures,
382383
clone_from,

0 commit comments

Comments
 (0)