-
Notifications
You must be signed in to change notification settings - Fork 13.3k
add asm_cfg
: #[cfg(...)]
within asm!
#140367
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
base: master
Are you sure you want to change the base?
Conversation
let is_configured_out = | ||
ecx.ecfg.features.asm_cfg() && strip_unconfigured.configure(attributes).is_none(); | ||
|
||
let template = p.parse_expr()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact that we use parse_expr
here mean that this does not work, and does not produce a great error message. This complains about an unexpected ,
after the 6
, but the real problem is that this cannot possibly be or expand to a string literal.
asm!(
#[cfg(false)]
a = const 6,
"nop",
);
Maybe we should specialize the parser to specifically only parse string literals and item macros?
if p.token == token::Eof { | ||
return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp })); | ||
} | ||
|
||
let first_template = p.parse_expr()?; | ||
let first_template = loop { | ||
let attributes = AsmAttrVec::parse(ecx, p)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attributes are now parsed before the parse_expr
below, and never attached to the expression. In theory that could interact with #[feature(stmt_expr_attributes)]
, but:
- that feature is unstable
- any attributes it parsed would have no effect
_ => { | ||
ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() }); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
non-cfg
attributes are now accepted by the parser, but emit this error. Previously attributes were only parsed (and later rejected, unless #[feature(stmt_expr_attributes)]
was enabled) on expressions. We now also always parse them on operands.
This comment has been minimized.
This comment has been minimized.
Hmm, rust analyzer also parses the assembly, but from what I can see it has no way of evaluating the @rust-lang/rust-analyzer how can we make this work? |
(I haven't looked at the PR, but) parsing does not evaluated cfgs. Hir lowering does. |
Well in any case the parser doesn't. I'm not exactly sure where to draw the line for when HIR lowering starts, but certainly macro expansion of builtin macros is able to do it, e.g. here for rust/compiler/rustc_builtin_macros/src/cfg.rs Lines 24 to 30 in 267cae5
Anyway, are you saying that rust-analyzer just cannot evaluate cfg's while parsing the |
rust-analyzer has to duplicate these changes either way. We don't use any of the touched rustc crates here. |
apparently it doesn't really use the asm parsing at present, so this may work?
Reminder, once the PR becomes ready for a review, use |
I'm still take a look through this, but I don't know enough about this area of the compiler to give a good review. Picking somebody who shows up in the blame a lot: r? @nnethercote |
Some changes occurred in src/tools/rustfmt cc @rust-lang/rustfmt |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rustbot author
loop { | ||
if p.token != token::Pound { | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be phrased as while
, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but I think this whole loop/function needs to change, to make the parsing of attributes consistent.
@nnethercote you seem to have worked on the attribute parsing a bit (and are the current reviewer here), what would be a good way to do this? e.g. this function is private, but I think it's what should be used here:
pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> { |
It returns an AttrWrapper
which deliberately does not give access to the inner attributes until you call collect_tokens
(another private function). Does it make sense to make those functions public, or should the asm attribute parsing live in rustc_parse
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
after thinking about this some more, it might makes sense to, now that parsing and validation are split, to move the asm parsing code into rustc_parse
?
|
||
fn main() { | ||
unsafe { | ||
// Templates are not allowed after operands (even if the operands are configured out). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a reasonable restriction? I'm thinking here about macros that might want to expand something like:
cfg_match! {
one => { "jmp here", options(some_option), };
two => { "jmp there", options(other_option), };
}
Into:
#[cfg(one)]
"jmp here",
#[cfg(one)]
options(some_option),
#[cfg(and(not(one), two)]
"jmp there",
#[cfg(and(not(one), two)]
options(other_option),
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm yeah that's a good point.
We'd have to make some further parser changes to make that work. Currently the parsing rules change based on whether a template has already been parsed (mostly for error message quality).
A second issue is that templates are currently parsed with the general expression parser, but some of the non-template arguments parse as valid expressions (e.g. options(noreturn)
looks like a function call, and const { 5 }
is also a valid expression).
A solution is to instead parse specifically string literals and macro calls, those are templates, and if those fail to parse, try to parse an operand.
//@ revisions: reva revb | ||
//@ only-x86_64 | ||
//@ run-pass | ||
#![feature(asm_cfg, cfg_match)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be worth showing that cfg_match!
works within asm!
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It really doesn't work right now. I believe there are some issues with using cfg_match!
in expression position.
error: macro expansion ignores `#` and any tokens following
--> /rustc/FAKE_PREFIX/library/core/src/macros/mod.rs:255:13
|
::: /home/folkertdev/rust/rust/tests/ui/asm/cfg.rs:84:9
|
LL | / cfg_match! {
LL | | reva => { "mov rax, 5" }
LL | | revb => { "mov rax, 10" }
LL | | },
| |_________- caused by the macro expansion here
|
= note: the usage of `cfg_match!` is likely invalid in expression context
help: you might be missing a semicolon here
--> /home/folkertdev/rust/rust/tests/ui/asm/cfg.rs:87:10
|
LL | };,
| +
Also using
cfg_match! {
reva => "mov rax, 5",
revb => "mov rax, 10",
},
Makes macro expansion hit the recursion limit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now you have to write cfg_match! {{ .. }}
for it to work in expression position (no longer requiring the double brackets is a stabilization blocker for cfg_match!
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still struggling with getting this to work. I think this should work, but it expands to an Expr::Block
, which is not a string, and hence rejected.
#[unsafe(naked)]
extern "C" fn with_cfg_match() -> u64 {
core::arch::naked_asm!(
cfg_match!{{
unix => {"mov rax, 5"}
_ => {"mov rax, 10"}
}},
"ret",
)
}
using this instead
cfg_match!{{
unix => "mov rax, 5",
_ => "mov rax, 10",
}},
does not parse,
error: expected identifier, found reserved identifier `_`
--> src/main.rs:10:9
|
10 | / cfg_match!{{
11 | | unix => "mov rax, 5",
12 | | _ => "mov rax, 10",
13 | | }},
| |__________^ expected identifier, found reserved identifier
So, am I still using this macro wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tracking issue: #140364
blocked on: #140490
This feature was discussed in #140279. It allows configuring templates and operands in the assembly macros, for example:
r? @tgross35
cc @traviscross @Amanieu
Now builds on #140490, which should be merged first.