Change:
#[unstable(feature = "stdsimd", issue = "48556")] mod core_arch; <ins> #[doc = include_str!("../../stdarch/crates/core_arch/src/core_arch_docs.md")] </ins> #[stable(feature = "simd_arch", since = "1.27.0")] <del> pub use core_arch::arch; </del> <ins> pub mod arch { #[stable(feature = "simd_arch", since = "1.27.0")] pub use crate::core_arch::arch::*; /// Inline assembly. /// /// Read the [unstable book] for the usage. /// /// [unstable book]: ../../unstable-book/library-features/asm.html #[unstable( feature = "asm", issue = "72016", reason = "inline assembly is not stable enough for use and is subject to change" )] #[rustc_builtin_macro] pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) { /* compiler built-in */ } /// Module-level inline assembly. #[unstable( feature = "global_asm", issue = "35119", reason = "`global_asm!` is not stable enough for use and is subject to change" )] #[rustc_builtin_macro] pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) { /* compiler built-in */ } } </ins>

Discussion:
Original issue preserved below: As of today, most macros in Rust, despite being declared by the stdlib, are not properly namespaced in the sense that every other type, function, and trait in the stdlib is namespaced. Instead, historically, macros "live" in the crate root due to the technical limitations of Rust 1.0, which did not support namespacing for macros. This is distinct from items such as or which are merely available as though they live in the crate root (via the prelude), but are actually defined at and . This has two primary downsides: stdlib API index is polluted by a wall of random, unrelated macros, many of which are relatively unimportant or irrelevant: an item in the crate root is tantamount to exporting that item from the prelude, which means that adding a macro there totally bypasses the discussion of whether the macro should be exported from the prelude. In Rust 1.51,  became the first stable macro to not be defined in the crate root. The machinery exists to namespace macros, and there seems to be at least loose consensus that this is worth using for new macros in the stdlib. One of the last remaining open questions for is where it should live. Given the above, and the history of prior discussion on this topic, there are two options for consideration: , where is every architecture supported by the macro. (For conciseness I will only be referring to in this document, but this decision also applies to any and all related macros, such as .) First, the non-advantages of either option: -time rejection of unsupported platforms: while makes it immediately and syntactically obvious that a given platform is unsupported by dint of a nonexistent symbol, using on an unsupported platform is still a compiler error. in the prelude: while is straightforwardly obvious to export from the prelude, and friends could still possibly be exported from the prelude via . -dependent behavior: even aside from the literal assembly code itself, it is possible for the macro to have slightly different semantics on different architectures, e.g. architecture-specific register classes or clobbering behavior. While is more explicit about this potential difference, it is not necessary for enabling such behavior. -specific stabilization or deprecation: while is more straightforward to deprecate/stabilize on a per-target basis, can be used to the same effect even for .

The relevant differences between the the two options: the symbol: even assuming that is not to the prelude, would still be trivial to in architecture-dependent code. Conversely, without a prelude addition, would have to fall back on a few patterns when writing architecture-dependent code, some of which are more verbose than others. Below, Pattern is the one that comes closest to the ergonomics of , but it may be non-obvious, and it involves using a glob-import, which some may find distasteful (or even be linting against): : the documentation of would have to document all architecture-dependent behavior, which could make it unwieldy. would isolate architecture-dependent documentation, however, every module would be forced to duplicate all non-architecture-dependent documentation, which seems unfortunate in its own way. reporting in the event of unguarded : having , or else having but having in the prelude, increases the chances that someone will write an invocation that is not guarded by any directive or any other mention of the original author's platform. Such an unguarded invocation would probably give unhelpful errors if the code is ever compiled for a platform that supports in general but was not considered by this specific invocation. Even worse, the code could compile but have unintended behavior. Conversely, essentially guarantees that an author's code will have to mention their intended platform somewhere, either in a or in an expression, and users on different platforms will receive obvious "cannot find value in this scope" errors when attempting to compile it. However, this benefit requires that never be to the prelude (and never adding any other way of circumventing the need to mention a platform, e.g. having both and ). proliferation: currently, supports architectures that do not have a corresponding module under . The approach would require a module for all supported architectures going forward. This includes adding modules for targets that may not ever be supported by rustc itself (e.g. SPIR-V), but are supported by the macro for the benefit of alternative backends. However, in either scenario alternative backends would still need to provide a patch to support new targets in , and adding a new module for any unstably-supported target doesn't seem like a particularly onerous part of that process. However, given enough time (and enough alternative backends, e.g.

