-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Lang proposal: Allow #[cfg(...)]
within asm!
#140279
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
Comments
Unfortunately I won't have the chance to work on this (at least for a while), but this been mentioned in a few different places so I wrote this up to keep track. I'll probably add help wanted labels in case somebody interested wants to pick this up before I do, but @rust-lang/lang would anybody mind giving this a vibe check first? I'll nominate as well, feel free to remove if that doesn't make sense. @rustbot label +I-lang-nominated +I-lang-easy-decision |
Cc @folkertdev in case you have any thoughts here, since you have done a lot of work on assembly internals recently. |
General thoughts: I like the concept, and it seems reasonable, but it also seems likely to become very verbose and redundant if you have many lines tagged with the same complex cfg. In C you can wrap multiple lines in the same ifdef, and I think this proposed syntax for I'm wondering if we should proactively address that use case, to avoid encouraging suboptimal formatting like that. For instance, perhaps we should allow: asm!(
"always",
#[cfg(complicated)]
{
"sometimes",
"perhaps",
}
); Also, ideally |
I mentioned blocks on the Zulip thread as well, and think they would be a nice to have because it also allows applying the same @adamgreig linked to an example at https://github.com/rust-embedded/cortex-m/blob/c3d664bba1148cc2d0f963ebeb788aa347ba81f7/cortex-m-rt/src/lib.rs#L528-L636 that uses merged strings and looks pretty good, for comparison. One downside of blocks is that we don't have any cases (that I can think of) where let x = [1, 2, #[cfg(unix)] { 3, 4 }, 5]; Which may make Unfortunately with grouping, this got less easy :) @rustbot label -I-lang-easy-decision (looks like I'm racing with rustbot on the labels) |
@tgross35 Yeah, that linked example is a good case study. I hadn't thought about the possibility of just using the newline and continuing the string constant. That doesn't look too bad. We should evaluate how we want to see people write that example, in an ideal world:
|
Personally, I would somewhat favor blocks, because I think multi-line strings blend together a bit much, and it's harder to see the boundaries between them. In the linked example, there are large comments and blank lines, which help; in the absence of those comments and blank lines, if you just have |
It seems like |
@ktnlvr I don't think exhaustiveness would be an issue here; if there's no matching branch, that |
In general, I'm very much in favor of this idea! The simple
Absolutely! I'm lacking some context here, but the Could we make this work (without an additional cfg_match! {
feature = "zero-init-ram" => concat!(
"ldr r0, =_ram_start",
"ldr r1, =_ram_end",
)
_ => concat!(
"ldr r0, =__sbss",
"ldr r1, =__ebss",
)
}
This is interesting. Firstly, this example could now be a naked function I believe. #[unsafe(naked)]
extern "C" fn Reset() {
naked_asm!(
"..."
)
} That removes a bunch of ceremony from the start/end. I see no real advantage to blocks versus asm!(
#[cfg(feature = "zero-init-ram")]
{
"ldr r0, =_ram_start",
"ldr r1, =_ram_end",
}
#[cfg(feature = "zero-init-ram")]
concat!(
"ldr r0, =_ram_start",
"ldr r1, =_ram_end",
)
) So after some playing around I end up with this https://gist.github.com/folkertdev/6a5ebe1272b38472310cec3964b6ae44. The formatter removes additional whitespace, but formatting of I'd definitely be up for implementing this if we feel like we have a design that works. Just a plain asm!(
#[cfg(target_feature = "sse2")]
"nop",
) So we could start there, and see what else makes sense to add as we go? |
We should also probably ask whether it makes sense to special-case Maybe some version of cc #115585 |
@traviscross to clarify, are you suggesting that |
fn f(_: u8, _: u8) {}
macro_rules! g {
($($es:expr),*) => { f($($es),*) };
}
fn main() {
f(#[cfg(true)] 0, #[cfg(false)] 0, #[cfg(true)] 0); //~ OK
g!(#[cfg(true)] 0, #[cfg(false)] 0, #[cfg(true)] 0); //~ OK
} ...so we do -- even through macro calls in the form of My personal estimate is that we'd treat doing that for asm as a straightforward extension (i.e. no RFC required), and that a stabilization PR for this would be likely well received upon its nomination for us. |
I'm digging through the code a bit more (it's not an area I know well, but we'll get there), and the reason for the "removing an expression is not supported in this position" error is the use of trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
// ...
fn expand_cfg_false(
&mut self,
collector: &mut InvocationCollector<'_, '_>,
_pos: usize,
span: Span,
) {
collector.cx.dcx().emit_err(RemoveNodeNotSupported { span, descr: Self::descr() });
}
} Normal expressions follow a different path, and for them The format!(
#[cfg(false)]
"foo",
#[cfg(true)]
"bar",
) Also using the expression logic for expansion clearly saves a lot of code, but I think some specialization is needed to tell the |
The |
Ok yes that is much easier, thanks!
Is that restriction needed? I don't see why this sort of thing is useful per se, but it seems kind of arbitrary to disallow e.g. this: std::arch::asm!(
"/* {0} */",
#[cfg(a)]
const 4,
#[cfg(not(a))]
const 8,
); Actually, do you have specific examples that you'd want to disallow? |
My main concern is that the index of operands can vary depending on which cfgs are enabled. We don't have to disallow it, but I feel it makes the std::arch::asm!(
"/* {0} */",
#[cfg(a)]
const 4,
const 8, // <-- the index of this operand depends on cfg(a)
); |
If we do that, it may make sense to also disallow use of positional arguments in string literals that are gated by |
That seems, to me, to be the territory of lints rather than hard errors: we could lint if a |
Agreed. For a lint, perhaps we'd want it to push people toward One might say, in a way, that we already accept this sort of thing. unsafe extern "C" {
fn printf(fmt: *const i8, ...);
}
fn main() {
unsafe {
printf(
c"%s".as_ptr(),
#[cfg(false)]
c"Hello world.\n".as_ptr(),
c"Hello Rust.\n".as_ptr(),
);
}
} |
Background
Currently there isn't an easy way to enable or disable portions of assembly based on configuration. This can be a annoyance for
asm!
that can be worked around with multiple assembly blocks, but it is especially a problem inglobal_asm!
andnaked_asm!
because small config-based changes mean the entire assembly block needs to be duplicated.Another workaround is possible by defining multiple
macro_rules!
of the same name that are enabled/disabled based on the desired config and expand to strings. This option is inconvenient and fragments the assembly.Proposal
Allow
#[cfg(...)]
in all assembly macros:The configuration applies to a single comma-separated segment.
We may want to also support blocks in order to allow grouping instructions without merging strings, or grouping directives:
This design has some concerns and there may be a better option, see discussion in the thread.
Zulip discussion: #project-inline-asm > `cfg` in assembly blocks
The text was updated successfully, but these errors were encountered: