Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upTracking issue for `asm` (inline assembly) #29722
Comments
aturon
added
T-lang
B-unstable
labels
Nov 9, 2015
This comment has been minimized.
This comment has been minimized.
|
Will there be any difficulties with ensuring the backward-compatibility of inline assembly in stable code? |
This comment has been minimized.
This comment has been minimized.
|
@main-- has a great comment at rust-lang/rfcs#1471 (comment) that I'm reproducing here for posterity:
|
This comment has been minimized.
This comment has been minimized.
briansmith
commented
Apr 8, 2016
|
I personally think it would be better to do what Microsoft did in MSVC x64: define a (nearly-)comprehensive set of intrinsic functions, for each asm instruction, and do "inline asm" exclusively through those intrinsics. Otherwise, it's very difficult to optimize the code surrounding inline asm, which is ironic since many uses of inline asm are intended to be performance optimizations. One advantage of the instrinsic-based approach is that it doesn't need to be an all-or-nothing thing. You can define the most needed intrinsics first, and build the set out incrementally. For example, for crypto, having Further, the intrinsics would be a good idea to add even if it were ultimately decided to support inline asm, as they are much more convenient to use (based on my experience using them in C and C++), so starting with the intrinsics and seeing how far we get seems like a zero-risk-of-being-wrong thing. |
This comment has been minimized.
This comment has been minimized.
|
Intrinsics are good, but I expect that kind of hackery will be rare, but I think it's still a useful thing to support. |
This comment has been minimized.
This comment has been minimized.
|
Inline asm is also useful for code that wants to do its own register/stack allocation (e.g. naked functions). |
This comment has been minimized.
This comment has been minimized.
|
@briansmith yeah those are some excellent reasons to use intrinsics where possible. But it's nice to have inline assembly as the ultimate excape hatch. |
This comment has been minimized.
This comment has been minimized.
main--
commented
Apr 9, 2016
•
|
@briansmith Note that On the other hand, intrinsics critically depend on a "sufficiently smart compiler" to achieve at least the performance one would get with a hand-rolled asm implementation. My knowledge on this is outdated but unless there has been significant progress, intrinsics-based implementations are still measurably inferior in many - if not most - cases. Of course they're much more convenient to use but I'd say that programmers really don't care much about that when they're willing to descend into the world of specific CPU instructions. Now another interesting consideration is that intrinsics could be coupled with fallback code on architectures where they're not supported. This gives you the best of both worlds: Your code is still portable - it can just employ some hardware accelerated operations where the hardware supports them. Of course this only really pays off for either very common instructions or if the application has one obvious target architecture. Now the reason why I'm mentioning this is that while one could argue that this may potentially even be undesirable with compiler-provided intrinsics (as you'd probably care about whether you actually get the accelerated versions plus compiler complexity is never good) I'd say that it's a different story if the intrinsics are provided by a library (and only implemented using inline asm). In fact, this is the big picture I'd prefer even though I can see myself using intrinsics more than inline asm. (I consider the intrinsics from RFC #1199 somewhat orthogonal to this discussion as they exist mostly to make SIMD work.) |
sanxiyn
added
the
A-inline-assembly
label
May 10, 2016
This comment has been minimized.
This comment has been minimized.
I'm not sure what you mean here. It's true that the compiler can't break down the asm into its individual operations to do strength reduction or peephole optimizations on it. But in the GCC model, at least, the compiler can allocate the registers it uses, copy it when it replicates code paths, delete it if it's never used, and so on. If the asm isn't volatile, GCC has enough information to treat it like any other opaque operation like, say, But I haven't used it a whole lot, especially not recently. And I have no experience with LLVM's rendition of the feature. So I'm wondering what's changed, or what I've misunderstood all this time. |
alexcrichton
referenced this issue
Sep 26, 2016
Open
can bypass validation of `asm!` in a function declaration using `inline(always)` #36718
Ms2ger
referenced this issue
Jun 29, 2017
Open
Tracking: Unstable Rust feature gates used by Servo #5286
This comment has been minimized.
This comment has been minimized.
|
We discussed this issue at the recent work week as @japaric's survey of the
Despite the issues listed above we wanted to be sure to at least come away with some ability to move this issue forward! To that end we brainstormed a few strategies of how we can nudge inline assembly towards stabilization. The primary way forward would be to investigate what clang does. Presumably clang and C have effectively stable inline assembly syntax and it may be likely that we can just mirror whatever clang does (especially wrt LLVM). It would be great to understand in greater depth how clang implements inline assembly. Does clang have its own translation layer? Does it validate any input parameters? (etc) Another possibility for moving forward is to see if there's an assembler we can just take off the shelf from elsewhere that's already stable. Some ideas here were nasm or the plan9 assembler. Using LLVM's assembler has the same problems about stability guarantees as the inline assembly instruction in the IR. (it's a possibility, but we need a stability guarantee before using it) |
This comment has been minimized.
This comment has been minimized.
|
I would like to point out that LLVM's inline asm syntax is different from the one used by clang/gcc. Differences include:
Clang will convert inline asm from the gcc format into the LLVM format before passing it on to LLVM. It also performs some validation of the constraints: for example it ensures that In light of this I think that we should implement the same translation and validation that clang does and support proper gcc inline asm syntax instead of the weird LLVM one. |
This comment has been minimized.
This comment has been minimized.
|
There's an excellent video about summaries with D, MSVC, gcc, LLVM, and Rust with slides online |
This comment has been minimized.
This comment has been minimized.
jcranmer
commented
Jul 20, 2017
|
As someone who'd love to be able to use inline ASM in stable Rust, and with more experience than I want trying to access some of the LLVM MC APIs from Rust, some thoughts:
|
Mark-Simulacrum
added
the
C-tracking-issue
label
Jul 22, 2017
This comment has been minimized.
This comment has been minimized.
|
I've been having a bit of play to see what can be done with procedural macros. I've written one that converts GCC style inline assembly to rust style https://github.com/parched/gcc-asm-rs. I've also started working on one that uses a DSL where the user doesn't have to understand the constraints and they're all handled automatically. So I've come to the conclusion that I think rust should just stabilise the bare building blocks, then the community can iterate out of tree with macros to come up with best solutions. Basically, just stabilise the llvm style we have now with only "r" and "i" and maybe "m" constraints, and no clobbers. Other constraints and clobbers can be stabilised later with their own mini rfc type things. |
This comment has been minimized.
This comment has been minimized.
|
Personally I'm starting to feel as though stabilizing this feature is the sort of massive task that will never get done unless somehow someone hires a full-time expert contractor to push on this for a whole year. I want to believe that @parched's suggestion of stabilizing |
This comment has been minimized.
This comment has been minimized.
main--
commented
Oct 4, 2017
As a data point, I happen to be working on a crate right now that depends on While it certainly has its advantages, I'm a bit wary of the "stabilize building blocks and leave the rest to proc-macros"-approach. It essentially outsources the design, RFC and implementation process to whoever wants to do the job, potentially no one. Of course having weaker stability/quality guarantees is the entire point (the tradeoff is that having something imperfect is already much better than having nothing at all), I understand that. At least the building blocks should be well-designed - and in my opinion, |
This comment has been minimized.
This comment has been minimized.
|
One idea, … Today, there is already a project, named dynasm, which can help you generate assembly code with a plugin used to pre-process the assembly with one flavor of x64 code. This project does not answer the problem of inline assembly, but it can certainly help, if rustc were to provide a way to map variables to registers, and accept to insert set of bytes in the code, such project could also be used to fill-up these set of bytes. This way, the only standardization part needed from rustc point of view, is the ability to inject any byte sequence in the generated code, and to enforce specific register allocations. This removes all the choice for specific languages flavors. Even without dynasm, this can also be used as a way to make macros for the cpuid / rtdsc instructions, which would just be translated into the raw sequence of bytes. I guess the next question might be if we want to add additional properties/constraints to the byte-sequences. |
This comment has been minimized.
This comment has been minimized.
|
[EDIT: I don't think anything I said in this comment is correct.] If we want to continue to use LLVM's integrated assembler (I assume this is faster than spawning an external assembler), then stabilization means stabilizing on exactly what LLVM's inline assembly expressions and integrated assembler support—and compensating for changes to those, should any occur. If we're willing to spawn an external assembler, then we can use any syntax we want, but we're then foregoing the advantages of the integrated assembler, and exposed to changes in whatever external assembler we're calling. |
This comment has been minimized.
This comment has been minimized.
|
I think it would be strange to stabilize on LLVM's format when even Clang doesn't do that. Presumably it does use LLVM's support internally, but it presents an interface more like GCC. |
This comment has been minimized.
This comment has been minimized.
|
I'm 100% fine with saying "Rust supports exactly what Clang supports" and calling it a day, especially since AFAIK Clang's stance is "Clang supports exactly what GCC supports". If we ever have a real Rust spec, we can soften the language to "inline assembly is implementation-defined". Precedence and de-facto standardization are powerful tools. If we can repurpose Clang's own code for translating GCC syntax to LLVM, all the better. The alternative backend concerns don't go away, but theoretically a Rust frontend to GCC wouldn't be much vexed. Less for us to design, less for us to endlessly bikeshed, less for us to teach, less for us to maintain. |
This comment has been minimized.
This comment has been minimized.
|
If we stabilize something defined in terms of what clang supports, then we should call it There are a few things I'd like to see in Rust inline assembly:
Norman Ramsey and Mary Fernández wrote some papers about the New Jersey Machine Code Toolkit way back when that have excellent ideas for describing assembly/machine language pairs in a compact way. They tackle (Pentium Pro-era) iA-32 instruction encodings; it is not at all limited to neat RISC ISAs. |
This comment has been minimized.
This comment has been minimized.
|
I'd like to reiterate again the conclusions from the most recent work week:
To me this is the definition of "if we stabilize this now we will guarantee to regret it in the future", and not only "regret it" but seems very likely for "causes serious problems to implement any new system". At the absolute bare minimum I'd firmly believe that bullet (2) cannot be compromised on (aka the definition of stable in "stable channel"). The other bullets would be quite sad into forgo as it erodes the expected quality of the Rust compiler which is currently quite high. |
This comment has been minimized.
This comment has been minimized.
|
@jcranmer wrote:
I would think that, in practice, it would be quite difficult to infer clobber lists. Just because a machine-language fragment uses a register doesn't mean it clobbers it; perhaps it saves it and restores it. Conservative approaches could discourage the code generator from using registers that would be fine to use. |
This comment has been minimized.
This comment has been minimized.
dancrossnyc
commented
Aug 22, 2018
|
@gnzlbg I took a look through https://rust-lang-nursery.github.io/stdsimd/x86_64/stdsimd/arch/x86_64/index.html and the only privileged instructions I saw in my quick sweep (I didn't do an exhaustive search) were |
This comment has been minimized.
This comment has been minimized.
|
@dancrossnyc yes some of those are the ones I meant (we implement These are available in |
This comment has been minimized.
This comment has been minimized.
dancrossnyc
commented
Aug 22, 2018
|
@gnzlbg I don't know why Don't get me wrong; the presence of LLVM intrinsics in |
This comment has been minimized.
This comment has been minimized.
|
@dancrossnyc when I mentioned
We already do and they are available in stable Rust.
I added these intrinsics. We realized the privilege issues and decided to add them anyways because it is perfectly fine for a program depending on
Agreed, that's why this issue is still open ;) |
This comment has been minimized.
This comment has been minimized.
dancrossnyc
commented
Aug 22, 2018
|
@gnzlbg sorry, I don't mean to derail this by rabbit-holing on However, as near as I can tell, the only intrinsics that require privileged execution are those related to I think otherwise we're in agreement here. |
This comment has been minimized.
This comment has been minimized.
|
FWIW per the If the vendor doesn't expose an intrinsic for it, then |
This comment has been minimized.
This comment has been minimized.
dancrossnyc
commented
Aug 22, 2018
|
Sorry, I understood you saying you wrote the intrinsics for Anyway, yes, I think we fundamentally agree: intrinsics aren't the place for everything, and that's why we'd like to see asm!() moved to stable. I'm really excited to hear that progress is being made in this area, as you said yesterday, and if we can gently nudge @Florob to bubble this up closer to the top of the stack, we'd be happy to do so! |
This comment has been minimized.
This comment has been minimized.
|
A few additional details and use cases for When you're writing an operating system, firmware, certain types of libraries, or certain other types of system code, you need full access to platform-level assembly. Even if we had intrinsics that exposed every single instruction in every architecture Rust supports (which we don't come anywhere close to having), that still wouldn't be enough for some of the stunts that people regularly pull with inline assembly. Here are a small fraction of things you can do with inline assembly that you can't easily do in other ways. Every single one of these is a real-world example I've seen (or in some cases written), not a hypothetical.
|
This comment has been minimized.
This comment has been minimized.
dancrossnyc
commented
Aug 22, 2018
|
+1e6 |
This comment has been minimized.
This comment has been minimized.
josevalaad
commented
Aug 23, 2018
|
Ok, I will try the intrinsics approach and see where it takes. You are probably right and that's the best approach for my case. Thank you! |
This comment has been minimized.
This comment has been minimized.
|
@joshtriplett nailed it! These are the exact use cases I had in mind. loop {
:thumbs_up:
}I would add a couple of other use cases:
|
This comment has been minimized.
This comment has been minimized.
|
@mark-i-m Absolutely! And generalizing a point that has sub-cases in both of our lists: translating between calling conventions. |
This comment has been minimized.
This comment has been minimized.
mqudsi
commented
Dec 14, 2018
|
I am closing out #53118 in favor of this issue and copying the PR here for the record. Note that this is from August, but a brief look seems to indicate the situation hasn't changed: The section on inline assembly needs an overhaul; in its present state it implies that the behavior and syntax is tied to I didn't find an authoritative source for the behavior of inline assembly when it comes to ARM target, but per my experimentation and referencing the ARM GCC inline assembly documentation, the following points seem to be completely off:
I think what threw me the most is the closing statement:
Which does not seem to be universally true. |
This comment has been minimized.
This comment has been minimized.
main--
commented
Dec 16, 2018
A notion of intel vs at&t syntax only exists on x86 (though there may be other cases I'm not aware of). It's unique in that they are two different languages sharing the same set of mnemonics to represent the same set of binary code. The GNU ecosystem has established at&t syntax as the dominating default for the x86 world which is why this is what inline asm defaults to. You are mistaken in that it is very much a direct binding to LLVM's inline assembler expressions which in turn mostly just dump plaintext (after processing substitutions) into the textual assembly program. None of this is unique (or even relevant) to or about today's
This is a direct consequence of the "dumb"/simple plaintext insertion I described above. As the error message indicates the
Your observation is entirely correct, each platform makes up both its own binary format and its own assembly language. They are completely independent and (mostly) unprocessed by the compiler - which is the entire point of programming in raw assembler!
Sadly there is quite a big mismatch between the inline asm implementation of LLVM that rustc exposes and the implementation of GCC (which clang emulates). Without a decision on how to move forward with
These workarounds apply to all but a small set of very specific edge cases. If you hit one of those (luckily I haven't yet) you're out of luck though. I agree that the documentation is quite lackluster but it's good enough for anyone familiar with inline asm. If you aren't, you probably should not be using it. Don't get me wrong - you should definitely feel free to experiment and learn but as |
This comment has been minimized.
This comment has been minimized.
You can also invoke the |
This comment has been minimized.
This comment has been minimized.
BartMassey
commented
Dec 16, 2018
|
@main-- wrote:
I mean, not entirely out of luck. You just have to use Rust's (I have another use case: I would like to teach systems programming computer architecture and stuff using Rust instead of C someday. Not having inline assembly would make this much more awkward.) I wish we would make inline assembly a priority in Rust and stabilize it sooner rather than later. Maybe this should be a Rust 2019 goal. I am fine with any of the solutions you list in your nice comment earlier: I could live with the problems of any of them. Being able to inline assembly code is for me a prerequisite to writing Rust instead of C everywhere: I really need it to be stable. |
This comment has been minimized.
This comment has been minimized.
Please write a Rust 2019 blog post and express this concern. I think if enough of us do that, we can influence the roadmap. |
This comment has been minimized.
This comment has been minimized.
mqudsi
commented
Dec 17, 2018
|
To clarify my comment above - the problem is that the documentation does not explain just how "deeply" the contents of the (What helped me was studying the expanded macro output, which cleared up what was going on) I think there needs to be less abstraction on that page. Make it clearer what gets parsed by LLVM and what gets interpreted as ASM directly. What parts are specific to rust, what parts are specific to the hardware you are running on, and what parts belong to the glue that holds them together. |
This comment has been minimized.
This comment has been minimized.
Recent progress on cross-language LTO makes me wonder if some of the downsides of this avenue can be reduced, effectively inlining this "external assembly blob". (probably not) |
This comment has been minimized.
This comment has been minimized.
BartMassey
commented
Dec 19, 2018
Even if this works, I don't want to write my inline assembly in C. I want to write it in Rust. :-) |
This comment has been minimized.
This comment has been minimized.
You can compile and link |
This comment has been minimized.
This comment has been minimized.
I believe this is not currently feasible as cross-language LTO relies on having LLVM IR and assembly would not generate this. |
This comment has been minimized.
This comment has been minimized.
You can stuff assembly into module level assembly in LLVM IR modules. |
This comment has been minimized.
This comment has been minimized.
|
Does anyone know what the most recent proposal/current status is? Since the theme of the year is "maturity and finishing what we started", it seems like a great opportunity to finally finish up |
This comment has been minimized.
This comment has been minimized.
|
Vague plans for an new (to be stabilized) syntax were discussed last February: https://paper.dropbox.com/doc/FFI-5NmXV30TGiSsr9dIxpqpq According to those notes @joshtriplett and @Amanieu signed up to write an RFC. |
aturon commentedNov 9, 2015
This issue tracks stabilization of inline assembly. The current feature has not gone through the RFC process, and will probably need to do so prior to stabilization.