Skip to content

Commit 21bb2f2

Browse files
committed
support #[cfg(...)] on arguments to the asm! macros
1 parent 4455c89 commit 21bb2f2

File tree

10 files changed

+373
-4
lines changed

10 files changed

+373
-4
lines changed

compiler/rustc_builtin_macros/messages.ftl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
builtin_macros_alloc_error_must_be_fn = alloc_error_handler must be a function
22
builtin_macros_alloc_must_statics = allocators must be statics
33
4+
builtin_macros_asm_attribute_not_supported =
5+
this attribute is not supported on assembly
6+
builtin_macros_asm_cfg =
7+
the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable
8+
49
builtin_macros_asm_clobber_abi = clobber_abi
510
builtin_macros_asm_clobber_no_reg = asm with `clobber_abi` must specify explicit registers for outputs
611
builtin_macros_asm_clobber_outputs = generic outputs

compiler/rustc_builtin_macros/src/asm.rs

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,20 @@ use rustc_index::bit_set::GrowableBitSet;
1010
use rustc_parse::exp;
1111
use rustc_parse::parser::{ExpKeywordPair, Parser};
1212
use rustc_session::lint;
13-
use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw};
13+
use rustc_session::parse::feature_err;
14+
use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw, sym};
1415
use rustc_target::asm::InlineAsmArch;
1516
use smallvec::smallvec;
1617
use {rustc_ast as ast, rustc_parse_format as parse};
1718

18-
use crate::errors;
1919
use crate::util::{ExprToSpannedString, expr_to_spanned_string};
20+
use crate::{errors, fluent_generated as fluent};
2021

2122
/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise
2223
/// not validated at all.
2324
pub struct AsmArg {
2425
pub kind: AsmArgKind,
26+
pub attributes: AsmAttrVec,
2527
pub span: Span,
2628
}
2729

@@ -52,6 +54,44 @@ struct ValidatedAsmArgs {
5254
pub options_spans: Vec<Span>,
5355
}
5456

57+
/// A parsed list of attributes that is not attached to any item.
58+
/// Used to check whether `asm!` arguments are configured out.
59+
pub struct AsmAttrVec(pub ast::AttrVec);
60+
61+
impl AsmAttrVec {
62+
fn parse<'a>(p: &mut Parser<'a>) -> PResult<'a, Self> {
63+
let mut attributes = ast::AttrVec::new();
64+
while p.token == token::Pound {
65+
let attr = p.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)?;
66+
attributes.push(attr);
67+
}
68+
69+
Ok(Self(attributes))
70+
}
71+
}
72+
impl ast::HasAttrs for AsmAttrVec {
73+
// Follows `ast::Expr`.
74+
const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
75+
76+
fn attrs(&self) -> &[rustc_ast::Attribute] {
77+
&self.0
78+
}
79+
80+
fn visit_attrs(&mut self, f: impl FnOnce(&mut rustc_ast::AttrVec)) {
81+
f(&mut self.0)
82+
}
83+
}
84+
85+
impl ast::HasTokens for AsmAttrVec {
86+
fn tokens(&self) -> Option<&rustc_ast::tokenstream::LazyAttrTokenStream> {
87+
None
88+
}
89+
90+
fn tokens_mut(&mut self) -> Option<&mut Option<rustc_ast::tokenstream::LazyAttrTokenStream>> {
91+
None
92+
}
93+
}
94+
5595
/// Used for better error messages when operand types are used that are not
5696
/// supported by the current macro (e.g. `in` or `out` for `global_asm!`)
5797
///
@@ -167,8 +207,13 @@ pub fn parse_asm_args<'a>(
167207

168208
let mut args = Vec::new();
169209

210+
let attributes = AsmAttrVec::parse(p)?;
170211
let first_template = p.parse_expr()?;
171-
args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) });
212+
args.push(AsmArg {
213+
span: first_template.span,
214+
kind: AsmArgKind::Template(first_template),
215+
attributes,
216+
});
172217

173218
let mut allow_templates = true;
174219

@@ -188,6 +233,7 @@ pub fn parse_asm_args<'a>(
188233
break;
189234
}
190235

236+
let attributes = AsmAttrVec::parse(p)?;
191237
let span_start = p.token.span;
192238

193239
// Parse `clobber_abi`.
@@ -197,6 +243,7 @@ pub fn parse_asm_args<'a>(
197243
args.push(AsmArg {
198244
kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?),
199245
span: span_start.to(p.prev_token.span),
246+
attributes,
200247
});
201248

202249
continue;
@@ -209,6 +256,7 @@ pub fn parse_asm_args<'a>(
209256
args.push(AsmArg {
210257
kind: AsmArgKind::Options(parse_options(p, asm_macro)?),
211258
span: span_start.to(p.prev_token.span),
259+
attributes,
212260
});
213261

214262
continue;
@@ -231,6 +279,7 @@ pub fn parse_asm_args<'a>(
231279
args.push(AsmArg {
232280
span: span_start.to(p.prev_token.span),
233281
kind: AsmArgKind::Operand(name, op),
282+
attributes,
234283
});
235284
} else if allow_templates {
236285
let template = p.parse_expr()?;
@@ -252,7 +301,11 @@ pub fn parse_asm_args<'a>(
252301
}
253302
}
254303

255-
args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) });
304+
args.push(AsmArg {
305+
span: template.span,
306+
kind: AsmArgKind::Template(template),
307+
attributes,
308+
});
256309
} else {
257310
p.unexpected_any()?
258311
}
@@ -278,6 +331,13 @@ fn validate_asm_args<'a>(
278331
) -> PResult<'a, ValidatedAsmArgs> {
279332
let dcx = ecx.dcx();
280333

334+
let strip_unconfigured = rustc_expand::config::StripUnconfigured {
335+
sess: ecx.sess,
336+
features: Some(ecx.ecfg.features),
337+
config_tokens: false,
338+
lint_node_id: ecx.current_expansion.lint_node_id,
339+
};
340+
281341
let mut validated = ValidatedAsmArgs {
282342
templates: vec![],
283343
operands: vec![],
@@ -291,6 +351,26 @@ fn validate_asm_args<'a>(
291351
let mut allow_templates = true;
292352

293353
for arg in args {
354+
for attr in arg.attributes.0.iter() {
355+
match attr.name() {
356+
Some(sym::cfg | sym::cfg_attr) => {
357+
if !ecx.ecfg.features.asm_cfg() {
358+
let span = attr.span();
359+
feature_err(ecx.sess, sym::asm_cfg, span, fluent::builtin_macros_asm_cfg)
360+
.emit();
361+
}
362+
}
363+
_ => {
364+
ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() });
365+
}
366+
}
367+
}
368+
369+
// Skip arguments that are configured out.
370+
if ecx.ecfg.features.asm_cfg() && strip_unconfigured.configure(arg.attributes).is_none() {
371+
continue;
372+
}
373+
294374
match arg.kind {
295375
AsmArgKind::Template(template) => {
296376
// The error for the first template is delayed.

compiler/rustc_builtin_macros/src/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,13 @@ pub(crate) struct AsmRequiresTemplate {
795795
pub(crate) span: Span,
796796
}
797797

798+
#[derive(Diagnostic)]
799+
#[diag(builtin_macros_asm_attribute_not_supported)]
800+
pub(crate) struct AsmAttributeNotSupported {
801+
#[primary_span]
802+
pub(crate) span: Span,
803+
}
804+
798805
#[derive(Diagnostic)]
799806
#[diag(builtin_macros_asm_expected_comma)]
800807
pub(crate) struct AsmExpectedComma {

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@ declare_features! (
371371
(unstable, arbitrary_self_types, "1.23.0", Some(44874)),
372372
/// Allows inherent and trait methods with arbitrary self types that are raw pointers.
373373
(unstable, arbitrary_self_types_pointers, "1.83.0", Some(44874)),
374+
/// Allows #[cfg(...)] on inline assembly templates and operands.
375+
(unstable, asm_cfg, "CURRENT_RUSTC_VERSION", Some(140364)),
374376
/// Enables experimental inline assembly support for additional architectures.
375377
(unstable, asm_experimental_arch, "1.58.0", Some(93335)),
376378
/// Enables experimental register support in inline assembly.

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ symbols! {
475475
as_ref,
476476
as_str,
477477
asm,
478+
asm_cfg,
478479
asm_const,
479480
asm_experimental_arch,
480481
asm_experimental_reg,

tests/ui/asm/cfg-parse-error.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//@ needs-asm-support
2+
#![feature(asm_cfg)]
3+
4+
use std::arch::asm;
5+
6+
fn main() {
7+
unsafe {
8+
// Templates are not allowed after operands (even if the operands are configured out).
9+
asm!(
10+
"",
11+
#[cfg(false)]
12+
clobber_abi("C"),
13+
#[cfg(false)]
14+
options(att_syntax),
15+
#[cfg(false)]
16+
a = out(reg) x,
17+
"",
18+
//~^ ERROR expected one of `clobber_abi`, `const`
19+
);
20+
asm!(
21+
#[cfg(false)]
22+
"",
23+
#[cfg(false)]
24+
const {
25+
5
26+
},
27+
"", //~ ERROR expected one of `clobber_abi`, `const`
28+
);
29+
30+
// This is currently accepted because `const { 5 }` parses as an expression.
31+
asm!(
32+
#[cfg(false)]
33+
const {
34+
5
35+
},
36+
"",
37+
);
38+
// This is not accepted because `a = out(reg) x` is not a valid expresion.
39+
asm!(
40+
#[cfg(false)]
41+
a = out(reg) x, //~ ERROR expected token: `,`
42+
"",
43+
);
44+
}
45+
}

tests/ui/asm/cfg-parse-error.stderr

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""`
2+
--> $DIR/cfg-parse-error.rs:17:13
3+
|
4+
LL | a = out(reg) x,
5+
| - expected one of 10 possible tokens
6+
LL | "",
7+
| ^^ unexpected token
8+
9+
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""`
10+
--> $DIR/cfg-parse-error.rs:27:13
11+
|
12+
LL | },
13+
| - expected one of 10 possible tokens
14+
LL | "",
15+
| ^^ unexpected token
16+
17+
error: expected token: `,`
18+
--> $DIR/cfg-parse-error.rs:41:26
19+
|
20+
LL | a = out(reg) x,
21+
| ^ expected `,`
22+
23+
error: aborting due to 3 previous errors
24+

tests/ui/asm/cfg.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Check that `cfg` and `cfg_attr` work as expected.
2+
//
3+
//@ revisions: reva revb
4+
//@ only-x86_64
5+
//@ run-pass
6+
#![feature(asm_cfg, cfg_match)]
7+
8+
use std::arch::{asm, naked_asm};
9+
10+
#[unsafe(naked)]
11+
extern "C" fn ignore_const_operand() -> u64 {
12+
naked_asm!(
13+
"mov rax, 5",
14+
#[cfg(revb)]
15+
"mov rax, {a}",
16+
"ret",
17+
#[cfg(revb)]
18+
a = const 10,
19+
)
20+
}
21+
22+
#[unsafe(naked)]
23+
extern "C" fn ignore_const_operand_cfg_attr() -> u64 {
24+
naked_asm!(
25+
"mov rax, 5",
26+
#[cfg_attr(true, cfg(revb))]
27+
"mov rax, {a}",
28+
"ret",
29+
#[cfg_attr(true, cfg(revb))]
30+
a = const 10,
31+
)
32+
}
33+
34+
#[unsafe(naked)]
35+
extern "C" fn const_operand() -> u64 {
36+
naked_asm!(
37+
"mov rax, {a}",
38+
"ret",
39+
#[cfg(reva)]
40+
a = const 5,
41+
#[cfg(revb)]
42+
a = const 10,
43+
)
44+
}
45+
46+
fn options() {
47+
// Without the cfg, this throws an error that the `noreturn` option is provided twice.
48+
unsafe {
49+
asm!(
50+
"nop",
51+
#[cfg(false)]
52+
options(att_syntax),
53+
options(att_syntax)
54+
)
55+
}
56+
}
57+
58+
fn clobber_abi() {
59+
// Without the cfg, this throws an error that the "C" abi is provided twice.
60+
unsafe {
61+
asm!(
62+
"nop",
63+
#[cfg(false)]
64+
clobber_abi("C"),
65+
clobber_abi("C"),
66+
);
67+
}
68+
}
69+
70+
#[unsafe(naked)]
71+
extern "C" fn first_template() -> u64 {
72+
naked_asm!(
73+
#[cfg(reva)]
74+
"mov rax, 5",
75+
#[cfg(revb)]
76+
"mov rax, 10",
77+
"ret",
78+
)
79+
}
80+
81+
pub fn main() {
82+
std::cfg_match! {
83+
reva => {
84+
assert_eq!(const_operand(), 5);
85+
assert_eq!(ignore_const_operand_cfg_attr(), 5);
86+
assert_eq!(ignore_const_operand(), 5);
87+
assert_eq!(first_template(), 5);
88+
89+
}
90+
revb => {
91+
assert_eq!(const_operand(), 10);
92+
assert_eq!(ignore_const_operand_cfg_attr(), 10);
93+
assert_eq!(ignore_const_operand(), 10);
94+
assert_eq!(first_template(), 10);
95+
96+
}
97+
}
98+
options();
99+
clobber_abi();
100+
}

0 commit comments

Comments
 (0)