@Amanieu
Thank you, this read very nicely, explained everything in a way that I could understand, made instantaneous sense, and feels like a syntax that I'd love to use for inline assembly.
What follows are some of the notes I wrote down while reading it, so interpret this as feedback that might help make the RFC more clear. While reading some sections, I got some questions, that were addressed by a subsequent section later on. For the real RFC, I'd recommend trying to forward-link sections more aggressively, but Discuss isn't really a good place to do that.
Excess arguments are required in that example for the outputs, but it is unclear to me from the example how they are useful for the inputs. Like, I'd find the following much more readable:
asm!(
"mul {b}, {a}",
a = in(reg) a, b = in("eax") b,
lateout("eax") lo, lateout("edx") hi
);
nomem
means that the asm code does not read or write to memory.
I know that this is the introduction, but maybe briefly explain here already how this could happen, e.g., if eax
contains a pointer address, the assembly block could read or write through it, e.g., into the stack, or into the heap.
sym
The user-level explanation would benefit from an example about this using a function or a static or both.
xmm[0-7]
(32-bit) xmm[0-15]
(64-bit)
I suppose that ymm
and zmm
are also accessible here right? (these are mentioned in the "template modifiers" section below but not here). Also, the note section below should explain what "32-bit" means in this context (e.g. consider x86_64-...-gnu-x32
with 32-bit pointers but on a 64-bit architecture for the distinction).
The stack pointer must be restored to its original value at the end of an asm code block.
I see the danger of allowing to modify sp
, but why should be impossible to do so?
The placeholders can be augmented by modifiers which are specified after the :
in the curly braces.
An example of this would be helpful in the user-level explanation.
pure
: The asm
block has no side effects, and its outputs depend only on its direct inputs (i.e. the values themselves, not what they point to). This allows the compiler to execute the asm
block fewer times than specified in the program (e.g. by hoisting it out of a loop) or even eliminate it entirely if the outputs are not used.
We don't have this restriction for const fn
yet, but maybe we end up having it (e.g. such that const functions can offset pointer values, but they can't read through them and return different values for the same input value). It might make sense to call this attribute const
instead of pure, or to leave that as an unresolved question. An alternative name would be readnone
to specify that the function does not read or write to memory, and use a different modifier to also specify that it does not have any side-effects, Such that "pure" becomes readnone+nosideeffect
.
Mapping to LLVM IR
The Cranelift issue is mentioned later, but I was wondering while reading this section: "What about cranelift?". Maybe add a hint somewhere around here that this is covered later?
Unfamiliarity
FWIW, every time I have to use inline assembly, I need to re-read GCC docs, since clang docs are horrible, and then re-read LLVM LangRef docs, and enjoy a lot of frustration to get anything done with their syntax. So I'd argue that "Unfamiliarity" is a good thing, and this RFC has been a joy to read and I have the feeling that I would actually be able to get some work done or read some code with it, without having to go through a lot of obscure compiler docs.
Should a pure
asm statement with no outputs be an error or just a warning?
If we make it an error we can always relax that to a warning later, once we find convincing use cases. Code generators could "just" not emit the assembly block if its pure and has no outputs.
Should we keep the same flags for the template modifiers as LLVM/GCC? Or should we use our own?
I did not understand what the issue was here from reading the RFC. I think an example somewhere showing the template modifiers with flags would have helped.
Should we allow passing a value expression (rvalue) as an inout
operand? The semantics would be that of an input which is allowed to be clobbered (i.e. the output is simply discarded).
This sounds useful.
Should we disallow the use of these registers on the frontend (rustc) or leave it for the backend (LLVM) to produce a warning if these are used?
The quality of the backend error messages is often not as good as that of rustc error messages (e.g. we could add a note explaining why this isn't allowed, and pointing users to the docs), so I very much prefer to error here in the frontend.
Do we need to add support for tied operands? Most use cases for those should already be covered by inout
.
Should we support x86 high byte registers ( ah
, bh
, ch
, dh
) as inputs/outputs? These are supported by LLVM but not by GCC, so I feel a bit uncomfortable relying on them.
Should we add formatting flags for imm
operands (e.g. x
to format a number as hex)? This is probably not needed in practice.
I'd wait for a convincing use cases.
Should we support memory operands ( "m"
)? This would allow generating more efficient code by taking advantage of addressing modes instead of using an intermediate register to hold the computed address.
I think we should support this as a future extension, but leave this out of the initial version of this RFC.
Should we support some sort of shorthand notation for operand names to avoid needing to write blah = out(reg) blah
? For example, if the expression is just a single identifier, we could implicitly allow that operand to be referred to using that identifier.
This sounds very handy. I wonder if we could do this for the format!
ing macros as well. I end up doing name = name
a lot. This can be done later, as part of a different RFC that does it for all formatting macros.
What should preserves_flags
do on architectures that don't have condition flags (e.g. RISC-V)? Do nothing? Compile-time error?
Compile-time error in the frontend, stating that this architecture does not have condition flags. Otherwise readers of the code might end up worrying whether that actually means something, when it does not.
Future possibilities
These all sound as things worth supporting, but better be supported in subsequent RFCs. This one is already long enough, and I think we should try to agree on the foundation first.