a GCC one) this could lead to quite a few submodules under , although to some degree that is the point of the module. -agnostic assembly: it is possible for a single invocation to properly support multiple targets. This is the flipside of point 3 stated above: where is maximally permissive, is maximally strict. With , writing platform-agnostic assembly would require a pattern like the following: TL;DR: the benefits of either approach are fairly small. is easier to use, but only if is not to the prelude. could lead to better error messages in some cases, but likewise only if is not to the prelude. If the decision is made to add to the prelude, then there is essentially no advantage to either option.
Strongly support . I find the kind of issue discussed in "3. Error reporting in the event of unguarded asm: having arch::asm" to be determinative. I'm less worried about error reporting, though, than about having something accidentally cleanly assemble with different semantics because of the wrong architecture. I have an example lying around somewhere in the issue tracker that will assemble for both and : this is scary, since the operand order for the default assembler is source, dest while is dest, source. So now you've got probably UB or at least a surprising bug when somebody accidentally compiles the unqualified code for the wrong architecture.
Another approach is to have arch-specific implementations in separate modules where the whole module is cfg'd, so you can use regular imports inside.
When you write I assume you mean , right?
Yes, this would technically live in , I was merely ignoring the for simplicity.
I'd like to see it in , so macro's stabilisation is not tight to a specific ISA's being stable. There are also archs that Rust can target but does not yet have a presence in , e.g. RISC-V.
I don't think this is actually a requirement; could be stabilized independently of . The converse is also true: could be stabilized only on certain targets via selective use of .
Oh another think I think it probably does make sense to have them in , as does not support all ISAs at the moment. It seems that nothing prevents us from having stablised with just in it.

Note that there is also an extensive Zulip thread where discussion is taking place: https://rust-!.60
As a somewhat tangential benefit to namespacing under the specific architecture modules is something like where tooling would benefit from understanding which architecture the asm block is for
One thing worth keeping in mind is that this decision may influence future macro namespacing. Consider these unstable macros: It would be a bit of a reach to suggest that these macros are universal enough that they need to live in the prelude; they really ought to be namespaced. But should they be namespaced under or under ? Whatever is decided for and could very well set precedent for that decision.
Now that an MVP , I propose that this issue has spent long enough percolating. I've reviewed the comments from all the prior threads and will here summarize what I believe to be the status of this issue, so that the libs team can make a final decision. Should the macro be defined as , or should each supported architecture in instead export its own copy of the macro (, etc.)? The most important fundamental difference between these two is that the first does not strictly require the user to have any directive in their code (though it is conceivable that they often (perhaps even usually) will as part of writing multi-platform code)), and the latter does strictly require it. Whatever is decided for will also apply to all of its (potential) siblings, e.g. , . Pros: to import and invoke; no is necessary. no is necessary, writing cross-platform does not require any extra effort. list of submodules in does not need to be tightly coupled to the platforms supported by . is no complication regarding the decision of whether to ever add to the prelude. Cons: there is no strict requirement that a directive appears anywhere in the user's code, it is possible that a library author might write an block intended for one platform that is then later executed on another platform, which at best would fail to compile, but at worst could run with unknown results. there are platform-specific features, the documentation for will have to mention all of them for all platforms. Pros: the presence of a somewhere is necessary to invoke the macro, users will have no choice but to explicitly gate their invocations to their intended platforms.

There is no risk that a library author might write an block for x86 that is then accidentally compiled for (and potentially even runs on, with unknown results) ARM. Cons: introduces tight coupling between the submodules in and the list of platforms supported by . This would be a departure from the current policy, where the possible submodules in are determined by available values of the argument to , which is part of rustc’s own target stability considerations, which are under the control of the compiler team rather than the libs team. The macro may support platforms that are highly backend-specific, such as SPIR-V via the rust-gpu backend; such modules will nonetheless need to be to . This may also complicate the situation of people using target specifications to target unsupported platforms, although many of these may already require changes to the compiler/stdlib in order to make usable in the first place. and invoking is slightly complicated, due to the need for . You must either not the item at all and refer to it only via its fully-qualified name, or -guard every import and invocation individually, or introduce local in each 'd context, or glob import , or have distinct 'd modules for every platform. each public item is specific to one , writing cross-platform has friction, however this can be addressed via creative aliases. this approach does not technically prevent Rust from ever adding to the prelude, ever doing so would allow users to use the macro without the presence of a mandatory directive, which would totally obviate the main advantage of this approach. Thus this is essentially a promise that will never be in the prelude (or any other location that would allow users to avoid ; in particular this is the reason we cannot do both approaches). individual item needs to duplicate a large portion (likely the majority) of the documentation.
We discussed this in today's meeting. Our consensus was to put in . We felt that per-arch paths for it would not provide substantive protection for the issues that are more likely to arise in practice; it addresses the mixing of top-level architectures, but does not address architecture variants that aren't reflected in , CPU features, OS-specific assembly, or many other similar things. Given that, we felt that we shouldn't inflict the additional pain of the arch-specific paths on the users of . plans to send in a PR that moves to and re-exports it from the prelude.

We can decide before stabilization whether to keep the re-export in the prelude. (Thank you to for the detailed summary of the alternatives and tradeoffs.)